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