on e94de40 help sign in
286 lines 11 kB history raw
1# The plan (that is, a package set description like an LTS set or a
2# plan.nix (derived from plan.json)) will produce a structure that
3# looks like, which is stored in config.plan.pkg-def:
4#
5# { packages = { "package" = { revision = hackageConfigs.$package.$version.revisions.default;
6# flags = { flag1 = true; flag2 = false; ... }; };
7# ... };
8# compiler = { version = "X.Y.Z"; nix-name ="ghcXYZ";
9# # packages that come bundled with the compiler
10# packages = { "bytestring" = "a.b.c.d"; ... }; };
11# }
13{ lib, config, pkgs, pkgconfPkgs, haskellLib, ... }:
15with lib;
16with types;
18let
19 # dealing with str is a bit annoying especially with `nullOr str` as that apparently defaults to ""
20 # instead of null :shrug:. This then messes with our option inheritance logic.
21 # Hence we have a uniqueStr type that ensures multiple identically defined options are collapsed
22 # without raising an error. And a way to fetch default options that will retain `null` if the
23 # option is not defined or "".
24 getDefaultOrNull = def: key: if def ? ${key} && def.${key} != "" then def.${key} else null;
25 mergeUniqueOption = locs: defs: let
26 mergeOneOption = loc: defs':
27 # we ignore "" as optionalString, will default to "".
28 let defs = filter (x: x.value != "") defs'; in
29 if defs == [] then null
30 else if length defs != 1 then
31 throw "The unique option `${showOption loc}' is defined multiple times, in ${showFiles (getFiles defs)}; with values `${concatStringsSep "', `" (map (x: x.value) defs)}'."
32 else (head defs).value;
33 in mergeOneOption locs (lists.unique defs);
34 uniqueStr = str // { merge = mergeUniqueOption; };
36 # This is just like listOf, except that it filters out all null elements.
37 listOfFilteringNulls = elemType: listOf elemType // {
38 # Mostly copied from nixpkgs/lib/types.nix
39 merge = loc: defs:
40 map (x: x.value) (filter (x: x ? value && x.value != null) (concatLists (imap1 (n: def:
41 if isList def.value then
42 imap1 (m: def':
43 (mergeDefinitions
44 (loc ++ ["[definition ${toString n}-entry ${toString m}]"])
45 elemType
46 [{ inherit (def) file; value = def'; }]
47 ).optionalValue
48 ) def.value
49 else
50 throw "The option value `${showOption loc}` in `${def.file}` is not a list.") defs)));
51 };
53 componentOptions = def: {
54 buildable = mkOption {
55 type = bool;
56 default = true;
57 };
58 configureFlags = mkOption {
59 type = listOfFilteringNulls str;
60 default = (def.configureFlags or []);
61 };
62 setupBuildFlags = mkOption {
63 type = listOfFilteringNulls str;
64 default = (def.setupBuildFlags or []);
65 };
66 testFlags = mkOption {
67 type = listOfFilteringNulls str;
68 default = (def.testFlags or []);
69 };
70 setupInstallFlags = mkOption {
71 type = listOfFilteringNulls str;
72 default = (def.setupInstallFlags or []);
73 };
74 setupHaddockFlags = mkOption {
75 type = listOfFilteringNulls str;
76 default = (def.setupHaddockFlags or []);
77 };
78 doExactConfig = mkOption {
79 type = bool;
80 default = (def.doExactConfig or false);
81 };
82 doCheck = mkOption {
83 type = bool;
84 default = (def.doCheck or true);
85 };
86 doCrossCheck = mkOption {
87 description = "Run doCheck also in cross compilation settings. This can be tricky as the test logic must know how to run the tests on the target.";
88 type = bool;
89 default = (def.doCrossCheck or false);
90 };
91 doHaddock = mkOption {
92 description = "Enable building of the Haddock documentation from the annotated Haskell source code.";
93 type = bool;
94 default = (def.doHaddock or true);
95 };
96 doHoogle = mkOption {
97 description = "Also build a hoogle index.";
98 type = bool;
99 default = (def.doHoogle or true);
100 };
101 doHyperlinkSource = mkOption {
102 description = "Link documentation to the source code.";
103 type = bool;
104 default = (def.doHyperlinkSource or true);
105 };
106 doQuickjump = mkOption {
107 description = "Generate an index for interactive documentation navigation.";
108 type = bool;
109 default = (def.doQuickjump or true);
110 };
111 doCoverage = mkOption {
112 description = "Enable production of test coverage reports.";
113 type = bool;
114 default = (def.doCoverage or false);
115 };
116 dontPatchELF = mkOption {
117 description = "If set, the patchelf command is not used to remove unnecessary RPATH entries. Only applies to Linux.";
118 type = bool;
119 default = (def.dontPatchELF or true);
120 };
121 dontStrip = mkOption {
122 description = "If set, libraries and executables are not stripped.";
123 type = bool;
124 default = (def.dontStrip or true);
125 };
126 enableDeadCodeElimination = mkOption {
127 description = "If set, enables split sections for link-time dead-code stripping. Only applies to Linux";
128 type = bool;
129 default = (def.enableDeadCodeElimination or true);
130 };
131 enableStatic = mkOption {
132 description = "If set, enables building static libraries and executables.";
133 type = bool;
134 default = (def.enableStatic or true);
135 };
136 enableShared = mkOption {
137 description = "If set, enables building shared libraries.";
138 type = bool;
139 default = (def.enableShared or true);
140 };
141 shellHook = mkOption {
142 description = "Hook to run when entering a shell";
143 type = unspecified; # Can be either a string or a function
144 default = (def.shellHook or "");
145 };
146 enableLibraryProfiling = mkOption {
147 type = bool;
148 default = (def.enableLibraryProfiling or false);
149 };
151 enableSeparateDataOutput = mkOption {
152 type = bool;
153 default = (def.enableSeparateDataOutput or false);
154 };
156 enableExecutableProfiling = mkOption {
157 type = bool;
158 default = (def.enableExecutableProfiling or false);
159 };
161 profilingDetail = mkOption {
162 type = nullOr uniqueStr;
163 default = (def.profilingDetail or "default");
164 };
166 keepSource = mkOption {
167 type = bool;
168 default = (def.keepSource or false);
169 description = "Keep component source in the store in a `source` output";
170 };
171 };
172 packageOptions = def: componentOptions def // {
173 preUnpack = mkOption {
174 type = nullOr lines;
175 default = (def.preUnpack or null);
176 };
177 postUnpack = mkOption {
178 type = nullOr uniqueStr;
179 default = getDefaultOrNull def "postUnpack";
180 };
181 preConfigure = mkOption {
182 type = nullOr uniqueStr;
183 default = getDefaultOrNull def "preConfigure";
184 };
185 postConfigure = mkOption {
186 type = nullOr uniqueStr;
187 default = getDefaultOrNull def "postConfigure";
188 };
189 preBuild = mkOption {
190 type = nullOr uniqueStr;
191 default = getDefaultOrNull def "preBuild";
192 };
193 postBuild = mkOption {
194 type = nullOr uniqueStr;
195 default = getDefaultOrNull def "postBuild";
196 };
197 preCheck = mkOption {
198 type = nullOr uniqueStr;
199 default = getDefaultOrNull def "preCheck";
200 };
201 # Wrapper for test executable run in checkPhase
202 testWrapper = mkOption {
203 type = listOfFilteringNulls str;
204 default = def.testWrapper or [];
205 description = "A command to run for executing tests in checkPhase, which takes the original test command as its arguments.";
206 example = "echo";
207 };
208 postCheck = mkOption {
209 type = nullOr uniqueStr;
210 default = getDefaultOrNull def "postCheck";
211 };
212 preInstall = mkOption {
213 type = nullOr uniqueStr;
214 default = getDefaultOrNull def "preInstall";
215 };
216 postInstall = mkOption {
217 type = nullOr uniqueStr;
218 default = getDefaultOrNull def "postInstall";
219 };
220 preHaddock = mkOption {
221 type = nullOr uniqueStr;
222 default = getDefaultOrNull def "preHaddock";
223 };
224 postHaddock = mkOption {
225 type = nullOr uniqueStr;
226 default = getDefaultOrNull def "postHaddock";
227 };
228 hardeningDisable = mkOption {
229 type = listOfFilteringNulls str;
230 default = (def.hardeningDisable or []);
231 };
232 };
235in {
236 # Global options. These are passed down to the package level, and from there to the
237 # component level, unless specifically overridden. Depending on the flag flags are
238 # combined or replaced. We seed the package Options with an empty set forcing the
239 # default values.
240 options = (packageOptions {}) // {
242 packages = mkOption {
243 type =
244 let mod_args = {
245 inherit pkgs pkgconfPkgs haskellLib;
246 inherit (config) hsPkgs errorHandler;
247 inherit (config.cabal) system compiler;
248 }; in
249 attrsOf (submodule (import ./package.nix {
250 inherit mod_args listOfFilteringNulls;
251 inherit componentOptions packageOptions;
252 parentConfig = config;
253 }));
254 };
256 compiler = {
257 version = mkOption {
258 type = str;
259 };
260 nix-name = mkOption {
261 type = str;
262 };
263 packages = mkOption {
264 type = attrsOf str;
265 };
266 };
268 plan.pkg-def = mkOption {
269 type = unspecified;
270 visible = false;
271 internal = true;
272 };
273 };
275 config = let module = config.plan.pkg-def config.hackage.configs; in {
276 inherit (module) compiler;
277 packages = lib.mapAttrs (name: { revision, ... }@revArgs: { system, compiler, flags, pkgs, hsPkgs, errorHandler, pkgconfPkgs, ... }@modArgs:
279 let m = if revision == null
280 then (abort "${name} has no revision!")
281 else revision (modArgs // { hsPkgs = hsPkgs // (mapAttrs (l: _: hsPkgs.${name}.components.sublibs.${l}) (m.components.sublibs or {})); });
282 in
283 m // { flags = lib.mapAttrs (_: lib.mkDefault) (m.flags // revArgs.flags or {}); }
284 ) (lib.filterAttrs (n: v: v == null || v.revision != null ) module.packages);
285 };