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 =
463 drv.postFixup or ""
464 + ''
465
466 # Remove every directory which could have links to other store paths.
467 rm -rf $out/lib $out/nix-support $out/share/doc
468 '';
469 disallowGhcReference = true;
470 });
471
472 /*
473 Build a source distribution tarball instead of using the source files
474 directly. The effect is that the package is built as if it were published
475 on hackage. This can be used as a test for the source distribution,
476 assuming the build fails when packaging mistakes are in the cabal file.
477
478 A faster implementation using `cabal-install` is available as
479 `buildFromCabalSdist` in your Haskell package set.
480 */
481 buildFromSdist =
482 pkg:
483 overrideCabal (drv: {
484 src = "${sdistTarball pkg}/${pkg.pname}-${pkg.version}.tar.gz";
485
486 # Revising and jailbreaking the cabal file has been handled in sdistTarball
487 revision = null;
488 editedCabalFile = null;
489 jailbreak = false;
490 }) pkg;
491
492 /*
493 Build the package in a strict way to uncover potential problems.
494 This includes buildFromSdist and failOnAllWarnings.
495 */
496 buildStrictly = pkg: buildFromSdist (failOnAllWarnings pkg);
497
498 # Disable core optimizations, significantly speeds up build time
499 disableOptimization = appendConfigureFlag "--disable-optimization";
500
501 /*
502 Turn on most of the compiler warnings and fail the build if any
503 of them occur.
504 */
505 failOnAllWarnings = appendConfigureFlag "--ghc-option=-Wall --ghc-option=-Werror";
506
507 /*
508 Add a post-build check to verify that dependencies declared in
509 the cabal file are actually used.
510
511 The first attrset argument can be used to configure the strictness
512 of this check and a list of ignored package names that would otherwise
513 cause false alarms.
514 */
515 checkUnusedPackages =
516 {
517 ignoreEmptyImports ? false,
518 ignoreMainModule ? false,
519 ignorePackages ? [ ],
520 }:
521 drv:
522 overrideCabal (_drv: {
523 postBuild =
524 let
525 args = lib.concatStringsSep " " (
526 lib.optional ignoreEmptyImports "--ignore-empty-imports"
527 ++ lib.optional ignoreMainModule "--ignore-main-module"
528 ++ map (pkg: "--ignore-package ${pkg}") ignorePackages
529 );
530 in
531 "${pkgs.haskellPackages.packunused}/bin/packunused" + lib.optionalString (args != "") " ${args}";
532 }) (appendConfigureFlag "--ghc-option=-ddump-minimal-imports" drv);
533
534 buildStackProject = pkgs.callPackage ../generic-stack-builder.nix { };
535
536 /*
537 Add a dummy command to trigger a build despite an equivalent
538 earlier build that is present in the store or cache.
539 */
540 triggerRebuild =
541 i:
542 overrideCabal (drv: {
543 postUnpack =
544 drv.postUnpack or ""
545 + ''
546
547 # trigger rebuild ${toString i}
548 '';
549 });
550
551 /*
552 Override the sources for the package and optionally the version.
553 This also takes of removing editedCabalFile.
554 */
555 overrideSrc =
556 {
557 src,
558 version ? null,
559 }:
560 drv:
561 overrideCabal (_: {
562 inherit src;
563 version = if version == null then drv.version else version;
564 editedCabalFile = null;
565 }) drv;
566
567 # Get all of the build inputs of a haskell package, divided by category.
568 getBuildInputs = p: p.getBuildInputs;
569
570 # Extract the haskell build inputs of a haskell package.
571 # This is useful to build environments for developing on that
572 # package.
573 getHaskellBuildInputs = p: (getBuildInputs p).haskellBuildInputs;
574
575 # Under normal evaluation, simply return the original package. Under
576 # nix-shell evaluation, return a nix-shell optimized environment.
577 shellAware = p: if lib.inNixShell then p.env else p;
578
579 ghcInfo = ghc: rec {
580 isCross = (ghc.cross or null) != null;
581 isGhcjs = ghc.isGhcjs or false;
582 nativeGhc = if isCross || isGhcjs then ghc.bootPkgs.ghc else ghc;
583 };
584
585 ### mkDerivation helpers
586 # These allow external users of a haskell package to extract
587 # information about how it is built in the same way that the
588 # generic haskell builder does, by reusing the same functions.
589 # Each function here has the same interface as mkDerivation and thus
590 # can be called for a given package simply by overriding the
591 # mkDerivation argument it used. See getHaskellBuildInputs above for
592 # an example of this.
593
594 # Some information about which phases should be run.
595 controlPhases =
596 ghc:
597 let
598 inherit (ghcInfo ghc) isCross;
599 in
600 {
601 doCheck ? !isCross,
602 doBenchmark ? false,
603 ...
604 }:
605 {
606 inherit doCheck doBenchmark;
607 };
608
609 # Utility to convert a directory full of `cabal2nix`-generated files into a
610 # package override set
611 #
612 # packagesFromDirectory : { directory : Directory, ... } -> HaskellPackageOverrideSet
613 packagesFromDirectory =
614 { directory, ... }:
615
616 self: super:
617 let
618 haskellPaths = lib.filter (lib.hasSuffix ".nix") (builtins.attrNames (builtins.readDir directory));
619
620 toKeyVal = file: {
621 name = builtins.replaceStrings [ ".nix" ] [ "" ] file;
622
623 value = self.callPackage (directory + "/${file}") { };
624 };
625
626 in
627 builtins.listToAttrs (map toKeyVal haskellPaths);
628
629 /*
630 INTERNAL function retained for backwards compatibility, use
631 haskell.packages.*.generateOptparseApplicativeCompletions instead!
632 */
633 __generateOptparseApplicativeCompletion =
634 exeName:
635 overrideCabal (drv: {
636 postInstall =
637 (drv.postInstall or "")
638 + ''
639 bashCompDir="''${!outputBin}/share/bash-completion/completions"
640 zshCompDir="''${!outputBin}/share/zsh/vendor-completions"
641 fishCompDir="''${!outputBin}/share/fish/vendor_completions.d"
642 mkdir -p "$bashCompDir" "$zshCompDir" "$fishCompDir"
643 "''${!outputBin}/bin/${exeName}" --bash-completion-script "''${!outputBin}/bin/${exeName}" >"$bashCompDir/${exeName}"
644 "''${!outputBin}/bin/${exeName}" --zsh-completion-script "''${!outputBin}/bin/${exeName}" >"$zshCompDir/_${exeName}"
645 "''${!outputBin}/bin/${exeName}" --fish-completion-script "''${!outputBin}/bin/${exeName}" >"$fishCompDir/${exeName}.fish"
646
647 # Sanity check
648 grep -F ${exeName} <$bashCompDir/${exeName} >/dev/null || {
649 echo 'Could not find ${exeName} in completion script.'
650 exit 1
651 }
652 '';
653 });
654
655 /*
656 Retained for backwards compatibility.
657 Use haskell.packages.*.generateOptparseApplicativeCompletions
658 which is cross aware instead.
659 */
660 generateOptparseApplicativeCompletions =
661 commands: pkg:
662 lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2211)
663 "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!"
664 (pkgs.lib.foldr __generateOptparseApplicativeCompletion pkg commands);
665
666 /*
667 Retained for backwards compatibility.
668 Use haskell.packages.*.generateOptparseApplicativeCompletions
669 which is cross aware instead.
670 */
671 generateOptparseApplicativeCompletion =
672 command: pkg:
673 lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2211)
674 "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!"
675 (__generateOptparseApplicativeCompletion command pkg);
676
677 # Don't fail at configure time if there are multiple versions of the
678 # same package in the (recursive) dependencies of the package being
679 # built. Will delay failures, if any, to compile time.
680 allowInconsistentDependencies = overrideCabal (drv: {
681 allowInconsistentDependencies = true;
682 });
683
684 # Work around a Cabal bug requiring pkg-config --static --libs to work even
685 # when linking dynamically, affecting Cabal 3.8 and 3.9.
686 # https://github.com/haskell/cabal/issues/8455
687 #
688 # For this, we treat the runtime system/pkg-config dependencies of a Haskell
689 # derivation as if they were propagated from their dependencies which allows
690 # pkg-config --static to work in most cases.
691 #
692 # Warning: This function may change or be removed at any time, e.g. if we find
693 # a different workaround, upstream fixes the bug or we patch Cabal.
694 __CabalEagerPkgConfigWorkaround =
695 let
696 # Take list of derivations and return list of the transitive dependency
697 # closure, only taking into account buildInputs. Loosely based on
698 # closePropagationFast.
699 propagatedPlainBuildInputs =
700 drvs:
701 builtins.map (i: i.val) (
702 builtins.genericClosure {
703 startSet = builtins.map (drv: {
704 key = drv.outPath;
705 val = drv;
706 }) drvs;
707 operator =
708 { val, ... }:
709 if !lib.isDerivation val then
710 [ ]
711 else
712 builtins.concatMap (
713 drv:
714 if !lib.isDerivation drv then
715 [ ]
716 else
717 [
718 {
719 key = drv.outPath;
720 val = drv;
721 }
722 ]
723 ) (val.buildInputs or [ ] ++ val.propagatedBuildInputs or [ ]);
724 }
725 );
726 in
727 overrideCabal (old: {
728 benchmarkPkgconfigDepends = propagatedPlainBuildInputs old.benchmarkPkgconfigDepends or [ ];
729 executablePkgconfigDepends = propagatedPlainBuildInputs old.executablePkgconfigDepends or [ ];
730 libraryPkgconfigDepends = propagatedPlainBuildInputs old.libraryPkgconfigDepends or [ ];
731 testPkgconfigDepends = propagatedPlainBuildInputs old.testPkgconfigDepends or [ ];
732 });
733}