Skip to content

contrib.sqlite: vendor the SQLite amalgamation + compile via zig cc for self-contained cross builds (miniaudio-style cache) #1372

Description

@paul-hammant

Proposal

Vendor the SQLite amalgamation (sqlite3.c / sqlite3.h, public domain) into contrib/sqlite/ and compile it from source through the same zig cc backend the rest of the toolchain cross-compiles with. This makes sqlite-using programs self-contained across targets — no target-system libsqlite3 required — and caches the compiled object in CI/release infra exactly like std.audio's miniaudio already does.

Why it fits Aether's model well

  • zig cc compiles C from source per-target (tools/ae_cross.c): zig bundles each target's libc/headers, so a dependency-free C amalgamation is the ideal cross case. The SQLite amalgamation is exactly that — pure C99, no external deps, public domain (zero license friction).
  • Precedent already in the tree: std.audio vendors miniaudio (std/audio/miniaudio.h, ~96k lines) and its compiled .o is cached in GitHub Actions, keyed by source content-hash, split restore/save (PRs read, only a merge-to-main fills, on a miss). A vendored sqlite3.c slots into the identical pattern. See .github/workflows/ci.yml "miniaudio object cache" steps.

What it fixes

Today contrib/sqlite links the system -lsqlite3 (aether_sqlite.c bundles nothing; ae_cross.c maps the veneer to -laether_sqlite -lsqlite3). That's the weak link for cross builds: there is no libsqlite3 for, say, aarch64-freebsd on a Linux host, so cross-compiling a sqlite program is effectively blocked. Compiling a vendored amalgamation per-target removes that dependency entirely.

Caveats (why this isn't a no-brainer — the trade-offs to decide)

  1. Repo size / the deliberate contrib policy. The amalgamation is ~4 MiB (~250k lines). aether_sqlite.c:36-38 explicitly says "Bundling the 4 MiB amalgamation in contrib/ would defeat the point of having moved sqlite to contrib/" (see docs/stdlib-vs-contrib.md). This proposal reverses that decision — worth an explicit call. Mitigation: fetch-on-demand the pinned amalgamation (checksum-verified) instead of committing it, or gate vendoring behind a build flag (--with-vendored-sqlite).

  2. Compile cost. ~250k lines is ~a minute per fresh compile (miniaudio's ~96k costs ~1 min). Acceptable only because it's cached — but see caveat 3 for the cross gap.

  3. The existing miniaudio cache is NATIVE-per-runner, NOT cross-target. ci.yml is explicit: "Never shared across platforms (matrix.os/matrix.cc are in the key)." So dropping sqlite into the miniaudio pattern gets you a cached object for each native matrix build, but not for --target cross objects. To cache cross-compiled sqlite (the actual payoff here) the cache key must be extended to include the target triple (sqlite-obj-<triple>-<srchash>), and the release workflow must compile+cache each --target variant. The caching machinery exists; this is a key-scheme + workflow addition, not new infra.

  4. Feature-flag set. The zig cc invocation needs the standard -DSQLITE_THREADSAFE=…, -DSQLITE_ENABLE_FTS5, etc. threaded through (ae_cross.c already plumbs feature_defs / user_cflags), and that set becomes part of the cache key so a flag change invalidates correctly.

  5. Version pinning / provenance. Pin an exact amalgamation version + SHA-256, documented, so builds are reproducible and the fetched/vendored blob is auditable.

Suggested phasing (the two decisions are separable)

  • Phase 1 — native vendoring + cache (drop-in, mirrors miniaudio exactly): fetch/pin the amalgamation, compile the bridge against it, cache the native .o per matrix runner. Immediate: removes the -lsqlite3 system dependency for native builds.
  • Phase 2 — cross caching: extend the cache key with the target triple and have --target builds compile+reuse per-triple sqlite objects. This is what actually enables self-contained sqlite cross-compiles at low build cost.

Acceptance

ae build --target aarch64-linux prog.ae (and other triples) links a working sqlite with no target-system libsqlite3 present, and CI/release reuses a cached per-(triple, srchash) sqlite object rather than recompiling the amalgamation every build.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions