feat: Add Wasm platform by conditionally building without swift-nio or async-kit - #116
Draft
scottmarchant wants to merge 3 commits into
Draft
feat: Add Wasm platform by conditionally building without swift-nio or async-kit#116scottmarchant wants to merge 3 commits into
scottmarchant wants to merge 3 commits into
Conversation
Motivation: Neither SwiftNIO nor AsyncKit can be built for `wasm32-unknown-wasip1`: NIOPosix is built on POSIX sockets and threads, neither of which WASI preview 1 provides, and AsyncKit's connection pool is built on NIOPosix in turn. SQLiteKit uses them for the connection pool and for the legacy `EventLoopFuture` half of an API whose `async` half is already the recommended one, so it can build without them, given somewhere to put the differences. Modifications: Gate the affected declarations with `#if canImport(...)`, keyed on the module that actually supplies each API rather than on a platform, so the sources stay in step with whatever the manifest resolves for the target being built and with the gates sqlite-nio and sql-kit apply to the same dependencies. What drops out where the modules are absent: - `SQLiteConnectionSource`, a `ConnectionPoolSource` over AsyncKit and `NIOThreadPool`, drops out entirely, along with the `AsyncKit` re-export. Callers use sqlite-nio's concrete `async` `SQLiteConnection` directly; there is no connection pool on those platforms. - The `SQLDatabase` wrapper's `eventLoop` and its `execute(sql:_:) -> EventLoopFuture<Void>` overload drop out. SQLKit removes both as protocol requirements on the same platforms. `withSession(_:)` stays: its signature names no NIO or AsyncKit type, and the async `withConnection` it calls is an unconditional requirement of sqlite-nio's `SQLiteDatabase`. - `SQLiteDataDecoder`'s JSON fallback reads a `[UInt8]` blob rather than a `ByteBuffer`, matching `SQLiteData`'s representation there. The decoder's two blob cases stay forked rather than collapsing to the `[UInt8]` form on both: `Data.init(buffer:byteTransferStrategy:)` is a zero-copy bridge with no `[UInt8]` equivalent, so collapsing them would silently add a copy where SwiftNIO is present. The gate names NIOFoundationCompat, which is the module that declares that initializer. `SQLiteDataEncoder`'s `import NIOCore` is dropped rather than gated: the file references no NIOCore symbol, so the import was already dead. Result: Where SwiftNIO and AsyncKit are available the public API and the symbol graph are unchanged, the same symbols with the same `docComment` line counts, and `diagnose-api-breaking-changes` reports no differences. Where they are absent SQLiteKit compiles down to the pool-free, `async`-only surface.
Motivation: AsyncKit cannot be built for `wasm32-unknown-wasip1`: its connection pool rides NIOPosix, which needs the POSIX sockets and threads WASI preview 1 lacks. NIOCore does build there, but sqlite-nio's WASI flavor is SwiftNIO-free, so a linked NIOCore would flip the `canImport(NIOCore)` gates against a SQLiteNIO that has no event loops, and NIOFoundationCompat would have no `ByteBuffer` to bridge. A WASI build therefore fails inside a dependency, before the conditional sources in the previous commit get a chance to matter. Modifications: Gate the NIOFoundationCompat and AsyncKit products on `.when(platforms: nonWASIPlatforms)` in both manifests. Target dependency conditions are evaluated per platform, so on WASI the products are simply not linked and the `canImport(...)` gates select the pool-free `async` API. `.when(platforms:)` can only include, never exclude, so excluding one platform means enumerating the others; each list is the set the manifest's own tools version knows about, noted as such so it is not extended without also raising that version. The tools-5.8 list omits `.visionOS`, which SPM only gained in 5.9; toolchains that know that platform read `Package@swift-5.9.swift` instead. Result: On every other platform the resolved dependency set is byte-identical to before. On WASI the build graph contains no SwiftNIO and no AsyncKit module.
Motivation: `vapor/ci`'s reusable unit-test workflow already knows how to build a package for `wasm32-unknown-wasip1`; sqlite-kit simply had not opted in, so nothing stops the SwiftNIO-free configuration from regressing unnoticed. Modifications: Pass `with_wasm: true` to the reusable workflow, as sql-kit already does. Result: The WASI build is checked on every pull request.
Closed
5 tasks
Author
|
Note, I plan to keep this in draft until the required dependency pull requests are merged. At that point, I'll update this, ensure CI passes, then move it to ready for review. |
MahdiBM
reviewed
Jul 30, 2026
MahdiBM
left a comment
There was a problem hiding this comment.
Nothing substantial so looks good to me. Deferring to Gwynne for the approval.
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.
Depends on vapor/sqlite-nio#110 and vapor/sql-kit#202 being merged and tagged before the WASI CI lane here can pass. The released siblings have no WASI support (sqlite-nio's current release, 1.13.0, predates its WASI PR), so the version floors here will need bumping to the first releases that include it once those exist. Stays in draft until then. The native build and tests already pass against the released siblings, so the ordering only matters for the WASI lane.
Builds SQLiteKit on
wasm32-unknown-wasip1without SwiftNIO or AsyncKit. Compiling under Embedded Swift needs a further set of gates; that work is kept out of this PR and is not being proposed yet.Changes:
SQLiteConnectionSourceand theAsyncKitre-export gate oncanImport(AsyncKit), so there is no connection pool on WASI. Every public member of that type namesNIOThreadPool,EventLoop,EventLoopFuture, orConnectionPoolSource, so nothing there survives the absence of those modules.SQLDatabasewrapper'seventLoopand itsEventLoopFutureexecuteoverload gate oncanImport(NIOCore), matching what SQLKit does to the corresponding protocol requirements.withSession(_:)is deliberately not gated: its signature names no NIO or AsyncKit type, and the asyncwithConnectionit calls is an unconditional requirement of sqlite-nio'sSQLiteDatabase, so the full-fidelity implementation compiles everywhere and WASI keeps session pinning rather than falling back to SQLKit's default.SQLiteDataDecoder's JSON fallback reads the blob as[UInt8]where NIOFoundationCompat is absent, matchingSQLiteData's representation there.Package.swiftat tools 5.8 and the existingPackage@swift-5.9.swift) get equivalent.when(platforms: nonWASIPlatforms)hunks, so 5.9+ toolchains are not silently left behind. Same idiom as Add support for WASILibc apple/swift-nio#2671. The tools-5.8 list omits.visionOS, which SPM only gained in 5.9; toolchains that know that platform read the 5.9 manifest instead.with_wasm: truetovapor/ci(sqlite-kit previously had nowith:block at all).Where SwiftNIO and AsyncKit are present, nothing changes: every gate is unconditionally true,
swift package diagnose-api-breaking-changesagainst the base reports no breaking changes, and no public symbol is added or removed.What WASI loses: the connection pool, and nothing else.
SQLiteConnectionSource, itsConnectionPoolItemconformance, and theAsyncKitre-export go away, along with theEventLoopFutureexecuteoverload andeventLoopthat SQLKit removes as requirements on the same platforms. WASI preview 1 is single-threaded, so a pool buys nothing there; callers open sqlite-nio's concrete asyncSQLiteConnectiondirectly and wrap it with.sql(). Upstream vapor/async-kit does not compile for WASI, so it is elided rather than ported, which removes vapor/async-kit#111 as a prerequisite entirely. If async-kit gains WASI support later, the condition relaxes and the pool comes back.Verification:
swift buildandswift test: 9 tests green against the released siblings, which resolve sqlite-nio at 1.13.0. The CI-flags build is warning-free.swift package diagnose-api-breaking-changesreports no breaking changes in SQLiteKit.--swift-sdk swift-6.3.1-RELEASE_wasm), with the sibling WASI branches wired in viaswift package edit --path(the committed manifests keep the upstreamvapor/...URLs and floors): green, zero SwiftNIO or AsyncKit object files, no NIO, AsyncKit, EventLoop, or ByteBuffer symbols.unit-tests / wasmlane cannot pass yet: it resolves the released siblings, which have no WASI support, so it will stay red until the dependency PRs land and are tagged and the floors here are bumped. That is the sequencing note at the top, not a defect in this diff.On tests: no test cases are added. The change introduces no behavior on any platform that compiled before, and the configuration it adds cannot be exercised in CI until the dependency PRs above are released (see the sequencing note at the top).
Notes for review: no SwiftPM traits, no manifest added, no tools-version bump, no new dependencies, and no
os(WASI)anywhere; every gate is a capability gate inSources/only.