Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stdenv: Nix-driven bootstrap of gcc #209870

Merged
merged 14 commits into from Apr 3, 2023
Merged
4 changes: 3 additions & 1 deletion nixos/release-small.nix
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ in rec {
stdenv
subversion
tarball
vim;
vim
tests-stdenv-gcc-stageCompare;
};

tested = let
Expand Down Expand Up @@ -131,6 +132,7 @@ in rec {
"nixos.tests.proxy"
"nixos.tests.simple"
"nixpkgs.jdk"
"nixpkgs.tests-stdenv-gcc-stageCompare"
])
];
};
Expand Down
18 changes: 12 additions & 6 deletions pkgs/applications/editors/emacs/generic.nix
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,17 @@ assert withXwidgets -> withGTK3 && webkitgtk != null;
assert withTreeSitter -> tree-sitter != null;


let
libGccJitLibraryPaths = [
"${lib.getLib libgccjit}/lib/gcc"
"${lib.getLib stdenv.cc.libc}/lib"
] ++ lib.optionals (stdenv.cc?cc.libgcc) [
"${lib.getLib stdenv.cc.cc.libgcc}/lib"
trofi marked this conversation as resolved.
Show resolved Hide resolved
];
in
(if withMacport then llvmPackages_6.stdenv else stdenv).mkDerivation (finalAttrs: (lib.optionalAttrs nativeComp {
NATIVE_FULL_AOT = "1";
LIBRARY_PATH = "${lib.getLib stdenv.cc.libc}/lib";
LIBRARY_PATH = lib.concatStringsSep ":" libGccJitLibraryPaths;
} // {
pname = pname + lib.optionalString ( !withX && !withNS && !withMacport && !withGTK2 && !withGTK3 ) "-nox";
inherit version;
Expand All @@ -75,17 +83,15 @@ assert withTreeSitter -> tree-sitter != null;
then ./native-comp-driver-options-28.patch
else ./native-comp-driver-options.patch;
backendPath = (lib.concatStringsSep " "
(builtins.map (x: ''"-B${x}"'') [
(builtins.map (x: ''"-B${x}"'') ([
# Paths necessary so the JIT compiler finds its libraries:
"${lib.getLib libgccjit}/lib"
"${lib.getLib libgccjit}/lib/gcc"
"${lib.getLib stdenv.cc.libc}/lib"

] ++ libGccJitLibraryPaths ++ [
# Executable paths necessary for compilation (ld, as):
"${lib.getBin stdenv.cc.cc}/bin"
"${lib.getBin stdenv.cc.bintools}/bin"
"${lib.getBin stdenv.cc.bintools.bintools}/bin"
]));
])));
})
];

Expand Down
42 changes: 39 additions & 3 deletions pkgs/build-support/cc-wrapper/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,40 @@
, isGNU ? false, isClang ? cc.isClang or false, gnugrep ? null
, buildPackages ? {}
, libcxx ? null

# Whether or not to add `-B` and `-L` to `nix-support/cc-{c,ld}flags`
, useCcForLibs ?

# Always add these flags for Clang, because in order to compile (most
# software) it needs libraries that are shipped and compiled with gcc.
if isClang then true

# Never add these flags for a build!=host cross-compiler or a host!=target
# ("cross-built-native") compiler; currently nixpkgs has a special build
# path for these (`crossStageStatic`). Hopefully at some point that build
# path will be merged with this one and this conditional will be removed.
else if (with stdenvNoCC; buildPlatform != hostPlatform || hostPlatform != targetPlatform) then false

# Never add these flags when wrapping the bootstrapFiles' compiler; it has a
# /usr/-like layout with everything smashed into a single outpath, so it has
# no trouble finding its own libraries.
else if (cc.passthru.isFromBootstrapFiles or false) then false

# Add these flags when wrapping `xgcc` (the first compiler that nixpkgs builds)
else if (cc.passthru.isXgcc or false) then true

# Add these flags when wrapping `stdenv.cc`
else if (cc.stdenv.cc.cc.passthru.isXgcc or false) then true

# Do not add these flags in any other situation. This is `false` mainly to
# prevent these flags from being added when wrapping *old* versions of gcc
# (e.g. `gcc6Stdenv`), since they will cause the old gcc to get `-B` and
# `-L` flags pointing at the new gcc's libstdc++ headers. Example failure:
# https://hydra.nixos.org/build/213125495
else false

# the derivation at which the `-B` and `-L` flags added by `useCcForLibs` will point
, gccForLibs ? if useCcForLibs then cc else null
# same as `gccForLibs`, but generalized beyond clang
, useCcForLibs ? isClang
}:

with lib;
Expand Down Expand Up @@ -319,14 +350,19 @@ stdenv.mkDerivation {
&& targetPlatform.isLinux
&& !(stdenv.targetPlatform.useAndroidPrebuilt or false)
&& !(stdenv.targetPlatform.useLLVM or false)
&& gccForLibs != null) ''
&& gccForLibs != null) (''
echo "--gcc-toolchain=${gccForLibs}" >> $out/nix-support/cc-cflags

# Pull in 'cc.out' target to get 'libstdc++fs.a'. It should be in
# 'cc.lib'. But it's a gcc package bug.
# TODO(trofi): remove once gcc is fixed to move libraries to .lib output.
echo "-L${gccForLibs}/${optionalString (targetPlatform != hostPlatform) "/${targetPlatform.config}"}/lib" >> $out/nix-support/cc-ldflags
''
# this ensures that when clang passes -lgcc_s to lld (as it does
# when building e.g. firefox), lld is able to find libgcc_s.so
This conversation was marked as resolved.
Show resolved Hide resolved
+ lib.optionalString (gccForLibs?libgcc) ''
echo "-L${gccForLibs.libgcc}/lib" >> $out/nix-support/cc-ldflags
'')
Comment on lines +361 to +365
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For more context, see: 0411461#r101585670


##
## General libc support
Expand Down
6 changes: 4 additions & 2 deletions pkgs/build-support/emacs/wrapper.nix
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,12 @@ runCommand
# Store all paths we want to add to emacs here, so that we only need to add
# one path to the load lists
deps = runCommand "emacs-packages-deps"
{
({
inherit explicitRequires lndir emacs;
nativeBuildInputs = lib.optional nativeComp gcc;
}
} // lib.optionalAttrs nativeComp {
inherit (emacs) LIBRARY_PATH;
})
''
findInputsOld() {
local pkg="$1"; shift
Expand Down
2 changes: 0 additions & 2 deletions pkgs/build-support/trivial-builders/test/references.nix
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,8 @@ testers.nixosTest {
""")
'';
meta = {
license = lib.licenses.mit; # nixpkgs license
maintainers = with lib.maintainers; [
roberth
];
description = "Run the Nixpkgs trivial builders tests";
};
}
17 changes: 11 additions & 6 deletions pkgs/development/compilers/gcc/11/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
, buildPackages
, libxcrypt
, disableGdbPlugin ? !enablePlugin
, nukeReferences
, callPackage
}:

# Make sure we get GNU sed.
Expand All @@ -49,7 +51,7 @@ with builtins;

let majorVersion = "11";
version = "${majorVersion}.3.0";
disableBootstrap = !(with stdenv; targetPlatform == hostPlatform && hostPlatform == buildPlatform);
disableBootstrap = true;

inherit (stdenv) buildPlatform hostPlatform targetPlatform;

Expand Down Expand Up @@ -159,7 +161,7 @@ let majorVersion = "11";

in

stdenv.mkDerivation ({
lib.pipe (stdenv.mkDerivation ({
pname = "${crossNameAddon}${name}";
inherit version;

Expand Down Expand Up @@ -250,9 +252,8 @@ stdenv.mkDerivation ({
targetConfig = if targetPlatform != hostPlatform then targetPlatform.config else null;

buildFlags =
let target =
lib.optionalString (profiledCompiler) "profiled" +
lib.optionalString (targetPlatform == hostPlatform && hostPlatform == buildPlatform && !disableBootstrap) "bootstrap";
let target = lib.optionalString (profiledCompiler) "profiled"
+ lib.optionalString (targetPlatform == hostPlatform && hostPlatform == buildPlatform && !disableBootstrap) "bootstrap";
in lib.optional (target != "") target;

inherit (callFile ../common/strip-attributes.nix { })
Expand Down Expand Up @@ -310,4 +311,8 @@ stdenv.mkDerivation ({
}

// optionalAttrs (enableMultilib) { dontMoveLib64 = true; }
)
))
[
(callPackage ../common/libgcc.nix { inherit langC langCC langJit; })
(callPackage ../common/checksum.nix { inherit langC langCC; })
]
14 changes: 11 additions & 3 deletions pkgs/development/compilers/gcc/12/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
, buildPackages
, libxcrypt
, disableGdbPlugin ? !enablePlugin
, nukeReferences
, callPackage
}:

# Make sure we get GNU sed.
Expand All @@ -54,7 +56,7 @@ with builtins;

let majorVersion = "12";
version = "${majorVersion}.2.0";
disableBootstrap = !(with stdenv; targetPlatform == hostPlatform && hostPlatform == buildPlatform);
disableBootstrap = true;

inherit (stdenv) buildPlatform hostPlatform targetPlatform;

Expand Down Expand Up @@ -177,6 +179,7 @@ let majorVersion = "12";
mpfr
name
noSysDirs
nukeReferences
patchelf
perl
profiledCompiler
Expand All @@ -194,7 +197,7 @@ let majorVersion = "12";

in

stdenv.mkDerivation ({
lib.pipe (stdenv.mkDerivation ({
pname = "${crossNameAddon}${name}";
inherit version;

Expand Down Expand Up @@ -344,4 +347,9 @@ stdenv.mkDerivation ({
}

// optionalAttrs (enableMultilib) { dontMoveLib64 = true; }
)
))
[
(callPackage ../common/libgcc.nix { inherit langC langCC langJit; })
(callPackage ../common/checksum.nix { inherit langC langCC; })
]

40 changes: 40 additions & 0 deletions pkgs/development/compilers/gcc/common/checksum.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{ lib
, stdenv
, nukeReferences
, langC
, langCC
, runtimeShell
}:

let
enableChecksum = (with stdenv; buildPlatform == hostPlatform && hostPlatform == targetPlatform) && langC && langCC;
in
(pkg: pkg.overrideAttrs (previousAttrs: lib.optionalAttrs enableChecksum {
outputs = previousAttrs.outputs ++ lib.optionals enableChecksum [ "checksum" ];
# This is a separate phase because gcc assembles its phase scripts
# in bash instead of nix (we should fix that).
preFixupPhases = (previousAttrs.preFixupPhases or []) ++ [ "postInstallSaveChecksumPhase" ];
#
# gcc uses an auxiliary utility `genchecksum` to md5-hash (most of) its
# `.o` and `.a` files prior to linking (in case the linker is
# nondeterministic). Since we want to compare across gccs built from two
# separate derivations, we wrap `genchecksum` with a `nuke-references`
# call. We also stash copies of the inputs to `genchecksum` in
# `$checksum/inputs/` -- this is extremely helpful for debugging since
# it's hard to get Nix to not delete the $NIX_BUILD_TOP of a successful
# build.
#
postInstallSaveChecksumPhase = ''
mv gcc/build/genchecksum gcc/build/.genchecksum-wrapped
cat > gcc/build/genchecksum <<\EOF
#!${runtimeShell}
${nukeReferences}/bin/nuke-refs $@
for INPUT in "$@"; do install -Dt $INPUT $checksum/inputs/; done
exec build/.genchecksum-wrapped $@
EOF
chmod +x gcc/build/genchecksum
rm gcc/*-checksum.*
make -C gcc cc1-checksum.o cc1plus-checksum.o
install -Dt $checksum/checksums/ gcc/cc*-checksum.o
'';
}))
96 changes: 96 additions & 0 deletions pkgs/development/compilers/gcc/common/libgcc.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
{ lib
, stdenv
, langC
, langCC
, langJit
}:

let
enableLibGccOutput = (with stdenv; targetPlatform == hostPlatform) && !langJit;
in
(pkg: pkg.overrideAttrs (previousAttrs: lib.optionalAttrs ((!langC) || langJit || enableLibGccOutput) {
outputs = previousAttrs.outputs ++ lib.optionals enableLibGccOutput [ "libgcc" ];
# This is a separate phase because gcc assembles its phase scripts
# in bash instead of nix (we should fix that).
preFixupPhases = (previousAttrs.preFixupPhases or []) ++ [ "preFixupLibGccPhase" ];
preFixupLibGccPhase =
# delete extra/unused builds of libgcc_s in non-langC builds
# (i.e. libgccjit, gnat, etc) to avoid potential confusion
lib.optionalString (!langC) ''
rm -f $out/lib/libgcc_s.so*
''

# TODO(amjoseph): remove the `libgcc_s.so` symlinks below and replace them
# with a `-L${gccForLibs.libgcc}/lib` in cc-wrapper's
# `$out/nix-support/cc-flags`. See also:
# - https://github.com/NixOS/nixpkgs/pull/209870#discussion_r1130614895
# - https://github.com/NixOS/nixpkgs/pull/209870#discussion_r1130635982
# - https://github.com/NixOS/nixpkgs/commit/404155c6acfa59456aebe6156b22fe385e7dec6f
#
# move `libgcc_s.so` into its own output, `$libgcc`
+ lib.optionalString enableLibGccOutput (''
# move libgcc from lib to its own output (libgcc)
mkdir -p $libgcc/lib
mv $lib/lib/libgcc_s.so $libgcc/lib/
mv $lib/lib/libgcc_s.so.1 $libgcc/lib/
ln -s $libgcc/lib/libgcc_s.so $lib/lib/
ln -s $libgcc/lib/libgcc_s.so.1 $lib/lib/
This conversation was marked as resolved.
Show resolved Hide resolved
''
#
# Nixpkgs ordinarily turns dynamic linking into pseudo-static linking:
# libraries are still loaded dynamically, exactly which copy of each
# library is loaded is permanently fixed at compile time (via RUNPATH).
# For libgcc_s we must revert to the "impure dynamic linking" style found
# in imperative software distributions. We must do this because
# `libgcc_s` calls `malloc()` and therefore has a `DT_NEEDED` for `libc`,
# which creates two problems:
#
# 1. A circular package dependency `glibc`<-`libgcc`<-`glibc`
#
# 2. According to the `-Wl,-rpath` flags added by Nixpkgs' `ld-wrapper`,
# the two versions of `glibc` in the cycle above are actually
# different packages. The later one is compiled by this `gcc`, but
# the earlier one was compiled by the compiler *that compiled* this
# `gcc` (usually the bootstrapFiles). In any event, the `glibc`
# dynamic loader won't honor that specificity without namespaced
# manual loads (`dlmopen()`). Once a `libc` is present in the address
# space of a process, that `libc` will be used to satisfy all
# `DT_NEEDED`s for `libc`, regardless of `RUNPATH`s.
#
# So we wipe the RUNPATH using `patchelf --set-rpath ""`. We can't use
# `patchelf --remove-rpath`, because at least as of patchelf 0.15.0 it
# will leave the old RUNPATH string in the file where the reference
# scanner can still find it:
#
# https://github.com/NixOS/patchelf/issues/453
#
# Note: we might be using the bootstrapFiles' copy of patchelf, so we have
# to keep doing it this way until both the issue is fixed *and* all the
# bootstrapFiles are regenerated, on every platform.
#
# This patchelfing is *not* effectively equivalent to copying
# `libgcc_s` into `glibc`'s outpath. There is one minor and one
# major difference:
#
# 1. (Minor): multiple builds of `glibc` (say, with different
# overrides or parameters) will all reference a single store
# path:
#
# /nix/store/xxx...xxx-gcc-libgcc/lib/libgcc_s.so.1
#
# This many-to-one referrer relationship will be visible in the store's
# dependency graph, and will be available to `nix-store -q` queries.
# Copying `libgcc_s` into each of its referrers would lose that
# information.
#
# 2. (Major): by referencing `libgcc_s.so.1`, rather than copying it, we
# are still able to run `nix-store -qd` on it to find out how it got
# built! Most importantly, we can see from that deriver which compiler
# was used to build it (or if it is part of the unpacked
# bootstrap-files). Copying `libgcc_s.so.1` from one outpath to
# another eliminates the ability to make these queries.
#
+ ''
patchelf --set-rpath "" $libgcc/lib/libgcc_s.so.1
'');
}))
Loading