From 8fcb42683344cf247a28a80db6264ea09f48d692 Mon Sep 17 00:00:00 2001 From: Adam Joseph Date: Mon, 24 Jul 2023 04:00:24 -0700 Subject: [PATCH 01/10] default-gcc-version: init This commit lifts the default gcc version to a top-level attribute so it can be overridden. --- pkgs/top-level/all-packages.nix | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index b47c2e749ebfa..3bd0a58a4d598 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -15597,14 +15597,12 @@ with pkgs; gbforth = callPackage ../development/compilers/gbforth { }; - inherit (let - num = - if (with stdenv.targetPlatform; isVc4 || libc == "relibc") then 6 - else 12; - numS = toString num; - in { - gcc = pkgs.${"gcc${numS}"}; - gccFun = callPackage (../development/compilers/gcc + "/${numS}"); + default-gcc-version = + if (with stdenv.targetPlatform; isVc4 || libc == "relibc") then 6 + else 12; + inherit ({ + gcc = pkgs.${"gcc${toString default-gcc-version}"}; + gccFun = callPackage (../development/compilers/gcc + "/${toString default-gcc-version}"); }) gcc gccFun; gcc-unwrapped = gcc.cc; From 627cf7c0c8504c79fbbb5afa1d5ba5887c99da21 Mon Sep 17 00:00:00 2001 From: Adam Joseph Date: Mon, 14 Aug 2023 13:26:58 -0700 Subject: [PATCH 02/10] gcc: add passthru.{target_libc,target_libgcc} --- pkgs/development/compilers/gcc/common/builder.nix | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pkgs/development/compilers/gcc/common/builder.nix b/pkgs/development/compilers/gcc/common/builder.nix index 6df4e32ddb765..d836930df18cc 100644 --- a/pkgs/development/compilers/gcc/common/builder.nix +++ b/pkgs/development/compilers/gcc/common/builder.nix @@ -9,7 +9,14 @@ let in originalAttrs: (stdenv.mkDerivation (finalAttrs: originalAttrs // { - passthru = (originalAttrs.passthru or {}) // { inherit forceLibgccToBuildCrtStuff; }; + passthru = let + target_libc = if stdenv.targetPlatform != stdenv.buildPlatform + then finalAttrs.finalPackage.libcCross + else finalAttrs.finalPackage.stdenv.cc.libc; + target_libgcc = target_libc.passthru.libgcc or null; + in (originalAttrs.passthru or {}) // { + inherit forceLibgccToBuildCrtStuff target_libc target_libgcc; + }; preUnpack = '' oldOpts="$(shopt -po nounset)" || true set -euo pipefail From 723cd01823b985f1fe302e3073bc4b3999cc8157 Mon Sep 17 00:00:00 2001 From: Adam Joseph Date: Tue, 15 Aug 2023 00:50:11 -0700 Subject: [PATCH 03/10] gccFun: allow to specify the version of gcc This commit causes `gccFun` to use `callPackage` with an argument `gcc_major_version` to indicate which version of gcc is desired. It defaults to `default-gcc-version`. --- pkgs/top-level/all-packages.nix | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 3bd0a58a4d598..c7d2d94497872 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -15602,7 +15602,10 @@ with pkgs; else 12; inherit ({ gcc = pkgs.${"gcc${toString default-gcc-version}"}; - gccFun = callPackage (../development/compilers/gcc + "/${toString default-gcc-version}"); + gccFun = + callPackage + ({ gcc_major_version }: callPackage (../development/compilers/gcc + "/${gcc_major_version}")) + { gcc_major_version = toString default-gcc-version; }; }) gcc gccFun; gcc-unwrapped = gcc.cc; From 6018a0129f8aac47671ac8eada96df40af59346a Mon Sep 17 00:00:00 2001 From: Adam Joseph Date: Tue, 15 Aug 2023 01:05:12 -0700 Subject: [PATCH 04/10] gccWithoutTargetLibc: inline let-binding for libcCross1 This is stylistic; this code has been inherited from quite a long time ago, and I never understood what the `1` meant. Let's just use `binutilsNoLibc.libc`, which is what `libcCross1` is defined to be, instead. --- pkgs/top-level/all-packages.nix | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index c7d2d94497872..ed2b7de4f65b7 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -15700,9 +15700,8 @@ with pkgs; # The GCC used to build libc for the target platform. Normal gccs will be # built with, and use, that cross-compiled libc. - gccWithoutTargetLibc = assert stdenv.targetPlatform != stdenv.hostPlatform; let - libcCross1 = binutilsNoLibc.libc; - in wrapCCWith { + gccWithoutTargetLibc = assert stdenv.targetPlatform != stdenv.hostPlatform; + wrapCCWith { cc = gccFun { # copy-pasted inherit noSysDirs; @@ -15714,7 +15713,7 @@ with pkgs; withoutTargetLibc = true; langCC = false; - libcCross = libcCross1; + libcCross = binutilsNoLibc.libc; targetPackages.stdenv.cc.bintools = binutilsNoLibc; enableShared = stdenv.targetPlatform.hasSharedLibraries @@ -15726,7 +15725,7 @@ with pkgs; ; }; bintools = binutilsNoLibc; - libc = libcCross1; + libc = binutilsNoLibc.libc; extraPackages = []; }; From 5c317fcd0040ee9652d2e2070ec179bd5cc07700 Mon Sep 17 00:00:00 2001 From: Adam Joseph Date: Tue, 15 Aug 2023 01:07:27 -0700 Subject: [PATCH 05/10] gccWithoutTargetLibc: allow to specify the version of gcc This commit causes `gccWithoutTargetLibc` to use `callPackage` with an argument `gcc_major_version` to indicate which version of gcc is desired. It defaults to `default-gcc-version`. --- pkgs/top-level/all-packages.nix | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index ed2b7de4f65b7..243096f1275e9 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -15700,9 +15700,10 @@ with pkgs; # The GCC used to build libc for the target platform. Normal gccs will be # built with, and use, that cross-compiled libc. - gccWithoutTargetLibc = assert stdenv.targetPlatform != stdenv.hostPlatform; - wrapCCWith { - cc = gccFun { + gccWithoutTargetLibc = + assert stdenv.targetPlatform != stdenv.hostPlatform; + callPackage ({ gcc_major_version }: wrapCCWith { + cc = (gccFun.override { inherit gcc_major_version; }) { # copy-pasted inherit noSysDirs; @@ -15727,7 +15728,7 @@ with pkgs; bintools = binutilsNoLibc; libc = binutilsNoLibc.libc; extraPackages = []; - }; + }) { gcc_major_version = toString default-gcc-version; }; # This expression will be pushed into pkgs/development/compilers/gcc/common # once the top-level gcc/${version}/default.nix files are deduplicated. From 20b5f9cafec9dcca593150f4aa032bd8eb1e2b37 Mon Sep 17 00:00:00 2001 From: Adam Joseph Date: Tue, 15 Aug 2023 01:09:52 -0700 Subject: [PATCH 06/10] gccWithoutTargetLibc: weaken assertion from target!=host to target!=build gccWithoutTargetLibc is useful not only for building cross compilers, but also for cross-building native compilers. Let's allow it to be used in that situation. --- pkgs/top-level/all-packages.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 243096f1275e9..40c7a357332da 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -15701,7 +15701,7 @@ with pkgs; # The GCC used to build libc for the target platform. Normal gccs will be # built with, and use, that cross-compiled libc. gccWithoutTargetLibc = - assert stdenv.targetPlatform != stdenv.hostPlatform; + assert stdenv.targetPlatform != stdenv.buildPlatform; callPackage ({ gcc_major_version }: wrapCCWith { cc = (gccFun.override { inherit gcc_major_version; }) { # copy-pasted From 7077352ebbccbba6178d9dcd34c6fd62a7bdc288 Mon Sep 17 00:00:00 2001 From: Adam Joseph Date: Tue, 15 Aug 2023 01:12:09 -0700 Subject: [PATCH 07/10] gcc: un-factor (x: (lowPrio (wrapCC x))) This commit simply changes ``` pkg = lowPrio (wrapCC ... ) ``` into ``` pkg = ... ``` and wraps the sole occurrence of `pkg` with `lowPrio (wrapCC ... )`. It has no effect on eval. I am placing it in a separate commit simply to make the next commit easier to understand. --- pkgs/top-level/all-packages.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 40c7a357332da..88742dfa01725 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -15736,7 +15736,7 @@ with pkgs; (lib.listToAttrs (map (version: let atLeast = lib.versionAtLeast version; attrName = "gcc${lib.replaceStrings ["."] [""] version}"; - pkg = lowPrio (wrapCC (callPackage (../development/compilers/gcc + "/${version}") ({ + pkg = callPackage (../development/compilers/gcc + "/${version}") ({ inherit noSysDirs; reproducibleBuild = true; profiledCompiler = false; @@ -15761,8 +15761,8 @@ with pkgs; } // lib.optionalAttrs (atLeast "6" && !(atLeast "9")) { # gcc 10 is too strict to cross compile gcc <= 8 stdenv = if (stdenv.targetPlatform != stdenv.buildPlatform) && stdenv.cc.isGNU then gcc7Stdenv else stdenv; - }))); - in lib.nameValuePair attrName pkg + }); + in lib.nameValuePair attrName (lowPrio (wrapCC pkg)) ) [ "4.8" "4.9" "6" "7" "8" "9" "10" "11" "12" "13" ])) gcc48 gcc49 gcc6 gcc7 gcc8 gcc9 gcc10 gcc11 gcc12 gcc13; From 82aece407c63c1e271ec5619665dd768c872bf6b Mon Sep 17 00:00:00 2001 From: Adam Joseph Date: Tue, 15 Aug 2023 01:24:48 -0700 Subject: [PATCH 08/10] gcc: check that target libc was built with libgcc version matching gcc --- .../compilers/gcc/common/builder.nix | 3 ++- .../compilers/gcc/common/libgcc.nix | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/pkgs/development/compilers/gcc/common/builder.nix b/pkgs/development/compilers/gcc/common/builder.nix index d836930df18cc..f825d32f906da 100644 --- a/pkgs/development/compilers/gcc/common/builder.nix +++ b/pkgs/development/compilers/gcc/common/builder.nix @@ -10,7 +10,8 @@ in originalAttrs: (stdenv.mkDerivation (finalAttrs: originalAttrs // { passthru = let - target_libc = if stdenv.targetPlatform != stdenv.buildPlatform + target_libc = if finalAttrs.withoutTargetLibc then null + else if finalAttrs.finalPackage.libcCross != null then finalAttrs.finalPackage.libcCross else finalAttrs.finalPackage.stdenv.cc.libc; target_libgcc = target_libc.passthru.libgcc or null; diff --git a/pkgs/development/compilers/gcc/common/libgcc.nix b/pkgs/development/compilers/gcc/common/libgcc.nix index 4ab6eb2b3b440..803ae0b72cb22 100644 --- a/pkgs/development/compilers/gcc/common/libgcc.nix +++ b/pkgs/development/compilers/gcc/common/libgcc.nix @@ -16,6 +16,26 @@ drv: lib.pipe drv ([ + # Check that the version of GCC exactly matches the version of + # libgcc (if any) used to build its target-glibc. For an example + # of the problems that occur if this happens, see + # https://github.com/NixOS/nixpkgs/issues/244871 + (pkg: pkg.overrideAttrs (finalAttrs: previousAttrs: { + + # We put the assertion on the `meta` attribute to ensure that + # we can examine `finalAttrs` without creating any infinite + # recursions. The meta checks (even the non-recursive ones) + # will force this assertion. + meta = let + inherit (finalAttrs) finalPackage; + inherit (finalPackage.passthru) target_libc target_libgcc; + in + assert (finalPackage.version != target_libgcc.version or finalPackage.version) + -> throw + "${finalPackage.name} version does not match target libgcc version ${target_libgcc.name} (from target libc ${target_libc.name})"; + previousAttrs.meta; + })) + (pkg: pkg.overrideAttrs (previousAttrs: lib.optionalAttrs ( targetPlatform != hostPlatform && From 760460086170439ce712a6dc3f6912baa0cb8bd9 Mon Sep 17 00:00:00 2001 From: Adam Joseph Date: Tue, 15 Aug 2023 01:34:42 -0700 Subject: [PATCH 09/10] libgcc: assert that it is built by gcc of same version This commit checks that libgcc is being built by gcc, and that the gcc it is being built by is exactly the same version. Any other combination of compiler and libgcc is unlikely to work. --- pkgs/development/libraries/gcc/libgcc/default.nix | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkgs/development/libraries/gcc/libgcc/default.nix b/pkgs/development/libraries/gcc/libgcc/default.nix index e2fbf55876fa1..f63feca2af21e 100644 --- a/pkgs/development/libraries/gcc/libgcc/default.nix +++ b/pkgs/development/libraries/gcc/libgcc/default.nix @@ -137,4 +137,10 @@ in stdenv.mkDerivation (finalAttrs: { ln -s "$out/lib/gcc/${stdenv.hostPlatform.config}/${finalAttrs.version}"/* "$out/lib" ln -s "$dev/lib/gcc/${stdenv.hostPlatform.config}/${finalAttrs.version}/include"/* "$dev/include/" ''; + + meta = + assert !stdenv.cc.cc.isGNU -> throw "you must use GCC to compile libgcc"; + assert (stdenv.cc.cc.version != finalAttrs.finalPackage.version) + -> throw "you tried to use ${stdenv.cc.cc.name} (${stdenv.cc.cc}) to compile ${finalAttrs.finalPackage.name}"; + { }; }) From efcb5e18e982923a1a1e735e340a2597b48d4df1 Mon Sep 17 00:00:00 2001 From: Adam Joseph Date: Tue, 15 Aug 2023 01:51:44 -0700 Subject: [PATCH 10/10] gcc: do not use an old cross-compiler build a new native compiler Closes #244871 This commit checks for the following situation: we are cross-building a native compiler (build!=(host==target)) using a cross-compiler ((build==host)!=target) which is *older than* the compiler being built. We check both the cross-compiler itself as well as the cross-compiler which was used to build the copy of libgcc against which the target libc is linked. This configuration often fails (see https://github.com/NixOS/nixpkgs/issues/244871 for one example). If you need to cross-build a newer-than-default-gcc-version native compiler, you must first build a newer-than-default-gcc-version cross-compiler. Previously, the user had to figure out how to hack in this upgrade on their own. The code added by this commit inserts the extra compiler build automatically. --- pkgs/top-level/all-packages.nix | 40 +++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 88742dfa01725..f5487809a74f5 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -15736,7 +15736,7 @@ with pkgs; (lib.listToAttrs (map (version: let atLeast = lib.versionAtLeast version; attrName = "gcc${lib.replaceStrings ["."] [""] version}"; - pkg = callPackage (../development/compilers/gcc + "/${version}") ({ + pkg = (callPackage (../development/compilers/gcc + "/${version}") ({ inherit noSysDirs; reproducibleBuild = true; profiledCompiler = false; @@ -15761,7 +15761,43 @@ with pkgs; } // lib.optionalAttrs (atLeast "6" && !(atLeast "9")) { # gcc 10 is too strict to cross compile gcc <= 8 stdenv = if (stdenv.targetPlatform != stdenv.buildPlatform) && stdenv.cc.isGNU then gcc7Stdenv else stdenv; - }); + + })).override (previousArgs: + let + inherit (previousArgs) stdenv libcCross; + in lib.optionalAttrs (!stdenv.buildPlatform.canExecute stdenv.targetPlatform && + libcCross != null && + (lib.versionOlder libcCross.passthru.libgcc.version version || + lib.versionOlder stdenv.cc.cc.version version)) + # The conditional above checks for the following situation: we are cross-building a + # native compiler (build!=(host==target)) using a cross-compiler ((build==host)!=target) + # which is *older than* the compiler being built. We check both the cross-compiler + # itself as well as the cross-compiler which was used to build the copy of libgcc + # against which the target libc is linked. + # + # This configuration often fails (see https://github.com/NixOS/nixpkgs/issues/244871 for + # one example). If you need to cross-build a newer-than-default-gcc-version native + # compiler, you must first build a newer-than-default-gcc-version cross-compiler. The + # code below does that automatically. + (let + gcc = wrapCCWith { + cc = (pkgsHostTarget.gccWithoutTargetLibc.override { + gcc_major_version = version; + }).cc; + bintools = binutilsNoLibc; + libc = binutilsNoLibc.libc; + extraPackages = []; + }; + libcCross' = libcCross.override (previousArgs: { + libgcc = previousArgs.libgcc.override (previousArgs: { + glibc = libcCross'.override { libgcc = null; }; + stdenvNoLibs = overrideCC previousArgs.stdenvNoLibs gcc; + inherit gcc; + }); + }); + in { + libcCross = libcCross'; + })); in lib.nameValuePair attrName (lowPrio (wrapCC pkg)) ) [ "4.8" "4.9" "6" "7" "8" "9" "10" "11" "12" "13" ])) gcc48 gcc49 gcc6 gcc7 gcc8 gcc9 gcc10 gcc11 gcc12 gcc13;