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 appendPatch = drv: x: appendPatches drv [x];
185 appendPatches = drv: xs: overrideCabal drv (drv: { patches = (drv.patches or []) ++ xs; });
186
187 doHyperlinkSource = drv: overrideCabal drv (drv: { hyperlinkSource = true; });
188 dontHyperlinkSource = drv: overrideCabal drv (drv: { hyperlinkSource = false; });
189
190 disableHardening = drv: flags: overrideCabal drv (drv: { hardeningDisable = flags; });
191
192 /* Let Nix strip the binary files.
193 * This removes debugging symbols.
194 */
195 doStrip = drv: overrideCabal drv (drv: { dontStrip = false; });
196
197 /* Stop Nix from stripping the binary files.
198 * This keeps debugging symbols.
199 */
200 dontStrip = drv: overrideCabal drv (drv: { dontStrip = true; });
201
202 /* Useful for debugging segfaults with gdb.
203 * This includes dontStrip.
204 */
205 enableDWARFDebugging = drv:
206 # -g: enables debugging symbols
207 # --disable-*-stripping: tell GHC not to strip resulting binaries
208 # dontStrip: see above
209 appendConfigureFlag (dontStrip drv) "--ghc-options=-g --disable-executable-stripping --disable-library-stripping";
210
211 /* Create a source distribution tarball like those found on hackage,
212 instead of building the package.
213 */
214 sdistTarball = pkg: lib.overrideDerivation pkg (drv: {
215 name = "${drv.pname}-source-${drv.version}";
216 # Since we disable the haddock phase, we also need to override the
217 # outputs since the separate doc output will not be produced.
218 outputs = ["out"];
219 buildPhase = "./Setup sdist";
220 haddockPhase = ":";
221 checkPhase = ":";
222 installPhase = "install -D dist/${drv.pname}-*.tar.gz $out/${drv.pname}-${drv.version}.tar.gz";
223 fixupPhase = ":";
224 });
225
226 /* Use the gold linker. It is a linker for ELF that is designed
227 "to run as fast as possible on modern systems"
228 */
229 linkWithGold = drv : appendConfigureFlag drv
230 "--ghc-option=-optl-fuse-ld=gold --ld-option=-fuse-ld=gold --with-ld=ld.gold";
231
232 /* link executables statically against haskell libs to reduce
233 closure size
234 */
235 justStaticExecutables = drv: overrideCabal drv (drv: {
236 enableSharedExecutables = false;
237 enableLibraryProfiling = false;
238 isLibrary = false;
239 doHaddock = false;
240 postFixup = "rm -rf $out/lib $out/nix-support $out/share/doc";
241 });
242
243 /* Build a source distribution tarball instead of using the source files
244 directly. The effect is that the package is built as if it were published
245 on hackage. This can be used as a test for the source distribution,
246 assuming the build fails when packaging mistakes are in the cabal file.
247 */
248 buildFromSdist = pkg: lib.overrideDerivation pkg (drv: {
249 unpackPhase = let src = sdistTarball pkg; tarname = "${pkg.pname}-${pkg.version}"; in ''
250 echo "Source tarball is at ${src}/${tarname}.tar.gz"
251 tar xf ${src}/${tarname}.tar.gz
252 cd ${pkg.pname}-*
253 '';
254 });
255
256 /* Build the package in a strict way to uncover potential problems.
257 This includes buildFromSdist and failOnAllWarnings.
258 */
259 buildStrictly = pkg: buildFromSdist (failOnAllWarnings pkg);
260
261 /* Turn on most of the compiler warnings and fail the build if any
262 of them occur. */
263 failOnAllWarnings = drv: appendConfigureFlag drv "--ghc-option=-Wall --ghc-option=-Werror";
264
265 /* Add a post-build check to verify that dependencies declared in
266 the cabal file are actually used.
267
268 The first attrset argument can be used to configure the strictness
269 of this check and a list of ignored package names that would otherwise
270 cause false alarms.
271 */
272 checkUnusedPackages =
273 { ignoreEmptyImports ? false
274 , ignoreMainModule ? false
275 , ignorePackages ? []
276 } : drv :
277 overrideCabal (appendConfigureFlag drv "--ghc-option=-ddump-minimal-imports") (_drv: {
278 postBuild = with lib;
279 let args = concatStringsSep " " (
280 optional ignoreEmptyImports "--ignore-empty-imports" ++
281 optional ignoreMainModule "--ignore-main-module" ++
282 map (pkg: "--ignore-package ${pkg}") ignorePackages
283 );
284 in "${pkgs.haskellPackages.packunused}/bin/packunused" +
285 optionalString (args != "") " ${args}";
286 });
287
288 buildStackProject = pkgs.callPackage ./generic-stack-builder.nix { };
289
290 /* Add a dummy command to trigger a build despite an equivalent
291 earlier build that is present in the store or cache.
292 */
293 triggerRebuild = drv: i: overrideCabal drv (drv: { postUnpack = ": trigger rebuild ${toString i}"; });
294
295 /* Override the sources for the package and optionaly the version.
296 This also takes of removing editedCabalFile.
297 */
298 overrideSrc = drv: { src, version ? drv.version }:
299 overrideCabal drv (_: { inherit src version; editedCabalFile = null; });
300
301 # Get all of the build inputs of a haskell package, divided by category.
302 getBuildInputs = p:
303 (overrideCabal p (args: {
304 passthru = (args.passthru or {}) // {
305 _getBuildInputs = extractBuildInputs p.compiler args;
306 };
307 }))._getBuildInputs;
308
309 # Extract the haskell build inputs of a haskell package.
310 # This is useful to build environments for developing on that
311 # package.
312 getHaskellBuildInputs = p: (getBuildInputs p).haskellBuildInputs;
313
314 # Under normal evaluation, simply return the original package. Under
315 # nix-shell evaluation, return a nix-shell optimized environment.
316 shellAware = p: if lib.inNixShell then p.env else p;
317
318 ghcInfo = ghc:
319 rec { isCross = (ghc.cross or null) != null;
320 isGhcjs = ghc.isGhcjs or false;
321 nativeGhc = if isCross || isGhcjs
322 then ghc.bootPkgs.ghc
323 else ghc;
324 };
325
326 ### mkDerivation helpers
327 # These allow external users of a haskell package to extract
328 # information about how it is built in the same way that the
329 # generic haskell builder does, by reusing the same functions.
330 # Each function here has the same interface as mkDerivation and thus
331 # can be called for a given package simply by overriding the
332 # mkDerivation argument it used. See getHaskellBuildInputs above for
333 # an example of this.
334
335 # Some information about which phases should be run.
336 controlPhases = ghc: let inherit (ghcInfo ghc) isCross; in
337 { doCheck ? !isCross && (lib.versionOlder "7.4" ghc.version)
338 , doBenchmark ? false
339 , ...
340 }: { inherit doCheck doBenchmark; };
341
342 # Divide the build inputs of the package into useful sets.
343 extractBuildInputs = ghc:
344 { setupHaskellDepends ? [], extraLibraries ? []
345 , librarySystemDepends ? [], executableSystemDepends ? []
346 , pkgconfigDepends ? [], libraryPkgconfigDepends ? []
347 , executablePkgconfigDepends ? [], testPkgconfigDepends ? []
348 , benchmarkPkgconfigDepends ? [], testDepends ? []
349 , testHaskellDepends ? [], testSystemDepends ? []
350 , testToolDepends ? [], benchmarkDepends ? []
351 , benchmarkHaskellDepends ? [], benchmarkSystemDepends ? []
352 , benchmarkToolDepends ? [], buildDepends ? []
353 , libraryHaskellDepends ? [], executableHaskellDepends ? []
354 , ...
355 }@args:
356 let inherit (ghcInfo ghc) isGhcjs nativeGhc;
357 inherit (controlPhases ghc args) doCheck doBenchmark;
358 isHaskellPkg = x: x ? isHaskellLibrary;
359 allPkgconfigDepends =
360 pkgconfigDepends ++ libraryPkgconfigDepends ++
361 executablePkgconfigDepends ++
362 lib.optionals doCheck testPkgconfigDepends ++
363 lib.optionals doBenchmark benchmarkPkgconfigDepends;
364 otherBuildInputs =
365 setupHaskellDepends ++ extraLibraries ++
366 librarySystemDepends ++ executableSystemDepends ++
367 allPkgconfigDepends ++
368 lib.optionals doCheck ( testDepends ++ testHaskellDepends ++
369 testSystemDepends ++ testToolDepends
370 ) ++
371 # ghcjs's hsc2hs calls out to the native hsc2hs
372 lib.optional isGhcjs nativeGhc ++
373 lib.optionals doBenchmark ( benchmarkDepends ++
374 benchmarkHaskellDepends ++
375 benchmarkSystemDepends ++
376 benchmarkToolDepends
377 );
378 propagatedBuildInputs =
379 buildDepends ++ libraryHaskellDepends ++
380 executableHaskellDepends;
381 allBuildInputs = propagatedBuildInputs ++ otherBuildInputs;
382 isHaskellPartition =
383 lib.partition isHaskellPkg allBuildInputs;
384 in
385 { haskellBuildInputs = isHaskellPartition.right;
386 systemBuildInputs = isHaskellPartition.wrong;
387 inherit propagatedBuildInputs otherBuildInputs
388 allPkgconfigDepends;
389 };
390
391 # Utility to convert a directory full of `cabal2nix`-generated files into a
392 # package override set
393 #
394 # packagesFromDirectory : { directory : Directory, ... } -> HaskellPackageOverrideSet
395 packagesFromDirectory =
396 { directory, ... }:
397
398 self: super:
399 let
400 haskellPaths = builtins.attrNames (builtins.readDir directory);
401
402 toKeyVal = file: {
403 name = builtins.replaceStrings [ ".nix" ] [ "" ] file;
404
405 value = self.callPackage (directory + "/${file}") { };
406 };
407
408 in
409 builtins.listToAttrs (map toKeyVal haskellPaths);
410}