fix(gnu.org/gcc): add libc-wrapper for end-user pkgx gcc (#8423) - #13094
fix(gnu.org/gcc): add libc-wrapper for end-user pkgx gcc (#8423)#13094tannevaled wants to merge 7 commits into
Conversation
End-user `pkgx gcc test.c` on a host without distro glibc-devel / libc6-dev couldn't find stdlib.h + crt*.o. Earlier attempts via runtime.env (pkgxdev#13084) and --with-sysroot (pkgxdev#13092) failed because both poisoned gcc's own bootstrap build. This take installs a thin POSIX-sh wrapper at {{prefix}}/bin/<tool> (modeled on the bklibcvenv pattern, brewkit#348) that: 1. resolves the sibling gnu.org/glibc bottle relative to its own install path 2. exec's the real binary (moved to libexec/gcc-wrap/) with -isystem $glibc/include + -L$glibc/lib appended 3. NO-OPs when CPATH is already set (brewkit build-context — the bootstrap gcc loop uses brewkit-composed CPATH, so injecting pkgx-glibc paths twice is unnecessary and could trigger C23 symbol mismatches like __isoc23_strtoul) Pinning gnu.org/glibc <2.38 avoids C23 symbol redirects absent on older CI/host runners; the wrapper's runtime CPATH check guards against build-time poisoning regardless. Wrappers cover gcc, g++, cpp, c++, gfortran. The existing cc -> gcc and gc++ -> c++ symlinks still resolve (now via the wrapper). The wrapper dispatches by $0 basename. Pairs with pkgxdev#13083 (multi-arch triplet symlinks). Together they should fully close pkgxdev#8423. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The runtime dep on gnu.org/glibc poisoned gcc's own bootstrap build:
brewkit exposes runtime deps via CPATH/LIBRARY_PATH during build too,
so configure-time test programs end up compiling against pkgx libc
2.34 but running on the host's ld-linux — and ld-linux finds host
libc 2.35 in the default path even with LD_LIBRARY_PATH set,
producing `cannot run C++ compiled programs`.
The wrapper itself doesn't need the dep declared — it does a runtime
sibling lookup (`bindir/../../../glibc/v*`) that no-ops if no pkgx
glibc is present. Users who want the bottled libc explicitly add it:
pkgx +gnu.org/gcc +gnu.org/glibc gcc test.c
This trades automatic resolution for a clean bootstrap. Closing
this trade-off the "right" way needs a pkgx feature to separate
runtime-only deps from build-time-injected deps.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When the wrapper detects a sibling pkgx glibc bottle, it now also passes: -Wl,--dynamic-linker=$glibc/lib/ld-linux-*.so.* -Wl,-rpath,$glibc/lib so the produced binary's ELF interpreter (PT_INTERP) points at pkgx ld-linux. End-to-end consistency: the same libc is used at compile, link, and exec. Without this, the binary's PT_INTERP would be the host's /lib64/ld-linux-x86-64.so.2 and the kernel would load host ld-linux at exec time — even with -L pointing at pkgx libc and rpath set — mixing libc internal layouts across host/pkgx. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When a pkgx glibc sibling is found, wrapper now also passes -nostdinc (and -nostdinc++ for g++/c++) to drop /usr/include from gcc's search path, then re-adds: - gcc's own builtin headers (lib/gcc/<triplet>/<ver>/include[+-fixed]) - libstdc++ headers for C++ tools (include/c++/<ver>[/<triplet>]) - pkgx glibc headers via -isystem Without -nostdinc, gcc still found /usr/include/stdlib.h first because -isystem only appends to the search chain — so pkgx-glibc headers were shadowed by whatever the host had. Modeled on Nix's cc-wrapper.sh (pkgs/build-support/cc-wrapper/). See pkgxdev#8423. The wrapper stays opportunistic (CPATH unset + sibling glibc), so the gcc bootstrap loop is unaffected. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The previous shape moved the real gcc binary to libexec/gcc-wrap/,
which broke gcc's relative lookup for cc1 / cc1plus / collect2 (gcc
expects them at lib/gcc/<triplet>/<version>/ relative to its OWN
location). Result: `cannot execute 'cc1': No such file or directory`
during fixincludes at make-install time.
Keep the real binary at bin/.${tool}-real (hidden by leading dot
to avoid cluttering bin/) and have bin/<tool> point at the wrapper.
cc1 lookup then resolves the same way it always did.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
72122da to
763095d
Compare
|
should this be re-evaluated in light of the changes (thin loader shim in zig) to bklibcvenv? the shims had the problem of messing with $0, which the loader doesn't. we should also ensure we test that we've done what we expected. |
Addresses the review (jhheider): the single shared wrapper muxed on `basename "$0"`, the same argv[0]/self-location fragility that sank the explicit-wrapper approach in brewkit. Rework: - Generate ONE wrapper per tool (gcc/g++/cpp/c++/gfortran) with the target + c++-ness baked in, so it never infers WHICH compiler it is from $0. Locate self by canonicalizing $0 through symlinks (`readlink -f`, the sh analogue of /proc/self/exe). External-symlink and triplet-symlink invocations now resolve correctly. - Fix a latent bug: the pkgx glibc bottle keeps crt*.o / libc.so.6 / ld-linux* in a VERSIONED sub-libdir (lib/glibc-X.Y), not lib/. The old wrapper's `-L$libc/lib` + `ls $libc/lib/ld-linux*` silently found nothing. Locate the sub-libdir via libc.so.6; add `-B $libcdir` (crt) and `-Wl,-rpath,$gcc_root/lib` (so C++/Fortran outputs find libstdc++/libgcc_s). - Add a linux test (jhheider: "ensure we test that we've done what we expected"): with a sibling glibc bottle, gcc/g++ compile+link+RUN and the produced binary's PT_INTERP is asserted to be the bottle's ld.so; plus a negative check that CPATH-set no-ops back to host libc. Validated end-to-end on x86_64 (real hardware): C, C++ (<vector>), gfortran, triplet-symlink and external-symlink invocations all produce pkgx-glibc binaries (PT_INTERP under the glibc prefix) that run; CPATH-set and no-glibc-sibling both correctly fall back to the host loader. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Reworked per your review:
While writing the test I also caught a latent bug in the original wrapper: the pkgx glibc bottle keeps Validated end-to-end on real x86_64 hardware (C, C++ |
CI (linux-aarch64) caught two bugs the earlier x86_64 scratch validation
missed — and this is exactly why the test was worth adding:
1. pkgx installs a literal `v*` convenience symlink next to the real
vX.Y.Z bottle dir; the wrapper's `.../glibc/v*` glob matched `v*`
first (ASCII '*' sorts before digits) and logical `pwd` kept the
literal name, baking `.../glibc/v*/lib/glibc-X.Y/ld-*` (unrunnable)
into PT_INTERP. Fix: skip candidates ending in '*' and resolve the
chosen dir physically (`cd -P`/`pwd -P`, also applied to bindir/
gcc_root) so no glob/symlink residue survives into $libc.
2. The test asserted against `{{deps.gnu.org/glibc.prefix}}`, but brewkit
doesn't template `{{deps.*}}` for TEST deps. Rework the test to capture
the host loader from a CPATH-set (no-op) build and assert the wrapped
build's PT_INTERP differs from it, matches `*/glibc-*/ld-*`, has no
glob residue, exists, and runs — no template needed. The glob-residue
guard is a permanent regression check for bug #1.
Re-validated against the REAL pkgx layout (all version symlinks incl.
`v*`) on x86_64: fully-resolved physical interpreter, C/C++/triplet-/
external-symlink all run, CPATH-set no-ops to host.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Adds a thin POSIX-sh wrapper at
{{prefix}}/bin/<tool>(gcc, g++, cpp, c++, gfortran) that injects-isystem $glibc/include -L$glibc/libfor end-user invocations, without poisoning gcc's own bootstrap build. The real binaries are moved to{{prefix}}/libexec/gcc-wrap/. Addsgnu.org/glibc: '<2.38'+kernel.org/linux-headers: '*'as Linux deps so pkgx provisions a sibling glibc bottle the wrapper can find.Why this approach
Previous attempts at #8423:
runtime.env CPATH): clobbered brewkit's composed CPATH during gcc's own build → couldn't findgmp.h. Closed.--with-sysroot): configure-time test programs link against the bottled glibc's interp path which doesn't exist on the host →cannot run C++ compiled programs. Closed.This take avoids both pitfalls because the wrapper:
$0-relative lookup, not via baked-in configure flags<2.38to avoid C23 symbol redirects (__isoc23_*) absent on older runnersPattern is modeled on the bklibcvenv shim (brewkit#348).
Pairs with
Test plan
pkgx gcc -E -x c /dev/nullsucceeds without distro glibc-develglibc-devel,pkgx gcc test.c -o test && ./testsucceedspkgx +gnu.org/gcc cc -v 2>&1 | grep gnu.org/glibcconfirms the wrapper found the bottleif: linux)🤖 Generated with Claude Code