Skip to content

Commit

Permalink
haskell.compiler.*: don't declare stage0 ghc as dep to stdenv
Browse files Browse the repository at this point in the history
Some GHC bindists have a normal `$out/lib` directory which contains
symlinks to all core libs. Because it is a normal lib directory, the
bintools setup hook will pick up on it and cause ld to pass the
appropriate -L and -rpath flags. We do not want this to happen,
especially in the case of the stage2 compiler. Not only will the final
ghc have an unnecessary reference (and thus increased closure size) to
the binary ghc, but the extra libraries in the rpath mess with the rts
and cause e.g. segfaults in GHCi.

Unfortunately, there is no way to prevent this. It is a fundamental flaw
in the cc and bintools wrappers that they do not actually distinguish
between the roles of dependencies (build, host, target). Instead
the mangleVar* function will translate the dependencies split up by
roles into platforms. This means that the wrappers can't distinguish
between depsBuildBuild and depsHostTarget (== buildInputs) when natively
compiling. As long as we are natively compiling the wrappers will put
the stage0 ghc (be it in depsBuildBuild, nativeBuildInputs etc.) into
the linker flags of the final ghc.

The solution is to sidestep the issue. We just had ghc in depsBuildBuild
to have it added to PATH. GHC itself will pass the appropriate linker
flags if necessary. To avoid the setup hooks picking up on the GHC
libraries we just don't put it into depsBuildBuild or any other
dependency list. Since the GHC build system accepts the GHC binary via
an absolute path, we don't even need to add the stage0 GHC to PATH.
  • Loading branch information
sternenseemann committed Sep 23, 2024
1 parent 931fd13 commit cde17cb
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 32 deletions.
12 changes: 8 additions & 4 deletions pkgs/development/compilers/ghc/8.10.7.nix
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,8 @@ stdenv.mkDerivation (rec {
for env in $(env | grep '^TARGET_' | sed -E 's|\+?=.*||'); do
export "''${env#TARGET_}=''${!env}"
done
# Stage0 (build->build) which builds stage 1
export GHC="${bootPkgs.ghc}/bin/ghc"
# GHC is a bit confused on its cross terminology, as these would normally be
# the *host* tools.
export CC="${toolPath "cc" targetCC}"
Expand Down Expand Up @@ -433,11 +435,13 @@ stdenv.mkDerivation (rec {
sphinx
];

# Stage0 compiler and the tools/libs it needs to build Stage1. Stage0 is
# build->build, Stage1 is build->target (which may be the same as
# host->target)
# Everything the stage0 compiler needs to build stage1: CC, bintools, extra libs.
# See also GHC, {CC,LD,AR}_STAGE0 in preConfigure.
depsBuildBuild = [
bootPkgs.ghc
# N.B. We do not declare bootPkgs.ghc in any of the stdenv.mkDerivation
# dependency lists to prevent the bintools setup hook from adding ghc's
# lib directory to the linker flags. Instead we tell configure about it
# via the GHC environment variable.
buildCC
# stage0 builds terminfo unconditionally, so we always need ncurses
ncurses
Expand Down
13 changes: 9 additions & 4 deletions pkgs/development/compilers/ghc/common-hadrian.nix
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,9 @@ stdenv.mkDerivation ({
export CC_STAGE0="$CC_FOR_BUILD"
export LD_STAGE0="$LD_FOR_BUILD"
export AR_STAGE0="$AR_FOR_BUILD"
# Stage0 (build->build) which builds stage 1
export GHC="${bootPkgs.ghc}/bin/ghc"
# GHC is a bit confused on its cross terminology, as these would normally be
# the *host* tools.
export CC="${toolPath "cc" targetCC}"
Expand Down Expand Up @@ -557,11 +560,13 @@ stdenv.mkDerivation ({

# For building runtime libs
depsBuildTarget = toolsForTarget;
# Stage0 compiler and the tools/libs it needs to build Stage1. Stage0 is
# build->build, Stage1 is build->target (which may be the same as
# host->target)
# Everything the stage0 compiler needs to build stage1: CC, bintools, extra libs.
# See also GHC, {CC,LD,AR}_STAGE0 in preConfigure.
depsBuildBuild = [
bootPkgs.ghc
# N.B. We do not declare bootPkgs.ghc in any of the stdenv.mkDerivation
# dependency lists to prevent the bintools setup hook from adding ghc's
# lib directory to the linker flags. Instead we tell configure about it
# via the GHC environment variable.
buildCC
# stage0 builds terminfo unconditionally, so we always need ncurses
ncurses
Expand Down
12 changes: 8 additions & 4 deletions pkgs/development/compilers/ghc/common-make-native-bignum.nix
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,8 @@ stdenv.mkDerivation (rec {
for env in $(env | grep '^TARGET_' | sed -E 's|\+?=.*||'); do
export "''${env#TARGET_}=''${!env}"
done
# Stage0 (build->build) which builds stage 1
export GHC="${bootPkgs.ghc}/bin/ghc"
# GHC is a bit confused on its cross terminology, as these would normally be
# the *host* tools.
export CC="${toolPath "cc" targetCC}"
Expand Down Expand Up @@ -451,11 +453,13 @@ stdenv.mkDerivation (rec {
xattr
];

# Stage0 compiler and the tools/libs it needs to build Stage1. Stage0 is
# build->build, Stage1 is build->target (which may be the same as
# host->target)
# Everything the stage0 compiler needs to build stage1: CC, bintools, extra libs.
# See also GHC, {CC,LD,AR}_STAGE0 in preConfigure.
depsBuildBuild = [
bootPkgs.ghc
# N.B. We do not declare bootPkgs.ghc in any of the stdenv.mkDerivation
# dependency lists to prevent the bintools setup hook from adding ghc's
# lib directory to the linker flags. Instead we tell configure about it
# via the GHC environment variable.
buildCC
# stage0 builds terminfo unconditionally, so we always need ncurses
ncurses
Expand Down
20 changes: 0 additions & 20 deletions pkgs/top-level/haskell-packages.nix
Original file line number Diff line number Diff line change
Expand Up @@ -280,11 +280,6 @@ in {
bb.packages.ghc928
else if stdenv.buildPlatform.isPower64 && stdenv.buildPlatform.isLittleEndian then
bb.packages.ghc928
else if stdenv.buildPlatform.isAarch64 && stdenv.buildPlatform.isLinux then
# For unknown reasons, compiling with a bindist GHC causes the final
# GHC's rpaths to contain the boot compiler on aarch64-linux / GHC 9.6.*.
# Note: Unclear if this is due to host or build aarch64-linux.
bb.packages.ghc928
else
bb.packages.ghc924Binary;
inherit (buildPackages.python3Packages) sphinx;
Expand All @@ -303,11 +298,6 @@ in {
bb.packages.ghc928
else if stdenv.buildPlatform.isPower64 && stdenv.buildPlatform.isLittleEndian then
bb.packages.ghc928
else if stdenv.buildPlatform.isAarch64 && stdenv.buildPlatform.isLinux then
# For unknown reasons, compiling with a bindist GHC causes the final
# GHC's rpaths to contain the boot compiler on aarch64-linux / GHC 9.6.*.
# Note: Unclear if this is due to host or build aarch64-linux.
bb.packages.ghc928
else
bb.packages.ghc924Binary;
inherit (buildPackages.python3Packages) sphinx;
Expand All @@ -326,11 +316,6 @@ in {
bb.packages.ghc928
else if stdenv.buildPlatform.isPower64 && stdenv.buildPlatform.isLittleEndian then
bb.packages.ghc928
else if stdenv.buildPlatform.isAarch64 && stdenv.buildPlatform.isLinux then
# For unknown reasons, compiling with a bindist GHC causes the final
# GHC's rpaths to contain the boot compiler on aarch64-linux / GHC 9.6.*.
# Note: Unclear if this is due to host or build aarch64-linux.
bb.packages.ghc928
else
bb.packages.ghc924Binary;
inherit (buildPackages.python3Packages) sphinx;
Expand All @@ -349,11 +334,6 @@ in {
bb.packages.ghc928
else if stdenv.buildPlatform.isPower64 && stdenv.buildPlatform.isLittleEndian then
bb.packages.ghc928
else if stdenv.buildPlatform.isAarch64 && stdenv.buildPlatform.isLinux then
# For unknown reasons, compiling with a bindist GHC causes the final
# GHC's rpaths to contain the boot compiler on aarch64-linux / GHC 9.6.*.
# Note: Unclear if this is due to host or build aarch64-linux.
bb.packages.ghc928
else
bb.packages.ghc924Binary;
inherit (buildPackages.python3Packages) sphinx;
Expand Down

0 comments on commit cde17cb

Please sign in to comment.