Add a libdbus-1 C ABI and a CMake build - #9
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a C ABI for the pure-Swift implementation, and a CMake build that ships it as
libdbus-swift.so.0. Modelled on thePureSwift/Bluetoothpackaging: a C target holding the header, a Swift target holding the entry points, a version script pinning the exports, and acheck-exportstarget that fails if the surface drifts.What the ABI is
A subset of libdbus-1, with the real
dbus_*symbol names and ABI-compatible types, so a C program using the core of libdbus links against this unchanged. 87 exported symbols covering errors, memory, messages, the message iterators, connections and bus operations.DBusErrorandDBusMessageIterreproduce the reference layouts exactly — callers allocate both on the stack — and the type codes and reply constants reproduce the reference values. The declarations are written against the documented ABI rather than copied from libdbus, so no AFL/GPL headers are vendored.What is deliberately absent
The main-loop integration:
dbus_connection_set_watch_functions,dbus_connection_set_timeout_functions,dbus_connection_dispatch,dbus_connection_read_write*andDBusPendingCall.That API exists to hand the caller raw pollable descriptors so it can drive I/O from its own event loop. This implementation owns its I/O in an actor with its own read loop, so there is no descriptor to hand over and no dispatch step to perform. Exposing those functions would mean either lying about what they do or building a second event loop next to the one that already exists. The blocking calls — which is what most callers of that API end up writing anyway — are fully supported.
cmake/symbols.txtpins the list andScripts/check-exports.shfails the build on a symbol in either direction, so that boundary is asserted rather than described.Notes on the implementation
The soname is this library's own, not libdbus-1's. It implements a subset, so claiming
libdbus-1.so.3would let a program linked against the real thing load this and fail at the first symbol outside it.Async to blocking. The Swift API is
async; the C ABI cannot be. A C caller arrives on its own thread, never on a cooperative thread, soblocking(_:)parks that thread on a semaphore while a detached task runs — which is safe precisely because the caller is not on the executor.Reference counting is
Unmanaged, not ARC: a pointer handed to C carries a retain that only the matchingunrefreleases.Iterator state.
DBusMessageIteris a stack value with no destructor, so its state cannot be an allocation the caller is expected to free. The state is owned by the message and reached through a pointer parked in one of the struct's opaque fields, which matches the reference's rule that an iterator is valid only while its message is. An uninitialized iterator is detected and refused rather than read as garbage.The variadics live in C. Swift cannot declare a C variadic function, so
dbus_set_error,dbus_message_append_argsanddbus_message_get_args(plus the_valistforms) are implemented inSources/CDBusABI/varargs.c. They contain no protocol logic — each walks ava_listand calls the Swift iterator entry points.Opt-in
The C ABI targets build only when
SWIFTPM_DBUS_CABI=1. They export fixed C symbol names, so linking them into a process that also links the real libdbus-1 is a duplicate-symbol error, and a Swift package that merely depends onDBusshould never be exposed to that.CMakeLists.txtalways builds them. The defaultswift buildand the existing 205-test suite are unaffected.The one change outside the new targets is
DBusBusTypegainingSendable, which it needs to cross into the detached task.Tests
a{sv}, abandoned containers, and refusing an uninitialized iterator..so. It is not a duplicate: it is the only thing that can exercise the variadic entry points, and it proves the shared object links and loads with every symbol resolvable from outside.Two real bugs surfaced this way and are fixed here:
dbus_message_append_argswas reading the array argument one indirection short. The reference takes the address of the array pointer, not the array.CMakeLists.txtnamed the static archives to the linker throughLINKER:strings, which CMake treats as opaque, so a changed archive did not relink the library — the build silently kept shipping the previous object code. They are now declared inLINK_DEPENDS. Worth knowing, because the same shape appears in the Bluetooth CMakeLists this was modelled on.CI
.github/workflows/cmake.ymlconfigures, builds, checks exports, runs the C smoke test, installs to a staging prefix and asserts the installed layout — on x86_64 and arm64, against Swift 6.2.3 and 6.3.3. It uses noble rather than jammy because CMakeLists requires CMake 3.26+ and jammy ships 3.22. A second job builds and tests the SwiftPM C ABI targets, which are otherwise never built.Also
Package.swiftreturns to trackingPureSwift/Socketatmain, now that PureSwift/Socket#27 has merged.