lol
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
112 /* doBenchmark enables dependency checking, compilation and execution
113 for benchmarks listed in the package description file.
114 */
115 doBenchmark = overrideCabal (drv: { doBenchmark = true; });
116 /* dontBenchmark disables dependency checking, compilation and execution
117 for benchmarks listed in the package description file.
118 */
119 dontBenchmark = overrideCabal (drv: { doBenchmark = false; });
120
121 /* doDistribute enables the distribution of binaries for the package
122 via hydra.
123 */
124 doDistribute = overrideCabal (drv: {
125 # lib.platforms.all is the default value for platforms (since GHC can cross-compile)
126 hydraPlatforms = lib.subtractLists (drv.badPlatforms or [])
127 (drv.platforms or lib.platforms.all);
128 });
129 /* dontDistribute disables the distribution of binaries for the package
130 via hydra.
131 */
132 dontDistribute = overrideCabal (drv: { hydraPlatforms = []; });
133
134 /* appendConfigureFlag adds a single argument that will be passed to the
135 cabal configure command, after the arguments that have been defined
136 in the initial declaration or previous overrides.
137
138 Example:
139
140 > haskell.lib.compose.appendConfigureFlag "--profiling-detail=all-functions" haskellPackages.servant
141 */
142 appendConfigureFlag = x: appendConfigureFlags [x];
143 appendConfigureFlags = xs: overrideCabal (drv: { configureFlags = (drv.configureFlags or []) ++ xs; });
144
145 appendBuildFlag = x: overrideCabal (drv: { buildFlags = (drv.buildFlags or []) ++ [x]; });
146 appendBuildFlags = xs: overrideCabal (drv: { buildFlags = (drv.buildFlags or []) ++ xs; });
147
148 /* removeConfigureFlag drv x is a Haskell package like drv, but with
149 all cabal configure arguments that are equal to x removed.
150
151 > haskell.lib.compose.removeConfigureFlag "--verbose" haskellPackages.servant
152 */
153 removeConfigureFlag = x: overrideCabal (drv: { configureFlags = lib.remove x (drv.configureFlags or []); });
154
155 addBuildTool = x: addBuildTools [x];
156 addBuildTools = xs: overrideCabal (drv: { buildTools = (drv.buildTools or []) ++ xs; });
157
158 addExtraLibrary = x: addExtraLibraries [x];
159 addExtraLibraries = xs: overrideCabal (drv: { extraLibraries = (drv.extraLibraries or []) ++ xs; });
160
161 addBuildDepend = x: addBuildDepends [x];
162 addBuildDepends = xs: overrideCabal (drv: { buildDepends = (drv.buildDepends or []) ++ xs; });
163
164 addTestToolDepend = x: addTestToolDepends [x];
165 addTestToolDepends = xs: overrideCabal (drv: { testToolDepends = (drv.testToolDepends or []) ++ xs; });
166
167 addPkgconfigDepend = x: addPkgconfigDepends [x];
168 addPkgconfigDepends = xs: overrideCabal (drv: { pkg-configDepends = (drv.pkg-configDepends or []) ++ xs; });
169
170 addSetupDepend = x: addSetupDepends [x];
171 addSetupDepends = xs: overrideCabal (drv: { setupHaskellDepends = (drv.setupHaskellDepends or []) ++ xs; });
172
173 enableCabalFlag = x: drv: appendConfigureFlag "-f${x}" (removeConfigureFlag "-f-${x}" drv);
174 disableCabalFlag = x: drv: appendConfigureFlag "-f-${x}" (removeConfigureFlag "-f${x}" drv);
175
176 markBroken = overrideCabal (drv: { broken = true; hydraPlatforms = []; });
177 unmarkBroken = overrideCabal (drv: { broken = false; });
178 markBrokenVersion = version: drv: assert drv.version == version; markBroken drv;
179 markUnbroken = overrideCabal (drv: { broken = false; });
180
181 enableLibraryProfiling = overrideCabal (drv: { enableLibraryProfiling = true; });
182 disableLibraryProfiling = overrideCabal (drv: { enableLibraryProfiling = false; });
183
184 enableExecutableProfiling = overrideCabal (drv: { enableExecutableProfiling = true; });
185 disableExecutableProfiling = overrideCabal (drv: { enableExecutableProfiling = false; });
186
187 enableSharedExecutables = overrideCabal (drv: { enableSharedExecutables = true; });
188 disableSharedExecutables = overrideCabal (drv: { enableSharedExecutables = false; });
189
190 enableSharedLibraries = overrideCabal (drv: { enableSharedLibraries = true; });
191 disableSharedLibraries = overrideCabal (drv: { enableSharedLibraries = false; });
192
193 enableDeadCodeElimination = overrideCabal (drv: { enableDeadCodeElimination = true; });
194 disableDeadCodeElimination = overrideCabal (drv: { enableDeadCodeElimination = false; });
195
196 enableStaticLibraries = overrideCabal (drv: { enableStaticLibraries = true; });
197 disableStaticLibraries = overrideCabal (drv: { enableStaticLibraries = false; });
198
199 enableSeparateBinOutput = overrideCabal (drv: { enableSeparateBinOutput = true; });
200
201 appendPatch = x: appendPatches [x];
202 appendPatches = xs: overrideCabal (drv: { patches = (drv.patches or []) ++ xs; });
203
204 /* Set a specific build target instead of compiling all targets in the package.
205 * For example, imagine we have a .cabal file with a library, and 2 executables "dev" and "server".
206 * We can build only "server" and not wait on the compilation of "dev" by using setBuildTarget as follows:
207 *
208 * > setBuildTarget "server" (callCabal2nix "thePackageName" thePackageSrc {})
209 *
210 */
211 setBuildTargets = xs: overrideCabal (drv: { buildTarget = lib.concatStringsSep " " xs; });
212 setBuildTarget = x: setBuildTargets [x];
213
214 doHyperlinkSource = overrideCabal (drv: { hyperlinkSource = true; });
215 dontHyperlinkSource = overrideCabal (drv: { hyperlinkSource = false; });
216
217 disableHardening = flags: overrideCabal (drv: { hardeningDisable = flags; });
218
219 /* Let Nix strip the binary files.
220 * This removes debugging symbols.
221 */
222 doStrip = overrideCabal (drv: { dontStrip = false; });
223
224 /* Stop Nix from stripping the binary files.
225 * This keeps debugging symbols.
226 */
227 dontStrip = overrideCabal (drv: { dontStrip = true; });
228
229 /* Useful for debugging segfaults with gdb.
230 * This includes dontStrip.
231 */
232 enableDWARFDebugging = drv:
233 # -g: enables debugging symbols
234 # --disable-*-stripping: tell GHC not to strip resulting binaries
235 # dontStrip: see above
236 appendConfigureFlag "--ghc-options=-g --disable-executable-stripping --disable-library-stripping" (dontStrip drv);
237
238 /* Create a source distribution tarball like those found on hackage,
239 instead of building the package.
240 */
241 sdistTarball = pkg: lib.overrideDerivation pkg (drv: {
242 name = "${drv.pname}-source-${drv.version}";
243 # Since we disable the haddock phase, we also need to override the
244 # outputs since the separate doc output will not be produced.
245 outputs = ["out"];
246 buildPhase = "./Setup sdist";
247 haddockPhase = ":";
248 checkPhase = ":";
249 installPhase = "install -D dist/${drv.pname}-*.tar.gz $out/${drv.pname}-${drv.version}.tar.gz";
250 fixupPhase = ":";
251 });
252
253 /* Create a documentation tarball suitable for uploading to Hackage instead
254 of building the package.
255 */
256 documentationTarball = pkg:
257 pkgs.lib.overrideDerivation pkg (drv: {
258 name = "${drv.name}-docs";
259 # Like sdistTarball, disable the "doc" output here.
260 outputs = [ "out" ];
261 buildPhase = ''
262 runHook preHaddock
263 ./Setup haddock --for-hackage
264 runHook postHaddock
265 '';
266 haddockPhase = ":";
267 checkPhase = ":";
268 installPhase = ''
269 runHook preInstall
270 mkdir -p "$out"
271 tar --format=ustar \
272 -czf "$out/${drv.name}-docs.tar.gz" \
273 -C dist/doc/html "${drv.name}-docs"
274 runHook postInstall
275 '';
276 });
277
278 /* Use the gold linker. It is a linker for ELF that is designed
279 "to run as fast as possible on modern systems"
280 */
281 linkWithGold = appendConfigureFlag
282 "--ghc-option=-optl-fuse-ld=gold --ld-option=-fuse-ld=gold --with-ld=ld.gold";
283
284 /* link executables statically against haskell libs to reduce
285 closure size
286 */
287 justStaticExecutables = overrideCabal (drv: {
288 enableSharedExecutables = false;
289 enableLibraryProfiling = false;
290 isLibrary = false;
291 doHaddock = false;
292 postFixup = drv.postFixup or "" + ''
293
294 # Remove every directory which could have links to other store paths.
295 rm -rf $out/lib $out/nix-support $out/share/doc
296 '';
297 });
298
299 /* Build a source distribution tarball instead of using the source files
300 directly. The effect is that the package is built as if it were published
301 on hackage. This can be used as a test for the source distribution,
302 assuming the build fails when packaging mistakes are in the cabal file.
303
304 A faster implementation using `cabal-install` is available as
305 `buildFromCabalSdist` in your Haskell package set.
306 */
307 buildFromSdist = pkg: overrideCabal (drv: {
308 src = "${sdistTarball pkg}/${pkg.pname}-${pkg.version}.tar.gz";
309
310 # Revising and jailbreaking the cabal file has been handled in sdistTarball
311 revision = null;
312 editedCabalFile = null;
313 jailbreak = false;
314 }) pkg;
315
316 /* Build the package in a strict way to uncover potential problems.
317 This includes buildFromSdist and failOnAllWarnings.
318 */
319 buildStrictly = pkg: buildFromSdist (failOnAllWarnings pkg);
320
321 /* Disable core optimizations, significantly speeds up build time */
322 disableOptimization = appendConfigureFlag "--disable-optimization";
323
324 /* Turn on most of the compiler warnings and fail the build if any
325 of them occur. */
326 failOnAllWarnings = appendConfigureFlag "--ghc-option=-Wall --ghc-option=-Werror";
327
328 /* Add a post-build check to verify that dependencies declared in
329 the cabal file are actually used.
330
331 The first attrset argument can be used to configure the strictness
332 of this check and a list of ignored package names that would otherwise
333 cause false alarms.
334 */
335 checkUnusedPackages =
336 { ignoreEmptyImports ? false
337 , ignoreMainModule ? false
338 , ignorePackages ? []
339 } : drv :
340 overrideCabal (_drv: {
341 postBuild = with lib;
342 let args = concatStringsSep " " (
343 optional ignoreEmptyImports "--ignore-empty-imports" ++
344 optional ignoreMainModule "--ignore-main-module" ++
345 map (pkg: "--ignore-package ${pkg}") ignorePackages
346 );
347 in "${pkgs.haskellPackages.packunused}/bin/packunused" +
348 optionalString (args != "") " ${args}";
349 }) (appendConfigureFlag "--ghc-option=-ddump-minimal-imports" drv);
350
351 buildStackProject = pkgs.callPackage ../generic-stack-builder.nix { };
352
353 /* Add a dummy command to trigger a build despite an equivalent
354 earlier build that is present in the store or cache.
355 */
356 triggerRebuild = i: overrideCabal (drv: { postUnpack = ": trigger rebuild ${toString i}"; });
357
358 /* Override the sources for the package and optionaly the version.
359 This also takes of removing editedCabalFile.
360 */
361 overrideSrc = { src, version ? null }: drv:
362 overrideCabal (_: { inherit src; version = if version == null then drv.version else version; editedCabalFile = null; }) drv;
363
364 # Get all of the build inputs of a haskell package, divided by category.
365 getBuildInputs = p: p.getBuildInputs;
366
367 # Extract the haskell build inputs of a haskell package.
368 # This is useful to build environments for developing on that
369 # package.
370 getHaskellBuildInputs = p: (getBuildInputs p).haskellBuildInputs;
371
372 # Under normal evaluation, simply return the original package. Under
373 # nix-shell evaluation, return a nix-shell optimized environment.
374 shellAware = p: if lib.inNixShell then p.env else p;
375
376 ghcInfo = ghc:
377 rec { isCross = (ghc.cross or null) != null;
378 isGhcjs = ghc.isGhcjs or false;
379 nativeGhc = if isCross || isGhcjs
380 then ghc.bootPkgs.ghc
381 else ghc;
382 };
383
384 ### mkDerivation helpers
385 # These allow external users of a haskell package to extract
386 # information about how it is built in the same way that the
387 # generic haskell builder does, by reusing the same functions.
388 # Each function here has the same interface as mkDerivation and thus
389 # can be called for a given package simply by overriding the
390 # mkDerivation argument it used. See getHaskellBuildInputs above for
391 # an example of this.
392
393 # Some information about which phases should be run.
394 controlPhases = ghc: let inherit (ghcInfo ghc) isCross; in
395 { doCheck ? !isCross && (lib.versionOlder "7.4" ghc.version)
396 , doBenchmark ? false
397 , ...
398 }: { inherit doCheck doBenchmark; };
399
400 # Utility to convert a directory full of `cabal2nix`-generated files into a
401 # package override set
402 #
403 # packagesFromDirectory : { directory : Directory, ... } -> HaskellPackageOverrideSet
404 packagesFromDirectory =
405 { directory, ... }:
406
407 self: super:
408 let
409 haskellPaths = builtins.attrNames (builtins.readDir directory);
410
411 toKeyVal = file: {
412 name = builtins.replaceStrings [ ".nix" ] [ "" ] file;
413
414 value = self.callPackage (directory + "/${file}") { };
415 };
416
417 in
418 builtins.listToAttrs (map toKeyVal haskellPaths);
419
420 /*
421 INTERNAL function retained for backwards compatibility, use
422 haskell.packages.*.generateOptparseApplicativeCompletions instead!
423 */
424 __generateOptparseApplicativeCompletion = exeName: overrideCabal (drv: {
425 postInstall = (drv.postInstall or "") + ''
426 bashCompDir="''${!outputBin}/share/bash-completion/completions"
427 zshCompDir="''${!outputBin}/share/zsh/vendor-completions"
428 fishCompDir="''${!outputBin}/share/fish/vendor_completions.d"
429 mkdir -p "$bashCompDir" "$zshCompDir" "$fishCompDir"
430 "''${!outputBin}/bin/${exeName}" --bash-completion-script "''${!outputBin}/bin/${exeName}" >"$bashCompDir/${exeName}"
431 "''${!outputBin}/bin/${exeName}" --zsh-completion-script "''${!outputBin}/bin/${exeName}" >"$zshCompDir/_${exeName}"
432 "''${!outputBin}/bin/${exeName}" --fish-completion-script "''${!outputBin}/bin/${exeName}" >"$fishCompDir/${exeName}.fish"
433
434 # Sanity check
435 grep -F ${exeName} <$bashCompDir/${exeName} >/dev/null || {
436 echo 'Could not find ${exeName} in completion script.'
437 exit 1
438 }
439 '';
440 });
441
442 /*
443 Retained for backwards compatibility.
444 Use haskell.packages.*.generateOptparseApplicativeCompletions
445 which is cross aware instead.
446 */
447 generateOptparseApplicativeCompletions = commands: pkg:
448 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!"
449 (pkgs.lib.foldr __generateOptparseApplicativeCompletion pkg commands);
450
451 /*
452 Retained for backwards compatibility.
453 Use haskell.packages.*.generateOptparseApplicativeCompletions
454 which is cross aware instead.
455 */
456 generateOptparseApplicativeCompletion = command: pkg:
457 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!"
458 (__generateOptparseApplicativeCompletion command pkg);
459
460 # Don't fail at configure time if there are multiple versions of the
461 # same package in the (recursive) dependencies of the package being
462 # built. Will delay failures, if any, to compile time.
463 allowInconsistentDependencies = overrideCabal (drv: {
464 allowInconsistentDependencies = true;
465 });
466}