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