on 19052d8 help sign in
haskell.nix/modules/package.nix
343 lines 9.6 kB history raw
1# package descriptions in hackage will look like:
2# { system, compiler, flags, pkgs, hsPkgs, pkgconfPkgs }:
3# { flags = { flag1 = false; flags2 = true; ... };
4# package = { specVersion = "X.Y"; identifier = { name = "..."; version = "a.b.c.d"; };
5# license = "..."; copyright = "..."; maintainer = "..."; author = "...";
6# homepage = "..."; url = "..."; synopsis = "..."; description = "...";
7# buildType = "Simple"; # or Custom, Autoconf, ...
8# };
9# components = {
10# "..." = { depends = [ (hsPkgs.base) ... ]; };
11# exes = { "..." = { depends = ... };
12# "..." = { depends = ... }; };
13# tests = { "..." = { depends = ... }; ... };
14# };
16{ parentConfig, mod_args, listOfFilteringNulls, componentOptions, packageOptions }:
17{ lib, config, pkgs, haskellLib, ... }:
19with lib;
20with types;
22# Work around issue that can cause _lots_ of files to be copied into the store.
23# See https://github.com/NixOS/nixpkgs/pull/64691
24let
25 path = types.path // { check = x: types.path.check (x.origSrc or x); };
27 componentType = submodule {
28 # add the shared componentOptions
29 options = (packageOptions config) // {
30 buildable = mkOption {
31 type = bool;
32 default = true;
33 };
34 depends = mkOption {
35 type = listOfFilteringNulls unspecified;
36 default = [];
37 };
38 libs = mkOption {
39 type = listOfFilteringNulls (nullOr package);
40 default = [];
41 };
42 frameworks = mkOption {
43 type = listOfFilteringNulls package;
44 default = [];
45 };
46 pkgconfig = mkOption {
47 type = listOf (listOfFilteringNulls package);
48 default = [];
49 };
50 build-tools = mkOption {
51 type = listOfFilteringNulls unspecified;
52 default = [];
53 };
54 modules = mkOption {
55 type = listOfFilteringNulls unspecified;
56 default = [];
57 };
58 asmSources = mkOption {
59 type = listOfFilteringNulls unspecified;
60 default = [];
61 };
62 cmmSources = mkOption {
63 type = listOfFilteringNulls unspecified;
64 default = [];
65 };
66 cSources = mkOption {
67 type = listOfFilteringNulls unspecified;
68 default = [];
69 };
70 cxxSources = mkOption {
71 type = listOfFilteringNulls unspecified;
72 default = [];
73 };
74 jsSources = mkOption {
75 type = listOfFilteringNulls unspecified;
76 default = [];
77 };
78 hsSourceDirs = mkOption {
79 type = listOfFilteringNulls unspecified;
80 default = ["."];
81 };
82 includeDirs = mkOption {
83 type = listOfFilteringNulls unspecified;
84 default = [];
85 };
86 includes = mkOption {
87 type = listOfFilteringNulls unspecified;
88 default = [];
89 };
90 mainPath = mkOption {
91 type = listOfFilteringNulls unspecified;
92 default = [];
93 };
94 extraSrcFiles = mkOption {
95 type = listOfFilteringNulls unspecified;
96 default = [];
97 };
98 platforms = mkOption {
99 type = nullOr (listOfFilteringNulls unspecified);
100 default = null;
101 };
102 };
103 };
105in {
106 # This is how the Nix expressions generated by *-to-nix receive
107 # their flags argument.
108 config._module.args = mod_args // { flags = config.flags; };
110 options = (packageOptions parentConfig) // {
111 # TODO: Add descriptions to everything.
112 flags = mkOption {
113 type = attrsOf bool;
114 };
115 package = {
116 specVersion = mkOption {
117 type = str;
118 };
120 identifier.name = mkOption {
121 type = str;
122 };
124 identifier.version = mkOption {
125 type = str;
126 };
128 license = mkOption {
129 type = str;
130 };
132 copyright = mkOption {
133 type = str;
134 };
136 maintainer = mkOption {
137 type = str;
138 };
140 author = mkOption {
141 type = str;
142 };
144 homepage = mkOption {
145 type = str;
146 };
148 url = mkOption {
149 type = str;
150 };
152 synopsis = mkOption {
153 type = str;
154 };
156 description = mkOption {
157 type = str;
158 };
160 buildType = mkOption {
161 type = str;
162 };
164 setup-depends = mkOption {
165 type = listOfFilteringNulls unspecified;
166 default = [];
167 };
169 detailLevel = mkOption {
170 type = str;
171 default = "MinimalDetails";
172 };
174 licenseFiles = mkOption {
175 type = listOfFilteringNulls unspecified;
176 default = [];
177 };
179 dataDir = mkOption {
180 type = str;
181 default = "";
182 };
184 dataFiles = mkOption {
185 type = listOfFilteringNulls unspecified;
186 default = [];
187 };
189 extraSrcFiles = mkOption {
190 type = listOfFilteringNulls unspecified;
191 default = [];
192 };
194 extraTmpFiles = mkOption {
195 type = listOfFilteringNulls unspecified;
196 default = [];
197 };
199 extraDocFiles = mkOption {
200 type = listOfFilteringNulls unspecified;
201 default = [];
202 };
204 cleanHpack = mkOption {
205 type = bool;
206 default = false;
207 };
209 isLocal = mkOption {
210 type = bool;
211 default = false;
212 };
214 isProject = mkOption {
215 type = bool;
216 default = false;
217 };
219 buildable = mkOption {
220 type = bool;
221 default = true;
222 };
223 };
225 components = {
226 setup = mkOption {
227 type = nullOr componentType;
228 default = {
229 depends = [];
230 libs = [];
231 frameworks = [];
232 doExactConfig = false;
233 # We have to set hsSourceDirs or cleanCabalComponent will
234 # include everything (and as a result all the components of
235 # the package will depend on everything in the package).
236 # TODO find a better way
237 hsSourceDirs = ["setup-src"];
238 includeDirs = [];
239 asmSources = [];
240 cSources = [];
241 cmmSources = [];
242 cxxSources = [];
243 jsSources = [];
244 extraSrcFiles = [ "Setup.hs" "Setup.lhs" ];
245 platforms = null;
246 };
247 };
248 library = mkOption {
249 type = nullOr componentType;
250 default = null;
251 };
252 sublibs = mkOption {
253 type = attrsOf componentType;
254 default = {};
255 };
256 foreignlibs = mkOption {
257 type = attrsOf componentType;
258 default = {};
259 };
260 exes = mkOption {
261 type = attrsOf componentType;
262 default = {};
263 };
264 tests = mkOption {
265 type = attrsOf componentType;
266 default = {};
267 };
268 benchmarks = mkOption {
269 type = attrsOf componentType;
270 default = {};
271 };
272 };
274 name = mkOption {
275 type = str;
276 default = "${config.package.identifier.name}-${config.package.identifier.version}";
277 defaultText = "\${config.package.identifier.name}-\${config.package.identifier.version}";
278 };
279 sha256 = mkOption {
280 type = nullOr str;
281 default = null;
282 };
283 src = mkOption {
284 type = either path package;
285 default = pkgs.fetchurl { url = "mirror://hackage/${config.name}.tar.gz"; inherit (config) sha256; };
286 defaultText = "pkgs.fetchurl { url = \"mirror://hackage/\${config.name}.tar.gz\"; inherit (config) sha256; };";
287 };
288 cabal-generator = mkOption {
289 type = nullOr str;
290 default = null;
291 };
292 revision = mkOption {
293 type = nullOr int;
294 default = null;
295 };
296 revisionSha256 = mkOption {
297 type = nullOr str;
298 default = null;
299 };
300 patches = mkOption {
301 type = listOf (either unspecified path);
302 default = [];
303 };
304 # This used to be `components.all` but it has been added back as `allComponent` to
305 # to avoid confusion. It is not mapped by `builder/hspkg-builder.nix` to anything
306 # you can build. Instead it is used internally when `configureAllComponents`
307 # is set or for tests whe on `cabal-doctest` is in the `setup-depends` of the package.
308 allComponent = mkOption {
309 type = componentType;
310 apply = all: all // {
311 # TODO: Should this check for the entire component
312 # definition to match, rather than just the identifier?
313 depends = builtins.filter (p: p.identifier != config.package.identifier) all.depends;
314 };
315 description = "The merged dependencies of all other components";
316 };
317 };
319 # This has one quirk. Manually setting options on the all component
320 # will be considered a conflict. This is almost always fine; most
321 # settings should be modified in either the package options, or an
322 # individual component's options. When this isn't sufficient,
323 # mkForce is a reasonable workaround.
324 #
325 # An alternative solution to mkForce for many of the options where
326 # this is relevant would be to switch from the bool type to
327 # something like an anyBool type, which would merge definitions by
328 # returning true if any is true.
329 config.allComponent =
330 let allComps = haskellLib.getAllComponents config;
331 in lib.mkMerge (
332 builtins.map (c:
333 # Exclude attributes that are likely to have conflicting definitions
334 # (a common use case for `all` is in `shellFor` and it only has an
335 # install phase).
336 builtins.removeAttrs c ["preCheck" "postCheck" "keepSource"]
337 ) allComps
338 ) // {
339 # If any one of the components needs us to keep the source
340 # then keep it for the `all` component
341 keepSource = lib.foldl' (x: comp: x || comp.keepSource) false allComps;
342 };