Skip to content

Commit 844faf7

Browse files
committed
build-script: opt-in linux-sysroot: directive in package.yml
Adds a top-level `linux-sysroot:` key to the package.yml schema that, when set, redirects the compiler at a specific glibc 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 (top-level + scalar form): linux-sysroot: gnu.org/glibc=~2.28 build: dependencies: gnu.org/glibc: ~2.28 # MUST also be a build dep kernel.org/linux-headers: ^7 # auto-picked-up if present When `linux-sysroot:` 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. kernel-headers is auto-detected from build.dependencies — if `kernel.org/linux-headers` is among them, its include dir is prepended. No behaviour change for recipes that don't declare `linux-sysroot:`, and no-op on non-linux hosts. ## Naming Renamed from the original `build.sysroot.libc:` (nested) form per @jhheider's review feedback on #343. Top-level + `linux-`-prefixed makes the platform context explicit at read-time. Did NOT go with "auto-detect from glibc dep presence" because some recipes have glibc in their dep closure transitively but don't want sysroot routing (e.g. Go binaries with cgo-disabled, kernel modules). Explicit > implicit here. ## Refs - #343 (this PR) - pkgxdev/pantry#12968 (motivating use case: glibc cascade) - #346 (Windows-port RFC — sibling pattern via bkwinvenv)
1 parent 47eb6dd commit 844faf7

1 file changed

Lines changed: 81 additions & 0 deletions

File tree

lib/porcelain/build-script.ts

Lines changed: 81 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+
// Linux sysroot routing — opt-in via `linux-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 `linux-sysroot:` key OR on non-
26+
// linux hosts — no behaviour change for existing recipes.
27+
const sysroot_env = await linux_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,77 @@ 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+
/// `linux-sysroot:` key. 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 (top-level key, scalar form):
100+
///
101+
/// linux-sysroot: gnu.org/glibc=~2.28
102+
/// build:
103+
/// dependencies:
104+
/// gnu.org/glibc: ~2.28 # MUST also be a build dep
105+
/// kernel.org/linux-headers: ^7 # auto-picked-up if present
106+
///
107+
/// `linux-sysroot:` points at the libc you want the compiler routed to.
108+
/// kernel-headers are auto-detected from `build.dependencies` (if
109+
/// `kernel.org/linux-headers` is among them, its include dir is
110+
/// prepended to -isystem; if not, only the libc's headers are wired).
111+
///
112+
/// The libc package MUST already be a `build.dependencies` entry (so
113+
/// it's installed and resolved); this directive routes existing deps,
114+
/// it doesn't add them.
115+
///
116+
/// No-op on non-linux hosts and on recipes without a `linux-sysroot:`
117+
/// key — no behaviour change for existing recipes.
118+
///
119+
/// Naming rationale (@jhheider review feedback on pkgxdev/brewkit#343):
120+
/// top-level + `linux-`-prefixed makes the platform context explicit at
121+
/// read-time. Alternate name `build.sysroot.libc:` (nested) was
122+
/// rejected because it didn't surface the linux-only scope.
123+
async function linux_sysroot_block(config: Config): Promise<string> {
124+
if (host().platform !== 'linux') return ''
125+
126+
const yml = await usePantry().project(config.pkg).yaml() as Record<string, unknown>
127+
const want_libc = yml['linux-sysroot'] as string | undefined
128+
if (!want_libc) return ''
129+
130+
const libc_project = want_libc.split(/[=<>~^]/)[0]
131+
const libc_install = config.deps.gas.find(i => i.pkg.project === libc_project)
132+
if (!libc_install) {
133+
throw new Error(`linux-sysroot='${want_libc}' but ${libc_project} is not in the resolved deps — declare it as a build.dependencies entry`)
134+
}
135+
136+
// Auto-detect kernel-headers from build.dependencies if present.
137+
// (No separate directive needed — if you declared the kernel-headers
138+
// bottle as a build dep, you want it on the sysroot's -isystem path.)
139+
const khdr_install = config.deps.gas.find(i => i.pkg.project === 'kernel.org/linux-headers')
140+
const khdr_path = khdr_install?.path.string
141+
142+
const ldso = (() => {
143+
switch (host().arch) {
144+
case 'x86-64': return 'ld-linux-x86-64.so.2'
145+
case 'aarch64': return 'ld-linux-aarch64.so.1'
146+
default: throw new Error(`linux-sysroot unsupported on ${host().arch}`)
147+
}
148+
})()
149+
150+
const libc = libc_install.path.string
151+
const isystems = [
152+
`-isystem ${libc}/include`,
153+
khdr_path ? `-isystem ${khdr_path}/include` : null,
154+
].filter(Boolean).join(' ')
155+
const wrap = `-nostdinc ${isystems} -B ${libc}/lib -Wl,--enable-new-dtags,--dynamic-linker=${libc}/lib/${ldso},--rpath=${libc}/lib`
156+
157+
return undent`
158+
# sysroot routing (linux-sysroot in package.yml)
159+
export SYSROOT=${libc}
160+
export CC="\${CC:-gcc} ${wrap}"
161+
export CXX="\${CXX:-g++} ${wrap} -nostdinc++"
162+
export CPP="\${CPP:-gcc} ${wrap} -E"
163+
`
164+
}
165+
85166
function flags(): string[] {
86167
const {platform, arch} = host()
87168
const is_linux_x86_64 = platform == 'linux' && arch == 'x86-64'

0 commit comments

Comments
 (0)