2023-12-14 15:07:34 +00:00
{
lib ,
stdenv ,
fetchurl ,
perl ,
gcc ,
ncurses5 ,
ncurses6 ,
gmp ,
libiconv ,
numactl ,
libffi ,
llvmPackages ,
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 = " h t t p s : / / d o w n l o a d s . h a s k e l l . o r g / g h c " ;
# 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 specfic 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 } / g h c - ${ version } - i 3 8 6 - d e b 9 - l i n u x . t a r . x z " ;
sha256 = " 5 8 b e 2 6 f 8 b 8 f 6 b 5 b d 8 b a f 5 c 3 2 a b b 0 3 e 2 c 4 6 2 1 6 4 6 b 2 1 4 2 f a b 1 0 e 5 c 7 d e 5 a f 5 c 5 0 f 8 " ;
} ;
exePathForLibraryCheck = " b i n / g h c " ;
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 = " l i b t i n f o . s o . 5 " ;
}
] ;
} ;
x86_64-linux = {
variantSuffix = " " ;
src = {
url = " ${ downloadsUrl } / ${ version } / g h c - ${ version } - x 8 6 _ 6 4 - d e b 1 1 - l i n u x . t a r . x z " ;
sha256 = " c 4 c 0 1 2 4 8 5 7 2 6 5 9 2 6 f 1 c f 2 2 a 0 9 d 9 5 0 d 7 b a 9 8 9 f f 9 4 0 5 3 a 4 d d f 3 d c d a b 5 3 5 9 f 4 c a b 7 " ;
} ;
exePathForLibraryCheck = " b i n / g h c " ;
archSpecificLibraries = [
{
nixPackage = gmp ;
fileToCheckFor = null ;
}
{
nixPackage = ncurses6 ;
fileToCheckFor = " l i b t i n f o . s o . 6 " ;
}
] ;
} ;
aarch64-linux = {
variantSuffix = " " ;
src = {
url = " ${ downloadsUrl } / ${ version } / g h c - ${ version } - a a r c h 6 4 - d e b 1 0 - l i n u x . t a r . x z " ;
sha256 = " 0 3 c 3 8 9 8 5 9 3 1 9 f 0 9 4 5 2 0 8 1 3 1 0 f c 1 3 a f 7 5 2 5 0 6 3 e a 8 9 3 0 8 3 0 e f 7 6 b e 2 a 1 4 b 3 1 2 2 7 1 e " ;
} ;
exePathForLibraryCheck = " b i n / g h c " ;
archSpecificLibraries = [
{
nixPackage = gmp ;
fileToCheckFor = null ;
}
{
nixPackage = ncurses6 ;
fileToCheckFor = " l i b t i n f o . s o . 6 " ;
}
{
nixPackage = numactl ;
fileToCheckFor = null ;
}
] ;
} ;
x86_64-darwin = {
variantSuffix = " " ;
src = {
url = " ${ downloadsUrl } / ${ version } / g h c - ${ version } - x 8 6 _ 6 4 - a p p l e - d a r w i n . t a r . x z " ;
sha256 = " d d e 4 6 1 1 8 a b 8 3 8 8 f b 1 0 6 6 3 1 2 c 0 9 7 1 2 3 e 9 3 b 1 d c f 6 a e 3 6 6 e 3 3 7 0 f 8 8 e a 4 5 6 3 8 2 c 9 d b " ;
} ;
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 } / g h c - ${ version } - a a r c h 6 4 - a p p l e - d a r w i n . t a r . x z " ;
sha256 = " e 1 c d f 4 5 8 9 2 6 b 2 e a f 5 2 d 2 a 8 2 8 7 d 9 9 a 9 6 5 0 4 0 f f 9 0 5 1 1 7 1 f 5 c 3 b 7 4 6 7 0 4 9 c f 0 e b 2 1 3 " ;
} ;
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 = " - m u s l " ;
src = {
url = " ${ downloadsUrl } / ${ version } / g h c - ${ version } - x 8 6 _ 6 4 - a l p i n e 3 _ 1 2 - l i n u x . t a r . x z " ;
sha256 = " 8 f 4 5 7 a f 0 a a 4 0 1 2 7 0 4 9 c 1 1 1 3 4 c 8 7 9 3 f 6 4 3 5 1 a 4 4 6 e 8 7 d a 1 f 8 e c 2 5 6 e 1 2 7 9 b 5 a b 6 1 f " ;
} ;
exePathForLibraryCheck = " b i n / g h c " ;
archSpecificLibraries = [
{
nixPackage = gmp ;
fileToCheckFor = null ;
}
{
nixPackage = ncurses6 ;
fileToCheckFor = " l i b n c u r s e s w . s o . 6 " ;
}
] ;
} ;
} ;
} ;
distSetName = if stdenv . hostPlatform . isMusl then " m u s l " else " d e f a u l t L i b c " ;
binDistUsed =
ghcBinDists . ${ distSetName } . ${ stdenv . hostPlatform . system }
or ( throw " c a n n o t b o o t s t r a p G H C o n t h i s p l a t f o r m ( ' ${ stdenv . hostPlatform . system } ' w i t h l i b c ' ${ distSetName } ' ) " ) ;
gmpUsed =
( builtins . head (
builtins . filter (
drv : lib . hasPrefix " g m p " ( drv . nixPackage . name or " " )
) binDistUsed . archSpecificLibraries
) ) . nixPackage ;
# GHC has other native backends (like PowerPC), but here only the ones
# we ship bindists for matter.
useLLVM =
! (
stdenv . targetPlatform . isx86 || ( stdenv . targetPlatform . isAarch64 && stdenv . targetPlatform . isDarwin )
) ;
libPath = lib . makeLibraryPath (
# Add arch-specific libraries.
map ( { nixPackage , . . . }: nixPackage ) binDistUsed . archSpecificLibraries
2024-12-10 19:26:33 +00:00
) ;
2023-12-14 15:07:34 +00:00
libEnvVar = lib . optionalString stdenv . hostPlatform . isDarwin " D Y " + " L D _ L I B R A R Y _ P A T H " ;
runtimeDeps =
[
targetPackages . stdenv . cc
targetPackages . stdenv . cc . bintools
coreutils # for cat
]
++ lib . optionals useLLVM [
( lib . getBin llvmPackages . llvm )
]
# On darwin, we need unwrapped bintools as well (for otool)
++ lib . optionals ( stdenv . targetPlatform . linker == " c c t o o l s " ) [
targetPackages . stdenv . cc . bintools . bintools
] ;
in
stdenv . mkDerivation rec {
inherit version ;
pname = " g h c - b i n a r y ${ 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 = '' g h c - ${ version } * / " ${ binDistUsed . exePathForLibraryCheck } " '' ;
in
lib . concatStringsSep " \n " [
( ''
shopt - u nullglob
echo " C h e c k i n g t h a t g h c b i n a r y e x i s t s i n b i n d i s t a t ${ buildExeGlob } "
if ! test - e $ { buildExeGlob } ; then
echo > & 2 " G H C b i n a r y ${ binDistUsed . exePathForLibraryCheck } c o u l d n o t b e f o u n d i n t h e b i n d i s t b u i l d d i r e c t o r y ( a t ${ buildExeGlob } ) f o r a r c h ${ stdenv . hostPlatform . system } , p l e a s e c h e c k t h a t g h c B i n D i s t s c o r r e c t l y r e f l e c t t h e b i n d i s t d e p e n d e n c i e s ! " ; exit 1 ;
fi
'' )
( lib . concatMapStringsSep " \n " (
{ fileToCheckFor , nixPackage }:
lib . optionalString ( fileToCheckFor != null ) ''
echo " C h e c k i n g b i n d i s t f o r ${ fileToCheckFor } t o e n s u r e t h a t i s s t i l l u s e d "
if ! readelf - d $ { buildExeGlob } | grep " ${ fileToCheckFor } " ; then
echo > & 2 " F i l e ${ fileToCheckFor } c o u l d n o t b e f o u n d i n ${ binDistUsed . exePathForLibraryCheck } f o r a r c h ${ stdenv . hostPlatform . system } , p l e a s e c h e c k t h a t g h c B i n D i s t s c o r r e c t l y r e f l e c t t h e b i n d i s t d e p e n d e n c i e s ! " ; exit 1 ;
fi
echo " C h e c k i n g t h a t t h e n i x p a c k a g e ${ nixPackage } c o n t a i n s ${ fileToCheckFor } "
if ! test - e " ${ lib . getLib nixPackage } / l i b / ${ fileToCheckFor } " ; then
echo > & 2 " N i x p a c k a g e ${ nixPackage } d i d n o t c o n t a i n ${ fileToCheckFor } f o r a r c h ${ stdenv . hostPlatform . system } , p l e a s e c h e c k t h a t g h c B i n D i s t s c o r r e c t l y r e f l e c t t h e b i n d i s t d e p e n d e n c i e s ! " ; exit 1 ;
fi
''
) binDistUsed . archSpecificLibraries )
2024-12-10 19:26:33 +00:00
]
2023-12-14 15:07:34 +00:00
)
# 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 + = " - n o _ d t r a c e _ d o f "
# not enough room in the object files for the full path to libiconv :(
for exe in $ ( find . - type f - executable ) ; do
2024-01-12 00:22:21 +00:00
isMachO $ exe || continue
2023-12-14 15:07:34 +00:00
ln - fs $ { libiconv } /lib/libiconv.dylib $ ( dirname $ exe ) /libiconv.dylib
install_name_tool - change /usr/lib/libiconv.2.dylib @ executable_path/libiconv.dylib - change /usr/local/lib/gcc/6/libgcc_s.1.dylib $ { gcc . cc . lib } /lib/libgcc_s.1.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 { } \ ;
''
+
# aarch64 does 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 ( stdenv . hostPlatform . isLinux && stdenv . hostPlatform . isAarch64 ) ''
find . - name package . conf . in \
- exec sed - i " s @ F F I _ L I B _ D I R @ F F I _ L I B _ D I R ${ numactl . out } / l i b @ 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 " L D = l d . g o l d " ;
# GHC has a patched config.sub and bindists' platforms should always work
dontUpdateAutotoolsGnuConfigScripts = true ;
configurePlatforms = [ ] ;
configureFlags =
lib . optional stdenv . hostPlatform . isDarwin " - - w i t h - g c c = ${ ./gcc-clang-wrapper.sh } "
# From: https://github.com/NixOS/nixpkgs/pull/43369/commits
++ lib . optional stdenv . hostPlatform . isMusl " - - d i s a b l e - l d - o v e r r i d e " ;
# 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 " $ o u t / b i n / " * ; do
test ! - h " $ i " || continue
isScript " $ i " || continue
sed - i - e ' 2 i export PATH = " ${ lib . makeBinPath runtimeDeps } : $ P A T H " ' " $ i "
done
2024-07-28 00:52:00 +00:00
''
+ lib . optionalString stdenv . targetPlatform . isDarwin ''
# Work around building with binary GHC on Darwin due to GHC’ s 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 / , ( " a r s u p p o r t s - L " , " Y E S " ) / , ( " a r s u p p o r t s - L " , " N O " ) / ' " $ o u t / l i b / g h c - ${ version } / l i b / s e t t i n g s "
2023-12-14 15:07:34 +00:00
'' ;
# 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 " $ o u t / l i b " - type f - name " * \. s o * " ) ; do
( cd $ out/lib ; ln - s $ p )
done
for p in $ ( find " $ o u t / l i b " - type f - executable ) ; do
if isELF " $ p " ; then
echo " P a t c h e l f i n g $ p "
patchelf - - set-rpath " \$ O R I G I N : \$ O R I G I N / . . / . . " $ p
fi
done
''
else
''
for p in $ ( find " $ o u t " - type f - executable ) ; do
if isELF " $ p " ; then
echo " P a t c h e l f i n g $ p "
patchelf - - set-rpath " ${ libPath } : $ ( p a t c h e l f - - p r i n t - r p a t h $ p ) " $ p
fi
2024-12-10 19:26:33 +00:00
done
''
)
2023-12-14 15:07:34 +00:00
+ lib . optionalString stdenv . hostPlatform . isDarwin ''
# not enough room in the object files for the full path to libiconv :(
for exe in $ ( find " $ o u t " - type f - executable ) ; do
2024-01-12 00:22:21 +00:00
isMachO $ exe || continue
2023-12-14 15:07:34 +00:00
ln - fs $ { libiconv } /lib/libiconv.dylib $ ( dirname $ exe ) /libiconv.dylib
install_name_tool - change /usr/lib/libiconv.2.dylib @ executable_path/libiconv.dylib - change /usr/local/lib/gcc/6/libgcc_s.1.dylib $ { gcc . cc . lib } /lib/libgcc_s.1.dylib $ exe
done
for file in $ ( find " $ o u t " - name setup-config ) ; do
substituteInPlace $ file - - replace /usr/bin/ranlib " $ ( t y p e - P r a n l i b ) "
done
''
# Recache package db which needs to happen for Hadrian bindists
# where we modify the package db before installing
+ ''
package_db = ( " $ o u t " /lib/ghc- * /lib/package.conf.d )
" $ o u t / b i n / g h c - p k g " - - package-db = " $ p a c k a g e _ d b " recache
'' ;
# In nixpkgs, musl based builds currently enable `pie` hardening by default
# (see `defaultHardeningFlags` in `make-derivation.nix`).
# But 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 = lib . optional stdenv . targetPlatform . isMusl " p i e " ;
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 \ $ ( [ | " y e s " | ] )
EOF
env - i $ out/bin/ghc - - make main . hs || exit 1
echo compilation ok
[ $ ( ./main ) == " y e s " ]
'' ;
passthru = {
targetPrefix = " " ;
enableShared = true ;
inherit llvmPackages ;
# Our Cabal compiler name
haskellCompilerName = " g h c - ${ 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 = rec {
homepage = " h t t p : / / h a s k e l l . o r g / g h c " ;
description = " G l a s g o w H a s k e l l C o m p i l e r " ;
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 ;
} ;
}