Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
4c88a55
Vendor BlueZ's bluetooth.h verbatim
colemancda Aug 1, 2026
8f7b3d9
Vendor BlueZ's hci.h verbatim
colemancda Aug 1, 2026
5c05b7f
Vendor BlueZ's hci_lib.h verbatim
colemancda Aug 1, 2026
59632e1
Vendor BlueZ's sdp.h verbatim
colemancda Aug 1, 2026
249d1a4
Vendor BlueZ's sdp_lib.h verbatim
colemancda Aug 1, 2026
186c4e1
Vendor BlueZ's l2cap.h verbatim
colemancda Aug 1, 2026
3ba48c5
Vendor BlueZ's rfcomm.h verbatim
colemancda Aug 1, 2026
23f2a90
Vendor BlueZ's sco.h verbatim
colemancda Aug 1, 2026
be74890
Vendor BlueZ's bnep.h verbatim
colemancda Aug 1, 2026
e2dab8c
Vendor BlueZ's cmtp.h verbatim
colemancda Aug 1, 2026
be04872
Vendor BlueZ's hidp.h verbatim
colemancda Aug 1, 2026
ee12a04
Vendor BlueZ's uuid.h verbatim
colemancda Aug 1, 2026
4dd84f7
Add the GPL-2.0-or-later license for the vendored headers
colemancda Aug 1, 2026
3c2b231
Add the CBluetoothLinuxABI umbrella header
colemancda Aug 1, 2026
a6af041
Add the CBluetoothLinuxABI module map
colemancda Aug 1, 2026
50b41b8
Document CBluetoothLinuxABI's licensing and stub table
colemancda Aug 1, 2026
73ab4d6
Record the exported symbol surface of libbluetooth.so.3
colemancda Aug 1, 2026
ca644a4
Record which exported symbols are already implemented
colemancda Aug 1, 2026
f6f94bd
Add the ownership audit for each exported symbol
colemancda Aug 1, 2026
b1469be
Add a generator for stubs of not-yet-implemented symbols
colemancda Aug 1, 2026
ba52bd9
Generate stubs for the not-yet-implemented symbols
colemancda Aug 1, 2026
555fe15
Add a generator for the linker version script and export list
colemancda Aug 1, 2026
0e33e28
Add the linker version script for libbluetooth.so.3
colemancda Aug 1, 2026
e6e57b8
Add the flat exported symbol list for check-exports
colemancda Aug 1, 2026
aa0ae21
Add a script to assert exported symbols against a list
colemancda Aug 1, 2026
7cb042e
Add the CMake shared-library placeholder translation unit
colemancda Aug 1, 2026
a5c0a94
Add the pkg-config template for libbluetooth
colemancda Aug 1, 2026
8c5e9e9
Add the CMake build for libbluetooth.so.3
colemancda Aug 1, 2026
0d08c6c
Add the opt-in CBluetoothLinuxABI target
colemancda Aug 1, 2026
6db3296
Document where the differential conformance driver lives
colemancda Aug 1, 2026
dc7f015
Add script to generate HCI lookup tables from BlueZ hci.c
colemancda Aug 1, 2026
feae81c
Add generated HCI lookup tables
colemancda Aug 1, 2026
6198c1f
Implement HCI string converter family
colemancda Aug 1, 2026
72cbde5
Wire BluetoothLinuxABI into the C ABI SwiftPM build
colemancda Aug 1, 2026
783cf08
Add unit tests for HCI string converters
colemancda Aug 1, 2026
9df0bc1
Mark HCI string converter symbols as implemented
colemancda Aug 2, 2026
29e270e
Regenerate C ABI stub table
colemancda Aug 2, 2026
23d5c4b
Add differential conformance driver for HCI string converters
colemancda Aug 2, 2026
ed0be9a
Add conformance compare script for BluetoothLinux drivers
colemancda Aug 2, 2026
fbda7ae
Wire BluetoothSDP and BluetoothLinuxABI into the CMake build
colemancda Aug 2, 2026
f533b48
Implement HCI device management family
colemancda Aug 2, 2026
56d0198
Add unit tests for HCI device management
colemancda Aug 2, 2026
99f47bb
Mark HCI device management symbols as implemented
colemancda Aug 2, 2026
74cc41e
Regenerate C ABI stub table
colemancda Aug 2, 2026
9c02486
Add shared HCI command request helpers
colemancda Aug 2, 2026
3c62693
Implement connection and link-policy HCI command wrappers
colemancda Aug 2, 2026
4facdb1
Implement remote-device query HCI command wrappers
colemancda Aug 2, 2026
14635ae
Implement local controller info HCI command wrappers
colemancda Aug 2, 2026
53403ad
Implement LE HCI command wrappers
colemancda Aug 2, 2026
b8cef5f
Implement controller configuration HCI command wrappers
colemancda Aug 2, 2026
c85791f
Implement hci_inquiry
colemancda Aug 2, 2026
c37234a
Mark HCI command wrapper symbols as implemented
colemancda Aug 2, 2026
4de8ba9
Regenerate C ABI stub table
colemancda Aug 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
263 changes: 263 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
##
## CMakeLists.txt
## BluetoothLinux
##
## Builds `libbluetooth.so.3` — a drop-in replacement for the shared
## library BlueZ installs, implemented in Swift.
##
## SwiftPM drives development and the test suites; CMake exists because
## soname, symbol versioning and install name are not expressible in
## `Package.swift`.
##
## Phase 0's deliverable is a library that loads, resolves every one of
## the 218 symbols the reference exports, and aborts loudly on every
## call that is not implemented yet (see scripts/gen_stubs.py). Each
## subsequent phase moves names from `scripts/symbols.txt` into
## `scripts/implemented.txt` and the stubs disappear.
##
## Replacing this library cannot break the Bluetooth stack itself:
## nothing in the `bluez` package links it — bluetoothd, bluetoothctl,
## btmon, hciconfig and sdptool all statically link
## libbluetooth-internal.a. It exists solely for third-party consumers.
## The corollary is that there is no "run bluetoothctl and see" smoke
## test, which is why Conformance/ matters.
##
## The Ninja generator is required — CMake does not support the Swift
## language with Makefiles.
##
## Usage:
## cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \
## -DBLUETOOTH_PACKAGE_PATH=../Bluetooth
## cmake --build build
## cmake --build build --target check-exports
## cmake --install build --prefix /usr/local
##

cmake_minimum_required(VERSION 3.26)

if(NOT CMAKE_GENERATOR MATCHES "Ninja")
message(FATAL_ERROR
"The Ninja generator is required for Swift targets; "
"re-run with -G Ninja.")
endif()

project(BluetoothLinuxABI
VERSION 3.19.15
DESCRIPTION "Swift implementation of the BlueZ libbluetooth ABI"
LANGUAGES C Swift)

# Checked after project(), which is what sets CMAKE_SYSTEM_NAME.
if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux")
message(FATAL_ERROR
"libbluetooth.so.3 is a Linux library; this build is only "
"supported on Linux.")
endif()

# ---------------------------------------------------------------------
# Options
# ---------------------------------------------------------------------

# Debian 5.85-4 ships libbluetooth.so.3.19.15 (libtool -version-info
# 22:15:19); master is heading to .3.19.16. The soname is what consumers
# actually bind to.
set(LIBBLUETOOTH_SOVERSION 3)
set(LIBBLUETOOTH_VERSION 3.19.15)

option(BLUETOOTH_ABI_STATIC_STDLIB
"Statically link the Swift runtime into the shared library" OFF)

option(BLUETOOTH_ABI_INSTALL_HEADERS
"Install the vendored BlueZ headers under <prefix>/include/bluetooth" ON)

# The Swift implementations of the non-socket half live in
# PureSwift/Bluetooth. Point this at a checkout of it.
set(BLUETOOTH_PACKAGE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../Bluetooth"
CACHE PATH "Path to a PureSwift/Bluetooth checkout")

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)

if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE)
endif()

include(GNUInstallDirs)

find_package(Python3 COMPONENTS Interpreter REQUIRED)

# ---------------------------------------------------------------------
# Generated sources
# ---------------------------------------------------------------------
#
# Both the stub table and the version script are derived from
# scripts/symbols.txt, so the export surface has exactly one source of
# truth. They are committed as well as generated, so that a plain
# `swift build` sees the same files.

set(LIBBLUETOOTH_STUBS
"${CMAKE_CURRENT_SOURCE_DIR}/Sources/CBluetoothLinuxABI/gen/cbt_stubs.c")
set(LIBBLUETOOTH_VERSION_SCRIPT
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/libbluetooth.map")

add_custom_command(
OUTPUT "${LIBBLUETOOTH_STUBS}"
COMMAND "${Python3_EXECUTABLE}"
"${CMAKE_CURRENT_SOURCE_DIR}/scripts/gen_stubs.py"
DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/scripts/gen_stubs.py"
"${CMAKE_CURRENT_SOURCE_DIR}/scripts/symbols.txt"
"${CMAKE_CURRENT_SOURCE_DIR}/scripts/implemented.txt"
COMMENT "Generating unimplemented-symbol stubs"
VERBATIM)

add_custom_command(
OUTPUT "${LIBBLUETOOTH_VERSION_SCRIPT}"
"${CMAKE_CURRENT_SOURCE_DIR}/scripts/exported.txt"
COMMAND "${Python3_EXECUTABLE}"
"${CMAKE_CURRENT_SOURCE_DIR}/scripts/gen_symbols.py"
DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/scripts/gen_symbols.py"
"${CMAKE_CURRENT_SOURCE_DIR}/scripts/symbols.txt"
COMMENT "Generating the version script"
VERBATIM)

# ---------------------------------------------------------------------
# CBluetoothLinuxABI — the C surface
# ---------------------------------------------------------------------
#
# `include/bluetooth/` holds the eleven public headers vendored verbatim
# from BlueZ; they are GPL-2.0-or-later, unlike the rest of this
# repository. See Sources/CBluetoothLinuxABI/README.md.

add_library(CBluetoothLinuxABI STATIC ${LIBBLUETOOTH_STUBS})

target_include_directories(CBluetoothLinuxABI PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}/Sources/CBluetoothLinuxABI/include")

# ---------------------------------------------------------------------
# BluetoothLinuxABI — the Swift implementations of the C entry points
# this repository owns (currently: the HCI string converter family)
# ---------------------------------------------------------------------

file(GLOB BLUETOOTH_LINUX_ABI_SOURCES CONFIGURE_DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/Sources/BluetoothLinuxABI/*.swift"
"${CMAKE_CURRENT_SOURCE_DIR}/Sources/BluetoothLinuxABI/gen/*.swift")

add_library(BluetoothLinuxABI STATIC ${BLUETOOTH_LINUX_ABI_SOURCES})

set_target_properties(BluetoothLinuxABI PROPERTIES
Swift_MODULE_NAME BluetoothLinuxABI)

target_link_libraries(BluetoothLinuxABI PUBLIC CBluetoothLinuxABI)

# ---------------------------------------------------------------------
# The non-socket half, from PureSwift/Bluetooth
# ---------------------------------------------------------------------

if(NOT EXISTS "${BLUETOOTH_PACKAGE_PATH}/CMakeLists.txt")
message(FATAL_ERROR
"PureSwift/Bluetooth not found at ${BLUETOOTH_PACKAGE_PATH}. "
"Set -DBLUETOOTH_PACKAGE_PATH=<path to a checkout>.")
endif()

# Its own shared library is not wanted here — this build links the
# static archives into libbluetooth.so.3 instead.
set(BLUETOOTH_ABI_SHARED OFF CACHE BOOL "" FORCE)
set(BLUETOOTH_ABI_INSTALL_HEADERS OFF CACHE BOOL "" FORCE)

add_subdirectory("${BLUETOOTH_PACKAGE_PATH}" "${CMAKE_CURRENT_BINARY_DIR}/Bluetooth")

# ---------------------------------------------------------------------
# libbluetooth.so.3
# ---------------------------------------------------------------------

add_library(bluetooth3 SHARED cmake/empty.c)

set_target_properties(bluetooth3 PROPERTIES
OUTPUT_NAME bluetooth
SOVERSION ${LIBBLUETOOTH_SOVERSION}
VERSION ${LIBBLUETOOTH_VERSION}
# The link is driven by swiftc, and `bluetooth3` would collide with
# the `Bluetooth` module; name the vestigial module explicitly.
Swift_MODULE_NAME libbluetooth_abi)

# Every entry point is referenced only from outside, so the archives
# holding them must be linked whole or the linker drops the lot.
#
# The archives are named explicitly through `LINKER:` (which expands to
# `-Xlinker` here) rather than via `$<LINK_LIBRARY:WHOLE_ARCHIVE>`:
# swiftc's driver silently discards bare `.a` arguments, so the result
# would be a shared library with no symbols in it.
target_link_options(bluetooth3 PRIVATE
"LINKER:--whole-archive"
"LINKER:$<TARGET_FILE:BluetoothABI>"
"LINKER:$<TARGET_FILE:BluetoothSDP>"
"LINKER:$<TARGET_FILE:BluetoothLinuxABI>"
"LINKER:$<TARGET_FILE:CBluetooth>"
"LINKER:$<TARGET_FILE:CBluetoothLinuxABI>"
"LINKER:--no-whole-archive")

add_dependencies(bluetooth3 BluetoothABI BluetoothSDP BluetoothLinuxABI CBluetooth CBluetoothLinuxABI)

target_link_libraries(bluetooth3 PRIVATE Bluetooth)

# Pin the export list. Upstream exports everything and carries no
# version script; see scripts/gen_symbols.py for why we do not, and for
# the audit that owes before this list is final.
target_link_options(bluetooth3 PRIVATE
"LINKER:--version-script=${LIBBLUETOOTH_VERSION_SCRIPT}")
set_target_properties(bluetooth3 PROPERTIES
LINK_DEPENDS "${LIBBLUETOOTH_VERSION_SCRIPT}")

add_custom_target(generate-version-script
DEPENDS "${LIBBLUETOOTH_VERSION_SCRIPT}")
add_dependencies(bluetooth3 generate-version-script)

if(BLUETOOTH_ABI_STATIC_STDLIB)
# Any process loading libbluetooth.so.3 otherwise pulls in
# libswiftCore. Measure the result against the 99 KB reference
# package before making this the default.
target_link_options(bluetooth3 PRIVATE "-static-stdlib")
endif()

# ---------------------------------------------------------------------
# Install
# ---------------------------------------------------------------------

configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/bluez.pc.in"
"${CMAKE_CURRENT_BINARY_DIR}/bluez.pc"
@ONLY)

install(TARGETS bluetooth3
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}")

install(FILES "${CMAKE_CURRENT_BINARY_DIR}/bluez.pc"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")

if(BLUETOOTH_ABI_INSTALL_HEADERS)
# The eleven headers the reference package installs.
file(GLOB LIBBLUETOOTH_HEADERS
"${CMAKE_CURRENT_SOURCE_DIR}/Sources/CBluetoothLinuxABI/include/bluetooth/*.h")
install(FILES ${LIBBLUETOOTH_HEADERS}
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/bluetooth")
endif()

# ---------------------------------------------------------------------
# Export verification
# ---------------------------------------------------------------------
#
# Diffs the built library's dynamic symbol table against the generated
# list. Both a missing and an extra symbol fail, so the export surface
# cannot drift silently — this is the check that makes "the library
# still resolves everything the reference did" an assertion rather than
# a hope.

add_custom_target(check-exports
COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/scripts/check-exports.sh"
"$<TARGET_FILE:bluetooth3>"
"${CMAKE_CURRENT_SOURCE_DIR}/scripts/exported.txt"
DEPENDS bluetooth3 generate-version-script
COMMENT "Checking exported symbols against scripts/exported.txt"
VERBATIM)
52 changes: 52 additions & 0 deletions Conformance/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Conformance

Differential conformance for `libbluetooth.so.3`: C programs compiled
twice — once against the reference library, once against ours — with the
outputs diffed and accepted deltas recorded.

Because no BlueZ binary links the shared library (`bluetoothd`,
`bluetoothctl`, `btmon`, `hciconfig` and `sdptool` all statically link
`libbluetooth-internal.a`), "nothing crashed" tells us nothing. These
programs are the only signal that the replacement behaves like the
original.

## Phase 1 — done, and it lives in PureSwift/Bluetooth

The `bluetooth.c` and `bt_uuid_*` families are implemented there, so
their conformance harness is there too: `Conformance/compare.sh` in the
Bluetooth checkout, with `conformance_address.c` and
`conformance_uuid.c`.

It can be pointed at the library this repository builds:

```
cmake --build .build/cmake
BLUEZ_SOURCE=<bluez source tree> \
../Bluetooth/Conformance/compare.sh .build/cmake/libbluetooth.so.3.19.15
```

## Phases 2–4 — to be added here

One driver per symbol family, following the same shape:

| Driver | Covers | Phase |
|---|---|---|
| `conformance_hci_strings.c` | `hci_*tostr`, `lmp_*`, `pal_*` | 2 |
| `conformance_hci.c` | device and command wrappers | 3 |
| `conformance_sdp_codec.c` | `sdp_gen_pdu` / `sdp_extract_pdu` / `sdp_extract_attr` | 4a |
| `conformance_sdp_session.c` | session, registration, async requests | 4b |

Two cheaper sources of coverage come first, though:

- **BlueZ's own unit tests, reused directly.** `unit/test-uuid.c`,
`unit/test-lib.c` and `unit/test-sdp.c` link
`libbluetooth-internal.la` today; pointing them at our library instead
is free coverage for phases 1 and 4.
- **Fuzzing the parsers.** `bt_string_to_uuid`, `str2ba`, `bachk` and
`sdp_extract_pdu` against random and semi-structured input, comparing
both implementations byte for byte. `sdp_extract_pdu` is the one place
arbitrary remote bytes reach the library.

Then an `LD_PRELOAD` substitution run against a real external consumer,
once one is identified — `bluez-cups` and the Python bindings are the
obvious candidates.
Loading
Loading