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