1{ lib, stdenv, buildPackages, buildHaskellPackages, ghc
2, jailbreak-cabal, hscolour, cpphs
3, ghcWithHoogle, ghcWithPackages
4, nodejs
5}:
6
7let
8 isCross = stdenv.buildPlatform != stdenv.hostPlatform;
9
10 # Pass the "wrong" C compiler rather than none at all so packages that just
11 # use the C preproccessor still work, see
12 # https://github.com/haskell/cabal/issues/6466 for details.
13 cc =
14 if stdenv.hasCC then "$CC"
15 else if stdenv.hostPlatform.isGhcjs then "${emscripten}/bin/emcc"
16 else "$CC_FOR_BUILD";
17
18 inherit (buildPackages)
19 fetchurl removeReferencesTo
20 pkg-config coreutils gnugrep glibcLocales
21 emscripten;
22in
23
24{ pname
25# Note that ghc.isGhcjs != stdenv.hostPlatform.isGhcjs.
26# ghc.isGhcjs implies that we are using ghcjs, a project separate from GHC.
27# (mere) stdenv.hostPlatform.isGhcjs means that we are using GHC's JavaScript
28# backend. The latter is a normal cross compilation backend and needs little
29# special accommodation.
30, dontStrip ? (ghc.isGhcjs or false || stdenv.hostPlatform.isGhcjs)
31, version, revision ? null
32, sha256 ? null
33, src ? fetchurl { url = "mirror://hackage/${pname}-${version}.tar.gz"; inherit sha256; }
34, buildDepends ? [], setupHaskellDepends ? [], libraryHaskellDepends ? [], executableHaskellDepends ? []
35, buildTarget ? ""
36, buildTools ? [], libraryToolDepends ? [], executableToolDepends ? [], testToolDepends ? [], benchmarkToolDepends ? []
37, configureFlags ? []
38, buildFlags ? []
39, haddockFlags ? []
40, description ? null
41, doCheck ? !isCross
42, doBenchmark ? false
43, doHoogle ? true
44, doHaddockQuickjump ? doHoogle
45, doInstallIntermediates ? false
46, editedCabalFile ? null
47, enableLibraryProfiling ? !(ghc.isGhcjs or false)
48, enableExecutableProfiling ? false
49, profilingDetail ? "exported-functions"
50# TODO enable shared libs for cross-compiling
51, enableSharedExecutables ? false
52, enableSharedLibraries ? !stdenv.hostPlatform.isStatic && (ghc.enableShared or false)
53, enableDeadCodeElimination ? (!stdenv.isDarwin) # TODO: use -dead_strip for darwin
54# Disabling this for ghcjs prevents this crash: https://gitlab.haskell.org/ghc/ghc/-/issues/23235
55, enableStaticLibraries ? !(stdenv.hostPlatform.isWindows || stdenv.hostPlatform.isWasm || stdenv.hostPlatform.isGhcjs)
56, enableHsc2hsViaAsm ? stdenv.hostPlatform.isWindows
57, extraLibraries ? [], librarySystemDepends ? [], executableSystemDepends ? []
58# On macOS, statically linking against system frameworks is not supported;
59# see https://developer.apple.com/library/content/qa/qa1118/_index.html
60# They must be propagated to the environment of any executable linking with the library
61, libraryFrameworkDepends ? [], executableFrameworkDepends ? []
62, homepage ? "https://hackage.haskell.org/package/${pname}"
63, platforms ? with lib.platforms; all # GHC can cross-compile
64, badPlatforms ? lib.platforms.none
65, hydraPlatforms ? null
66, hyperlinkSource ? true
67, isExecutable ? false, isLibrary ? !isExecutable
68, jailbreak ? false
69, license
70, enableParallelBuilding ? true
71, maintainers ? null
72, changelog ? null
73, mainProgram ? null
74, doCoverage ? false
75, doHaddock ? !(ghc.isHaLVM or false) && (ghc.hasHaddock or true)
76, doHaddockInterfaces ? doHaddock && lib.versionAtLeast ghc.version "9.0.1"
77, passthru ? {}
78, pkg-configDepends ? [], libraryPkgconfigDepends ? [], executablePkgconfigDepends ? [], testPkgconfigDepends ? [], benchmarkPkgconfigDepends ? []
79, testDepends ? [], testHaskellDepends ? [], testSystemDepends ? [], testFrameworkDepends ? []
80, benchmarkDepends ? [], benchmarkHaskellDepends ? [], benchmarkSystemDepends ? [], benchmarkFrameworkDepends ? []
81, testTarget ? "", testFlags ? []
82, broken ? false
83, preCompileBuildDriver ? null, postCompileBuildDriver ? null
84, preUnpack ? null, postUnpack ? null
85, patches ? null, patchPhase ? null, prePatch ? "", postPatch ? ""
86, preConfigure ? null, postConfigure ? null
87, preBuild ? null, postBuild ? null
88, preHaddock ? null, postHaddock ? null
89, installPhase ? null, preInstall ? null, postInstall ? null
90, checkPhase ? null, preCheck ? null, postCheck ? null
91, preFixup ? null, postFixup ? null
92, shellHook ? ""
93, coreSetup ? false # Use only core packages to build Setup.hs.
94, useCpphs ? false
95, hardeningDisable ? null
96, enableSeparateBinOutput ? false
97, enableSeparateDataOutput ? false
98, enableSeparateDocOutput ? doHaddock
99, enableSeparateIntermediatesOutput ? false
100, # Don't fail at configure time if there are multiple versions of the
101 # same package in the (recursive) dependencies of the package being
102 # built. Will delay failures, if any, to compile time.
103 allowInconsistentDependencies ? false
104, maxBuildCores ? 16 # more cores usually don't improve performance: https://ghc.haskell.org/trac/ghc/ticket/9221
105, # If set to true, this builds a pre-linked .o file for this Haskell library.
106 # This can make it slightly faster to load this library into GHCi, but takes
107 # extra disk space and compile time.
108 enableLibraryForGhci ? false
109 # Set this to a previous build of this same package to reuse the intermediate
110 # build products from that prior build as a starting point for accelerating
111 # this build
112, previousIntermediates ? null
113, # Cabal 3.8 which is shipped by default for GHC >= 9.3 always calls
114 # `pkg-config --libs --static` as part of the configure step. This requires
115 # Requires.private dependencies of pkg-config dependencies to be present in
116 # PKG_CONFIG_PATH which is normally not the case in nixpkgs (except in pkgsStatic).
117 # Since there is no patch or upstream patch yet, we replicate the automatic
118 # propagation of dependencies in pkgsStatic for allPkgConfigDepends for
119 # GHC >= 9.3 by default. This option allows overriding this behavior manually
120 # if mismatching Cabal and GHC versions are used.
121 # See also <https://github.com/haskell/cabal/issues/8455>.
122 __propagatePkgConfigDepends ? lib.versionAtLeast ghc.version "9.3"
123, # Propagation can easily lead to the argv limit being exceeded in linker or C
124 # compiler invocations. To work around this we can only propagate derivations
125 # that are known to provide pkg-config modules, as indicated by the presence
126 # of `meta.pkgConfigModules`. This option defaults to false for now, since
127 # this metadata is far from complete in nixpkgs.
128 __onlyPropagateKnownPkgConfigModules ? false
129} @ args:
130
131assert editedCabalFile != null -> revision != null;
132
133# --enable-static does not work on windows. This is a bug in GHC.
134# --enable-static will pass -staticlib to ghc, which only works for mach-o and elf.
135assert stdenv.hostPlatform.isWindows -> enableStaticLibraries == false;
136assert stdenv.hostPlatform.isWasm -> enableStaticLibraries == false;
137
138let
139
140 inherit (lib) optional optionals optionalString versionAtLeast
141 concatStringsSep enableFeature optionalAttrs;
142
143 isGhcjs = ghc.isGhcjs or false;
144 isHaLVM = ghc.isHaLVM or false;
145
146 # GHC used for building Setup.hs
147 #
148 # Same as our GHC, unless we're cross, in which case it is native GHC with the
149 # same version, or ghcjs, in which case its the ghc used to build ghcjs.
150 nativeGhc = buildHaskellPackages.ghc;
151
152 # the target dir for haddock documentation
153 docdir = docoutput: docoutput + "/share/doc/" + pname + "-" + version;
154
155 binDir = if enableSeparateBinOutput then "$bin/bin" else "$out/bin";
156
157 newCabalFileUrl = "mirror://hackage/${pname}-${version}/revision/${revision}.cabal";
158 newCabalFile = fetchurl {
159 url = newCabalFileUrl;
160 sha256 = editedCabalFile;
161 name = "${pname}-${version}-r${revision}.cabal";
162 };
163
164 defaultSetupHs = builtins.toFile "Setup.hs" ''
165 import Distribution.Simple
166 main = defaultMain
167 '';
168
169 # This awk expression transforms a package conf file like
170 #
171 # author: John Doe <john-doe@example.com>
172 # description:
173 # The purpose of this library is to do
174 # foo and bar among other things
175 #
176 # into a more easily processeable form:
177 #
178 # author: John Doe <john-doe@example.com>
179 # description: The purpose of this library is to do foo and bar among other things
180 unprettyConf = builtins.toFile "unpretty-cabal-conf.awk" ''
181 /^[^ ]+:/ {
182 # When the line starts with a new field, terminate the previous one with a newline
183 if (started == 1) print ""
184 # to strip leading spaces
185 $1=$1
186 printf "%s", $0
187 started=1
188 }
189
190 /^ +/ {
191 # to strip leading spaces
192 $1=$1
193 printf " %s", $0
194 }
195
196 # Terminate the final field with a newline
197 END { print "" }
198 '';
199
200 crossCabalFlags = [
201 "--with-ghc=${ghcCommand}"
202 "--with-ghc-pkg=${ghc.targetPrefix}ghc-pkg"
203 "--with-gcc=${cc}"
204 ] ++ optionals stdenv.hasCC [
205 "--with-ld=${stdenv.cc.bintools.targetPrefix}ld"
206 "--with-ar=${stdenv.cc.bintools.targetPrefix}ar"
207 # use the one that comes with the cross compiler.
208 "--with-hsc2hs=${ghc.targetPrefix}hsc2hs"
209 "--with-strip=${stdenv.cc.bintools.targetPrefix}strip"
210 ] ++ optionals (!isHaLVM) [
211 "--hsc2hs-option=--cross-compile"
212 (optionalString enableHsc2hsViaAsm "--hsc2hs-option=--via-asm")
213 ] ++ optional (allPkgconfigDepends != [])
214 "--with-pkg-config=${pkg-config.targetPrefix}pkg-config";
215
216 parallelBuildingFlags = "-j$NIX_BUILD_CORES" + optionalString stdenv.isLinux " +RTS -A64M -RTS";
217
218 crossCabalFlagsString =
219 lib.optionalString isCross (" " + lib.concatStringsSep " " crossCabalFlags);
220
221 buildFlagsString = optionalString (buildFlags != []) (" " + concatStringsSep " " buildFlags);
222
223 defaultConfigureFlags = [
224 "--verbose"
225 "--prefix=$out"
226 # Note: This must be kept in sync manually with mkGhcLibdir
227 ("--libdir=\\$prefix/lib/\\$compiler" + lib.optionalString (ghc ? hadrian) "/lib")
228 "--libsubdir=\\$abi/\\$libname"
229 (optionalString enableSeparateDataOutput "--datadir=$data/share/${ghcNameWithPrefix}")
230 (optionalString enableSeparateDocOutput "--docdir=${docdir "$doc"}")
231 ] ++ optionals stdenv.hasCC [
232 "--with-gcc=$CC" # Clang won't work without that extra information.
233 ] ++ [
234 "--package-db=$packageConfDir"
235 (optionalString (enableSharedExecutables && stdenv.isLinux) "--ghc-option=-optl=-Wl,-rpath=$out/${ghcLibdir}/${pname}-${version}")
236 (optionalString (enableSharedExecutables && stdenv.isDarwin) "--ghc-option=-optl=-Wl,-headerpad_max_install_names")
237 (optionalString enableParallelBuilding "--ghc-options=${parallelBuildingFlags}")
238 (optionalString useCpphs "--with-cpphs=${cpphs}/bin/cpphs --ghc-options=-cpp --ghc-options=-pgmP${cpphs}/bin/cpphs --ghc-options=-optP--cpp")
239 (enableFeature enableLibraryProfiling "library-profiling")
240 (optionalString (enableExecutableProfiling || enableLibraryProfiling) "--profiling-detail=${profilingDetail}")
241 (enableFeature enableExecutableProfiling "profiling")
242 (enableFeature enableSharedLibraries "shared")
243 (enableFeature doCoverage "coverage")
244 (enableFeature enableStaticLibraries "static")
245 (enableFeature enableSharedExecutables "executable-dynamic")
246 (enableFeature doCheck "tests")
247 (enableFeature doBenchmark "benchmarks")
248 "--enable-library-vanilla" # TODO: Should this be configurable?
249 (enableFeature enableLibraryForGhci "library-for-ghci")
250 (enableFeature enableDeadCodeElimination "split-sections")
251 (enableFeature (!dontStrip) "library-stripping")
252 (enableFeature (!dontStrip) "executable-stripping")
253 ] ++ optionals isGhcjs [
254 "--ghcjs"
255 ] ++ optionals isCross ([
256 "--configure-option=--host=${stdenv.hostPlatform.config}"
257 ] ++ crossCabalFlags
258 ) ++ optionals enableSeparateBinOutput [
259 "--bindir=${binDir}"
260 ] ++ optionals (doHaddockInterfaces && isLibrary) [
261 "--ghc-options=-haddock"
262 ];
263
264 postPhases = optional doInstallIntermediates "installIntermediatesPhase";
265
266 setupCompileFlags = [
267 (optionalString (!coreSetup) "-package-db=$setupPackageConfDir")
268 (optionalString enableParallelBuilding parallelBuildingFlags)
269 "-threaded" # https://github.com/haskell/cabal/issues/2398
270 "-rtsopts" # allow us to pass RTS flags to the generated Setup executable
271 ];
272
273 isHaskellPkg = x: x ? isHaskellLibrary;
274
275 # Work around a Cabal bug requiring pkg-config --static --libs to work even
276 # when linking dynamically, affecting Cabal 3.8 and 3.9.
277 # https://github.com/haskell/cabal/issues/8455
278 #
279 # For this, we treat the runtime system/pkg-config dependencies of a Haskell
280 # derivation as if they were propagated from their dependencies which allows
281 # pkg-config --static to work in most cases.
282 allPkgconfigDepends =
283 let
284 # If __onlyPropagateKnownPkgConfigModules is set, packages without
285 # meta.pkgConfigModules will be filtered out, otherwise all packages in
286 # buildInputs and propagatePlainBuildInputs are propagated.
287 propagateValue = drv:
288 lib.isDerivation drv
289 && (__onlyPropagateKnownPkgConfigModules -> drv ? meta.pkgConfigModules);
290
291 # Take list of derivations and return list of the transitive dependency
292 # closure, only taking into account buildInputs. Loosely based on
293 # closePropagationFast.
294 propagatePlainBuildInputs = drvs:
295 builtins.map (i: i.val) (
296 builtins.genericClosure {
297 startSet = builtins.map (drv:
298 { key = drv.outPath; val = drv; }
299 ) (builtins.filter propagateValue drvs);
300 operator = { val, ... }:
301 builtins.concatMap (drv:
302 if propagateValue drv
303 then [ { key = drv.outPath; val = drv; } ]
304 else [ ]
305 ) (val.buildInputs or [ ] ++ val.propagatedBuildInputs or [ ]);
306 }
307 );
308 in
309
310 if __propagatePkgConfigDepends
311 then propagatePlainBuildInputs allPkgconfigDepends'
312 else allPkgconfigDepends';
313 allPkgconfigDepends' =
314 pkg-configDepends ++ libraryPkgconfigDepends ++ executablePkgconfigDepends ++
315 optionals doCheck testPkgconfigDepends ++ optionals doBenchmark benchmarkPkgconfigDepends;
316
317 depsBuildBuild = [ nativeGhc ]
318 # CC_FOR_BUILD may be necessary if we have no C preprocessor for the host
319 # platform. See crossCabalFlags above for more details.
320 ++ lib.optionals (!stdenv.hasCC) [ buildPackages.stdenv.cc ];
321 collectedToolDepends =
322 buildTools ++ libraryToolDepends ++ executableToolDepends ++
323 optionals doCheck testToolDepends ++
324 optionals doBenchmark benchmarkToolDepends;
325 nativeBuildInputs =
326 [ ghc removeReferencesTo ] ++ optional (allPkgconfigDepends != []) (assert pkg-config != null; pkg-config) ++
327 setupHaskellDepends ++ collectedToolDepends ++ optional stdenv.hostPlatform.isGhcjs nodejs;
328 propagatedBuildInputs = buildDepends ++ libraryHaskellDepends ++ executableHaskellDepends ++ libraryFrameworkDepends;
329 otherBuildInputsHaskell =
330 optionals doCheck (testDepends ++ testHaskellDepends) ++
331 optionals doBenchmark (benchmarkDepends ++ benchmarkHaskellDepends);
332 otherBuildInputsSystem =
333 extraLibraries ++ librarySystemDepends ++ executableSystemDepends ++ executableFrameworkDepends ++
334 allPkgconfigDepends ++
335 optionals doCheck (testSystemDepends ++ testFrameworkDepends) ++
336 optionals doBenchmark (benchmarkSystemDepends ++ benchmarkFrameworkDepends);
337 # TODO next rebuild just define as `otherBuildInputsHaskell ++ otherBuildInputsSystem`
338 otherBuildInputs =
339 extraLibraries ++ librarySystemDepends ++ executableSystemDepends ++ executableFrameworkDepends ++
340 allPkgconfigDepends ++
341 optionals doCheck (testDepends ++ testHaskellDepends ++ testSystemDepends ++ testFrameworkDepends) ++
342 optionals doBenchmark (benchmarkDepends ++ benchmarkHaskellDepends ++ benchmarkSystemDepends ++ benchmarkFrameworkDepends);
343
344 setupCommand = "./Setup";
345
346 ghcCommand' = if isGhcjs then "ghcjs" else "ghc";
347 ghcCommand = "${ghc.targetPrefix}${ghcCommand'}";
348
349 ghcNameWithPrefix = "${ghc.targetPrefix}${ghc.haskellCompilerName}";
350 mkGhcLibdir = ghc: "lib/${ghc.targetPrefix}${ghc.haskellCompilerName}"
351 + lib.optionalString (ghc ? hadrian) "/lib";
352 ghcLibdir = mkGhcLibdir ghc;
353
354 nativeGhcCommand = "${nativeGhc.targetPrefix}ghc";
355
356 buildPkgDb = thisGhc: packageConfDir: ''
357 # If this dependency has a package database, then copy the contents of it,
358 # unless it is one of our GHCs. These can appear in our dependencies when
359 # we are doing native builds, and they have package databases in them, but
360 # we do not want to copy them over.
361 #
362 # We don't need to, since those packages will be provided by the GHC when
363 # we compile with it, and doing so can result in having multiple copies of
364 # e.g. Cabal in the database with the same name and version, which is
365 # ambiguous.
366 if [ -d "$p/${mkGhcLibdir thisGhc}/package.conf.d" ] && [ "$p" != "${ghc}" ] && [ "$p" != "${nativeGhc}" ]; then
367 cp -f "$p/${mkGhcLibdir thisGhc}/package.conf.d/"*.conf ${packageConfDir}/
368 continue
369 fi
370 '';
371
372 intermediatesDir = "share/haskell/${ghc.version}/${pname}-${version}/dist";
373in lib.fix (drv:
374
375stdenv.mkDerivation ({
376 inherit pname version;
377
378 outputs = [ "out" ]
379 ++ (optional enableSeparateDataOutput "data")
380 ++ (optional enableSeparateDocOutput "doc")
381 ++ (optional enableSeparateBinOutput "bin")
382 ++ (optional enableSeparateIntermediatesOutput "intermediates");
383
384 setOutputFlags = false;
385
386 pos = builtins.unsafeGetAttrPos "pname" args;
387
388 prePhases = ["setupCompilerEnvironmentPhase"];
389 preConfigurePhases = ["compileBuildDriverPhase"];
390 preInstallPhases = ["haddockPhase"];
391
392 inherit src;
393
394 inherit depsBuildBuild nativeBuildInputs;
395 buildInputs = otherBuildInputs ++ optionals (!isLibrary) propagatedBuildInputs
396 # For patchShebangsAuto in fixupPhase
397 ++ optionals stdenv.hostPlatform.isGhcjs [ nodejs ];
398 propagatedBuildInputs = optionals isLibrary propagatedBuildInputs;
399
400 LANG = "en_US.UTF-8"; # GHC needs the locale configured during the Haddock phase.
401
402 prePatch = optionalString (editedCabalFile != null) ''
403 echo "Replace Cabal file with edited version from ${newCabalFileUrl}."
404 cp ${newCabalFile} ${pname}.cabal
405 '' + prePatch;
406
407 postPatch = optionalString jailbreak ''
408 echo "Run jailbreak-cabal to lift version restrictions on build inputs."
409 ${jailbreak-cabal}/bin/jailbreak-cabal ${pname}.cabal
410 '' + postPatch;
411
412 setupCompilerEnvironmentPhase = ''
413 NIX_BUILD_CORES=$(( NIX_BUILD_CORES < ${toString maxBuildCores} ? NIX_BUILD_CORES : ${toString maxBuildCores} ))
414 runHook preSetupCompilerEnvironment
415
416 echo "Build with ${ghc}."
417 ${optionalString (isLibrary && hyperlinkSource) "export PATH=${hscolour}/bin:$PATH"}
418
419 builddir="$(mktemp -d)"
420 setupPackageConfDir="$builddir/setup-package.conf.d"
421 mkdir -p $setupPackageConfDir
422 packageConfDir="$builddir/package.conf.d"
423 mkdir -p $packageConfDir
424
425 setupCompileFlags="${concatStringsSep " " setupCompileFlags}"
426 configureFlags="${concatStringsSep " " defaultConfigureFlags} $configureFlags"
427 ''
428 # We build the Setup.hs on the *build* machine, and as such should only add
429 # dependencies for the build machine.
430 #
431 # pkgs* arrays defined in stdenv/setup.hs
432 + ''
433 for p in "''${pkgsBuildBuild[@]}" "''${pkgsBuildHost[@]}" "''${pkgsBuildTarget[@]}"; do
434 ${buildPkgDb nativeGhc "$setupPackageConfDir"}
435 done
436 ${nativeGhcCommand}-pkg --package-db="$setupPackageConfDir" recache
437 ''
438 # For normal components
439 + ''
440 for p in "''${pkgsHostHost[@]}" "''${pkgsHostTarget[@]}"; do
441 ${buildPkgDb ghc "$packageConfDir"}
442 if [ -d "$p/include" ]; then
443 configureFlags+=" --extra-include-dirs=$p/include"
444 fi
445 if [ -d "$p/lib" ]; then
446 configureFlags+=" --extra-lib-dirs=$p/lib"
447 fi
448 if [[ -d "$p/Library/Frameworks" ]]; then
449 configureFlags+=" --extra-framework-dirs=$p/Library/Frameworks"
450 fi
451 '' + ''
452 done
453 ''
454 + (optionalString stdenv.hostPlatform.isGhcjs ''
455 export EM_CACHE="$(realpath "$(mktemp -d emcache.XXXXXXXXXX)")"
456 cp -Lr ${emscripten}/share/emscripten/cache/* "$EM_CACHE/"
457 chmod u+rwX -R "$EM_CACHE"
458 '')
459 # only use the links hack if we're actually building dylibs. otherwise, the
460 # "dynamic-library-dirs" point to nonexistent paths, and the ln command becomes
461 # "ln -s $out/lib/links", which tries to recreate the links dir and fails
462 #
463 # Note: We need to disable this work-around when using intermediate build
464 # products from a prior build because otherwise Nix will change permissions on
465 # the `$out/lib/links` directory to read-only when the build is done after the
466 # dist directory has already been exported, which triggers an unnecessary
467 # rebuild of modules included in the exported dist directory.
468 + (optionalString (stdenv.isDarwin && (enableSharedLibraries || enableSharedExecutables) && !enableSeparateIntermediatesOutput) ''
469 # Work around a limit in the macOS Sierra linker on the number of paths
470 # referenced by any one dynamic library:
471 #
472 # Create a local directory with symlinks of the *.dylib (macOS shared
473 # libraries) from all the dependencies.
474 local dynamicLinksDir="$out/lib/links"
475 mkdir -p $dynamicLinksDir
476
477 # Unprettify all package conf files before reading/writing them
478 for d in "$packageConfDir/"*; do
479 # gawk -i inplace seems to strip the last newline
480 gawk -f ${unprettyConf} "$d" > tmp
481 mv tmp "$d"
482 done
483
484 for d in $(grep '^dynamic-library-dirs:' "$packageConfDir"/* | cut -d' ' -f2- | tr ' ' '\n' | sort -u); do
485 for lib in "$d/"*.{dylib,so}; do
486 # Allow overwriting because C libs can be pulled in multiple times.
487 ln -sf "$lib" "$dynamicLinksDir"
488 done
489 done
490 # Edit the local package DB to reference the links directory.
491 for f in "$packageConfDir/"*.conf; do
492 sed -i "s,dynamic-library-dirs: .*,dynamic-library-dirs: $dynamicLinksDir," "$f"
493 done
494 '') + ''
495 ${ghcCommand}-pkg --package-db="$packageConfDir" recache
496
497 runHook postSetupCompilerEnvironment
498 '';
499
500 compileBuildDriverPhase = ''
501 runHook preCompileBuildDriver
502
503 for i in Setup.hs Setup.lhs ${defaultSetupHs}; do
504 test -f $i && break
505 done
506
507 echo setupCompileFlags: $setupCompileFlags
508 ${nativeGhcCommand} $setupCompileFlags --make -o Setup -odir $builddir -hidir $builddir $i
509
510 runHook postCompileBuildDriver
511 '';
512
513 # Cabal takes flags like `--configure-option=--host=...` instead
514 configurePlatforms = [];
515 inherit configureFlags;
516
517 # Note: the options here must be always added, regardless of whether the
518 # package specifies `hardeningDisable`.
519 hardeningDisable = lib.optionals (args ? hardeningDisable) hardeningDisable
520 ++ lib.optional (ghc.isHaLVM or false) "all"
521 # Static libraries (ie. all of pkgsStatic.haskellPackages) fail to build
522 # because by default Nix adds `-pie` to the linker flags: this
523 # conflicts with the `-r` and `-no-pie` flags added by GHC (see
524 # https://gitlab.haskell.org/ghc/ghc/-/issues/19580). hardeningDisable
525 # changes the default Nix behavior regarding adding "hardening" flags.
526 ++ lib.optional enableStaticLibraries "pie";
527
528 configurePhase = ''
529 runHook preConfigure
530
531 unset GHC_PACKAGE_PATH # Cabal complains if this variable is set during configure.
532
533 echo configureFlags: $configureFlags
534 ${setupCommand} configure $configureFlags 2>&1 | ${coreutils}/bin/tee "$NIX_BUILD_TOP/cabal-configure.log"
535 ${lib.optionalString (!allowInconsistentDependencies) ''
536 if ${gnugrep}/bin/egrep -q -z 'Warning:.*depends on multiple versions' "$NIX_BUILD_TOP/cabal-configure.log"; then
537 echo >&2 "*** abort because of serious configure-time warning from Cabal"
538 exit 1
539 fi
540 ''}
541 export GHC_PACKAGE_PATH="$packageConfDir:"
542
543 runHook postConfigure
544 '';
545
546 buildPhase =
547 ''
548 runHook preBuild
549 ''
550 + lib.optionalString (previousIntermediates != null)
551 ''
552 mkdir -p dist;
553 rm -r dist/build
554 cp -r ${previousIntermediates}/${intermediatesDir}/build dist/build
555 find dist/build -exec chmod u+w {} +
556 find dist/build -exec touch -d '1970-01-01T00:00:00Z' {} +
557 ''
558 + ''
559 ${setupCommand} build ${buildTarget}${crossCabalFlagsString}${buildFlagsString}
560 runHook postBuild
561 '';
562
563 inherit doCheck;
564
565 # Run test suite(s) and pass `checkFlags` as well as `checkFlagsArray`.
566 # `testFlags` are added to `checkFlagsArray` each prefixed with
567 # `--test-option`, so Cabal passes it to the underlying test suite binary.
568 checkPhase = ''
569 runHook preCheck
570 checkFlagsArray+=(
571 "--show-details=streaming"
572 ${lib.escapeShellArgs (builtins.map (opt: "--test-option=${opt}") testFlags)}
573 )
574 ${setupCommand} test ${testTarget} $checkFlags ''${checkFlagsArray:+"''${checkFlagsArray[@]}"}
575 runHook postCheck
576 '';
577
578 haddockPhase = ''
579 runHook preHaddock
580 ${optionalString (doHaddock && isLibrary) ''
581 ${setupCommand} haddock --html \
582 ${optionalString doHoogle "--hoogle"} \
583 ${optionalString doHaddockQuickjump "--quickjump"} \
584 ${optionalString (isLibrary && hyperlinkSource) "--hyperlink-source"} \
585 ${lib.concatStringsSep " " haddockFlags}
586 ''}
587 runHook postHaddock
588 '';
589
590 installPhase = ''
591 runHook preInstall
592
593 ${if !isLibrary && buildTarget == "" then "${setupCommand} install"
594 # ^^ if the project is not a library, and no build target is specified, we can just use "install".
595 else if !isLibrary then "${setupCommand} copy ${buildTarget}"
596 # ^^ if the project is not a library, and we have a build target, then use "copy" to install
597 # just the target specified; "install" will error here, since not all targets have been built.
598 else ''
599 ${setupCommand} copy ${buildTarget}
600 local packageConfDir="$out/${ghcLibdir}/package.conf.d"
601 local packageConfFile="$packageConfDir/${pname}-${version}.conf"
602 mkdir -p "$packageConfDir"
603 ${setupCommand} register --gen-pkg-config=$packageConfFile
604 if [ -d "$packageConfFile" ]; then
605 mv "$packageConfFile/"* "$packageConfDir"
606 rmdir "$packageConfFile"
607 fi
608 for packageConfFile in "$packageConfDir/"*; do
609 local pkgId=$(gawk -f ${unprettyConf} "$packageConfFile" \
610 | grep '^id:' | cut -d' ' -f2)
611 mv "$packageConfFile" "$packageConfDir/$pkgId.conf"
612 done
613
614 # delete confdir if there are no libraries
615 find $packageConfDir -maxdepth 0 -empty -delete;
616 ''}
617 ${optionalString isGhcjs ''
618 for exeDir in "${binDir}/"*.jsexe; do
619 exe="''${exeDir%.jsexe}"
620 printWords '#!${nodejs}/bin/node' > "$exe"
621 echo >> "$exe"
622 cat "$exeDir/all.js" >> "$exe"
623 chmod +x "$exe"
624 done
625 ''}
626 ${optionalString doCoverage "mkdir -p $out/share && cp -r dist/hpc $out/share"}
627
628 ${optionalString enableSeparateDocOutput ''
629 for x in ${docdir "$doc"}"/html/src/"*.html; do
630 remove-references-to -t $out $x
631 done
632 mkdir -p $doc
633 ''}
634 ${optionalString enableSeparateDataOutput "mkdir -p $data"}
635
636 runHook postInstall
637 '';
638
639 ${if doInstallIntermediates then "installIntermediatesPhase" else null} = ''
640 runHook preInstallIntermediates
641 intermediatesOutput=${if enableSeparateIntermediatesOutput then "$intermediates" else "$out"}
642 installIntermediatesDir="$intermediatesOutput/${intermediatesDir}"
643 mkdir -p "$installIntermediatesDir"
644 cp -r dist/build "$installIntermediatesDir"
645 runHook postInstallIntermediates
646 '';
647
648 passthru = passthru // rec {
649
650 inherit pname version;
651
652 compiler = ghc;
653
654 # All this information is intended just for `shellFor`. It should be
655 # considered unstable and indeed we knew how to keep it private we would.
656 getCabalDeps = {
657 inherit
658 buildDepends
659 buildTools
660 executableFrameworkDepends
661 executableHaskellDepends
662 executablePkgconfigDepends
663 executableSystemDepends
664 executableToolDepends
665 extraLibraries
666 libraryFrameworkDepends
667 libraryHaskellDepends
668 libraryPkgconfigDepends
669 librarySystemDepends
670 libraryToolDepends
671 pkg-configDepends
672 setupHaskellDepends
673 ;
674 } // lib.optionalAttrs doCheck {
675 inherit
676 testDepends
677 testFrameworkDepends
678 testHaskellDepends
679 testPkgconfigDepends
680 testSystemDepends
681 testToolDepends
682 ;
683 } // lib.optionalAttrs doBenchmark {
684 inherit
685 benchmarkDepends
686 benchmarkFrameworkDepends
687 benchmarkHaskellDepends
688 benchmarkPkgconfigDepends
689 benchmarkSystemDepends
690 benchmarkToolDepends
691 ;
692 };
693
694 # Attributes for the old definition of `shellFor`. Should be removed but
695 # this predates the warning at the top of `getCabalDeps`.
696 getBuildInputs = rec {
697 inherit propagatedBuildInputs otherBuildInputs allPkgconfigDepends;
698 haskellBuildInputs = isHaskellPartition.right;
699 systemBuildInputs = isHaskellPartition.wrong;
700 isHaskellPartition = lib.partition
701 isHaskellPkg
702 (propagatedBuildInputs ++ otherBuildInputs ++ depsBuildBuild ++ nativeBuildInputs);
703 };
704
705 isHaskellLibrary = isLibrary;
706
707 # TODO: ask why the split outputs are configurable at all?
708 # TODO: include tests for split if possible
709 # Given the haskell package, returns
710 # the directory containing the haddock documentation.
711 # `null' if no haddock documentation was built.
712 # TODO: fetch the self from the fixpoint instead
713 haddockDir = self: if doHaddock then "${docdir self.doc}/html" else null;
714
715 # Creates a derivation containing all of the necessary dependencies for building the
716 # parent derivation. The attribute set that it takes as input can be viewed as:
717 #
718 # { withHoogle }
719 #
720 # The derivation that it builds contains no outpaths because it is meant for use
721 # as an environment
722 #
723 # # Example use
724 # # Creates a shell with all of the dependencies required to build the "hello" package,
725 # # and with python:
726 #
727 # > nix-shell -E 'with (import <nixpkgs> {}); \
728 # > haskell.packages.ghc865.hello.envFunc { buildInputs = [ python ]; }'
729 envFunc = { withHoogle ? false }:
730 let
731 name = "ghc-shell-for-${drv.name}";
732
733 withPackages = if withHoogle then ghcWithHoogle else ghcWithPackages;
734
735 # We use the `ghcWithPackages` function from `buildHaskellPackages` if we
736 # want a shell for the sake of cross compiling a package. In the native case
737 # we don't use this at all, and instead put the setupDepends in the main
738 # `ghcWithPackages`. This way we don't have two wrapper scripts called `ghc`
739 # shadowing each other on the PATH.
740 ghcEnvForBuild =
741 assert isCross;
742 buildHaskellPackages.ghcWithPackages (_: setupHaskellDepends);
743
744 ghcEnv = withPackages (_:
745 otherBuildInputsHaskell ++
746 propagatedBuildInputs ++
747 lib.optionals (!isCross) setupHaskellDepends);
748
749 ghcCommandCaps = lib.toUpper ghcCommand';
750 in stdenv.mkDerivation {
751 inherit name shellHook;
752
753 depsBuildBuild = lib.optional isCross ghcEnvForBuild;
754 nativeBuildInputs =
755 [ ghcEnv ] ++ optional (allPkgconfigDepends != []) pkg-config ++
756 collectedToolDepends;
757 buildInputs =
758 otherBuildInputsSystem;
759 phases = ["installPhase"];
760 installPhase = "echo $nativeBuildInputs $buildInputs > $out";
761 LANG = "en_US.UTF-8";
762 LOCALE_ARCHIVE = lib.optionalString (stdenv.hostPlatform.libc == "glibc") "${buildPackages.glibcLocales}/lib/locale/locale-archive";
763 "NIX_${ghcCommandCaps}" = "${ghcEnv}/bin/${ghcCommand}";
764 "NIX_${ghcCommandCaps}PKG" = "${ghcEnv}/bin/${ghcCommand}-pkg";
765 # TODO: is this still valid?
766 "NIX_${ghcCommandCaps}_DOCDIR" = "${ghcEnv}/share/doc/ghc/html";
767 "NIX_${ghcCommandCaps}_LIBDIR" = if ghc.isHaLVM or false
768 then "${ghcEnv}/lib/HaLVM-${ghc.version}"
769 else "${ghcEnv}/${ghcLibdir}";
770 };
771
772 env = envFunc { };
773
774 };
775
776 meta = { inherit homepage license platforms; }
777 // optionalAttrs (args ? broken) { inherit broken; }
778 // optionalAttrs (args ? description) { inherit description; }
779 // optionalAttrs (args ? maintainers) { inherit maintainers; }
780 // optionalAttrs (args ? hydraPlatforms) { inherit hydraPlatforms; }
781 // optionalAttrs (args ? badPlatforms) { inherit badPlatforms; }
782 // optionalAttrs (args ? changelog) { inherit changelog; }
783 // optionalAttrs (args ? mainProgram) { inherit mainProgram; }
784 ;
785
786}
787// optionalAttrs (args ? preCompileBuildDriver) { inherit preCompileBuildDriver; }
788// optionalAttrs (args ? postCompileBuildDriver) { inherit postCompileBuildDriver; }
789// optionalAttrs (args ? preUnpack) { inherit preUnpack; }
790// optionalAttrs (args ? postUnpack) { inherit postUnpack; }
791// optionalAttrs (args ? patches) { inherit patches; }
792// optionalAttrs (args ? patchPhase) { inherit patchPhase; }
793// optionalAttrs (args ? preConfigure) { inherit preConfigure; }
794// optionalAttrs (args ? postConfigure) { inherit postConfigure; }
795// optionalAttrs (args ? preBuild) { inherit preBuild; }
796// optionalAttrs (args ? postBuild) { inherit postBuild; }
797// optionalAttrs (args ? doBenchmark) { inherit doBenchmark; }
798// optionalAttrs (args ? checkPhase) { inherit checkPhase; }
799// optionalAttrs (args ? preCheck) { inherit preCheck; }
800// optionalAttrs (args ? postCheck) { inherit postCheck; }
801// optionalAttrs (args ? preHaddock) { inherit preHaddock; }
802// optionalAttrs (args ? postHaddock) { inherit postHaddock; }
803// optionalAttrs (args ? preInstall) { inherit preInstall; }
804// optionalAttrs (args ? installPhase) { inherit installPhase; }
805// optionalAttrs (args ? postInstall) { inherit postInstall; }
806// optionalAttrs (args ? preFixup) { inherit preFixup; }
807// optionalAttrs (args ? postFixup) { inherit postFixup; }
808// optionalAttrs (args ? dontStrip) { inherit dontStrip; }
809// optionalAttrs (postPhases != []) { inherit postPhases; }
810// optionalAttrs (stdenv.buildPlatform.libc == "glibc"){ LOCALE_ARCHIVE = "${glibcLocales}/lib/locale/locale-archive"; }
811
812# Implicit pointer to integer conversions are errors by default since clang 15.
813# Works around https://gitlab.haskell.org/ghc/ghc/-/issues/23456.
814// lib.optionalAttrs (stdenv.hasCC && stdenv.cc.isClang) {
815 NIX_CFLAGS_COMPILE = "-Wno-error=int-conversion";
816}
817)
818)