push sheeet
Some checks failed
Periodic Merges (6h) / master → staging-nixos (push) Failing after 12m50s
Periodic Merges (6h) / master → staging-next (push) Failing after 12m54s
Periodic Merges (24h) / merge-base(master,staging) → haskell-updates (push) Failing after 11m54s
Periodic Merges (6h) / staging-next → staging (push) Failing after 12m13s
Periodic Merges (24h) / staging-next-25.05 → staging-25.05 (push) Failing after 13m24s
Periodic Merges (24h) / release-25.05 → staging-next-25.05 (push) Failing after 14m28s

This commit is contained in:
Dark Steveneq
2025-10-09 14:15:47 +02:00
commit 646b892680
49168 changed files with 5897842 additions and 0 deletions

View File

@@ -0,0 +1,535 @@
{
lib,
stdenv,
fetchurl,
perl,
gcc,
ncurses5,
ncurses6,
gmp,
libiconv,
numactl,
libffi,
llvmPackages,
replaceVarsWith,
coreutils,
targetPackages,
# minimal = true; will remove files that aren't strictly necessary for
# regular builds and GHC bootstrapping.
# This is "useful" for staying within hydra's output limits for at least the
# aarch64-linux architecture.
minimal ? false,
}:
# Prebuilt only does native
assert stdenv.targetPlatform == stdenv.hostPlatform;
let
downloadsUrl = "https://downloads.haskell.org/ghc";
# Copy sha256 from https://downloads.haskell.org/~ghc/9.0.2/SHA256SUMS
version = "9.0.2";
# Information about available bindists that we use in the build.
#
# # Bindist library checking
#
# The field `archSpecificLibraries` also provides a way for us get notified
# early when the upstream bindist changes its dependencies (e.g. because a
# newer Debian version is used that uses a new `ncurses` version).
#
# Usage:
#
# * You can find the `fileToCheckFor` of libraries by running `readelf -d`
# on the compiler binary (`exePathForLibraryCheck`).
# * To skip library checking for an architecture,
# set `exePathForLibraryCheck = null`.
# * To skip file checking for a specific arch specific library,
# set `fileToCheckFor = null`.
ghcBinDists = {
# Binary distributions for the default libc (e.g. glibc, or libSystem on Darwin)
# nixpkgs uses for the respective system.
defaultLibc = {
i686-linux = {
variantSuffix = "";
src = {
url = "${downloadsUrl}/${version}/ghc-${version}-i386-deb9-linux.tar.xz";
sha256 = "fdeb9f8928fbe994064778a8e1e85bb1a58a6cd3dd7b724fcc2a1dcfda6cad47";
};
exePathForLibraryCheck = "ghc/stage2/build/tmp/ghc-stage2";
archSpecificLibraries = [
{
nixPackage = gmp;
fileToCheckFor = null;
}
# The i686-linux bindist provided by GHC HQ is currently built on Debian 9,
# which link it against `libtinfo.so.5` (ncurses 5).
# Other bindists are linked `libtinfo.so.6` (ncurses 6).
{
nixPackage = ncurses5;
fileToCheckFor = "libtinfo.so.5";
}
];
};
x86_64-linux = {
variantSuffix = "";
src = {
url = "${downloadsUrl}/${version}/ghc-${version}-x86_64-deb10-linux.tar.xz";
sha256 = "5d0b9414b10cfb918453bcd01c5ea7a1824fe95948b08498d6780f20ba247afc";
};
exePathForLibraryCheck = "ghc/stage2/build/tmp/ghc-stage2";
archSpecificLibraries = [
{
nixPackage = gmp;
fileToCheckFor = null;
}
{
nixPackage = ncurses6;
fileToCheckFor = "libtinfo.so.6";
}
{
nixPackage = numactl;
fileToCheckFor = null;
}
];
};
aarch64-linux = {
variantSuffix = "";
src = {
url = "${downloadsUrl}/${version}/ghc-${version}-aarch64-deb10-linux.tar.xz";
sha256 = "cb016344c70a872738a24af60bd15d3b18749087b9905c1b3f1b1549dc01f46d";
};
exePathForLibraryCheck = "ghc/stage2/build/tmp/ghc-stage2";
archSpecificLibraries = [
{
nixPackage = gmp;
fileToCheckFor = null;
}
{
nixPackage = ncurses6;
fileToCheckFor = "libtinfo.so.6";
}
{
nixPackage = numactl;
fileToCheckFor = null;
}
];
};
x86_64-darwin = {
variantSuffix = "";
src = {
url = "${downloadsUrl}/${version}/ghc-${version}-x86_64-apple-darwin.tar.xz";
sha256 = "e1fe990eb987f5c4b03e0396f9c228a10da71769c8a2bc8fadbc1d3b10a0f53a";
};
exePathForLibraryCheck = null; # we don't have a library check for darwin yet
archSpecificLibraries = [
{
nixPackage = gmp;
fileToCheckFor = null;
}
{
nixPackage = ncurses6;
fileToCheckFor = null;
}
{
nixPackage = libiconv;
fileToCheckFor = null;
}
];
isHadrian = true;
};
aarch64-darwin = {
variantSuffix = "";
src = {
url = "${downloadsUrl}/${version}/ghc-${version}-aarch64-apple-darwin.tar.xz";
sha256 = "b1fcab17fe48326d2ff302d70c12bc4cf4d570dfbbce68ab57c719cfec882b05";
};
exePathForLibraryCheck = null; # we don't have a library check for darwin yet
archSpecificLibraries = [
{
nixPackage = gmp;
fileToCheckFor = null;
}
{
nixPackage = ncurses6;
fileToCheckFor = null;
}
{
nixPackage = libiconv;
fileToCheckFor = null;
}
];
isHadrian = true;
};
};
# Binary distributions for the musl libc for the respective system.
musl = {
x86_64-linux = {
variantSuffix = "-musl";
src = {
url = "${downloadsUrl}/${version}/ghc-${version}-x86_64-alpine3.12-linux-gmp.tar.xz";
sha256 = "5bb1e7192c2b9fcff68930dbdc65509d345138e9a43c5d447056a68decc05ec8";
};
exePathForLibraryCheck = "bin/ghc";
archSpecificLibraries = [
{
nixPackage = gmp;
fileToCheckFor = null;
}
{
nixPackage = ncurses6;
fileToCheckFor = "libncursesw.so.6";
}
];
isHadrian = true;
};
};
};
distSetName = if stdenv.hostPlatform.isMusl then "musl" else "defaultLibc";
binDistUsed =
ghcBinDists.${distSetName}.${stdenv.hostPlatform.system}
or (throw "cannot bootstrap GHC on this platform ('${stdenv.hostPlatform.system}' with libc '${distSetName}')");
gmpUsed =
(builtins.head (
builtins.filter (
drv: lib.hasPrefix "gmp" (drv.nixPackage.name or "")
) binDistUsed.archSpecificLibraries
)).nixPackage;
useLLVM = !(import ./common-have-ncg.nix { inherit lib stdenv version; });
libPath = lib.makeLibraryPath (
# Add arch-specific libraries.
map ({ nixPackage, ... }: nixPackage) binDistUsed.archSpecificLibraries
);
libEnvVar = lib.optionalString stdenv.hostPlatform.isDarwin "DY" + "LD_LIBRARY_PATH";
runtimeDeps = [
targetPackages.stdenv.cc
targetPackages.stdenv.cc.bintools
coreutils # for cat
]
++ lib.optionals useLLVM [
# Allow the use of newer LLVM versions; see the script for details.
(replaceVarsWith {
name = "subopt";
src = ./subopt.bash;
dir = "bin";
isExecutable = true;
preBuild = ''
name=opt
'';
replacements = {
inherit (stdenv) shell;
opt = lib.getExe' llvmPackages.llvm "opt";
};
})
(lib.getBin llvmPackages.llvm)
]
# On darwin, we need unwrapped bintools as well (for otool)
++ lib.optionals (stdenv.targetPlatform.linker == "cctools") [
targetPackages.stdenv.cc.bintools.bintools
];
in
stdenv.mkDerivation {
inherit version;
pname = "ghc-binary${binDistUsed.variantSuffix}";
src = fetchurl binDistUsed.src;
nativeBuildInputs = [ perl ];
# Set LD_LIBRARY_PATH or equivalent so that the programs running as part
# of the bindist installer can find the libraries they expect.
# Cannot patchelf beforehand due to relative RPATHs that anticipate
# the final install location.
${libEnvVar} = libPath;
postUnpack =
# Verify our assumptions of which `libtinfo.so` (ncurses) version is used,
# so that we know when ghc bindists upgrade that and we need to update the
# version used in `libPath`.
lib.optionalString (binDistUsed.exePathForLibraryCheck != null)
# Note the `*` glob because some GHCs have a suffix when unpacked, e.g.
# the musl bindist has dir `ghc-VERSION-x86_64-unknown-linux/`.
# As a result, don't shell-quote this glob when splicing the string.
(
let
buildExeGlob = ''ghc-${version}*/"${binDistUsed.exePathForLibraryCheck}"'';
in
lib.concatStringsSep "\n" [
''
shopt -u nullglob
echo "Checking that ghc binary exists in bindist at ${buildExeGlob}"
if ! test -e ${buildExeGlob}; then
echo >&2 "GHC binary ${binDistUsed.exePathForLibraryCheck} could not be found in the bindist build directory (at ${buildExeGlob}) for arch ${stdenv.hostPlatform.system}, please check that ghcBinDists correctly reflect the bindist dependencies!"; exit 1;
fi
''
(lib.concatMapStringsSep "\n" (
{ fileToCheckFor, nixPackage }:
lib.optionalString (fileToCheckFor != null) ''
echo "Checking bindist for ${fileToCheckFor} to ensure that is still used"
if ! readelf -d ${buildExeGlob} | grep "${fileToCheckFor}"; then
echo >&2 "File ${fileToCheckFor} could not be found in ${binDistUsed.exePathForLibraryCheck} for arch ${stdenv.hostPlatform.system}, please check that ghcBinDists correctly reflect the bindist dependencies!"; exit 1;
fi
echo "Checking that the nix package ${nixPackage} contains ${fileToCheckFor}"
if ! test -e "${lib.getLib nixPackage}/lib/${fileToCheckFor}"; then
echo >&2 "Nix package ${nixPackage} did not contain ${fileToCheckFor} for arch ${stdenv.hostPlatform.system}, please check that ghcBinDists correctly reflect the bindist dependencies!"; exit 1;
fi
''
) binDistUsed.archSpecificLibraries)
]
)
# GHC has dtrace probes, which causes ld to try to open /usr/lib/libdtrace.dylib
# during linking
+ lib.optionalString stdenv.hostPlatform.isDarwin ''
export NIX_LDFLAGS+=" -no_dtrace_dof"
# not enough room in the object files for the full path to libiconv :(
for exe in $(find . -type f -executable); do
isScript $exe && continue
ln -fs ${libiconv}/lib/libiconv.dylib $(dirname $exe)/libiconv.dylib
install_name_tool -change /usr/lib/libiconv.2.dylib @executable_path/libiconv.dylib $exe
done
''
+
# Some scripts used during the build need to have their shebangs patched
''
patchShebangs ghc-${version}/utils/
patchShebangs ghc-${version}/configure
test -d ghc-${version}/inplace/bin && \
patchShebangs ghc-${version}/inplace/bin
''
+
# We have to patch the GMP paths for the integer-gmp package.
''
find . -name ghc-bignum.buildinfo \
-exec sed -i "s@extra-lib-dirs: @extra-lib-dirs: ${lib.getLib gmpUsed}/lib@" {} \;
# we need to modify the package db directly for hadrian bindists
find . -name 'ghc-bignum*.conf' \
-exec sed -e '/^[a-z-]*library-dirs/a \ ${lib.getLib gmpUsed}/lib' -i {} \;
''
+ lib.optionalString stdenv.hostPlatform.isDarwin ''
# we need to modify the package db directly for hadrian bindists
# (all darwin bindists are hadrian-based for 9.2.2)
find . -name 'base*.conf' \
-exec sed -e '/^[a-z-]*library-dirs/a \ ${lib.getLib libiconv}/lib' -i {} \;
# To link RTS in the end we also need libffi now
find . -name 'rts*.conf' \
-exec sed -e '/^[a-z-]*library-dirs/a \ ${lib.getLib libffi}/lib' \
-e 's@/.*/Developer/.*/usr/include/ffi@${lib.getDev libffi}/include@' \
-i {} \;
''
+
# Some platforms do HAVE_NUMA so -lnuma requires it in library-dirs in rts/package.conf.in
# FFI_LIB_DIR is a good indication of places it must be needed.
lib.optionalString
(
lib.meta.availableOn stdenv.hostPlatform numactl
&& builtins.any ({ nixPackage, ... }: nixPackage == numactl) binDistUsed.archSpecificLibraries
)
''
find . -name package.conf.in \
-exec sed -i "s@FFI_LIB_DIR@FFI_LIB_DIR ${numactl.out}/lib@g" {} \;
''
+
# Rename needed libraries and binaries, fix interpreter
lib.optionalString stdenv.hostPlatform.isLinux ''
find . -type f -executable -exec patchelf \
--interpreter ${stdenv.cc.bintools.dynamicLinker} {} \;
'';
# fix for `configure: error: Your linker is affected by binutils #16177`
preConfigure = lib.optionalString stdenv.targetPlatform.isAarch32 "LD=ld.gold";
configurePlatforms = [ ];
configureFlags = [
"--with-gmp-includes=${lib.getDev gmpUsed}/include"
# Note `--with-gmp-libraries` does nothing for GHC bindists:
# https://gitlab.haskell.org/ghc/ghc/-/merge_requests/6124
]
++ lib.optional stdenv.hostPlatform.isDarwin "--with-gcc=${./gcc-clang-wrapper.sh}"
# From: https://github.com/NixOS/nixpkgs/pull/43369/commits
++ lib.optional stdenv.hostPlatform.isMusl "--disable-ld-override";
# No building is necessary, but calling make without flags ironically
# calls install-strip ...
dontBuild = true;
# GHC tries to remove xattrs when installing to work around Gatekeeper
# (see https://gitlab.haskell.org/ghc/ghc/-/issues/17418). This step normally
# succeeds in nixpkgs because xattrs are not allowed in the store, but it
# can fail when a file has the `com.apple.provenance` xattr, and it cant be
# modified (such as target of the symlink to `libiconv.dylib`).
# The `com.apple.provenance` xattr is a new feature of macOS as of macOS 13.
# See: https://eclecticlight.co/2023/03/13/ventura-has-changed-app-quarantine-with-a-new-xattr/
makeFlags = lib.optionals stdenv.buildPlatform.isDarwin [ "XATTR=/does-not-exist" ];
# Patch scripts to include runtime dependencies in $PATH.
postInstall = ''
for i in "$out/bin/"*; do
test ! -h "$i" || continue
isScript "$i" || continue
sed -i -e '2i export PATH="${lib.makeBinPath runtimeDeps}:$PATH"' "$i"
done
''
# On Darwin, GHC doesn't install a bundled libffi.so, but instead uses the
# system one (see postUnpack). Due to a bug in Hadrian, the (bundled) libffi
# headers are installed anyways. This problem has been fixed in GHC 9.2:
# https://gitlab.haskell.org/ghc/ghc/-/merge_requests/8189. While the system
# header should shadow the GHC installed ones, remove them to be safe.
+ lib.optionalString (stdenv.hostPlatform.isDarwin && binDistUsed.isHadrian or false) ''
echo Deleting redundant libffi headers:
find "$out" '(' -name ffi.h -or -name ffitarget.h ')' -print -delete
'';
# Apparently necessary for the ghc Alpine (musl) bindist:
# When we strip, and then run the
# patchelf --set-rpath "${libPath}:$(patchelf --print-rpath $p)" $p
# below, running ghc (e.g. during `installCheckPhase)` gives some apparently
# corrupted rpath or whatever makes the loader work on nonsensical strings:
# running install tests
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: : symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: ir6zf6c9f86pfx8sr30n2vjy-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/../lib/x86_64-linux-ghc-8.10.5/libHSexceptions-0.10.4-ghc8.10.5.so: symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: y/lib/ghc-8.10.5/bin/../lib/x86_64-linux-ghc-8.10.5/libHStemplate-haskell-2.16.0.0-ghc8.10.5.so: symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: 8.10.5/libHStemplate-haskell-2.16.0.0-ghc8.10.5.so: symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: <20>: symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: <20>?: symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: 64-linux-ghc-8.10.5/libHSexceptions-0.10.4-ghc8.10.5.so: symbol not found
# This is extremely bogus and should be investigated.
dontStrip = if stdenv.hostPlatform.isMusl then true else false; # `if` for explicitness
# On Linux, use patchelf to modify the executables so that they can
# find editline/gmp.
postFixup =
lib.optionalString (stdenv.hostPlatform.isLinux && !(binDistUsed.isStatic or false)) (
if stdenv.hostPlatform.isAarch64 then
# Keep rpath as small as possible on aarch64 for patchelf#244. All Elfs
# are 2 directories deep from $out/lib, so pooling symlinks there makes
# a short rpath.
''
(cd $out/lib; ln -s ${ncurses6.out}/lib/libtinfo.so.6)
(cd $out/lib; ln -s ${lib.getLib gmpUsed}/lib/libgmp.so.10)
(cd $out/lib; ln -s ${numactl.out}/lib/libnuma.so.1)
for p in $(find "$out/lib" -type f -name "*\.so*"); do
(cd $out/lib; ln -s $p)
done
for p in $(find "$out/lib" -type f -executable); do
if isELF "$p"; then
echo "Patchelfing $p"
patchelf --set-rpath "\$ORIGIN:\$ORIGIN/../.." $p
fi
done
''
else
''
for p in $(find "$out" -type f -executable); do
if isELF "$p"; then
echo "Patchelfing $p"
patchelf --set-rpath "${libPath}:$(patchelf --print-rpath $p)" $p
fi
done
''
)
+ lib.optionalString stdenv.hostPlatform.isDarwin ''
# not enough room in the object files for the full path to libiconv :(
for exe in $(find "$out" -type f -executable); do
isScript $exe && continue
ln -fs ${libiconv}/lib/libiconv.dylib $(dirname $exe)/libiconv.dylib
install_name_tool -change /usr/lib/libiconv.2.dylib @executable_path/libiconv.dylib $exe
done
for file in $(find "$out" -name setup-config); do
substituteInPlace $file --replace /usr/bin/ranlib "$(type -P ranlib)"
done
''
+ lib.optionalString minimal ''
# Remove profiling files
find $out -type f -name '*.p_o' -delete
find $out -type f -name '*.p_hi' -delete
find $out -type f -name '*_p.a' -delete
# `-f` because e.g. musl bindist does not have this file.
rm -f $out/lib/ghc-*/bin/ghc-iserv-prof
# Hydra will redistribute this derivation, so we have to keep the docs for
# legal reasons (retaining the legal notices etc)
# As a last resort we could unpack the docs separately and symlink them in.
# They're in $out/share/{doc,man}.
''
# Recache package db which needs to happen for Hadrian bindists
# where we modify the package db before installing
+ ''
shopt -s nullglob
package_db=("$out"/lib/ghc-*/lib/package.conf.d "$out"/lib/ghc-*/package.conf.d)
"$out/bin/ghc-pkg" --package-db="$package_db" recache
'';
# GHC cannot currently produce outputs that are ready for `-pie` linking.
# Thus, disable `pie` hardening, otherwise `recompile with -fPIE` errors appear.
# See:
# * https://github.com/NixOS/nixpkgs/issues/129247
# * https://gitlab.haskell.org/ghc/ghc/-/issues/19580
hardeningDisable = [ "pie" ];
doInstallCheck = true;
installCheckPhase = ''
# Sanity check, can ghc create executables?
cd $TMP
mkdir test-ghc; cd test-ghc
cat > main.hs << EOF
{-# LANGUAGE TemplateHaskell #-}
module Main where
main = putStrLn \$([|"yes"|])
EOF
env -i $out/bin/ghc --make main.hs || exit 1
echo compilation ok
[ $(./main) == "yes" ]
'';
passthru = {
targetPrefix = "";
enableShared = true;
inherit llvmPackages;
# Our Cabal compiler name
haskellCompilerName = "ghc-${version}";
}
# We duplicate binDistUsed here since we have a sensible default even if no bindist is available,
# this makes sure that getting the `meta` attribute doesn't throw even on unsupported platforms.
// lib.optionalAttrs (ghcBinDists.${distSetName}.${stdenv.hostPlatform.system}.isHadrian or false) {
# Normal GHC derivations expose the hadrian derivation used to build them
# here. In the case of bindists we just make sure that the attribute exists,
# as it is used for checking if a GHC derivation has been built with hadrian.
# The isHadrian mechanism will become obsolete with GHCs that use hadrian
# exclusively, i.e. 9.6 (and 9.4?).
hadrian = null;
};
meta = {
homepage = "http://haskell.org/ghc";
description = "Glasgow Haskell Compiler";
license = lib.licenses.bsd3;
# HACK: since we can't encode the libc / abi in platforms, we need
# to make the platform list dependent on the evaluation platform
# in order to avoid eval errors with musl which supports less
# platforms than the default libcs (i. e. glibc / libSystem).
# This is done for the benefit of Hydra, so `packagePlatforms`
# won't return any platforms that would cause an evaluation
# failure for `pkgsMusl.haskell.compiler.ghc922Binary`, as
# long as the evaluator runs on a platform that supports
# `pkgsMusl`.
platforms = builtins.attrNames ghcBinDists.${distSetName};
teams = [ lib.teams.haskell ];
};
}

View File

@@ -0,0 +1,4 @@
import ./common-hadrian.nix {
version = "9.10.1";
sha256 = "bf386a302d4ee054791ffd51748900f15d71760fd199157922d120cc1f89e2f7";
}

View File

@@ -0,0 +1,4 @@
import ./common-hadrian.nix {
version = "9.10.2";
sha256 = "55fd40a005575ac6b33ea928beda81e8c56ffea354b6ac474ee9f9911f23a8de";
}

View File

@@ -0,0 +1,4 @@
import ./common-hadrian.nix {
version = "9.10.3";
sha256 = "d266864b9e0b7b741abe8c9d6a790d7c01c21cf43a1419839119255878ebc59a";
}

View File

@@ -0,0 +1,4 @@
import ./common-hadrian.nix {
version = "9.12.1";
sha256 = "4a7410bdeec70f75717087b8f94bf5a6598fd61b3a0e1f8501d8f10be1492754";
}

View File

@@ -0,0 +1,4 @@
import ./common-hadrian.nix {
version = "9.12.2";
sha256 = "0e49cd5dde43f348c5716e5de9a5d7a0f8d68d945dc41cf75dfdefe65084f933";
}

View File

@@ -0,0 +1,500 @@
{
lib,
stdenv,
fetchurl,
perl,
gcc,
ncurses5,
ncurses6,
gmp,
libiconv,
numactl,
libffi,
coreutils,
targetPackages,
# minimal = true; will remove files that aren't strictly necessary for
# regular builds and GHC bootstrapping.
# This is "useful" for staying within hydra's output limits for at least the
# aarch64-linux architecture.
minimal ? false,
}:
# Prebuilt only does native
assert stdenv.targetPlatform == stdenv.hostPlatform;
let
downloadsUrl = "https://downloads.haskell.org/ghc";
# Copy sha256 from https://downloads.haskell.org/~ghc/9.2.4/SHA256SUMS
version = "9.2.4";
# Information about available bindists that we use in the build.
#
# # Bindist library checking
#
# The field `archSpecificLibraries` also provides a way for us get notified
# early when the upstream bindist changes its dependencies (e.g. because a
# newer Debian version is used that uses a new `ncurses` version).
#
# Usage:
#
# * You can find the `fileToCheckFor` of libraries by running `readelf -d`
# on the compiler binary (`exePathForLibraryCheck`).
# * To skip library checking for an architecture,
# set `exePathForLibraryCheck = null`.
# * To skip file checking for a specific arch specific library,
# set `fileToCheckFor = null`.
ghcBinDists = {
# Binary distributions for the default libc (e.g. glibc, or libSystem on Darwin)
# nixpkgs uses for the respective system.
defaultLibc = {
i686-linux = {
variantSuffix = "";
src = {
url = "${downloadsUrl}/${version}/ghc-${version}-i386-deb9-linux.tar.xz";
sha256 = "5dc1eb9c65f01b1e5c5693af72af07a4e9e75c6920e620fd598daeefa804487a";
};
exePathForLibraryCheck = "ghc/stage2/build/tmp/ghc-stage2";
archSpecificLibraries = [
{
nixPackage = gmp;
fileToCheckFor = null;
}
# The i686-linux bindist provided by GHC HQ is currently built on Debian 9,
# which link it against `libtinfo.so.5` (ncurses 5).
# Other bindists are linked `libtinfo.so.6` (ncurses 6).
{
nixPackage = ncurses5;
fileToCheckFor = "libtinfo.so.5";
}
];
};
x86_64-linux = {
variantSuffix = "";
src = {
url = "${downloadsUrl}/${version}/ghc-${version}-x86_64-deb10-linux.tar.xz";
sha256 = "a77a91a39d9b0167124b7e97648b2b52973ae0978cb259e0d44f0752a75037cb";
};
exePathForLibraryCheck = "ghc/stage2/build/tmp/ghc-stage2";
archSpecificLibraries = [
{
nixPackage = gmp;
fileToCheckFor = null;
}
{
nixPackage = ncurses6;
fileToCheckFor = "libtinfo.so.6";
}
];
};
aarch64-linux = {
variantSuffix = "";
src = {
url = "${downloadsUrl}/${version}/ghc-${version}-aarch64-deb10-linux.tar.xz";
sha256 = "fc7dbc6bae36ea5ac30b7e9a263b7e5be3b45b0eb3e893ad0bc2c950a61f14ec";
};
exePathForLibraryCheck = "ghc/stage2/build/tmp/ghc-stage2";
archSpecificLibraries = [
{
nixPackage = gmp;
fileToCheckFor = null;
}
{
nixPackage = ncurses6;
fileToCheckFor = "libtinfo.so.6";
}
{
nixPackage = numactl;
fileToCheckFor = null;
}
];
};
x86_64-darwin = {
variantSuffix = "";
src = {
url = "${downloadsUrl}/${version}/ghc-${version}-x86_64-apple-darwin.tar.xz";
sha256 = "f2e8366fd3754dd9388510792aba2d2abecb1c2f7f1e5555f6065c3c5e2ffec4";
};
exePathForLibraryCheck = null; # we don't have a library check for darwin yet
archSpecificLibraries = [
{
nixPackage = gmp;
fileToCheckFor = null;
}
{
nixPackage = ncurses6;
fileToCheckFor = null;
}
{
nixPackage = libiconv;
fileToCheckFor = null;
}
];
isHadrian = true;
};
aarch64-darwin = {
variantSuffix = "";
src = {
url = "${downloadsUrl}/${version}/ghc-${version}-aarch64-apple-darwin.tar.xz";
sha256 = "8cf8408544a1a43adf1bbbb0dd6b074efadffc68bfa1a792947c52e825171224";
};
exePathForLibraryCheck = null; # we don't have a library check for darwin yet
archSpecificLibraries = [
{
nixPackage = gmp;
fileToCheckFor = null;
}
{
nixPackage = ncurses6;
fileToCheckFor = null;
}
{
nixPackage = libiconv;
fileToCheckFor = null;
}
];
isHadrian = true;
};
};
# Binary distributions for the musl libc for the respective system.
musl = {
x86_64-linux = {
variantSuffix = "-musl";
src = {
url = "${downloadsUrl}/${version}/ghc-${version}-x86_64-alpine3.12-linux-gmp.tar.xz";
sha256 = "026348947d30a156b84de5d6afeaa48fdcb2795b47954cd8341db00d3263a481";
};
isStatic = true;
isHadrian = true;
# We can't check the RPATH for statically linked executable
exePathForLibraryCheck = null;
archSpecificLibraries = [
{
nixPackage = gmp.override { withStatic = true; };
fileToCheckFor = null;
}
];
};
};
};
distSetName = if stdenv.hostPlatform.isMusl then "musl" else "defaultLibc";
binDistUsed =
ghcBinDists.${distSetName}.${stdenv.hostPlatform.system}
or (throw "cannot bootstrap GHC on this platform ('${stdenv.hostPlatform.system}' with libc '${distSetName}')");
gmpUsed =
(builtins.head (
builtins.filter (
drv: lib.hasPrefix "gmp" (drv.nixPackage.name or "")
) binDistUsed.archSpecificLibraries
)).nixPackage;
libPath = lib.makeLibraryPath (
# Add arch-specific libraries.
map ({ nixPackage, ... }: nixPackage) binDistUsed.archSpecificLibraries
);
libEnvVar = lib.optionalString stdenv.hostPlatform.isDarwin "DY" + "LD_LIBRARY_PATH";
runtimeDeps = [
targetPackages.stdenv.cc
targetPackages.stdenv.cc.bintools
coreutils # for cat
]
# On darwin, we need unwrapped bintools as well (for otool)
++ lib.optionals (stdenv.targetPlatform.linker == "cctools") [
targetPackages.stdenv.cc.bintools.bintools
];
in
stdenv.mkDerivation {
inherit version;
pname = "ghc-binary${binDistUsed.variantSuffix}";
src = fetchurl binDistUsed.src;
nativeBuildInputs = [ perl ];
# Set LD_LIBRARY_PATH or equivalent so that the programs running as part
# of the bindist installer can find the libraries they expect.
# Cannot patchelf beforehand due to relative RPATHs that anticipate
# the final install location.
${libEnvVar} = libPath;
postUnpack =
# Verify our assumptions of which `libtinfo.so` (ncurses) version is used,
# so that we know when ghc bindists upgrade that and we need to update the
# version used in `libPath`.
lib.optionalString (binDistUsed.exePathForLibraryCheck != null)
# Note the `*` glob because some GHCs have a suffix when unpacked, e.g.
# the musl bindist has dir `ghc-VERSION-x86_64-unknown-linux/`.
# As a result, don't shell-quote this glob when splicing the string.
(
let
buildExeGlob = ''ghc-${version}*/"${binDistUsed.exePathForLibraryCheck}"'';
in
lib.concatStringsSep "\n" [
''
shopt -u nullglob
echo "Checking that ghc binary exists in bindist at ${buildExeGlob}"
if ! test -e ${buildExeGlob}; then
echo >&2 "GHC binary ${binDistUsed.exePathForLibraryCheck} could not be found in the bindist build directory (at ${buildExeGlob}) for arch ${stdenv.hostPlatform.system}, please check that ghcBinDists correctly reflect the bindist dependencies!"; exit 1;
fi
''
(lib.concatMapStringsSep "\n" (
{ fileToCheckFor, nixPackage }:
lib.optionalString (fileToCheckFor != null) ''
echo "Checking bindist for ${fileToCheckFor} to ensure that is still used"
if ! readelf -d ${buildExeGlob} | grep "${fileToCheckFor}"; then
echo >&2 "File ${fileToCheckFor} could not be found in ${binDistUsed.exePathForLibraryCheck} for arch ${stdenv.hostPlatform.system}, please check that ghcBinDists correctly reflect the bindist dependencies!"; exit 1;
fi
echo "Checking that the nix package ${nixPackage} contains ${fileToCheckFor}"
if ! test -e "${lib.getLib nixPackage}/lib/${fileToCheckFor}"; then
echo >&2 "Nix package ${nixPackage} did not contain ${fileToCheckFor} for arch ${stdenv.hostPlatform.system}, please check that ghcBinDists correctly reflect the bindist dependencies!"; exit 1;
fi
''
) binDistUsed.archSpecificLibraries)
]
)
# GHC has dtrace probes, which causes ld to try to open /usr/lib/libdtrace.dylib
# during linking
+ lib.optionalString stdenv.hostPlatform.isDarwin ''
export NIX_LDFLAGS+=" -no_dtrace_dof"
# not enough room in the object files for the full path to libiconv :(
for exe in $(find . -type f -executable); do
isScript $exe && continue
ln -fs ${libiconv}/lib/libiconv.dylib $(dirname $exe)/libiconv.dylib
install_name_tool -change /usr/lib/libiconv.2.dylib @executable_path/libiconv.dylib $exe
done
''
+
# Some scripts used during the build need to have their shebangs patched
''
patchShebangs ghc-${version}/utils/
patchShebangs ghc-${version}/configure
test -d ghc-${version}/inplace/bin && \
patchShebangs ghc-${version}/inplace/bin
''
+
# We have to patch the GMP paths for the integer-gmp package.
''
find . -name ghc-bignum.buildinfo \
-exec sed -i "s@extra-lib-dirs: @extra-lib-dirs: ${lib.getLib gmpUsed}/lib@" {} \;
# we need to modify the package db directly for hadrian bindists
find . -name 'ghc-bignum*.conf' \
-exec sed -e '/^[a-z-]*library-dirs/a \ ${lib.getLib gmpUsed}/lib' -i {} \;
''
+ lib.optionalString stdenv.hostPlatform.isDarwin ''
# we need to modify the package db directly for hadrian bindists
# (all darwin bindists are hadrian-based for 9.2.2)
find . -name 'base*.conf' \
-exec sed -e '/^[a-z-]*library-dirs/a \ ${lib.getLib libiconv}/lib' -i {} \;
# To link RTS in the end we also need libffi now
find . -name 'rts*.conf' \
-exec sed -e '/^[a-z-]*library-dirs/a \ ${lib.getLib libffi}/lib' \
-e 's@/Library/Developer/.*/usr/include/ffi@${lib.getDev libffi}/include@' \
-i {} \;
''
+
# Some platforms do HAVE_NUMA so -lnuma requires it in library-dirs in rts/package.conf.in
# FFI_LIB_DIR is a good indication of places it must be needed.
lib.optionalString
(
lib.meta.availableOn stdenv.hostPlatform numactl
&& builtins.any ({ nixPackage, ... }: nixPackage == numactl) binDistUsed.archSpecificLibraries
)
''
find . -name package.conf.in \
-exec sed -i "s@FFI_LIB_DIR@FFI_LIB_DIR ${numactl.out}/lib@g" {} \;
''
+
# Rename needed libraries and binaries, fix interpreter
lib.optionalString stdenv.hostPlatform.isLinux ''
find . -type f -executable -exec patchelf \
--interpreter ${stdenv.cc.bintools.dynamicLinker} {} \;
'';
# fix for `configure: error: Your linker is affected by binutils #16177`
preConfigure = lib.optionalString stdenv.targetPlatform.isAarch32 "LD=ld.gold";
configurePlatforms = [ ];
configureFlags = [
"--with-gmp-includes=${lib.getDev gmpUsed}/include"
# Note `--with-gmp-libraries` does nothing for GHC bindists:
# https://gitlab.haskell.org/ghc/ghc/-/merge_requests/6124
]
++ lib.optional stdenv.hostPlatform.isDarwin "--with-gcc=${./gcc-clang-wrapper.sh}"
# From: https://github.com/NixOS/nixpkgs/pull/43369/commits
++ lib.optional stdenv.hostPlatform.isMusl "--disable-ld-override";
# No building is necessary, but calling make without flags ironically
# calls install-strip ...
dontBuild = true;
# GHC tries to remove xattrs when installing to work around Gatekeeper
# (see https://gitlab.haskell.org/ghc/ghc/-/issues/17418). This step normally
# succeeds in nixpkgs because xattrs are not allowed in the store, but it
# can fail when a file has the `com.apple.provenance` xattr, and it cant be
# modified (such as target of the symlink to `libiconv.dylib`).
# The `com.apple.provenance` xattr is a new feature of macOS as of macOS 13.
# See: https://eclecticlight.co/2023/03/13/ventura-has-changed-app-quarantine-with-a-new-xattr/
makeFlags = lib.optionals stdenv.buildPlatform.isDarwin [ "XATTR=/does-not-exist" ];
# Patch scripts to include runtime dependencies in $PATH.
postInstall = ''
for i in "$out/bin/"*; do
test ! -h "$i" || continue
isScript "$i" || continue
sed -i -e '2i export PATH="${lib.makeBinPath runtimeDeps}:$PATH"' "$i"
done
'';
# Apparently necessary for the ghc Alpine (musl) bindist:
# When we strip, and then run the
# patchelf --set-rpath "${libPath}:$(patchelf --print-rpath $p)" $p
# below, running ghc (e.g. during `installCheckPhase)` gives some apparently
# corrupted rpath or whatever makes the loader work on nonsensical strings:
# running install tests
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: : symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: ir6zf6c9f86pfx8sr30n2vjy-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/../lib/x86_64-linux-ghc-8.10.5/libHSexceptions-0.10.4-ghc8.10.5.so: symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: y/lib/ghc-8.10.5/bin/../lib/x86_64-linux-ghc-8.10.5/libHStemplate-haskell-2.16.0.0-ghc8.10.5.so: symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: 8.10.5/libHStemplate-haskell-2.16.0.0-ghc8.10.5.so: symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: <20>: symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: <20>?: symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: 64-linux-ghc-8.10.5/libHSexceptions-0.10.4-ghc8.10.5.so: symbol not found
# This is extremely bogus and should be investigated.
dontStrip = if stdenv.hostPlatform.isMusl then true else false; # `if` for explicitness
# On Linux, use patchelf to modify the executables so that they can
# find editline/gmp.
postFixup =
lib.optionalString (stdenv.hostPlatform.isLinux && !(binDistUsed.isStatic or false)) (
if stdenv.hostPlatform.isAarch64 then
# Keep rpath as small as possible on aarch64 for patchelf#244. All Elfs
# are 2 directories deep from $out/lib, so pooling symlinks there makes
# a short rpath.
''
(cd $out/lib; ln -s ${ncurses6.out}/lib/libtinfo.so.6)
(cd $out/lib; ln -s ${lib.getLib gmpUsed}/lib/libgmp.so.10)
(cd $out/lib; ln -s ${numactl.out}/lib/libnuma.so.1)
for p in $(find "$out/lib" -type f -name "*\.so*"); do
(cd $out/lib; ln -s $p)
done
for p in $(find "$out/lib" -type f -executable); do
if isELF "$p"; then
echo "Patchelfing $p"
patchelf --set-rpath "\$ORIGIN:\$ORIGIN/../.." $p
fi
done
''
else
''
for p in $(find "$out" -type f -executable); do
if isELF "$p"; then
echo "Patchelfing $p"
patchelf --set-rpath "${libPath}:$(patchelf --print-rpath $p)" $p
fi
done
''
)
+ lib.optionalString stdenv.hostPlatform.isDarwin ''
# not enough room in the object files for the full path to libiconv :(
for exe in $(find "$out" -type f -executable); do
isScript $exe && continue
ln -fs ${libiconv}/lib/libiconv.dylib $(dirname $exe)/libiconv.dylib
install_name_tool -change /usr/lib/libiconv.2.dylib @executable_path/libiconv.dylib $exe
done
for file in $(find "$out" -name setup-config); do
substituteInPlace $file --replace /usr/bin/ranlib "$(type -P ranlib)"
done
''
+ lib.optionalString minimal ''
# Remove profiling files
find $out -type f -name '*.p_o' -delete
find $out -type f -name '*.p_hi' -delete
find $out -type f -name '*_p.a' -delete
# `-f` because e.g. musl bindist does not have this file.
rm -f $out/lib/ghc-*/bin/ghc-iserv-prof
# Hydra will redistribute this derivation, so we have to keep the docs for
# legal reasons (retaining the legal notices etc)
# As a last resort we could unpack the docs separately and symlink them in.
# They're in $out/share/{doc,man}.
''
# Recache package db which needs to happen for Hadrian bindists
# where we modify the package db before installing
+ ''
shopt -s nullglob
package_db=("$out"/lib/ghc-*/lib/package.conf.d "$out"/lib/ghc-*/package.conf.d)
"$out/bin/ghc-pkg" --package-db="$package_db" recache
'';
# GHC cannot currently produce outputs that are ready for `-pie` linking.
# Thus, disable `pie` hardening, otherwise `recompile with -fPIE` errors appear.
# See:
# * https://github.com/NixOS/nixpkgs/issues/129247
# * https://gitlab.haskell.org/ghc/ghc/-/issues/19580
hardeningDisable = [ "pie" ];
doInstallCheck = true;
installCheckPhase = ''
# Sanity check, can ghc create executables?
cd $TMP
mkdir test-ghc; cd test-ghc
cat > main.hs << EOF
{-# LANGUAGE TemplateHaskell #-}
module Main where
main = putStrLn \$([|"yes"|])
EOF
env -i $out/bin/ghc --make main.hs || exit 1
echo compilation ok
[ $(./main) == "yes" ]
'';
passthru = {
targetPrefix = "";
enableShared = true;
llvmPackages = null;
# Our Cabal compiler name
haskellCompilerName = "ghc-${version}";
}
# We duplicate binDistUsed here since we have a sensible default even if no bindist is available,
# this makes sure that getting the `meta` attribute doesn't throw even on unsupported platforms.
// lib.optionalAttrs (ghcBinDists.${distSetName}.${stdenv.hostPlatform.system}.isHadrian or false) {
# Normal GHC derivations expose the hadrian derivation used to build them
# here. In the case of bindists we just make sure that the attribute exists,
# as it is used for checking if a GHC derivation has been built with hadrian.
# The isHadrian mechanism will become obsolete with GHCs that use hadrian
# exclusively, i.e. 9.6 (and 9.4?).
hadrian = null;
};
meta = {
homepage = "http://haskell.org/ghc";
description = "Glasgow Haskell Compiler";
license = lib.licenses.bsd3;
# HACK: since we can't encode the libc / abi in platforms, we need
# to make the platform list dependent on the evaluation platform
# in order to avoid eval errors with musl which supports less
# platforms than the default libcs (i. e. glibc / libSystem).
# This is done for the benefit of Hydra, so `packagePlatforms`
# won't return any platforms that would cause an evaluation
# failure for `pkgsMusl.haskell.compiler.ghc922Binary`, as
# long as the evaluator runs on a platform that supports
# `pkgsMusl`.
platforms = builtins.attrNames ghcBinDists.${distSetName};
teams = [ lib.teams.haskell ];
broken = !(import ./common-have-ncg.nix { inherit lib stdenv version; });
};
}

View File

@@ -0,0 +1,6 @@
# DO NOT port this expression to hadrian. It is not possible to build a GHC
# cross compiler with 9.4.* and hadrian.
import ./common-make-native-bignum.nix {
version = "9.4.8";
sha256 = "0bf407eb67fe3e3c24b0f4c8dea8cb63e07f63ca0f76cf2058565143507ab85e";
}

View File

@@ -0,0 +1,474 @@
{
lib,
stdenv,
fetchurl,
perl,
gcc,
ncurses5,
ncurses6,
gmp,
libiconv,
numactl,
libffi,
coreutils,
targetPackages,
# minimal = true; will remove files that aren't strictly necessary for
# regular builds and GHC bootstrapping.
# This is "useful" for staying within hydra's output limits for at least the
# aarch64-linux architecture.
minimal ? false,
}:
# Prebuilt only does native
assert stdenv.targetPlatform == stdenv.hostPlatform;
let
downloadsUrl = "https://downloads.haskell.org/ghc";
# Copy sha256 from https://downloads.haskell.org/~ghc/9.6.3/SHA256SUMS
version = "9.6.3";
# Information about available bindists that we use in the build.
#
# # Bindist library checking
#
# The field `archSpecificLibraries` also provides a way for us get notified
# early when the upstream bindist changes its dependencies (e.g. because a
# newer Debian version is used that uses a new `ncurses` version).
#
# Usage:
#
# * You can find the `fileToCheckFor` of libraries by running `readelf -d`
# on the compiler binary (`exePathForLibraryCheck`).
# * To skip library checking for an architecture,
# set `exePathForLibraryCheck = null`.
# * To skip file checking for a specific arch specific library,
# set `fileToCheckFor = null`.
ghcBinDists = {
# Binary distributions for the default libc (e.g. glibc, or libSystem on Darwin)
# nixpkgs uses for the respective system.
defaultLibc = {
i686-linux = {
variantSuffix = "";
src = {
url = "${downloadsUrl}/${version}/ghc-${version}-i386-deb9-linux.tar.xz";
sha256 = "58be26f8b8f6b5bd8baf5c32abb03e2c4621646b2142fab10e5c7de5af5c50f8";
};
exePathForLibraryCheck = "bin/ghc";
archSpecificLibraries = [
{
nixPackage = gmp;
fileToCheckFor = null;
}
# The i686-linux bindist provided by GHC HQ is currently built on Debian 9,
# which link it against `libtinfo.so.5` (ncurses 5).
# Other bindists are linked `libtinfo.so.6` (ncurses 6).
{
nixPackage = ncurses5;
fileToCheckFor = "libtinfo.so.5";
}
];
};
x86_64-linux = {
variantSuffix = "";
src = {
url = "${downloadsUrl}/${version}/ghc-${version}-x86_64-deb11-linux.tar.xz";
sha256 = "c4c0124857265926f1cf22a09d950d7ba989ff94053a4ddf3dcdab5359f4cab7";
};
exePathForLibraryCheck = "bin/ghc";
archSpecificLibraries = [
{
nixPackage = gmp;
fileToCheckFor = null;
}
{
nixPackage = ncurses6;
fileToCheckFor = "libtinfo.so.6";
}
];
};
aarch64-linux = {
variantSuffix = "";
src = {
url = "${downloadsUrl}/${version}/ghc-${version}-aarch64-deb10-linux.tar.xz";
sha256 = "03c389859319f09452081310fc13af7525063ea8930830ef76be2a14b312271e";
};
exePathForLibraryCheck = "bin/ghc";
archSpecificLibraries = [
{
nixPackage = gmp;
fileToCheckFor = null;
}
{
nixPackage = ncurses6;
fileToCheckFor = "libtinfo.so.6";
}
{
nixPackage = numactl;
fileToCheckFor = null;
}
];
};
x86_64-darwin = {
variantSuffix = "";
src = {
url = "${downloadsUrl}/${version}/ghc-${version}-x86_64-apple-darwin.tar.xz";
sha256 = "dde46118ab8388fb1066312c097123e93b1dcf6ae366e3370f88ea456382c9db";
};
exePathForLibraryCheck = null; # we don't have a library check for darwin yet
archSpecificLibraries = [
{
nixPackage = gmp;
fileToCheckFor = null;
}
{
nixPackage = ncurses6;
fileToCheckFor = null;
}
{
nixPackage = libiconv;
fileToCheckFor = null;
}
];
};
aarch64-darwin = {
variantSuffix = "";
src = {
url = "${downloadsUrl}/${version}/ghc-${version}-aarch64-apple-darwin.tar.xz";
sha256 = "e1cdf458926b2eaf52d2a8287d99a965040ff9051171f5c3b7467049cf0eb213";
};
exePathForLibraryCheck = null; # we don't have a library check for darwin yet
archSpecificLibraries = [
{
nixPackage = gmp;
fileToCheckFor = null;
}
{
nixPackage = ncurses6;
fileToCheckFor = null;
}
{
nixPackage = libiconv;
fileToCheckFor = null;
}
];
};
};
# Binary distributions for the musl libc for the respective system.
musl = {
x86_64-linux = {
variantSuffix = "-musl";
src = {
url = "${downloadsUrl}/${version}/ghc-${version}-x86_64-alpine3_12-linux.tar.xz";
sha256 = "8f457af0aa40127049c11134c8793f64351a446e87da1f8ec256e1279b5ab61f";
};
exePathForLibraryCheck = "bin/ghc";
archSpecificLibraries = [
{
nixPackage = gmp;
fileToCheckFor = null;
}
{
nixPackage = ncurses6;
fileToCheckFor = "libncursesw.so.6";
}
];
};
};
};
distSetName = if stdenv.hostPlatform.isMusl then "musl" else "defaultLibc";
binDistUsed =
ghcBinDists.${distSetName}.${stdenv.hostPlatform.system}
or (throw "cannot bootstrap GHC on this platform ('${stdenv.hostPlatform.system}' with libc '${distSetName}')");
gmpUsed =
(builtins.head (
builtins.filter (
drv: lib.hasPrefix "gmp" (drv.nixPackage.name or "")
) binDistUsed.archSpecificLibraries
)).nixPackage;
libPath = lib.makeLibraryPath (
# Add arch-specific libraries.
map ({ nixPackage, ... }: nixPackage) binDistUsed.archSpecificLibraries
);
libEnvVar = lib.optionalString stdenv.hostPlatform.isDarwin "DY" + "LD_LIBRARY_PATH";
runtimeDeps = [
targetPackages.stdenv.cc
targetPackages.stdenv.cc.bintools
coreutils # for cat
]
# On darwin, we need unwrapped bintools as well (for otool)
++ lib.optionals (stdenv.targetPlatform.linker == "cctools") [
targetPackages.stdenv.cc.bintools.bintools
];
in
stdenv.mkDerivation {
inherit version;
pname = "ghc-binary${binDistUsed.variantSuffix}";
src = fetchurl binDistUsed.src;
nativeBuildInputs = [ perl ];
# Set LD_LIBRARY_PATH or equivalent so that the programs running as part
# of the bindist installer can find the libraries they expect.
# Cannot patchelf beforehand due to relative RPATHs that anticipate
# the final install location.
${libEnvVar} = libPath;
postUnpack =
# Verify our assumptions of which `libtinfo.so` (ncurses) version is used,
# so that we know when ghc bindists upgrade that and we need to update the
# version used in `libPath`.
lib.optionalString (binDistUsed.exePathForLibraryCheck != null)
# Note the `*` glob because some GHCs have a suffix when unpacked, e.g.
# the musl bindist has dir `ghc-VERSION-x86_64-unknown-linux/`.
# As a result, don't shell-quote this glob when splicing the string.
(
let
buildExeGlob = ''ghc-${version}*/"${binDistUsed.exePathForLibraryCheck}"'';
in
lib.concatStringsSep "\n" [
''
shopt -u nullglob
echo "Checking that ghc binary exists in bindist at ${buildExeGlob}"
if ! test -e ${buildExeGlob}; then
echo >&2 "GHC binary ${binDistUsed.exePathForLibraryCheck} could not be found in the bindist build directory (at ${buildExeGlob}) for arch ${stdenv.hostPlatform.system}, please check that ghcBinDists correctly reflect the bindist dependencies!"; exit 1;
fi
''
(lib.concatMapStringsSep "\n" (
{ fileToCheckFor, nixPackage }:
lib.optionalString (fileToCheckFor != null) ''
echo "Checking bindist for ${fileToCheckFor} to ensure that is still used"
if ! readelf -d ${buildExeGlob} | grep "${fileToCheckFor}"; then
echo >&2 "File ${fileToCheckFor} could not be found in ${binDistUsed.exePathForLibraryCheck} for arch ${stdenv.hostPlatform.system}, please check that ghcBinDists correctly reflect the bindist dependencies!"; exit 1;
fi
echo "Checking that the nix package ${nixPackage} contains ${fileToCheckFor}"
if ! test -e "${lib.getLib nixPackage}/lib/${fileToCheckFor}"; then
echo >&2 "Nix package ${nixPackage} did not contain ${fileToCheckFor} for arch ${stdenv.hostPlatform.system}, please check that ghcBinDists correctly reflect the bindist dependencies!"; exit 1;
fi
''
) binDistUsed.archSpecificLibraries)
]
)
# GHC has dtrace probes, which causes ld to try to open /usr/lib/libdtrace.dylib
# during linking
+ lib.optionalString stdenv.hostPlatform.isDarwin ''
export NIX_LDFLAGS+=" -no_dtrace_dof"
# not enough room in the object files for the full path to libiconv :(
for exe in $(find . -type f -executable); do
isMachO $exe || continue
ln -fs ${libiconv}/lib/libiconv.dylib $(dirname $exe)/libiconv.dylib
install_name_tool -change /usr/lib/libiconv.2.dylib @executable_path/libiconv.dylib $exe
done
''
# We have to patch the GMP paths for the ghc-bignum package, for hadrian by
# modifying the package-db directly
+ ''
find . -name 'ghc-bignum*.conf' \
-exec sed -e '/^[a-z-]*library-dirs/a \ ${lib.getLib gmpUsed}/lib' -i {} \;
''
# Similar for iconv and libffi on darwin
+ lib.optionalString stdenv.hostPlatform.isDarwin ''
find . -name 'base*.conf' \
-exec sed -e '/^[a-z-]*library-dirs/a \ ${lib.getLib libiconv}/lib' -i {} \;
# To link RTS in the end we also need libffi now
find . -name 'rts*.conf' \
-exec sed -e '/^[a-z-]*library-dirs/a \ ${lib.getLib libffi}/lib' \
-e 's@/Library/Developer/.*/usr/include/ffi@${lib.getDev libffi}/include@' \
-i {} \;
''
+
# Some platforms do HAVE_NUMA so -lnuma requires it in library-dirs in rts/package.conf.in
# FFI_LIB_DIR is a good indication of places it must be needed.
lib.optionalString
(
lib.meta.availableOn stdenv.hostPlatform numactl
&& builtins.any ({ nixPackage, ... }: nixPackage == numactl) binDistUsed.archSpecificLibraries
)
''
find . -name package.conf.in \
-exec sed -i "s@FFI_LIB_DIR@FFI_LIB_DIR ${numactl.out}/lib@g" {} \;
''
+
# Rename needed libraries and binaries, fix interpreter
lib.optionalString stdenv.hostPlatform.isLinux ''
find . -type f -executable -exec patchelf \
--interpreter ${stdenv.cc.bintools.dynamicLinker} {} \;
'';
# fix for `configure: error: Your linker is affected by binutils #16177`
preConfigure = lib.optionalString stdenv.targetPlatform.isAarch32 "LD=ld.gold";
# GHC has a patched config.sub and bindists' platforms should always work
dontUpdateAutotoolsGnuConfigScripts = true;
configurePlatforms = [ ];
configureFlags =
lib.optional stdenv.hostPlatform.isDarwin "--with-gcc=${./gcc-clang-wrapper.sh}"
# From: https://github.com/NixOS/nixpkgs/pull/43369/commits
++ lib.optional stdenv.hostPlatform.isMusl "--disable-ld-override";
# No building is necessary, but calling make without flags ironically
# calls install-strip ...
dontBuild = true;
# GHC tries to remove xattrs when installing to work around Gatekeeper
# (see https://gitlab.haskell.org/ghc/ghc/-/issues/17418). This step normally
# succeeds in nixpkgs because xattrs are not allowed in the store, but it
# can fail when a file has the `com.apple.provenance` xattr, and it cant be
# modified (such as target of the symlink to `libiconv.dylib`).
# The `com.apple.provenance` xattr is a new feature of macOS as of macOS 13.
# See: https://eclecticlight.co/2023/03/13/ventura-has-changed-app-quarantine-with-a-new-xattr/
makeFlags = lib.optionals stdenv.buildPlatform.isDarwin [ "XATTR=/does-not-exist" ];
# Patch scripts to include runtime dependencies in $PATH.
postInstall = ''
for i in "$out/bin/"*; do
test ! -h "$i" || continue
isScript "$i" || continue
sed -i -e '2i export PATH="${lib.makeBinPath runtimeDeps}:$PATH"' "$i"
done
''
+ lib.optionalString stdenv.targetPlatform.isDarwin ''
# Work around building with binary GHC on Darwin due to GHCs use of `ar -L` when it
# detects `llvm-ar` even though the resulting archives are not supported by ld64.
# https://gitlab.haskell.org/ghc/ghc/-/issues/23188
# https://github.com/haskell/cabal/issues/8882
sed -i -e 's/,("ar supports -L", "YES")/,("ar supports -L", "NO")/' "$out/lib/ghc-${version}/lib/settings"
'';
# Apparently necessary for the ghc Alpine (musl) bindist:
# When we strip, and then run the
# patchelf --set-rpath "${libPath}:$(patchelf --print-rpath $p)" $p
# below, running ghc (e.g. during `installCheckPhase)` gives some apparently
# corrupted rpath or whatever makes the loader work on nonsensical strings:
# running install tests
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: : symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: ir6zf6c9f86pfx8sr30n2vjy-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/../lib/x86_64-linux-ghc-8.10.5/libHSexceptions-0.10.4-ghc8.10.5.so: symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: y/lib/ghc-8.10.5/bin/../lib/x86_64-linux-ghc-8.10.5/libHStemplate-haskell-2.16.0.0-ghc8.10.5.so: symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: 8.10.5/libHStemplate-haskell-2.16.0.0-ghc8.10.5.so: symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: <20>: symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: <20>?: symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: 64-linux-ghc-8.10.5/libHSexceptions-0.10.4-ghc8.10.5.so: symbol not found
# This is extremely bogus and should be investigated.
dontStrip = if stdenv.hostPlatform.isMusl then true else false; # `if` for explicitness
# On Linux, use patchelf to modify the executables so that they can
# find editline/gmp.
postFixup =
lib.optionalString (stdenv.hostPlatform.isLinux && !(binDistUsed.isStatic or false)) (
if stdenv.hostPlatform.isAarch64 then
# Keep rpath as small as possible on aarch64 for patchelf#244. All Elfs
# are 2 directories deep from $out/lib, so pooling symlinks there makes
# a short rpath.
''
(cd $out/lib; ln -s ${ncurses6.out}/lib/libtinfo.so.6)
(cd $out/lib; ln -s ${lib.getLib gmpUsed}/lib/libgmp.so.10)
(cd $out/lib; ln -s ${numactl.out}/lib/libnuma.so.1)
for p in $(find "$out/lib" -type f -name "*\.so*"); do
(cd $out/lib; ln -s $p)
done
for p in $(find "$out/lib" -type f -executable); do
if isELF "$p"; then
echo "Patchelfing $p"
patchelf --set-rpath "\$ORIGIN:\$ORIGIN/../.." $p
fi
done
''
else
''
for p in $(find "$out" -type f -executable); do
if isELF "$p"; then
echo "Patchelfing $p"
patchelf --set-rpath "${libPath}:$(patchelf --print-rpath $p)" $p
fi
done
''
)
+ lib.optionalString stdenv.hostPlatform.isDarwin ''
# not enough room in the object files for the full path to libiconv :(
for exe in $(find "$out" -type f -executable); do
isMachO $exe || continue
ln -fs ${libiconv}/lib/libiconv.dylib $(dirname $exe)/libiconv.dylib
install_name_tool -change /usr/lib/libiconv.2.dylib @executable_path/libiconv.dylib $exe
done
for file in $(find "$out" -name setup-config); do
substituteInPlace $file --replace /usr/bin/ranlib "$(type -P ranlib)"
done
''
# Recache package db which needs to happen for Hadrian bindists
# where we modify the package db before installing
+ ''
package_db=("$out"/lib/ghc-*/lib/package.conf.d)
"$out/bin/ghc-pkg" --package-db="$package_db" recache
'';
# GHC cannot currently produce outputs that are ready for `-pie` linking.
# Thus, disable `pie` hardening, otherwise `recompile with -fPIE` errors appear.
# See:
# * https://github.com/NixOS/nixpkgs/issues/129247
# * https://gitlab.haskell.org/ghc/ghc/-/issues/19580
hardeningDisable = [ "pie" ];
doInstallCheck = true;
installCheckPhase = ''
# Sanity check, can ghc create executables?
cd $TMP
mkdir test-ghc; cd test-ghc
cat > main.hs << EOF
{-# LANGUAGE TemplateHaskell #-}
module Main where
main = putStrLn \$([|"yes"|])
EOF
env -i $out/bin/ghc --make main.hs || exit 1
echo compilation ok
[ $(./main) == "yes" ]
'';
passthru = {
targetPrefix = "";
enableShared = true;
llvmPackages = null;
# Our Cabal compiler name
haskellCompilerName = "ghc-${version}";
# Normal GHC derivations expose the hadrian derivation used to build them
# here. In the case of bindists we just make sure that the attribute exists,
# as it is used for checking if a GHC derivation has been built with hadrian.
hadrian = null;
};
meta = {
homepage = "http://haskell.org/ghc";
description = "Glasgow Haskell Compiler";
license = lib.licenses.bsd3;
# HACK: since we can't encode the libc / abi in platforms, we need
# to make the platform list dependent on the evaluation platform
# in order to avoid eval errors with musl which supports less
# platforms than the default libcs (i. e. glibc / libSystem).
# This is done for the benefit of Hydra, so `packagePlatforms`
# won't return any platforms that would cause an evaluation
# failure for `pkgsMusl.haskell.compiler.ghc922Binary`, as
# long as the evaluator runs on a platform that supports
# `pkgsMusl`.
platforms = builtins.attrNames ghcBinDists.${distSetName};
teams = [ lib.teams.haskell ];
broken = !(import ./common-have-ncg.nix { inherit lib stdenv version; });
};
}

View File

@@ -0,0 +1,4 @@
import ./common-hadrian.nix {
version = "9.6.7";
sha256 = "sha256-0FO/bOHViKdc/oyTFiaUhunY+4nc32/ZKDb6Lj32EwU=";
}

View File

@@ -0,0 +1,489 @@
{
lib,
stdenv,
fetchurl,
perl,
gcc,
ncurses6,
gmp,
libiconv,
numactl,
libffi,
coreutils,
targetPackages,
# minimal = true; will remove files that aren't strictly necessary for
# regular builds and GHC bootstrapping.
# This is "useful" for staying within hydra's output limits for at least the
# aarch64-linux architecture.
minimal ? false,
}:
# Prebuilt only does native
assert stdenv.targetPlatform == stdenv.hostPlatform;
let
downloadsUrl = "https://downloads.haskell.org/ghc";
# Copy sha256 from https://downloads.haskell.org/~ghc/9.8.4/SHA256SUMS
version = "9.8.4";
# Information about available bindists that we use in the build.
#
# # Bindist library checking
#
# The field `archSpecificLibraries` also provides a way for us get notified
# early when the upstream bindist changes its dependencies (e.g. because a
# newer Debian version is used that uses a new `ncurses` version).
#
# Usage:
#
# * You can find the `fileToCheckFor` of libraries by running `readelf -d`
# on the compiler binary (`exePathForLibraryCheck`).
# * To skip library checking for an architecture,
# set `exePathForLibraryCheck = null`.
# * To skip file checking for a specific arch specific library,
# set `fileToCheckFor = null`.
ghcBinDists = {
# Binary distributions for the default libc (e.g. glibc, or libSystem on Darwin)
# nixpkgs uses for the respective system.
defaultLibc = {
i686-linux = {
variantSuffix = "";
src = {
url = "${downloadsUrl}/${version}/ghc-${version}-i386-deb10-linux.tar.xz";
sha256 = "e5efce16c654d5e702986258a87dd9531e1722b8051823c8ce1150ce3c5899ae";
};
exePathForLibraryCheck = "bin/ghc";
archSpecificLibraries = [
{
nixPackage = gmp;
fileToCheckFor = null;
}
{
nixPackage = ncurses6;
fileToCheckFor = "libtinfo.so.6";
}
];
};
x86_64-linux = {
variantSuffix = "";
src = {
url = "${downloadsUrl}/${version}/ghc-${version}-x86_64-deb11-linux.tar.xz";
sha256 = "af151db8682b8c763f5a44f960f65453d794c95b60f151abc82dbdefcbe6f8ad";
};
exePathForLibraryCheck = "bin/ghc";
archSpecificLibraries = [
{
nixPackage = gmp;
fileToCheckFor = null;
}
{
nixPackage = ncurses6;
fileToCheckFor = "libtinfo.so.6";
}
];
};
aarch64-linux = {
variantSuffix = "";
src = {
url = "${downloadsUrl}/${version}/ghc-${version}-aarch64-deb11-linux.tar.xz";
sha256 = "310204daf2df6ad16087be94b3498ca414a0953b29e94e8ec8eb4a5c9bf603d3";
};
exePathForLibraryCheck = "bin/ghc";
archSpecificLibraries = [
{
nixPackage = gmp;
fileToCheckFor = null;
}
{
nixPackage = ncurses6;
fileToCheckFor = "libtinfo.so.6";
}
{
nixPackage = numactl;
fileToCheckFor = null;
}
];
};
x86_64-darwin = {
variantSuffix = "";
src = {
url = "${downloadsUrl}/${version}/ghc-${version}-x86_64-apple-darwin.tar.xz";
sha256 = "de7baacfb1513ab0e4ccf8911045cceee84bc8a4e39b89bd975ed3135e5f7d96";
};
exePathForLibraryCheck = null; # we don't have a library check for darwin yet
archSpecificLibraries = [
{
nixPackage = gmp;
fileToCheckFor = null;
}
{
nixPackage = ncurses6;
fileToCheckFor = null;
}
{
nixPackage = libiconv;
fileToCheckFor = null;
}
];
};
aarch64-darwin = {
variantSuffix = "";
src = {
url = "${downloadsUrl}/${version}/ghc-${version}-aarch64-apple-darwin.tar.xz";
sha256 = "e2f12a922754fd28511512875bf6d9eb3e0cce7fc963a7266f6e1661aeabd7ed";
};
exePathForLibraryCheck = null; # we don't have a library check for darwin yet
archSpecificLibraries = [
{
nixPackage = gmp;
fileToCheckFor = null;
}
{
nixPackage = ncurses6;
fileToCheckFor = null;
}
{
nixPackage = libiconv;
fileToCheckFor = null;
}
];
};
};
# Binary distributions for the musl libc for the respective system.
musl = {
aarch64-linux = {
variantSuffix = "-musl";
src = {
url = "${downloadsUrl}/${version}/ghc-${version}-aarch64-alpine3_18-linux.tar.xz";
sha256 = "b5c86a0cda0bd62d5eeeb52b1937c3bd00c70cd67dd74226ce787d5c429a4e62";
};
exePathForLibraryCheck = "bin/ghc";
archSpecificLibraries = [
{
nixPackage = gmp;
fileToCheckFor = null;
}
{
nixPackage = ncurses6;
fileToCheckFor = "libncursesw.so.6";
}
];
};
x86_64-linux = {
variantSuffix = "-musl";
src = {
url = "${downloadsUrl}/${version}/ghc-${version}-x86_64-alpine3_12-linux.tar.xz";
sha256 = "e34bb16e8387509adc96a3d98b4a444bab425d12864c38a3629f2860b4bec2e7";
};
exePathForLibraryCheck = "bin/ghc";
archSpecificLibraries = [
{
nixPackage = gmp;
fileToCheckFor = null;
}
{
nixPackage = ncurses6;
fileToCheckFor = "libncursesw.so.6";
}
];
};
};
};
distSetName = if stdenv.hostPlatform.isMusl then "musl" else "defaultLibc";
binDistUsed =
ghcBinDists.${distSetName}.${stdenv.hostPlatform.system}
or (throw "cannot bootstrap GHC on this platform ('${stdenv.hostPlatform.system}' with libc '${distSetName}')");
gmpUsed =
(builtins.head (
builtins.filter (
drv: lib.hasPrefix "gmp" (drv.nixPackage.name or "")
) binDistUsed.archSpecificLibraries
)).nixPackage;
libPath = lib.makeLibraryPath (
# Add arch-specific libraries.
map ({ nixPackage, ... }: nixPackage) binDistUsed.archSpecificLibraries
);
libEnvVar = lib.optionalString stdenv.hostPlatform.isDarwin "DY" + "LD_LIBRARY_PATH";
runtimeDeps = [
targetPackages.stdenv.cc
targetPackages.stdenv.cc.bintools
coreutils # for cat
]
# On darwin, we need unwrapped bintools as well (for otool)
++ lib.optionals (stdenv.targetPlatform.linker == "cctools") [
targetPackages.stdenv.cc.bintools.bintools
];
in
stdenv.mkDerivation {
inherit version;
pname = "ghc-binary${binDistUsed.variantSuffix}";
src = fetchurl binDistUsed.src;
nativeBuildInputs = [ perl ];
# Set LD_LIBRARY_PATH or equivalent so that the programs running as part
# of the bindist installer can find the libraries they expect.
# Cannot patchelf beforehand due to relative RPATHs that anticipate
# the final install location.
${libEnvVar} = libPath;
postUnpack =
# Verify our assumptions of which `libtinfo.so` (ncurses) version is used,
# so that we know when ghc bindists upgrade that and we need to update the
# version used in `libPath`.
lib.optionalString (binDistUsed.exePathForLibraryCheck != null)
# Note the `*` glob because some GHCs have a suffix when unpacked, e.g.
# the musl bindist has dir `ghc-VERSION-x86_64-unknown-linux/`.
# As a result, don't shell-quote this glob when splicing the string.
(
let
buildExeGlob = ''ghc-${version}*/"${binDistUsed.exePathForLibraryCheck}"'';
in
lib.concatStringsSep "\n" [
''
shopt -u nullglob
echo "Checking that ghc binary exists in bindist at ${buildExeGlob}"
if ! test -e ${buildExeGlob}; then
echo >&2 "GHC binary ${binDistUsed.exePathForLibraryCheck} could not be found in the bindist build directory (at ${buildExeGlob}) for arch ${stdenv.hostPlatform.system}, please check that ghcBinDists correctly reflect the bindist dependencies!"; exit 1;
fi
''
(lib.concatMapStringsSep "\n" (
{ fileToCheckFor, nixPackage }:
lib.optionalString (fileToCheckFor != null) ''
echo "Checking bindist for ${fileToCheckFor} to ensure that is still used"
if ! readelf -d ${buildExeGlob} | grep "${fileToCheckFor}"; then
echo >&2 "File ${fileToCheckFor} could not be found in ${binDistUsed.exePathForLibraryCheck} for arch ${stdenv.hostPlatform.system}, please check that ghcBinDists correctly reflect the bindist dependencies!"; exit 1;
fi
echo "Checking that the nix package ${nixPackage} contains ${fileToCheckFor}"
if ! test -e "${lib.getLib nixPackage}/lib/${fileToCheckFor}"; then
echo >&2 "Nix package ${nixPackage} did not contain ${fileToCheckFor} for arch ${stdenv.hostPlatform.system}, please check that ghcBinDists correctly reflect the bindist dependencies!"; exit 1;
fi
''
) binDistUsed.archSpecificLibraries)
]
)
# GHC has dtrace probes, which causes ld to try to open /usr/lib/libdtrace.dylib
# during linking
+ lib.optionalString stdenv.hostPlatform.isDarwin ''
export NIX_LDFLAGS+=" -no_dtrace_dof"
# not enough room in the object files for the full path to libiconv :(
for exe in $(find . -type f -executable); do
isMachO $exe || continue
ln -fs ${libiconv}/lib/libiconv.dylib $(dirname $exe)/libiconv.dylib
install_name_tool -change /usr/lib/libiconv.2.dylib @executable_path/libiconv.dylib $exe
done
''
# We have to patch the GMP paths for the ghc-bignum package, for hadrian by
# modifying the package-db directly
+ ''
find . -name 'ghc-bignum*.conf' \
-exec sed -e '/^[a-z-]*library-dirs/a \ ${lib.getLib gmpUsed}/lib' -i {} \;
''
# Similar for iconv and libffi on darwin
+ lib.optionalString stdenv.hostPlatform.isDarwin ''
find . -name 'base*.conf' \
-exec sed -e '/^[a-z-]*library-dirs/a \ ${lib.getLib libiconv}/lib' -i {} \;
# To link RTS in the end we also need libffi now
find . -name 'rts*.conf' \
-exec sed -e '/^[a-z-]*library-dirs/a \ ${lib.getLib libffi}/lib' \
-e 's@/Library/Developer/.*/usr/include/ffi@${lib.getDev libffi}/include@' \
-i {} \;
''
+
# Some platforms do HAVE_NUMA so -lnuma requires it in library-dirs in rts/package.conf.in
# FFI_LIB_DIR is a good indication of places it must be needed.
lib.optionalString
(
lib.meta.availableOn stdenv.hostPlatform numactl
&& builtins.any ({ nixPackage, ... }: nixPackage == numactl) binDistUsed.archSpecificLibraries
)
''
find . -name package.conf.in \
-exec sed -i "s@FFI_LIB_DIR@FFI_LIB_DIR ${numactl.out}/lib@g" {} \;
''
+
# Rename needed libraries and binaries, fix interpreter
lib.optionalString stdenv.hostPlatform.isLinux ''
find . -type f -executable -exec patchelf \
--interpreter ${stdenv.cc.bintools.dynamicLinker} {} \;
'';
# fix for `configure: error: Your linker is affected by binutils #16177`
preConfigure = lib.optionalString stdenv.targetPlatform.isAarch32 "LD=ld.gold";
# GHC has a patched config.sub and bindists' platforms should always work
dontUpdateAutotoolsGnuConfigScripts = true;
configurePlatforms = [ ];
configureFlags =
lib.optional stdenv.hostPlatform.isDarwin "--with-gcc=${./gcc-clang-wrapper.sh}"
# From: https://github.com/NixOS/nixpkgs/pull/43369/commits
++ lib.optional stdenv.hostPlatform.isMusl "--disable-ld-override";
# No building is necessary, but calling make without flags ironically
# calls install-strip ...
dontBuild = true;
# Patch scripts to include runtime dependencies in $PATH.
postInstall = ''
for i in "$out/bin/"*; do
test ! -h "$i" || continue
isScript "$i" || continue
sed -i -e '2i export PATH="${lib.makeBinPath runtimeDeps}:$PATH"' "$i"
done
''
+ lib.optionalString stdenv.targetPlatform.isDarwin ''
# Work around building with binary GHC on Darwin due to GHCs use of `ar -L` when it
# detects `llvm-ar` even though the resulting archives are not supported by ld64.
# https://gitlab.haskell.org/ghc/ghc/-/issues/23188
# https://github.com/haskell/cabal/issues/8882
sed -i -e 's/,("ar supports -L", "YES")/,("ar supports -L", "NO")/' "$out/lib/ghc-${version}/lib/settings"
'';
# Apparently necessary for the ghc Alpine (musl) bindist:
# When we strip, and then run the
# patchelf --set-rpath "${libPath}:$(patchelf --print-rpath $p)" $p
# below, running ghc (e.g. during `installCheckPhase)` gives some apparently
# corrupted rpath or whatever makes the loader work on nonsensical strings:
# running install tests
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: : symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: ir6zf6c9f86pfx8sr30n2vjy-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/../lib/x86_64-linux-ghc-8.10.5/libHSexceptions-0.10.4-ghc8.10.5.so: symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: y/lib/ghc-8.10.5/bin/../lib/x86_64-linux-ghc-8.10.5/libHStemplate-haskell-2.16.0.0-ghc8.10.5.so: symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: 8.10.5/libHStemplate-haskell-2.16.0.0-ghc8.10.5.so: symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: <20>: symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: <20>?: symbol not found
# Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: 64-linux-ghc-8.10.5/libHSexceptions-0.10.4-ghc8.10.5.so: symbol not found
# This is extremely bogus and should be investigated.
dontStrip = if stdenv.hostPlatform.isMusl then true else false; # `if` for explicitness
# On Linux, use patchelf to modify the executables so that they can
# find editline/gmp.
postFixup =
lib.optionalString (stdenv.hostPlatform.isLinux && !(binDistUsed.isStatic or false)) (
if stdenv.hostPlatform.isAarch64 then
# Keep rpath as small as possible on aarch64 for patchelf#244. All Elfs
# are 2 directories deep from $out/lib, so pooling symlinks there makes
# a short rpath.
''
(cd $out/lib; ln -s ${lib.getLib gmpUsed}/lib/libgmp.so.10)
''
+ (
if stdenv.hostPlatform.isMusl then
''
(cd $out/lib; ln -s ${ncurses6.out}/lib/libncursesw.so.6)
''
else
''
(cd $out/lib; ln -s ${ncurses6.out}/lib/libtinfo.so.6)
''
)
+ ''
for p in $(find "$out/lib" -type f -name "*\.so*"); do
(cd $out/lib; ln -s $p)
done
for p in $(find "$out/lib" -type f -executable); do
if isELF "$p"; then
echo "Patchelfing $p"
patchelf --set-rpath "\$ORIGIN:\$ORIGIN/../.." $p
fi
done
''
else
''
for p in $(find "$out" -type f -executable); do
if isELF "$p"; then
echo "Patchelfing $p"
patchelf --set-rpath "${libPath}:$(patchelf --print-rpath $p)" $p
fi
done
''
)
+ lib.optionalString stdenv.hostPlatform.isDarwin ''
# not enough room in the object files for the full path to libiconv :(
for exe in $(find "$out" -type f -executable); do
isMachO $exe || continue
ln -fs ${libiconv}/lib/libiconv.dylib $(dirname $exe)/libiconv.dylib
install_name_tool -change /usr/lib/libiconv.2.dylib @executable_path/libiconv.dylib $exe
done
for file in $(find "$out" -name setup-config); do
substituteInPlace $file --replace /usr/bin/ranlib "$(type -P ranlib)"
done
''
# Recache package db which needs to happen for Hadrian bindists
# where we modify the package db before installing
+ ''
package_db=("$out"/lib/ghc-*/lib/package.conf.d)
"$out/bin/ghc-pkg" --package-db="$package_db" recache
'';
# GHC cannot currently produce outputs that are ready for `-pie` linking.
# Thus, disable `pie` hardening, otherwise `recompile with -fPIE` errors appear.
# See:
# * https://github.com/NixOS/nixpkgs/issues/129247
# * https://gitlab.haskell.org/ghc/ghc/-/issues/19580
hardeningDisable = [ "pie" ];
doInstallCheck = true;
installCheckPhase = ''
# Sanity check, can ghc create executables?
cd $TMP
mkdir test-ghc; cd test-ghc
cat > main.hs << EOF
{-# LANGUAGE TemplateHaskell #-}
module Main where
main = putStrLn \$([|"yes"|])
EOF
env -i $out/bin/ghc --make main.hs || exit 1
echo compilation ok
[ $(./main) == "yes" ]
'';
passthru = {
targetPrefix = "";
enableShared = true;
llvmPackages = null;
# Our Cabal compiler name
haskellCompilerName = "ghc-${version}";
# Normal GHC derivations expose the hadrian derivation used to build them
# here. In the case of bindists we just make sure that the attribute exists,
# as it is used for checking if a GHC derivation has been built with hadrian.
hadrian = null;
};
meta = {
homepage = "http://haskell.org/ghc";
description = "Glasgow Haskell Compiler";
license = lib.licenses.bsd3;
# HACK: since we can't encode the libc / abi in platforms, we need
# to make the platform list dependent on the evaluation platform
# in order to avoid eval errors with musl which supports less
# platforms than the default libcs (i. e. glibc / libSystem).
# This is done for the benefit of Hydra, so `packagePlatforms`
# won't return any platforms that would cause an evaluation
# failure for `pkgsMusl.haskell.compiler.ghc922Binary`, as
# long as the evaluator runs on a platform that supports
# `pkgsMusl`.
platforms = builtins.attrNames ghcBinDists.${distSetName};
maintainers = lib.teams.haskell.members;
broken = !(import ./common-have-ncg.nix { inherit lib stdenv version; });
};
}

View File

@@ -0,0 +1,4 @@
import ./common-hadrian.nix {
version = "9.8.4";
sha256 = "17e8188f3c8a5c2f73fb4e35d01032e8dc258835ec876d52c8ad8ee3d24b2fc5";
}

View File

@@ -0,0 +1,596 @@
This patch is based on https://github.com/sternenseemann/cabal/compare/Cabal-v3.12.0.0..e836ef53c1f80bf99a70f9c4ee5976e9f3830215
and has been postprocessed with `filterdiff --strip=1 --addoldprefix=a/libraries/Cabal/ --addnewprefix=b/libraries/Cabal/`.
Reasoning and explanation of the patch can be found in the comment in the diff for PathsModule.hs below.
diffCabal/src/Distribution/Simple/Build/PathsModule.hs b/Cabal/src/Distribution/Simple/Build/PathsModule.hs
index 892e5bd38..391f5b130 100644
--- a/libraries/Cabal/Cabal/src/Distribution/Simple/Build/PathsModule.hs
+++ b/libraries/Cabal/Cabal/src/Distribution/Simple/Build/PathsModule.hs
@@ -51,6 +51,7 @@ generatePathsModule pkg_descr lbi clbi =
, Z.zIsWindows = isWindows
, Z.zIsI386 = buildArch == I386
, Z.zIsX8664 = buildArch == X86_64
+ , Z.zOr = (||)
, Z.zNot = not
, Z.zManglePkgName = showPkgName
, Z.zPrefix = show flat_prefix
@@ -60,8 +61,110 @@ generatePathsModule pkg_descr lbi clbi =
, Z.zDatadir = zDatadir
, Z.zLibexecdir = zLibexecdir
, Z.zSysconfdir = zSysconfdir
+ , -- Sadly we can't be cleverer about this we can't have literals in the template
+ Z.zShouldEmitDataDir = shouldEmit "DataDir"
+ , Z.zShouldEmitLibDir = shouldEmit "LibDir"
+ , Z.zShouldEmitDynLibDir = shouldEmit "DynLibDir"
+ , Z.zShouldEmitLibexecDir = shouldEmit "LibexecDir"
+ , Z.zShouldEmitSysconfDir = shouldEmit "SysconfDir"
+ , Z.zWarning = zWarning
+ , Z.zShouldEmitWarning = zShouldEmitWarning
}
where
+ -- GHC's NCG backend for aarch64-darwin does not support link-time dead code
+ -- elimination to the extent that NCG does for other targets. Consequently,
+ -- we struggle with unnecessarily retained store path references due to the
+ -- use of `Paths_*` modules even if `getLibDir` is not used, it'll end up
+ -- in the final library or executables we build.
+ --
+ -- When using a different output for the executables and library, this
+ -- becomes more sinister: The library will contain a reference to the bin
+ -- output and itself due to `getLibDir` and `getBinDir`, but the executables
+ -- will do so, too. Either due to linking dynamically or because the library
+ -- is linked statically into the executable and retains those references.
+ -- Since Nix disallows cyclical references between two outputs, it becomes
+ -- impossible to use the `Paths_*` module and a separate `bin` output for
+ -- aarch64-darwin.
+ --
+ -- The solution we have resorted to for now, is to trim the `Paths_*` module
+ -- dynamically depending on what references *could* be used without causing
+ -- a cyclical reference. That has the effect that any code that would not
+ -- cause a cyclical reference with dead code elimination will compile and
+ -- work for aarch64-darwin. If the code would use a `get*Dir` function that
+ -- has been omitted, this would indicate that the code would have caused a
+ -- cyclical reference anyways.
+ --
+ -- The logic for this makes some pretty big assumptions about installation
+ -- prefixes that probably only hold fully in nixpkgs with
+ -- `haskellPackages.mkDerivation`. Simple uses outside nixpkgs that have
+ -- everything below the same prefix should continue to work as expected,
+ -- though.
+ --
+ -- We assume the following:
+ --
+ -- - flat_prefix is `$out`.
+ -- - flat_libdir etc. are always below `$out`.
+ --
+ -- Since in the normal case due to static linking `$bin` and `$out` will
+ -- have the same references in libraries/executables, we need to either
+ -- prevent usage of `getBinDir` or `getLibDir` to break the cycle in case
+ -- `flat_bindir` is not below `$out`. We have decided to always allow usage
+ -- of `getBinDir`, so `getLibDir` gets dropped if a separate `bin` output is
+ -- used. This has the simple reason that `$out` which contains `flat_libdir`
+ -- tends to be quite big we would like to have a `bin` output that doesn't
+ -- require keeping that around.
+ pathEmittable :: FilePath -> Bool
+ pathEmittable p
+ -- If the executable installation target is below `$out` the reference
+ -- cycle is within a single output (since libs are installed to `$out`)
+ -- and thus unproblematic. We can use any and all `get*Dir` functions.
+ | flat_prefix `isPrefixOf` flat_bindir = True
+ -- Otherwise, we need to disallow all `get*Dir` functions that would cause
+ -- a reference to `$out` which contains the libraries that would in turn
+ -- reference `$bin`. This always include `flat_libdir` and friends, but
+ -- can also include `flat_datadir` if no separate output for data files is
+ -- used.
+ | otherwise = not (flat_prefix `isPrefixOf` p)
+
+ -- This list maps the "name" of the directory to whether we want to include
+ -- it in the `Paths_*` module or not. `shouldEmit` performs a lookup in this.
+ dirs :: [(String, Bool)]
+ dirs =
+ map
+ (\(name, path) -> (name, pathEmittable path))
+ [ ("LibDir", flat_libdir)
+ , ("DynLibDir", flat_dynlibdir)
+ , ("DataDir", flat_datadir)
+ , ("LibexecDir", flat_libexecdir)
+ , ("SysconfDir", flat_sysconfdir)
+ ]
+
+ shouldEmit :: String -> Bool
+ shouldEmit name =
+ case lookup name dirs of
+ Just b -> b
+ Nothing -> error "panic! BUG in Cabal Paths_ patch for aarch64-darwin, report this at https://github.com/nixos/nixpkgs/issues"
+
+ -- This is a comma separated list of all functions that have been emitted.
+ -- This is included in a GHC warning which will be attached to the `Paths_*`
+ -- module in case we are dropping any `get*Dir` functions that would
+ -- normally exist.
+ --
+ -- TODO: getDataFileName is not accounted for at the moment.
+ omittedFunctions :: String
+ omittedFunctions =
+ intercalate ", " $
+ map (("get" ++) . fst) $
+ filter (not . snd) dirs
+
+ zWarning :: String
+ zWarning =
+ show $
+ "The following functions have been omitted by a nixpkgs-specific patch to Cabal: "
+ ++ omittedFunctions
+ zShouldEmitWarning :: Bool
+ zShouldEmitWarning = any (not . snd) dirs
+
supports_cpp = supports_language_pragma
supports_rebindable_syntax = ghc_newer_than (mkVersion [7, 0, 1])
supports_language_pragma = ghc_newer_than (mkVersion [6, 6, 1])
diffCabal/src/Distribution/Simple/Build/PathsModule/Z.hs b/Cabal/src/Distribution/Simple/Build/PathsModule/Z.hs
index 25c924720..a8278675e 100644
--- a/libraries/Cabal/Cabal/src/Distribution/Simple/Build/PathsModule/Z.hs
+++ b/libraries/Cabal/Cabal/src/Distribution/Simple/Build/PathsModule/Z.hs
@@ -19,6 +19,14 @@ data Z
zDatadir :: FilePath,
zLibexecdir :: FilePath,
zSysconfdir :: FilePath,
+ zShouldEmitLibDir :: Bool,
+ zShouldEmitDynLibDir :: Bool,
+ zShouldEmitLibexecDir :: Bool,
+ zShouldEmitDataDir :: Bool,
+ zShouldEmitSysconfDir :: Bool,
+ zShouldEmitWarning :: Bool,
+ zWarning :: String,
+ zOr :: (Bool -> Bool -> Bool),
zNot :: (Bool -> Bool),
zManglePkgName :: (PackageName -> String)}
deriving Generic
@@ -54,10 +62,51 @@ render z_root = execWriter $ do
tell "{-# OPTIONS_GHC -w #-}\n"
tell "module Paths_"
tell (zManglePkgName z_root (zPackageName z_root))
- tell " (\n"
+ tell "\n"
+ tell " "
+ if (zShouldEmitWarning z_root)
+ then do
+ tell "{-# WARNING "
+ tell (zWarning z_root)
+ tell " #-}"
+ return ()
+ else do
+ return ()
+ tell "\n"
+ tell " (\n"
tell " version,\n"
- tell " getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir,\n"
- tell " getDataFileName, getSysconfDir\n"
+ tell " getBinDir,\n"
+ if (zOr z_root (zNot z_root (zAbsolute z_root)) (zShouldEmitLibDir z_root))
+ then do
+ tell " getLibDir,\n"
+ return ()
+ else do
+ return ()
+ if (zOr z_root (zNot z_root (zAbsolute z_root)) (zShouldEmitDynLibDir z_root))
+ then do
+ tell " getDynLibDir,\n"
+ return ()
+ else do
+ return ()
+ if (zOr z_root (zNot z_root (zAbsolute z_root)) (zShouldEmitLibexecDir z_root))
+ then do
+ tell " getLibexecDir,\n"
+ return ()
+ else do
+ return ()
+ if (zOr z_root (zNot z_root (zAbsolute z_root)) (zShouldEmitDataDir z_root))
+ then do
+ tell " getDataFileName,\n"
+ tell " getDataDir,\n"
+ return ()
+ else do
+ return ()
+ if (zOr z_root (zNot z_root (zAbsolute z_root)) (zShouldEmitSysconfDir z_root))
+ then do
+ tell " getSysconfDir\n"
+ return ()
+ else do
+ return ()
tell " ) where\n"
tell "\n"
if (zNot z_root (zAbsolute z_root))
@@ -106,12 +155,15 @@ render z_root = execWriter $ do
tell (zVersionDigits z_root)
tell " []\n"
tell "\n"
- tell "getDataFileName :: FilePath -> IO FilePath\n"
- tell "getDataFileName name = do\n"
- tell " dir <- getDataDir\n"
- tell " return (dir `joinFileName` name)\n"
- tell "\n"
- tell "getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir, getSysconfDir :: IO FilePath\n"
+ if (zOr z_root (zNot z_root (zAbsolute z_root)) (zShouldEmitDataDir z_root))
+ then do
+ tell "getDataFileName :: FilePath -> IO FilePath\n"
+ tell "getDataFileName name = do\n"
+ tell " dir <- getDataDir\n"
+ tell " return (dir `joinFileName` name)\n"
+ return ()
+ else do
+ return ()
tell "\n"
let
z_var0_function_defs = do
@@ -139,6 +191,7 @@ render z_root = execWriter $ do
tell "\n"
if (zRelocatable z_root)
then do
+ tell "\n"
tell "\n"
tell "getPrefixDirReloc :: FilePath -> IO FilePath\n"
tell "getPrefixDirReloc dirRel = do\n"
@@ -148,31 +201,37 @@ render z_root = execWriter $ do
tell (zBindir z_root)
tell ") `joinFileName` dirRel)\n"
tell "\n"
+ tell "getBinDir :: IO FilePath\n"
tell "getBinDir = catchIO (getEnv \""
tell (zManglePkgName z_root (zPackageName z_root))
tell "_bindir\") (\\_ -> getPrefixDirReloc $ "
tell (zBindir z_root)
tell ")\n"
+ tell "getLibDir :: IO FilePath\n"
tell "getLibDir = catchIO (getEnv \""
tell (zManglePkgName z_root (zPackageName z_root))
tell "_libdir\") (\\_ -> getPrefixDirReloc $ "
tell (zLibdir z_root)
tell ")\n"
+ tell "getDynLibDir :: IO FilePath\n"
tell "getDynLibDir = catchIO (getEnv \""
tell (zManglePkgName z_root (zPackageName z_root))
tell "_dynlibdir\") (\\_ -> getPrefixDirReloc $ "
tell (zDynlibdir z_root)
tell ")\n"
+ tell "getDataDir :: IO FilePath\n"
tell "getDataDir = catchIO (getEnv \""
tell (zManglePkgName z_root (zPackageName z_root))
tell "_datadir\") (\\_ -> getPrefixDirReloc $ "
tell (zDatadir z_root)
tell ")\n"
+ tell "getLibexecDir :: IO FilePath\n"
tell "getLibexecDir = catchIO (getEnv \""
tell (zManglePkgName z_root (zPackageName z_root))
tell "_libexecdir\") (\\_ -> getPrefixDirReloc $ "
tell (zLibexecdir z_root)
tell ")\n"
+ tell "getSysconfDir :: IO FilePath\n"
tell "getSysconfDir = catchIO (getEnv \""
tell (zManglePkgName z_root (zPackageName z_root))
tell "_sysconfdir\") (\\_ -> getPrefixDirReloc $ "
@@ -186,72 +245,119 @@ render z_root = execWriter $ do
if (zAbsolute z_root)
then do
tell "\n"
- tell "bindir, libdir, dynlibdir, datadir, libexecdir, sysconfdir :: FilePath\n"
+ tell "bindir :: FilePath\n"
tell "bindir = "
tell (zBindir z_root)
tell "\n"
- tell "libdir = "
- tell (zLibdir z_root)
- tell "\n"
- tell "dynlibdir = "
- tell (zDynlibdir z_root)
+ tell "getBinDir :: IO FilePath\n"
+ tell "getBinDir = catchIO (getEnv \""
+ tell (zManglePkgName z_root (zPackageName z_root))
+ tell "_bindir\") (\\_ -> return bindir)\n"
tell "\n"
- tell "datadir = "
- tell (zDatadir z_root)
+ if (zShouldEmitLibDir z_root)
+ then do
+ tell "libdir :: FilePath\n"
+ tell "libdir = "
+ tell (zLibdir z_root)
+ tell "\n"
+ tell "getLibDir :: IO FilePath\n"
+ tell "getLibDir = catchIO (getEnv \""
+ tell (zManglePkgName z_root (zPackageName z_root))
+ tell "_libdir\") (\\_ -> return libdir)\n"
+ return ()
+ else do
+ return ()
tell "\n"
- tell "libexecdir = "
- tell (zLibexecdir z_root)
+ if (zShouldEmitDynLibDir z_root)
+ then do
+ tell "dynlibdir :: FilePath\n"
+ tell "dynlibdir = "
+ tell (zDynlibdir z_root)
+ tell "\n"
+ tell "getDynLibDir :: IO FilePath\n"
+ tell "getDynLibDir = catchIO (getEnv \""
+ tell (zManglePkgName z_root (zPackageName z_root))
+ tell "_dynlibdir\") (\\_ -> return dynlibdir)\n"
+ return ()
+ else do
+ return ()
tell "\n"
- tell "sysconfdir = "
- tell (zSysconfdir z_root)
+ if (zShouldEmitDataDir z_root)
+ then do
+ tell "datadir :: FilePath\n"
+ tell "datadir = "
+ tell (zDatadir z_root)
+ tell "\n"
+ tell "getDataDir :: IO FilePath\n"
+ tell "getDataDir = catchIO (getEnv \""
+ tell (zManglePkgName z_root (zPackageName z_root))
+ tell "_datadir\") (\\_ -> return datadir)\n"
+ return ()
+ else do
+ return ()
tell "\n"
+ if (zShouldEmitLibexecDir z_root)
+ then do
+ tell "libexecdir :: FilePath\n"
+ tell "libexecdir = "
+ tell (zLibexecdir z_root)
+ tell "\n"
+ tell "getLibexecDir :: IO FilePath\n"
+ tell "getLibexecDir = catchIO (getEnv \""
+ tell (zManglePkgName z_root (zPackageName z_root))
+ tell "_libexecdir\") (\\_ -> return libexecdir)\n"
+ return ()
+ else do
+ return ()
tell "\n"
- tell "getBinDir = catchIO (getEnv \""
- tell (zManglePkgName z_root (zPackageName z_root))
- tell "_bindir\") (\\_ -> return bindir)\n"
- tell "getLibDir = catchIO (getEnv \""
- tell (zManglePkgName z_root (zPackageName z_root))
- tell "_libdir\") (\\_ -> return libdir)\n"
- tell "getDynLibDir = catchIO (getEnv \""
- tell (zManglePkgName z_root (zPackageName z_root))
- tell "_dynlibdir\") (\\_ -> return dynlibdir)\n"
- tell "getDataDir = catchIO (getEnv \""
- tell (zManglePkgName z_root (zPackageName z_root))
- tell "_datadir\") (\\_ -> return datadir)\n"
- tell "getLibexecDir = catchIO (getEnv \""
- tell (zManglePkgName z_root (zPackageName z_root))
- tell "_libexecdir\") (\\_ -> return libexecdir)\n"
- tell "getSysconfDir = catchIO (getEnv \""
- tell (zManglePkgName z_root (zPackageName z_root))
- tell "_sysconfdir\") (\\_ -> return sysconfdir)\n"
+ if (zShouldEmitSysconfDir z_root)
+ then do
+ tell "sysconfdir :: FilePath\n"
+ tell "sysconfdir = "
+ tell (zSysconfdir z_root)
+ tell "\n"
+ tell "getSysconfDir :: IO FilePath\n"
+ tell "getSysconfDir = catchIO (getEnv \""
+ tell (zManglePkgName z_root (zPackageName z_root))
+ tell "_sysconfdir\") (\\_ -> return sysconfdir)\n"
+ return ()
+ else do
+ return ()
tell "\n"
return ()
else do
if (zIsWindows z_root)
then do
+ tell "\n"
tell "\n"
tell "prefix :: FilePath\n"
tell "prefix = "
tell (zPrefix z_root)
tell "\n"
tell "\n"
+ tell "getBinDir :: IO FilePath\n"
tell "getBinDir = getPrefixDirRel $ "
tell (zBindir z_root)
tell "\n"
+ tell "getLibDir :: IO FilePath\n"
tell "getLibDir = "
tell (zLibdir z_root)
tell "\n"
+ tell "getDynLibDir :: IO FilePath\n"
tell "getDynLibDir = "
tell (zDynlibdir z_root)
tell "\n"
+ tell "getDataDir :: IO FilePath\n"
tell "getDataDir = catchIO (getEnv \""
tell (zManglePkgName z_root (zPackageName z_root))
tell "_datadir\") (\\_ -> "
tell (zDatadir z_root)
tell ")\n"
+ tell "getLibexecDir :: IO FilePath\n"
tell "getLibexecDir = "
tell (zLibexecdir z_root)
tell "\n"
+ tell "getSysconfDir :: IO FilePath\n"
tell "getSysconfDir = "
tell (zSysconfdir z_root)
tell "\n"
diffcabal-dev-scripts/src/GenPathsModule.hs b/cabal-dev-scripts/src/GenPathsModule.hs
index 46ef779e2..e9f5e099f 100644
--- a/libraries/Cabal/cabal-dev-scripts/src/GenPathsModule.hs
+++ b/libraries/Cabal/cabal-dev-scripts/src/GenPathsModule.hs
@@ -41,6 +41,16 @@ $(capture "decls" [d|
, zLibexecdir :: FilePath
, zSysconfdir :: FilePath
+ , zShouldEmitLibDir :: Bool
+ , zShouldEmitDynLibDir :: Bool
+ , zShouldEmitLibexecDir :: Bool
+ , zShouldEmitDataDir :: Bool
+ , zShouldEmitSysconfDir :: Bool
+
+ , zShouldEmitWarning :: Bool
+ , zWarning :: String
+
+ , zOr :: Bool -> Bool -> Bool
, zNot :: Bool -> Bool
, zManglePkgName :: PackageName -> String
}
difftemplates/Paths_pkg.template.hs b/templates/Paths_pkg.template.hs
index 8e1e03d27..cc5c86701 100644
--- a/libraries/Cabal/templates/Paths_pkg.template.hs
+++ b/libraries/Cabal/templates/Paths_pkg.template.hs
@@ -14,10 +14,31 @@
{% endif %}
{-# OPTIONS_GHC -fno-warn-missing-import-lists #-}
{-# OPTIONS_GHC -w #-}
-module Paths_{{ manglePkgName packageName }} (
+module Paths_{{ manglePkgName packageName }}
+ {% if shouldEmitWarning %}{-# WARNING {{ warning }} #-}{% endif %}
+ (
version,
- getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir,
- getDataFileName, getSysconfDir
+ getBinDir,
+{# We only care about the absolute case for our emit logic, since only in this
+ case references are incurred. We are not going to hit isWindows and relocatable
+ has no absolute references to begin with.
+#}
+{% if or (not absolute) shouldEmitLibDir %}
+ getLibDir,
+{% endif %}
+{% if or (not absolute) shouldEmitDynLibDir %}
+ getDynLibDir,
+{% endif %}
+{% if or (not absolute) shouldEmitLibexecDir %}
+ getLibexecDir,
+{% endif %}
+{% if or (not absolute) shouldEmitDataDir %}
+ getDataFileName,
+ getDataDir,
+{% endif %}
+{% if or (not absolute) shouldEmitSysconfDir %}
+ getSysconfDir
+{% endif %}
) where
{% if not absolute %}
@@ -56,12 +77,12 @@ catchIO = Exception.catch
version :: Version
version = Version {{ versionDigits }} []
+{% if or (not absolute) shouldEmitDataDir %}
getDataFileName :: FilePath -> IO FilePath
getDataFileName name = do
dir <- getDataDir
return (dir `joinFileName` name)
-
-getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir, getSysconfDir :: IO FilePath
+{% endif %}
{% defblock function_defs %}
minusFileName :: FilePath -> String -> FilePath
@@ -90,48 +111,93 @@ splitFileName p = (reverse (path2++drive), reverse fname)
{% if relocatable %}
+{# Relocatable can not incur any absolute references, so we can ignore it.
+ Additionally, --enable-relocatable is virtually useless in Nix builds
+#}
+
getPrefixDirReloc :: FilePath -> IO FilePath
getPrefixDirReloc dirRel = do
exePath <- getExecutablePath
let (dir,_) = splitFileName exePath
return ((dir `minusFileName` {{ bindir }}) `joinFileName` dirRel)
+getBinDir :: IO FilePath
getBinDir = catchIO (getEnv "{{ manglePkgName packageName }}_bindir") (\_ -> getPrefixDirReloc $ {{ bindir }})
+getLibDir :: IO FilePath
getLibDir = catchIO (getEnv "{{ manglePkgName packageName }}_libdir") (\_ -> getPrefixDirReloc $ {{ libdir }})
+getDynLibDir :: IO FilePath
getDynLibDir = catchIO (getEnv "{{ manglePkgName packageName }}_dynlibdir") (\_ -> getPrefixDirReloc $ {{ dynlibdir }})
+getDataDir :: IO FilePath
getDataDir = catchIO (getEnv "{{ manglePkgName packageName }}_datadir") (\_ -> getPrefixDirReloc $ {{ datadir }})
+getLibexecDir :: IO FilePath
getLibexecDir = catchIO (getEnv "{{ manglePkgName packageName }}_libexecdir") (\_ -> getPrefixDirReloc $ {{ libexecdir }})
+getSysconfDir :: IO FilePath
getSysconfDir = catchIO (getEnv "{{ manglePkgName packageName }}_sysconfdir") (\_ -> getPrefixDirReloc $ {{ sysconfdir }})
{% useblock function_defs %}
{% elif absolute %}
-bindir, libdir, dynlibdir, datadir, libexecdir, sysconfdir :: FilePath
+bindir :: FilePath
bindir = {{ bindir }}
-libdir = {{ libdir }}
-dynlibdir = {{ dynlibdir }}
-datadir = {{ datadir }}
-libexecdir = {{ libexecdir }}
-sysconfdir = {{ sysconfdir }}
-
+getBinDir :: IO FilePath
getBinDir = catchIO (getEnv "{{ manglePkgName packageName }}_bindir") (\_ -> return bindir)
+
+{% if shouldEmitLibDir %}
+libdir :: FilePath
+libdir = {{ libdir }}
+getLibDir :: IO FilePath
getLibDir = catchIO (getEnv "{{ manglePkgName packageName }}_libdir") (\_ -> return libdir)
+{% endif %}
+
+{% if shouldEmitDynLibDir %}
+dynlibdir :: FilePath
+dynlibdir = {{ dynlibdir }}
+getDynLibDir :: IO FilePath
getDynLibDir = catchIO (getEnv "{{ manglePkgName packageName }}_dynlibdir") (\_ -> return dynlibdir)
+{% endif %}
+
+{% if shouldEmitDataDir %}
+datadir :: FilePath
+datadir = {{ datadir }}
+getDataDir :: IO FilePath
getDataDir = catchIO (getEnv "{{ manglePkgName packageName }}_datadir") (\_ -> return datadir)
+{% endif %}
+
+{% if shouldEmitLibexecDir %}
+libexecdir :: FilePath
+libexecdir = {{ libexecdir }}
+getLibexecDir :: IO FilePath
getLibexecDir = catchIO (getEnv "{{ manglePkgName packageName }}_libexecdir") (\_ -> return libexecdir)
+{% endif %}
+
+{% if shouldEmitSysconfDir %}
+sysconfdir :: FilePath
+sysconfdir = {{ sysconfdir }}
+getSysconfDir :: IO FilePath
getSysconfDir = catchIO (getEnv "{{ manglePkgName packageName }}_sysconfdir") (\_ -> return sysconfdir)
+{% endif %}
{% elif isWindows %}
+{# We are only trying to fix the problem for aarch64-darwin with this patch,
+ so let's ignore Windows which we can reach via pkgsCross, for example.
+#}
+
prefix :: FilePath
prefix = {{ prefix }}
+getBinDir :: IO FilePath
getBinDir = getPrefixDirRel $ {{ bindir }}
+getLibDir :: IO FilePath
getLibDir = {{ libdir }}
+getDynLibDir :: IO FilePath
getDynLibDir = {{ dynlibdir }}
+getDataDir :: IO FilePath
getDataDir = catchIO (getEnv "{{ manglePkgName packageName }}_datadir") (\_ -> {{ datadir }})
+getLibexecDir :: IO FilePath
getLibexecDir = {{ libexecdir }}
+getSysconfDir :: IO FilePath
getSysconfDir = {{ sysconfdir }}
getPrefixDirRel :: FilePath -> IO FilePath

View File

@@ -0,0 +1,602 @@
This patch is based on https://github.com/sternenseemann/cabal/compare/982646d67b95b32813b89ab5d2d2f4d4dc03fb2b..7c49047f253e1f128e2df356400ec5da6f11066b
and has been postprocessed with `filterdiff --strip=1 --addoldprefix=a/libraries/Cabal/ --addnewprefix=b/libraries/Cabal/`.
Note that the base for the diff is not the Cabal 3.6.3.0 release tag, but
982646d67b95b32813b89ab5d2d2f4d4dc03fb2b which is obtained by applying
https://github.com/haskell/cabal/commit/6c796218c92f93c95e94d5ec2d077f6956f68e98
on top of said release tag. That patch is applied to all our GHCs in the 9.2 series.
Reasoning and explanation of the patch can be found in the comment in the diff for PathsModule.hs below.
diffCabal/src/Distribution/Simple/Build/PathsModule.hs b/Cabal/src/Distribution/Simple/Build/PathsModule.hs
index b2be7e1a8..9b63e9850 100644
--- a/libraries/Cabal/Cabal/src/Distribution/Simple/Build/PathsModule.hs
+++ b/libraries/Cabal/Cabal/src/Distribution/Simple/Build/PathsModule.hs
@@ -46,6 +46,7 @@ generatePathsModule pkg_descr lbi clbi = Z.render Z.Z
, Z.zIsWindows = isWindows
, Z.zIsI386 = buildArch == I386
, Z.zIsX8664 = buildArch == X86_64
+ , Z.zOr = (||)
, Z.zNot = not
, Z.zManglePkgName = showPkgName
@@ -56,8 +57,112 @@ generatePathsModule pkg_descr lbi clbi = Z.render Z.Z
, Z.zDatadir = zDatadir
, Z.zLibexecdir = zLibexecdir
, Z.zSysconfdir = zSysconfdir
+
+ -- Sadly we can't be cleverer about this we can't have literals in the template
+ , Z.zShouldEmitDataDir = shouldEmit "DataDir"
+ , Z.zShouldEmitLibDir = shouldEmit "LibDir"
+ , Z.zShouldEmitDynLibDir = shouldEmit "DynLibDir"
+ , Z.zShouldEmitLibexecDir = shouldEmit "LibexecDir"
+ , Z.zShouldEmitSysconfDir = shouldEmit "SysconfDir"
+
+ , Z.zWarning = zWarning
+ , Z.zShouldEmitWarning = zShouldEmitWarning
}
where
+ -- GHC's NCG backend for aarch64-darwin does not support link-time dead code
+ -- elimination to the extent that NCG does for other targets. Consequently,
+ -- we struggle with unnecessarily retained store path references due to the
+ -- use of `Paths_*` modules even if `getLibDir` is not used, it'll end up
+ -- in the final library or executables we build.
+ --
+ -- When using a different output for the executables and library, this
+ -- becomes more sinister: The library will contain a reference to the bin
+ -- output and itself due to `getLibDir` and `getBinDir`, but the executables
+ -- will do so, too. Either due to linking dynamically or because the library
+ -- is linked statically into the executable and retains those references.
+ -- Since Nix disallows cyclical references between two outputs, it becomes
+ -- impossible to use the `Paths_*` module and a separate `bin` output for
+ -- aarch64-darwin.
+ --
+ -- The solution we have resorted to for now, is to trim the `Paths_*` module
+ -- dynamically depending on what references *could* be used without causing
+ -- a cyclical reference. That has the effect that any code that would not
+ -- cause a cyclical reference with dead code elimination will compile and
+ -- work for aarch64-darwin. If the code would use a `get*Dir` function that
+ -- has been omitted, this would indicate that the code would have caused a
+ -- cyclical reference anyways.
+ --
+ -- The logic for this makes some pretty big assumptions about installation
+ -- prefixes that probably only hold fully in nixpkgs with
+ -- `haskellPackages.mkDerivation`. Simple uses outside nixpkgs that have
+ -- everything below the same prefix should continue to work as expected,
+ -- though.
+ --
+ -- We assume the following:
+ --
+ -- - flat_prefix is `$out`.
+ -- - flat_libdir etc. are always below `$out`.
+ --
+ -- Since in the normal case due to static linking `$bin` and `$out` will
+ -- have the same references in libraries/executables, we need to either
+ -- prevent usage of `getBinDir` or `getLibDir` to break the cycle in case
+ -- `flat_bindir` is not below `$out`. We have decided to always allow usage
+ -- of `getBinDir`, so `getLibDir` gets dropped if a separate `bin` output is
+ -- used. This has the simple reason that `$out` which contains `flat_libdir`
+ -- tends to be quite big we would like to have a `bin` output that doesn't
+ -- require keeping that around.
+ pathEmittable :: FilePath -> Bool
+ pathEmittable p
+ -- If the executable installation target is below `$out` the reference
+ -- cycle is within a single output (since libs are installed to `$out`)
+ -- and thus unproblematic. We can use any and all `get*Dir` functions.
+ | flat_prefix `isPrefixOf` flat_bindir = True
+ -- Otherwise, we need to disallow all `get*Dir` functions that would cause
+ -- a reference to `$out` which contains the libraries that would in turn
+ -- reference `$bin`. This always include `flat_libdir` and friends, but
+ -- can also include `flat_datadir` if no separate output for data files is
+ -- used.
+ | otherwise = not (flat_prefix `isPrefixOf` p)
+
+ -- This list maps the "name" of the directory to whether we want to include
+ -- it in the `Paths_*` module or not. `shouldEmit` performs a lookup in this.
+ dirs :: [(String, Bool)]
+ dirs =
+ map
+ (\(name, path) -> (name, pathEmittable path))
+ [ ("LibDir", flat_libdir)
+ , ("DynLibDir", flat_dynlibdir)
+ , ("DataDir", flat_datadir)
+ , ("LibexecDir", flat_libexecdir)
+ , ("SysconfDir", flat_sysconfdir)
+ ]
+
+ shouldEmit :: String -> Bool
+ shouldEmit name =
+ case lookup name dirs of
+ Just b -> b
+ Nothing -> error "panic! BUG in Cabal Paths_ patch for aarch64-darwin, report this at https://github.com/nixos/nixpkgs/issues"
+
+ -- This is a comma separated list of all functions that have been omitted.
+ -- This is included in a GHC warning which will be attached to the `Paths_*`
+ -- module in case we are dropping any `get*Dir` functions that would
+ -- normally exist.
+ --
+ -- TODO: getDataFileName is not accounted for at the moment.
+ omittedFunctions :: String
+ omittedFunctions =
+ intercalate ", "
+ $ map (("get" ++) . fst)
+ $ filter (not . snd) dirs
+
+ zWarning :: String
+ zWarning =
+ show $
+ "The following functions have been omitted by a nixpkgs-specific patch to Cabal: "
+ ++ omittedFunctions
+ zShouldEmitWarning :: Bool
+ zShouldEmitWarning = any (not . snd) dirs
+
supports_cpp = supports_language_pragma
supports_rebindable_syntax = ghc_newer_than (mkVersion [7,0,1])
supports_language_pragma = ghc_newer_than (mkVersion [6,6,1])
diffCabal/src/Distribution/Simple/Build/PathsModule/Z.hs b/Cabal/src/Distribution/Simple/Build/PathsModule/Z.hs
index 6488ea061..a6cdc8e31 100644
--- a/libraries/Cabal/Cabal/src/Distribution/Simple/Build/PathsModule/Z.hs
+++ b/libraries/Cabal/Cabal/src/Distribution/Simple/Build/PathsModule/Z.hs
@@ -18,6 +18,14 @@ data Z
zDatadir :: FilePath,
zLibexecdir :: FilePath,
zSysconfdir :: FilePath,
+ zShouldEmitLibDir :: Bool,
+ zShouldEmitDynLibDir :: Bool,
+ zShouldEmitLibexecDir :: Bool,
+ zShouldEmitDataDir :: Bool,
+ zShouldEmitSysconfDir :: Bool,
+ zShouldEmitWarning :: Bool,
+ zWarning :: String,
+ zOr :: (Bool -> Bool -> Bool),
zNot :: (Bool -> Bool),
zManglePkgName :: (PackageName -> String)}
deriving Generic
@@ -45,10 +53,51 @@ render z_root = execWriter $ do
tell "{-# OPTIONS_GHC -w #-}\n"
tell "module Paths_"
tell (zManglePkgName z_root (zPackageName z_root))
- tell " (\n"
+ tell "\n"
+ tell " "
+ if (zShouldEmitWarning z_root)
+ then do
+ tell "{-# WARNING "
+ tell (zWarning z_root)
+ tell " #-}"
+ return ()
+ else do
+ return ()
+ tell "\n"
+ tell " (\n"
tell " version,\n"
- tell " getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir,\n"
- tell " getDataFileName, getSysconfDir\n"
+ tell " getBinDir,\n"
+ if (zOr z_root (zNot z_root (zAbsolute z_root)) (zShouldEmitLibDir z_root))
+ then do
+ tell " getLibDir,\n"
+ return ()
+ else do
+ return ()
+ if (zOr z_root (zNot z_root (zAbsolute z_root)) (zShouldEmitDynLibDir z_root))
+ then do
+ tell " getDynLibDir,\n"
+ return ()
+ else do
+ return ()
+ if (zOr z_root (zNot z_root (zAbsolute z_root)) (zShouldEmitLibexecDir z_root))
+ then do
+ tell " getLibexecDir,\n"
+ return ()
+ else do
+ return ()
+ if (zOr z_root (zNot z_root (zAbsolute z_root)) (zShouldEmitDataDir z_root))
+ then do
+ tell " getDataFileName,\n"
+ tell " getDataDir,\n"
+ return ()
+ else do
+ return ()
+ if (zOr z_root (zNot z_root (zAbsolute z_root)) (zShouldEmitSysconfDir z_root))
+ then do
+ tell " getSysconfDir\n"
+ return ()
+ else do
+ return ()
tell " ) where\n"
tell "\n"
if (zNot z_root (zAbsolute z_root))
@@ -97,12 +146,15 @@ render z_root = execWriter $ do
tell (zVersionDigits z_root)
tell " []\n"
tell "\n"
- tell "getDataFileName :: FilePath -> IO FilePath\n"
- tell "getDataFileName name = do\n"
- tell " dir <- getDataDir\n"
- tell " return (dir `joinFileName` name)\n"
- tell "\n"
- tell "getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir, getSysconfDir :: IO FilePath\n"
+ if (zOr z_root (zNot z_root (zAbsolute z_root)) (zShouldEmitDataDir z_root))
+ then do
+ tell "getDataFileName :: FilePath -> IO FilePath\n"
+ tell "getDataFileName name = do\n"
+ tell " dir <- getDataDir\n"
+ tell " return (dir `joinFileName` name)\n"
+ return ()
+ else do
+ return ()
tell "\n"
let
z_var0_function_defs = do
@@ -130,6 +182,7 @@ render z_root = execWriter $ do
tell "\n"
if (zRelocatable z_root)
then do
+ tell "\n"
tell "\n"
tell "getPrefixDirReloc :: FilePath -> IO FilePath\n"
tell "getPrefixDirReloc dirRel = do\n"
@@ -139,31 +192,37 @@ render z_root = execWriter $ do
tell (zBindir z_root)
tell ") `joinFileName` dirRel)\n"
tell "\n"
+ tell "getBinDir :: IO FilePath\n"
tell "getBinDir = catchIO (getEnv \""
tell (zManglePkgName z_root (zPackageName z_root))
tell "_bindir\") (\\_ -> getPrefixDirReloc $ "
tell (zBindir z_root)
tell ")\n"
+ tell "getLibDir :: IO FilePath\n"
tell "getLibDir = catchIO (getEnv \""
tell (zManglePkgName z_root (zPackageName z_root))
tell "_libdir\") (\\_ -> getPrefixDirReloc $ "
tell (zLibdir z_root)
tell ")\n"
+ tell "getDynLibDir :: IO FilePath\n"
tell "getDynLibDir = catchIO (getEnv \""
tell (zManglePkgName z_root (zPackageName z_root))
tell "_dynlibdir\") (\\_ -> getPrefixDirReloc $ "
tell (zDynlibdir z_root)
tell ")\n"
+ tell "getDataDir :: IO FilePath\n"
tell "getDataDir = catchIO (getEnv \""
tell (zManglePkgName z_root (zPackageName z_root))
tell "_datadir\") (\\_ -> getPrefixDirReloc $ "
tell (zDatadir z_root)
tell ")\n"
+ tell "getLibexecDir :: IO FilePath\n"
tell "getLibexecDir = catchIO (getEnv \""
tell (zManglePkgName z_root (zPackageName z_root))
tell "_libexecdir\") (\\_ -> getPrefixDirReloc $ "
tell (zLibexecdir z_root)
tell ")\n"
+ tell "getSysconfDir :: IO FilePath\n"
tell "getSysconfDir = catchIO (getEnv \""
tell (zManglePkgName z_root (zPackageName z_root))
tell "_sysconfdir\") (\\_ -> getPrefixDirReloc $ "
@@ -177,72 +236,119 @@ render z_root = execWriter $ do
if (zAbsolute z_root)
then do
tell "\n"
- tell "bindir, libdir, dynlibdir, datadir, libexecdir, sysconfdir :: FilePath\n"
+ tell "bindir :: FilePath\n"
tell "bindir = "
tell (zBindir z_root)
tell "\n"
- tell "libdir = "
- tell (zLibdir z_root)
- tell "\n"
- tell "dynlibdir = "
- tell (zDynlibdir z_root)
+ tell "getBinDir :: IO FilePath\n"
+ tell "getBinDir = catchIO (getEnv \""
+ tell (zManglePkgName z_root (zPackageName z_root))
+ tell "_bindir\") (\\_ -> return bindir)\n"
tell "\n"
- tell "datadir = "
- tell (zDatadir z_root)
+ if (zShouldEmitLibDir z_root)
+ then do
+ tell "libdir :: FilePath\n"
+ tell "libdir = "
+ tell (zLibdir z_root)
+ tell "\n"
+ tell "getLibDir :: IO FilePath\n"
+ tell "getLibDir = catchIO (getEnv \""
+ tell (zManglePkgName z_root (zPackageName z_root))
+ tell "_libdir\") (\\_ -> return libdir)\n"
+ return ()
+ else do
+ return ()
tell "\n"
- tell "libexecdir = "
- tell (zLibexecdir z_root)
+ if (zShouldEmitDynLibDir z_root)
+ then do
+ tell "dynlibdir :: FilePath\n"
+ tell "dynlibdir = "
+ tell (zDynlibdir z_root)
+ tell "\n"
+ tell "getDynLibDir :: IO FilePath\n"
+ tell "getDynLibDir = catchIO (getEnv \""
+ tell (zManglePkgName z_root (zPackageName z_root))
+ tell "_dynlibdir\") (\\_ -> return dynlibdir)\n"
+ return ()
+ else do
+ return ()
tell "\n"
- tell "sysconfdir = "
- tell (zSysconfdir z_root)
+ if (zShouldEmitDataDir z_root)
+ then do
+ tell "datadir :: FilePath\n"
+ tell "datadir = "
+ tell (zDatadir z_root)
+ tell "\n"
+ tell "getDataDir :: IO FilePath\n"
+ tell "getDataDir = catchIO (getEnv \""
+ tell (zManglePkgName z_root (zPackageName z_root))
+ tell "_datadir\") (\\_ -> return datadir)\n"
+ return ()
+ else do
+ return ()
tell "\n"
+ if (zShouldEmitLibexecDir z_root)
+ then do
+ tell "libexecdir :: FilePath\n"
+ tell "libexecdir = "
+ tell (zLibexecdir z_root)
+ tell "\n"
+ tell "getLibexecDir :: IO FilePath\n"
+ tell "getLibexecDir = catchIO (getEnv \""
+ tell (zManglePkgName z_root (zPackageName z_root))
+ tell "_libexecdir\") (\\_ -> return libexecdir)\n"
+ return ()
+ else do
+ return ()
tell "\n"
- tell "getBinDir = catchIO (getEnv \""
- tell (zManglePkgName z_root (zPackageName z_root))
- tell "_bindir\") (\\_ -> return bindir)\n"
- tell "getLibDir = catchIO (getEnv \""
- tell (zManglePkgName z_root (zPackageName z_root))
- tell "_libdir\") (\\_ -> return libdir)\n"
- tell "getDynLibDir = catchIO (getEnv \""
- tell (zManglePkgName z_root (zPackageName z_root))
- tell "_dynlibdir\") (\\_ -> return dynlibdir)\n"
- tell "getDataDir = catchIO (getEnv \""
- tell (zManglePkgName z_root (zPackageName z_root))
- tell "_datadir\") (\\_ -> return datadir)\n"
- tell "getLibexecDir = catchIO (getEnv \""
- tell (zManglePkgName z_root (zPackageName z_root))
- tell "_libexecdir\") (\\_ -> return libexecdir)\n"
- tell "getSysconfDir = catchIO (getEnv \""
- tell (zManglePkgName z_root (zPackageName z_root))
- tell "_sysconfdir\") (\\_ -> return sysconfdir)\n"
+ if (zShouldEmitSysconfDir z_root)
+ then do
+ tell "sysconfdir :: FilePath\n"
+ tell "sysconfdir = "
+ tell (zSysconfdir z_root)
+ tell "\n"
+ tell "getSysconfDir :: IO FilePath\n"
+ tell "getSysconfDir = catchIO (getEnv \""
+ tell (zManglePkgName z_root (zPackageName z_root))
+ tell "_sysconfdir\") (\\_ -> return sysconfdir)\n"
+ return ()
+ else do
+ return ()
tell "\n"
return ()
else do
if (zIsWindows z_root)
then do
+ tell "\n"
tell "\n"
tell "prefix :: FilePath\n"
tell "prefix = "
tell (zPrefix z_root)
tell "\n"
tell "\n"
+ tell "getBinDir :: IO FilePath\n"
tell "getBinDir = getPrefixDirRel $ "
tell (zBindir z_root)
tell "\n"
+ tell "getLibDir :: IO FilePath\n"
tell "getLibDir = "
tell (zLibdir z_root)
tell "\n"
+ tell "getDynLibDir :: IO FilePath\n"
tell "getDynLibDir = "
tell (zDynlibdir z_root)
tell "\n"
+ tell "getDataDir :: IO FilePath\n"
tell "getDataDir = catchIO (getEnv \""
tell (zManglePkgName z_root (zPackageName z_root))
tell "_datadir\") (\\_ -> "
tell (zDatadir z_root)
tell ")\n"
+ tell "getLibexecDir :: IO FilePath\n"
tell "getLibexecDir = "
tell (zLibexecdir z_root)
tell "\n"
+ tell "getSysconfDir :: IO FilePath\n"
tell "getSysconfDir = "
tell (zSysconfdir z_root)
tell "\n"
diffcabal-dev-scripts/src/GenPathsModule.hs b/cabal-dev-scripts/src/GenPathsModule.hs
index e4b930635..9b978f284 100644
--- a/libraries/Cabal/cabal-dev-scripts/src/GenPathsModule.hs
+++ b/libraries/Cabal/cabal-dev-scripts/src/GenPathsModule.hs
@@ -41,6 +41,16 @@ $(capture "decls" [d|
, zLibexecdir :: FilePath
, zSysconfdir :: FilePath
+ , zShouldEmitLibDir :: Bool
+ , zShouldEmitDynLibDir :: Bool
+ , zShouldEmitLibexecDir :: Bool
+ , zShouldEmitDataDir :: Bool
+ , zShouldEmitSysconfDir :: Bool
+
+ , zShouldEmitWarning :: Bool
+ , zWarning :: String
+
+ , zOr :: Bool -> Bool -> Bool
, zNot :: Bool -> Bool
, zManglePkgName :: PackageName -> String
}
difftemplates/Paths_pkg.template.hs b/templates/Paths_pkg.template.hs
index 6bc6b7875..aa90a9382 100644
--- a/libraries/Cabal/templates/Paths_pkg.template.hs
+++ b/libraries/Cabal/templates/Paths_pkg.template.hs
@@ -9,10 +9,31 @@
{% endif %}
{-# OPTIONS_GHC -fno-warn-missing-import-lists #-}
{-# OPTIONS_GHC -w #-}
-module Paths_{{ manglePkgName packageName }} (
+module Paths_{{ manglePkgName packageName }}
+ {% if shouldEmitWarning %}{-# WARNING {{ warning }} #-}{% endif %}
+ (
version,
- getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir,
- getDataFileName, getSysconfDir
+ getBinDir,
+{# We only care about the absolute case for our emit logic, since only in this
+ case references are incurred. We are not going to hit isWindows and relocatable
+ has no absolute references to begin with.
+#}
+{% if or (not absolute) shouldEmitLibDir %}
+ getLibDir,
+{% endif %}
+{% if or (not absolute) shouldEmitDynLibDir %}
+ getDynLibDir,
+{% endif %}
+{% if or (not absolute) shouldEmitLibexecDir %}
+ getLibexecDir,
+{% endif %}
+{% if or (not absolute) shouldEmitDataDir %}
+ getDataFileName,
+ getDataDir,
+{% endif %}
+{% if or (not absolute) shouldEmitSysconfDir %}
+ getSysconfDir
+{% endif %}
) where
{% if not absolute %}
@@ -51,12 +72,12 @@ catchIO = Exception.catch
version :: Version
version = Version {{ versionDigits }} []
+{% if or (not absolute) shouldEmitDataDir %}
getDataFileName :: FilePath -> IO FilePath
getDataFileName name = do
dir <- getDataDir
return (dir `joinFileName` name)
-
-getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir, getSysconfDir :: IO FilePath
+{% endif %}
{% defblock function_defs %}
minusFileName :: FilePath -> String -> FilePath
@@ -85,48 +106,93 @@ splitFileName p = (reverse (path2++drive), reverse fname)
{% if relocatable %}
+{# Relocatable can not incur any absolute references, so we can ignore it.
+ Additionally, --enable-relocatable is virtually useless in Nix builds
+#}
+
getPrefixDirReloc :: FilePath -> IO FilePath
getPrefixDirReloc dirRel = do
exePath <- getExecutablePath
let (dir,_) = splitFileName exePath
return ((dir `minusFileName` {{ bindir }}) `joinFileName` dirRel)
+getBinDir :: IO FilePath
getBinDir = catchIO (getEnv "{{ manglePkgName packageName }}_bindir") (\_ -> getPrefixDirReloc $ {{ bindir }})
+getLibDir :: IO FilePath
getLibDir = catchIO (getEnv "{{ manglePkgName packageName }}_libdir") (\_ -> getPrefixDirReloc $ {{ libdir }})
+getDynLibDir :: IO FilePath
getDynLibDir = catchIO (getEnv "{{ manglePkgName packageName }}_dynlibdir") (\_ -> getPrefixDirReloc $ {{ dynlibdir }})
+getDataDir :: IO FilePath
getDataDir = catchIO (getEnv "{{ manglePkgName packageName }}_datadir") (\_ -> getPrefixDirReloc $ {{ datadir }})
+getLibexecDir :: IO FilePath
getLibexecDir = catchIO (getEnv "{{ manglePkgName packageName }}_libexecdir") (\_ -> getPrefixDirReloc $ {{ libexecdir }})
+getSysconfDir :: IO FilePath
getSysconfDir = catchIO (getEnv "{{ manglePkgName packageName }}_sysconfdir") (\_ -> getPrefixDirReloc $ {{ sysconfdir }})
{% useblock function_defs %}
{% elif absolute %}
-bindir, libdir, dynlibdir, datadir, libexecdir, sysconfdir :: FilePath
+bindir :: FilePath
bindir = {{ bindir }}
-libdir = {{ libdir }}
-dynlibdir = {{ dynlibdir }}
-datadir = {{ datadir }}
-libexecdir = {{ libexecdir }}
-sysconfdir = {{ sysconfdir }}
-
+getBinDir :: IO FilePath
getBinDir = catchIO (getEnv "{{ manglePkgName packageName }}_bindir") (\_ -> return bindir)
+
+{% if shouldEmitLibDir %}
+libdir :: FilePath
+libdir = {{ libdir }}
+getLibDir :: IO FilePath
getLibDir = catchIO (getEnv "{{ manglePkgName packageName }}_libdir") (\_ -> return libdir)
+{% endif %}
+
+{% if shouldEmitDynLibDir %}
+dynlibdir :: FilePath
+dynlibdir = {{ dynlibdir }}
+getDynLibDir :: IO FilePath
getDynLibDir = catchIO (getEnv "{{ manglePkgName packageName }}_dynlibdir") (\_ -> return dynlibdir)
+{% endif %}
+
+{% if shouldEmitDataDir %}
+datadir :: FilePath
+datadir = {{ datadir }}
+getDataDir :: IO FilePath
getDataDir = catchIO (getEnv "{{ manglePkgName packageName }}_datadir") (\_ -> return datadir)
+{% endif %}
+
+{% if shouldEmitLibexecDir %}
+libexecdir :: FilePath
+libexecdir = {{ libexecdir }}
+getLibexecDir :: IO FilePath
getLibexecDir = catchIO (getEnv "{{ manglePkgName packageName }}_libexecdir") (\_ -> return libexecdir)
+{% endif %}
+
+{% if shouldEmitSysconfDir %}
+sysconfdir :: FilePath
+sysconfdir = {{ sysconfdir }}
+getSysconfDir :: IO FilePath
getSysconfDir = catchIO (getEnv "{{ manglePkgName packageName }}_sysconfdir") (\_ -> return sysconfdir)
+{% endif %}
{% elif isWindows %}
+{# We are only trying to fix the problem for aarch64-darwin with this patch,
+ so let's ignore Windows which we can reach via pkgsCross, for example.
+#}
+
prefix :: FilePath
prefix = {{ prefix }}
+getBinDir :: IO FilePath
getBinDir = getPrefixDirRel $ {{ bindir }}
+getLibDir :: IO FilePath
getLibDir = {{ libdir }}
+getDynLibDir :: IO FilePath
getDynLibDir = {{ dynlibdir }}
+getDataDir :: IO FilePath
getDataDir = catchIO (getEnv "{{ manglePkgName packageName }}_datadir") (\_ -> {{ datadir }})
+getLibexecDir :: IO FilePath
getLibexecDir = {{ libexecdir }}
+getSysconfDir :: IO FilePath
getSysconfDir = {{ sysconfdir }}
getPrefixDirRel :: FilePath -> IO FilePath

View File

@@ -0,0 +1,885 @@
{
version,
rev ? null,
sha256,
url ?
if rev != null then
"https://gitlab.haskell.org/ghc/ghc.git"
else
"https://downloads.haskell.org/ghc/${version}/ghc-${version}-src.tar.xz",
postFetch ? null,
}:
{
lib,
stdenv,
stdenvNoCC,
pkgsBuildTarget,
pkgsHostTarget,
buildPackages,
targetPackages,
fetchpatch,
# build-tools
bootPkgs,
autoreconfHook,
coreutils,
fetchurl,
fetchgit,
perl,
python3,
sphinx,
xattr,
autoSignDarwinBinariesHook,
bash,
srcOnly,
libiconv ? null,
ncurses,
# GHC can be built with system libffi or a bundled one.
libffi ? null,
useLLVM ? !(import ./common-have-ncg.nix { inherit lib stdenv version; }),
# LLVM is conceptually a run-time-only dependency, but for
# non-x86, we need LLVM to bootstrap later stages, so it becomes a
# build-time dependency too.
buildTargetLlvmPackages,
llvmPackages,
# If enabled, GHC will be built with the GPL-free but slightly slower native
# bignum backend instead of the faster but GPLed gmp backend.
enableNativeBignum ?
!(lib.meta.availableOn stdenv.hostPlatform gmp && lib.meta.availableOn stdenv.targetPlatform gmp)
|| stdenv.targetPlatform.isGhcjs,
gmp,
# If enabled, use -fPIC when compiling static libs.
enableRelocatedStaticLibs ? stdenv.targetPlatform != stdenv.hostPlatform,
# Exceeds Hydra output limit (at the time of writing ~3GB) when cross compiled to riscv64.
# A riscv64 cross-compiler fits into the limit comfortably.
enableProfiledLibs ? !stdenv.hostPlatform.isRiscV64,
# Whether to build dynamic libs for the standard library (on the target
# platform). Static libs are always built.
enableShared ? with stdenv.targetPlatform; !isWindows && !useiOSPrebuilt && !isStatic && !isGhcjs,
# Whether to build terminfo.
enableTerminfo ?
!(
stdenv.targetPlatform.isWindows
|| stdenv.targetPlatform.isGhcjs
# Before <https://gitlab.haskell.org/ghc/ghc/-/merge_requests/13932>,
# we couldn't force hadrian to build terminfo for cross.
|| (
lib.versionOlder version "9.15.20250808"
&& (stdenv.buildPlatform != stdenv.hostPlatform || stdenv.hostPlatform != stdenv.targetPlatform)
)
),
# Libdw.c only supports x86_64, i686 and s390x as of 2022-08-04
enableDwarf ?
(stdenv.targetPlatform.isx86 || (stdenv.targetPlatform.isS390 && stdenv.targetPlatform.is64bit))
&& lib.meta.availableOn stdenv.hostPlatform elfutils
&& lib.meta.availableOn stdenv.targetPlatform elfutils
&&
# HACK: elfutils is marked as broken on static platforms
# which availableOn can't tell.
!stdenv.targetPlatform.isStatic
&& !stdenv.hostPlatform.isStatic,
elfutils,
# Enable NUMA support in RTS
enableNuma ? lib.meta.availableOn stdenv.targetPlatform numactl,
numactl,
# What flavour to build. Flavour string may contain a flavour and flavour
# transformers as accepted by hadrian.
ghcFlavour ?
let
# TODO(@sternenseemann): does using the static flavour make sense?
baseFlavour = "release";
# Note: in case hadrian's flavour transformers cease being expressive
# enough for us, we'll need to resort to defining a "nixpkgs" flavour
# in hadrianUserSettings and using that instead.
transformers =
lib.optionals useLLVM [ "llvm" ]
++ lib.optionals (!enableShared) [
"no_dynamic_libs"
"no_dynamic_ghc"
]
++ lib.optionals (!enableProfiledLibs) [ "no_profiled_libs" ]
# While split sections are now enabled by default in ghc 8.8 for windows,
# they seem to lead to `too many sections` errors when building base for
# profiling.
++ (if stdenv.targetPlatform.isWindows then [ "no_split_sections" ] else [ "split_sections" ]);
in
baseFlavour + lib.concatMapStrings (t: "+${t}") transformers,
# Contents of the UserSettings.hs file to use when compiling hadrian.
hadrianUserSettings ? ''
module UserSettings (
userFlavours, userPackages, userDefaultFlavour,
verboseCommand, buildProgressColour, successColour, finalStage
) where
import Flavour.Type
import Expression
import {-# SOURCE #-} Settings.Default
-- no way to set this via the command line
finalStage :: Stage
finalStage = ${
# N. B. hadrian ignores this setting if it doesn't agree it's possible,
# i.e. when its cross-compiling setting is true. So while we could, in theory,
# build Stage2 if hostPlatform.canExecute targetPlatform, hadrian won't play
# ball (with make, Stage2 was built if hostPlatform.system == targetPlatform.system).
if stdenv.hostPlatform == stdenv.targetPlatform then
"Stage2" # native compiler
else
"Stage1" # cross compiler
}
userDefaultFlavour :: String
userDefaultFlavour = "release"
userFlavours :: [Flavour]
userFlavours = []
-- Disable Colours
buildProgressColour :: BuildProgressColour
buildProgressColour = mkBuildProgressColour (Dull Reset)
successColour :: SuccessColour
successColour = mkSuccessColour (Dull Reset)
-- taken from src/UserSettings.hs unchanged, need to be there
userPackages :: [Package]
userPackages = []
verboseCommand :: Predicate
verboseCommand = do
verbosity <- expr getVerbosity
return $ verbosity >= Verbose
'',
ghcSrc ? srcOnly {
name = "ghc-${version}"; # -source appended by srcOnly
src = (if rev != null then fetchgit else fetchurl) (
{
inherit url sha256;
}
// lib.optionalAttrs (rev != null) {
inherit rev;
}
// lib.optionalAttrs (postFetch != null) {
inherit postFetch;
}
);
patches =
let
enableHyperlinkedSource =
# Disable haddock generating pretty source listings to stay under 3GB on aarch64-linux
!(stdenv.hostPlatform.isAarch64 && stdenv.hostPlatform.isLinux)
# 9.8 and 9.10 don't run into this problem for some reason
|| (lib.versionAtLeast version "9.8" && lib.versionOlder version "9.11");
in
lib.optionals
(
# 2025-01-16: unix >= 2.8.6.0 is unaffected which is shipped by GHC 9.12.1 and 9.8.4
lib.versionOlder version "9.11"
&& !(lib.versionAtLeast version "9.6.7" && lib.versionOlder version "9.8")
&& !(lib.versionAtLeast version "9.8.4" && lib.versionOlder version "9.9")
&& !(lib.versionAtLeast version "9.10.2" && lib.versionOlder version "9.11")
)
[
# Determine size of time related types using hsc2hs instead of assuming CLong.
# Prevents failures when e.g. stat(2)ing on 32bit systems with 64bit time_t etc.
# https://github.com/haskell/ghcup-hs/issues/1107
# https://gitlab.haskell.org/ghc/ghc/-/issues/25095
# Note that in normal situations this shouldn't be the case since nixpkgs
# doesn't set -D_FILE_OFFSET_BITS=64 and friends (yet).
(fetchpatch {
name = "unix-fix-ctimeval-size-32-bit.patch";
url = "https://github.com/haskell/unix/commit/8183e05b97ce870dd6582a3677cc82459ae566ec.patch";
sha256 = "17q5yyigqr5kxlwwzb95sx567ysfxlw6bp3j4ji20lz0947aw6gv";
stripLen = 1;
extraPrefix = "libraries/unix/";
})
]
++ lib.optionals (lib.versionAtLeast version "9.6" && lib.versionOlder version "9.8") [
# Fix unlit being installed under a different name than is used in the
# settings file: https://gitlab.haskell.org/ghc/ghc/-/issues/23317
(fetchpatch {
name = "ghc-9.6-fix-unlit-path.patch";
url = "https://gitlab.haskell.org/ghc/ghc/-/commit/8fde4ac84ec7b1ead238cb158bbef48555d12af9.patch";
hash = "sha256-3+CyRBpebEZi8YpS22SsdGQHqi0drR7cCKPtKbR3zyE=";
})
]
++ lib.optionals (stdenv.targetPlatform.isDarwin && stdenv.targetPlatform.isAarch64) [
# Prevent the paths module from emitting symbols that we don't use
# when building with separate outputs.
#
# These cause problems as they're not eliminated by GHC's dead code
# elimination on aarch64-darwin. (see
# https://github.com/NixOS/nixpkgs/issues/140774 for details).
(
if lib.versionOlder version "9.10" then
./Cabal-at-least-3.6-paths-fix-cycle-aarch64-darwin.patch
else
./Cabal-3.12-paths-fix-cycle-aarch64-darwin.patch
)
]
++ lib.optionals stdenv.targetPlatform.isWindows [
# https://gitlab.haskell.org/ghc/ghc/-/merge_requests/13919
(fetchpatch {
name = "include-modern-utimbuf.patch";
url = "https://gitlab.haskell.org/ghc/ghc/-/commit/7e75928ed0f1c4654de6ddd13d0b00bf4b5c6411.patch";
hash = "sha256-sb+AHdkGkCu8MW0xoQIpD5kEc0zYX8udAMDoC+TWc0Q=";
})
]
# Prevents passing --hyperlinked-source to haddock. Note that this can
# be configured via a user defined flavour now. Unfortunately, it is
# impossible to import an existing flavour in UserSettings, so patching
# the defaults is actually simpler and less maintenance intensive
# compared to keeping an entire flavour definition in sync with upstream
# manually. See also https://gitlab.haskell.org/ghc/ghc/-/issues/23625
++ lib.optionals (!enableHyperlinkedSource) [
(
if lib.versionOlder version "9.8" then
../../tools/haskell/hadrian/disable-hyperlinked-source-pre-9.8.patch
else
../../tools/haskell/hadrian/disable-hyperlinked-source-extra-args.patch
)
]
++ lib.optionals (lib.versionAtLeast version "9.8" && lib.versionOlder version "9.12") [
(fetchpatch {
name = "enable-ignore-build-platform-mismatch.patch";
url = "https://gitlab.haskell.org/ghc/ghc/-/commit/4ee094d46effd06093090fcba70f0a80d2a57e6c.patch";
includes = [ "configure.ac" ];
hash = "sha256-L3FQvcm9QB59BOiR2g5/HACAufIG08HiT53EIOjj64g=";
})
]
# Fixes stack overrun in rts which crashes an process whenever
# freeHaskellFunPtr is called with nixpkgs' hardening flags.
# https://gitlab.haskell.org/ghc/ghc/-/issues/25485
# https://gitlab.haskell.org/ghc/ghc/-/merge_requests/13599
++ lib.optionals (lib.versionOlder version "9.13") [
(fetchpatch {
name = "ghc-rts-adjustor-fix-i386-stack-overrun.patch";
url = "https://gitlab.haskell.org/ghc/ghc/-/commit/39bb6e583d64738db51441a556d499aa93a4fc4a.patch";
sha256 = "0w5fx413z924bi2irsy1l4xapxxhrq158b5gn6jzrbsmhvmpirs0";
})
]
# Missing ELF symbols
++ lib.optionals stdenv.targetPlatform.isAndroid [
./ghc-define-undefined-elf-st-visibility.patch
];
stdenv = stdenvNoCC;
},
# GHC's build system hadrian built from the GHC-to-build's source tree
# using our bootstrap GHC.
hadrian ? import ../../tools/haskell/hadrian/make-hadrian.nix { inherit bootPkgs lib; } {
inherit ghcSrc;
ghcVersion = version;
userSettings = hadrianUserSettings;
},
# Whether to build sphinx documentation.
# TODO(@sternenseemann): Hadrian ignores the --docs flag if finalStage = Stage1
enableDocs ? (
# Docs disabled if we are building on musl because it's a large task to keep
# all `sphinx` dependencies building in this environment.
!stdenv.buildPlatform.isMusl
),
# Whether to disable the large address space allocator
# necessary fix for iOS: https://www.reddit.com/r/haskell/comments/4ttdz1/building_an_osxi386_to_iosarm64_cross_compiler/d5qvd67/
disableLargeAddressSpace ? stdenv.targetPlatform.isiOS,
# Whether to build an unregisterised version of GHC.
# GHC will normally auto-detect whether it can do a registered build, but this
# option will force it to do an unregistered build when set to true.
# See https://gitlab.haskell.org/ghc/ghc/-/wikis/building/unregisterised
enableUnregisterised ? false,
}:
assert !enableNativeBignum -> gmp != null;
# GHC does not support building when all 3 platforms are different.
assert stdenv.buildPlatform == stdenv.hostPlatform || stdenv.hostPlatform == stdenv.targetPlatform;
# It is currently impossible to cross-compile GHC with Hadrian.
assert lib.assertMsg (stdenv.buildPlatform == stdenv.hostPlatform)
"GHC >= 9.6 can't be cross-compiled. If you meant to build a GHC cross-compiler, use `buildPackages`.";
let
inherit (stdenv) buildPlatform hostPlatform targetPlatform;
# TODO(@Ericson2314) Make unconditional
targetPrefix = lib.optionalString (targetPlatform != hostPlatform) "${targetPlatform.config}-";
# TODO(@sternenseemann): there's no stage0:exe:haddock target by default,
# so haddock isn't available for GHC cross-compilers. Can we fix that?
hasHaddock = stdenv.hostPlatform == stdenv.targetPlatform;
hadrianSettings =
# -fexternal-dynamic-refs apparently (because it's not clear from the
# documentation) makes the GHC RTS able to load static libraries, which may
# be needed for TemplateHaskell. This solution was described in
# https://www.tweag.io/blog/2020-09-30-bazel-static-haskell
lib.optionals enableRelocatedStaticLibs [
"*.*.ghc.*.opts += -fPIC -fexternal-dynamic-refs"
]
++ lib.optionals targetPlatform.useAndroidPrebuilt [
"*.*.ghc.c.opts += -optc-std=gnu99"
];
# Splicer will pull out correct variations
libDeps =
platform:
lib.optional enableTerminfo ncurses
++ lib.optionals (!targetPlatform.isGhcjs) [ libffi ]
# Bindist configure script fails w/o elfutils in linker search path
# https://gitlab.haskell.org/ghc/ghc/-/issues/22081
++ lib.optional enableDwarf elfutils
++ lib.optional (!enableNativeBignum) gmp
++ lib.optional (
platform.libc != "glibc"
&& !targetPlatform.isWindows
&& !targetPlatform.isGhcjs
&& !targetPlatform.useAndroidPrebuilt
) libiconv;
# TODO(@sternenseemann): is buildTarget LLVM unnecessary?
# GHC doesn't seem to have {LLC,OPT}_HOST
toolsForTarget = [
(
if targetPlatform.isGhcjs then
pkgsBuildTarget.emscripten
else
pkgsBuildTarget.targetPackages.stdenv.cc
)
]
++ lib.optional useLLVM buildTargetLlvmPackages.llvm;
buildCC = buildPackages.stdenv.cc;
targetCC = builtins.head toolsForTarget;
installCC =
if targetPlatform.isGhcjs then
pkgsHostTarget.emscripten
else
pkgsHostTarget.targetPackages.stdenv.cc;
# toolPath calculates the absolute path to the name tool associated with a
# given `stdenv.cc` derivation, i.e. it picks the correct derivation to take
# the tool from (cc, cc.bintools, cc.bintools.bintools) and adds the correct
# subpath of the tool.
toolPath =
name: cc:
let
tools =
{
"cc" = cc;
"c++" = cc;
as = cc.bintools;
ar = cc.bintools;
ranlib = cc.bintools;
nm = cc.bintools;
readelf = cc.bintools;
objdump = cc.bintools;
ld = cc.bintools;
"ld.gold" = cc.bintools;
windres = cc.bintools;
otool = cc.bintools.bintools;
# GHC needs install_name_tool on all darwin platforms. The same one can
# be used on both platforms. It is safe to use with linker-generated
# signatures because it will update the signatures automatically after
# modifying the target binary.
install_name_tool = cc.bintools.bintools;
# strip on darwin is wrapped to enable deterministic mode.
strip =
# TODO(@sternenseemann): also use wrapper if linker == "bfd" or "gold"
if stdenv.targetPlatform.isDarwin then cc.bintools else cc.bintools.bintools;
# clang is used as an assembler on darwin with the LLVM backend
clang = cc;
}
.${name};
in
getToolExe tools name;
# targetPrefix aware lib.getExe'
getToolExe = drv: name: lib.getExe' drv "${drv.targetPrefix or ""}${name}";
# Use gold either following the default, or to avoid the BFD linker due to some bugs / perf issues.
# But we cannot avoid BFD when using musl libc due to https://sourceware.org/bugzilla/show_bug.cgi?id=23856
# see #84670 and #49071 for more background.
useLdGold =
targetPlatform.linker == "gold"
|| (
targetPlatform.linker == "bfd"
&& (targetCC.bintools.bintools.hasGold or false)
&& !targetPlatform.isMusl
);
# Makes debugging easier to see which variant is at play in `nix-store -q --tree`.
variantSuffix = lib.concatStrings [
(lib.optionalString stdenv.hostPlatform.isMusl "-musl")
(lib.optionalString enableNativeBignum "-native-bignum")
];
# These libraries are library dependencies of the standard libraries bundled
# by GHC (core libs) users will link their compiled artifacts again. Thus,
# they should be taken from targetPackages.
#
# We need to use pkgsHostTarget if we are cross compiling a native GHC compiler,
# though (when native compiling GHC, pkgsHostTarget == targetPackages):
#
# 1. targetPackages would be empty(-ish) in this situation since we can't
# execute cross compiled compilers in order to obtain the libraries
# that would be in targetPackages.
# 2. pkgsHostTarget is fine to use since hostPlatform == targetPlatform in this
# situation.
# 3. The core libs used by the final GHC (stage 2) for user artifacts are also
# used to build stage 2 GHC itself, i.e. the core libs are both host and
# target.
targetLibs = {
inherit (if hostPlatform != targetPlatform then targetPackages else pkgsHostTarget)
elfutils
gmp
libffi
ncurses
numactl
;
};
# Our Cabal compiler name
haskellCompilerName = "ghc-${version}";
in
stdenv.mkDerivation (
{
pname = "${targetPrefix}ghc${variantSuffix}";
inherit version;
src = ghcSrc;
enableParallelBuilding = true;
postPatch = ''
patchShebangs --build .
'';
# GHC is a bit confused on its cross terminology.
# TODO(@sternenseemann): investigate coreutils dependencies and pass absolute paths
preConfigure = ''
for env in $(env | grep '^TARGET_' | sed -E 's|\+?=.*||'); do
export "''${env#TARGET_}=''${!env}"
done
# No need for absolute paths since these tools only need to work during the build
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}"
export CXX="${toolPath "c++" targetCC}"
# Use gold to work around https://sourceware.org/bugzilla/show_bug.cgi?id=16177
export LD="${toolPath "ld${lib.optionalString useLdGold ".gold"}" targetCC}"
export AS="${toolPath "as" targetCC}"
export AR="${toolPath "ar" targetCC}"
export NM="${toolPath "nm" targetCC}"
export RANLIB="${toolPath "ranlib" targetCC}"
export READELF="${toolPath "readelf" targetCC}"
export STRIP="${toolPath "strip" targetCC}"
export OBJDUMP="${toolPath "objdump" targetCC}"
''
+ lib.optionalString (stdenv.targetPlatform.linker == "cctools") ''
export OTOOL="${toolPath "otool" targetCC}"
export INSTALL_NAME_TOOL="${toolPath "install_name_tool" targetCC}"
''
+ lib.optionalString useLLVM ''
export LLC="${getToolExe buildTargetLlvmPackages.llvm "llc"}"
export OPT="${getToolExe buildTargetLlvmPackages.llvm "opt"}"
''
# LLVMAS should be a "specific LLVM compatible assembler" which needs to understand
# assembly produced by LLVM. The easiest way to be sure is to use clang from the same
# version as llc and opt. Note that the naming chosen by GHC is misleading, clang can
# be used as an assembler, llvm-as converts IR into machine code.
+ lib.optionalString (useLLVM && lib.versionAtLeast version "9.10") ''
export LLVMAS="${getToolExe buildTargetLlvmPackages.clang "clang"}"
''
+ lib.optionalString (useLLVM && stdenv.targetPlatform.isDarwin) ''
# LLVM backend on Darwin needs clang: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/codegens.html#llvm-code-generator-fllvm
# The executable we specify via $CLANG is used as an assembler (exclusively, it seems, but this isn't
# clarified in any user facing documentation). As such, it'll be called on assembly produced by $CC
# which usually comes from the darwin stdenv. To prevent a situation where $CLANG doesn't understand
# the assembly it is given, we need to make sure that it matches the LLVM version of $CC if possible.
# It is unclear (at the time of writing 2024-09-01) whether $CC should match the LLVM version we use
# for llc and opt which would require using a custom darwin stdenv for targetCC.
# 2025-09-06: The existence of LLVMAS suggests that matching $CC is fine (correct?) here.
export CLANG="${
if targetCC.isClang then
toolPath "clang" targetCC
else
getToolExe buildTargetLlvmPackages.clang "clang"
}"
''
# Haddock and sphinx need a working locale
+ lib.optionalString (enableDocs || hasHaddock) (
''
export LANG="en_US.UTF-8"
''
+ lib.optionalString (stdenv.buildPlatform.libc == "glibc") ''
export LOCALE_ARCHIVE="${buildPackages.glibcLocales}/lib/locale/locale-archive"
''
)
+ lib.optionalString (!stdenv.hostPlatform.isDarwin) ''
export NIX_LDFLAGS+=" -rpath $out/lib/ghc-${version}"
''
+ lib.optionalString stdenv.hostPlatform.isDarwin ''
export NIX_LDFLAGS+=" -no_dtrace_dof"
# GHC tries the host xattr /usr/bin/xattr by default which fails since it expects python to be 2.7
export XATTR=${lib.getBin xattr}/bin/xattr
''
# If we are not using release tarballs, some files need to be generated using
# the boot script.
+ lib.optionalString (rev != null) ''
echo ${version} > VERSION
echo ${rev} > GIT_COMMIT_ID
./boot
''
+ lib.optionalString targetPlatform.useAndroidPrebuilt ''
sed -i -e '5i ,("armv7a-unknown-linux-androideabi", ("e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64", "cortex-a8", ""))' llvm-targets
''
+ lib.optionalString targetPlatform.isMusl ''
echo "patching llvm-targets for musl targets..."
echo "Cloning these existing '*-linux-gnu*' targets:"
grep linux-gnu llvm-targets | sed 's/^/ /'
echo "(go go gadget sed)"
sed -i 's,\(^.*linux-\)gnu\(.*\)$,\0\n\1musl\2,' llvm-targets
echo "llvm-targets now contains these '*-linux-musl*' targets:"
grep linux-musl llvm-targets | sed 's/^/ /'
echo "And now patching to preserve '-musleabi' as done with '-gnueabi'"
# (aclocal.m4 is actual source, but patch configure as well since we don't re-gen)
for x in configure aclocal.m4; do
substituteInPlace $x \
--replace '*-android*|*-gnueabi*)' \
'*-android*|*-gnueabi*|*-musleabi*)'
done
''
# Need to make writable EM_CACHE for emscripten. The path in EM_CACHE must be absolute.
# https://gitlab.haskell.org/ghc/ghc/-/wikis/javascript-backend#configure-fails-with-sub-word-sized-atomic-operations-not-available
+ lib.optionalString targetPlatform.isGhcjs ''
export EM_CACHE="$(realpath $(mktemp -d emcache.XXXXXXXXXX))"
cp -Lr ${
targetCC # == emscripten
}/share/emscripten/cache/* "$EM_CACHE/"
chmod u+rwX -R "$EM_CACHE"
''
# Create bash array hadrianFlagsArray for use in buildPhase. Do it in
# preConfigure, so overrideAttrs can be used to modify it effectively.
# hadrianSettings are passed via the command line so they are more visible
# in the build log.
+ ''
hadrianFlagsArray=(
"-j$NIX_BUILD_CORES"
${lib.escapeShellArgs hadrianSettings}
)
'';
${if targetPlatform.isGhcjs then "configureScript" else null} = "emconfigure ./configure";
# GHC currently ships an edited config.sub so ghcjs is accepted which we can not rollback
${if targetPlatform.isGhcjs then "dontUpdateAutotoolsGnuConfigScripts" else null} = true;
# TODO(@Ericson2314): Always pass "--target" and always prefix.
configurePlatforms = [
"build"
"host"
]
++ lib.optional (targetPlatform != hostPlatform) "target";
# `--with` flags for libraries needed for RTS linker
configureFlags = [
"--datadir=$doc/share/doc/ghc"
]
++ lib.optionals enableTerminfo [
"--with-curses-includes=${lib.getDev targetLibs.ncurses}/include"
"--with-curses-libraries=${lib.getLib targetLibs.ncurses}/lib"
]
++ lib.optionals (libffi != null && !targetPlatform.isGhcjs) [
"--with-system-libffi"
"--with-ffi-includes=${targetLibs.libffi.dev}/include"
"--with-ffi-libraries=${targetLibs.libffi.out}/lib"
]
++ lib.optionals (targetPlatform == hostPlatform && !enableNativeBignum) [
"--with-gmp-includes=${targetLibs.gmp.dev}/include"
"--with-gmp-libraries=${targetLibs.gmp.out}/lib"
]
++
lib.optionals
(targetPlatform == hostPlatform && hostPlatform.libc != "glibc" && !targetPlatform.isWindows)
[
"--with-iconv-includes=${libiconv}/include"
"--with-iconv-libraries=${libiconv}/lib"
]
++ lib.optionals (targetPlatform != hostPlatform) [
"--enable-bootstrap-with-devel-snapshot"
]
++ lib.optionals useLdGold [
"CFLAGS=-fuse-ld=gold"
"CONF_GCC_LINKER_OPTS_STAGE1=-fuse-ld=gold"
"CONF_GCC_LINKER_OPTS_STAGE2=-fuse-ld=gold"
]
++ lib.optionals disableLargeAddressSpace [
"--disable-large-address-space"
]
++ lib.optionals enableDwarf [
"--enable-dwarf-unwind"
"--with-libdw-includes=${lib.getDev targetLibs.elfutils}/include"
"--with-libdw-libraries=${lib.getLib targetLibs.elfutils}/lib"
]
++ lib.optionals enableNuma [
"--enable-numa"
"--with-libnuma-includes=${lib.getDev targetLibs.numactl}/include"
"--with-libnuma-libraries=${lib.getLib targetLibs.numactl}/lib"
]
++ lib.optionals targetPlatform.isDarwin [
# Darwin uses llvm-ar. GHC will try to use `-L` with `ar` when it is `llvm-ar`
# but it doesnt currently work because Cabal never uses `-L` on Darwin. See:
# https://gitlab.haskell.org/ghc/ghc/-/issues/23188
# https://github.com/haskell/cabal/issues/8882
"fp_cv_prog_ar_supports_dash_l=no"
]
++ lib.optionals enableUnregisterised [
"--enable-unregisterised"
]
++
lib.optionals
(stdenv.buildPlatform.isAarch64 && stdenv.buildPlatform.isMusl && lib.versionOlder version "9.12")
[
# The bootstrap binaries for aarch64 musl were built for the wrong triple.
# https://gitlab.haskell.org/ghc/ghc/-/merge_requests/13182
"--enable-ignore-build-platform-mismatch"
];
# Make sure we never relax`$PATH` and hooks support for compatibility.
strictDeps = true;
# Dont add -liconv to LDFLAGS automatically so that GHC will add it itself.
dontAddExtraLibs = true;
nativeBuildInputs = [
autoreconfHook
perl
hadrian
bootPkgs.alex
bootPkgs.happy
bootPkgs.hscolour
# Python is used in a few scripts invoked by hadrian to generate e.g. rts headers.
python3
# Tool used to update GHC's settings file in postInstall
bootPkgs.ghc-settings-edit
]
++ lib.optionals (stdenv.hostPlatform.isDarwin && stdenv.hostPlatform.isAarch64) [
autoSignDarwinBinariesHook
]
++ lib.optionals enableDocs [
sphinx
];
# For building runtime libs
depsBuildTarget = toolsForTarget;
# Everything the stage0 compiler needs to build stage1: CC, bintools, extra libs.
# See also GHC, {CC,LD,AR}_STAGE0 in preConfigure.
depsBuildBuild = [
# 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
];
# Prevent stage0 ghc from leaking into the final result. This was an issue
# with GHC 9.6.
disallowedReferences = [
bootPkgs.ghc
];
buildInputs = [ bash ] ++ (libDeps hostPlatform);
# stage0:ghc (i.e. stage1) doesn't need to link against libnuma, so it's target specific
depsTargetTarget = map lib.getDev (
libDeps targetPlatform ++ lib.optionals enableNuma [ targetLibs.numactl ]
);
depsTargetTargetPropagated = map (lib.getOutput "out") (
libDeps targetPlatform ++ lib.optionals enableNuma [ targetLibs.numactl ]
);
hadrianFlags = [
"--flavour=${ghcFlavour}"
"--bignum=${if enableNativeBignum then "native" else "gmp"}"
"--docs=${if enableDocs then "no-sphinx-pdfs" else "no-sphinx"}"
]
++ lib.optionals (lib.versionAtLeast version "9.8") [
# In 9.14 this will be default with release flavour.
# See https://gitlab.haskell.org/ghc/ghc/-/merge_requests/13444
"--hash-unit-ids"
];
buildPhase = ''
runHook preBuild
# hadrianFlagsArray is created in preConfigure
echo "hadrianFlags: $hadrianFlags ''${hadrianFlagsArray[@]}"
# We need to go via the bindist for installing
hadrian $hadrianFlags "''${hadrianFlagsArray[@]}" binary-dist-dir
runHook postBuild
'';
# required, because otherwise all symbols from HSffi.o are stripped, and
# that in turn causes GHCi to abort
stripDebugFlags = [ "-S" ] ++ lib.optional (!targetPlatform.isDarwin) "--keep-file-symbols";
checkTarget = "test";
# GHC cannot currently produce outputs that are ready for `-pie` linking.
# Thus, disable `pie` hardening, otherwise `recompile with -fPIE` errors appear.
# See:
# * https://github.com/NixOS/nixpkgs/issues/129247
# * https://gitlab.haskell.org/ghc/ghc/-/issues/19580
hardeningDisable = [
"format"
"pie"
];
# big-parallel allows us to build with more than 2 cores on
# Hydra which already warrants a significant speedup
requiredSystemFeatures = [ "big-parallel" ];
outputs = [
"out"
"doc"
];
# We need to configure the bindist *again* before installing
# https://gitlab.haskell.org/ghc/ghc/-/issues/22058
# TODO(@sternenseemann): it would be nice if the bindist could be an intermediate
# derivation, but since it is > 2GB even on x86_64-linux, not a good idea?
preInstall = ''
pushd _build/bindist/*
''
# the bindist configure script uses different env variables than the GHC configure script
# see https://github.com/NixOS/nixpkgs/issues/267250 and https://gitlab.haskell.org/ghc/ghc/-/issues/24211
+ lib.optionalString (stdenv.targetPlatform.linker == "cctools") ''
export InstallNameToolCmd=$INSTALL_NAME_TOOL
export OtoolCmd=$OTOOL
''
+ ''
$configureScript $configureFlags "''${configureFlagsArray[@]}"
'';
postInstall = ''
# leave bindist directory
popd
settingsFile="$out/lib/${targetPrefix}${haskellCompilerName}/lib/settings"
# Make the installed GHC use the host->target tools.
ghc-settings-edit "$settingsFile" \
"C compiler command" "${toolPath "cc" installCC}" \
"Haskell CPP command" "${toolPath "cc" installCC}" \
"C++ compiler command" "${toolPath "c++" installCC}" \
"ld command" "${toolPath "ld${lib.optionalString useLdGold ".gold"}" installCC}" \
"Merge objects command" "${toolPath "ld${lib.optionalString useLdGold ".gold"}" installCC}" \
"ar command" "${toolPath "ar" installCC}" \
"ranlib command" "${toolPath "ranlib" installCC}"
''
+ lib.optionalString (stdenv.targetPlatform.linker == "cctools") ''
ghc-settings-edit "$settingsFile" \
"otool command" "${toolPath "otool" installCC}" \
"install_name_tool command" "${toolPath "install_name_tool" installCC}"
''
+ lib.optionalString useLLVM ''
ghc-settings-edit "$settingsFile" \
"LLVM llc command" "${getToolExe llvmPackages.llvm "llc"}" \
"LLVM opt command" "${getToolExe llvmPackages.llvm "opt"}"
''
# See comment for LLVMAS in preConfigure
+ lib.optionalString (useLLVM && lib.versionAtLeast version "9.10") ''
ghc-settings-edit "$settingsFile" \
"LLVM llvm-as command" "${getToolExe llvmPackages.clang "clang"}"
''
+ lib.optionalString (useLLVM && stdenv.targetPlatform.isDarwin) ''
ghc-settings-edit "$settingsFile" \
"LLVM clang command" "${
# See comment for CLANG in preConfigure
if installCC.isClang then toolPath "clang" installCC else getToolExe llvmPackages.clang "clang"
}"
''
+ lib.optionalString stdenv.targetPlatform.isWindows ''
ghc-settings-edit "$settingsFile" \
"windres command" "${toolPath "windres" installCC}"
''
+ ''
# Install the bash completion file.
install -Dm 644 utils/completion/ghc.bash $out/share/bash-completion/completions/${targetPrefix}ghc
'';
passthru = {
inherit bootPkgs targetPrefix haskellCompilerName;
inherit llvmPackages;
inherit enableShared;
inherit hasHaddock;
# Expose hadrian used for bootstrapping, for debugging purposes
inherit hadrian;
bootstrapAvailable = lib.meta.availableOn stdenv.buildPlatform bootPkgs.ghc;
};
meta = {
homepage = "http://haskell.org/ghc";
description = "Glasgow Haskell Compiler";
maintainers = with lib.maintainers; [
guibou
];
teams = [ lib.teams.haskell ];
timeout = 24 * 3600;
platforms = lib.platforms.all;
inherit (bootPkgs.ghc.meta) license;
# To be fixed by <https://github.com/NixOS/nixpkgs/pull/440774>.
broken = useLLVM;
};
dontStrip = targetPlatform.useAndroidPrebuilt || targetPlatform.isWasm;
}
// lib.optionalAttrs targetPlatform.useAndroidPrebuilt {
dontPatchELF = true;
noAuditTmpdir = true;
}
)

View File

@@ -0,0 +1,14 @@
# Determines whether the Native Code Generation (NCG) backend of the given
# GHC `version` is supported for compiling to `stdenv.targetPlatform`.
{
version,
stdenv,
lib,
}:
stdenv.targetPlatform.isx86
|| stdenv.targetPlatform.isPower
|| (lib.versionOlder version "9.4" && stdenv.targetPlatform.isSparc)
|| (lib.versionAtLeast version "9.2" && stdenv.targetPlatform.isAarch64)
|| (lib.versionAtLeast version "9.6" && stdenv.targetPlatform.isGhcjs)
|| (lib.versionAtLeast version "9.12" && stdenv.targetPlatform.isRiscV64)

View File

@@ -0,0 +1,685 @@
{
version,
sha256,
url ? "https://downloads.haskell.org/ghc/${version}/ghc-${version}-src.tar.xz",
}:
{
lib,
stdenv,
pkgsBuildTarget,
pkgsHostTarget,
buildPackages,
targetPackages,
# build-tools
bootPkgs,
autoconf,
automake,
coreutils,
fetchpatch,
fetchurl,
perl,
python3,
m4,
sphinx,
xattr,
autoSignDarwinBinariesHook,
bash,
libiconv ? null,
ncurses,
# GHC can be built with system libffi or a bundled one.
libffi ? null,
useLLVM ? !(import ./common-have-ncg.nix { inherit lib stdenv version; }),
# LLVM is conceptually a run-time-only dependency, but for
# non-x86, we need LLVM to bootstrap later stages, so it becomes a
# build-time dependency too.
buildTargetLlvmPackages,
llvmPackages,
# If enabled, GHC will be built with the GPL-free but slightly slower native
# bignum backend instead of the faster but GPLed gmp backend.
enableNativeBignum ?
!(lib.meta.availableOn stdenv.hostPlatform gmp && lib.meta.availableOn stdenv.targetPlatform gmp),
gmp,
# If enabled, use -fPIC when compiling static libs.
enableRelocatedStaticLibs ? stdenv.targetPlatform != stdenv.hostPlatform,
# Exceeds Hydra output limit (at the time of writing ~3GB) when cross compiled to riscv64.
# A riscv64 cross-compiler fits into the limit comfortably.
enableProfiledLibs ? !stdenv.hostPlatform.isRiscV64,
# Whether to build dynamic libs for the standard library (on the target
# platform). Static libs are always built.
enableShared ? with stdenv.targetPlatform; !isWindows && !useiOSPrebuilt && !isStatic,
# Whether to build terminfo.
enableTerminfo ?
!(
stdenv.targetPlatform.isWindows
# terminfo can't be built for cross
|| (stdenv.buildPlatform != stdenv.hostPlatform)
|| (stdenv.hostPlatform != stdenv.targetPlatform)
),
# Enable NUMA support in RTS
enableNuma ? lib.meta.availableOn stdenv.targetPlatform numactl,
numactl,
# What flavour to build. An empty string indicates no
# specific flavour and falls back to ghc default values.
ghcFlavour ? lib.optionalString (stdenv.targetPlatform != stdenv.hostPlatform) (
if useLLVM then "perf-cross" else "perf-cross-ncg"
),
# Whether to build sphinx documentation.
enableDocs ? (
# Docs disabled if we are building on musl because it's a large task to keep
# all `sphinx` dependencies building in this environment.
!stdenv.buildPlatform.isMusl
),
enableHaddockProgram ?
# Disabled for cross; see note [HADDOCK_DOCS].
(stdenv.buildPlatform == stdenv.hostPlatform && stdenv.targetPlatform == stdenv.hostPlatform),
# Whether to disable the large address space allocator
# necessary fix for iOS: https://www.reddit.com/r/haskell/comments/4ttdz1/building_an_osxi386_to_iosarm64_cross_compiler/d5qvd67/
disableLargeAddressSpace ? stdenv.targetPlatform.isiOS,
# Whether to build an unregisterised version of GHC.
# GHC will normally auto-detect whether it can do a registered build, but this
# option will force it to do an unregistered build when set to true.
# See https://gitlab.haskell.org/ghc/ghc/-/wikis/building/unregisterised
# Registerised RV64 compiler produces programs that segfault
# See https://gitlab.haskell.org/ghc/ghc/-/issues/23957
enableUnregisterised ? stdenv.hostPlatform.isRiscV64 || stdenv.targetPlatform.isRiscV64,
}:
assert !enableNativeBignum -> gmp != null;
# Cross cannot currently build the `haddock` program for silly reasons,
# see note [HADDOCK_DOCS].
assert
(stdenv.buildPlatform != stdenv.hostPlatform || stdenv.targetPlatform != stdenv.hostPlatform)
-> !enableHaddockProgram;
# GHC does not support building when all 3 platforms are different.
assert stdenv.buildPlatform == stdenv.hostPlatform || stdenv.hostPlatform == stdenv.targetPlatform;
let
inherit (stdenv) buildPlatform hostPlatform targetPlatform;
# TODO(@Ericson2314) Make unconditional
targetPrefix = lib.optionalString (targetPlatform != hostPlatform) "${targetPlatform.config}-";
buildMK = ''
BuildFlavour = ${ghcFlavour}
ifneq \"\$(BuildFlavour)\" \"\"
include mk/flavours/\$(BuildFlavour).mk
endif
BUILD_SPHINX_HTML = ${if enableDocs then "YES" else "NO"}
BUILD_SPHINX_PDF = NO
WITH_TERMINFO = ${if enableTerminfo then "YES" else "NO"}
''
+
# Note [HADDOCK_DOCS]:
# Unfortunately currently `HADDOCK_DOCS` controls both whether the `haddock`
# program is built (which we generally always want to have a complete GHC install)
# and whether it is run on the GHC sources to generate hyperlinked source code
# (which is impossible for cross-compilation); see:
# https://gitlab.haskell.org/ghc/ghc/-/issues/20077
# This implies that currently a cross-compiled GHC will never have a `haddock`
# program, so it can never generate haddocks for any packages.
# If this is solved in the future, we'd like to unconditionally
# build the haddock program (removing the `enableHaddockProgram` option).
''
HADDOCK_DOCS = ${if enableHaddockProgram then "YES" else "NO"}
# Build haddocks for boot packages with hyperlinking
EXTRA_HADDOCK_OPTS += --hyperlinked-source --quickjump
DYNAMIC_GHC_PROGRAMS = ${if enableShared then "YES" else "NO"}
BIGNUM_BACKEND = ${if enableNativeBignum then "native" else "gmp"}
''
+ lib.optionalString (targetPlatform != hostPlatform) ''
Stage1Only = ${if targetPlatform.system == hostPlatform.system then "NO" else "YES"}
CrossCompilePrefix = ${targetPrefix}
''
+ lib.optionalString (!enableProfiledLibs) ''
BUILD_PROF_LIBS = NO
''
+
# -fexternal-dynamic-refs apparently (because it's not clear from the documentation)
# makes the GHC RTS able to load static libraries, which may be needed for TemplateHaskell.
# This solution was described in https://www.tweag.io/blog/2020-09-30-bazel-static-haskell
lib.optionalString enableRelocatedStaticLibs ''
GhcLibHcOpts += -fPIC -fexternal-dynamic-refs
GhcRtsHcOpts += -fPIC -fexternal-dynamic-refs
''
+ lib.optionalString targetPlatform.useAndroidPrebuilt ''
EXTRA_CC_OPTS += -std=gnu99
'';
# Splicer will pull out correct variations
libDeps =
platform:
lib.optional enableTerminfo ncurses
++ [ libffi ]
++ lib.optional (!enableNativeBignum) gmp
++ lib.optional (platform.libc != "glibc" && !targetPlatform.isWindows) libiconv;
# TODO(@sternenseemann): is buildTarget LLVM unnecessary?
# GHC doesn't seem to have {LLC,OPT}_HOST
toolsForTarget = [
pkgsBuildTarget.targetPackages.stdenv.cc
]
++ lib.optional useLLVM buildTargetLlvmPackages.llvm;
buildCC = buildPackages.stdenv.cc;
targetCC = builtins.head toolsForTarget;
installCC = pkgsHostTarget.targetPackages.stdenv.cc;
# toolPath calculates the absolute path to the name tool associated with a
# given `stdenv.cc` derivation, i.e. it picks the correct derivation to take
# the tool from (cc, cc.bintools, cc.bintools.bintools) and adds the correct
# subpath of the tool.
toolPath =
name: cc:
let
tools =
{
"cc" = cc;
"c++" = cc;
as = cc.bintools;
ar = cc.bintools;
ranlib = cc.bintools;
nm = cc.bintools;
readelf = cc.bintools;
objdump = cc.bintools;
ld = cc.bintools;
"ld.gold" = cc.bintools;
otool = cc.bintools.bintools;
# GHC needs install_name_tool on all darwin platforms. The same one can
# be used on both platforms. It is safe to use with linker-generated
# signatures because it will update the signatures automatically after
# modifying the target binary.
install_name_tool = cc.bintools.bintools;
# strip on darwin is wrapped to enable deterministic mode.
strip =
# TODO(@sternenseemann): also use wrapper if linker == "bfd" or "gold"
if stdenv.targetPlatform.isDarwin then cc.bintools else cc.bintools.bintools;
# clang is used as an assembler on darwin with the LLVM backend
clang = cc;
}
.${name};
in
getToolExe tools name;
# targetPrefix aware lib.getExe'
getToolExe = drv: name: lib.getExe' drv "${drv.targetPrefix or ""}${name}";
# Use gold either following the default, or to avoid the BFD linker due to some bugs / perf issues.
# But we cannot avoid BFD when using musl libc due to https://sourceware.org/bugzilla/show_bug.cgi?id=23856
# see #84670 and #49071 for more background.
useLdGold =
targetPlatform.linker == "gold"
|| (
targetPlatform.linker == "bfd"
&& (targetCC.bintools.bintools.hasGold or false)
&& !targetPlatform.isMusl
);
# Makes debugging easier to see which variant is at play in `nix-store -q --tree`.
variantSuffix = lib.concatStrings [
(lib.optionalString stdenv.hostPlatform.isMusl "-musl")
(lib.optionalString enableNativeBignum "-native-bignum")
];
# These libraries are library dependencies of the standard libraries bundled
# by GHC (core libs) users will link their compiled artifacts again. Thus,
# they should be taken from targetPackages.
#
# We need to use pkgsHostTarget if we are cross compiling a native GHC compiler,
# though (when native compiling GHC, pkgsHostTarget == targetPackages):
#
# 1. targetPackages would be empty(-ish) in this situation since we can't
# execute cross compiled compilers in order to obtain the libraries
# that would be in targetPackages.
# 2. pkgsHostTarget is fine to use since hostPlatform == targetPlatform in this
# situation.
# 3. The core libs used by the final GHC (stage 2) for user artifacts are also
# used to build stage 2 GHC itself, i.e. the core libs are both host and
# target.
targetLibs = {
inherit (if hostPlatform != targetPlatform then targetPackages else pkgsHostTarget)
gmp
libffi
ncurses
numactl
;
};
in
stdenv.mkDerivation (
rec {
pname = "${targetPrefix}ghc${variantSuffix}";
inherit version;
src = fetchurl {
inherit url sha256;
};
enableParallelBuilding = true;
outputs = [
"out"
"doc"
];
patches = [
# Determine size of time related types using hsc2hs instead of assuming CLong.
# Prevents failures when e.g. stat(2)ing on 32bit systems with 64bit time_t etc.
# https://github.com/haskell/ghcup-hs/issues/1107
# https://gitlab.haskell.org/ghc/ghc/-/issues/25095
# Note that in normal situations this shouldn't be the case since nixpkgs
# doesn't set -D_FILE_OFFSET_BITS=64 and friends (yet).
(fetchpatch {
name = "unix-fix-ctimeval-size-32-bit.patch";
url = "https://github.com/haskell/unix/commit/8183e05b97ce870dd6582a3677cc82459ae566ec.patch";
sha256 = "17q5yyigqr5kxlwwzb95sx567ysfxlw6bp3j4ji20lz0947aw6gv";
stripLen = 1;
extraPrefix = "libraries/unix/";
})
# Fix docs build with Sphinx >= 7 https://gitlab.haskell.org/ghc/ghc/-/issues/24129
./docs-sphinx-7.patch
# Correctly record libnuma's library and include directories in the
# package db. This fixes linking whenever stdenv and propagation won't
# quite pass the correct -L flags to the linker, e.g. when using GHC
# outside of stdenv/nixpkgs or build->build compilation in pkgsStatic.
./ghc-9.4-rts-package-db-libnuma-dirs.patch
]
# Before GHC 9.6, GHC, when used to compile C sources (i.e. to drive the CC), would first
# invoke the C compiler to generate assembly and later call the assembler on the result of
# that operation. Unfortunately, that is brittle in a lot of cases, e.g. when using mismatched
# CC / assembler (https://gitlab.haskell.org/ghc/ghc/-/merge_requests/12005). This issue
# does not affect us. However, LLVM 18 introduced a check in clang that makes sure no
# non private labels occur between .cfi_startproc and .cfi_endproc which causes the
# assembly that the same version (!) of clang generates from rts/StgCRun.c to be rejected.
# This causes GHC to fail compilation on mach-o platforms ever since we upgraded to
# LLVM 19.
#
# clang compiles the same file without issues whithout the roundtrip via assembly. Thus,
# the solution is to backport those changes from GHC 9.6 that skip the intermediate
# assembly step.
#
# https://gitlab.haskell.org/ghc/ghc/-/issues/25608#note_622589
# https://gitlab.haskell.org/ghc/ghc/-/merge_requests/6877
++ [
# Need to use this patch so the next one applies, passes file location info to the cc phase
(fetchpatch {
name = "ghc-add-location-to-cc-phase.patch";
url = "https://gitlab.haskell.org/ghc/ghc/-/commit/4a7256a75af2fc0318bef771a06949ffb3939d5a.patch";
hash = "sha256-DnTI+i1zMebeWvw75D59vMaEEBb2Nr9HusxTyhmdy2M=";
})
# Makes Cc phase directly generate object files instead of assembly
(fetchpatch {
name = "ghc-cc-directly-emit-object.patch";
url = "https://gitlab.haskell.org/ghc/ghc/-/commit/96811ba491495b601ec7d6a32bef8563b0292109.patch";
hash = "sha256-G8u7/MK/tGOEN8Wxccxj/YIOP7mL2G9Co1WKdHXOo6I=";
})
]
++ [
# Don't generate code that doesn't compile when --enable-relocatable is passed to Setup.hs
# Can be removed if the Cabal library included with ghc backports the linked fix
(fetchpatch {
url = "https://github.com/haskell/cabal/commit/6c796218c92f93c95e94d5ec2d077f6956f68e98.patch";
stripLen = 1;
extraPrefix = "libraries/Cabal/";
sha256 = "sha256-yRQ6YmMiwBwiYseC5BsrEtDgFbWvst+maGgDtdD0vAY=";
})
]
# Fixes stack overrun in rts which crashes an process whenever
# freeHaskellFunPtr is called with nixpkgs' hardening flags.
# https://gitlab.haskell.org/ghc/ghc/-/issues/25485
# https://gitlab.haskell.org/ghc/ghc/-/merge_requests/13599
# TODO: patch doesn't apply for < 9.4, but may still be necessary?
++ [
(fetchpatch {
name = "ghc-rts-adjustor-fix-i386-stack-overrun.patch";
url = "https://gitlab.haskell.org/ghc/ghc/-/commit/39bb6e583d64738db51441a556d499aa93a4fc4a.patch";
sha256 = "0w5fx413z924bi2irsy1l4xapxxhrq158b5gn6jzrbsmhvmpirs0";
})
]
++ lib.optionals (stdenv.targetPlatform.isDarwin && stdenv.targetPlatform.isAarch64) [
# Prevent the paths module from emitting symbols that we don't use
# when building with separate outputs.
#
# These cause problems as they're not eliminated by GHC's dead code
# elimination on aarch64-darwin. (see
# https://github.com/NixOS/nixpkgs/issues/140774 for details).
./Cabal-at-least-3.6-paths-fix-cycle-aarch64-darwin.patch
];
postPatch = "patchShebangs .";
# GHC is a bit confused on its cross terminology.
# TODO(@sternenseemann): investigate coreutils dependencies and pass absolute paths
preConfigure = ''
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}"
export CXX="${toolPath "c++" targetCC}"
# Use gold to work around https://sourceware.org/bugzilla/show_bug.cgi?id=16177
export LD="${toolPath "ld${lib.optionalString useLdGold ".gold"}" targetCC}"
export AS="${toolPath "as" targetCC}"
export AR="${toolPath "ar" targetCC}"
export NM="${toolPath "nm" targetCC}"
export RANLIB="${toolPath "ranlib" targetCC}"
export READELF="${toolPath "readelf" targetCC}"
export STRIP="${toolPath "strip" targetCC}"
export OBJDUMP="${toolPath "objdump" targetCC}"
''
+ lib.optionalString (stdenv.targetPlatform.linker == "cctools") ''
export OTOOL="${toolPath "otool" targetCC}"
export INSTALL_NAME_TOOL="${toolPath "install_name_tool" targetCC}"
''
+ lib.optionalString useLLVM ''
export LLC="${getToolExe buildTargetLlvmPackages.llvm "llc"}"
export OPT="${getToolExe buildTargetLlvmPackages.llvm "opt"}"
''
+ lib.optionalString (useLLVM && stdenv.targetPlatform.isDarwin) ''
# LLVM backend on Darwin needs clang: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/codegens.html#llvm-code-generator-fllvm
# The executable we specify via $CLANG is used as an assembler (exclusively, it seems, but this isn't
# clarified in any user facing documentation). As such, it'll be called on assembly produced by $CC
# which usually comes from the darwin stdenv. To prevent a situation where $CLANG doesn't understand
# the assembly it is given, we need to make sure that it matches the LLVM version of $CC if possible.
# It is unclear (at the time of writing 2024-09-01) whether $CC should match the LLVM version we use
# for llc and opt which would require using a custom darwin stdenv for targetCC.
export CLANG="${
if targetCC.isClang then
toolPath "clang" targetCC
else
getToolExe buildTargetLlvmPackages.clang "clang"
}"
''
+ ''
# No need for absolute paths since these tools only need to work during the build
export CC_STAGE0="$CC_FOR_BUILD"
export LD_STAGE0="$LD_FOR_BUILD"
export AR_STAGE0="$AR_FOR_BUILD"
echo -n "${buildMK}" > mk/build.mk
sed -i -e 's|-isysroot /Developer/SDKs/MacOSX10.5.sdk||' configure
''
# Haddock and sphinx need a working locale
+ lib.optionalString (enableDocs || enableHaddockProgram) (
''
export LANG="en_US.UTF-8"
''
+ lib.optionalString (stdenv.buildPlatform.libc == "glibc") ''
export LOCALE_ARCHIVE="${buildPackages.glibcLocales}/lib/locale/locale-archive"
''
)
+ lib.optionalString (!stdenv.hostPlatform.isDarwin) ''
export NIX_LDFLAGS+=" -rpath $out/lib/ghc-${version}"
''
+ lib.optionalString stdenv.hostPlatform.isDarwin ''
export NIX_LDFLAGS+=" -no_dtrace_dof"
''
+ lib.optionalString (stdenv.hostPlatform.isDarwin) ''
# GHC tries the host xattr /usr/bin/xattr by default which fails since it expects python to be 2.7
export XATTR=${lib.getBin xattr}/bin/xattr
''
+ lib.optionalString targetPlatform.useAndroidPrebuilt ''
sed -i -e '5i ,("armv7a-unknown-linux-androideabi", ("e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64", "cortex-a8", ""))' llvm-targets
''
+ lib.optionalString targetPlatform.isMusl ''
echo "patching llvm-targets for musl targets..."
echo "Cloning these existing '*-linux-gnu*' targets:"
grep linux-gnu llvm-targets | sed 's/^/ /'
echo "(go go gadget sed)"
sed -i 's,\(^.*linux-\)gnu\(.*\)$,\0\n\1musl\2,' llvm-targets
echo "llvm-targets now contains these '*-linux-musl*' targets:"
grep linux-musl llvm-targets | sed 's/^/ /'
echo "And now patching to preserve '-musleabi' as done with '-gnueabi'"
# (aclocal.m4 is actual source, but patch configure as well since we don't re-gen)
for x in configure aclocal.m4; do
substituteInPlace $x \
--replace '*-android*|*-gnueabi*)' \
'*-android*|*-gnueabi*|*-musleabi*)'
done
'';
# Although it is usually correct to pass --host, we don't do that here because
# GHC's usage of build, host, and target is non-standard.
# See https://gitlab.haskell.org/ghc/ghc/-/wikis/building/cross-compiling
# TODO(@Ericson2314): Always pass "--target" and always prefix.
configurePlatforms = [
"build"
]
++ lib.optional (buildPlatform != hostPlatform || targetPlatform != hostPlatform) "target";
# `--with` flags for libraries needed for RTS linker
configureFlags = [
"--datadir=$doc/share/doc/ghc"
]
++ lib.optionals enableTerminfo [
"--with-curses-includes=${lib.getDev targetLibs.ncurses}/include"
"--with-curses-libraries=${lib.getLib targetLibs.ncurses}/lib"
]
++ lib.optionals (libffi != null) [
"--with-system-libffi"
"--with-ffi-includes=${targetLibs.libffi.dev}/include"
"--with-ffi-libraries=${targetLibs.libffi.out}/lib"
]
++ lib.optionals (targetPlatform == hostPlatform && !enableNativeBignum) [
"--with-gmp-includes=${targetLibs.gmp.dev}/include"
"--with-gmp-libraries=${targetLibs.gmp.out}/lib"
]
++
lib.optionals
(targetPlatform == hostPlatform && hostPlatform.libc != "glibc" && !targetPlatform.isWindows)
[
"--with-iconv-includes=${libiconv}/include"
"--with-iconv-libraries=${libiconv}/lib"
]
++ lib.optionals (targetPlatform != hostPlatform) [
"--enable-bootstrap-with-devel-snapshot"
]
++ lib.optionals useLdGold [
"CFLAGS=-fuse-ld=gold"
"CONF_GCC_LINKER_OPTS_STAGE1=-fuse-ld=gold"
"CONF_GCC_LINKER_OPTS_STAGE2=-fuse-ld=gold"
]
++ lib.optionals disableLargeAddressSpace [
"--disable-large-address-space"
]
++ lib.optionals enableNuma [
"--enable-numa"
"--with-libnuma-includes=${lib.getDev targetLibs.numactl}/include"
"--with-libnuma-libraries=${lib.getLib targetLibs.numactl}/lib"
]
++ lib.optionals enableUnregisterised [
"--enable-unregisterised"
];
# Make sure we never relax`$PATH` and hooks support for compatibility.
strictDeps = true;
# Dont add -liconv to LDFLAGS automatically so that GHC will add it itself.
dontAddExtraLibs = true;
nativeBuildInputs = [
perl
autoconf
automake
m4
python3
bootPkgs.alex
bootPkgs.happy
bootPkgs.hscolour
bootPkgs.ghc-settings-edit
]
++ lib.optionals (stdenv.hostPlatform.isDarwin && stdenv.hostPlatform.isAarch64) [
autoSignDarwinBinariesHook
]
++ lib.optionals enableDocs [
sphinx
];
# Everything the stage0 compiler needs to build stage1: CC, bintools, extra libs.
# See also GHC, {CC,LD,AR}_STAGE0 in preConfigure.
depsBuildBuild = [
# 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
];
# For building runtime libs
depsBuildTarget = toolsForTarget;
# Prevent stage0 ghc from leaking into the final result. This was an issue
# with GHC 9.6.
disallowedReferences = [
bootPkgs.ghc
];
buildInputs = [ bash ] ++ (libDeps hostPlatform);
# stage1 GHC doesn't need to link against libnuma, so it's target specific
depsTargetTarget = map lib.getDev (
libDeps targetPlatform ++ lib.optionals enableNuma [ targetLibs.numactl ]
);
depsTargetTargetPropagated = map (lib.getOutput "out") (
libDeps targetPlatform ++ lib.optionals enableNuma [ targetLibs.numactl ]
);
# required, because otherwise all symbols from HSffi.o are stripped, and
# that in turn causes GHCi to abort
stripDebugFlags = [ "-S" ] ++ lib.optional (!targetPlatform.isDarwin) "--keep-file-symbols";
checkTarget = "test";
# GHC cannot currently produce outputs that are ready for `-pie` linking.
# Thus, disable `pie` hardening, otherwise `recompile with -fPIE` errors appear.
# See:
# * https://github.com/NixOS/nixpkgs/issues/129247
# * https://gitlab.haskell.org/ghc/ghc/-/issues/19580
hardeningDisable = [
"format"
"pie"
];
# big-parallel allows us to build with more than 2 cores on
# Hydra which already warrants a significant speedup
requiredSystemFeatures = [ "big-parallel" ];
# Install occasionally fails due to a race condition in minimal builds.
# > /nix/store/wyzpysxwgs3qpvmylm9krmfzh2plicix-coreutils-9.7/bin/install -c -m 755 -d "/nix/store/xzb3390rhvhg2a0cvzmrvjspw1d8nf8h-ghc-riscv64-unknown-linux-gnu-9.4.8/bin"
# > install: cannot create regular file '/nix/store/xzb3390rhvhg2a0cvzmrvjspw1d8nf8h-ghc-riscv64-unknown-linux-gnu-9.4.8/lib/ghc-9.4.8': No such file or directory
preInstall = ''
mkdir -p "$out/lib/${passthru.haskellCompilerName}"
'';
postInstall = ''
settingsFile="$out/lib/${targetPrefix}${passthru.haskellCompilerName}/settings"
# Make the installed GHC use the host->target tools.
ghc-settings-edit "$settingsFile" \
"C compiler command" "${toolPath "cc" installCC}" \
"Haskell CPP command" "${toolPath "cc" installCC}" \
"C++ compiler command" "${toolPath "c++" installCC}" \
"ld command" "${toolPath "ld${lib.optionalString useLdGold ".gold"}" installCC}" \
"Merge objects command" "${toolPath "ld${lib.optionalString useLdGold ".gold"}" installCC}" \
"ar command" "${toolPath "ar" installCC}" \
"ranlib command" "${toolPath "ranlib" installCC}"
''
+ lib.optionalString (stdenv.targetPlatform.linker == "cctools") ''
ghc-settings-edit "$settingsFile" \
"otool command" "${toolPath "otool" installCC}" \
"install_name_tool command" "${toolPath "install_name_tool" installCC}"
''
+ lib.optionalString useLLVM ''
ghc-settings-edit "$settingsFile" \
"LLVM llc command" "${getToolExe llvmPackages.llvm "llc"}" \
"LLVM opt command" "${getToolExe llvmPackages.llvm "opt"}"
''
+ lib.optionalString (useLLVM && stdenv.targetPlatform.isDarwin) ''
ghc-settings-edit "$settingsFile" \
"LLVM clang command" "${
# See comment for CLANG in preConfigure
if installCC.isClang then toolPath "clang" installCC else getToolExe llvmPackages.clang "clang"
}"
''
+ ''
# Install the bash completion file.
install -D -m 444 utils/completion/ghc.bash $out/share/bash-completion/completions/${targetPrefix}ghc
'';
passthru = {
inherit bootPkgs targetPrefix;
inherit llvmPackages;
inherit enableShared;
# This is used by the haskell builder to query
# the presence of the haddock program.
hasHaddock = enableHaddockProgram;
# Our Cabal compiler name
haskellCompilerName = "ghc-${version}";
bootstrapAvailable = lib.meta.availableOn stdenv.buildPlatform bootPkgs.ghc;
};
meta = {
homepage = "http://haskell.org/ghc";
description = "Glasgow Haskell Compiler";
maintainers = with lib.maintainers; [
guibou
];
teams = [ lib.teams.haskell ];
timeout = 24 * 3600;
platforms = lib.platforms.all;
inherit (bootPkgs.ghc.meta) license;
# To be fixed by <https://github.com/NixOS/nixpkgs/pull/440774>.
broken = useLLVM;
};
}
// lib.optionalAttrs targetPlatform.useAndroidPrebuilt {
dontStrip = true;
dontPatchELF = true;
noAuditTmpdir = true;
}
)

View File

@@ -0,0 +1,8 @@
Fix build of docs after sphinx update.
https://github.com/sphinx-doc/sphinx/pull/11381
https://gitlab.haskell.org/ghc/ghc/-/issues/24129
--- a/docs/users_guide/rtd-theme/layout.html
+++ b/docs/users_guide/rtd-theme/layout.html
@@ -67 +67 @@
- <link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" />
+ <link rel="stylesheet" href="{{ pathto('_static/' + styles[-1], 1) }}" type="text/css" />

View File

@@ -0,0 +1,46 @@
#!@shell@
inPreprocessorMode () {
hasE=0
hasU=0
hasT=0
for arg in "$@"
do
if [ 'x-E' = "x$arg" ]; then hasE=1; fi
if [ 'x-undef' = "x$arg" ]; then hasU=1; fi
if [ 'x-traditional' = "x$arg" ]; then hasT=1; fi
done
[ "$hasE$hasU$hasT" = '111' ]
}
extraClangArgs="-Wno-invalid-pp-token -Wno-unicode -Wno-trigraphs"
adjustPreprocessorLanguage () {
newArgs=''
while [ $# -gt 0 ]
do
newArgs="$newArgs $1"
if [ "$1" = '-x' ]
then
shift
if [ $# -gt 0 ]
then
if [ "$1" = 'c' ]
then
newArgs="$newArgs assembler-with-cpp"
else
newArgs="$newArgs $1"
fi
fi
fi
shift
done
echo $newArgs
}
if inPreprocessorMode "$@"
then
exec clang $extraClangArgs `adjustPreprocessorLanguage "$@"`
else
exec clang $extraClangArgs "${@/-nodefaultlibs/}"
fi

View File

@@ -0,0 +1,100 @@
From a0b547f41939304adfc0c430314c342dd69306ae Mon Sep 17 00:00:00 2001
From: sterni <sternenseemann@systemli.org>
Date: Thu, 17 Jul 2025 21:21:29 +0200
Subject: [PATCH] rts: record libnuma include and lib dirs in package conf
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The --with-libnuma-libraries and --with-libnuma-includes flags were
originally introduced for hadrian in def486c90ef6f37d81d0d9c6df7544
and curiously never supported by the make build system — even though
the addition was made in the 9.0 series and even backported to the
8.10 series.
While the make build system knows when to link against libnuma, it won't
enforce its specific directories by adding them to rts.conf in the
package db. This commit implements this retroactively for the make build
system, modeled after how make does the same sort of thing for Libdw.
The Libdw logic also affects the bindist configure file in
distrib/configure.ac which isn't replicate since we don't need it.
---
mk/config.mk.in | 4 ++++
rts/ghc.mk | 8 ++++++++
rts/package.conf.in | 5 +++--
rts/rts.cabal.in | 1 +
4 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/mk/config.mk.in b/mk/config.mk.in
index 2ff2bea9b6..d95f927dbd 100644
--- a/mk/config.mk.in
+++ b/mk/config.mk.in
@@ -324,6 +324,10 @@ LibdwIncludeDir=@LibdwIncludeDir@
# rts/Libdw.c:set_initial_registers()
GhcRtsWithLibdw=$(strip $(if $(filter $(TargetArch_CPP),i386 x86_64 s390x),@UseLibdw@,NO))
+UseLibNuma=@UseLibNuma@
+LibNumaLibDir=@LibNumaLibDir@
+LibNumaIncludeDir=@LibNumaIncludeDir@
+
################################################################################
#
# Paths (see paths.mk)
diff --git a/rts/ghc.mk b/rts/ghc.mk
index 36a82f9f2c..854bb8e013 100644
--- a/rts/ghc.mk
+++ b/rts/ghc.mk
@@ -573,6 +573,14 @@ rts_PACKAGE_CPP_OPTS += -DLIBDW_INCLUDE_DIR=
rts_PACKAGE_CPP_OPTS += -DLIBDW_LIB_DIR=
endif
+ifeq "$(UseLibNuma)" "YES"
+rts_PACKAGE_CPP_OPTS += -DLIBNUMA_INCLUDE_DIR=$(LibNumaIncludeDir)
+rts_PACKAGE_CPP_OPTS += -DLIBNUMA_LIB_DIR=$(LibNumaLibDir)
+else
+rts_PACKAGE_CPP_OPTS += -DLIBNUMA_INCLUDE_DIR=
+rts_PACKAGE_CPP_OPTS += -DLIBNUMA_LIB_DIR=
+endif
+
# -----------------------------------------------------------------------------
# dependencies
diff --git a/rts/package.conf.in b/rts/package.conf.in
index cb5a436f5c..9e5ae48adb 100644
--- a/rts/package.conf.in
+++ b/rts/package.conf.in
@@ -18,9 +18,9 @@ hidden-modules:
import-dirs:
#if defined(INSTALLING)
-library-dirs: LIB_DIR FFI_LIB_DIR LIBDW_LIB_DIR
+library-dirs: LIB_DIR FFI_LIB_DIR LIBDW_LIB_DIR LIBNUMA_LIB_DIR
#else /* !INSTALLING */
-library-dirs: TOP"/rts/dist-install/build" FFI_LIB_DIR LIBDW_LIB_DIR
+library-dirs: TOP"/rts/dist-install/build" FFI_LIB_DIR LIBDW_LIB_DIR LIBNUMA_LIB_DIR
#endif
hs-libraries: "HSrts" FFI_LIB
@@ -74,6 +74,7 @@ include-dirs: TOP"/rts/include"
TOP"/rts/dist-install/build/include"
FFI_INCLUDE_DIR
LIBDW_INCLUDE_DIR
+ LIBNUMA_INCLUDE_DIR
#endif
includes: Rts.h
diff --git a/rts/rts.cabal.in b/rts/rts.cabal.in
index a8882268ac..debf2ba0a0 100644
--- a/rts/rts.cabal.in
+++ b/rts/rts.cabal.in
@@ -154,6 +154,7 @@ library
include-dirs: include
@FFIIncludeDir@
@LibdwIncludeDir@
+ @LibNumaIncludeDir@
includes: Rts.h
install-includes: Cmm.h HsFFI.h MachDeps.h Rts.h RtsAPI.h Stg.h
ghcautoconf.h ghcconfig.h ghcplatform.h ghcversion.h
--
2.50.0

View File

@@ -0,0 +1,24 @@
diff --git a/rts/linker/ElfTypes.h b/rts/linker/ElfTypes.h
index f5e2f819d9..7f75087738 100644
--- a/rts/linker/ElfTypes.h
+++ b/rts/linker/ElfTypes.h
@@ -33,6 +33,9 @@
#define Elf_Sym Elf64_Sym
#define Elf_Rel Elf64_Rel
#define Elf_Rela Elf64_Rela
+#if !defined(ELF64_ST_VISIBILITY)
+#define ELF64_ST_VISIBILITY(o) ((o)&0x3)
+#endif
#if !defined(ELF_ST_VISIBILITY)
#define ELF_ST_VISIBILITY ELF64_ST_VISIBILITY
#endif
@@ -60,6 +63,9 @@
#define Elf_Sym Elf32_Sym
#define Elf_Rel Elf32_Rel
#define Elf_Rela Elf32_Rela
+#if !defined(ELF32_ST_VISIBILITY)
+#define ELF32_ST_VISIBILITY(o) ((o)&0x3)
+#endif
#if !defined(ELF_ST_VISIBILITY)
#define ELF_ST_VISIBILITY ELF32_ST_VISIBILITY
#endif /* ELF_ST_VISIBILITY */

View File

@@ -0,0 +1,5 @@
import ./common-hadrian.nix {
version = "9.15.20250811";
rev = "c8d76a2994b8620c54adc2069f4728135d6b5059";
sha256 = "001rf9z5a1v2xpm9qjzz2p966m5bxmqcnykq0xgb3qf40vi9rnh4";
}

View File

@@ -0,0 +1,25 @@
diff -urd a/aclocal.m4 b/aclocal.m4
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -1199,7 +1199,8 @@
# thinks that target == host so it never checks the unqualified
# tools for Windows. See #14274.
AC_DEFUN([FP_PROG_AR],
-[if test -z "$fp_prog_ar"; then
+[AC_SUBST(fp_prog_ar,$AR)
+if test -z "$fp_prog_ar"; then
if test "$HostOS" = "mingw32"
then
AC_PATH_PROG([fp_prog_ar], [ar])
diff -urd a/configure b/configure
--- a/configure
+++ b/configure
@@ -10744,6 +10744,8 @@
test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+fp_prog_ar=$AR
+
if test -z "$fp_prog_ar"; then
if test "$HostOS" = "mingw32"
then

View File

@@ -0,0 +1,80 @@
#!@shell@
# This script wraps the LLVM `opt(1)` executable and maps the options
# passed by old versions of GHC to the equivalents passed by newer
# versions that support recent versions of LLVM.
#
# It achieves the same effect as the following GHC change externally:
# <https://gitlab.haskell.org/ghc/ghc/-/merge_requests/8999>.
#
# This is used solely for bootstrapping newer GHCs from the GHC 9.0.2
# binary on AArch64, as that is the only architecture supported by that
# binary distribution that requires LLVM, and our later binary packages
# all use the native code generator for all supported platforms.
#
# No attempt is made to support custom LLVM optimization flags, or the
# undocumented flag to disable TBAA, or avoid
# <https://gitlab.haskell.org/ghc/ghc/-/issues/23870>, as these are not
# required to bootstrap GHC and at worst will produce an error message.
#
# It is called `subopt` to reflect the fact that it uses `opt(1)` as a
# subprocess, and the fact that the GHC build system situation
# requiring this hack is suboptimal.
set -e
expect() {
if [[ $1 != $2 ]]; then
printf >&2 'subopt: got %q; expected %q\n' "$1" "$2"
return 2
fi
}
if [[ $NIX_DEBUG -ge 1 ]]; then
printf >&2 'subopt: before:'
printf >&2 ' %q' "$@"
printf >&2 '\n'
fi
args=()
while [[ $# -gt 0 ]]; do
case "$1" in
-enable-new-pm=0)
shift 1
;;
-mem2reg)
expect "$2" -globalopt
expect "$3" -lower-expect
expect "$4" -enable-tbaa
expect "$5" -tbaa
args+=('-passes=function(require<tbaa>),function(mem2reg),globalopt,function(lower-expect)')
shift 5
;;
-O1)
expect "$2" -globalopt
expect "$3" -enable-tbaa
expect "$4" -tbaa
args+=('-passes=default<O1>')
shift 4
;;
-O2)
expect "$2" -enable-tbaa
expect "$3" -tbaa
args+=('-passes=default<O2>')
shift 3
;;
*)
args+=("$1")
shift 1
;;
esac
done
if [[ $NIX_DEBUG -ge 1 ]]; then
printf >&2 'subopt: after:'
printf >&2 ' %q' "${args[@]}"
printf >&2 '\n'
fi
exec @opt@ "${args[@]}"