on stack-cache-native help sign in
haskell.nix/modules/package.nix
333 lines 9.5 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 path = types.path // { check = x: types.path.check (x.origSrc or x); };
26in {
27 # This is how the Nix expressions generated by *-to-nix receive
28 # their flags argument.
29 config._module.args = mod_args // { flags = config.flags; };
31 options = (packageOptions parentConfig) // {
32 # TODO: Add descriptions to everything.
33 flags = mkOption {
34 type = attrsOf bool;
35 };
36 package = {
37 specVersion = mkOption {
38 type = str;
39 };
41 identifier.name = mkOption {
42 type = str;
43 };
45 identifier.version = mkOption {
46 type = str;
47 };
49 license = mkOption {
50 type = str;
51 };
53 copyright = mkOption {
54 type = str;
55 };
57 maintainer = mkOption {
58 type = str;
59 };
61 author = mkOption {
62 type = str;
63 };
65 homepage = mkOption {
66 type = str;
67 };
69 url = mkOption {
70 type = str;
71 };
73 synopsis = mkOption {
74 type = str;
75 };
77 description = mkOption {
78 type = str;
79 };
81 buildType = mkOption {
82 type = str;
83 };
85 setup-depends = mkOption {
86 type = listOfFilteringNulls unspecified;
87 default = [];
88 };
90 detailLevel = mkOption {
91 type = str;
92 default = "MinimalDetails";
93 };
95 licenseFiles = mkOption {
96 type = listOfFilteringNulls unspecified;
97 default = [];
98 };
100 dataDir = mkOption {
101 type = str;
102 default = "";
103 };
105 dataFiles = mkOption {
106 type = listOfFilteringNulls unspecified;
107 default = [];
108 };
110 extraSrcFiles = mkOption {
111 type = listOfFilteringNulls unspecified;
112 default = [];
113 };
115 extraTmpFiles = mkOption {
116 type = listOfFilteringNulls unspecified;
117 default = [];
118 };
120 extraDocFiles = mkOption {
121 type = listOfFilteringNulls unspecified;
122 default = [];
123 };
125 cleanHpack = mkOption {
126 type = bool;
127 default = false;
128 };
130 isLocal = mkOption {
131 type = bool;
132 default = false;
133 };
135 ghcOptions = mkOption {
136 type = nullOr str;
137 default = null;
138 };
139 };
141 components = let
142 componentType = submodule {
143 # add the shared componentOptions
144 options = (packageOptions config) // {
145 buildable = mkOption {
146 type = bool;
147 default = true;
148 };
149 depends = mkOption {
150 type = listOfFilteringNulls unspecified;
151 default = [];
152 };
153 libs = mkOption {
154 type = listOfFilteringNulls (nullOr package);
155 default = [];
156 };
157 frameworks = mkOption {
158 type = listOfFilteringNulls package;
159 default = [];
160 };
161 pkgconfig = mkOption {
162 type = listOf (listOfFilteringNulls package);
163 default = [];
164 };
165 build-tools = mkOption {
166 type = listOfFilteringNulls unspecified;
167 default = [];
168 };
169 modules = mkOption {
170 type = listOfFilteringNulls unspecified;
171 default = [];
172 };
173 asmSources = mkOption {
174 type = listOfFilteringNulls unspecified;
175 default = [];
176 };
177 cmmSources = mkOption {
178 type = listOfFilteringNulls unspecified;
179 default = [];
180 };
181 cSources = mkOption {
182 type = listOfFilteringNulls unspecified;
183 default = [];
184 };
185 cxxSources = mkOption {
186 type = listOfFilteringNulls unspecified;
187 default = [];
188 };
189 jsSources = mkOption {
190 type = listOfFilteringNulls unspecified;
191 default = [];
192 };
193 hsSourceDirs = mkOption {
194 type = listOfFilteringNulls unspecified;
195 default = ["."];
196 };
197 includeDirs = mkOption {
198 type = listOfFilteringNulls unspecified;
199 default = [];
200 };
201 includes = mkOption {
202 type = listOfFilteringNulls unspecified;
203 default = [];
204 };
205 mainPath = mkOption {
206 type = listOfFilteringNulls unspecified;
207 default = [];
208 };
209 extraSrcFiles = mkOption {
210 type = listOfFilteringNulls unspecified;
211 default = [];
212 };
213 platforms = mkOption {
214 type = nullOr (listOfFilteringNulls unspecified);
215 default = null;
216 };
217 };
218 };
219 in {
220 setup = mkOption {
221 type = nullOr componentType;
222 default = {
223 depends = [];
224 libs = [];
225 frameworks = [];
226 doExactConfig = false;
227 # We have to set hsSourceDirs or cleanCabalComponent will
228 # include everything (and as a result all the components of
229 # the package will depend on eveything in the package).
230 # TODO find a better way
231 hsSourceDirs = ["setup-src"];
232 includeDirs = [];
233 asmSources = [];
234 cSources = [];
235 cmmSources = [];
236 cxxSources = [];
237 jsSources = [];
238 extraSrcFiles = [ "Setup.hs" "Setup.lhs" ];
239 platforms = null;
240 };
241 };
242 library = mkOption {
243 type = nullOr componentType;
244 default = null;
245 };
246 sublibs = mkOption {
247 type = attrsOf componentType;
248 default = {};
249 };
250 foreignlibs = mkOption {
251 type = attrsOf componentType;
252 default = {};
253 };
254 exes = mkOption {
255 type = attrsOf componentType;
256 default = {};
257 };
258 tests = mkOption {
259 type = attrsOf componentType;
260 default = {};
261 };
262 benchmarks = mkOption {
263 type = attrsOf componentType;
264 default = {};
265 };
266 all = mkOption {
267 type = componentType;
268 apply = all: all // {
269 # TODO: Should this check for the entire component
270 # definition to match, rather than just the identifier?
271 depends = builtins.filter (p: p.identifier != config.package.identifier) all.depends;
272 };
273 description = "The merged dependencies of all other components";
274 };
275 };
277 name = mkOption {
278 type = str;
279 default = "${config.package.identifier.name}-${config.package.identifier.version}";
280 defaultText = "\${config.package.identifier.name}-\${config.package.identifier.version}";
281 };
282 sha256 = mkOption {
283 type = nullOr str;
284 default = null;
285 };
286 src = mkOption {
287 type = either path package;
288 default = pkgs.fetchurl { url = "mirror://hackage/${config.name}.tar.gz"; inherit (config) sha256; };
289 defaultText = "pkgs.fetchurl { url = \"mirror://hackage/\${config.name}.tar.gz\"; inherit (config) sha256; };";
290 };
291 cabal-generator = mkOption {
292 type = nullOr str;
293 default = null;
294 };
295 revision = mkOption {
296 type = nullOr int;
297 default = null;
298 };
299 revisionSha256 = mkOption {
300 type = nullOr str;
301 default = null;
302 };
303 patches = mkOption {
304 type = listOf (either unspecified path);
305 default = [];
306 };
307 };
309 # This has one quirk. Manually setting options on the all component
310 # will be considered a conflict. This is almost always fine; most
311 # settings should be modified in either the package options, or an
312 # individual component's options. When this isn't sufficient,
313 # mkForce is a reasonable workaround.
314 #
315 # An alternative solution to mkForce for many of the options where
316 # this is relevant would be to switch from the bool type to
317 # something like an anyBool type, which would merge definitions by
318 # returning true if any is true.
319 config.components.all =
320 let allComps = haskellLib.getAllComponents config;
321 in lib.mkMerge (
322 builtins.map (c:
323 # Exclude attributes that are likely to have conflicting definitions
324 # (a common use case for `all` is in `shellFor` and it only has an
325 # install phase).
326 builtins.removeAttrs c ["preCheck" "postCheck" "keepSource"]
327 ) allComps
328 ) // {
329 # If any one of the components needs us to keep the source
330 # then keep it for the `all` component
331 keepSource = lib.foldl' (x: comp: x || comp.keepSource) false allComps;
332 };