2a59d27e69
LLVM 12–17 have been dropped for Nixpkgs 25.11. As discussed recently on Matrix, this backports upstream changes to allow the use of LLVM 20 for all GHC versions from 9.4.8 onward. I looked over GHC commits mentioning LLVM since the release of 9.4.8, and read the discussions and issues around the relevant bumps, and attempted to be quite thorough, but I obviously cannot guarantee that this is wholly comprehensive. It seems like upstream generally bumps the upper bound on the basis of “it builds successfully for me”, with specific adaptations for new versions being fairly uncommon and only coming for obvious build blockers or reactively in response to bug reports. I have backported both kinds of changes here. For some commits, trivial conflict resolutions and adaptations were required. It would be possible to pass the affected files to `fetchpatch` as `excludes` and keep smaller fix‐up patches in tree in some cases, but I opted to keep it simple and vendor complete backport patches instead. I did not attempt to backport every single change to the LLVM backend, only those that seemed directly relevant to support for newer versions; if you’d get the same issue with the older LLVM, that’s just a GHC bug. These changes should actually make it easier to cross‐compile for new architectures, as more recent LLVMs will have better support for newer platforms, and it will be easier to backport GHC changes to enable new platforms with less drift in the backend. These patches do result in two breaking changes. Firstly, the minimum LLVM version is bumped to 13 across the board. This is irrelevant for Nixpkgs as we pin a specific LLVM version anyway, and versions below LLVM 18 will be removed imminently. Secondly, support for the hidden `-fno-llvm-tbaa` flag is dropped. This can be replaced with custom `-optlo` flags to control the passes more directly, but the main use of this undocumented flag appears to have been to [work around] the lack of support for newer LLVM versions, anyway. [work around]: <https://gitlab.haskell.org/ghc/ghc/-/issues/22220> I successfully built the following on `aarch64-linux`: * `pkgsCross.armv7l-hf-multiplatform.buildPackages.haskell.compiler.ghc948` * `pkgsCross.armv7l-hf-multiplatform.buildPackages.haskell.compiler.ghc967` * `pkgsCross.armv7l-hf-multiplatform.buildPackages.haskell.compiler.ghc984` * `pkgsCross.armv7l-hf-multiplatform.buildPackages.haskell.compiler.ghc9102` * `pkgsCross.armv7l-hf-multiplatform.buildPackages.haskell.compiler.ghc9121` * `pkgsCross.armv7l-hf-multiplatform.buildPackages.haskell.compiler.ghc9122` * `pkgsCross.riscv64.haskell.compiler.ghc948` The GHC 9.4.8 with an ARMv7 host platform segfaults when I try to run GHC, though e.g. `ghc-pkg --help` runs successfully. The GHC 9.10.3 build targeting ARMv7 crashed inside `llc(1)`, so I tried RISC‐V, which has some platform mismatch issue relating to `libffi`, so I tried z/Architecture, which failed with an invalid floating point constant in the LLVM IR, so I tried 64‐bit MIPS, which failed with a different `libffi` issue, so I tried 32‐bit MIPS, which failed to compile `compiler-rt`, so I gave up. I confirmed that both of the ARMv7 issues reproduce with944e8fd4f4, the revision before they were bumped from their old versions of LLVM, so these are not regressions. I built a test program with the ARMv7 cross‐compilers and confirmed that they run on the AArch64 builder. I also confirmed that the cross‐compiled RISC‐V GHC successfully runs under `qemu-riscv64(1)`. It will only try to build programs via the C backend, though, as that is the only option for unregisterised™ targets, so it’s not clear to me how useful LLVM support in 9.4.8 really is for bootstrapping new platforms; I guess even RISC‐V would require more backporting work to produce a cross‐compiled GHC that will use LLVM to compile its own input. I didn’t bother setting up all the binfmt machinery to get it through compiling and running a test program, but it at least makes the attempt. (cherry picked from commitb6be8a03a7)
208 lines
7.8 KiB
Nix
208 lines
7.8 KiB
Nix
{
|
|
lib,
|
|
stdenv,
|
|
haskellPackages,
|
|
symlinkJoin,
|
|
makeWrapper,
|
|
# GHC will have LLVM available if necessary for the respective target,
|
|
# so useLLVM only needs to be changed if -fllvm is to be used for a
|
|
# platform that has NCG support
|
|
useLLVM ? false,
|
|
withHoogle ? false,
|
|
# Whether to install `doc` outputs for GHC and all included libraries.
|
|
installDocumentation ? true,
|
|
hoogleWithPackages,
|
|
postBuild ? "",
|
|
}:
|
|
|
|
# This argument is a function which selects a list of Haskell packages from any
|
|
# passed Haskell package set.
|
|
#
|
|
# Example:
|
|
# (hpkgs: [ hpkgs.mtl hpkgs.lens ])
|
|
selectPackages:
|
|
|
|
# It's probably a good idea to include the library "ghc-paths" in the
|
|
# compiler environment, because we have a specially patched version of
|
|
# that package in Nix that honors these environment variables
|
|
#
|
|
# NIX_GHC
|
|
# NIX_GHCPKG
|
|
# NIX_GHC_DOCDIR
|
|
# NIX_GHC_LIBDIR
|
|
#
|
|
# instead of hard-coding the paths. The wrapper sets these variables
|
|
# appropriately to configure ghc-paths to point back to the wrapper
|
|
# instead of to the pristine GHC package, which doesn't know any of the
|
|
# additional libraries.
|
|
#
|
|
# A good way to import the environment set by the wrapper below into
|
|
# your shell is to add the following snippet to your ~/.bashrc:
|
|
#
|
|
# if [ -e ~/.nix-profile/bin/ghc ]; then
|
|
# eval $(grep export ~/.nix-profile/bin/ghc)
|
|
# fi
|
|
|
|
let
|
|
inherit (haskellPackages) ghc;
|
|
|
|
hoogleWithPackages' = if withHoogle then hoogleWithPackages selectPackages else null;
|
|
|
|
packages = selectPackages haskellPackages ++ [ hoogleWithPackages' ];
|
|
|
|
isHaLVM = ghc.isHaLVM or false;
|
|
ghcCommand' = "ghc";
|
|
ghcCommand = "${ghc.targetPrefix}${ghcCommand'}";
|
|
ghcCommandCaps = lib.toUpper ghcCommand';
|
|
libDir =
|
|
if isHaLVM then
|
|
"$out/lib/HaLVM-${ghc.version}"
|
|
else
|
|
"$out/lib/${ghc.targetPrefix}${ghc.haskellCompilerName}"
|
|
+ lib.optionalString (ghc ? hadrian) "/lib";
|
|
docDir = "$out/share/doc/ghc/html";
|
|
packageCfgDir = "${libDir}/package.conf.d";
|
|
paths = lib.concatLists (
|
|
map (pkg: [ pkg ] ++ lib.optionals installDocumentation [ (lib.getOutput "doc" pkg) ]) (
|
|
lib.filter (x: x ? isHaskellLibrary) (lib.closePropagation packages)
|
|
)
|
|
);
|
|
hasLibraries = lib.any (x: x.isHaskellLibrary) paths;
|
|
# Clang is needed on Darwin for -fllvm to work.
|
|
# GHC >= 9.10 needs an LLVM specific assembler which we use clang for.
|
|
# https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/codegens.html#llvm-code-generator-fllvm
|
|
llvm = lib.makeBinPath (
|
|
[ ghc.llvmPackages.llvm ]
|
|
++ lib.optionals (lib.versionAtLeast ghc.version "9.10" || stdenv.targetPlatform.isDarwin) [
|
|
ghc.llvmPackages.clang
|
|
]
|
|
);
|
|
in
|
|
|
|
if paths == [ ] && !useLLVM then
|
|
ghc
|
|
else
|
|
symlinkJoin {
|
|
# this makes computing paths from the name attribute impossible;
|
|
# if such a feature is needed, the real compiler name should be saved
|
|
# as a dedicated drv attribute, like `compiler-name`
|
|
name = ghc.name + "-with-packages";
|
|
paths = paths ++ [ ghc ] ++ lib.optionals installDocumentation [ (lib.getOutput "doc" ghc) ];
|
|
nativeBuildInputs = [ makeWrapper ];
|
|
postBuild = ''
|
|
# wrap compiler executables with correct env variables
|
|
|
|
for prg in ${ghcCommand} ${ghcCommand}i ${ghcCommand}-${ghc.version} ${ghcCommand}i-${ghc.version}; do
|
|
if [[ -x "${ghc}/bin/$prg" ]]; then
|
|
rm -f $out/bin/$prg
|
|
makeWrapper ${ghc}/bin/$prg $out/bin/$prg \
|
|
--add-flags '"-B$NIX_${ghcCommandCaps}_LIBDIR"' \
|
|
--set "NIX_${ghcCommandCaps}" "$out/bin/${ghcCommand}" \
|
|
--set "NIX_${ghcCommandCaps}PKG" "$out/bin/${ghcCommand}-pkg" \
|
|
--set "NIX_${ghcCommandCaps}_DOCDIR" "${docDir}" \
|
|
--set "NIX_${ghcCommandCaps}_LIBDIR" "${libDir}" \
|
|
${lib.optionalString useLLVM ''--prefix "PATH" ":" "${llvm}"''}
|
|
fi
|
|
done
|
|
|
|
for prg in runghc runhaskell; do
|
|
if [[ -x "${ghc}/bin/$prg" ]]; then
|
|
rm -f $out/bin/$prg
|
|
makeWrapper ${ghc}/bin/$prg $out/bin/$prg \
|
|
--add-flags "-f $out/bin/${ghcCommand}" \
|
|
--set "NIX_${ghcCommandCaps}" "$out/bin/${ghcCommand}" \
|
|
--set "NIX_${ghcCommandCaps}PKG" "$out/bin/${ghcCommand}-pkg" \
|
|
--set "NIX_${ghcCommandCaps}_DOCDIR" "${docDir}" \
|
|
--set "NIX_${ghcCommandCaps}_LIBDIR" "${libDir}"
|
|
fi
|
|
done
|
|
|
|
for prg in ${ghcCommand}-pkg ${ghcCommand}-pkg-${ghc.version}; do
|
|
if [[ -x "${ghc}/bin/$prg" ]]; then
|
|
rm -f $out/bin/$prg
|
|
makeWrapper ${ghc}/bin/$prg $out/bin/$prg --add-flags "--global-package-db=${packageCfgDir}"
|
|
fi
|
|
done
|
|
|
|
# haddock needs to be wrapped like GHC, see
|
|
# https://github.com/NixOS/nixpkgs/issues/36976 krank:ignore-line
|
|
if [[ -x "${ghc}/bin/haddock" ]]; then
|
|
rm -f $out/bin/haddock
|
|
makeWrapper ${ghc}/bin/haddock $out/bin/haddock \
|
|
--add-flags '"-B$NIX_${ghcCommandCaps}_LIBDIR"' \
|
|
--set "NIX_${ghcCommandCaps}_LIBDIR" "${libDir}"
|
|
fi
|
|
|
|
''
|
|
+ (lib.optionalString (stdenv.targetPlatform.isDarwin && !stdenv.targetPlatform.isiOS) ''
|
|
# Work around a linker limit in macOS Sierra (see generic-builder.nix):
|
|
local packageConfDir="${packageCfgDir}";
|
|
local dynamicLinksDir="$out/lib/links";
|
|
mkdir -p $dynamicLinksDir
|
|
# Clean up the old links that may have been (transitively) included by
|
|
# symlinkJoin:
|
|
rm -f $dynamicLinksDir/*
|
|
|
|
dynamicLibraryDirs=()
|
|
|
|
for pkg in $($out/bin/ghc-pkg list --simple-output); do
|
|
dynamicLibraryDirs+=($($out/bin/ghc-pkg --simple-output field "$pkg" dynamic-library-dirs))
|
|
done
|
|
|
|
for dynamicLibraryDir in $(echo "''${dynamicLibraryDirs[@]}" | tr ' ' '\n' | sort -u); do
|
|
echo "Linking $dynamicLibraryDir/*.dylib from $dynamicLinksDir"
|
|
find "$dynamicLibraryDir" -name '*.dylib' -exec ln -s {} "$dynamicLinksDir" \;
|
|
done
|
|
|
|
for f in $packageConfDir/*.conf; do
|
|
# Initially, $f is a symlink to a read-only file in one of the inputs
|
|
# (as a result of this symlinkJoin derivation).
|
|
# Replace it with a copy whose dynamic-library-dirs points to
|
|
# $dynamicLinksDir
|
|
cp $f $f-tmp
|
|
rm $f
|
|
sed "N;s,dynamic-library-dirs:\s*.*\n,dynamic-library-dirs: $dynamicLinksDir\n," $f-tmp > $f
|
|
rm $f-tmp
|
|
done
|
|
'')
|
|
+ ''
|
|
${lib.optionalString hasLibraries ''
|
|
# GHC 8.10 changes.
|
|
# Instead of replacing package.cache[.lock] with the new file,
|
|
# ghc-pkg is now trying to open the file. These file are symlink
|
|
# to another nix derivation, so they are not writable. Removing
|
|
# them allow the correct behavior of ghc-pkg recache
|
|
# See: https://github.com/NixOS/nixpkgs/issues/79441 krank:ignore-line
|
|
rm ${packageCfgDir}/package.cache.lock
|
|
rm ${packageCfgDir}/package.cache
|
|
|
|
$out/bin/${ghcCommand}-pkg recache
|
|
''}
|
|
$out/bin/${ghcCommand}-pkg check
|
|
''
|
|
+ postBuild;
|
|
preferLocalBuild = true;
|
|
passthru = {
|
|
inherit (ghc) version meta targetPrefix;
|
|
|
|
hoogle = hoogleWithPackages';
|
|
|
|
# Inform users about backwards incompatibilities with <= 21.05
|
|
override =
|
|
_:
|
|
throw ''
|
|
The ghc.withPackages wrapper itself can now be overridden, but no longer
|
|
the result of calling it (as before). Consequently overrides need to be
|
|
adjusted: Instead of
|
|
|
|
(ghc.withPackages (p: [ p.my-package ])).override { withLLLVM = true; }
|
|
|
|
use
|
|
|
|
(ghc.withPackages.override { useLLVM = true; }) (p: [ p.my-package ])
|
|
|
|
Also note that withLLVM has been renamed to useLLVM for consistency with
|
|
the GHC Nix expressions.'';
|
|
};
|
|
}
|