diff --git a/lib/systems/default.nix b/lib/systems/default.nix index 262cb722a090..0d00ff953530 100644 --- a/lib/systems/default.nix +++ b/lib/systems/default.nix @@ -153,6 +153,16 @@ let # Derived meta-data useLLVM = final.isFreeBSD || final.isOpenBSD; + # Use the split GCC package set (`gccNGPackages`) instead of the + # monolithic `gcc`. No platform selects it yet; it is opt-in, set + # explicitly on a platform spec, so that the split set can be exercised + # before anything depends on it. + # + # I (@Ericson2314) plan on making obscure low-tier platforms (e.g. + # NetBSD) use it soon, so we can dogfood GCC NG and thereby iron out its + # bugs. + useGccNG = false; + libc = if final.isDarwin then "libSystem" diff --git a/pkgs/build-support/cc-wrapper/default.nix b/pkgs/build-support/cc-wrapper/default.nix index bf079f1b0e53..d926d85425a3 100644 --- a/pkgs/build-support/cc-wrapper/default.nix +++ b/pkgs/build-support/cc-wrapper/default.nix @@ -804,9 +804,15 @@ stdenvNoCC.mkDerivation { include -cxx-isystem "${getDev libcxx}/include/c++/v1" >> $out/nix-support/libcxx-cxxflags echo "-stdlib=libc++" >> $out/nix-support/libcxx-ldflags '' - # GCC NG friendly libc++ + # This is the GCC NG case, libstdc++ is being built as a separate package. + # + # Point at `include-cxx`, not `include`. `libcxx` is also a propagated + # target-target dep, so the generic setup hook puts its `include` on the *C* + # include path -- and libstdc++ ships headers named after C headers + # (`math.h`, `stdlib.h`, `stdckdint.h`, ...) that are only meant to shadow + # the C ones in C++. A sibling directory the setup hook ignores is enough. + optionalString (libcxx != null && libcxx.isGNU or false) '' - include -isystem "${getDev libcxx}/include" >> $out/nix-support/libcxx-cxxflags + include -isystem "${getDev libcxx}/include-cxx" >> $out/nix-support/libcxx-cxxflags '' ## diff --git a/pkgs/by-name/mu/musl/package.nix b/pkgs/by-name/mu/musl/package.nix index 3df1b2289934..3cfdae35710a 100644 --- a/pkgs/by-name/mu/musl/package.nix +++ b/pkgs/by-name/mu/musl/package.nix @@ -174,7 +174,14 @@ stdenv.mkDerivation (finalAttrs: { install -D ${tree_h} $dev/include/sys/tree.h ''; - passthru.linuxHeaders = linuxHeaders; + passthru = { + linuxHeaders = linuxHeaders; + + # musl's threads are POSIX threads. `libgcc` and `libstdc++` have to be + # configured for the same threading model as each other, so rather than + # have each guess, they take it from the libc they are built against. + threadModel = "posix"; + }; meta = { description = "Efficient, small, quality libc implementation"; diff --git a/pkgs/development/compilers/gcc/ng/15/libstdcxx/force-regular-dirs.patch b/pkgs/development/compilers/gcc/ng/15/libstdcxx/force-regular-dirs.patch index e80cffdce1e7..2812ba748e80 100644 --- a/pkgs/development/compilers/gcc/ng/15/libstdcxx/force-regular-dirs.patch +++ b/pkgs/development/compilers/gcc/ng/15/libstdcxx/force-regular-dirs.patch @@ -1,9 +1,21 @@ From db427c55334dd2edc11397d3a92d55dc9c06d1c3 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sun, 20 Jul 2025 14:20:00 -0400 -Subject: [PATCH] libstdc++: Force regular include/lib dir +Subject: [PATCH] libstdc++: Force regular lib dir, headers in include-cxx Delete a bunch of unneeded logic to do this. + +The library goes straight in $(libdir), with no version-specific or +target-specific subdirectory. + +The headers go in $(includedir)-cxx rather than $(includedir). libstdc++ +intentionally ships headers named after C headers -- `math.h`, `stdlib.h`, +`complex.h`, `stdckdint.h` and friends -- whose whole purpose is to shadow +the C ones when compiling C++. Installed into the ordinary include dir they +shadow them when compiling *C* too, because the generic setup hook puts that +directory on the C include path. A sibling directory keeps them out of C's +way while staying findable for C++; no version or target subdirectory is +needed, since the store path already separates one libstdc++ from another. --- libstdc++-v3/acinclude.m4 | 80 ++------------------------------ libstdc++-v3/include/Makefile.am | 2 +- @@ -21,7 +33,7 @@ index a0094c2dd95..a0718dff394 100644 - glibcxx_toolexeclibdir=no + glibcxx_toolexecdir='$(libdir)' + glibcxx_toolexeclibdir='$(libdir)' -+ gxx_include_dir='$(includedir)' ++ gxx_include_dir='$(includedir)-cxx' glibcxx_prefixdir=$prefix - AC_MSG_CHECKING([for gxx-include-dir]) diff --git a/pkgs/development/compilers/gcc/ng/README.md b/pkgs/development/compilers/gcc/ng/README.md index b40070caebc6..0bbde4ef5cbf 100644 --- a/pkgs/development/compilers/gcc/ng/README.md +++ b/pkgs/development/compilers/gcc/ng/README.md @@ -1,3 +1,58 @@ # GCC Next-Generation -Experimental split GCC package set based on the LLVM package set design. +Experimental split GCC package set, based on the LLVM package set design. + +The monolithic `gcc` derivation builds the compiler and every runtime library in one go, so a change to the target libc rebuilds the compiler too. +This set separates them — `gcc`, `libgcc`, `libstdcxx` and the rest are individual packages — so the compiler stops depending on the libc, and each piece can be rebuilt on its own. +Because GCC is not a multi-target compiler — a single target is baked into the binary with CPP — we still need to rebuild it more than we do LLVM, but someday that should change. + +A platform opts in with `useGccNG`, in the same way it would opt into `useLLVM`. + +## The bootstrap chain + +The libc and libgcc depend on each other: a libc's own sources call into libgcc for integer and floating-point helpers and for stack unwinding, and a libgcc that can use the libc's threads needs the libc. + +The way out we currently use is the same one the LLVM set takes with `compiler-rt-no-libc` and `compiler-rt-libc` — build the runtime twice, either side of the libc. +It is unclear whether this works in general to resolve the circularity, but we shall see. + +That gives four compilers, each one step further along: + +| compiler | libc | libgcc | used to build | +|---|---|---|---| +| `gccNoLibgcc` | headers, or nothing | — | `libgcc-no-libc` | +| `gccWithLibgcc` | headers, or nothing | `libgcc-no-libc` | the libc | +| `gccWithLibcAndBasicLibgcc` | real | `libgcc-no-libc` | `libgcc-libc` | +| `gccWithLibc` / `gcc` | real | `libgcc-libc` | everything else | + +`libgcc` resolves to `libgcc-libc` wherever a libc exists; only those first three stages ever see `libgcc-no-libc`. +The bootstrap one is single-threaded and compiled against the libc's headers at best, so it is not intended for use beyond building the libc. + +Nothing is passed down to say which stage is which. +It follows from the compiler: each package reads `stdenv.cc.libc` and needs no flag of its own. +That is also how the two `libgcc`s differ — same expression, different `stdenv`. + +## Where the pre-libc stage is written down + +`binutilsNoLibc` carries `preLibcHeaders` as its `libc`: the header-only stand-in for platforms that have one, and nothing at all for platforms that do not. +`wrapCCWith` defaults `libc` to `bintools.libc`, so the bootstrap compilers inherit it, and everything built with them reads `stdenv.cc.libc`. + +Setting a sysroot as well is unusual for nixpkgs, since headers normally reach a compiler through the wrapper, as they do here. +It is needed because `gcc/configure` takes `target_header_dir` from `--with-sysroot`, and `target_header_dir` is what decides `inhibit_libc`. +A libgcc built with `inhibit_libc` still compiles, links and installs, just with pieces silently missing. + +Given that, the sysroot is derived from `stdenv.cc.libc` too, so it cannot disagree with the headers. + +## Threading + +Which threading model is available is a property of the libc, so the libc declares it as `passthru.threadModel`; `libgcc` reads it from there, and `libstdcxx` takes both the model and the generated `gthr-default.h` from `libgcc`. + +Reading it from the compiler instead, with `$CC -v | sed -n 's/^Thread model: //p'`, reports the wrong component: in this set the compiler is configured separately from libgcc, so the two can disagree. +A platform whose libc declares nothing gets `single`. + +## Relationship to the monolithic set + +Both are packaged from the same sources and, for now, the same version: `gccNGPackages` tracks `default-gcc-version` with no fallback, so bumping the monolithic default past what is packaged here is an evaluation error rather than a silent version skew. + +Nothing selects GGN NG yet by default. +The plan is for very exotic package sets to switch to this first. +The main tier-1 native Linux package sets cached on `cache.nixos.org` will come later. diff --git a/pkgs/development/compilers/gcc/ng/common/default.nix b/pkgs/development/compilers/gcc/ng/common/default.nix index 0e040fdb1320..9929fee1e655 100644 --- a/pkgs/development/compilers/gcc/ng/common/default.nix +++ b/pkgs/development/compilers/gcc/ng/common/default.nix @@ -131,6 +131,7 @@ makeScopeWithSplicing' { "-B${targetGccPackages.libssp}/lib" "-B${targetGccPackages.libatomic}/lib" "-B${targetGccPackages.libgomp}/lib" + "-B${targetGccPackages.libstdcxx}/lib" "-B${targetGccPackages.libgfortran}/lib/" ]; }; @@ -163,10 +164,20 @@ makeScopeWithSplicing' { "-B${targetGccPackages.libssp}/lib" "-B${targetGccPackages.libatomic}/lib" "-B${targetGccPackages.libgomp}/lib" + # `libcxx` above tells cc-wrapper where the C++ *headers* are; it does + # not put the library itself on the link path for a GNU compiler. So + # every C++ link failed with `cannot find -lstdc++` until this was + # added, in the same style as the other runtime libraries. + "-B${targetGccPackages.libstdcxx}/lib" "-I${targetGccPackages.libgomp}/lib/gcc/${metadata.release_version}/include" ]; }; + # Stage 1 of the bootstrap chain; see ../README.md. + # + # No `libc` is passed: `wrapCCWith` defaults it to `bintools.libc`, and + # `binutilsNoLibc` carries `preLibcHeaders`. That is the only place the + # pre-libc stage is written down. gccNoLibgcc = wrapCCWith { cc = gccPackages.gcc-unwrapped; libcxx = null; @@ -177,10 +188,51 @@ makeScopeWithSplicing' { ]; }; - libgcc = callPackage ./libgcc { + # Built before there is a libc, and not intended for use beyond getting + # one built. Note the two differ only by `stdenv`: which stage this is + # follows from the compiler, never from an argument. + libgcc-no-libc = callPackage ./libgcc { stdenv = overrideCC stdenv buildGccPackages.gccNoLibgcc; }; + # The real one, built against the finished libc, so it can use that + # libc's threads. This is what everything above the libc gets. + libgcc-libc = callPackage ./libgcc { + stdenv = overrideCC stdenv buildGccPackages.gccWithLibcAndBasicLibgcc; + }; + + libgcc = + if stdenv.hostPlatform.libc == null then gccPackages.libgcc-no-libc else gccPackages.libgcc-libc; + + # Stage 2: libgcc available, libc not yet — what compiling a libc needs. + # `binutilsNoLibc` is what keeps the libc out, so nothing here refers to + # a libc derivation and the cycle stays broken. + gccWithLibgcc = wrapCCWith { + cc = gccPackages.gcc-unwrapped; + libcxx = null; + bintools = binutilsNoLibc; + extraPackages = [ + targetGccPackages.libgcc-no-libc + ]; + nixSupport.cc-cflags = [ + "-B${targetGccPackages.libgcc-no-libc}/lib" + ]; + }; + + # Stage 3: real libc, bootstrap libgcc still. The finished libgcc is what + # this is about to build. + gccWithLibcAndBasicLibgcc = wrapCCWith { + cc = gccPackages.gcc-unwrapped; + libcxx = null; + bintools = binutils; + extraPackages = [ + targetGccPackages.libgcc-no-libc + ]; + nixSupport.cc-cflags = [ + "-B${targetGccPackages.libgcc-no-libc}/lib" + ]; + }; + gccWithLibc = wrapCCWith { cc = gccPackages.gcc-unwrapped; libcxx = null; diff --git a/pkgs/development/compilers/gcc/ng/common/gcc/default.nix b/pkgs/development/compilers/gcc/ng/common/gcc/default.nix index 1c7a57d9a6cc..fd282acfda67 100644 --- a/pkgs/development/compilers/gcc/ng/common/gcc/default.nix +++ b/pkgs/development/compilers/gcc/ng/common/gcc/default.nix @@ -27,11 +27,18 @@ texinfo, which, gettext, + flex, + bison, + # Whether `monorepoSrc` is a VCS checkout rather than a release tarball. A + # checkout lacks the generated sources (gengtype-lex.cc and friends) that a + # tarball ships pre-built, so they have to be regenerated with flex and bison. + fromVCS ? false, getVersionFile, buildGccPackages, - targetPackages, - libc, bintools, + # Build the shared runtime libraries, and so have the driver's specs emit + # `-lgcc_s`. Derived the way the monolithic build derives it. + enableTargetShared ? stdenv.targetPlatform.hasSharedLibraries, }: let inherit (stdenv) targetPlatform hostPlatform; @@ -81,6 +88,36 @@ stdenv.mkDerivation (finalAttrs: { hash = "sha256-54/HzM+aeWq8CTkQu8Pualqc/LgRLS0+8EY8uPUsD+s="; }) + # Not upstream yet; a follow-up to the series above (drop the `/raw` to + # read them). They extend that series' `-as` preference to `PATH`, + # where we put the cross toolchain, so a cross compiler finds its tools + # the way a native one does. See below for the problems `--with-as` and + # `--with-ld` cause, and thus why we want to avoid them. + (fetchpatch { + name = "driver-factor-out-env-path-parsing.patch"; + url = "https://inbox.sourceware.org/gcc-patches/20260810065714.2215299-1-git@JohnEricson.me/raw"; + hash = "sha256-2qUUMWuyxX4mVaBPeNnHIiMl/aN7ejWM5stTSFWxD7g="; + }) + (fetchpatch { + name = "driver-search-PATH-ourselves.patch"; + url = "https://inbox.sourceware.org/gcc-patches/20260810065714.2215299-2-git@JohnEricson.me/raw"; + # The posted patch is against trunk, which spells this cast with the C++ + # operator. GCC 15 still uses the CONST_CAST macro, and the line is + # context rather than a change, so it cannot fuzz-match. Rewrite it + # here rather than keeping a forked copy of the whole patch. + postFetch = '' + substituteInPlace "$out" \ + --replace-fail 'string, const_cast (commands[i].argv),' \ + 'string, CONST_CAST (char **, commands[i].argv),' + ''; + hash = "sha256-uD8xJxQus2qyNgNDN/63WnURNuUJFDkhaXPph7g/DIk="; + }) + (fetchpatch { + name = "driver-search-PATH-machine-prefix.patch"; + url = "https://inbox.sourceware.org/gcc-patches/20260810065714.2215299-3-git@JohnEricson.me/raw"; + hash = "sha256-Q5CJpJKD11kadIKselQdHgNe26GqojpyAAmlAyHnsB0="; + }) + (getVersionFile "gcc/fix-collect2-paths.diff") ]; @@ -93,12 +130,39 @@ stdenv.mkDerivation (finalAttrs: { strictDeps = true; depsBuildBuild = [ buildPackages.stdenv.cc ]; + + # The target assembler and linker have to be *runnable here*, during + # configure. GCC probes them for capabilities, and a probe it cannot run is + # not an error -- it silently records "no". Without the target `ld` on + # `PATH`, every `gcc_cv_ld_*` probe fails that way, and the two that matter + # most are `HAVE_GAS_HIDDEN` (which needs `gcc_cv_as_hidden` *and* + # `gcc_cv_ld_hidden`) and `HAVE_LD_EH_FRAME_HDR`. + # + # Losing `HAVE_GAS_HIDDEN` is the nasty one: `-fvisibility=hidden` degrades + # into a no-op that merely warns, so every symbol stays preemptible and GCC's + # own same-translation-unit `R_X86_64_PC32` references become invalid when + # linking a shared library. + # + # The *unwrapped* bintools: `as` and `ld` proper carry no target-libc + # reference, so the decoupling this package set exists for still holds. + # + # Note this is `PATH` rather than `--with-as`/`--with-ld` on purpose. We want + # configure to *ask* the real tools what they support, and we want it to bake + # nothing else: those flags record `DEFAULT_ASSEMBLER`/`DEFAULT_LINKER` as + # absolute store paths, and the driver then runs exactly those binaries + # instead of the wrapped ones it is meant to defer to. + depsBuildTarget = [ (bintools.bintools or bintools) ]; + nativeBuildInputs = [ texinfo which gettext ] - ++ lib.optional (perl != null) perl; + ++ lib.optional (perl != null) perl + ++ lib.optionals fromVCS [ + flex + bison + ]; buildInputs = [ gmp @@ -197,7 +261,11 @@ stdenv.mkDerivation (finalAttrs: { "--disable-install-libiberty" "--disable-multilib" "--disable-nls" - "--disable-shared" + # Derived rather than forced off: the driver's specs only emit `-lgcc_s` + # for a target that has shared libraries, so hardcoding this leaves every + # throwing C++ program unlinkable even though `libgcc_s.so` is built and + # findable. Same predicate the monolithic build uses. + (lib.enableFeature enableTargetShared "shared") "--enable-default-pie" "--enable-languages=${ lib.concatStrings ( @@ -218,12 +286,26 @@ stdenv.mkDerivation (finalAttrs: { "--without-headers" "--with-gnu-as" "--with-gnu-ld" - "--with-as=${lib.getExe' bintools "${bintools.targetPrefix}as"}" + # Deliberately no `--with-as` / `--with-ld`. Those bake + # `DEFAULT_ASSEMBLER` and `DEFAULT_LINKER` -- absolute store paths -- into + # the compiler, so the driver runs exactly those binaries and stops + # deferring to the wrapped tools it is meant to use. + # + # Nothing has to be baked. At configure time the tools are on `PATH` via + # `depsBuildTarget`, which is what the capability probes need; at use time + # the driver finds them on `PATH` under their target-prefixed names, via + # the `find_a_program` patches above. "--with-system-zlib" "--without-included-gettext" "--enable-linker-build-id" - "--with-sysroot=${lib.getDev (targetPackages.libc or libc)}" - "--with-native-system-header-dir=/include" + # Deliberately *no* `--with-sysroot` / `--with-native-system-header-dir` + # pointing at the target libc. Baking a libc store path into the compiler + # makes every libc change rebuild the compiler, which is precisely the + # coupling this split package set exists to remove. cc-wrapper already + # supplies the target libc (`-idirafter /include` and the + # corresponding `-B`/`-L` flags), so the compiler proper does not need to + # know about it -- exactly as in the LLVM package set, where `clang` + # likewise carries no libc reference (`--without-headers` above). ] ++ lib.optionals enablePlugin [ "--enable-plugin" @@ -236,6 +318,20 @@ stdenv.mkDerivation (finalAttrs: { "--with-multilib-list=" ]; + # `LIMITS_H_TEST` decides whether gcc's generated `syslimits.h` chains to the + # target libc's `limits.h` (`#include_next`) or is emitted self-contained. It + # defaults to a `[ -f $(BUILD_SYSTEM_HEADER_DIR)/limits.h ]` probe, which + # necessarily fails here: we deliberately do not point the compiler at a + # sysroot (see `configureFlags`), so there is no libc for it to find at build + # time. + # + # Self-contained is the wrong answer regardless. Every target in this package + # set is hosted, and cc-wrapper always supplies a libc, so the chained header + # is what resolves correctly at *use* time. Without it, anything the libc's + # `limits.h` defines and gcc's does not -- `PATH_MAX` being the common one -- + # goes missing from every libgcc source that needs it. + makeFlags = [ "LIMITS_H_TEST=true" ]; + doCheck = false; postInstall = '' diff --git a/pkgs/development/compilers/gcc/ng/common/libatomic/default.nix b/pkgs/development/compilers/gcc/ng/common/libatomic/default.nix index db4261a186cc..ce51cf4dd520 100644 --- a/pkgs/development/compilers/gcc/ng/common/libatomic/default.nix +++ b/pkgs/development/compilers/gcc/ng/common/libatomic/default.nix @@ -14,29 +14,34 @@ stdenv.mkDerivation (finalAttrs: { pname = "libatomic"; inherit version; - src = runCommand "libatomic-src-${version}" { src = monorepoSrc; } '' - runPhase unpackPhase + src = runCommand "libatomic-src-${version}" { src = monorepoSrc; } ( + '' + runPhase unpackPhase - mkdir -p "$out/gcc" - cp gcc/BASE-VER "$out/gcc" - cp gcc/DATESTAMP "$out/gcc" + mkdir -p "$out/gcc" + cp gcc/BASE-VER "$out/gcc" + cp gcc/DATESTAMP "$out/gcc" - cp -r libatomic "$out" + cp -r libatomic "$out" - cp -r config "$out" - cp -r multilib.am "$out" - cp -r libtool.m4 "$out" + cp -r config "$out" + cp -r multilib.am "$out" + cp -r libtool.m4 "$out" - cp config.guess "$out" - cp config.rpath "$out" - cp config.sub "$out" - cp config-ml.in "$out" - cp ltmain.sh "$out" - cp install-sh "$out" - cp mkinstalldirs "$out" + cp config.guess "$out" + cp config.rpath "$out" + cp config.sub "$out" + cp config-ml.in "$out" + cp ltmain.sh "$out" + cp install-sh "$out" + cp mkinstalldirs "$out" - [[ -f MD5SUMS ]]; cp MD5SUMS "$out" - ''; + '' + # `MD5SUMS` exists only in release tarballs, not in a VCS checkout. + + '' + if [[ -f MD5SUMS ]]; then cp MD5SUMS "$out"; fi + '' + ); patches = [ (fetchpatch { diff --git a/pkgs/development/compilers/gcc/ng/common/libbacktrace/default.nix b/pkgs/development/compilers/gcc/ng/common/libbacktrace/default.nix index 307a7061bc57..75cf777746a7 100644 --- a/pkgs/development/compilers/gcc/ng/common/libbacktrace/default.nix +++ b/pkgs/development/compilers/gcc/ng/common/libbacktrace/default.nix @@ -11,28 +11,33 @@ stdenv.mkDerivation (finalAttrs: { pname = "libbacktrace"; inherit version; - src = runCommand "libbacktrace-src-${version}" { src = monorepoSrc; } '' - runPhase unpackPhase + src = runCommand "libbacktrace-src-${version}" { src = monorepoSrc; } ( + '' + runPhase unpackPhase - mkdir -p "$out/gcc" - cp gcc/BASE-VER "$out/gcc" - cp gcc/DATESTAMP "$out/gcc" + mkdir -p "$out/gcc" + cp gcc/BASE-VER "$out/gcc" + cp gcc/DATESTAMP "$out/gcc" - cp -r include "$out" - cp -r libbacktrace "$out" + cp -r include "$out" + cp -r libbacktrace "$out" - cp config.guess "$out" - cp config.rpath "$out" - cp config.sub "$out" - cp config-ml.in "$out" - cp ltmain.sh "$out" - cp install-sh "$out" - cp move-if-change "$out" - cp mkinstalldirs "$out" - cp test-driver "$out" + cp config.guess "$out" + cp config.rpath "$out" + cp config.sub "$out" + cp config-ml.in "$out" + cp ltmain.sh "$out" + cp install-sh "$out" + cp move-if-change "$out" + cp mkinstalldirs "$out" + cp test-driver "$out" - [[ -f MD5SUMS ]]; cp MD5SUMS "$out" - ''; + '' + # `MD5SUMS` exists only in release tarballs, not in a VCS checkout. + + '' + if [[ -f MD5SUMS ]]; then cp MD5SUMS "$out"; fi + '' + ); outputs = [ "out" diff --git a/pkgs/development/compilers/gcc/ng/common/libgcc/default.nix b/pkgs/development/compilers/gcc/ng/common/libgcc/default.nix index d88c2e6c0ae5..29220191a26d 100644 --- a/pkgs/development/compilers/gcc/ng/common/libgcc/default.nix +++ b/pkgs/development/compilers/gcc/ng/common/libgcc/default.nix @@ -12,9 +12,37 @@ buildPackages, which, python3, + # Build `libgcc_s` as well as `libgcc.a`, deriving it the way the monolithic + # build does rather than forcing it off. + enableShared ? stdenv.hostPlatform.hasSharedLibraries, }: +let + # A libc needs libgcc to build, and a libgcc that can use the libc's threads + # needs the libc, so this package is instantiated twice — see + # `libgcc-no-libc` and `libgcc-libc` in the package set, the same split the + # LLVM package set makes with `compiler-rt-no-libc` and `compiler-rt-libc`. + # + # Nothing here says which of the two it is. The difference is entirely in the + # compiler it is handed: the bootstrap wrapper's `libc` is `preLibcHeaders` + # (or nothing at all, on platforms without one), the later wrapper's is the + # finished libc. Everything below reads that one value. + libc = stdenv.cc.libc or null; + + # Which threading model may be used is decided by the libc, so take it from + # there rather than guess — and pass it on in `passthru` so that `libstdcxx`, + # which has to agree, reads the same answer instead of probing for its own. + # + # This is what makes the bootstrap build single-threaded without being told + # to be: a headers-only package declares no `threadModel`, and a real libc + # does. That build exists only to get the libc built and is thrown away + # afterwards, so there is nothing to be gained from threading it, and plenty + # to go wrong: `gthr-posix.h` includes `` unconditionally, which + # at that point is either absent or a stand-in for a libc that does not exist + # yet. + threadModel = if libc == null then "single" else libc.threadModel or "single"; +in stdenv.mkDerivation (finalAttrs: { - pname = "libgcc"; + pname = "libgcc" + lib.optionalString (libc == null) "-no-libc"; inherit version; src = monorepoSrc; @@ -83,9 +111,37 @@ stdenv.mkDerivation (finalAttrs: { buildRoot=$(readlink -e "./build") ''; - postPatch = '' - sourceRoot=$(readlink -e "./libgcc") - ''; + postPatch = + # `SHLIB_LC` defaults to `-lc`, so the `libgcc_s.so` rule cannot link + # before libc exists. The monolithic build clobbers it for exactly this + # reason; do the same rather than giving up on shared libgcc. Runs while + # the working directory is still the monorepo root, before `sourceRoot` + # is repointed below. + lib.optionalString enableShared ( + import ../../../common/libgcc-buildstuff.nix { inherit lib stdenv; } + ) + # gcc's installed `limits.h` chains to the target libc's with + # `#include_next`. Where the compiler has a libc — headers-only or real — + # that resolves, and it has to: some targets build libgcc sources that need + # what only the libc header defines. Where it does not, the chain has + # nowhere to land, and + # even configure's `AC_PROG_CPP` probe fails -- it includes `` + # precisely because that "exists even on freestanding compilers" -- after + # which configure falls back to `/lib/cpp` and reports that as the error. + # + # Only in that case, put gcc's own `glimits.h` earlier on the include path + # for this build. That is the self-contained variant, the same file gcc + # installs when configured against no libc, so nothing is invented here. + # The compiler keeps shipping the chained header either way, which is what + # has to stay correct for everything compiled against a real libc later. + + lib.optionalString (libc == null) '' + mkdir -p "$NIX_BUILD_TOP/freestanding-include" + cp gcc/glimits.h "$NIX_BUILD_TOP/freestanding-include/limits.h" + export NIX_CFLAGS_COMPILE="-isystem $NIX_BUILD_TOP/freestanding-include ''${NIX_CFLAGS_COMPILE-}" + '' + + '' + sourceRoot=$(readlink -e "./libgcc") + ''; enableParallelBuilding = true; @@ -100,6 +156,35 @@ stdenv.mkDerivation (finalAttrs: { cd "$buildRoot/gcc" ( + '' + # `AS`, `CC`, `CPP` and `LD` still name the *target* tools at this point, + # under their machine-prefixed names. Snapshot them before the + # `*_FOR_BUILD` assignments below overwrite them. + # + # Deriving `AS_FOR_TARGET` from `$AS` *after* `AS=$AS_FOR_BUILD` asked for + # the basename of the build assembler -- plain `as` -- inside the target + # compiler's `bin`, and a cross wrapper installs only prefixed names, so + # the path did not exist. Likewise `ld`. + # + # That is not an error `gcc/configure` reports. It probes the target + # assembler and linker for capabilities, and a probe it cannot run simply + # records "no"; with neither tool found, *every* `gcc_cv_as_*`/`gcc_cv_ld_*` + # answer came back "no". The one that matters here is + # `HAVE_LD_EH_FRAME_HDR`: `unwind-dw2-fde-dip.c` gates `USE_PT_GNU_EH_FRAME` + # on it, so without it the unwinder is compiled with no `dl_iterate_phdr` + # lookup at all -- only the `__register_frame` registry, which nothing + # populates for normally linked objects. libgcc and libstdc++ then build, + # link and install perfectly cleanly, and every C++ `throw` finds no FDE + # and calls `std::terminate`. + # + # `CPP` in particular is not always exported, so fall back to the + # machine-prefixed name the wrappers install. + + '' + targetAs=$(basename "''${AS:-${stdenv.hostPlatform.config}-as}") + targetCc=$(basename "''${CC:-${stdenv.hostPlatform.config}-cc}") + targetCpp=$(basename "''${CPP:-${stdenv.hostPlatform.config}-cpp}") + targetLd=$(basename "''${LD:-${stdenv.hostPlatform.config}-ld}") + export AS_FOR_BUILD=${lib.getExe' buildPackages.stdenv.cc "$(basename $AS_FOR_BUILD)"} export CC_FOR_BUILD=${lib.getExe' buildPackages.stdenv.cc "$(basename $CC_FOR_BUILD)"} export CPP_FOR_BUILD=${lib.getExe' buildPackages.stdenv.cc "$(basename $CPP_FOR_BUILD)"} @@ -112,10 +197,10 @@ stdenv.mkDerivation (finalAttrs: { export CXX=$CXX_FOR_BUILD export LD=$LD_FOR_BUILD - export AS_FOR_TARGET=${lib.getExe' stdenv.cc "$(basename $AS)"} - export CC_FOR_TARGET=${lib.getExe' stdenv.cc "$(basename $CC)"} - export CPP_FOR_TARGET=${lib.getExe' stdenv.cc "$(basename $CPP)"} - export LD_FOR_TARGET=${lib.getExe' stdenv.cc.bintools "$(basename $LD)"} + export AS_FOR_TARGET=${lib.getExe' stdenv.cc "$targetAs"} + export CC_FOR_TARGET=${lib.getExe' stdenv.cc "$targetCc"} + export CPP_FOR_TARGET=${lib.getExe' stdenv.cc "$targetCpp"} + export LD_FOR_TARGET=${lib.getExe' stdenv.cc.bintools "$targetLd"} export NIX_CFLAGS_COMPILE_FOR_BUILD+=' -DGENERATOR_FILE=1' @@ -184,6 +269,21 @@ stdenv.mkDerivation (finalAttrs: { "--with-system-zlib" ] + # `gcc/configure` sets `inhibit_libc=true` when host != target and + # `$target_header_dir/stdio.h` does not exist. `inhibit_libc` makes + # `tsystem.h` skip and friends, which is fine for the generic + # sources but breaks the target-specific ones that genuinely need libc + # declarations -- the profiling support files are the usual casualty. + # + # `target_header_dir` is derived from `--with-sysroot`, *not* from + # `--with-headers`, so the sysroot pair is what has to be set. Point it at + # whichever libc the compiler carries, which in the bootstrap build is the + # headers-only one. This affects only the `gcc/configure` run that generates + # libgcc's makefile fragments, not the compiler that gets shipped. + ++ lib.optionals (libc != null) [ + "--with-sysroot=${lib.getDev libc}" + "--with-native-system-header-dir=/include" + ] ++ lib.optional (!stdenv.hostPlatform.isRiscV) # RISC-V does not like it being empty @@ -202,13 +302,14 @@ stdenv.mkDerivation (finalAttrs: { configureFlags = [ "--disable-dependency-tracking" - "gcc_cv_target_thread_file=single" + "gcc_cv_target_thread_file=${threadModel}" # $CC cannot link binaries, let alone run then "cross_compiling=true" - # Do not have dynamic linker without libc "--enable-static" - "--disable-shared" - ]; + ] + # `libgcc_s` needs no libc: it comes out with an empty `DT_NEEDED`, which is + # why the monolithic build ships one even from its nolibc stage. + ++ lib.optional (!enableShared) "--disable-shared"; # Set the variable back the way it was, see corresponding code in # `preConfigure`. @@ -226,6 +327,7 @@ stdenv.mkDerivation (finalAttrs: { passthru = { isGNU = true; + inherit threadModel; }; meta = gcc_meta // { diff --git a/pkgs/development/compilers/gcc/ng/common/libgomp/default.nix b/pkgs/development/compilers/gcc/ng/common/libgomp/default.nix index 9053e88a80f9..19b74a3da0d9 100644 --- a/pkgs/development/compilers/gcc/ng/common/libgomp/default.nix +++ b/pkgs/development/compilers/gcc/ng/common/libgomp/default.nix @@ -13,30 +13,35 @@ stdenv.mkDerivation (finalAttrs: { pname = "libgomp"; inherit version; - src = runCommand "libgomp-src-${version}" { src = monorepoSrc; } '' - runPhase unpackPhase + src = runCommand "libgomp-src-${version}" { src = monorepoSrc; } ( + '' + runPhase unpackPhase - mkdir -p "$out/gcc" - cp gcc/BASE-VER "$out/gcc" - cp gcc/DATESTAMP "$out/gcc" + mkdir -p "$out/gcc" + cp gcc/BASE-VER "$out/gcc" + cp gcc/DATESTAMP "$out/gcc" - cp -r libgomp "$out" - cp -r include "$out" + cp -r libgomp "$out" + cp -r include "$out" - cp -r config "$out" - cp -r multilib.am "$out" - cp -r libtool.m4 "$out" + cp -r config "$out" + cp -r multilib.am "$out" + cp -r libtool.m4 "$out" - cp config.guess "$out" - cp config.rpath "$out" - cp config.sub "$out" - cp config-ml.in "$out" - cp ltmain.sh "$out" - cp install-sh "$out" - cp mkinstalldirs "$out" + cp config.guess "$out" + cp config.rpath "$out" + cp config.sub "$out" + cp config-ml.in "$out" + cp ltmain.sh "$out" + cp install-sh "$out" + cp mkinstalldirs "$out" - [[ -f MD5SUMS ]]; cp MD5SUMS "$out" - ''; + '' + # `MD5SUMS` exists only in release tarballs, not in a VCS checkout. + + '' + if [[ -f MD5SUMS ]]; then cp MD5SUMS "$out"; fi + '' + ); outputs = [ "out" diff --git a/pkgs/development/compilers/gcc/ng/common/libiberty/default.nix b/pkgs/development/compilers/gcc/ng/common/libiberty/default.nix index a83d9580dabc..28a099b9f9fb 100644 --- a/pkgs/development/compilers/gcc/ng/common/libiberty/default.nix +++ b/pkgs/development/compilers/gcc/ng/common/libiberty/default.nix @@ -11,26 +11,31 @@ stdenv.mkDerivation (finalAttrs: { pname = "libiberty"; inherit version; - src = runCommand "libiberty-src-${version}" { src = monorepoSrc; } '' - runPhase unpackPhase + src = runCommand "libiberty-src-${version}" { src = monorepoSrc; } ( + '' + runPhase unpackPhase - mkdir -p "$out/gcc" - cp gcc/BASE-VER "$out/gcc" - cp gcc/DATESTAMP "$out/gcc" + mkdir -p "$out/gcc" + cp gcc/BASE-VER "$out/gcc" + cp gcc/DATESTAMP "$out/gcc" - cp -r include "$out" - cp -r libiberty "$out" + cp -r include "$out" + cp -r libiberty "$out" - cp config.guess "$out" - cp config.rpath "$out" - cp config.sub "$out" - cp config-ml.in "$out" - cp ltmain.sh "$out" - cp install-sh "$out" - cp mkinstalldirs "$out" + cp config.guess "$out" + cp config.rpath "$out" + cp config.sub "$out" + cp config-ml.in "$out" + cp ltmain.sh "$out" + cp install-sh "$out" + cp mkinstalldirs "$out" - [[ -f MD5SUMS ]]; cp MD5SUMS "$out" - ''; + '' + # `MD5SUMS` exists only in release tarballs, not in a VCS checkout. + + '' + if [[ -f MD5SUMS ]]; then cp MD5SUMS "$out"; fi + '' + ); outputs = [ "out" diff --git a/pkgs/development/compilers/gcc/ng/common/libquadmath/default.nix b/pkgs/development/compilers/gcc/ng/common/libquadmath/default.nix index 982b4177b749..41e309459027 100644 --- a/pkgs/development/compilers/gcc/ng/common/libquadmath/default.nix +++ b/pkgs/development/compilers/gcc/ng/common/libquadmath/default.nix @@ -11,25 +11,30 @@ stdenv.mkDerivation (finalAttrs: { pname = "libquadmath"; inherit version; - src = runCommand "libquadmath-src-${version}" { src = monorepoSrc; } '' - runPhase unpackPhase + src = runCommand "libquadmath-src-${version}" { src = monorepoSrc; } ( + '' + runPhase unpackPhase - mkdir -p "$out/gcc" - cp gcc/BASE-VER "$out/gcc" - cp gcc/DATESTAMP "$out/gcc" + mkdir -p "$out/gcc" + cp gcc/BASE-VER "$out/gcc" + cp gcc/DATESTAMP "$out/gcc" - cp -r libquadmath "$out" + cp -r libquadmath "$out" - cp config.guess "$out" - cp config.rpath "$out" - cp config.sub "$out" - cp config-ml.in "$out" - cp ltmain.sh "$out" - cp install-sh "$out" - cp mkinstalldirs "$out" + cp config.guess "$out" + cp config.rpath "$out" + cp config.sub "$out" + cp config-ml.in "$out" + cp ltmain.sh "$out" + cp install-sh "$out" + cp mkinstalldirs "$out" - [[ -f MD5SUMS ]]; cp MD5SUMS "$out" - ''; + '' + # `MD5SUMS` exists only in release tarballs, not in a VCS checkout. + + '' + if [[ -f MD5SUMS ]]; then cp MD5SUMS "$out"; fi + '' + ); sourceRoot = "${finalAttrs.src.name}/libquadmath"; diff --git a/pkgs/development/compilers/gcc/ng/common/libsanitizer/default.nix b/pkgs/development/compilers/gcc/ng/common/libsanitizer/default.nix index b42736d122bd..c90ed42e5848 100644 --- a/pkgs/development/compilers/gcc/ng/common/libsanitizer/default.nix +++ b/pkgs/development/compilers/gcc/ng/common/libsanitizer/default.nix @@ -12,25 +12,30 @@ stdenv.mkDerivation (finalAttrs: { pname = "libsanitizer"; inherit version; - src = runCommand "libsanitizer-src-${version}" { src = monorepoSrc; } '' - runPhase unpackPhase + src = runCommand "libsanitizer-src-${version}" { src = monorepoSrc; } ( + '' + runPhase unpackPhase - mkdir -p "$out/gcc" - cp gcc/BASE-VER "$out/gcc" - cp gcc/DATESTAMP "$out/gcc" + mkdir -p "$out/gcc" + cp gcc/BASE-VER "$out/gcc" + cp gcc/DATESTAMP "$out/gcc" - cp -r libsanitizer "$out" + cp -r libsanitizer "$out" - cp config.guess "$out" - cp config.rpath "$out" - cp config.sub "$out" - cp config-ml.in "$out" - cp ltmain.sh "$out" - cp install-sh "$out" - cp mkinstalldirs "$out" + cp config.guess "$out" + cp config.rpath "$out" + cp config.sub "$out" + cp config-ml.in "$out" + cp ltmain.sh "$out" + cp install-sh "$out" + cp mkinstalldirs "$out" - [[ -f MD5SUMS ]]; cp MD5SUMS "$out" - ''; + '' + # `MD5SUMS` exists only in release tarballs, not in a VCS checkout. + + '' + if [[ -f MD5SUMS ]]; then cp MD5SUMS "$out"; fi + '' + ); sourceRoot = "${finalAttrs.src.name}/libsanitizer"; diff --git a/pkgs/development/compilers/gcc/ng/common/libssp/default.nix b/pkgs/development/compilers/gcc/ng/common/libssp/default.nix index bcc5d4b9c2e3..45840d75f3bb 100644 --- a/pkgs/development/compilers/gcc/ng/common/libssp/default.nix +++ b/pkgs/development/compilers/gcc/ng/common/libssp/default.nix @@ -14,28 +14,33 @@ stdenv.mkDerivation (finalAttrs: { pname = "libssp"; inherit version; - src = runCommand "libssp-src-${version}" { src = monorepoSrc; } '' - runPhase unpackPhase + src = runCommand "libssp-src-${version}" { src = monorepoSrc; } ( + '' + runPhase unpackPhase - mkdir -p "$out/gcc" - cp gcc/BASE-VER "$out/gcc" - cp gcc/DATESTAMP "$out/gcc" + mkdir -p "$out/gcc" + cp gcc/BASE-VER "$out/gcc" + cp gcc/DATESTAMP "$out/gcc" - cp -r libssp "$out" + cp -r libssp "$out" - cp -r config "$out" - cp -r multilib.am "$out" + cp -r config "$out" + cp -r multilib.am "$out" - cp config.guess "$out" - cp config.rpath "$out" - cp config.sub "$out" - cp config-ml.in "$out" - cp ltmain.sh "$out" - cp install-sh "$out" - cp mkinstalldirs "$out" + cp config.guess "$out" + cp config.rpath "$out" + cp config.sub "$out" + cp config-ml.in "$out" + cp ltmain.sh "$out" + cp install-sh "$out" + cp mkinstalldirs "$out" - [[ -f MD5SUMS ]]; cp MD5SUMS "$out" - ''; + '' + # `MD5SUMS` exists only in release tarballs, not in a VCS checkout. + + '' + if [[ -f MD5SUMS ]]; then cp MD5SUMS "$out"; fi + '' + ); outputs = [ "out" diff --git a/pkgs/development/compilers/gcc/ng/common/libstdcxx/default.nix b/pkgs/development/compilers/gcc/ng/common/libstdcxx/default.nix index 8e43df2ab635..08865b5ff519 100644 --- a/pkgs/development/compilers/gcc/ng/common/libstdcxx/default.nix +++ b/pkgs/development/compilers/gcc/ng/common/libstdcxx/default.nix @@ -10,41 +10,55 @@ autoreconfHook269, runCommand, gettext, + libgcc, }: stdenv.mkDerivation (finalAttrs: { pname = "libstdcxx"; inherit version; - src = runCommand "libstdcxx-src-${version}" { src = monorepoSrc; } '' - runPhase unpackPhase + src = runCommand "libstdcxx-src-${version}" { src = monorepoSrc; } ( + '' + runPhase unpackPhase - mkdir -p "$out/gcc" - cp gcc/BASE-VER "$out/gcc" - cp gcc/DATESTAMP "$out/gcc" + mkdir -p "$out/gcc" + cp gcc/BASE-VER "$out/gcc" + cp gcc/DATESTAMP "$out/gcc" - mkdir -p "$out/libgcc" - cp libgcc/gthr*.h "$out/libgcc" - cp libgcc/unwind-pe.h "$out/libgcc" + mkdir -p "$out/libgcc" + cp libgcc/gthr*.h "$out/libgcc" + cp libgcc/unwind-pe.h "$out/libgcc" - cp -r libstdc++-v3 "$out" + cp -r libstdc++-v3 "$out" - cp -r libiberty "$out" - cp -r include "$out" - cp -r contrib "$out" + cp -r libiberty "$out" + cp -r include "$out" + cp -r contrib "$out" - cp -r config "$out" - cp -r multilib.am "$out" + cp -r config "$out" + cp -r multilib.am "$out" - cp config.guess "$out" - cp config.rpath "$out" - cp config.sub "$out" - cp config-ml.in "$out" - cp ltmain.sh "$out" - cp install-sh "$out" - cp mkinstalldirs "$out" + cp config.guess "$out" + cp config.rpath "$out" + cp config.sub "$out" + cp config-ml.in "$out" + cp ltmain.sh "$out" + cp install-sh "$out" + cp mkinstalldirs "$out" - [[ -f MD5SUMS ]]; cp MD5SUMS "$out" - ''; + '' + # `src/Makefile` runs this to compute `LTLDFLAGS`, reaching out of the + # subdirectory for it as `$(top_srcdir)/../libtool-ldflags`. Missing, the + # shell says "No such file or directory" and `LTLDFLAGS` silently comes out + # empty, dropping our `LDFLAGS` from every library link. + + '' + cp libtool-ldflags "$out" + + '' + # `MD5SUMS` exists only in release tarballs, not in a VCS checkout. + + '' + if [[ -f MD5SUMS ]]; then cp MD5SUMS "$out"; fi + '' + ); outputs = [ "out" @@ -85,6 +99,59 @@ stdenv.mkDerivation (finalAttrs: { cd "$buildRoot" configureScript=$sourceRoot/configure chmod +x "$configureScript" + + '' + # Build libstdc++ with the *C* driver, not `g++`. `g++` implies `-lstdc++`, + # which cannot be satisfied while building the very library that provides + # it, and the link fails with `cannot find -lstdc++`. + # + # This is not our invention; it is what a monolithic build does, and + # `libstdc++-v3/src/Makefile.am` says so where it defines `CXXLINK`: + # + # We cannot allow g++ to be used since this would add -lstdc++ to the + # link line which of course is problematic at this point. So, we get + # the top-level directory to configure libstdc++-v3 to use gcc as the + # C++ compilation driver. + # + # The top level does that through `RAW_CXX_FOR_TARGET`, which is `xgcc` + # -- not `xg++` -- plus `-shared-libgcc` and `-nostdinc++`. Building + # standalone there is no top level to arrange it, so arrange it here. The + # C driver still compiles `.cc` as C++ by extension; what it drops is the + # implicit `-lstdc++`. `-shared-libgcc` puts back the linkage `g++` would + # have chosen, which the C driver does not default to, and `-nostdinc++` + # keeps any already-installed C++ headers out of a build whose whole + # purpose is to produce them. + + '' + cxxForLibstdcxx="$CC -shared-libgcc -nostdinc++" + + '' + # Put libgcc's `gthr-default.h` where libstdc++ expects to find it. + # + # It is not a source file. In a monolithic build libgcc's Makefile creates + # it by copying whichever `gthr-.h` matches the target's thread + # model, and libstdc++ -- configured inside that same build tree -- picks + # it up. Standalone there is no such tree, so take the one `libgcc` + # installed. The two implement a single threading model between them, and + # copying libgcc's own answer is what makes them agree by construction + # rather than by two independent guesses that can drift apart. `libgcc` + # decided it from the libc; `gcc_cv_target_thread_file` below repeats the + # same value. + # + # The consequence of getting this wrong is worth spelling out, because the + # installed headers do not show it. `GLIBCXX_CHECK_GTHREADS` compiles + # `#include "gthr.h"` and requires `__GTHREADS_CXX0X`, which + # `gthr-posix.h` defines unconditionally. With no posix `gthr-default.h` + # on the include path that test fails, configure concludes there are no + # gthreads, and `_GLIBCXX_HAS_GTHREADS` is left undefined in + # `c++config.h` -- so `` compiles away and `std::mutex` does not + # exist. Stating the intent with `--enable-threads=posix` does not help: + # the probe overrides intent with an empirical answer, so the header has + # to actually be there. + + '' + cp ${lib.getDev libgcc}/include/gthr-default.h "$sourceRoot/../libgcc/gthr-default.h" + + export CXX="$cxxForLibstdcxx" + echo "libstdcxx: building with CXX=$CXX" ''; configurePlatforms = [ @@ -94,11 +161,19 @@ stdenv.mkDerivation (finalAttrs: { configureFlags = [ "--disable-dependency-tracking" - "gcc_cv_target_thread_file=posix" + # The same answer `libgcc` used, and the model of the `gthr-default.h` + # copied in above. A mismatch here is silent: the unwinder's locks vanish + # while `libstdc++` still hands out `std::thread`. + "gcc_cv_target_thread_file=${libgcc.threadModel}" "cross_compiling=true" "--disable-multilib" - "--enable-clocale=gnu" + # `gnu` is the glibc locale model: `config/locale/gnu/ctype_members.cc` + # reads `__ctype_b`, which only glibc has. Forcing it everywhere breaks any + # other libc -- on musl the build fails converting `const unsigned short *` + # to `const ctype_base::mask *`. Configure picks the right model from the + # host triple on its own, as it does for the monolithic build, which passes + # no `--enable-clocale` at all. "--disable-libstdcxx-pch" "--disable-vtable-verify" "--enable-libstdcxx-visibility" diff --git a/pkgs/development/libraries/glibc/common.nix b/pkgs/development/libraries/glibc/common.nix index 5e391970a271..6ce41321494e 100644 --- a/pkgs/development/libraries/glibc/common.nix +++ b/pkgs/development/libraries/glibc/common.nix @@ -278,6 +278,11 @@ stdenv.mkDerivation ( passthru = { inherit version; minorRelease = version; + + # glibc's threads are POSIX threads. `libgcc` and `libstdc++` have to be + # configured for the same threading model as each other, so rather than + # have each guess, they take it from the libc they are built against. + threadModel = "posix"; }; } diff --git a/pkgs/os-specific/bsd/netbsd/pkgs/libc.nix b/pkgs/os-specific/bsd/netbsd/pkgs/libc.nix index e23ba1a4fd36..a67bc775f92e 100644 --- a/pkgs/os-specific/bsd/netbsd/pkgs/libc.nix +++ b/pkgs/os-specific/bsd/netbsd/pkgs/libc.nix @@ -47,5 +47,11 @@ symlinkJoin { fixupPhase ''; + # NetBSD's threads are POSIX threads — `libpthread` is joined in above. + # `libgcc` and `libstdc++` have to be configured for the same threading model + # as each other, so rather than have each guess, they take it from the libc + # they are built against. + passthru.threadModel = "posix"; + meta.platforms = lib.platforms.netbsd; } diff --git a/pkgs/stdenv/cross/default.nix b/pkgs/stdenv/cross/default.nix index f2adc91e2bb1..98d93014faf2 100644 --- a/pkgs/stdenv/cross/default.nix +++ b/pkgs/stdenv/cross/default.nix @@ -127,6 +127,8 @@ lib.init bootStages buildPackages.zig.cc else if crossSystem.useArocc or false then buildPackages.arocc + else if crossSystem.useGccNG or false then + buildPackages.gccNGPackages.gcc else buildPackages.gcc; diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 57f5bbbe422b..479ef887d380 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -88,6 +88,8 @@ with pkgs; ( if stdenvNoCC.hostPlatform.isDarwin || stdenvNoCC.hostPlatform.useLLVM or false then overrideCC stdenvNoCC buildPackages.llvmPackages.clangNoCompilerRt + else if stdenvNoCC.hostPlatform.useGccNG or false then + overrideCC stdenvNoCC buildPackages.gccNGPackages.gccNoLibgcc else gccCrossLibcStdenv ) @@ -99,6 +101,11 @@ with pkgs; ( if stdenvNoCC.hostPlatform.isDarwin || stdenvNoCC.hostPlatform.useLLVM or false then overrideCC stdenvNoCC buildPackages.llvmPackages.clangNoLibc + else if stdenvNoCC.hostPlatform.useGccNG or false then + # The split package set can express the two rungs separately, the way + # the LLVM set does: no libgcc above, libgcc but no libc here. The + # monolithic `gccCrossLibcStdenv` has to serve both. + overrideCC stdenvNoCC buildPackages.gccNGPackages.gccWithLibgcc else gccCrossLibcStdenv ) @@ -3244,9 +3251,11 @@ with pkgs; # NOTE: keep this with the "NG" label until we're ready to drop the monolithic GCC gccNGPackagesSet = recurseIntoAttrs (callPackages ../development/compilers/gcc/ng { }); gccNGPackages_15 = gccNGPackagesSet."15"; + gccNGPackages = gccNGPackagesSet.${toString default-gcc-version}; mkGCCNGPackages = gccNGPackagesSet.mkPackage; }) gccNGPackages_15 + gccNGPackages mkGCCNGPackages ; @@ -5622,11 +5631,22 @@ with pkgs; if stdenv.hostPlatform != stdenv.buildPlatform then { stdenv = gccCrossLibcStdenv; # doesn't compile without gcc - libgcc = callPackage ../development/libraries/gcc/libgcc { - gcc = gccCrossLibcStdenv.cc; - glibc = glibc.override { libgcc = null; }; - stdenvNoLibs = gccCrossLibcStdenv; - }; + # glibc `dlopen`s `libgcc_s.so` without consulting the usual search + # path, so it has to name one that already exists. The monolithic + # build gets there by building glibc twice: the inner `libgcc = null` + # one names no libgcc at all, and is used for nothing but building the + # libgcc the real one names. The split package set already has that + # rung as a package of its own — `libgcc-no-libc` is what exists + # before any libc does — so there is nothing left to open-code here. + libgcc = + if stdenv.hostPlatform.useGccNG or false then + gccNGPackages.libgcc-no-libc + else + callPackage ../development/libraries/gcc/libgcc { + gcc = gccCrossLibcStdenv.cc; + glibc = glibc.override { libgcc = null; }; + stdenvNoLibs = gccCrossLibcStdenv; + }; } else {