Skip to content

feat(ci): build and ship glibc (gnu) binaries via cross#244

Open
sksat (sksat) wants to merge 2 commits into
mainfrom
feat/cross-glibc-build
Open

feat(ci): build and ship glibc (gnu) binaries via cross#244
sksat (sksat) wants to merge 2 commits into
mainfrom
feat/cross-glibc-build

Conversation

@sksat

Copy link
Copy Markdown
Member

What

Distribute dynamically linked glibc binaries for x86_64 and aarch64 alongside the existing musl (static) ones. The musl build is unchanged.

Why / how

A new build_gnu job builds the gnu targets with cross rather than a bare cargo build. cross's *-unknown-linux-gnu images are Ubuntu 20.04 based (glibc 2.31), so the binaries stay runnable on older distros. Building natively on the ubuntu-24.04 runner would instead bake in its glibc 2.39 symbols and silently produce binaries that won't start on anything older.

Only the gnu targets go through cross; the musl build stays a native cargo build with the apt-installed musl-tools / aarch64 linker.

kble-serialport builds cleanly under cross because serialport 4.9 uses nix (sysfs) on Linux, not libudev — no extra C libs needed in the image.

TDD / guard

scripts/verify-release-binary.sh is the test that captures the intended behavior, run from the build_gnu job over every produced binary:

  1. linkage — gnu must be dynamically linked against glibc.
  2. run — smoke-runs --help on the host-arch (x86_64) binaries; structurally checks the aarch64 ones (can't execute on an x86_64 runner).
  3. glibc floor — the highest required GLIBC_x.y symbol version must be ≤ 2.31, so a non-portable build fails the release instead of shipping.

The floor discriminates exactly between a cross build (≤2.31, passes) and a native modern build (~2.39, fails). It's tunable via --glibc-floor / the script's DEFAULT_GLIBC_FLOOR if a target needs a different floor.

Validation

The actual cross build can only be exercised in CI (no local container engine). This PR touches .github/workflows/release.yml, which is in the workflow's pull_request path filter, so opening it runs build_gnu end-to-end (build → verify) on Actions.

🤖 Generated with Claude Code

@sksat sksat (sksat) self-assigned this Jun 16, 2026
Distribute dynamically linked glibc binaries for x86_64 and aarch64
alongside the existing musl (static) ones, so users on glibc systems can
take a native build.

A new `build_gnu` job builds these targets with `cross` rather than a
bare `cargo build`: cross's *-unknown-linux-gnu images are Ubuntu 20.04
based (glibc 2.31), so the binaries stay runnable on older distros.
Building natively on the ubuntu-24.04 runner would instead bake in its
glibc 2.39 symbols and silently produce binaries that won't start on
anything older. The musl build stays a native cargo build.

scripts/verify-release-binary.sh guards against that failure mode: it
asserts each binary's linkage (gnu dynamic), smoke-runs the host-arch
ones (--help), and enforces a glibc-2.31 floor on the required symbol
versions so a non-portable build fails the release rather than shipping.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sksat sksat (sksat) force-pushed the feat/cross-glibc-build branch from e39e5d5 to 902e13a Compare June 16, 2026 13:03
Add the verification step to the musl `build` job too (it was only on the
new gnu job), so the static binaries we ship are guarded the same way.
While wiring it up, found the linkage check only matched "statically
linked"; modern rustc emits static-PIE musl binaries that `file` reports
as "static-pie linked", which the check rejected. Assert the real
invariant instead — a musl binary carries no dynamic loader — so both
spellings pass while a dynamically linked binary still fails.

Also link the CHANGELOG entry to the PR.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant