about summary refs log tree commit diff
path: root/pkgs/development/compilers/ghc/common-make-native-bignum.nix
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/development/compilers/ghc/common-make-native-bignum.nix')
-rw-r--r--pkgs/development/compilers/ghc/common-make-native-bignum.nix187
1 files changed, 151 insertions, 36 deletions
diff --git a/pkgs/development/compilers/ghc/common-make-native-bignum.nix b/pkgs/development/compilers/ghc/common-make-native-bignum.nix
index 2b23cd75a0ed..7593a175c77c 100644
--- a/pkgs/development/compilers/ghc/common-make-native-bignum.nix
+++ b/pkgs/development/compilers/ghc/common-make-native-bignum.nix
@@ -3,7 +3,7 @@
 , url ? "https://downloads.haskell.org/ghc/${version}/ghc-${version}-src.tar.xz"
 }:
 
-{ lib, stdenv, pkgsBuildTarget, pkgsHostTarget, targetPackages
+{ lib, stdenv, pkgsBuildTarget, pkgsHostTarget, buildPackages, targetPackages
 
 # build-tools
 , bootPkgs
@@ -35,14 +35,19 @@
 , # 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
 
 , # Whether to build terminfo.
-  enableTerminfo ? !stdenv.targetPlatform.isWindows
+  enableTerminfo ? !(stdenv.targetPlatform.isWindows
+                     # terminfo can't be built for cross
+                     || (stdenv.buildPlatform != stdenv.hostPlatform)
+                     || (stdenv.hostPlatform != stdenv.targetPlatform))
 
 , # What flavour to build. An empty string indicates no
   # specific flavour and falls back to ghc default values.
@@ -58,24 +63,33 @@
 
 , enableHaddockProgram ?
     # Disabled for cross; see note [HADDOCK_DOCS].
-    (stdenv.targetPlatform == stdenv.hostPlatform)
+    (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.targetPlatform != stdenv.hostPlatform) -> !enableHaddockProgram;
+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;
 
-  inherit (bootPkgs) ghc;
-
   # TODO(@Ericson2314) Make unconditional
   targetPrefix = lib.optionalString
     (targetPlatform != hostPlatform)
@@ -88,6 +102,8 @@ let
     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`
@@ -134,7 +150,9 @@ let
     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
@@ -145,12 +163,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;
@@ -169,6 +188,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}";
@@ -185,14 +207,30 @@ 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)
+      gmp
+      libffi
+      ncurses;
+  };
 
-# 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 targetCC == pkgsHostTarget.targetPackages.stdenv.cc;
-assert buildTargetLlvmPackages.llvm == llvmPackages.llvm;
-assert stdenv.targetPlatform.isDarwin -> buildTargetLlvmPackages.clang == llvmPackages.clang;
+in
 
 stdenv.mkDerivation (rec {
   pname = "${targetPrefix}ghc${variantSuffix}";
@@ -282,6 +320,8 @@ stdenv.mkDerivation (rec {
     for env in $(env | grep '^TARGET_' | sed -E 's|\+?=.*||'); do
       export "''${env#TARGET_}=''${!env}"
     done
+    # Stage0 (build->build) which builds stage 1
+    export GHC="${bootPkgs.ghc}/bin/ghc"
     # GHC is a bit confused on its cross terminology, as these would normally be
     # the *host* tools.
     export CC="${toolPath "cc" targetCC}"
@@ -294,6 +334,7 @@ stdenv.mkDerivation (rec {
     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}"
@@ -302,20 +343,35 @@ stdenv.mkDerivation (rec {
     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"
+    }"
   ''
   + ''
+    # 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
   ''
   + lib.optionalString (lib.versionOlder version "9.2" || lib.versionAtLeast version "9.4") ''
     sed -i -e 's|-isysroot /Developer/SDKs/MacOSX10.5.sdk||' configure
-  '' + lib.optionalString (stdenv.isLinux && hostPlatform.libc == "glibc") ''
+  '' + lib.optionalString (stdenv.hostPlatform.isLinux && hostPlatform.libc == "glibc") ''
     export LOCALE_ARCHIVE="${glibcLocales}/lib/locale/locale-archive"
-  '' + lib.optionalString (!stdenv.isDarwin) ''
+  '' + lib.optionalString (!stdenv.hostPlatform.isDarwin) ''
     export NIX_LDFLAGS+=" -rpath $out/lib/ghc-${version}"
-  '' + lib.optionalString stdenv.isDarwin ''
+  '' + lib.optionalString stdenv.hostPlatform.isDarwin ''
     export NIX_LDFLAGS+=" -no_dtrace_dof"
-  '' + lib.optionalString (stdenv.isDarwin && lib.versionAtLeast version "9.2") ''
+  '' + lib.optionalString (stdenv.hostPlatform.isDarwin && lib.versionAtLeast version "9.2") ''
 
     # 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
@@ -346,21 +402,26 @@ stdenv.mkDerivation (rec {
       'MinBootGhcVersion="8.10"'
   '';
 
+  # 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" "host" ]
-    ++ lib.optional (targetPlatform != hostPlatform) "target";
+  configurePlatforms = [ "build" ]
+    ++ lib.optional (buildPlatform != hostPlatform || targetPlatform != hostPlatform) "target";
 
   # `--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) [
     "--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"
@@ -372,6 +433,8 @@ stdenv.mkDerivation (rec {
     "CONF_GCC_LINKER_OPTS_STAGE2=-fuse-ld=gold"
   ] ++ lib.optionals (disableLargeAddressSpace) [
     "--disable-large-address-space"
+  ] ++ lib.optionals enableUnregisterised [
+    "--enable-unregisterised"
   ];
 
   # Make sure we never relax`$PATH` and hooks support for compatibility.
@@ -382,21 +445,39 @@ stdenv.mkDerivation (rec {
 
   nativeBuildInputs = [
     perl autoconf automake m4 python3
-    ghc bootPkgs.alex bootPkgs.happy bootPkgs.hscolour
-  ] ++ lib.optionals (stdenv.isDarwin && stdenv.isAarch64) [
+    bootPkgs.alex bootPkgs.happy bootPkgs.hscolour
+    bootPkgs.ghc-settings-edit
+  ] ++ lib.optionals (stdenv.hostPlatform.isDarwin && stdenv.hostPlatform.isAarch64) [
     autoSignDarwinBinariesHook
   ] ++ lib.optionals enableDocs [
     sphinx
-  ] ++ lib.optionals (stdenv.isDarwin && lib.versions.majorMinor version == "9.0") [
+  ] ++ lib.optionals (stdenv.hostPlatform.isDarwin && lib.versions.majorMinor version == "9.0") [
     # TODO(@sternenseemann): backport addition of XATTR env var like
     # https://gitlab.haskell.org/ghc/ghc/-/merge_requests/6447
     xattr
   ];
 
+  # 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;
 
-  buildInputs = [ perl bash ] ++ (libDeps hostPlatform);
+  # Prevent stage0 ghc from leaking into the final result. This was an issue
+  # with GHC 9.6.
+  disallowedReferences = [
+    bootPkgs.ghc
+  ];
+
+  buildInputs = [ bash ] ++ (libDeps hostPlatform);
 
   depsTargetTarget = map lib.getDev (libDeps targetPlatform);
   depsTargetTargetPropagated = map (lib.getOutput "out") (libDeps targetPlatform);
@@ -423,6 +504,39 @@ stdenv.mkDerivation (rec {
   requiredSystemFeatures = [ "big-parallel" ];
 
   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" "${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 -D -m 444 utils/completion/ghc.bash $out/share/bash-completion/completions/${targetPrefix}ghc
   '';
@@ -448,7 +562,8 @@ stdenv.mkDerivation (rec {
       guibou
     ] ++ lib.teams.haskell.members;
     timeout = 24 * 3600;
-    inherit (ghc.meta) license platforms;
+    platforms = lib.platforms.all;
+    inherit (bootPkgs.ghc.meta) license;
   };
 
 } // lib.optionalAttrs targetPlatform.useAndroidPrebuilt {