1{
2 lib,
3 stdenv,
4 fetchurl,
5 perl,
6 gcc,
7 ncurses6,
8 gmp,
9 libiconv,
10 numactl,
11 libffi,
12 coreutils,
13 targetPackages,
14
15 # minimal = true; will remove files that aren't strictly necessary for
16 # regular builds and GHC bootstrapping.
17 # This is "useful" for staying within hydra's output limits for at least the
18 # aarch64-linux architecture.
19 minimal ? false,
20}:
21
22# Prebuilt only does native
23assert stdenv.targetPlatform == stdenv.hostPlatform;
24
25let
26 downloadsUrl = "https://downloads.haskell.org/ghc";
27
28 # Copy sha256 from https://downloads.haskell.org/~ghc/9.8.4/SHA256SUMS
29 version = "9.8.4";
30
31 # Information about available bindists that we use in the build.
32 #
33 # # Bindist library checking
34 #
35 # The field `archSpecificLibraries` also provides a way for us get notified
36 # early when the upstream bindist changes its dependencies (e.g. because a
37 # newer Debian version is used that uses a new `ncurses` version).
38 #
39 # Usage:
40 #
41 # * You can find the `fileToCheckFor` of libraries by running `readelf -d`
42 # on the compiler binary (`exePathForLibraryCheck`).
43 # * To skip library checking for an architecture,
44 # set `exePathForLibraryCheck = null`.
45 # * To skip file checking for a specific arch specific library,
46 # set `fileToCheckFor = null`.
47 ghcBinDists = {
48 # Binary distributions for the default libc (e.g. glibc, or libSystem on Darwin)
49 # nixpkgs uses for the respective system.
50 defaultLibc = {
51 i686-linux = {
52 variantSuffix = "";
53 src = {
54 url = "${downloadsUrl}/${version}/ghc-${version}-i386-deb10-linux.tar.xz";
55 sha256 = "e5efce16c654d5e702986258a87dd9531e1722b8051823c8ce1150ce3c5899ae";
56 };
57 exePathForLibraryCheck = "bin/ghc";
58 archSpecificLibraries = [
59 {
60 nixPackage = gmp;
61 fileToCheckFor = null;
62 }
63 {
64 nixPackage = ncurses6;
65 fileToCheckFor = "libtinfo.so.6";
66 }
67 ];
68 };
69 x86_64-linux = {
70 variantSuffix = "";
71 src = {
72 url = "${downloadsUrl}/${version}/ghc-${version}-x86_64-deb11-linux.tar.xz";
73 sha256 = "af151db8682b8c763f5a44f960f65453d794c95b60f151abc82dbdefcbe6f8ad";
74 };
75 exePathForLibraryCheck = "bin/ghc";
76 archSpecificLibraries = [
77 {
78 nixPackage = gmp;
79 fileToCheckFor = null;
80 }
81 {
82 nixPackage = ncurses6;
83 fileToCheckFor = "libtinfo.so.6";
84 }
85 ];
86 };
87 aarch64-linux = {
88 variantSuffix = "";
89 src = {
90 url = "${downloadsUrl}/${version}/ghc-${version}-aarch64-deb11-linux.tar.xz";
91 sha256 = "310204daf2df6ad16087be94b3498ca414a0953b29e94e8ec8eb4a5c9bf603d3";
92 };
93 exePathForLibraryCheck = "bin/ghc";
94 archSpecificLibraries = [
95 {
96 nixPackage = gmp;
97 fileToCheckFor = null;
98 }
99 {
100 nixPackage = ncurses6;
101 fileToCheckFor = "libtinfo.so.6";
102 }
103 {
104 nixPackage = numactl;
105 fileToCheckFor = null;
106 }
107 ];
108 };
109 x86_64-darwin = {
110 variantSuffix = "";
111 src = {
112 url = "${downloadsUrl}/${version}/ghc-${version}-x86_64-apple-darwin.tar.xz";
113 sha256 = "de7baacfb1513ab0e4ccf8911045cceee84bc8a4e39b89bd975ed3135e5f7d96";
114 };
115 exePathForLibraryCheck = null; # we don't have a library check for darwin yet
116 archSpecificLibraries = [
117 {
118 nixPackage = gmp;
119 fileToCheckFor = null;
120 }
121 {
122 nixPackage = ncurses6;
123 fileToCheckFor = null;
124 }
125 {
126 nixPackage = libiconv;
127 fileToCheckFor = null;
128 }
129 ];
130 };
131 aarch64-darwin = {
132 variantSuffix = "";
133 src = {
134 url = "${downloadsUrl}/${version}/ghc-${version}-aarch64-apple-darwin.tar.xz";
135 sha256 = "e2f12a922754fd28511512875bf6d9eb3e0cce7fc963a7266f6e1661aeabd7ed";
136 };
137 exePathForLibraryCheck = null; # we don't have a library check for darwin yet
138 archSpecificLibraries = [
139 {
140 nixPackage = gmp;
141 fileToCheckFor = null;
142 }
143 {
144 nixPackage = ncurses6;
145 fileToCheckFor = null;
146 }
147 {
148 nixPackage = libiconv;
149 fileToCheckFor = null;
150 }
151 ];
152 };
153 };
154 # Binary distributions for the musl libc for the respective system.
155 musl = {
156 aarch64-linux = {
157 variantSuffix = "-musl";
158 src = {
159 url = "${downloadsUrl}/${version}/ghc-${version}-aarch64-alpine3_18-linux.tar.xz";
160 sha256 = "b5c86a0cda0bd62d5eeeb52b1937c3bd00c70cd67dd74226ce787d5c429a4e62";
161 };
162 exePathForLibraryCheck = "bin/ghc";
163 archSpecificLibraries = [
164 {
165 nixPackage = gmp;
166 fileToCheckFor = null;
167 }
168 {
169 nixPackage = ncurses6;
170 fileToCheckFor = "libncursesw.so.6";
171 }
172 ];
173 };
174 x86_64-linux = {
175 variantSuffix = "-musl";
176 src = {
177 url = "${downloadsUrl}/${version}/ghc-${version}-x86_64-alpine3_12-linux.tar.xz";
178 sha256 = "e34bb16e8387509adc96a3d98b4a444bab425d12864c38a3629f2860b4bec2e7";
179 };
180 exePathForLibraryCheck = "bin/ghc";
181 archSpecificLibraries = [
182 {
183 nixPackage = gmp;
184 fileToCheckFor = null;
185 }
186 {
187 nixPackage = ncurses6;
188 fileToCheckFor = "libncursesw.so.6";
189 }
190 ];
191 };
192 };
193 };
194
195 distSetName = if stdenv.hostPlatform.isMusl then "musl" else "defaultLibc";
196
197 binDistUsed =
198 ghcBinDists.${distSetName}.${stdenv.hostPlatform.system}
199 or (throw "cannot bootstrap GHC on this platform ('${stdenv.hostPlatform.system}' with libc '${distSetName}')");
200
201 gmpUsed =
202 (builtins.head (
203 builtins.filter (
204 drv: lib.hasPrefix "gmp" (drv.nixPackage.name or "")
205 ) binDistUsed.archSpecificLibraries
206 )).nixPackage;
207
208 libPath = lib.makeLibraryPath (
209 # Add arch-specific libraries.
210 map ({ nixPackage, ... }: nixPackage) binDistUsed.archSpecificLibraries
211 );
212
213 libEnvVar = lib.optionalString stdenv.hostPlatform.isDarwin "DY" + "LD_LIBRARY_PATH";
214
215 runtimeDeps = [
216 targetPackages.stdenv.cc
217 targetPackages.stdenv.cc.bintools
218 coreutils # for cat
219 ]
220 # On darwin, we need unwrapped bintools as well (for otool)
221 ++ lib.optionals (stdenv.targetPlatform.linker == "cctools") [
222 targetPackages.stdenv.cc.bintools.bintools
223 ];
224
225in
226
227stdenv.mkDerivation {
228 inherit version;
229 pname = "ghc-binary${binDistUsed.variantSuffix}";
230
231 src = fetchurl binDistUsed.src;
232
233 nativeBuildInputs = [ perl ];
234
235 # Set LD_LIBRARY_PATH or equivalent so that the programs running as part
236 # of the bindist installer can find the libraries they expect.
237 # Cannot patchelf beforehand due to relative RPATHs that anticipate
238 # the final install location.
239 ${libEnvVar} = libPath;
240
241 postUnpack =
242 # Verify our assumptions of which `libtinfo.so` (ncurses) version is used,
243 # so that we know when ghc bindists upgrade that and we need to update the
244 # version used in `libPath`.
245 lib.optionalString (binDistUsed.exePathForLibraryCheck != null)
246 # Note the `*` glob because some GHCs have a suffix when unpacked, e.g.
247 # the musl bindist has dir `ghc-VERSION-x86_64-unknown-linux/`.
248 # As a result, don't shell-quote this glob when splicing the string.
249 (
250 let
251 buildExeGlob = ''ghc-${version}*/"${binDistUsed.exePathForLibraryCheck}"'';
252 in
253 lib.concatStringsSep "\n" [
254 (''
255 shopt -u nullglob
256 echo "Checking that ghc binary exists in bindist at ${buildExeGlob}"
257 if ! test -e ${buildExeGlob}; then
258 echo >&2 "GHC binary ${binDistUsed.exePathForLibraryCheck} could not be found in the bindist build directory (at ${buildExeGlob}) for arch ${stdenv.hostPlatform.system}, please check that ghcBinDists correctly reflect the bindist dependencies!"; exit 1;
259 fi
260 '')
261 (lib.concatMapStringsSep "\n" (
262 { fileToCheckFor, nixPackage }:
263 lib.optionalString (fileToCheckFor != null) ''
264 echo "Checking bindist for ${fileToCheckFor} to ensure that is still used"
265 if ! readelf -d ${buildExeGlob} | grep "${fileToCheckFor}"; then
266 echo >&2 "File ${fileToCheckFor} could not be found in ${binDistUsed.exePathForLibraryCheck} for arch ${stdenv.hostPlatform.system}, please check that ghcBinDists correctly reflect the bindist dependencies!"; exit 1;
267 fi
268
269 echo "Checking that the nix package ${nixPackage} contains ${fileToCheckFor}"
270 if ! test -e "${lib.getLib nixPackage}/lib/${fileToCheckFor}"; then
271 echo >&2 "Nix package ${nixPackage} did not contain ${fileToCheckFor} for arch ${stdenv.hostPlatform.system}, please check that ghcBinDists correctly reflect the bindist dependencies!"; exit 1;
272 fi
273 ''
274 ) binDistUsed.archSpecificLibraries)
275 ]
276 )
277 # GHC has dtrace probes, which causes ld to try to open /usr/lib/libdtrace.dylib
278 # during linking
279 + lib.optionalString stdenv.hostPlatform.isDarwin ''
280 export NIX_LDFLAGS+=" -no_dtrace_dof"
281 # not enough room in the object files for the full path to libiconv :(
282 for exe in $(find . -type f -executable); do
283 isMachO $exe || continue
284 ln -fs ${libiconv}/lib/libiconv.dylib $(dirname $exe)/libiconv.dylib
285 install_name_tool -change /usr/lib/libiconv.2.dylib @executable_path/libiconv.dylib $exe
286 done
287 ''
288
289 # We have to patch the GMP paths for the ghc-bignum package, for hadrian by
290 # modifying the package-db directly
291 + ''
292 find . -name 'ghc-bignum*.conf' \
293 -exec sed -e '/^[a-z-]*library-dirs/a \ ${lib.getLib gmpUsed}/lib' -i {} \;
294 ''
295 # Similar for iconv and libffi on darwin
296 + lib.optionalString stdenv.hostPlatform.isDarwin ''
297 find . -name 'base*.conf' \
298 -exec sed -e '/^[a-z-]*library-dirs/a \ ${lib.getLib libiconv}/lib' -i {} \;
299
300 # To link RTS in the end we also need libffi now
301 find . -name 'rts*.conf' \
302 -exec sed -e '/^[a-z-]*library-dirs/a \ ${lib.getLib libffi}/lib' \
303 -e 's@/Library/Developer/.*/usr/include/ffi@${lib.getDev libffi}/include@' \
304 -i {} \;
305 ''
306 +
307 # Some platforms do HAVE_NUMA so -lnuma requires it in library-dirs in rts/package.conf.in
308 # FFI_LIB_DIR is a good indication of places it must be needed.
309 lib.optionalString
310 (
311 lib.meta.availableOn stdenv.hostPlatform numactl
312 && builtins.any ({ nixPackage, ... }: nixPackage == numactl) binDistUsed.archSpecificLibraries
313 )
314 ''
315 find . -name package.conf.in \
316 -exec sed -i "s@FFI_LIB_DIR@FFI_LIB_DIR ${numactl.out}/lib@g" {} \;
317 ''
318 +
319 # Rename needed libraries and binaries, fix interpreter
320 lib.optionalString stdenv.hostPlatform.isLinux ''
321 find . -type f -executable -exec patchelf \
322 --interpreter ${stdenv.cc.bintools.dynamicLinker} {} \;
323 '';
324
325 # fix for `configure: error: Your linker is affected by binutils #16177`
326 preConfigure = lib.optionalString stdenv.targetPlatform.isAarch32 "LD=ld.gold";
327
328 # GHC has a patched config.sub and bindists' platforms should always work
329 dontUpdateAutotoolsGnuConfigScripts = true;
330
331 configurePlatforms = [ ];
332 configureFlags =
333 lib.optional stdenv.hostPlatform.isDarwin "--with-gcc=${./gcc-clang-wrapper.sh}"
334 # From: https://github.com/NixOS/nixpkgs/pull/43369/commits
335 ++ lib.optional stdenv.hostPlatform.isMusl "--disable-ld-override";
336
337 # No building is necessary, but calling make without flags ironically
338 # calls install-strip ...
339 dontBuild = true;
340
341 # Patch scripts to include runtime dependencies in $PATH.
342 postInstall = ''
343 for i in "$out/bin/"*; do
344 test ! -h "$i" || continue
345 isScript "$i" || continue
346 sed -i -e '2i export PATH="${lib.makeBinPath runtimeDeps}:$PATH"' "$i"
347 done
348 ''
349 + lib.optionalString stdenv.targetPlatform.isDarwin ''
350 # Work around building with binary GHC on Darwin due to GHC’s use of `ar -L` when it
351 # detects `llvm-ar` even though the resulting archives are not supported by ld64.
352 # https://gitlab.haskell.org/ghc/ghc/-/issues/23188
353 # https://github.com/haskell/cabal/issues/8882
354 sed -i -e 's/,("ar supports -L", "YES")/,("ar supports -L", "NO")/' "$out/lib/ghc-${version}/lib/settings"
355 '';
356
357 # Apparently necessary for the ghc Alpine (musl) bindist:
358 # When we strip, and then run the
359 # patchelf --set-rpath "${libPath}:$(patchelf --print-rpath $p)" $p
360 # below, running ghc (e.g. during `installCheckPhase)` gives some apparently
361 # corrupted rpath or whatever makes the loader work on nonsensical strings:
362 # running install tests
363 # Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: : symbol not found
364 # 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
365 # 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
366 # 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
367 # Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: �: symbol not found
368 # Error relocating /nix/store/...-ghc-8.10.2-binary/lib/ghc-8.10.5/bin/ghc: �?: symbol not found
369 # 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
370 # This is extremely bogus and should be investigated.
371 dontStrip = if stdenv.hostPlatform.isMusl then true else false; # `if` for explicitness
372
373 # On Linux, use patchelf to modify the executables so that they can
374 # find editline/gmp.
375 postFixup =
376 lib.optionalString (stdenv.hostPlatform.isLinux && !(binDistUsed.isStatic or false)) (
377 if stdenv.hostPlatform.isAarch64 then
378 # Keep rpath as small as possible on aarch64 for patchelf#244. All Elfs
379 # are 2 directories deep from $out/lib, so pooling symlinks there makes
380 # a short rpath.
381 ''
382 (cd $out/lib; ln -s ${lib.getLib gmpUsed}/lib/libgmp.so.10)
383 ''
384 + (
385 if stdenv.hostPlatform.isMusl then
386 ''
387 (cd $out/lib; ln -s ${ncurses6.out}/lib/libncursesw.so.6)
388 ''
389 else
390 ''
391 (cd $out/lib; ln -s ${ncurses6.out}/lib/libtinfo.so.6)
392 ''
393 )
394 + ''
395 for p in $(find "$out/lib" -type f -name "*\.so*"); do
396 (cd $out/lib; ln -s $p)
397 done
398
399 for p in $(find "$out/lib" -type f -executable); do
400 if isELF "$p"; then
401 echo "Patchelfing $p"
402 patchelf --set-rpath "\$ORIGIN:\$ORIGIN/../.." $p
403 fi
404 done
405 ''
406 else
407 ''
408 for p in $(find "$out" -type f -executable); do
409 if isELF "$p"; then
410 echo "Patchelfing $p"
411 patchelf --set-rpath "${libPath}:$(patchelf --print-rpath $p)" $p
412 fi
413 done
414 ''
415 )
416 + lib.optionalString stdenv.hostPlatform.isDarwin ''
417 # not enough room in the object files for the full path to libiconv :(
418 for exe in $(find "$out" -type f -executable); do
419 isMachO $exe || continue
420 ln -fs ${libiconv}/lib/libiconv.dylib $(dirname $exe)/libiconv.dylib
421 install_name_tool -change /usr/lib/libiconv.2.dylib @executable_path/libiconv.dylib $exe
422 done
423
424 for file in $(find "$out" -name setup-config); do
425 substituteInPlace $file --replace /usr/bin/ranlib "$(type -P ranlib)"
426 done
427 ''
428 # Recache package db which needs to happen for Hadrian bindists
429 # where we modify the package db before installing
430 + ''
431 package_db=("$out"/lib/ghc-*/lib/package.conf.d)
432 "$out/bin/ghc-pkg" --package-db="$package_db" recache
433 '';
434
435 # GHC cannot currently produce outputs that are ready for `-pie` linking.
436 # Thus, disable `pie` hardening, otherwise `recompile with -fPIE` errors appear.
437 # See:
438 # * https://github.com/NixOS/nixpkgs/issues/129247
439 # * https://gitlab.haskell.org/ghc/ghc/-/issues/19580
440 hardeningDisable = [ "pie" ];
441
442 doInstallCheck = true;
443 installCheckPhase = ''
444 # Sanity check, can ghc create executables?
445 cd $TMP
446 mkdir test-ghc; cd test-ghc
447 cat > main.hs << EOF
448 {-# LANGUAGE TemplateHaskell #-}
449 module Main where
450 main = putStrLn \$([|"yes"|])
451 EOF
452 env -i $out/bin/ghc --make main.hs || exit 1
453 echo compilation ok
454 [ $(./main) == "yes" ]
455 '';
456
457 passthru = {
458 targetPrefix = "";
459 enableShared = true;
460
461 llvmPackages = null;
462
463 # Our Cabal compiler name
464 haskellCompilerName = "ghc-${version}";
465
466 # Normal GHC derivations expose the hadrian derivation used to build them
467 # here. In the case of bindists we just make sure that the attribute exists,
468 # as it is used for checking if a GHC derivation has been built with hadrian.
469 hadrian = null;
470 };
471
472 meta = rec {
473 homepage = "http://haskell.org/ghc";
474 description = "Glasgow Haskell Compiler";
475 license = lib.licenses.bsd3;
476 # HACK: since we can't encode the libc / abi in platforms, we need
477 # to make the platform list dependent on the evaluation platform
478 # in order to avoid eval errors with musl which supports less
479 # platforms than the default libcs (i. e. glibc / libSystem).
480 # This is done for the benefit of Hydra, so `packagePlatforms`
481 # won't return any platforms that would cause an evaluation
482 # failure for `pkgsMusl.haskell.compiler.ghc922Binary`, as
483 # long as the evaluator runs on a platform that supports
484 # `pkgsMusl`.
485 platforms = builtins.attrNames ghcBinDists.${distSetName};
486 maintainers = lib.teams.haskell.members;
487 broken = !(import ./common-have-ncg.nix { inherit lib stdenv version; });
488 };
489}