on 147cf21 help sign in
haskell.nix/default.nix
253 lines 10 kB history raw
1{ pkgs ? import nixpkgs nixpkgsArgs
2# Use a pinned nixpkgs rather than the one on NIX_PATH
3, nixpkgs ? ./nixpkgs
4# Provide args to the nixpkgs instantiation.
5, nixpkgsArgs ? {}
6# You can provide different pins for hackage.nix and stackage.nix if required.
7# It's also possible to override these sources with NIX_PATH.
8, hackageSourceJSON ? ./hackage-src.json
9, stackageSourceJSON ? ./stackage-src.json
10}:
12let
13 # pkg-def's may reference boot packages, but those
14 # are not guaranteed to be available on hackage, as
15 # it is a manual process. They might eventually show
16 # up much later on hackage; but are not installable
17 # anyway. Therefore we just strip them out of the
18 # pkg-def's packages.
19 #
20 # Note: these will need to be provided by alternative
21 # means outside of hackage.
22 boot-pkgs = [ "rts" "ghc" "ghc-boot-th" "ghc-boot" "ghci"
23 "ghc-heap" # since ghc 8.6.
24 "iserv" "libiserv" "iserv-proxy"
25 ];
26 strip-pkg-def = pkgs: pkg-def: hackage: with pkgs.lib;
27 mapAttrs (k: v: if k == "packages"
28 then filterAttrs (k: _: !(builtins.elem k boot-pkgs)) v
29 else v)
30 (pkg-def hackage);
32 # ghc hackage patches.
33 # these are patches that turn hackage packages into the same as the ones
34 # ghc ships with the supposedly same version. See GHC Track Issue: 16199
35 ghcHackagePatches = import ./patches;
37 compat = import ./lib/compat.nix;
39 # Utility function for downloading a pinned git repo, that can be
40 # overridden with NIX_PATH.
41 fetchExternal = import ./lib/fetch-external.nix;
43 # Function for cleaning haskell source directories pulled from iohk-nix
44 cleanSourceHaskell = pkgs.callPackage ./lib/clean-source-haskell.nix {};
46 # All packages from Hackage as Nix expressions
47 hackageSrc = fetchExternal {
48 name = "hackage-exprs-source";
49 specJSON = hackageSourceJSON;
50 override = "hackage";
51 };
52 hackage = import hackageSrc;
54 # Contains the hashes of the cabal 01-index.tar.gz for given
55 # index states. Starting from April 1st 2019.
56 indexStateHashesPath = hackageSrc + "/index-state-hashes.nix";
58 # The set of all Stackage snapshots
59 stackageSrc = fetchExternal {
60 name = "stackage-snapshot-source";
61 specJSON = stackageSourceJSON;
62 override = "stackage";
63 };
64 stackage = import stackageSrc;
66 packages = pkgs: self: (rec {
67 inherit pkgs; # Make pkgs available (it is the underlying nixpkgs)
69 # Packages built to run on the build platform, not the host platform
70 buildPackages = pkgs.buildPackages.lib.makeScope pkgs.buildPackages.newScope
71 (packages pkgs.buildPackages);
73 # Utility functions for working with the component builder.
74 haskellLib = let hl = import ./lib { inherit (pkgs) lib; haskellLib = hl; }; in hl;
76 # Create a Haskell package set based on a cabal build plan (plan-to-nix)
77 # and Nix expressions representing cabal packages (cabal-to-nix).
78 mkPkgSet =
79 { pkg-def # Base package set. Either from stackage (via stack-to-nix) or from a cabal projects plan file (via plan-to-nix)
80 , pkg-def-extras ? [] # Additional packages to augment the Base package set `pkg-def` with.
81 , modules ? []
82 }@args:
84 import ./package-set.nix (args // {
85 inherit hackage pkgs;
86 pkg-def = strip-pkg-def pkgs pkg-def;
87 });
89 # Create a Haskell package set based on a Stack configuration.
90 mkStackPkgSet =
91 { stack-pkgs # Path to the output of stack-to-nix
92 , pkg-def-extras ? []
93 , modules ? []
94 }@args:
96 let
97 # The Stackage release referenced in the stack config
98 pkg-def = stackage.${stack-pkgs.resolver} or (throw ''
99 This version of stackage.nix does not know about the Stackage resolver ${stack-pkgs.resolver}.
100 You may need to update haskell.nix to one that includes a newer stackage.nix.
101 '');
102 # The compiler referenced in the stack config
103 compiler = (stack-pkgs.extras hackage).compiler or (pkg-def hackage).compiler;
104 patchesModule = ghcHackagePatches.${compiler.nix-name} or {};
105 in self.mkPkgSet {
106 inherit pkg-def;
107 pkg-def-extras = [ stack-pkgs.extras ] ++ pkg-def-extras;
108 # set doExactConfig = true. The stackage set should be consistent
109 # and we should trust stackage here!
110 modules = [ { doExactConfig = true; } patchesModule ] ++ stack-pkgs.modules or [] ++ modules;
111 };
113 # Create a Haskell package set based on a Cabal configuration.
114 mkCabalProjectPkgSet =
115 { plan-pkgs # Path to the output of plan-to-nix
116 , pkg-def-extras ? []
117 , modules ? []
118 }@args:
120 let
121 pkg-def = plan-pkgs.pkgs;
122 # The compiler referenced in the stack config
123 compiler = (plan-pkgs.extras hackage).compiler or (pkg-def hackage).compiler;
124 patchesModule = ghcHackagePatches.${compiler.nix-name} or {};
125 in self.mkPkgSet {
126 inherit pkg-def;
127 pkg-def-extras = [ plan-pkgs.extras ] ++ pkg-def-extras;
128 # set doExactConfig = true, as we trust cabals resolution for
129 # the plan.
130 modules = [ { doExactConfig = true; } patchesModule ] ++ plan-pkgs.modules or [] ++ modules;
131 };
133 # Package sets for all stackage snapshots.
134 snapshots = self.callPackage ./snapshots.nix {};
135 # Pick a recent LTS snapshot to be our "default" package set.
136 haskellPackages = self.snapshots."lts-13.26";
138 # Programs for generating Nix expressions from Cabal and Stack
139 # files. This version of nix-tools may be cross compiled.
140 # We probably never want to actually cross compile nix-tools on
141 # it's own.
142 nix-tools-cross-compiled = pkgs.callPackage ./nix-tools {
143 inherit fetchExternal cleanSourceHaskell;
144 hpack = pkgs.haskell.lib.justStaticExecutables
145 (pkgs.haskellPackages.hpack);
146 inherit (self) mkCabalProjectPkgSet;
147 };
148 # While `nix-tools-cross-compiled` may be cross compiled,
149 # getting it from `buildPackages` we should get
150 # nix-tools suitable for running on the build system.
151 nix-tools = buildPackages.nix-tools-cross-compiled;
152 # TODO perhaps there is a cleaner way to get a suitable nix-tools.
154 # Snapshots of Hackage and Stackage, converted to Nix expressions,
155 # regularly updated.
156 inherit hackageSrc stackageSrc;
157 inherit hackage stackage;
158 inherit indexStateHashesPath;
160 # Make this handy overridable fetch function available.
161 inherit fetchExternal;
163 # Function for cleaning haskell source diretories.
164 inherit cleanSourceHaskell;
166 # Produce a fixed output derivation from a moving target (hackage index tarball)
167 hackageTarball = { index-state, sha256 }:
168 assert sha256 != null;
169 pkgs.fetchurl {
170 name = "01-index.tar.gz-at-${builtins.replaceStrings [":"] [""] index-state}";
171 url = "https://hackage.haskell.org/01-index.tar.gz";
172 downloadToTemp = true;
173 postFetch = "${self.nix-tools}/bin/truncate-index -o $out -i $downloadedFile -s ${index-state}";
175 outputHashAlgo = "sha256";
176 outputHash = sha256;
177 };
179 mkLocalHackageRepo = import ./mk-local-hackage-repo { inherit (self) hackageTarball; inherit pkgs; };
181 dotCabal = { index-state, sha256, cabal-install ? pkgs.cabal-install }@args:
182 pkgs.runCommand "dot-cabal-at-${builtins.replaceStrings [":"] [""] index-state}" { nativeBuildInputs = [ cabal-install ]; } ''
183 mkdir -p $out/.cabal
184 cat <<EOF > $out/.cabal/config
185 repository cached
186 url: file:${self.mkLocalHackageRepo
187 (builtins.removeAttrs args ["cabal-install"])}
188 secure: True
189 root-keys:
190 key-threshold: 0
191 EOF
192 mkdir -p $out/.cabal/packages/cached
193 HOME=$out cabal new-update cached
194 '';
196 update-index-state-hashes = self.callPackage ./scripts/update-index-state-hashes.nix {};
198 # Function to call stackToNix
199 callStackToNix = import ./lib/call-stack-to-nix.nix {
200 pkgs = buildPackages.pkgs;
201 inherit (buildPackages.pkgs) runCommand;
202 inherit (buildPackages) nix-tools;
203 };
205 # Takes a haskell src directory runs cabal new-configure and plan-to-nix.
206 # Resulting nix files are added to nix-plan subdirectory.
207 callCabalProjectToNix = import ./lib/call-cabal-project-to-nix.nix {
208 index-state-hashes = import indexStateHashesPath;
209 inherit (buildPackages) dotCabal haskellLib;
210 pkgs = buildPackages.pkgs;
211 inherit (buildPackages.pkgs.haskellPackages) hpack;
212 inherit (buildPackages.pkgs) runCommand cabal-install ghc symlinkJoin cacert;
213 inherit (buildPackages) nix-tools;
214 };
216 # Loads a plan and filters the package directories using cleanSourceWith
217 importAndFilterProject = import ./lib/import-and-filter-project.nix {
218 inherit pkgs haskellLib;
219 };
221 # References to the unpacked sources, for caching in a Hydra jobset.
222 source-pins = self.callPackage ./lib/make-source-pins.nix {
223 sources = [ hackageSrc stackageSrc pkgs.path ];
224 };
226 # Build a specific package (name, version) against a given index-stage
227 # from hackage. This is useful if you want to build an executable from
228 # a given package.
229 # NB: If no explicit index-state is provided the most recent one from
230 # the index-state-hashes is used. This guarantees reproducability wrt
231 # to the haskell.nix revision. If reproducability beyond haskell.nix
232 # is required, a specific index-state should be provided!
233 hackage-package =
234 { name
235 , version
236 , index-state ? builtins.trace "Using latest index state!" pkgs.lib.last (builtins.attrNames (import indexStateHashesPath))
237 }:
238 let tarball = pkgs.fetchurl {
239 url = "mirror://hackage/${name}-${version}.tar.gz";
240 inherit (hackage.${name}.${version}) sha256; };
241 in let src = buildPackages.pkgs.runCommand "${name}-${version}-src" { } ''
242 tmp=$(mktemp -d)
243 cd $tmp
244 tar xzf ${tarball}
245 mv "${name}-${version}" $out
246 '';
247 in let plan-pkgs = (callCabalProjectToNix { inherit src; index-state = builtins.trace "Using index-state: ${index-state}" index-state; }).pkgs;
248 in let pkg-set = mkCabalProjectPkgSet { inherit plan-pkgs; };
249 in pkg-set.config.hsPkgs.${name};
250 });
252in
253 pkgs.lib.makeScope pkgs.newScope (packages pkgs)