nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1{
2 lib,
3 stdenv,
4 buildPackages,
5 buildHaskellPackages,
6 ghc,
7 jailbreak-cabal,
8 hscolour,
9 cpphs,
10 runCommandCC,
11 ghcWithHoogle,
12 ghcWithPackages,
13 haskellLib,
14 iserv-proxy,
15 nodejs,
16 writeShellScriptBin,
17}:
18
19let
20 isCross = stdenv.buildPlatform != stdenv.hostPlatform;
21
22 crossSupport = rec {
23 emulator = stdenv.hostPlatform.emulator buildPackages;
24
25 canProxyTH =
26 # iserv-proxy currently does not build on GHC 9.6
27 lib.versionAtLeast ghc.version "9.8" && stdenv.hostPlatform.emulatorAvailable buildPackages;
28
29 iservWrapper =
30 let
31 wrapperScript =
32 enableProfiling:
33 let
34 overrides = haskellLib.overrideCabal {
35 enableLibraryProfiling = enableProfiling;
36 enableExecutableProfiling = enableProfiling;
37 };
38 buildProxy = lib.getExe' iserv-proxy.build "iserv-proxy";
39 hostProxy = lib.getExe' (overrides iserv-proxy.host) "iserv-proxy-interpreter";
40 in
41 buildPackages.writeShellScriptBin ("iserv-wrapper" + lib.optionalString enableProfiling "-prof") ''
42 set -euo pipefail
43 PORT=$((5000 + $RANDOM % 5000))
44 (>&2 echo "---> Starting interpreter on port $PORT")
45 ${emulator} ${hostProxy} tmp $PORT &
46 RISERV_PID="$!"
47 trap "kill $RISERV_PID" EXIT # Needs cleanup when building without sandbox
48 ${buildProxy} $@ 127.0.0.1 "$PORT"
49 (>&2 echo "---> killing interpreter...")
50 '';
51
52 # GHC will add `-prof` to the external interpreter when doing a profiled build.
53 # Since a single derivation can build with both profiling and non-profiling versions
54 # we need both versions made available
55 both = buildPackages.symlinkJoin {
56 name = "iserv-wrapper-both";
57 paths = map wrapperScript [
58 false
59 true
60 ];
61 };
62
63 in
64 "${both}/bin/iserv-wrapper";
65 };
66
67 # Pass the "wrong" C compiler rather than none at all so packages that just
68 # use the C preproccessor still work, see
69 # https://github.com/haskell/cabal/issues/6466 for details.
70 cc =
71 if stdenv.hasCC then
72 "$CC"
73 else if stdenv.hostPlatform.isGhcjs then
74 "${emscripten}/bin/emcc"
75 else
76 "$CC_FOR_BUILD";
77
78 inherit (buildPackages)
79 fetchurl
80 removeReferencesTo
81 pkg-config
82 coreutils
83 glibcLocales
84 emscripten
85 ;
86
87in
88
89{
90 pname,
91 dontStrip ? stdenv.hostPlatform.isGhcjs,
92 version,
93 revision ? null,
94 sha256 ? null,
95 src ? fetchurl {
96 url = "mirror://hackage/${pname}-${version}.tar.gz";
97 inherit sha256;
98 },
99 sourceRoot ? null,
100 setSourceRoot ? null,
101 # Extra environment variables to set during the build.
102 # See: `../../../doc/languages-frameworks/haskell.section.md`
103 env ? { },
104 buildDepends ? [ ],
105 setupHaskellDepends ? [ ],
106 libraryHaskellDepends ? [ ],
107 executableHaskellDepends ? [ ],
108 buildTarget ? "",
109 buildTools ? [ ],
110 libraryToolDepends ? [ ],
111 executableToolDepends ? [ ],
112 testToolDepends ? [ ],
113 benchmarkToolDepends ? [ ],
114 configureFlags ? [ ],
115 buildFlags ? [ ],
116 haddockFlags ? [ ],
117 description ? null,
118 doCheck ? !isCross,
119 doBenchmark ? false,
120 doHoogle ? true,
121 doHaddockQuickjump ? doHoogle,
122 doInstallIntermediates ? false,
123 editedCabalFile ? null,
124 enableLibraryProfiling ? !stdenv.hostPlatform.isGhcjs,
125 enableExecutableProfiling ? false,
126 profilingDetail ? "exported-functions",
127 # TODO enable shared libs for cross-compiling
128 enableSharedExecutables ? false,
129 enableSharedLibraries ?
130 !stdenv.hostPlatform.isStatic
131 && (ghc.enableShared or false)
132 && !stdenv.hostPlatform.useAndroidPrebuilt, # TODO: figure out why /build leaks into RPATH
133 enableDeadCodeElimination ? (!stdenv.hostPlatform.isDarwin), # TODO: use -dead_strip for darwin
134 # Disabling this for JS prevents this crash: https://gitlab.haskell.org/ghc/ghc/-/issues/23235
135 enableStaticLibraries ?
136 !(stdenv.hostPlatform.isWindows || stdenv.hostPlatform.isWasm || stdenv.hostPlatform.isGhcjs),
137 enableHsc2hsViaAsm ? stdenv.hostPlatform.isWindows,
138 extraLibraries ? [ ],
139 librarySystemDepends ? [ ],
140 executableSystemDepends ? [ ],
141 # On macOS, statically linking against system frameworks is not supported;
142 # see https://developer.apple.com/library/content/qa/qa1118/_index.html
143 # They must be propagated to the environment of any executable linking with the library
144 libraryFrameworkDepends ? [ ],
145 executableFrameworkDepends ? [ ],
146 homepage ? "https://hackage.haskell.org/package/${pname}",
147 platforms ? lib.platforms.all, # GHC can cross-compile
148 badPlatforms ? lib.platforms.none,
149 hydraPlatforms ? null,
150 hyperlinkSource ? true,
151 isExecutable ? false,
152 isLibrary ? !isExecutable,
153 jailbreak ? false,
154 license ? null,
155 enableParallelBuilding ? true,
156 maintainers ? null,
157 teams ? null,
158 changelog ? null,
159 mainProgram ? null,
160 doCoverage ? false,
161 doHaddock ? !(ghc.isHaLVM or false) && (ghc.hasHaddock or true),
162 doHaddockInterfaces ? doHaddock && lib.versionAtLeast ghc.version "9.0.1",
163 passthru ? { },
164 pkg-configDepends ? [ ],
165 libraryPkgconfigDepends ? [ ],
166 executablePkgconfigDepends ? [ ],
167 testPkgconfigDepends ? [ ],
168 benchmarkPkgconfigDepends ? [ ],
169 testDepends ? [ ],
170 testHaskellDepends ? [ ],
171 testSystemDepends ? [ ],
172 testFrameworkDepends ? [ ],
173 benchmarkDepends ? [ ],
174 benchmarkHaskellDepends ? [ ],
175 benchmarkSystemDepends ? [ ],
176 benchmarkFrameworkDepends ? [ ],
177 # testTarget is deprecated. Use testTargets instead.
178 testTarget ? "",
179 testTargets ? lib.strings.splitString " " testTarget,
180 testFlags ? [ ],
181 broken ? false,
182 preCompileBuildDriver ? null,
183 postCompileBuildDriver ? null,
184 preUnpack ? null,
185 postUnpack ? null,
186 patches ? null,
187 patchPhase ? null,
188 prePatch ? "",
189 postPatch ? "",
190 preConfigure ? null,
191 postConfigure ? null,
192 preBuild ? null,
193 postBuild ? null,
194 preHaddock ? null,
195 postHaddock ? null,
196 installPhase ? null,
197 preInstall ? null,
198 postInstall ? null,
199 checkPhase ? null,
200 preCheck ? null,
201 postCheck ? null,
202 preFixup ? null,
203 postFixup ? null,
204 shellHook ? "",
205 coreSetup ? false, # Use only core packages to build Setup.hs.
206 useCpphs ? false,
207 hardeningDisable ? null,
208 enableObjectDeterminism ? lib.versionAtLeast ghc.version "9.12",
209 enableSeparateBinOutput ? false,
210 enableSeparateDataOutput ? false,
211 enableSeparateDocOutput ? doHaddock,
212 enableSeparateIntermediatesOutput ? false,
213 # Don't fail at configure time if there are multiple versions of the
214 # same package in the (recursive) dependencies of the package being
215 # built. Will delay failures, if any, to compile time.
216 allowInconsistentDependencies ? false,
217 maxBuildCores ? 16, # more cores usually don't improve performance: https://ghc.haskell.org/trac/ghc/ticket/9221
218 # If set to true, this builds a pre-linked .o file for this Haskell library.
219 # This can make it slightly faster to load this library into GHCi, but takes
220 # extra disk space and compile time.
221 enableLibraryForGhci ? false,
222 # Set this to a previous build of this same package to reuse the intermediate
223 # build products from that prior build as a starting point for accelerating
224 # this build
225 previousIntermediates ? null,
226 # References to these store paths are forbidden in the produced output.
227 disallowedRequisites ? [ ],
228 # Whether to allow the produced output to refer to `ghc`.
229 #
230 # This is used by `haskell.lib.justStaticExecutables` to help prevent static
231 # Haskell binaries from having erroneous dependencies on GHC.
232 #
233 # See https://nixos.org/manual/nixpkgs/unstable/#haskell-packaging-helpers
234 # or its source doc/languages-frameworks/haskell.section.md
235 disallowGhcReference ? false,
236 # By default we convert the `.cabal` file to Unix line endings to work around
237 # Hackage converting them to DOS line endings when revised, see
238 # <https://github.com/haskell/hackage-server/issues/316>.
239 # Pass `true` to disable this behavior.
240 dontConvertCabalFileToUnix ? false,
241 # Cabal 3.8 which is shipped by default for GHC >= 9.3 always calls
242 # `pkg-config --libs --static` as part of the configure step. This requires
243 # Requires.private dependencies of pkg-config dependencies to be present in
244 # PKG_CONFIG_PATH which is normally not the case in nixpkgs (except in pkgsStatic).
245 # Since there is no patch or upstream patch yet, we replicate the automatic
246 # propagation of dependencies in pkgsStatic for allPkgConfigDepends for
247 # GHC >= 9.3 by default. This option allows overriding this behavior manually
248 # if mismatching Cabal and GHC versions are used.
249 # See also <https://github.com/haskell/cabal/issues/8455>.
250 __propagatePkgConfigDepends ? lib.versionAtLeast ghc.version "9.3",
251 # Propagation can easily lead to the argv limit being exceeded in linker or C
252 # compiler invocations. To work around this we can only propagate derivations
253 # that are known to provide pkg-config modules, as indicated by the presence
254 # of `meta.pkgConfigModules`. This option defaults to false for now, since
255 # this metadata is far from complete in nixpkgs.
256 __onlyPropagateKnownPkgConfigModules ? false,
257
258 enableExternalInterpreter ? isCross && crossSupport.canProxyTH,
259}@args:
260
261assert editedCabalFile != null -> revision != null;
262
263# We only use iserv-proxy for the external interpreter
264assert enableExternalInterpreter -> crossSupport.canProxyTH;
265
266# --enable-static does not work on windows. This is a bug in GHC.
267# --enable-static will pass -staticlib to ghc, which only works for mach-o and elf.
268assert stdenv.hostPlatform.isWindows -> enableStaticLibraries == false;
269assert stdenv.hostPlatform.isWasm -> enableStaticLibraries == false;
270
271let
272
273 inherit (lib)
274 optional
275 optionals
276 optionalString
277 versionAtLeast
278 concatStringsSep
279 enableFeature
280 optionalAttrs
281 ;
282
283 isHaLVM = ghc.isHaLVM or false;
284
285 # GHC used for building Setup.hs
286 #
287 # Same as our GHC, unless we're cross, in which case it is native GHC with the
288 # same version.
289 nativeGhc = buildHaskellPackages.ghc;
290
291 # the target dir for haddock documentation
292 docdir = docoutput: docoutput + "/share/doc/" + pname + "-" + version;
293
294 binDir = if enableSeparateBinOutput then "$bin/bin" else "$out/bin";
295
296 newCabalFileUrl = "mirror://hackage/${pname}-${version}/revision/${revision}.cabal";
297 newCabalFile = fetchurl {
298 url = newCabalFileUrl;
299 sha256 = editedCabalFile;
300 name = "${pname}-${version}-r${revision}.cabal";
301 };
302
303 defaultSetupHs = builtins.toFile "Setup.hs" ''
304 import Distribution.Simple
305 main = defaultMain
306 '';
307
308 # This awk expression transforms a package conf file like
309 #
310 # author: John Doe <john-doe@example.com>
311 # description:
312 # The purpose of this library is to do
313 # foo and bar among other things
314 #
315 # into a more easily processeable form:
316 #
317 # author: John Doe <john-doe@example.com>
318 # description: The purpose of this library is to do foo and bar among other things
319 unprettyConf = builtins.toFile "unpretty-cabal-conf.awk" ''
320 /^[^ ]+:/ {
321 # When the line starts with a new field, terminate the previous one with a newline
322 if (started == 1) print ""
323 # to strip leading spaces
324 $1=$1
325 printf "%s", $0
326 started=1
327 }
328
329 /^ +/ {
330 # to strip leading spaces
331 $1=$1
332 printf " %s", $0
333 }
334
335 # Terminate the final field with a newline
336 END { print "" }
337 '';
338
339 crossCabalFlags = [
340 "--with-ghc=${ghcCommand}"
341 "--with-ghc-pkg=${ghc.targetPrefix}ghc-pkg"
342 "--with-gcc=${cc}"
343 ]
344 ++ optionals stdenv.hasCC [
345 "--with-ld=${stdenv.cc.bintools.targetPrefix}ld"
346 "--with-ar=${stdenv.cc.bintools.targetPrefix}ar"
347 # use the one that comes with the cross compiler.
348 "--with-hsc2hs=${ghc.targetPrefix}hsc2hs"
349 "--with-strip=${stdenv.cc.bintools.targetPrefix}strip"
350 ]
351 ++ optionals (!isHaLVM) [
352 "--hsc2hs-option=--cross-compile"
353 (optionalString enableHsc2hsViaAsm "--hsc2hs-option=--via-asm")
354 ]
355 ++ optional (allPkgconfigDepends != [ ]) "--with-pkg-config=${pkg-config.targetPrefix}pkg-config"
356
357 ++ optionals enableExternalInterpreter (
358 map (opt: "--ghc-option=${opt}") [
359 "-fexternal-interpreter"
360 "-pgmi"
361 crossSupport.iservWrapper
362 ]
363 );
364
365 makeGhcOptions = opts: lib.concatStringsSep " " (map (opt: "--ghc-option=${opt}") opts);
366
367 defaultConfigureFlags = [
368 "--verbose"
369 "--prefix=$out"
370 # Note: This must be kept in sync manually with mkGhcLibdir
371 ("--libdir=\\$prefix/lib/\\$compiler" + lib.optionalString (ghc ? hadrian) "/lib")
372 "--libsubdir=\\$abi/\\$libname"
373 (optionalString enableSeparateDataOutput "--datadir=$data/share/${ghcNameWithPrefix}")
374 (optionalString enableSeparateDocOutput "--docdir=${docdir "$doc"}")
375 ]
376 ++ optionals stdenv.hasCC [
377 "--with-gcc=$CC" # Clang won't work without that extra information.
378 ]
379 ++ [
380 "--package-db=$packageConfDir"
381 (optionalString (
382 enableSharedExecutables && stdenv.hostPlatform.isLinux
383 ) "--ghc-option=-optl=-Wl,-rpath=$out/${ghcLibdir}/${pname}-${version}")
384 (optionalString (
385 enableSharedExecutables && stdenv.hostPlatform.isDarwin
386 ) "--ghc-option=-optl=-Wl,-headerpad_max_install_names")
387 (optionalString enableParallelBuilding (makeGhcOptions [
388 "-j$NIX_BUILD_CORES"
389 "+RTS"
390 "-A64M"
391 "-RTS"
392 ]))
393 (optionalString useCpphs (
394 "--with-cpphs=${cpphs}/bin/cpphs "
395 + (makeGhcOptions [
396 "-cpp"
397 "-pgmP${cpphs}/bin/cpphs"
398 "-optP--cpp"
399 ])
400 ))
401 (enableFeature enableLibraryProfiling "library-profiling")
402 (optionalString (
403 enableExecutableProfiling || enableLibraryProfiling
404 ) "--profiling-detail=${profilingDetail}")
405 (enableFeature enableExecutableProfiling "profiling")
406 (enableFeature enableSharedLibraries "shared")
407 (enableFeature doCoverage "coverage")
408 (enableFeature enableStaticLibraries "static")
409 (enableFeature enableSharedExecutables "executable-dynamic")
410 (enableFeature doCheck "tests")
411 (enableFeature doBenchmark "benchmarks")
412 "--enable-library-vanilla" # TODO: Should this be configurable?
413 (enableFeature enableLibraryForGhci "library-for-ghci")
414 (enableFeature enableDeadCodeElimination "split-sections")
415 (enableFeature (!dontStrip) "library-stripping")
416 (enableFeature (!dontStrip) "executable-stripping")
417 ]
418 ++ optionals enableObjectDeterminism [
419 "--ghc-option=-fobject-determinism"
420 ]
421 ++ optionals isCross (
422 [
423 "--configure-option=--host=${stdenv.hostPlatform.config}"
424 ]
425 ++ crossCabalFlags
426 )
427 ++ optionals enableSeparateBinOutput [
428 "--bindir=${binDir}"
429 ]
430 ++ optionals (doHaddockInterfaces && isLibrary) [
431 "--ghc-option=-haddock"
432 ];
433
434 postPhases = optional doInstallIntermediates "installIntermediatesPhase";
435
436 setupCompileFlags = [
437 (optionalString (!coreSetup) "-package-db=$setupPackageConfDir")
438 "-threaded" # https://github.com/haskell/cabal/issues/2398
439 ];
440
441 isHaskellPkg = x: x ? isHaskellLibrary;
442
443 # Work around a Cabal bug requiring pkg-config --static --libs to work even
444 # when linking dynamically, affecting Cabal 3.8 and 3.9.
445 # https://github.com/haskell/cabal/issues/8455
446 #
447 # For this, we treat the runtime system/pkg-config dependencies of a Haskell
448 # derivation as if they were propagated from their dependencies which allows
449 # pkg-config --static to work in most cases.
450 allPkgconfigDepends =
451 let
452 # If __onlyPropagateKnownPkgConfigModules is set, packages without
453 # meta.pkgConfigModules will be filtered out, otherwise all packages in
454 # buildInputs and propagatePlainBuildInputs are propagated.
455 propagateValue =
456 drv: lib.isDerivation drv && (__onlyPropagateKnownPkgConfigModules -> drv ? meta.pkgConfigModules);
457
458 # Take list of derivations and return list of the transitive dependency
459 # closure, only taking into account buildInputs. Loosely based on
460 # closePropagationFast.
461 propagatePlainBuildInputs =
462 drvs:
463 map (i: i.val) (
464 builtins.genericClosure {
465 startSet = map (drv: {
466 key = drv.outPath;
467 val = drv;
468 }) (builtins.filter propagateValue drvs);
469 operator =
470 { val, ... }:
471 builtins.concatMap (
472 drv:
473 if propagateValue drv then
474 [
475 {
476 key = drv.outPath;
477 val = drv;
478 }
479 ]
480 else
481 [ ]
482 ) (val.buildInputs or [ ] ++ val.propagatedBuildInputs or [ ]);
483 }
484 );
485 in
486
487 if __propagatePkgConfigDepends then
488 propagatePlainBuildInputs allPkgconfigDepends'
489 else
490 allPkgconfigDepends';
491 allPkgconfigDepends' =
492 pkg-configDepends
493 ++ libraryPkgconfigDepends
494 ++ executablePkgconfigDepends
495 ++ optionals doCheck testPkgconfigDepends
496 ++ optionals doBenchmark benchmarkPkgconfigDepends;
497
498 depsBuildBuild = [
499 nativeGhc
500 ]
501 # CC_FOR_BUILD may be necessary if we have no C preprocessor for the host
502 # platform. See crossCabalFlags above for more details.
503 ++ lib.optionals (!stdenv.hasCC) [ buildPackages.stdenv.cc ];
504 collectedToolDepends =
505 buildTools
506 ++ libraryToolDepends
507 ++ executableToolDepends
508 ++ optionals doCheck testToolDepends
509 ++ optionals doBenchmark benchmarkToolDepends;
510 nativeBuildInputs = [
511 ghc
512 removeReferencesTo
513 ]
514 ++ optional (allPkgconfigDepends != [ ]) (
515 assert pkg-config != null;
516 pkg-config
517 )
518 ++ setupHaskellDepends
519 ++ collectedToolDepends
520 ++ optional stdenv.hostPlatform.isGhcjs nodejs;
521 propagatedBuildInputs =
522 buildDepends ++ libraryHaskellDepends ++ executableHaskellDepends ++ libraryFrameworkDepends;
523 otherBuildInputsHaskell =
524 optionals doCheck (testDepends ++ testHaskellDepends)
525 ++ optionals doBenchmark (benchmarkDepends ++ benchmarkHaskellDepends);
526 otherBuildInputsSystem =
527 extraLibraries
528 ++ librarySystemDepends
529 ++ executableSystemDepends
530 ++ executableFrameworkDepends
531 ++ allPkgconfigDepends
532 ++ optionals doCheck (testSystemDepends ++ testFrameworkDepends)
533 ++ optionals doBenchmark (benchmarkSystemDepends ++ benchmarkFrameworkDepends);
534 # TODO next rebuild just define as `otherBuildInputsHaskell ++ otherBuildInputsSystem`
535 otherBuildInputs =
536 extraLibraries
537 ++ librarySystemDepends
538 ++ executableSystemDepends
539 ++ executableFrameworkDepends
540 ++ allPkgconfigDepends
541 ++ optionals doCheck (
542 testDepends ++ testHaskellDepends ++ testSystemDepends ++ testFrameworkDepends
543 )
544 ++ optionals doBenchmark (
545 benchmarkDepends ++ benchmarkHaskellDepends ++ benchmarkSystemDepends ++ benchmarkFrameworkDepends
546 );
547
548 setupCommand = "./Setup";
549
550 ghcCommand' = "ghc";
551 ghcCommand = "${ghc.targetPrefix}${ghcCommand'}";
552
553 ghcNameWithPrefix = "${ghc.targetPrefix}${ghc.haskellCompilerName}";
554 mkGhcLibdir =
555 ghc:
556 "lib/${ghc.targetPrefix}${ghc.haskellCompilerName}" + lib.optionalString (ghc ? hadrian) "/lib";
557 ghcLibdir = mkGhcLibdir ghc;
558
559 nativeGhcCommand = "${nativeGhc.targetPrefix}ghc";
560
561 buildPkgDb = thisGhc: packageConfDir: ''
562 # If this dependency has a package database, then copy the contents of it,
563 # unless it is one of our GHCs. These can appear in our dependencies when
564 # we are doing native builds, and they have package databases in them, but
565 # we do not want to copy them over.
566 #
567 # We don't need to, since those packages will be provided by the GHC when
568 # we compile with it, and doing so can result in having multiple copies of
569 # e.g. Cabal in the database with the same name and version, which is
570 # ambiguous.
571 if [ -d "$p/${mkGhcLibdir thisGhc}/package.conf.d" ] && [ "$p" != "${ghc}" ] && [ "$p" != "${nativeGhc}" ]; then
572 cp -f "$p/${mkGhcLibdir thisGhc}/package.conf.d/"*.conf ${packageConfDir}/
573 continue
574 fi
575 '';
576
577 intermediatesDir = "share/haskell/${ghc.version}/${pname}-${version}/dist";
578
579 jsexe = rec {
580 shouldAdd = stdenv.hostPlatform.isGhcjs && isExecutable;
581 shouldCopy = shouldAdd && !doInstallIntermediates;
582 shouldSymlink = shouldAdd && doInstallIntermediates;
583 };
584
585 # This is a script suitable for --test-wrapper of Setup.hs' test command
586 # (https://cabal.readthedocs.io/en/3.12/setup-commands.html#cmdoption-runhaskell-Setup.hs-test-test-wrapper).
587 # We use it to set some environment variables that the test suite may need,
588 # e.g. GHC_PACKAGE_PATH to invoke GHC(i) at runtime with build dependencies
589 # available. See the comment accompanying checkPhase below on how to customize
590 # this behavior. We need to use a wrapper script since Cabal forbids setting
591 # certain environment variables since they can alter GHC's behavior (e.g.
592 # GHC_PACKAGE_PATH) and cause failures. While building, Cabal will set
593 # GHC_ENVIRONMENT to make the packages picked at configure time available to
594 # GHC, but unfortunately not at test time. The test wrapper script will be
595 # executed after such environment checks, so we can take some liberties which
596 # is unproblematic since we know our synthetic package db matches what Cabal
597 # will see at configure time exactly. See also
598 # <https://github.com/haskell/cabal/issues/7792>.
599 testWrapperScript = buildPackages.writeShellScript "haskell-generic-builder-test-wrapper.sh" ''
600 set -eu
601
602 # We expect this to be either empty or set by checkPhase
603 if [[ -n "''${NIX_GHC_PACKAGE_PATH_FOR_TEST}" ]]; then
604 export GHC_PACKAGE_PATH="''${NIX_GHC_PACKAGE_PATH_FOR_TEST}"
605 fi
606
607 exec "$@"
608 '';
609
610 testTargetsString =
611 lib.warnIf (testTarget != "")
612 "haskellPackages.mkDerivation: testTarget is deprecated. Use testTargets instead"
613 (lib.concatStringsSep " " testTargets);
614
615 env' = {
616 LANG = "en_US.UTF-8"; # GHC needs the locale configured during the Haddock phase.
617 }
618 // env
619 # Implicit pointer to integer conversions are errors by default since clang 15.
620 # Works around https://gitlab.haskell.org/ghc/ghc/-/issues/23456. krank:ignore-line
621 # A fix was included in GHC 9.10.* and backported to 9.6.5 and 9.8.2 (but we no longer
622 # ship 9.8.1).
623 // optionalAttrs (lib.versionOlder ghc.version "9.6.5" && stdenv.hasCC && stdenv.cc.isClang) {
624 NIX_CFLAGS_COMPILE =
625 "-Wno-error=int-conversion"
626 + lib.optionalString (env ? NIX_CFLAGS_COMPILE) (" " + env.NIX_CFLAGS_COMPILE);
627 };
628
629in
630lib.fix (
631 drv:
632
633 stdenv.mkDerivation (
634 {
635 inherit pname version;
636
637 outputs = [
638 "out"
639 ]
640 ++ (optional enableSeparateDataOutput "data")
641 ++ (optional enableSeparateDocOutput "doc")
642 ++ (optional enableSeparateBinOutput "bin")
643 ++ (optional enableSeparateIntermediatesOutput "intermediates");
644
645 setOutputFlags = false;
646
647 pos = builtins.unsafeGetAttrPos "pname" args;
648
649 prePhases = [ "setupCompilerEnvironmentPhase" ];
650 preConfigurePhases = [ "compileBuildDriverPhase" ];
651 preInstallPhases = [ "haddockPhase" ];
652
653 inherit src;
654
655 inherit depsBuildBuild nativeBuildInputs;
656 buildInputs =
657 otherBuildInputs
658 ++ optionals (!isLibrary) propagatedBuildInputs
659 # For patchShebangsAuto in fixupPhase
660 ++ optionals stdenv.hostPlatform.isGhcjs [ nodejs ];
661 propagatedBuildInputs = optionals isLibrary propagatedBuildInputs;
662
663 env =
664 optionalAttrs (stdenv.buildPlatform.libc == "glibc") {
665 LOCALE_ARCHIVE = "${glibcLocales}/lib/locale/locale-archive";
666 }
667 // env';
668
669 prePatch =
670 optionalString (editedCabalFile != null) ''
671 echo "Replace Cabal file with edited version from ${newCabalFileUrl}."
672 cp ${newCabalFile} ${pname}.cabal
673 ''
674 + prePatch
675 + "\n"
676 # cabal2nix-generated expressions run hpack not until prePatch to create
677 # the .cabal file (if necessary)
678 + lib.optionalString (!dontConvertCabalFileToUnix) ''
679 sed -i -e 's/\r$//' *.cabal
680 '';
681
682 postPatch =
683 optionalString jailbreak ''
684 echo "Run jailbreak-cabal to lift version restrictions on build inputs."
685 ${jailbreak-cabal}/bin/jailbreak-cabal *.cabal
686 ''
687 + postPatch;
688
689 setupCompilerEnvironmentPhase = ''
690 NIX_BUILD_CORES=$(( NIX_BUILD_CORES < ${toString maxBuildCores} ? NIX_BUILD_CORES : ${toString maxBuildCores} ))
691 runHook preSetupCompilerEnvironment
692
693 echo "Build with ${ghc}."
694 ${optionalString (isLibrary && hyperlinkSource) "export PATH=${hscolour}/bin:$PATH"}
695
696 builddir="$(mktemp -d)"
697 setupPackageConfDir="$builddir/setup-package.conf.d"
698 mkdir -p $setupPackageConfDir
699 packageConfDir="$builddir/package.conf.d"
700 mkdir -p $packageConfDir
701
702 setupCompileFlags="${concatStringsSep " " setupCompileFlags}"
703 configureFlags="${concatStringsSep " " defaultConfigureFlags} $configureFlags"
704 ''
705 # We build the Setup.hs on the *build* machine, and as such should only add
706 # dependencies for the build machine.
707 #
708 # pkgs* arrays defined in stdenv/setup.hs
709 + ''
710 for p in "''${pkgsBuildBuild[@]}" "''${pkgsBuildHost[@]}" "''${pkgsBuildTarget[@]}"; do
711 ${buildPkgDb nativeGhc "$setupPackageConfDir"}
712 done
713 ${nativeGhcCommand}-pkg --package-db="$setupPackageConfDir" recache
714 ''
715 # For normal components
716 + ''
717 for p in "''${pkgsHostHost[@]}" "''${pkgsHostTarget[@]}"; do
718 ${buildPkgDb ghc "$packageConfDir"}
719 if [ -d "$p/include" ]; then
720 appendToVar configureFlags "--extra-include-dirs=$p/include"
721 fi
722 if [ -d "$p/lib" ]; then
723 appendToVar configureFlags "--extra-lib-dirs=$p/lib"
724 fi
725 if [[ -d "$p/Library/Frameworks" ]]; then
726 appendToVar configureFlags "--extra-framework-dirs=$p/Library/Frameworks"
727 fi
728 ''
729 + ''
730 done
731 ''
732 + (optionalString stdenv.hostPlatform.isGhcjs ''
733 export EM_CACHE="$(realpath "$(mktemp -d emcache.XXXXXXXXXX)")"
734 cp -Lr ${emscripten}/share/emscripten/cache/* "$EM_CACHE/"
735 chmod u+rwX -R "$EM_CACHE"
736 '')
737 # only use the links hack if we're actually building dylibs. otherwise, the
738 # "dynamic-library-dirs" point to nonexistent paths, and the ln command becomes
739 # "ln -s $out/lib/links", which tries to recreate the links dir and fails
740 #
741 # Note: We need to disable this work-around when using intermediate build
742 # products from a prior build because otherwise Nix will change permissions on
743 # the `$out/lib/links` directory to read-only when the build is done after the
744 # dist directory has already been exported, which triggers an unnecessary
745 # rebuild of modules included in the exported dist directory.
746 + (optionalString
747 (
748 stdenv.hostPlatform.isDarwin
749 && (enableSharedLibraries || enableSharedExecutables)
750 && !enableSeparateIntermediatesOutput
751 )
752 ''
753 # Work around a limit in the macOS Sierra linker on the number of paths
754 # referenced by any one dynamic library:
755 #
756 # Create a local directory with symlinks of the *.dylib (macOS shared
757 # libraries) from all the dependencies.
758 local dynamicLinksDir="$out/lib/links"
759 mkdir -p $dynamicLinksDir
760
761 # Unprettify all package conf files before reading/writing them
762 for d in "$packageConfDir/"*; do
763 # gawk -i inplace seems to strip the last newline
764 gawk -f ${unprettyConf} "$d" > tmp
765 mv tmp "$d"
766 done
767
768 for d in $(grep '^dynamic-library-dirs:' "$packageConfDir"/* | cut -d' ' -f2- | tr ' ' '\n' | sort -u); do
769 for lib in "$d/"*.{dylib,so}; do
770 # Allow overwriting because C libs can be pulled in multiple times.
771 ln -sf "$lib" "$dynamicLinksDir"
772 done
773 done
774 # Edit the local package DB to reference the links directory.
775 for f in "$packageConfDir/"*.conf; do
776 sed -i "s,dynamic-library-dirs: .*,dynamic-library-dirs: $dynamicLinksDir," "$f"
777 done
778 ''
779 )
780 + ''
781 ${ghcCommand}-pkg --package-db="$packageConfDir" recache
782
783 runHook postSetupCompilerEnvironment
784 '';
785
786 compileBuildDriverPhase = ''
787 runHook preCompileBuildDriver
788
789 for i in Setup.hs Setup.lhs ${defaultSetupHs}; do
790 test -f $i && break
791 done
792
793 echo setupCompileFlags: $setupCompileFlags
794 ${nativeGhcCommand} $setupCompileFlags --make -o Setup -odir $builddir -hidir $builddir $i
795
796 runHook postCompileBuildDriver
797 '';
798
799 # Cabal takes flags like `--configure-option=--host=...` instead
800 configurePlatforms = [ ];
801 inherit configureFlags buildFlags;
802
803 # Note: the options here must be always added, regardless of whether the
804 # package specifies `hardeningDisable`.
805 hardeningDisable =
806 lib.optionals (args ? hardeningDisable) hardeningDisable
807 ++ lib.optional (ghc.isHaLVM or false) "all";
808
809 configurePhase = ''
810 runHook preConfigure
811
812 echo configureFlags: $configureFlags
813 ${setupCommand} configure $configureFlags 2>&1 | ${coreutils}/bin/tee "$NIX_BUILD_TOP/cabal-configure.log"
814 ${lib.optionalString (!allowInconsistentDependencies) ''
815 if grep -E -q -z 'Warning:.*depends on multiple versions' "$NIX_BUILD_TOP/cabal-configure.log"; then
816 echo >&2 "*** abort because of serious configure-time warning from Cabal"
817 exit 1
818 fi
819 ''}
820
821 runHook postConfigure
822 '';
823
824 buildPhase = ''
825 runHook preBuild
826 ''
827 + lib.optionalString (previousIntermediates != null) ''
828 mkdir -p dist;
829 rm -r dist/build
830 cp -r ${previousIntermediates}/${intermediatesDir}/build dist/build
831 find dist/build -exec chmod u+w {} +
832 find dist/build -exec touch -d '1970-01-01T00:00:00Z' {} +
833 ''
834 + ''
835 ${setupCommand} build ${buildTarget} $buildFlags
836 runHook postBuild
837 '';
838
839 inherit doCheck;
840
841 # Run test suite(s) and pass `checkFlags` as well as `checkFlagsArray`.
842 # `testFlags` are added to `checkFlagsArray` each prefixed with
843 # `--test-option`, so Cabal passes it to the underlying test suite binary.
844 #
845 # We also take care of setting GHC_PACKAGE_PATH during test suite execution,
846 # so it can run GHC(i) with build dependencies available:
847 # - If NIX_GHC_PACKAGE_PATH_FOR_TEST is set, it become the value of GHC_PACKAGE_PATH
848 # while the test suite is executed.
849 # - If it is empty, it'll be unset during test suite execution.
850 # - Otherwise GHC_PACKAGE_PATH will have the package db used for configuring
851 # plus GHC's core packages.
852 checkPhase = ''
853 runHook preCheck
854 checkFlagsArray+=(
855 "--show-details=streaming"
856 "--test-wrapper=${testWrapperScript}"
857 ${lib.escapeShellArgs (map (opt: "--test-option=${opt}") testFlags)}
858 )
859 export NIX_GHC_PACKAGE_PATH_FOR_TEST="''${NIX_GHC_PACKAGE_PATH_FOR_TEST:-$packageConfDir:}"
860 ${setupCommand} test ${testTargetsString} $checkFlags ''${checkFlagsArray:+"''${checkFlagsArray[@]}"}
861 runHook postCheck
862 '';
863
864 haddockPhase = ''
865 runHook preHaddock
866 ${optionalString (doHaddock && isLibrary) ''
867 ${setupCommand} haddock --html \
868 ${optionalString doHoogle "--hoogle"} \
869 ${optionalString doHaddockQuickjump "--quickjump"} \
870 ${optionalString (isLibrary && hyperlinkSource) "--hyperlink-source"} \
871 ${optionalString enableParallelBuilding "--haddock-option=-j$NIX_BUILD_CORES"} \
872 --haddock-option=--no-tmp-comp-dir \
873 ${lib.concatStringsSep " " haddockFlags}
874 ''}
875 runHook postHaddock
876 '';
877
878 installPhase = ''
879 runHook preInstall
880
881 ${
882 if !isLibrary && buildTarget == "" then
883 "${setupCommand} install"
884 # ^^ if the project is not a library, and no build target is specified, we can just use "install".
885 else if !isLibrary then
886 "${setupCommand} copy ${buildTarget}"
887 # ^^ if the project is not a library, and we have a build target, then use "copy" to install
888 # just the target specified; "install" will error here, since not all targets have been built.
889 else
890 ''
891 ${setupCommand} copy ${buildTarget}
892 local packageConfDir="$out/${ghcLibdir}/package.conf.d"
893 local packageConfFile="$packageConfDir/${pname}-${version}.conf"
894 mkdir -p "$packageConfDir"
895 ${setupCommand} register --gen-pkg-config=$packageConfFile
896 if [ -d "$packageConfFile" ]; then
897 mv "$packageConfFile/"* "$packageConfDir"
898 rmdir "$packageConfFile"
899 fi
900 for packageConfFile in "$packageConfDir/"*; do
901 local pkgId=$(gawk -f ${unprettyConf} "$packageConfFile" \
902 | grep '^id:' | cut -d' ' -f2)
903 mv "$packageConfFile" "$packageConfDir/$pkgId.conf"
904 done
905
906 # delete confdir if there are no libraries
907 find $packageConfDir -maxdepth 0 -empty -delete;
908 ''
909 }
910
911
912 ${optionalString doCoverage "mkdir -p $out/share && cp -r dist/hpc $out/share"}
913
914 ${optionalString jsexe.shouldCopy ''
915 for jsexeDir in dist/build/*/*.jsexe; do
916 bn=$(basename $jsexeDir)
917 exe="''${bn%.jsexe}"
918 cp -r dist/build/$exe/$exe.jsexe ${binDir}
919 done
920 ''}
921
922 ${optionalString enableSeparateDocOutput ''
923 for x in ${docdir "$doc"}"/html/src/"*.html; do
924 remove-references-to -t $out $x
925 done
926 mkdir -p $doc
927 ''}
928 ${optionalString enableSeparateDataOutput "mkdir -p $data"}
929
930 runHook postInstall
931 '';
932
933 ${if doInstallIntermediates then "installIntermediatesPhase" else null} = ''
934 runHook preInstallIntermediates
935 intermediatesOutput=${if enableSeparateIntermediatesOutput then "$intermediates" else "$out"}
936 installIntermediatesDir="$intermediatesOutput/${intermediatesDir}"
937 mkdir -p "$installIntermediatesDir"
938 cp -r dist/build "$installIntermediatesDir"
939 runHook postInstallIntermediates
940
941 ${optionalString jsexe.shouldSymlink ''
942 for jsexeDir in $installIntermediatesDir/build/*/*.jsexe; do
943 bn=$(basename $jsexeDir)
944 exe="''${bn%.jsexe}"
945 (cd ${binDir} && ln -s $installIntermediatesDir/build/$exe/$exe.jsexe)
946 done
947 ''}
948 '';
949
950 passthru = passthru // rec {
951
952 inherit pname version disallowGhcReference;
953
954 compiler = ghc;
955
956 # All this information is intended just for `shellFor`. It should be
957 # considered unstable and indeed we knew how to keep it private we would.
958 getCabalDeps = {
959 inherit
960 buildDepends
961 buildTools
962 executableFrameworkDepends
963 executableHaskellDepends
964 executablePkgconfigDepends
965 executableSystemDepends
966 executableToolDepends
967 extraLibraries
968 libraryFrameworkDepends
969 libraryHaskellDepends
970 libraryPkgconfigDepends
971 librarySystemDepends
972 libraryToolDepends
973 pkg-configDepends
974 setupHaskellDepends
975 ;
976 }
977 // lib.optionalAttrs doCheck {
978 inherit
979 testDepends
980 testFrameworkDepends
981 testHaskellDepends
982 testPkgconfigDepends
983 testSystemDepends
984 testToolDepends
985 ;
986 }
987 // lib.optionalAttrs doBenchmark {
988 inherit
989 benchmarkDepends
990 benchmarkFrameworkDepends
991 benchmarkHaskellDepends
992 benchmarkPkgconfigDepends
993 benchmarkSystemDepends
994 benchmarkToolDepends
995 ;
996 };
997
998 # Attributes for the old definition of `shellFor`. Should be removed but
999 # this predates the warning at the top of `getCabalDeps`.
1000 getBuildInputs = rec {
1001 inherit propagatedBuildInputs otherBuildInputs allPkgconfigDepends;
1002 haskellBuildInputs = isHaskellPartition.right;
1003 systemBuildInputs = isHaskellPartition.wrong;
1004 isHaskellPartition = lib.partition isHaskellPkg (
1005 propagatedBuildInputs ++ otherBuildInputs ++ depsBuildBuild ++ nativeBuildInputs
1006 );
1007 };
1008
1009 isHaskellLibrary = isLibrary;
1010
1011 # TODO: ask why the split outputs are configurable at all?
1012 # TODO: include tests for split if possible
1013 # Given the haskell package, returns
1014 # the directory containing the haddock documentation.
1015 # `null' if no haddock documentation was built.
1016 # TODO: fetch the self from the fixpoint instead
1017 haddockDir = self: if doHaddock then "${docdir self.doc}/html" else null;
1018
1019 # Creates a derivation containing all of the necessary dependencies for building the
1020 # parent derivation. The attribute set that it takes as input can be viewed as:
1021 #
1022 # { withHoogle }
1023 #
1024 # The derivation that it builds contains no outpaths because it is meant for use
1025 # as an environment
1026 #
1027 # # Example use
1028 # # Creates a shell with all of the dependencies required to build the "hello" package,
1029 # # and with python:
1030 #
1031 # > nix-shell -E 'with (import <nixpkgs> {}); \
1032 # > haskellPackages.hello.envFunc { buildInputs = [ python ]; }'
1033 envFunc =
1034 {
1035 withHoogle ? false,
1036 }:
1037 let
1038 name = "ghc-shell-for-${drv.name}";
1039
1040 withPackages = if withHoogle then ghcWithHoogle else ghcWithPackages;
1041
1042 # We use the `ghcWithPackages` function from `buildHaskellPackages` if we
1043 # want a shell for the sake of cross compiling a package. In the native case
1044 # we don't use this at all, and instead put the setupDepends in the main
1045 # `ghcWithPackages`. This way we don't have two wrapper scripts called `ghc`
1046 # shadowing each other on the PATH.
1047 ghcEnvForBuild =
1048 assert isCross;
1049 buildHaskellPackages.ghcWithPackages (_: setupHaskellDepends);
1050
1051 ghcEnv = withPackages (
1052 _: otherBuildInputsHaskell ++ propagatedBuildInputs ++ lib.optionals (!isCross) setupHaskellDepends
1053 );
1054
1055 ghcCommandCaps = lib.toUpper ghcCommand';
1056 in
1057 runCommandCC name {
1058 inherit shellHook;
1059
1060 depsBuildBuild = lib.optional isCross ghcEnvForBuild;
1061 nativeBuildInputs = [
1062 ghcEnv
1063 ]
1064 ++ optional (allPkgconfigDepends != [ ]) pkg-config
1065 ++ collectedToolDepends;
1066 buildInputs = otherBuildInputsSystem;
1067
1068 env = {
1069 "NIX_${ghcCommandCaps}" = "${ghcEnv}/bin/${ghcCommand}";
1070 "NIX_${ghcCommandCaps}PKG" = "${ghcEnv}/bin/${ghcCommand}-pkg";
1071 # TODO: is this still valid?
1072 "NIX_${ghcCommandCaps}_DOCDIR" = "${ghcEnv}/share/doc/ghc/html";
1073 "NIX_${ghcCommandCaps}_LIBDIR" =
1074 if ghc.isHaLVM or false then "${ghcEnv}/lib/HaLVM-${ghc.version}" else "${ghcEnv}/${ghcLibdir}";
1075 }
1076 // optionalAttrs (stdenv.buildPlatform.libc == "glibc") {
1077 # TODO: Why is this written in terms of `buildPackages`, unlike
1078 # the outer `env`?
1079 #
1080 # According to @sternenseemann [1]:
1081 #
1082 # > The condition is based on `buildPlatform`, so it needs to
1083 # > match. `LOCALE_ARCHIVE` is set to accompany `LANG` which
1084 # > concerns things we execute on the build platform like
1085 # > `haddock`.
1086 # >
1087 # > Arguably the outer non `buildPackages` one is incorrect and
1088 # > probably works by accident in most cases since the locale
1089 # > archive is not platform specific (the trouble is that it
1090 # > may sometimes be impossible to cross-compile). At least
1091 # > that would be my assumption.
1092 #
1093 # [1]: https://github.com/NixOS/nixpkgs/pull/424368#discussion_r2202683378
1094 LOCALE_ARCHIVE = "${buildPackages.glibcLocales}/lib/locale/locale-archive";
1095 }
1096 // env';
1097 } "echo $nativeBuildInputs $buildInputs > $out";
1098
1099 env = envFunc { };
1100
1101 };
1102
1103 meta = {
1104 inherit homepage platforms;
1105 }
1106 // optionalAttrs (args ? broken) { inherit broken; }
1107 // optionalAttrs (args ? description) { inherit description; }
1108 // optionalAttrs (args ? license) { inherit license; }
1109 // optionalAttrs (args ? maintainers) { inherit maintainers; }
1110 // optionalAttrs (args ? teams) { inherit teams; }
1111 // optionalAttrs (args ? hydraPlatforms) { inherit hydraPlatforms; }
1112 // optionalAttrs (args ? badPlatforms) { inherit badPlatforms; }
1113 // optionalAttrs (args ? changelog) { inherit changelog; }
1114 // optionalAttrs (args ? mainProgram) { inherit mainProgram; };
1115
1116 }
1117 // optionalAttrs (args ? sourceRoot) { inherit sourceRoot; }
1118 // optionalAttrs (args ? setSourceRoot) { inherit setSourceRoot; }
1119 // optionalAttrs (args ? preCompileBuildDriver) { inherit preCompileBuildDriver; }
1120 // optionalAttrs (args ? postCompileBuildDriver) { inherit postCompileBuildDriver; }
1121 // optionalAttrs (args ? preUnpack) { inherit preUnpack; }
1122 // optionalAttrs (args ? postUnpack) { inherit postUnpack; }
1123 // optionalAttrs (args ? patches) { inherit patches; }
1124 // optionalAttrs (args ? patchPhase) { inherit patchPhase; }
1125 // optionalAttrs (args ? preConfigure) { inherit preConfigure; }
1126 // optionalAttrs (args ? postConfigure) { inherit postConfigure; }
1127 // optionalAttrs (args ? preBuild) { inherit preBuild; }
1128 // optionalAttrs (args ? postBuild) { inherit postBuild; }
1129 // optionalAttrs (args ? doBenchmark) { inherit doBenchmark; }
1130 // optionalAttrs (args ? checkPhase) { inherit checkPhase; }
1131 // optionalAttrs (args ? preCheck) { inherit preCheck; }
1132 // optionalAttrs (args ? postCheck) { inherit postCheck; }
1133 // optionalAttrs (args ? preHaddock) { inherit preHaddock; }
1134 // optionalAttrs (args ? postHaddock) { inherit postHaddock; }
1135 // optionalAttrs (args ? preInstall) { inherit preInstall; }
1136 // optionalAttrs (args ? installPhase) { inherit installPhase; }
1137 // optionalAttrs (args ? postInstall) { inherit postInstall; }
1138 // optionalAttrs (args ? preFixup) { inherit preFixup; }
1139 // optionalAttrs (args ? postFixup) { inherit postFixup; }
1140 // optionalAttrs (args ? dontStrip) { inherit dontStrip; }
1141 // optionalAttrs (postPhases != [ ]) { inherit postPhases; }
1142 // optionalAttrs (disallowedRequisites != [ ] || disallowGhcReference) {
1143 disallowedRequisites = disallowedRequisites ++ (if disallowGhcReference then [ ghc ] else [ ]);
1144 }
1145 )
1146)