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