1# This expression takes a file like `hackage-packages.nix` and constructs
2# a full package set out of that.
3
4{ # package-set used for build tools (all of nixpkgs)
5 buildPackages
6
7, # A haskell package set for Setup.hs, compiler plugins, and similar
8 # build-time uses.
9 buildHaskellPackages
10
11, # package-set used for non-haskell dependencies (all of nixpkgs)
12 pkgs
13
14, # stdenv provides our build and host platforms
15 stdenv
16
17, # this module provides the list of known licenses and maintainers
18 lib
19
20 # needed for overrideCabal & packageSourceOverrides
21, haskellLib
22
23, # hashes for downloading Hackage packages
24 all-cabal-hashes
25
26, # compiler to use
27 ghc
28
29, # A function that takes `{ pkgs, lib, callPackage }` as the first arg and
30 # `self` as second, and returns a set of haskell packages
31 package-set
32
33, # The final, fully overriden package set usable with the nixpkgs fixpoint
34 # overriding functionality
35 extensible-self
36}:
37
38# return value: a function from self to the package set
39self:
40
41let
42 inherit (stdenv) buildPlatform hostPlatform;
43
44 inherit (lib) fix' extends makeOverridable;
45 inherit (haskellLib) overrideCabal;
46
47 mkDerivationImpl = pkgs.callPackage ./generic-builder.nix {
48 inherit stdenv;
49 nodejs = buildPackages.nodejs-slim;
50 inherit (self) buildHaskellPackages ghc ghcWithHoogle ghcWithPackages;
51 inherit (self.buildHaskellPackages) jailbreak-cabal;
52 hscolour = overrideCabal (drv: {
53 isLibrary = false;
54 doHaddock = false;
55 hyperlinkSource = false; # Avoid depending on hscolour for this build.
56 postFixup = "rm -rf $out/lib $out/share $out/nix-support";
57 }) self.buildHaskellPackages.hscolour;
58 cpphs = overrideCabal (drv: {
59 isLibrary = false;
60 postFixup = "rm -rf $out/lib $out/share $out/nix-support";
61 }) (self.cpphs.overrideScope (self: super: {
62 mkDerivation = drv: super.mkDerivation (drv // {
63 enableSharedExecutables = false;
64 enableSharedLibraries = false;
65 doHaddock = false;
66 useCpphs = false;
67 });
68 }));
69 };
70
71 mkDerivation = makeOverridable mkDerivationImpl;
72
73 # manualArgs are the arguments that were explictly passed to `callPackage`, like:
74 #
75 # callPackage foo { bar = null; };
76 #
77 # here `bar` is a manual argument.
78 callPackageWithScope = scope: fn: manualArgs:
79 let
80 # this code is copied from callPackage in lib/customisation.nix
81 #
82 # we cannot use `callPackage` here because we want to call `makeOverridable`
83 # on `drvScope` (we cannot add `overrideScope` after calling `callPackage` because then it is
84 # lost on `.override`) but determine the auto-args based on `drv` (the problem here
85 # is that nix has no way to "passthrough" args while preserving the reflection
86 # info that callPackage uses to determine the arguments).
87 drv = if lib.isFunction fn then fn else import fn;
88 auto = builtins.intersectAttrs (lib.functionArgs drv) scope;
89
90 # Converts a returned function to a functor attribute set if necessary
91 ensureAttrs = v: if builtins.isFunction v then { __functor = _: v; } else v;
92
93 # this wraps the `drv` function to add a `overrideScope` function to the result.
94 drvScope = allArgs: ensureAttrs (drv allArgs) // {
95 overrideScope = f:
96 let newScope = mkScope (fix' (extends f scope.__unfix__));
97 # note that we have to be careful here: `allArgs` includes the auto-arguments that
98 # weren't manually specified. If we would just pass `allArgs` to the recursive call here,
99 # then we wouldn't look up any packages in the scope in the next interation, because it
100 # appears as if all arguments were already manually passed, so the scope change would do
101 # nothing.
102 in callPackageWithScope newScope drv manualArgs;
103 };
104 in lib.makeOverridable drvScope (auto // manualArgs);
105
106 mkScope = scope: let
107 ps = pkgs.__splicedPackages;
108 scopeSpliced = pkgs.splicePackages {
109 pkgsBuildBuild = scope.buildHaskellPackages.buildHaskellPackages;
110 pkgsBuildHost = scope.buildHaskellPackages;
111 pkgsBuildTarget = {};
112 pkgsHostHost = {};
113 pkgsHostTarget = scope;
114 pkgsTargetTarget = {};
115 } // {
116 # Don't splice these
117 inherit (scope) ghc buildHaskellPackages;
118 };
119 in ps // ps.xorg // ps.gnome2 // { inherit stdenv; } // scopeSpliced;
120 defaultScope = mkScope self;
121 callPackage = drv: args: callPackageWithScope defaultScope drv args;
122
123 # Use cabal2nix to create a default.nix for the package sources found at 'src'.
124 haskellSrc2nix = { name, src, sha256 ? null, extraCabal2nixOptions ? "" }:
125 let
126 sha256Arg = if sha256 == null then "--sha256=" else ''--sha256="${sha256}"'';
127 in buildPackages.runCommand "cabal2nix-${name}" {
128 nativeBuildInputs = [ buildPackages.cabal2nix-unwrapped ];
129 preferLocalBuild = true;
130 allowSubstitutes = false;
131 LANG = "en_US.UTF-8";
132 LOCALE_ARCHIVE = pkgs.lib.optionalString (buildPlatform.libc == "glibc") "${buildPackages.glibcLocales}/lib/locale/locale-archive";
133 } ''
134 export HOME="$TMP"
135 mkdir -p "$out"
136 cabal2nix --compiler=${self.ghc.haskellCompilerName} --system=${hostPlatform.config} ${sha256Arg} "${src}" ${extraCabal2nixOptions} > "$out/default.nix"
137 '';
138
139 all-cabal-hashes-component = name: version: buildPackages.runCommand "all-cabal-hashes-component-${name}-${version}" {} ''
140 tar --wildcards -xzvf ${all-cabal-hashes} \*/${name}/${version}/${name}.{json,cabal}
141 mkdir -p $out
142 mv */${name}/${version}/${name}.{json,cabal} $out
143 '';
144
145 hackage2nix = name: version: let component = all-cabal-hashes-component name version; in self.haskellSrc2nix {
146 name = "${name}-${version}";
147 sha256 = ''$(sed -e 's/.*"SHA256":"//' -e 's/".*$//' "${component}/${name}.json")'';
148 src = "${component}/${name}.cabal";
149 };
150
151 # Adds a nix file as an input to the haskell derivation it
152 # produces. This is useful for callHackage / callCabal2nix to
153 # prevent the generated default.nix from being garbage collected
154 # (requiring it to be frequently rebuilt), which can be an
155 # annoyance.
156 callPackageKeepDeriver = src: args:
157 overrideCabal (orig: {
158 preConfigure = ''
159 # Generated from ${src}
160 ${orig.preConfigure or ""}
161 '';
162 passthru = orig.passthru or {} // {
163 # When using callCabal2nix or callHackage, it is often useful
164 # to debug a failure by inspecting the Nix expression
165 # generated by cabal2nix. This can be accessed via this
166 # cabal2nixDeriver field.
167 cabal2nixDeriver = src;
168 };
169 }) (self.callPackage src args);
170
171in package-set { inherit pkgs lib callPackage; } self // {
172
173 inherit mkDerivation callPackage haskellSrc2nix hackage2nix buildHaskellPackages;
174
175 inherit (haskellLib) packageSourceOverrides;
176
177 # callHackage :: Text -> Text -> AttrSet -> HaskellPackage
178 #
179 # e.g., while overriding a package set:
180 # '... foo = self.callHackage "foo" "1.5.3" {}; ...'
181 callHackage = name: version: callPackageKeepDeriver (self.hackage2nix name version);
182
183 # callHackageDirect
184 # :: { pkg :: Text, ver :: Text, sha256 :: Text }
185 # -> AttrSet
186 # -> HaskellPackage
187 #
188 # This function does not depend on all-cabal-hashes and therefore will work
189 # for any version that has been released on hackage as opposed to only
190 # versions released before whatever version of all-cabal-hashes you happen
191 # to be currently using.
192 callHackageDirect = {pkg, ver, sha256}:
193 let pkgver = "${pkg}-${ver}";
194 in self.callCabal2nix pkg (pkgs.fetchzip {
195 url = "mirror://hackage/${pkgver}/${pkgver}.tar.gz";
196 inherit sha256;
197 });
198
199 # Creates a Haskell package from a source package by calling cabal2nix on the source.
200 callCabal2nixWithOptions = name: src: extraCabal2nixOptions: args:
201 let
202 filter = path: type:
203 pkgs.lib.hasSuffix ".cabal" path ||
204 baseNameOf path == "package.yaml";
205 expr = self.haskellSrc2nix {
206 inherit name extraCabal2nixOptions;
207 src = if pkgs.lib.canCleanSource src
208 then pkgs.lib.cleanSourceWith { inherit src filter; }
209 else src;
210 };
211 in overrideCabal (orig: {
212 inherit src;
213 }) (callPackageKeepDeriver expr args);
214
215 callCabal2nix = name: src: args: self.callCabal2nixWithOptions name src "" args;
216
217 # : { root : Path
218 # , name : Defaulted String
219 # , source-overrides : Defaulted (Either Path VersionNumber)
220 # , overrides : Defaulted (HaskellPackageOverrideSet)
221 # , modifier : Defaulted
222 # , returnShellEnv : Defaulted
223 # , withHoogle : Defaulted
224 # , cabal2nixOptions : Defaulted
225 # } -> NixShellAwareDerivation
226 #
227 # Given a path to a haskell package directory, an optional package name
228 # which defaults to the base name of the path, an optional set of source
229 # overrides as appropriate for the 'packageSourceOverrides' function, an
230 # optional set of arbitrary overrides, and an optional haskell package
231 # modifier, return a derivation appropriate for nix-build or nix-shell to
232 # build that package.
233 #
234 # If 'returnShellEnv' is true this returns a derivation which will give you
235 # an environment suitable for developing the listed packages with an
236 # incremental tool like cabal-install.
237 #
238 # If 'withHoogle' is true (the default if a shell environment is requested)
239 # then 'ghcWithHoogle' is used to generate the derivation (instead of
240 # 'ghcWithPackages'), see the documentation there for more information.
241 #
242 # 'cabal2nixOptions' can contain extra command line arguments to pass to
243 # 'cabal2nix' when generating the package derivation, for example setting
244 # a cabal flag with '--flag=myflag'.
245 developPackage =
246 { root
247 , name ? if builtins.typeOf root == "path" then builtins.baseNameOf root else ""
248 , source-overrides ? {}
249 , overrides ? self: super: {}
250 , modifier ? drv: drv
251 , returnShellEnv ? pkgs.lib.inNixShell
252 , withHoogle ? returnShellEnv
253 , cabal2nixOptions ? "" }:
254 let drv =
255 (extensible-self.extend
256 (pkgs.lib.composeExtensions
257 (self.packageSourceOverrides source-overrides)
258 overrides))
259 .callCabal2nixWithOptions name root cabal2nixOptions {};
260 in if returnShellEnv
261 then (modifier drv).envFunc {inherit withHoogle;}
262 else modifier drv;
263
264 # This can be used to easily create a derivation containing GHC and the specified set of Haskell packages.
265 #
266 # Example:
267 # $ nix-shell -p 'haskellPackages.ghcWithPackages (hpkgs: [ hpkgs.mtl hpkgs.lens ])'
268 # $ ghci # in the nix-shell
269 # Prelude > import Control.Lens
270 #
271 # GHC is setup with a package database with all the specified Haskell packages.
272 #
273 # ghcWithPackages :: (HaskellPkgSet -> [ HaskellPkg ]) -> Derivation
274 ghcWithPackages = self.callPackage ./with-packages-wrapper.nix {
275 haskellPackages = self;
276 };
277
278
279 # Put 'hoogle' into the derivation's PATH with a database containing all
280 # the package's dependencies; run 'hoogle server --local' in a shell to
281 # host a search engine for the dependencies.
282 #
283 # Example usage:
284 # $ nix-shell -p 'haskellPackages.hoogleWithPackages (p: [ p.mtl p.lens ])'
285 # [nix-shell] $ hoogle server
286 #
287 # hoogleWithPackages :: (HaskellPkgSet -> [ HaskellPkg ]) -> Derivation
288 #
289 # To reload the Hoogle server automatically on .cabal file changes try
290 # this:
291 # echo *.cabal | entr -r -- nix-shell --run 'hoogle server --local'
292 hoogleWithPackages = self.callPackage ./hoogle.nix {
293 haskellPackages = self;
294 };
295 hoogleLocal =
296 { packages ? [] }:
297 lib.warn "hoogleLocal is deprecated, use hoogleWithPackages instead" (
298 self.hoogleWithPackages (_: packages)
299 );
300 # This is like a combination of ghcWithPackages and hoogleWithPackages.
301 # It provides a derivation containing both GHC and Hoogle with an index of
302 # the given Haskell package database.
303 #
304 # Example:
305 # $ nix-shell -p 'haskellPackages.ghcWithHoogle (hpkgs: [ hpkgs.conduit hpkgs.lens ])'
306 #
307 # ghcWithHoogle :: (HaskellPkgSet -> [ HaskellPkg ]) -> Derivation
308 ghcWithHoogle = self.ghcWithPackages.override {
309 withHoogle = true;
310 };
311
312 # Returns a derivation whose environment contains a GHC with only
313 # the dependencies of packages listed in `packages`, not the
314 # packages themselves. Using nix-shell on this derivation will
315 # give you an environment suitable for developing the listed
316 # packages with an incremental tool like cabal-install.
317 #
318 # In addition to the "packages" arg and "withHoogle" arg, anything that
319 # can be passed into stdenv.mkDerivation can be included in the input attrset
320 #
321 # # default.nix
322 # with import <nixpkgs> {};
323 # haskellPackages.extend (haskell.lib.compose.packageSourceOverrides {
324 # frontend = ./frontend;
325 # backend = ./backend;
326 # common = ./common;
327 # })
328 #
329 # # shell.nix
330 # let pkgs = import <nixpkgs> {} in
331 # (import ./.).shellFor {
332 # packages = p: [p.frontend p.backend p.common];
333 # withHoogle = true;
334 # buildInputs = [ pkgs.python pkgs.cabal-install ];
335 # }
336 #
337 # -- cabal.project
338 # packages:
339 # frontend/
340 # backend/
341 # common/
342 #
343 # bash$ nix-shell --run "cabal new-build all"
344 # bash$ nix-shell --run "python"
345 shellFor =
346 { # Packages to create this development shell for. These are usually
347 # your local packages.
348 packages
349 , # Whether or not to generate a Hoogle database for all the
350 # dependencies.
351 withHoogle ? false
352 , # Whether or not to include benchmark dependencies of your local
353 # packages. You should set this to true if you have benchmarks defined
354 # in your local packages that you want to be able to run with cabal benchmark
355 doBenchmark ? false
356 # An optional function that can modify the generic builder arguments
357 # for the fake package that shellFor uses to construct its environment.
358 #
359 # Example:
360 # let
361 # # elided...
362 # haskellPkgs = pkgs.haskell.packages.ghc884.override (hpArgs: {
363 # overrides = pkgs.lib.composeExtensions (hpArgs.overrides or (_: _: { })) (
364 # _hfinal: hprev: {
365 # mkDerivation = args: hprev.mkDerivation ({
366 # doCheck = false;
367 # doBenchmark = false;
368 # doHoogle = true;
369 # doHaddock = true;
370 # enableLibraryProfiling = false;
371 # enableExecutableProfiling = false;
372 # } // args);
373 # }
374 # );
375 # });
376 # in
377 # haskellPkgs.shellFor {
378 # packages = p: [ p.foo ];
379 # genericBuilderArgsModifier = args: args // { doCheck = true; doBenchmark = true };
380 # }
381 #
382 # This will disable tests and benchmarks for everything in "haskellPkgs"
383 # (which will invalidate the binary cache), and then re-enable them
384 # for the "shellFor" environment (ensuring that any test/benchmark
385 # dependencies for "foo" will be available within the nix-shell).
386 , genericBuilderArgsModifier ? (args: args)
387
388 # Extra dependencies, in the form of cabal2nix build attributes.
389 #
390 # An example use case is when you have Haskell scripts that use
391 # libraries that don't occur in your packages' dependencies.
392 #
393 # Example:
394 #
395 # extraDependencies = p: {
396 # libraryHaskellDepends = [ p.releaser ];
397 # };
398 , extraDependencies ? p: {}
399 , ...
400 } @ args:
401 let
402 # A list of the packages we want to build a development shell for.
403 # This is a list of Haskell package derivations.
404 selected = packages self;
405
406 # This is a list of attribute sets, where each attribute set
407 # corresponds to the build inputs of one of the packages input to shellFor.
408 #
409 # Each attribute has keys like buildDepends, executableHaskellDepends,
410 # testPkgconfigDepends, etc. The values for the keys of the attribute
411 # set are lists of dependencies.
412 #
413 # Example:
414 # cabalDepsForSelected
415 # => [
416 # # This may be the attribute set corresponding to the `backend`
417 # # package in the example above.
418 # { buildDepends = [ gcc ... ];
419 # libraryHaskellDepends = [ lens conduit ... ];
420 # ...
421 # }
422 # # This may be the attribute set corresponding to the `common`
423 # # package in the example above.
424 # { testHaskellDepends = [ tasty hspec ... ];
425 # libraryHaskellDepends = [ lens aeson ];
426 # benchmarkHaskellDepends = [ criterion ... ];
427 # ...
428 # }
429 # ...
430 # ]
431 cabalDepsForSelected = map (p: p.getCabalDeps) selected;
432
433 # A predicate that takes a derivation as input, and tests whether it is
434 # the same as any of the `selected` packages.
435 #
436 # Returns true if the input derivation is not in the list of `selected`
437 # packages.
438 #
439 # isNotSelected :: Derivation -> Bool
440 #
441 # Example:
442 #
443 # isNotSelected common [ frontend backend common ]
444 # => false
445 #
446 # isNotSelected lens [ frontend backend common ]
447 # => true
448 isNotSelected = input: pkgs.lib.all (p: input.outPath or null != p.outPath) selected;
449
450 # A function that takes a list of list of derivations, filters out all
451 # the `selected` packages from each list, and concats the results.
452 #
453 # zipperCombinedPkgs :: [[Derivation]] -> [Derivation]
454 #
455 # Example:
456 # zipperCombinedPkgs [ [ lens conduit ] [ aeson frontend ] ]
457 # => [ lens conduit aeson ]
458 #
459 # Note: The reason this isn't just the function `pkgs.lib.concat` is
460 # that we need to be careful to remove dependencies that are in the
461 # `selected` packages.
462 #
463 # For instance, in the above example, if `common` is a dependency of
464 # `backend`, then zipperCombinedPkgs needs to be careful to filter out
465 # `common`, because cabal will end up ignoring that built version,
466 # assuming new-style commands.
467 zipperCombinedPkgs = vals:
468 pkgs.lib.concatMap
469 (drvList: pkgs.lib.filter isNotSelected drvList)
470 vals;
471
472 # Zip `cabalDepsForSelected` into a single attribute list, combining
473 # the derivations in all the individual attributes.
474 #
475 # Example:
476 # packageInputs
477 # => # Assuming the value of cabalDepsForSelected is the same as
478 # # the example in cabalDepsForSelected:
479 # { buildDepends = [ gcc ... ];
480 # libraryHaskellDepends = [ lens conduit aeson ... ];
481 # testHaskellDepends = [ tasty hspec ... ];
482 # benchmarkHaskellDepends = [ criterion ... ];
483 # ...
484 # }
485 #
486 # See the Note in `zipperCombinedPkgs` for what gets filtered out from
487 # each of these dependency lists.
488 packageInputs =
489 pkgs.lib.zipAttrsWith (_name: zipperCombinedPkgs) (cabalDepsForSelected ++ [ (extraDependencies self) ]);
490
491 # A attribute set to pass to `haskellPackages.mkDerivation`.
492 #
493 # The important thing to note here is that all the fields from
494 # packageInputs are set correctly.
495 genericBuilderArgs = {
496 pname =
497 if pkgs.lib.length selected == 1
498 then (pkgs.lib.head selected).name
499 else "packages";
500 version = "0";
501 license = null;
502 }
503 // packageInputs
504 // pkgs.lib.optionalAttrs doBenchmark {
505 # `doBenchmark` needs to explicitly be set here because haskellPackages.mkDerivation defaults it to `false`. If the user wants benchmark dependencies included in their development shell, it has to be explicitly enabled here.
506 doBenchmark = true;
507 };
508
509 # This is a pseudo Haskell package derivation that contains all the
510 # dependencies for the packages in `selected`.
511 #
512 # This is a derivation created with `haskellPackages.mkDerivation`.
513 #
514 # pkgWithCombinedDeps :: HaskellDerivation
515 pkgWithCombinedDeps = self.mkDerivation (genericBuilderArgsModifier genericBuilderArgs);
516
517 # The derivation returned from `envFunc` for `pkgWithCombinedDeps`.
518 #
519 # This is a derivation that can be run with `nix-shell`. It provides a
520 # GHC with a package database with all the dependencies of our
521 # `selected` packages.
522 #
523 # This is a derivation created with `stdenv.mkDerivation` (not
524 # `haskellPackages.mkDerivation`).
525 #
526 # pkgWithCombinedDepsDevDrv :: Derivation
527 pkgWithCombinedDepsDevDrv = pkgWithCombinedDeps.envFunc { inherit withHoogle; };
528
529 mkDerivationArgs = builtins.removeAttrs args [ "genericBuilderArgsModifier" "packages" "withHoogle" "doBenchmark" "extraDependencies" ];
530
531 in pkgWithCombinedDepsDevDrv.overrideAttrs (old: mkDerivationArgs // {
532 nativeBuildInputs = old.nativeBuildInputs ++ mkDerivationArgs.nativeBuildInputs or [];
533 buildInputs = old.buildInputs ++ mkDerivationArgs.buildInputs or [];
534 });
535
536 ghc = ghc // {
537 withPackages = self.ghcWithPackages;
538 withHoogle = self.ghcWithHoogle;
539 };
540
541 /*
542 Run `cabal sdist` on a source.
543
544 Unlike `haskell.lib.sdistTarball`, this does not require any dependencies
545 to be present, as it uses `cabal-install` instead of building `Setup.hs`.
546 This makes `cabalSdist` faster than `sdistTarball`.
547 */
548 cabalSdist = {
549 src,
550 name ? if src?name then "${src.name}-sdist.tar.gz" else "source.tar.gz"
551 }:
552 pkgs.runCommandLocal name
553 {
554 inherit src;
555 nativeBuildInputs = [ buildHaskellPackages.cabal-install ];
556 dontUnpack = false;
557 } ''
558 unpackPhase
559 cd "''${sourceRoot:-.}"
560 patchPhase
561 mkdir out
562 HOME=$PWD cabal sdist --output-directory out
563 mv out/*.tar.gz $out
564 '';
565
566 /*
567 Like `haskell.lib.buildFromSdist`, but using `cabal sdist` instead of
568 building `./Setup`.
569
570 Unlike `haskell.lib.buildFromSdist`, this does not require any dependencies
571 to be present. This makes `buildFromCabalSdist` faster than `haskell.lib.buildFromSdist`.
572 */
573 buildFromCabalSdist = pkg:
574 haskellLib.overrideSrc
575 {
576 src = self.cabalSdist { inherit (pkg) src; };
577 version = pkg.version;
578 }
579 pkg;
580
581 }