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