1- { lib , stdenv , pkgsBuildTarget , pkgsHostTarget , targetPackages
1+ { lib , stdenv , pkgsBuildTarget , pkgsHostTarget , buildPackages , targetPackages
22
33# build-tools
44, bootPkgs
3232, # If enabled, use -fPIC when compiling static libs.
3333 enableRelocatedStaticLibs ? stdenv . targetPlatform != stdenv . hostPlatform
3434
35- , enableProfiledLibs ? true
35+ # Exceeds Hydra output limit (at the time of writing ~3GB) when cross compiled to riscv64.
36+ # A riscv64 cross-compiler fits into the limit comfortably.
37+ , enableProfiledLibs ? ! stdenv . hostPlatform . isRiscV64
3638
3739, # Whether to build dynamic libs for the standard library (on the target
3840 # platform). Static libs are always built.
3941 enableShared ? ! stdenv . targetPlatform . isWindows && ! stdenv . targetPlatform . useiOSPrebuilt
4042
4143, # Whether to build terminfo.
42- enableTerminfo ? ! stdenv . targetPlatform . isWindows
44+ enableTerminfo ? ! ( stdenv . targetPlatform . isWindows
45+ # terminfo can't be built for cross
46+ || ( stdenv . buildPlatform != stdenv . hostPlatform )
47+ || ( stdenv . hostPlatform != stdenv . targetPlatform ) )
4348
4449, # What flavour to build. An empty string indicates no
4550 # specific flavour and falls back to ghc default values.
5560
5661, enableHaddockProgram ?
5762 # Disabled for cross; see note [HADDOCK_DOCS].
58- ( stdenv . targetPlatform == stdenv . hostPlatform )
63+ ( stdenv . buildPlatform == stdenv . hostPlatform && stdenv . targetPlatform == stdenv . hostPlatform )
5964
6065, # Whether to disable the large address space allocator
6166 # necessary fix for iOS: https://www.reddit.com/r/haskell/comments/4ttdz1/building_an_osxi386_to_iosarm64_cross_compiler/d5qvd67/
6267 disableLargeAddressSpace ? stdenv . targetPlatform . isiOS
68+
69+ , # Whether to build an unregisterised version of GHC.
70+ # GHC will normally auto-detect whether it can do a registered build, but this
71+ # option will force it to do an unregistered build when set to true.
72+ # See https://gitlab.haskell.org/ghc/ghc/-/wikis/building/unregisterised
73+ enableUnregisterised ? false
6374} @args :
6475
6576assert ! enableIntegerSimple -> gmp != null ;
6677
6778# Cross cannot currently build the `haddock` program for silly reasons,
6879# see note [HADDOCK_DOCS].
69- assert ( stdenv . targetPlatform != stdenv . hostPlatform ) -> ! enableHaddockProgram ;
80+ assert ( stdenv . buildPlatform != stdenv . hostPlatform || stdenv . targetPlatform != stdenv . hostPlatform ) -> ! enableHaddockProgram ;
81+
82+ # GHC does not support building when all 3 platforms are different.
83+ assert stdenv . buildPlatform == stdenv . hostPlatform || stdenv . hostPlatform == stdenv . targetPlatform ;
7084
7185let
72- libffi_name = if stdenv . hostPlatform . isDarwin && stdenv . hostPlatform . isAarch64 then "libffi" else "libffi_3_3" ;
7386 inherit ( stdenv ) buildPlatform hostPlatform targetPlatform ;
7487
75- inherit ( bootPkgs ) ghc ;
76-
7788 # TODO(@Ericson2314) Make unconditional
7889 targetPrefix = lib . optionalString
7990 ( targetPlatform != hostPlatform )
8697 endif
8798 BUILD_SPHINX_HTML = ${ if enableDocs then "YES" else "NO" }
8899 BUILD_SPHINX_PDF = NO
100+
101+ WITH_TERMINFO = ${ if enableTerminfo then "YES" else "NO" }
89102 '' +
90103 # Note [HADDOCK_DOCS]:
91104 # Unfortunately currently `HADDOCK_DOCS` controls both whether the `haddock`
134147 pkgsBuildTarget . targetPackages . stdenv . cc
135148 ] ++ lib . optional useLLVM buildTargetLlvmPackages . llvm ;
136149
150+ buildCC = buildPackages . stdenv . cc ;
137151 targetCC = builtins . head toolsForTarget ;
152+ installCC = pkgsHostTarget . targetPackages . stdenv . cc ;
138153
139154 # toolPath calculates the absolute path to the name tool associated with a
140155 # given `stdenv.cc` derivation, i.e. it picks the correct derivation to take
@@ -145,12 +160,13 @@ let
145160 tools = {
146161 "cc" = cc ;
147162 "c++" = cc ;
148- as = cc . bintools . bintools ;
163+ as = cc . bintools ;
149164
150- ar = cc . bintools . bintools ;
151- ranlib = cc . bintools . bintools ;
152- nm = cc . bintools . bintools ;
153- readelf = cc . bintools . bintools ;
165+ ar = cc . bintools ;
166+ ranlib = cc . bintools ;
167+ nm = cc . bintools ;
168+ readelf = cc . bintools ;
169+ objdump = cc . bintools ;
154170
155171 ld = cc . bintools ;
156172 "ld.gold" = cc . bintools ;
169185 if stdenv . targetPlatform . isDarwin
170186 then cc . bintools
171187 else cc . bintools . bintools ;
188+
189+ # clang is used as an assembler on darwin with the LLVM backend
190+ clang = cc ;
172191 } . ${ name } ;
173192 in
174193 "${ tools } /bin/${ tools . targetPrefix } ${ name } " ;
@@ -185,14 +204,40 @@ let
185204 ( lib . optionalString enableIntegerSimple "-integer-simple" )
186205 ] ;
187206
188- in
207+ libffi_name =
208+ if stdenv . hostPlatform . isDarwin && stdenv . hostPlatform . isAarch64
209+ then "libffi"
210+ else "libffi_3_3" ;
211+
212+ # These libraries are library dependencies of the standard libraries bundled
213+ # by GHC (core libs) users will link their compiled artifacts again. Thus,
214+ # they should be taken from targetPackages.
215+ #
216+ # We need to use pkgsHostTarget if we are cross compiling a native GHC compiler,
217+ # though (when native compiling GHC, pkgsHostTarget == targetPackages):
218+ #
219+ # 1. targetPackages would be empty(-ish) in this situation since we can't
220+ # execute cross compiled compilers in order to obtain the libraries
221+ # that would be in targetPackages.
222+ # 2. pkgsHostTarget is fine to use since hostPlatform == targetPlatform in this
223+ # situation.
224+ # 3. The core libs used by the final GHC (stage 2) for user artifacts are also
225+ # used to build stage 2 GHC itself, i.e. the core libs are both host and
226+ # target.
227+ targetLibs =
228+ let
229+ basePackageSet =
230+ if hostPlatform != targetPlatform
231+ then targetPackages
232+ else pkgsHostTarget ;
233+ in
234+ {
235+ inherit ( basePackageSet ) gmp ncurses ;
236+ # dynamic inherits are not possible in Nix
237+ libffi = basePackageSet . ${ libffi_name } ;
238+ } ;
189239
190- # C compiler, bintools and LLVM are used at build time, but will also leak into
191- # the resulting GHC's settings file and used at runtime. This means that we are
192- # currently only able to build GHC if hostPlatform == buildPlatform.
193- assert targetCC == pkgsHostTarget . targetPackages . stdenv . cc ;
194- assert buildTargetLlvmPackages . llvm == llvmPackages . llvm ;
195- assert stdenv . targetPlatform . isDarwin -> buildTargetLlvmPackages . clang == llvmPackages . clang ;
240+ in
196241
197242stdenv . mkDerivation ( rec {
198243 version = "8.10.7" ;
@@ -277,6 +322,8 @@ stdenv.mkDerivation (rec {
277322 for env in $(env | grep '^TARGET_' | sed -E 's|\+?=.*||'); do
278323 export "'' ${env#TARGET_}='' ${!env}"
279324 done
325+ # Stage0 (build->build) which builds stage 1
326+ export GHC="${ bootPkgs . ghc } /bin/ghc"
280327 # GHC is a bit confused on its cross terminology, as these would normally be
281328 # the *host* tools.
282329 export CC="${ toolPath "cc" targetCC } "
@@ -289,6 +336,7 @@ stdenv.mkDerivation (rec {
289336 export RANLIB="${ toolPath "ranlib" targetCC } "
290337 export READELF="${ toolPath "readelf" targetCC } "
291338 export STRIP="${ toolPath "strip" targetCC } "
339+ export OBJDUMP="${ toolPath "objdump" targetCC } "
292340 '' + lib . optionalString ( stdenv . targetPlatform . linker == "cctools" ) ''
293341 export OTOOL="${ toolPath "otool" targetCC } "
294342 export INSTALL_NAME_TOOL="${ toolPath "install_name_tool" targetCC } "
@@ -297,8 +345,22 @@ stdenv.mkDerivation (rec {
297345 export OPT="${ lib . getBin buildTargetLlvmPackages . llvm } /bin/opt"
298346 '' + lib . optionalString ( useLLVM && stdenv . targetPlatform . isDarwin ) ''
299347 # LLVM backend on Darwin needs clang: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/codegens.html#llvm-code-generator-fllvm
300- export CLANG="${ buildTargetLlvmPackages . clang } /bin/${ buildTargetLlvmPackages . clang . targetPrefix } clang"
348+ # The executable we specify via $CLANG is used as an assembler (exclusively, it seems, but this isn't
349+ # clarified in any user facing documentation). As such, it'll be called on assembly produced by $CC
350+ # which usually comes from the darwin stdenv. To prevent a situation where $CLANG doesn't understand
351+ # the assembly it is given, we need to make sure that it matches the LLVM version of $CC if possible.
352+ # It is unclear (at the time of writing 2024-09-01) whether $CC should match the LLVM version we use
353+ # for llc and opt which would require using a custom darwin stdenv for targetCC.
354+ export CLANG="${
355+ if targetCC . isClang
356+ then toolPath "clang" targetCC
357+ else "${ buildTargetLlvmPackages . clang } /bin/${ buildTargetLlvmPackages . clang . targetPrefix } clang"
358+ } "
301359 '' + ''
360+ # No need for absolute paths since these tools only need to work during the build
361+ export CC_STAGE0="$CC_FOR_BUILD"
362+ export LD_STAGE0="$LD_FOR_BUILD"
363+ export AR_STAGE0="$AR_FOR_BUILD"
302364
303365 echo -n "${ buildMK } " > mk/build.mk
304366 sed -i -e 's|-isysroot /Developer/SDKs/MacOSX10.5.sdk||' configure
@@ -329,21 +391,26 @@ stdenv.mkDerivation (rec {
329391 done
330392 '' ;
331393
394+ # Although it is usually correct to pass --host, we don't do that here because
395+ # GHC's usage of build, host, and target is non-standard.
396+ # See https://gitlab.haskell.org/ghc/ghc/-/wikis/building/cross-compiling
332397 # TODO(@Ericson2314): Always pass "--target" and always prefix.
333- configurePlatforms = [ "build" "host" ]
334- ++ lib . optional ( targetPlatform != hostPlatform ) "target" ;
398+ configurePlatforms = [ "build" ]
399+ ++ lib . optional ( buildPlatform != hostPlatform || targetPlatform != hostPlatform ) "target" ;
335400
336401 # `--with` flags for libraries needed for RTS linker
337402 configureFlags = [
338403 "--datadir=$doc/share/doc/ghc"
339- "--with-curses-includes=${ ncurses . dev } /include" "--with-curses-libraries=${ ncurses . out } /lib"
404+ ] ++ lib . optionals enableTerminfo [
405+ "--with-curses-includes=${ lib . getDev targetLibs . ncurses } /include"
406+ "--with-curses-libraries=${ lib . getLib targetLibs . ncurses } /lib"
340407 ] ++ lib . optionals ( args . ${ libffi_name } != null ) [
341408 "--with-system-libffi"
342- "--with-ffi-includes=${ targetPackages . ${ libffi_name } . dev } /include"
343- "--with-ffi-libraries=${ targetPackages . ${ libffi_name } . out } /lib"
409+ "--with-ffi-includes=${ targetLibs . libffi . dev } /include"
410+ "--with-ffi-libraries=${ targetLibs . libffi . out } /lib"
344411 ] ++ lib . optionals ( targetPlatform == hostPlatform && ! enableIntegerSimple ) [
345- "--with-gmp-includes=${ targetPackages . gmp . dev } /include"
346- "--with-gmp-libraries=${ targetPackages . gmp . out } /lib"
412+ "--with-gmp-includes=${ targetLibs . gmp . dev } /include"
413+ "--with-gmp-libraries=${ targetLibs . gmp . out } /lib"
347414 ] ++ lib . optionals ( targetPlatform == hostPlatform && hostPlatform . libc != "glibc" && ! targetPlatform . isWindows ) [
348415 "--with-iconv-includes=${ libiconv } /include"
349416 "--with-iconv-libraries=${ libiconv } /lib"
@@ -355,6 +422,8 @@ stdenv.mkDerivation (rec {
355422 "CONF_GCC_LINKER_OPTS_STAGE2=-fuse-ld=gold"
356423 ] ++ lib . optionals ( disableLargeAddressSpace ) [
357424 "--disable-large-address-space"
425+ ] ++ lib . optionals enableUnregisterised [
426+ "--enable-unregisterised"
358427 ] ;
359428
360429 # Make sure we never relax`$PATH` and hooks support for compatibility.
@@ -365,17 +434,35 @@ stdenv.mkDerivation (rec {
365434
366435 nativeBuildInputs = [
367436 perl autoreconfHook autoconf automake m4 python3
368- ghc bootPkgs . alex bootPkgs . happy bootPkgs . hscolour
437+ bootPkgs . alex bootPkgs . happy bootPkgs . hscolour
438+ bootPkgs . ghc-settings-edit
369439 ] ++ lib . optionals ( stdenv . hostPlatform . isDarwin && stdenv . hostPlatform . isAarch64 ) [
370440 autoSignDarwinBinariesHook
371441 ] ++ lib . optionals enableDocs [
372442 sphinx
373443 ] ;
374444
445+ # Everything the stage0 compiler needs to build stage1: CC, bintools, extra libs.
446+ # See also GHC, {CC,LD,AR}_STAGE0 in preConfigure.
447+ depsBuildBuild = [
448+ # N.B. We do not declare bootPkgs.ghc in any of the stdenv.mkDerivation
449+ # dependency lists to prevent the bintools setup hook from adding ghc's
450+ # lib directory to the linker flags. Instead we tell configure about it
451+ # via the GHC environment variable.
452+ buildCC
453+ # stage0 builds terminfo unconditionally, so we always need ncurses
454+ ncurses
455+ ] ;
375456 # For building runtime libs
376457 depsBuildTarget = toolsForTarget ;
377458
378- buildInputs = [ perl bash ] ++ ( libDeps hostPlatform ) ;
459+ # Prevent stage0 ghc from leaking into the final result. This was an issue
460+ # with GHC 9.6.
461+ disallowedReferences = [
462+ bootPkgs . ghc
463+ ] ;
464+
465+ buildInputs = [ bash ] ++ ( libDeps hostPlatform ) ;
379466
380467 depsTargetTarget = map lib . getDev ( libDeps targetPlatform ) ;
381468 depsTargetTargetPropagated = map ( lib . getOutput "out" ) ( libDeps targetPlatform ) ;
@@ -402,6 +489,39 @@ stdenv.mkDerivation (rec {
402489 requiredSystemFeatures = [ "big-parallel" ] ;
403490
404491 postInstall = ''
492+ settingsFile="$out/lib/${ targetPrefix } ${ passthru . haskellCompilerName } /settings"
493+
494+ # Make the installed GHC use the host->target tools.
495+ ghc-settings-edit "$settingsFile" \
496+ "C compiler command" "${ toolPath "cc" installCC } " \
497+ "Haskell CPP command" "${ toolPath "cc" installCC } " \
498+ "C++ compiler command" "${ toolPath "c++" installCC } " \
499+ "ld command" "${ toolPath "ld${ lib . optionalString useLdGold ".gold" } " installCC } " \
500+ "Merge objects command" "${ toolPath "ld${ lib . optionalString useLdGold ".gold" } " installCC } " \
501+ "ar command" "${ toolPath "ar" installCC } " \
502+ "ranlib command" "${ toolPath "ranlib" installCC } "
503+ ''
504+ + lib . optionalString ( stdenv . targetPlatform . linker == "cctools" ) ''
505+ ghc-settings-edit "$settingsFile" \
506+ "otool command" "${ toolPath "otool" installCC } " \
507+ "install_name_tool command" "${ toolPath "install_name_tool" installCC } "
508+ ''
509+ + lib . optionalString useLLVM ''
510+ ghc-settings-edit "$settingsFile" \
511+ "LLVM llc command" "${ lib . getBin llvmPackages . llvm } /bin/llc" \
512+ "LLVM opt command" "${ lib . getBin llvmPackages . llvm } /bin/opt"
513+ ''
514+ + lib . optionalString ( useLLVM && stdenv . targetPlatform . isDarwin ) ''
515+ ghc-settings-edit "$settingsFile" \
516+ "LLVM clang command" "${
517+ # See comment for CLANG in preConfigure
518+ if installCC . isClang
519+ then toolPath "clang" installCC
520+ else "${ llvmPackages . clang } /bin/${ llvmPackages . clang . targetPrefix } clang"
521+ } "
522+ ''
523+ + ''
524+
405525 # Install the bash completion file.
406526 install -D -m 444 utils/completion/ghc.bash $out/share/bash-completion/completions/${ targetPrefix } ghc
407527 '' ;
@@ -427,7 +547,8 @@ stdenv.mkDerivation (rec {
427547 guibou
428548 ] ++ lib . teams . haskell . members ;
429549 timeout = 24 * 3600 ;
430- inherit ( ghc . meta ) license platforms ;
550+ platforms = lib . platforms . all ;
551+ inherit ( bootPkgs . ghc . meta ) license ;
431552 } ;
432553
433554} // lib . optionalAttrs targetPlatform . useAndroidPrebuilt {
0 commit comments