diff options
Diffstat (limited to 'pkgs/development/compilers/ghc/common-hadrian.nix')
-rw-r--r-- | pkgs/development/compilers/ghc/common-hadrian.nix | 220 |
1 files changed, 178 insertions, 42 deletions
diff --git a/pkgs/development/compilers/ghc/common-hadrian.nix b/pkgs/development/compilers/ghc/common-hadrian.nix index 31d1e52ea95b..7bb2135779a4 100644 --- a/pkgs/development/compilers/ghc/common-hadrian.nix +++ b/pkgs/development/compilers/ghc/common-hadrian.nix @@ -10,8 +10,10 @@ { lib , stdenv +, stdenvNoCC , pkgsBuildTarget , pkgsHostTarget +, buildPackages , targetPackages , fetchpatch @@ -58,15 +60,23 @@ , # If enabled, use -fPIC when compiling static libs. enableRelocatedStaticLibs ? stdenv.targetPlatform != stdenv.hostPlatform -, enableProfiledLibs ? true + # 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. + # FIXME(@sternenseemann): This actually doesn't influence what hadrian does, + # just what buildInputs etc. looks like. It would be best if we could actually + # tell it what to do like it was possible with make. enableTerminfo ? !(stdenv.targetPlatform.isWindows - || stdenv.targetPlatform.isGhcjs) + || stdenv.targetPlatform.isGhcjs + # terminfo can't be built for cross + || (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 || @@ -117,9 +127,8 @@ -- no way to set this via the command line finalStage :: Stage finalStage = ${ - # Always build the stage 2 compiler if possible. Note we can currently - # assume hostPlatform == buildPlatform. - # TODO(@sternenseemann): improve this condition when we can cross-compile GHC + # Always build the stage 2 compiler if possible. + # TODO(@sternensemann): unify condition with make-built GHCs if stdenv.hostPlatform.canExecute stdenv.targetPlatform then "Stage2" # native compiler or “native” cross e.g. pkgsStatic else "Stage1" # cross compiler @@ -160,10 +169,11 @@ patches = let - # Disable haddock generating pretty source listings to stay under 3GB on aarch64-linux enableHyperlinkedSource = - lib.versionAtLeast version "9.8" || - !(stdenv.hostPlatform.isAarch64 && stdenv.hostPlatform.isLinux); + # 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 [ # Fix docs build with Sphinx >= 7 https://gitlab.haskell.org/ghc/ghc/-/issues/24129 @@ -178,6 +188,15 @@ hash = "sha256-MpvTmFFsNiPDoOp9BhZyWeapeibQ77zgEV+xzZ1UAXs="; }) ] + ++ 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. @@ -189,18 +208,24 @@ then ./Cabal-at-least-3.6-paths-fix-cycle-aarch64-darwin.patch else ./Cabal-3.12-paths-fix-cycle-aarch64-darwin.patch) ] - # Prevents passing --hyperlinked-source to haddock. This is a custom - # workaround as we wait for this to be configurable via userSettings or - # similar. https://gitlab.haskell.org/ghc/ghc/-/issues/23625 + # 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) [ - # TODO(@sternenseemann): Doesn't apply for GHC >= 9.8 - ../../tools/haskell/hadrian/disable-hyperlinked-source.patch + (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) ] # Incorrect bounds on Cabal in hadrian # https://gitlab.haskell.org/ghc/ghc/-/issues/24100 ++ lib.optionals (lib.elem version [ "9.8.1" "9.8.2" ]) [ ../../tools/haskell/hadrian/hadrian-9.8.1-allow-Cabal-3.10.patch ]; + + stdenv = stdenvNoCC; } # GHC's build system hadrian built from the GHC-to-build's source tree @@ -222,15 +247,25 @@ , # 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 stdenv.buildPlatform == stdenv.hostPlatform; + let inherit (stdenv) buildPlatform hostPlatform targetPlatform; - inherit (bootPkgs) ghc; - # TODO(@Ericson2314) Make unconditional targetPrefix = lib.optionalString (targetPlatform != hostPlatform) @@ -265,7 +300,12 @@ let 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 @@ -276,12 +316,13 @@ let tools = { "cc" = cc; "c++" = cc; - as = cc.bintools.bintools; + as = cc.bintools; - ar = cc.bintools.bintools; - ranlib = cc.bintools.bintools; - nm = cc.bintools.bintools; - readelf = cc.bintools.bintools; + ar = cc.bintools; + ranlib = cc.bintools; + nm = cc.bintools; + readelf = cc.bintools; + objdump = cc.bintools; ld = cc.bintools; "ld.gold" = cc.bintools; @@ -300,6 +341,9 @@ let 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 "${tools}/bin/${tools.targetPrefix}${name}"; @@ -316,14 +360,34 @@ let (lib.optionalString enableNativeBignum "-native-bignum") ]; -in + # 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; + }; + + # Our Cabal compiler name + haskellCompilerName = "ghc-${version}"; -# C compiler, bintools and LLVM are used at build time, but will also leak into -# the resulting GHC's settings file and used at runtime. This means that we are -# currently only able to build GHC if hostPlatform == buildPlatform. -assert !targetPlatform.isGhcjs -> targetCC == pkgsHostTarget.targetPackages.stdenv.cc; -assert buildTargetLlvmPackages.llvm == llvmPackages.llvm; -assert stdenv.targetPlatform.isDarwin -> buildTargetLlvmPackages.clang == llvmPackages.clang; +in stdenv.mkDerivation ({ pname = "${targetPrefix}ghc${variantSuffix}"; @@ -346,6 +410,13 @@ stdenv.mkDerivation ({ 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}" @@ -358,6 +429,7 @@ stdenv.mkDerivation ({ 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}" @@ -366,7 +438,17 @@ stdenv.mkDerivation ({ export OPT="${lib.getBin buildTargetLlvmPackages.llvm}/bin/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 - export CLANG="${buildTargetLlvmPackages.clang}/bin/${buildTargetLlvmPackages.clang.targetPrefix}clang" + # 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 "${buildTargetLlvmPackages.clang}/bin/${buildTargetLlvmPackages.clang.targetPrefix}clang" + }" '' + lib.optionalString (stdenv.hostPlatform.isLinux && hostPlatform.libc == "glibc") '' export LOCALE_ARCHIVE="${glibcLocales}/lib/locale/locale-archive" @@ -433,14 +515,16 @@ stdenv.mkDerivation ({ # `--with` flags for libraries needed for RTS linker configureFlags = [ "--datadir=$doc/share/doc/ghc" - "--with-curses-includes=${ncurses.dev}/include" "--with-curses-libraries=${ncurses.out}/lib" + ] ++ 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=${targetPackages.libffi.dev}/include" - "--with-ffi-libraries=${targetPackages.libffi.out}/lib" + "--with-ffi-includes=${targetLibs.libffi.dev}/include" + "--with-ffi-libraries=${targetLibs.libffi.out}/lib" ] ++ lib.optionals (targetPlatform == hostPlatform && !enableNativeBignum) [ - "--with-gmp-includes=${targetPackages.gmp.dev}/include" - "--with-gmp-libraries=${targetPackages.gmp.out}/lib" + "--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" @@ -454,14 +538,16 @@ stdenv.mkDerivation ({ "--disable-large-address-space" ] ++ lib.optionals enableDwarf [ "--enable-dwarf-unwind" - "--with-libdw-includes=${lib.getDev targetPackages.elfutils}/include" - "--with-libdw-libraries=${lib.getLib targetPackages.elfutils}/lib" + "--with-libdw-includes=${lib.getDev targetLibs.elfutils}/include" + "--with-libdw-libraries=${lib.getLib targetLibs.elfutils}/lib" ] ++ lib.optionals targetPlatform.isDarwin [ # Darwin uses llvm-ar. GHC will try to use `-L` with `ar` when it is `llvm-ar` # but it doesn’t 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" ]; # Make sure we never relax`$PATH` and hooks support for compatibility. @@ -471,11 +557,13 @@ stdenv.mkDerivation ({ dontAddExtraLibs = true; nativeBuildInputs = [ - perl ghc hadrian bootPkgs.alex bootPkgs.happy bootPkgs.hscolour + perl hadrian bootPkgs.alex bootPkgs.happy bootPkgs.hscolour # autoconf and friends are necessary for hadrian to create the bindist autoconf automake m4 # 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 [ @@ -484,8 +572,25 @@ stdenv.mkDerivation ({ # 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 = [ perl bash ] ++ (libDeps hostPlatform); + buildInputs = [ bash ] ++ (libDeps hostPlatform); depsTargetTarget = map lib.getDev (libDeps targetPlatform); depsTargetTargetPropagated = map (lib.getOutput "out") (libDeps targetPlatform); @@ -553,19 +658,49 @@ stdenv.mkDerivation ({ # 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" "${lib.getBin llvmPackages.llvm}/bin/llc" \ + "LLVM opt command" "${lib.getBin llvmPackages.llvm}/bin/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 "${llvmPackages.clang}/bin/${llvmPackages.clang.targetPrefix}clang" + }" + '' + + '' + # Install the bash completion file. install -Dm 644 utils/completion/ghc.bash $out/share/bash-completion/completions/${targetPrefix}ghc ''; passthru = { - inherit bootPkgs targetPrefix; + inherit bootPkgs targetPrefix haskellCompilerName; inherit llvmPackages; inherit enableShared; - # Our Cabal compiler name - haskellCompilerName = "ghc-${version}"; - # Expose hadrian used for bootstrapping, for debugging purposes inherit hadrian; @@ -581,7 +716,8 @@ stdenv.mkDerivation ({ guibou ] ++ lib.teams.haskell.members; timeout = 24 * 3600; - inherit (ghc.meta) license platforms; + platforms = lib.platforms.all; + inherit (bootPkgs.ghc.meta) license; }; dontStrip = targetPlatform.useAndroidPrebuilt || targetPlatform.isWasm; |