1# TODO(@Ericson2314): Remove `pkgs` param, which is only used for
2# `buildStackProject`, `justStaticExecutables` and `checkUnusedPackages`
3{ pkgs, lib }:
4
5rec {
6
7 /*
8 This function takes a file like `hackage-packages.nix` and constructs
9 a full package set out of that.
10 */
11 makePackageSet = import ../make-package-set.nix;
12
13 /*
14 The function overrideCabal lets you alter the arguments to the
15 mkDerivation function.
16
17 Example:
18
19 First, note how the aeson package is constructed in hackage-packages.nix:
20
21 "aeson" = callPackage ({ mkDerivation, attoparsec, <snip>
22 }:
23 mkDerivation {
24 pname = "aeson";
25 <snip>
26 homepage = "https://github.com/bos/aeson";
27 })
28
29 The mkDerivation function of haskellPackages will take care of putting
30 the homepage in the right place, in meta.
31
32 > haskellPackages.aeson.meta.homepage
33 "https://github.com/bos/aeson"
34
35 > x = haskell.lib.compose.overrideCabal (old: { homepage = old.homepage + "#readme"; }) haskellPackages.aeson
36 > x.meta.homepage
37 "https://github.com/bos/aeson#readme"
38 */
39 overrideCabal =
40 f: drv:
41 (drv.override (
42 args:
43 args
44 // {
45 mkDerivation = drv: (args.mkDerivation drv).override f;
46 }
47 ))
48 // {
49 overrideScope = scope: overrideCabal f (drv.overrideScope scope);
50 };
51
52 # : Map Name (Either Path VersionNumber) -> HaskellPackageOverrideSet
53 # Given a set whose values are either paths or version strings, produces
54 # a package override set (i.e. (self: super: { etc. })) that sets
55 # the packages named in the input set to the corresponding versions
56 packageSourceOverrides =
57 overrides: self: super:
58 pkgs.lib.mapAttrs (
59 name: src:
60 let
61 isPath = x: builtins.substring 0 1 (toString x) == "/";
62 generateExprs = if isPath src then self.callCabal2nix else self.callHackage;
63 in
64 generateExprs name src { }
65 ) overrides;
66
67 /*
68 doCoverage modifies a haskell package to enable the generation
69 and installation of a coverage report.
70
71 See https://wiki.haskell.org/Haskell_program_coverage
72 */
73 doCoverage = overrideCabal (drv: {
74 doCoverage = true;
75 });
76
77 /*
78 dontCoverage modifies a haskell package to disable the generation
79 and installation of a coverage report.
80 */
81 dontCoverage = overrideCabal (drv: {
82 doCoverage = false;
83 });
84
85 /*
86 doHaddock modifies a haskell package to enable the generation and
87 installation of API documentation from code comments using the
88 haddock tool.
89 */
90 doHaddock = overrideCabal (drv: {
91 doHaddock = true;
92 });
93
94 /*
95 dontHaddock modifies a haskell package to disable the generation and
96 installation of API documentation from code comments using the
97 haddock tool.
98 */
99 dontHaddock = overrideCabal (drv: {
100 doHaddock = false;
101 });
102
103 /*
104 doJailbreak enables the removal of version bounds from the cabal
105 file. You may want to avoid this function.
106
107 This is useful when a package reports that it can not be built
108 due to version mismatches. In some cases, removing the version
109 bounds entirely is an easy way to make a package build, but at
110 the risk of breaking software in non-obvious ways now or in the
111 future.
112
113 Instead of jailbreaking, you can patch the cabal file.
114
115 Note that jailbreaking at this time, doesn't lift bounds on
116 conditional branches.
117 https://github.com/peti/jailbreak-cabal/issues/7 has further details.
118 */
119 doJailbreak = overrideCabal (drv: {
120 jailbreak = true;
121 });
122
123 /*
124 dontJailbreak restores the use of the version bounds the check
125 the use of dependencies in the package description.
126 */
127 dontJailbreak = overrideCabal (drv: {
128 jailbreak = false;
129 });
130
131 /*
132 doCheck enables dependency checking, compilation and execution
133 of test suites listed in the package description file.
134 */
135 doCheck = overrideCabal (drv: {
136 doCheck = true;
137 });
138 /*
139 dontCheck disables dependency checking, compilation and execution
140 of test suites listed in the package description file.
141 */
142 dontCheck = overrideCabal (drv: {
143 doCheck = false;
144 });
145 /*
146 The dontCheckIf variant sets doCheck = false if the condition
147 applies. In any other case the previously set/default value is used.
148 This prevents accidentally re-enabling tests in a later override.
149 */
150 dontCheckIf = condition: if condition then dontCheck else lib.id;
151
152 /*
153 doBenchmark enables dependency checking and compilation
154 for benchmarks listed in the package description file.
155 Benchmarks are, however, not executed at the moment.
156 */
157 doBenchmark = overrideCabal (drv: {
158 doBenchmark = true;
159 });
160 /*
161 dontBenchmark disables dependency checking, compilation and execution
162 for benchmarks listed in the package description file.
163 */
164 dontBenchmark = overrideCabal (drv: {
165 doBenchmark = false;
166 });
167
168 /*
169 doDistribute enables the distribution of binaries for the package
170 via hydra.
171 */
172 doDistribute = overrideCabal (drv: {
173 # lib.platforms.all is the default value for platforms (since GHC can cross-compile)
174 hydraPlatforms = lib.subtractLists (drv.badPlatforms or [ ]) (drv.platforms or lib.platforms.all);
175 });
176 /*
177 dontDistribute disables the distribution of binaries for the package
178 via hydra.
179 */
180 dontDistribute = overrideCabal (drv: {
181 hydraPlatforms = [ ];
182 });
183
184 /*
185 appendConfigureFlag adds a single argument that will be passed to the
186 cabal configure command, after the arguments that have been defined
187 in the initial declaration or previous overrides.
188
189 Example:
190
191 > haskell.lib.compose.appendConfigureFlag "--profiling-detail=all-functions" haskellPackages.servant
192 */
193 appendConfigureFlag = x: appendConfigureFlags [ x ];
194 appendConfigureFlags =
195 xs:
196 overrideCabal (drv: {
197 configureFlags = (drv.configureFlags or [ ]) ++ xs;
198 });
199
200 appendBuildFlag =
201 x:
202 overrideCabal (drv: {
203 buildFlags = (drv.buildFlags or [ ]) ++ [ x ];
204 });
205 appendBuildFlags =
206 xs:
207 overrideCabal (drv: {
208 buildFlags = (drv.buildFlags or [ ]) ++ xs;
209 });
210
211 /*
212 removeConfigureFlag drv x is a Haskell package like drv, but with
213 all cabal configure arguments that are equal to x removed.
214
215 > haskell.lib.compose.removeConfigureFlag "--verbose" haskellPackages.servant
216 */
217 removeConfigureFlag =
218 x:
219 overrideCabal (drv: {
220 configureFlags = lib.remove x (drv.configureFlags or [ ]);
221 });
222
223 addBuildTool = x: addBuildTools [ x ];
224 addBuildTools =
225 xs:
226 overrideCabal (drv: {
227 buildTools = (drv.buildTools or [ ]) ++ xs;
228 });
229
230 addExtraLibrary = x: addExtraLibraries [ x ];
231 addExtraLibraries =
232 xs:
233 overrideCabal (drv: {
234 extraLibraries = (drv.extraLibraries or [ ]) ++ xs;
235 });
236
237 addBuildDepend = x: addBuildDepends [ x ];
238 addBuildDepends =
239 xs:
240 overrideCabal (drv: {
241 buildDepends = (drv.buildDepends or [ ]) ++ xs;
242 });
243
244 addTestToolDepend = x: addTestToolDepends [ x ];
245 addTestToolDepends =
246 xs:
247 overrideCabal (drv: {
248 testToolDepends = (drv.testToolDepends or [ ]) ++ xs;
249 });
250
251 addPkgconfigDepend = x: addPkgconfigDepends [ x ];
252 addPkgconfigDepends =
253 xs:
254 overrideCabal (drv: {
255 pkg-configDepends = (drv.pkg-configDepends or [ ]) ++ xs;
256 });
257
258 addSetupDepend = x: addSetupDepends [ x ];
259 addSetupDepends =
260 xs:
261 overrideCabal (drv: {
262 setupHaskellDepends = (drv.setupHaskellDepends or [ ]) ++ xs;
263 });
264
265 enableCabalFlag = x: drv: appendConfigureFlag "-f${x}" (removeConfigureFlag "-f-${x}" drv);
266 disableCabalFlag = x: drv: appendConfigureFlag "-f-${x}" (removeConfigureFlag "-f${x}" drv);
267
268 markBroken = overrideCabal (drv: {
269 broken = true;
270 hydraPlatforms = [ ];
271 });
272 unmarkBroken = overrideCabal (drv: {
273 broken = false;
274 });
275 markBrokenVersion =
276 version: drv:
277 assert drv.version == version;
278 markBroken drv;
279 markUnbroken = overrideCabal (drv: {
280 broken = false;
281 });
282
283 /*
284 disableParallelBuilding drops the -j<n> option from the GHC
285 command line for the given package. This can be useful in rare
286 situations where parallel building of a package causes GHC to
287 fail for some reason.
288 */
289 disableParallelBuilding = overrideCabal (drv: {
290 enableParallelBuilding = false;
291 });
292
293 enableLibraryProfiling = overrideCabal (drv: {
294 enableLibraryProfiling = true;
295 });
296 disableLibraryProfiling = overrideCabal (drv: {
297 enableLibraryProfiling = false;
298 });
299
300 enableExecutableProfiling = overrideCabal (drv: {
301 enableExecutableProfiling = true;
302 });
303 disableExecutableProfiling = overrideCabal (drv: {
304 enableExecutableProfiling = false;
305 });
306
307 enableSharedExecutables = overrideCabal (drv: {
308 enableSharedExecutables = true;
309 });
310 disableSharedExecutables = overrideCabal (drv: {
311 enableSharedExecutables = false;
312 });
313
314 enableSharedLibraries = overrideCabal (drv: {
315 enableSharedLibraries = true;
316 });
317 disableSharedLibraries = overrideCabal (drv: {
318 enableSharedLibraries = false;
319 });
320
321 enableDeadCodeElimination = overrideCabal (drv: {
322 enableDeadCodeElimination = true;
323 });
324 disableDeadCodeElimination = overrideCabal (drv: {
325 enableDeadCodeElimination = false;
326 });
327
328 enableStaticLibraries = overrideCabal (drv: {
329 enableStaticLibraries = true;
330 });
331 disableStaticLibraries = overrideCabal (drv: {
332 enableStaticLibraries = false;
333 });
334
335 enableSeparateBinOutput = overrideCabal (drv: {
336 enableSeparateBinOutput = true;
337 });
338
339 appendPatch = x: appendPatches [ x ];
340 appendPatches =
341 xs:
342 overrideCabal (drv: {
343 patches = (drv.patches or [ ]) ++ xs;
344 });
345
346 /*
347 Set a specific build target instead of compiling all targets in the package.
348 For example, imagine we have a .cabal file with a library, and 2 executables "dev" and "server".
349 We can build only "server" and not wait on the compilation of "dev" by using setBuildTarget as follows:
350
351 > setBuildTarget "server" (callCabal2nix "thePackageName" thePackageSrc {})
352 */
353 setBuildTargets =
354 xs:
355 overrideCabal (drv: {
356 buildTarget = lib.concatStringsSep " " xs;
357 });
358 setBuildTarget = x: setBuildTargets [ x ];
359
360 doHyperlinkSource = overrideCabal (drv: {
361 hyperlinkSource = true;
362 });
363 dontHyperlinkSource = overrideCabal (drv: {
364 hyperlinkSource = false;
365 });
366
367 disableHardening =
368 flags:
369 overrideCabal (drv: {
370 hardeningDisable = flags;
371 });
372
373 /*
374 Let Nix strip the binary files.
375 This removes debugging symbols.
376 */
377 doStrip = overrideCabal (drv: {
378 dontStrip = false;
379 });
380
381 /*
382 Stop Nix from stripping the binary files.
383 This keeps debugging symbols.
384 */
385 dontStrip = overrideCabal (drv: {
386 dontStrip = true;
387 });
388
389 /*
390 Useful for debugging segfaults with gdb.
391 This includes dontStrip.
392 */
393 enableDWARFDebugging =
394 drv:
395 # -g: enables debugging symbols
396 # --disable-*-stripping: tell GHC not to strip resulting binaries
397 # dontStrip: see above
398 appendConfigureFlag "--ghc-options=-g --disable-executable-stripping --disable-library-stripping" (
399 dontStrip drv
400 );
401
402 /*
403 Create a source distribution tarball like those found on hackage,
404 instead of building the package.
405 */
406 sdistTarball =
407 pkg:
408 lib.overrideDerivation pkg (drv: {
409 name = "${drv.pname}-source-${drv.version}";
410 # Since we disable the haddock phase, we also need to override the
411 # outputs since the separate doc output will not be produced.
412 outputs = [ "out" ];
413 buildPhase = "./Setup sdist";
414 haddockPhase = ":";
415 checkPhase = ":";
416 installPhase = "install -D dist/${drv.pname}-*.tar.gz $out/${drv.pname}-${drv.version}.tar.gz";
417 fixupPhase = ":";
418 });
419
420 /*
421 Create a documentation tarball suitable for uploading to Hackage instead
422 of building the package.
423 */
424 documentationTarball =
425 pkg:
426 pkgs.lib.overrideDerivation pkg (drv: {
427 name = "${drv.name}-docs";
428 # Like sdistTarball, disable the "doc" output here.
429 outputs = [ "out" ];
430 buildPhase = ''
431 runHook preHaddock
432 ./Setup haddock --for-hackage
433 runHook postHaddock
434 '';
435 haddockPhase = ":";
436 checkPhase = ":";
437 installPhase = ''
438 runHook preInstall
439 mkdir -p "$out"
440 tar --format=ustar \
441 -czf "$out/${drv.name}-docs.tar.gz" \
442 -C dist/doc/html "${drv.name}-docs"
443 runHook postInstall
444 '';
445 });
446
447 /*
448 Use the gold linker. It is a linker for ELF that is designed
449 "to run as fast as possible on modern systems"
450 */
451 linkWithGold = appendConfigureFlag "--ghc-option=-optl-fuse-ld=gold --ld-option=-fuse-ld=gold --with-ld=ld.gold";
452
453 /*
454 link executables statically against haskell libs to reduce
455 closure size
456 */
457 justStaticExecutables = overrideCabal (drv: {
458 enableSharedExecutables = false;
459 enableLibraryProfiling = drv.enableExecutableProfiling or false;
460 isLibrary = false;
461 doHaddock = false;
462 postFixup = drv.postFixup or "" + ''
463
464 # Remove every directory which could have links to other store paths.
465 rm -rf $out/lib $out/nix-support $out/share/doc
466 '';
467 disallowGhcReference = true;
468 });
469
470 /*
471 Build a source distribution tarball instead of using the source files
472 directly. The effect is that the package is built as if it were published
473 on hackage. This can be used as a test for the source distribution,
474 assuming the build fails when packaging mistakes are in the cabal file.
475
476 A faster implementation using `cabal-install` is available as
477 `buildFromCabalSdist` in your Haskell package set.
478 */
479 buildFromSdist =
480 pkg:
481 overrideCabal (drv: {
482 src = "${sdistTarball pkg}/${pkg.pname}-${pkg.version}.tar.gz";
483
484 # Revising and jailbreaking the cabal file has been handled in sdistTarball
485 revision = null;
486 editedCabalFile = null;
487 jailbreak = false;
488 }) pkg;
489
490 /*
491 Build the package in a strict way to uncover potential problems.
492 This includes buildFromSdist and failOnAllWarnings.
493 */
494 buildStrictly = pkg: buildFromSdist (failOnAllWarnings pkg);
495
496 # Disable core optimizations, significantly speeds up build time
497 disableOptimization = appendConfigureFlag "--disable-optimization";
498
499 /*
500 Turn on most of the compiler warnings and fail the build if any
501 of them occur.
502 */
503 failOnAllWarnings = appendConfigureFlag "--ghc-option=-Wall --ghc-option=-Werror";
504
505 /*
506 Add a post-build check to verify that dependencies declared in
507 the cabal file are actually used.
508
509 The first attrset argument can be used to configure the strictness
510 of this check and a list of ignored package names that would otherwise
511 cause false alarms.
512 */
513 checkUnusedPackages =
514 {
515 ignoreEmptyImports ? false,
516 ignoreMainModule ? false,
517 ignorePackages ? [ ],
518 }:
519 drv:
520 overrideCabal (_drv: {
521 postBuild =
522 let
523 args = lib.concatStringsSep " " (
524 lib.optional ignoreEmptyImports "--ignore-empty-imports"
525 ++ lib.optional ignoreMainModule "--ignore-main-module"
526 ++ map (pkg: "--ignore-package ${pkg}") ignorePackages
527 );
528 in
529 "${pkgs.haskellPackages.packunused}/bin/packunused" + lib.optionalString (args != "") " ${args}";
530 }) (appendConfigureFlag "--ghc-option=-ddump-minimal-imports" drv);
531
532 buildStackProject = pkgs.callPackage ../generic-stack-builder.nix { };
533
534 /*
535 Add a dummy command to trigger a build despite an equivalent
536 earlier build that is present in the store or cache.
537 */
538 triggerRebuild =
539 i:
540 overrideCabal (drv: {
541 postUnpack = drv.postUnpack or "" + ''
542
543 # trigger rebuild ${toString i}
544 '';
545 });
546
547 /*
548 Override the sources for the package and optionally the version.
549 This also takes of removing editedCabalFile.
550 */
551 overrideSrc =
552 {
553 src,
554 version ? null,
555 }:
556 drv:
557 overrideCabal (_: {
558 inherit src;
559 version = if version == null then drv.version else version;
560 editedCabalFile = null;
561 }) drv;
562
563 # Get all of the build inputs of a haskell package, divided by category.
564 getBuildInputs = p: p.getBuildInputs;
565
566 # Extract the haskell build inputs of a haskell package.
567 # This is useful to build environments for developing on that
568 # package.
569 getHaskellBuildInputs = p: (getBuildInputs p).haskellBuildInputs;
570
571 # Under normal evaluation, simply return the original package. Under
572 # nix-shell evaluation, return a nix-shell optimized environment.
573 shellAware = p: if lib.inNixShell then p.env else p;
574
575 # Utility to convert a directory full of `cabal2nix`-generated files into a
576 # package override set
577 #
578 # packagesFromDirectory : { directory : Directory, ... } -> HaskellPackageOverrideSet
579 packagesFromDirectory =
580 { directory, ... }:
581
582 self: super:
583 let
584 haskellPaths = lib.filter (lib.hasSuffix ".nix") (builtins.attrNames (builtins.readDir directory));
585
586 toKeyVal = file: {
587 name = builtins.replaceStrings [ ".nix" ] [ "" ] file;
588
589 value = self.callPackage (directory + "/${file}") { };
590 };
591
592 in
593 builtins.listToAttrs (map toKeyVal haskellPaths);
594
595 /*
596 INTERNAL function retained for backwards compatibility, use
597 haskell.packages.*.generateOptparseApplicativeCompletions instead!
598 */
599 __generateOptparseApplicativeCompletion =
600 exeName:
601 overrideCabal (drv: {
602 postInstall = (drv.postInstall or "") + ''
603 bashCompDir="''${!outputBin}/share/bash-completion/completions"
604 zshCompDir="''${!outputBin}/share/zsh/vendor-completions"
605 fishCompDir="''${!outputBin}/share/fish/vendor_completions.d"
606 mkdir -p "$bashCompDir" "$zshCompDir" "$fishCompDir"
607 "''${!outputBin}/bin/${exeName}" --bash-completion-script "''${!outputBin}/bin/${exeName}" >"$bashCompDir/${exeName}"
608 "''${!outputBin}/bin/${exeName}" --zsh-completion-script "''${!outputBin}/bin/${exeName}" >"$zshCompDir/_${exeName}"
609 "''${!outputBin}/bin/${exeName}" --fish-completion-script "''${!outputBin}/bin/${exeName}" >"$fishCompDir/${exeName}.fish"
610
611 # Sanity check
612 grep -F ${exeName} <$bashCompDir/${exeName} >/dev/null || {
613 echo 'Could not find ${exeName} in completion script.'
614 exit 1
615 }
616 '';
617 });
618
619 /*
620 Retained for backwards compatibility.
621 Use haskell.packages.*.generateOptparseApplicativeCompletions
622 which is cross aware instead.
623 */
624 generateOptparseApplicativeCompletions =
625 commands: pkg:
626 lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2211)
627 "haskellLib.generateOptparseApplicativeCompletions is deprecated in favor of haskellPackages.generateOptparseApplicativeCompletions. Please change ${pkg.name} to use the latter and make sure it uses its matching haskell.packages set!"
628 (pkgs.lib.foldr __generateOptparseApplicativeCompletion pkg commands);
629
630 /*
631 Retained for backwards compatibility.
632 Use haskell.packages.*.generateOptparseApplicativeCompletions
633 which is cross aware instead.
634 */
635 generateOptparseApplicativeCompletion =
636 command: pkg:
637 lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2211)
638 "haskellLib.generateOptparseApplicativeCompletion is deprecated in favor of haskellPackages.generateOptparseApplicativeCompletions (plural!). Please change ${pkg.name} to use the latter and make sure it uses its matching haskell.packages set!"
639 (__generateOptparseApplicativeCompletion command pkg);
640
641 # Don't fail at configure time if there are multiple versions of the
642 # same package in the (recursive) dependencies of the package being
643 # built. Will delay failures, if any, to compile time.
644 allowInconsistentDependencies = overrideCabal (drv: {
645 allowInconsistentDependencies = true;
646 });
647
648 # Work around a Cabal bug requiring pkg-config --static --libs to work even
649 # when linking dynamically, affecting Cabal 3.8 and 3.9.
650 # https://github.com/haskell/cabal/issues/8455
651 #
652 # For this, we treat the runtime system/pkg-config dependencies of a Haskell
653 # derivation as if they were propagated from their dependencies which allows
654 # pkg-config --static to work in most cases.
655 #
656 # Warning: This function may change or be removed at any time, e.g. if we find
657 # a different workaround, upstream fixes the bug or we patch Cabal.
658 __CabalEagerPkgConfigWorkaround =
659 let
660 # Take list of derivations and return list of the transitive dependency
661 # closure, only taking into account buildInputs. Loosely based on
662 # closePropagationFast.
663 propagatedPlainBuildInputs =
664 drvs:
665 builtins.map (i: i.val) (
666 builtins.genericClosure {
667 startSet = builtins.map (drv: {
668 key = drv.outPath;
669 val = drv;
670 }) drvs;
671 operator =
672 { val, ... }:
673 if !lib.isDerivation val then
674 [ ]
675 else
676 builtins.concatMap (
677 drv:
678 if !lib.isDerivation drv then
679 [ ]
680 else
681 [
682 {
683 key = drv.outPath;
684 val = drv;
685 }
686 ]
687 ) (val.buildInputs or [ ] ++ val.propagatedBuildInputs or [ ]);
688 }
689 );
690 in
691 overrideCabal (old: {
692 benchmarkPkgconfigDepends = propagatedPlainBuildInputs old.benchmarkPkgconfigDepends or [ ];
693 executablePkgconfigDepends = propagatedPlainBuildInputs old.executablePkgconfigDepends or [ ];
694 libraryPkgconfigDepends = propagatedPlainBuildInputs old.libraryPkgconfigDepends or [ ];
695 testPkgconfigDepends = propagatedPlainBuildInputs old.testPkgconfigDepends or [ ];
696 });
697}