Skip to content

RFC: build.sysroot directive for hermetic-build / manylinux targeting#343

Draft
tannevaled wants to merge 1 commit into
pkgxdev:mainfrom
tannevaled:feat/sysroot-directive
Draft

RFC: build.sysroot directive for hermetic-build / manylinux targeting#343
tannevaled wants to merge 1 commit into
pkgxdev:mainfrom
tannevaled:feat/sysroot-directive

Conversation

@tannevaled
Copy link
Copy Markdown

Status: draft / RFC

Proposes a small additive directive to the pantry yaml schema that unblocks the rest of the pantry to follow the same hermetic-build path we demonstrated empirically in pkgxdev/pantry#12968 (gnu.org/glibc).

The brewkit side is concrete (this PR, 75 lines). The pantry yaml schema docs need a companion update; opening as draft so the schema can be discussed first.

Problem

Today the pantry has no way to express "build me against THIS libc bottle, not the build host's libc". Every recipe inherits the runner's libc — so bottles carry the runner's ABI assumptions, can't target older manylinux baselines, can't be built on Alpine/musl hosts.

We worked around this empirically in pkgxdev/pantry#12968 by manually constructing the CC/CXX/CPP wrappers inside Docker containers (see pkgm/notes/session-2026-05-{19,20}.md). The pattern works — 18 glibc bottles (9 versions × 2 arches) all build host-independent — but it's recipe-side boilerplate that should be a schema feature.

Proposal

Add a build.sysroot: block:

build:
  sysroot:
    libc: gnu.org/glibc=~2.28                      # required
    kernel-headers: kernel.org/linux-headers=^7    # optional
  dependencies:
    gnu.org/glibc: ~2.28              # MUST also be a build dep
    kernel.org/linux-headers: ^7      # MUST also be a build dep

When build.sysroot.libc is set, the build script gains these exports BEFORE the user script runs:

export SYSROOT=<libc-install-prefix>
export CC="${CC:-gcc} -nostdinc -isystem <libc>/include [-isystem <khdr>/include] -B <libc>/lib -Wl,--enable-new-dtags,--dynamic-linker=<libc>/lib/ld-linux-*.so.*,--rpath=<libc>/lib"
export CXX="${CXX:-g++} <same> -nostdinc++"
export CPP="${CPP:-gcc} <same> -E"

Recipes that opt in get the routing automatically; recipes that don't are unchanged.

Behaviour change for existing recipes

None. The new helper returns an empty string when the yaml has no build.sysroot block. No existing pantry yml ships such a block.

Companion changes

Concrete use cases unlocked

  1. manylinux targeting: build.sysroot.libc: gnu.org/glibc=~2.17 produces a binary that runs on CentOS 7 / RHEL 7.
  2. Alpine / musl host build: with a musl bottle one day, build.sysroot.libc: musl.cc=*.
  3. HPC bootstrap cascade (feat(binutils): support older versions (2.27–2.38) for HPC bootstrap cascade pantry#12966, #12967, #12968): the gnu.org/glibc recipe for older versions becomes a single yml block instead of the manual Dockerfile dance.
  4. dist.pkgx.dev parity with Nix nixpkgs: every package gets a known glibc baseline.

Open questions

  1. Schema bikeshed: build.sysroot.libc vs build.libc? build.sysroot.libc vs separate top-level sysroot:?
  2. Should the directive imply the dep (auto-add to build.dependencies)? Current sketch requires the recipe to declare both for clarity.
  3. Should the wrapper include -D_FORTIFY_SOURCE=… or similar hardening flags by default? Currently passthrough.
  4. How does this interact with darwin? Initial implementation: host().arch switch covers x86-64 / aarch64 linux; darwin throws. Should it be a no-op on darwin instead?

Happy to iterate on the schema, split into smaller PRs, or rework as needed.

Refs

🤖 Generated with Claude Code

Adds a `build.sysroot:` block to the package.yml schema that, when
set, redirects the compiler at a specific glibc / kernel-headers
bottle (instead of using the build host's libc). This is the
hermetic-build / host-independent piece needed for the pantry to
target manylinux baselines, build on Alpine-musl hosts, and produce
bottles that don't carry the build runner's libc ABI assumptions.

YAML schema:

    build:
      sysroot:
        libc: gnu.org/glibc=~2.28               # required
        kernel-headers: kernel.org/linux-headers=^7   # optional
      dependencies:
        gnu.org/glibc: ~2.28      # MUST also be a build dep
        kernel.org/linux-headers: ^7  # MUST also be a build dep

When `build.sysroot.libc` is set, the generated build script exports:

    SYSROOT=<libc-install-prefix>
    CC="${CC:-gcc} -nostdinc -isystem <libc>/include [-isystem <khdr>/include] -B <libc>/lib -Wl,--enable-new-dtags,--dynamic-linker=<libc>/lib/ld-linux-*.so.*,--rpath=<libc>/lib"
    CXX="${CXX:-g++} <same> -nostdinc++"
    CPP="${CPP:-gcc} <same> -E"

The libc package must already be a `build.dependencies` entry (so
it's installed and resolved); this directive merely routes existing
deps, it doesn't add them. Same constraint for `kernel-headers`.

No behaviour change for recipes that don't declare `build.sysroot:` —
the helper returns an empty string and no env exports are emitted.

This is the brewkit half of the feature; the pantry yaml schema docs
need a companion update (will land in a pkgxdev/pantry PR).

Empirical motivation: pkgxdev/pantry#12968 (gnu.org/glibc) and the
HPC-cascade work in pkgm/notes/session-2026-05-{19,20}.md. We
demonstrated that this exact sysroot routing pattern (done manually
inside Docker containers) enables building glibc 2.17–2.43 host-
independent on both linux/x86-64 and linux/aarch64 from a clean
debian:bookworm-slim with no apt-installed compiler. Formalising it
in the recipe schema unblocks the rest of the pantry to follow the
same hermetic-build path.
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