lol
1{ version
2, rev ? null
3, sha256
4, url ?
5 if rev != null
6 then "https://gitlab.haskell.org/ghc/ghc.git"
7 else "https://downloads.haskell.org/ghc/${version}/ghc-${version}-src.tar.xz"
8
9}:
10
11{ lib
12, stdenv
13, pkgsBuildTarget
14, pkgsHostTarget
15, targetPackages
16
17# build-tools
18, bootPkgs
19, autoconf
20, automake
21, coreutils
22, fetchpatch
23, fetchurl
24, fetchgit
25, perl
26, python3
27, m4
28, sphinx
29, xattr
30, autoSignDarwinBinariesHook
31, bash
32
33, libiconv ? null, ncurses
34, glibcLocales ? null
35
36, # GHC can be built with system libffi or a bundled one.
37 libffi ? null
38
39, useLLVM ? !(stdenv.targetPlatform.isx86
40 || stdenv.targetPlatform.isPower
41 || stdenv.targetPlatform.isSparc
42 || stdenv.targetPlatform.isAarch64
43 || stdenv.targetPlatform.isGhcjs)
44, # LLVM is conceptually a run-time-only dependency, but for
45 # non-x86, we need LLVM to bootstrap later stages, so it becomes a
46 # build-time dependency too.
47 buildTargetLlvmPackages
48, llvmPackages
49
50, # If enabled, GHC will be built with the GPL-free but slightly slower native
51 # bignum backend instead of the faster but GPLed gmp backend.
52 enableNativeBignum ? !(lib.meta.availableOn stdenv.hostPlatform gmp
53 && lib.meta.availableOn stdenv.targetPlatform gmp)
54 || stdenv.targetPlatform.isGhcjs
55, gmp
56
57, # If enabled, use -fPIC when compiling static libs.
58 enableRelocatedStaticLibs ? stdenv.targetPlatform != stdenv.hostPlatform
59
60, enableProfiledLibs ? true
61
62, # Whether to build dynamic libs for the standard library (on the target
63 # platform). Static libs are always built.
64 enableShared ? with stdenv.targetPlatform; !isWindows && !useiOSPrebuilt && !isStatic && !isGhcjs
65
66, # Whether to build terminfo.
67 enableTerminfo ? !(stdenv.targetPlatform.isWindows
68 || stdenv.targetPlatform.isGhcjs)
69
70, # Libdw.c only supports x86_64, i686 and s390x as of 2022-08-04
71 enableDwarf ? (stdenv.targetPlatform.isx86 ||
72 (stdenv.targetPlatform.isS390 && stdenv.targetPlatform.is64bit)) &&
73 lib.meta.availableOn stdenv.hostPlatform elfutils &&
74 lib.meta.availableOn stdenv.targetPlatform elfutils &&
75 # HACK: elfutils is marked as broken on static platforms
76 # which availableOn can't tell.
77 !stdenv.targetPlatform.isStatic &&
78 !stdenv.hostPlatform.isStatic
79, elfutils
80
81, # What flavour to build. Flavour string may contain a flavour and flavour
82 # transformers as accepted by hadrian.
83 ghcFlavour ?
84 let
85 # TODO(@sternenseemann): does using the static flavour make sense?
86 baseFlavour = "release";
87 # Note: in case hadrian's flavour transformers cease being expressive
88 # enough for us, we'll need to resort to defining a "nixpkgs" flavour
89 # in hadrianUserSettings and using that instead.
90 transformers =
91 lib.optionals useLLVM [ "llvm" ]
92 ++ lib.optionals (!enableShared) [
93 "no_dynamic_libs"
94 "no_dynamic_ghc"
95 ]
96 ++ lib.optionals (!enableProfiledLibs) [ "no_profiled_libs" ]
97 # While split sections are now enabled by default in ghc 8.8 for windows,
98 # they seem to lead to `too many sections` errors when building base for
99 # profiling.
100 ++ lib.optionals (!stdenv.targetPlatform.isWindows) [ "split_sections" ]
101 ;
102 in
103 baseFlavour + lib.concatMapStrings (t: "+${t}") transformers
104
105, # Contents of the UserSettings.hs file to use when compiling hadrian.
106 hadrianUserSettings ? ''
107 module UserSettings (
108 userFlavours, userPackages, userDefaultFlavour,
109 verboseCommand, buildProgressColour, successColour, finalStage
110 ) where
111
112 import Flavour.Type
113 import Expression
114 import {-# SOURCE #-} Settings.Default
115
116 -- no way to set this via the command line
117 finalStage :: Stage
118 finalStage = ${
119 if stdenv.hostPlatform == stdenv.targetPlatform
120 then "Stage2" # native compiler
121 else "Stage1" # cross compiler
122 }
123
124 userDefaultFlavour :: String
125 userDefaultFlavour = "release"
126
127 userFlavours :: [Flavour]
128 userFlavours = []
129
130 -- Disable Colours
131 buildProgressColour :: BuildProgressColour
132 buildProgressColour = mkBuildProgressColour (Dull Reset)
133 successColour :: SuccessColour
134 successColour = mkSuccessColour (Dull Reset)
135
136 -- taken from src/UserSettings.hs unchanged, need to be there
137 userPackages :: [Package]
138 userPackages = []
139 verboseCommand :: Predicate
140 verboseCommand = do
141 verbosity <- expr getVerbosity
142 return $ verbosity >= Verbose
143 ''
144
145, ghcSrc ? (if rev != null then fetchgit else fetchurl) ({
146 inherit url sha256;
147 } // lib.optionalAttrs (rev != null) {
148 inherit rev;
149 })
150
151 # GHC's build system hadrian built from the GHC-to-build's source tree
152 # using our bootstrap GHC.
153, hadrian ? import ../../tools/haskell/hadrian/make-hadrian.nix { inherit bootPkgs lib; } {
154 ghcSrc = ghcSrc;
155 ghcVersion = version;
156 userSettings = hadrianUserSettings;
157 # Disable haddock generating pretty source listings to stay under 3GB on aarch64-linux
158 enableHyperlinkedSource =
159 # TODO(@sternenseemann): Disabling currently doesn't work with GHC >= 9.8
160 lib.versionAtLeast version "9.8" ||
161 !(stdenv.hostPlatform.isAarch64 && stdenv.hostPlatform.isLinux);
162 }
163
164, # Whether to build sphinx documentation.
165 enableDocs ? (
166 # Docs disabled for musl and cross because it's a large task to keep
167 # all `sphinx` dependencies building in those environments.
168 # `sphinx` pulls in among others:
169 # Ruby, Python, Perl, Rust, OpenGL, Xorg, gtk, LLVM.
170 (stdenv.targetPlatform == stdenv.hostPlatform)
171 && !stdenv.hostPlatform.isMusl
172 )
173
174, # Whether to disable the large address space allocator
175 # necessary fix for iOS: https://www.reddit.com/r/haskell/comments/4ttdz1/building_an_osxi386_to_iosarm64_cross_compiler/d5qvd67/
176 disableLargeAddressSpace ? stdenv.targetPlatform.isiOS
177}:
178
179assert !enableNativeBignum -> gmp != null;
180
181let
182 inherit (stdenv) buildPlatform hostPlatform targetPlatform;
183
184 inherit (bootPkgs) ghc;
185
186 # TODO(@Ericson2314) Make unconditional
187 targetPrefix = lib.optionalString
188 (targetPlatform != hostPlatform)
189 "${targetPlatform.config}-";
190
191 hadrianSettings =
192 # -fexternal-dynamic-refs apparently (because it's not clear from the
193 # documentation) makes the GHC RTS able to load static libraries, which may
194 # be needed for TemplateHaskell. This solution was described in
195 # https://www.tweag.io/blog/2020-09-30-bazel-static-haskell
196 lib.optionals enableRelocatedStaticLibs [
197 "*.*.ghc.*.opts += -fPIC -fexternal-dynamic-refs"
198 ]
199 ++ lib.optionals targetPlatform.useAndroidPrebuilt [
200 "*.*.ghc.c.opts += -optc-std=gnu99"
201 ];
202
203 # Splicer will pull out correct variations
204 libDeps = platform: lib.optional enableTerminfo ncurses
205 ++ lib.optionals (!targetPlatform.isGhcjs) [libffi]
206 # Bindist configure script fails w/o elfutils in linker search path
207 # https://gitlab.haskell.org/ghc/ghc/-/issues/22081
208 ++ lib.optional enableDwarf elfutils
209 ++ lib.optional (!enableNativeBignum) gmp
210 ++ lib.optional (platform.libc != "glibc" && !targetPlatform.isWindows && !targetPlatform.isGhcjs) libiconv;
211
212 # TODO(@sternenseemann): is buildTarget LLVM unnecessary?
213 # GHC doesn't seem to have {LLC,OPT}_HOST
214 toolsForTarget = [
215 (if targetPlatform.isGhcjs
216 then pkgsBuildTarget.emscripten
217 else pkgsBuildTarget.targetPackages.stdenv.cc)
218 ] ++ lib.optional useLLVM buildTargetLlvmPackages.llvm;
219
220 targetCC = builtins.head toolsForTarget;
221
222 # Sometimes we have to dispatch between the bintools wrapper and the unwrapped
223 # derivation for certain tools depending on the platform.
224 bintoolsFor = {
225 # GHC needs install_name_tool on all darwin platforms. On aarch64-darwin it is
226 # part of the bintools wrapper (due to codesigning requirements), but not on
227 # x86_64-darwin.
228 install_name_tool =
229 if stdenv.targetPlatform.isAarch64
230 then targetCC.bintools
231 else targetCC.bintools.bintools;
232 # Same goes for strip.
233 strip =
234 # TODO(@sternenseemann): also use wrapper if linker == "bfd" or "gold"
235 if stdenv.targetPlatform.isAarch64 && stdenv.targetPlatform.isDarwin
236 then targetCC.bintools
237 else targetCC.bintools.bintools;
238 };
239
240 # Use gold either following the default, or to avoid the BFD linker due to some bugs / perf issues.
241 # But we cannot avoid BFD when using musl libc due to https://sourceware.org/bugzilla/show_bug.cgi?id=23856
242 # see #84670 and #49071 for more background.
243 useLdGold = targetPlatform.linker == "gold" ||
244 (targetPlatform.linker == "bfd" && (targetCC.bintools.bintools.hasGold or false) && !targetPlatform.isMusl);
245
246 # Makes debugging easier to see which variant is at play in `nix-store -q --tree`.
247 variantSuffix = lib.concatStrings [
248 (lib.optionalString stdenv.hostPlatform.isMusl "-musl")
249 (lib.optionalString enableNativeBignum "-native-bignum")
250 ];
251
252in
253
254# C compiler, bintools and LLVM are used at build time, but will also leak into
255# the resulting GHC's settings file and used at runtime. This means that we are
256# currently only able to build GHC if hostPlatform == buildPlatform.
257assert !targetPlatform.isGhcjs -> targetCC == pkgsHostTarget.targetPackages.stdenv.cc;
258assert buildTargetLlvmPackages.llvm == llvmPackages.llvm;
259assert stdenv.targetPlatform.isDarwin -> buildTargetLlvmPackages.clang == llvmPackages.clang;
260
261stdenv.mkDerivation ({
262 pname = "${targetPrefix}ghc${variantSuffix}";
263 inherit version;
264
265 src = ghcSrc;
266
267 enableParallelBuilding = true;
268
269 patches = [
270 # Fix docs build with Sphinx >= 7 https://gitlab.haskell.org/ghc/ghc/-/issues/24129
271 (if lib.versionAtLeast version "9.8"
272 then ./docs-sphinx-7-ghc98.patch
273 else ./docs-sphinx-7.patch )
274 ];
275 postPatch = ''
276 patchShebangs --build .
277 '';
278
279 # GHC needs the locale configured during the Haddock phase.
280 LANG = "en_US.UTF-8";
281
282 # GHC is a bit confused on its cross terminology.
283 # TODO(@sternenseemann): investigate coreutils dependencies and pass absolute paths
284 preConfigure = ''
285 for env in $(env | grep '^TARGET_' | sed -E 's|\+?=.*||'); do
286 export "''${env#TARGET_}=''${!env}"
287 done
288 # GHC is a bit confused on its cross terminology, as these would normally be
289 # the *host* tools.
290 export CC="${targetCC}/bin/${targetCC.targetPrefix}cc"
291 export CXX="${targetCC}/bin/${targetCC.targetPrefix}c++"
292 # Use gold to work around https://sourceware.org/bugzilla/show_bug.cgi?id=16177
293 export LD="${targetCC.bintools}/bin/${targetCC.bintools.targetPrefix}ld${lib.optionalString useLdGold ".gold"}"
294 export AS="${targetCC.bintools.bintools}/bin/${targetCC.bintools.targetPrefix}as"
295 export AR="${targetCC.bintools.bintools}/bin/${targetCC.bintools.targetPrefix}ar"
296 export NM="${targetCC.bintools.bintools}/bin/${targetCC.bintools.targetPrefix}nm"
297 export RANLIB="${targetCC.bintools.bintools}/bin/${targetCC.bintools.targetPrefix}ranlib"
298 export READELF="${targetCC.bintools.bintools}/bin/${targetCC.bintools.targetPrefix}readelf"
299 export STRIP="${bintoolsFor.strip}/bin/${bintoolsFor.strip.targetPrefix}strip"
300 '' + lib.optionalString (stdenv.targetPlatform.linker == "cctools") ''
301 export OTOOL="${targetCC.bintools.bintools}/bin/${targetCC.bintools.targetPrefix}otool"
302 export INSTALL_NAME_TOOL="${bintoolsFor.install_name_tool}/bin/${bintoolsFor.install_name_tool.targetPrefix}install_name_tool"
303 '' + lib.optionalString useLLVM ''
304 export LLC="${lib.getBin buildTargetLlvmPackages.llvm}/bin/llc"
305 export OPT="${lib.getBin buildTargetLlvmPackages.llvm}/bin/opt"
306 '' + lib.optionalString (useLLVM && stdenv.targetPlatform.isDarwin) ''
307 # LLVM backend on Darwin needs clang: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/codegens.html#llvm-code-generator-fllvm
308 export CLANG="${buildTargetLlvmPackages.clang}/bin/${buildTargetLlvmPackages.clang.targetPrefix}clang"
309 '' +
310 lib.optionalString (stdenv.isLinux && hostPlatform.libc == "glibc") ''
311 export LOCALE_ARCHIVE="${glibcLocales}/lib/locale/locale-archive"
312 '' + lib.optionalString (!stdenv.isDarwin) ''
313 export NIX_LDFLAGS+=" -rpath $out/lib/ghc-${version}"
314 '' + lib.optionalString stdenv.isDarwin ''
315 export NIX_LDFLAGS+=" -no_dtrace_dof"
316
317 # GHC tries the host xattr /usr/bin/xattr by default which fails since it expects python to be 2.7
318 export XATTR=${lib.getBin xattr}/bin/xattr
319 ''
320 # If we are not using release tarballs, some files need to be generated using
321 # the boot script.
322 + lib.optionalString (rev != null) ''
323 echo ${version} > VERSION
324 echo ${rev} > GIT_COMMIT_ID
325 ./boot
326 ''
327 + lib.optionalString targetPlatform.useAndroidPrebuilt ''
328 sed -i -e '5i ,("armv7a-unknown-linux-androideabi", ("e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64", "cortex-a8", ""))' llvm-targets
329 '' + lib.optionalString targetPlatform.isMusl ''
330 echo "patching llvm-targets for musl targets..."
331 echo "Cloning these existing '*-linux-gnu*' targets:"
332 grep linux-gnu llvm-targets | sed 's/^/ /'
333 echo "(go go gadget sed)"
334 sed -i 's,\(^.*linux-\)gnu\(.*\)$,\0\n\1musl\2,' llvm-targets
335 echo "llvm-targets now contains these '*-linux-musl*' targets:"
336 grep linux-musl llvm-targets | sed 's/^/ /'
337
338 echo "And now patching to preserve '-musleabi' as done with '-gnueabi'"
339 # (aclocal.m4 is actual source, but patch configure as well since we don't re-gen)
340 for x in configure aclocal.m4; do
341 substituteInPlace $x \
342 --replace '*-android*|*-gnueabi*)' \
343 '*-android*|*-gnueabi*|*-musleabi*)'
344 done
345 ''
346 # Need to make writable EM_CACHE for emscripten
347 # https://gitlab.haskell.org/ghc/ghc/-/wikis/javascript-backend#configure-fails-with-sub-word-sized-atomic-operations-not-available
348 + lib.optionalString targetPlatform.isGhcjs ''
349 export EM_CACHE="$(mktemp -d emcache.XXXXXXXXXX)"
350 cp -Lr ${targetCC /* == emscripten */}/share/emscripten/cache/* "$EM_CACHE/"
351 chmod u+rwX -R "$EM_CACHE"
352 ''
353 # Create bash array hadrianFlagsArray for use in buildPhase. Do it in
354 # preConfigure, so overrideAttrs can be used to modify it effectively.
355 # hadrianSettings are passed via the command line so they are more visible
356 # in the build log.
357 + ''
358 hadrianFlagsArray=(
359 "-j$NIX_BUILD_CORES"
360 ${lib.escapeShellArgs hadrianSettings}
361 )
362 '';
363
364 ${if targetPlatform.isGhcjs then "configureScript" else null} = "emconfigure ./configure";
365 # GHC currently ships an edited config.sub so ghcjs is accepted which we can not rollback
366 ${if targetPlatform.isGhcjs then "dontUpdateAutotoolsGnuConfigScripts" else null} = true;
367
368 # TODO(@Ericson2314): Always pass "--target" and always prefix.
369 configurePlatforms = [ "build" "host" ]
370 ++ lib.optional (targetPlatform != hostPlatform) "target";
371
372 # `--with` flags for libraries needed for RTS linker
373 configureFlags = [
374 "--datadir=$doc/share/doc/ghc"
375 "--with-curses-includes=${ncurses.dev}/include" "--with-curses-libraries=${ncurses.out}/lib"
376 ] ++ lib.optionals (libffi != null && !targetPlatform.isGhcjs) [
377 "--with-system-libffi"
378 "--with-ffi-includes=${targetPackages.libffi.dev}/include"
379 "--with-ffi-libraries=${targetPackages.libffi.out}/lib"
380 ] ++ lib.optionals (targetPlatform == hostPlatform && !enableNativeBignum) [
381 "--with-gmp-includes=${targetPackages.gmp.dev}/include"
382 "--with-gmp-libraries=${targetPackages.gmp.out}/lib"
383 ] ++ lib.optionals (targetPlatform == hostPlatform && hostPlatform.libc != "glibc" && !targetPlatform.isWindows) [
384 "--with-iconv-includes=${libiconv}/include"
385 "--with-iconv-libraries=${libiconv}/lib"
386 ] ++ lib.optionals (targetPlatform != hostPlatform) [
387 "--enable-bootstrap-with-devel-snapshot"
388 ] ++ lib.optionals useLdGold [
389 "CFLAGS=-fuse-ld=gold"
390 "CONF_GCC_LINKER_OPTS_STAGE1=-fuse-ld=gold"
391 "CONF_GCC_LINKER_OPTS_STAGE2=-fuse-ld=gold"
392 ] ++ lib.optionals (disableLargeAddressSpace) [
393 "--disable-large-address-space"
394 ] ++ lib.optionals enableDwarf [
395 "--enable-dwarf-unwind"
396 "--with-libdw-includes=${lib.getDev elfutils}/include"
397 "--with-libdw-libraries=${lib.getLib elfutils}/lib"
398 ] ++ lib.optionals targetPlatform.isDarwin [
399 # Darwin uses llvm-ar. GHC will try to use `-L` with `ar` when it is `llvm-ar`
400 # but it doesn’t currently work because Cabal never uses `-L` on Darwin. See:
401 # https://gitlab.haskell.org/ghc/ghc/-/issues/23188
402 # https://github.com/haskell/cabal/issues/8882
403 "fp_cv_prog_ar_supports_dash_l=no"
404 ];
405
406 # Make sure we never relax`$PATH` and hooks support for compatibility.
407 strictDeps = true;
408
409 # Don’t add -liconv to LDFLAGS automatically so that GHC will add it itself.
410 dontAddExtraLibs = true;
411
412 nativeBuildInputs = [
413 perl ghc hadrian bootPkgs.alex bootPkgs.happy bootPkgs.hscolour
414 # autoconf and friends are necessary for hadrian to create the bindist
415 autoconf automake m4
416 # Python is used in a few scripts invoked by hadrian to generate e.g. rts headers.
417 python3
418 ] ++ lib.optionals (stdenv.isDarwin && stdenv.isAarch64) [
419 autoSignDarwinBinariesHook
420 ] ++ lib.optionals enableDocs [
421 sphinx
422 ];
423
424 # For building runtime libs
425 depsBuildTarget = toolsForTarget;
426
427 buildInputs = [ perl bash ] ++ (libDeps hostPlatform);
428
429 depsTargetTarget = map lib.getDev (libDeps targetPlatform);
430 depsTargetTargetPropagated = map (lib.getOutput "out") (libDeps targetPlatform);
431
432 hadrianFlags = [
433 "--flavour=${ghcFlavour}"
434 "--bignum=${if enableNativeBignum then "native" else "gmp"}"
435 "--docs=${if enableDocs then "no-sphinx-pdfs" else "no-sphinx"}"
436 ];
437
438 buildPhase = ''
439 runHook preBuild
440
441 # hadrianFlagsArray is created in preConfigure
442 echo "hadrianFlags: $hadrianFlags ''${hadrianFlagsArray[@]}"
443
444 # We need to go via the bindist for installing
445 hadrian $hadrianFlags "''${hadrianFlagsArray[@]}" binary-dist-dir
446
447 runHook postBuild
448 '';
449
450 # required, because otherwise all symbols from HSffi.o are stripped, and
451 # that in turn causes GHCi to abort
452 stripDebugFlags = [ "-S" ] ++ lib.optional (!targetPlatform.isDarwin) "--keep-file-symbols";
453
454 checkTarget = "test";
455
456 hardeningDisable =
457 [ "format" ]
458 # In nixpkgs, musl based builds currently enable `pie` hardening by default
459 # (see `defaultHardeningFlags` in `make-derivation.nix`).
460 # But GHC cannot currently produce outputs that are ready for `-pie` linking.
461 # Thus, disable `pie` hardening, otherwise `recompile with -fPIE` errors appear.
462 # See:
463 # * https://github.com/NixOS/nixpkgs/issues/129247
464 # * https://gitlab.haskell.org/ghc/ghc/-/issues/19580
465 ++ lib.optional stdenv.targetPlatform.isMusl "pie";
466
467 # big-parallel allows us to build with more than 2 cores on
468 # Hydra which already warrants a significant speedup
469 requiredSystemFeatures = [ "big-parallel" ];
470
471 outputs = [ "out" "doc" ];
472
473 # We need to configure the bindist *again* before installing
474 # https://gitlab.haskell.org/ghc/ghc/-/issues/22058
475 # TODO(@sternenseemann): it would be nice if the bindist could be an intermediate
476 # derivation, but since it is > 2GB even on x86_64-linux, not a good idea?
477 preInstall = ''
478 pushd _build/bindist/*
479
480 $configureScript $configureFlags "''${configureFlagsArray[@]}"
481 '';
482
483 postInstall = ''
484 # leave bindist directory
485 popd
486
487 # Install the bash completion file.
488 install -Dm 644 utils/completion/ghc.bash $out/share/bash-completion/completions/${targetPrefix}ghc
489 '';
490
491 passthru = {
492 inherit bootPkgs targetPrefix;
493
494 inherit llvmPackages;
495 inherit enableShared;
496
497 # Our Cabal compiler name
498 haskellCompilerName = "ghc-${version}";
499
500 # Expose hadrian used for bootstrapping, for debugging purposes
501 inherit hadrian;
502 };
503
504 meta = {
505 homepage = "http://haskell.org/ghc";
506 description = "The Glasgow Haskell Compiler";
507 maintainers = with lib.maintainers; [
508 guibou
509 ] ++ lib.teams.haskell.members;
510 timeout = 24 * 3600;
511 inherit (ghc.meta) license platforms;
512 };
513
514 dontStrip = targetPlatform.useAndroidPrebuilt || targetPlatform.isWasm;
515} // lib.optionalAttrs targetPlatform.useAndroidPrebuilt {
516 dontPatchELF = true;
517 noAuditTmpdir = true;
518})