Skip to content

Commit 641b45f

Browse files
committed
build-script: opt-in build.sysroot directive in package.yml
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.
1 parent 2e18552 commit 641b45f

1 file changed

Lines changed: 75 additions & 0 deletions

File tree

lib/porcelain/build-script.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ export default async function(config: Config, PATH?: Path): Promise<string> {
1717
}
1818
const user_script = await usePantry().getScript(config.pkg, 'build', config.deps.gas, config)
1919

20+
// Sysroot routing — opt-in via `build.sysroot:` in package.yml.
21+
// When set, redirects the compiler at a specific glibc / kernel-headers
22+
// bottle (instead of the build host's libc). Lines emitted into the
23+
// generated script export CC / CXX / CPP / SYSROOT before the user
24+
// script runs, so recipes don't need to know the paths themselves.
25+
// Empty string when the yaml has no `build.sysroot` block — no
26+
// behaviour change for existing recipes.
27+
const sysroot_env = await sysroot_block(config)
28+
2029
const pkgx = find_in_PATH('pkgx')
2130
const bash = find_in_PATH('bash')
2231
const gum = find_in_PATH('gum')
@@ -71,6 +80,7 @@ export default async function(config: Config, PATH?: Path): Promise<string> {
7180
fi
7281
mkdir -p $HOME
7382
${FLAGS.join('\n ')}
83+
${sysroot_env}
7484
7585
env -u GH_TOKEN -u GITHUB_TOKEN
7686
@@ -82,6 +92,71 @@ export default async function(config: Config, PATH?: Path): Promise<string> {
8292
`
8393
}
8494

95+
/// Emit `export CC=… CXX=… CPP=… SYSROOT=…` if the package.yml has a
96+
/// `build.sysroot` block. Resolves the libc bottle from the package's
97+
/// installed deps so the recipe doesn't need to know the absolute path.
98+
///
99+
/// YAML schema:
100+
/// build:
101+
/// sysroot:
102+
/// libc: gnu.org/glibc=~2.28 # required
103+
/// kernel-headers: kernel.org/linux-headers=^7 # optional
104+
///
105+
/// The libc package must already be a build dependency of the recipe
106+
/// (so it's installed and available in config.deps); this directive
107+
/// doesn't add a dep, just routes the compiler at it. Same for the
108+
/// kernel headers when set.
109+
async function sysroot_block(config: Config): Promise<string> {
110+
const yml = await usePantry().project(config.pkg).yaml() as Record<string, unknown>
111+
const build = yml.build as Record<string, unknown> | undefined
112+
const sysroot = build?.sysroot as Record<string, unknown> | undefined
113+
if (!sysroot) return ''
114+
115+
const want_libc = sysroot.libc as string | undefined
116+
if (!want_libc) {
117+
console.warn("build.sysroot present but no libc — skipping")
118+
return ''
119+
}
120+
const libc_project = want_libc.split(/[=<>~^]/)[0]
121+
const libc_install = config.deps.gas.find(i => i.pkg.project === libc_project)
122+
if (!libc_install) {
123+
throw new Error(`build.sysroot.libc='${want_libc}' but ${libc_project} is not in the resolved deps — declare it as a build.dependencies entry`)
124+
}
125+
126+
const want_khdr = sysroot['kernel-headers'] as string | undefined
127+
const khdr_path: string | undefined = want_khdr
128+
? (() => {
129+
const proj = want_khdr.split(/[=<>~^]/)[0]
130+
const inst = config.deps.gas.find(i => i.pkg.project === proj)
131+
if (!inst) throw new Error(`build.sysroot.kernel-headers='${want_khdr}' not in resolved deps`)
132+
return inst.path.string
133+
})()
134+
: undefined
135+
136+
const ldso = (() => {
137+
switch (host().arch) {
138+
case 'x86-64': return 'ld-linux-x86-64.so.2'
139+
case 'aarch64': return 'ld-linux-aarch64.so.1'
140+
default: throw new Error(`sysroot directive unsupported on ${host().arch}`)
141+
}
142+
})()
143+
144+
const libc = libc_install.path.string
145+
const isystems = [
146+
`-isystem ${libc}/include`,
147+
khdr_path ? `-isystem ${khdr_path}/include` : null,
148+
].filter(Boolean).join(' ')
149+
const wrap = `-nostdinc ${isystems} -B ${libc}/lib -Wl,--enable-new-dtags,--dynamic-linker=${libc}/lib/${ldso},--rpath=${libc}/lib`
150+
151+
return undent`
152+
# sysroot routing (build.sysroot in package.yml)
153+
export SYSROOT=${libc}
154+
export CC="\${CC:-gcc} ${wrap}"
155+
export CXX="\${CXX:-g++} ${wrap} -nostdinc++"
156+
export CPP="\${CPP:-gcc} ${wrap} -E"
157+
`
158+
}
159+
85160
function flags(): string[] {
86161
const {platform, arch} = host()
87162
const is_linux_x86_64 = platform == 'linux' && arch == 'x86-64'

0 commit comments

Comments
 (0)