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