1let lib = import ./default.nix;
2 inherit (builtins) isFunction head tail isList isAttrs isInt attrNames;
3
4in
5
6with import ./lists.nix;
7with import ./attrsets.nix;
8with import ./strings.nix;
9
10rec {
11
12 # returns default if env var is not set
13 maybeEnv = name: default:
14 let value = builtins.getEnv name; in
15 if value == "" then default else value;
16
17 defaultMergeArg = x : y: if builtins.isAttrs y then
18 y
19 else
20 (y x);
21 defaultMerge = x: y: x // (defaultMergeArg x y);
22 foldArgs = merger: f: init: x:
23 let arg = (merger init (defaultMergeArg init x));
24 # now add the function with composed args already applied to the final attrs
25 base = (setAttrMerge "passthru" {} (f arg)
26 ( z: z // rec {
27 function = foldArgs merger f arg;
28 args = (lib.attrByPath ["passthru" "args"] {} z) // x;
29 } ));
30 withStdOverrides = base // {
31 override = base.passthru.function;
32 };
33 in
34 withStdOverrides;
35
36
37 # predecessors: proposed replacement for applyAndFun (which has a bug cause it merges twice)
38 # the naming "overridableDelayableArgs" tries to express that you can
39 # - override attr values which have been supplied earlier
40 # - use attr values before they have been supplied by accessing the fix point
41 # name "fixed"
42 # f: the (delayed overridden) arguments are applied to this
43 #
44 # initial: initial attrs arguments and settings. see defaultOverridableDelayableArgs
45 #
46 # returns: f applied to the arguments // special attributes attrs
47 # a) merge: merge applied args with new args. Wether an argument is overridden depends on the merge settings
48 # b) replace: this let's you replace and remove names no matter which merge function has been set
49 #
50 # examples: see test cases "res" below;
51 overridableDelayableArgs =
52 f: # the function applied to the arguments
53 initial: # you pass attrs, the functions below are passing a function taking the fix argument
54 let
55 takeFixed = if isFunction initial then initial else (fixed : initial); # transform initial to an expression always taking the fixed argument
56 tidy = args:
57 let # apply all functions given in "applyPreTidy" in sequence
58 applyPreTidyFun = fold ( n: a: x: n ( a x ) ) lib.id (maybeAttr "applyPreTidy" [] args);
59 in removeAttrs (applyPreTidyFun args) ( ["applyPreTidy"] ++ (maybeAttr "removeAttrs" [] args) ); # tidy up args before applying them
60 fun = n: x:
61 let newArgs = fixed:
62 let args = takeFixed fixed;
63 mergeFun = args.${n};
64 in if isAttrs x then (mergeFun args x)
65 else assert isFunction x;
66 mergeFun args (x ( args // { inherit fixed; }));
67 in overridableDelayableArgs f newArgs;
68 in
69 (f (tidy (lib.fix takeFixed))) // {
70 merge = fun "mergeFun";
71 replace = fun "keepFun";
72 };
73 defaultOverridableDelayableArgs = f:
74 let defaults = {
75 mergeFun = mergeAttrByFunc; # default merge function. merge strategie (concatenate lists, strings) is given by mergeAttrBy
76 keepFun = a: b: { inherit (a) removeAttrs mergeFun keepFun mergeAttrBy; } // b; # even when using replace preserve these values
77 applyPreTidy = []; # list of functions applied to args before args are tidied up (usage case : prepareDerivationArgs)
78 mergeAttrBy = mergeAttrBy // {
79 applyPreTidy = a: b: a ++ b;
80 removeAttrs = a: b: a ++ b;
81 };
82 removeAttrs = ["mergeFun" "keepFun" "mergeAttrBy" "removeAttrs" "fixed" ]; # before applying the arguments to the function make sure these names are gone
83 };
84 in (overridableDelayableArgs f defaults).merge;
85
86
87
88 # rec { # an example of how composedArgsAndFun can be used
89 # a = composedArgsAndFun (x: x) { a = ["2"]; meta = { d = "bar";}; };
90 # # meta.d will be lost ! It's your task to preserve it (eg using a merge function)
91 # b = a.passthru.function { a = [ "3" ]; meta = { d2 = "bar2";}; };
92 # # instead of passing/ overriding values you can use a merge function:
93 # c = b.passthru.function ( x: { a = x.a ++ ["4"]; }); # consider using (maybeAttr "a" [] x)
94 # }
95 # result:
96 # {
97 # a = { a = ["2"]; meta = { d = "bar"; }; passthru = { function = .. }; };
98 # b = { a = ["3"]; meta = { d2 = "bar2"; }; passthru = { function = .. }; };
99 # c = { a = ["3" "4"]; meta = { d2 = "bar2"; }; passthru = { function = .. }; };
100 # # c2 is equal to c
101 # }
102 composedArgsAndFun = f: foldArgs defaultMerge f {};
103
104
105 # shortcut for attrByPath ["name"] default attrs
106 maybeAttrNullable = maybeAttr;
107
108 # shortcut for attrByPath ["name"] default attrs
109 maybeAttr = name: default: attrs: attrs.${name} or default;
110
111
112 # Return the second argument if the first one is true or the empty version
113 # of the second argument.
114 ifEnable = cond: val:
115 if cond then val
116 else if builtins.isList val then []
117 else if builtins.isAttrs val then {}
118 # else if builtins.isString val then ""
119 else if val == true || val == false then false
120 else null;
121
122
123 # Return true only if there is an attribute and it is true.
124 checkFlag = attrSet: name:
125 if name == "true" then true else
126 if name == "false" then false else
127 if (elem name (attrByPath ["flags"] [] attrSet)) then true else
128 attrByPath [name] false attrSet ;
129
130
131 # Input : attrSet, [ [name default] ... ], name
132 # Output : its value or default.
133 getValue = attrSet: argList: name:
134 ( attrByPath [name] (if checkFlag attrSet name then true else
135 if argList == [] then null else
136 let x = builtins.head argList; in
137 if (head x) == name then
138 (head (tail x))
139 else (getValue attrSet
140 (tail argList) name)) attrSet );
141
142
143 # Input : attrSet, [[name default] ...], [ [flagname reqs..] ... ]
144 # Output : are reqs satisfied? It's asserted.
145 checkReqs = attrSet: argList: condList:
146 (
147 fold lib.and true
148 (map (x: let name = (head x); in
149
150 ((checkFlag attrSet name) ->
151 (fold lib.and true
152 (map (y: let val=(getValue attrSet argList y); in
153 (val!=null) && (val!=false))
154 (tail x))))) condList));
155
156
157 # This function has O(n^2) performance.
158 uniqList = { inputList, acc ? [] }:
159 let go = xs: acc:
160 if xs == []
161 then []
162 else let x = head xs;
163 y = if elem x acc then [] else [x];
164 in y ++ go (tail xs) (y ++ acc);
165 in go inputList acc;
166
167 uniqListExt = { inputList,
168 outputList ? [],
169 getter ? (x: x),
170 compare ? (x: y: x==y) }:
171 if inputList == [] then outputList else
172 let x = head inputList;
173 isX = y: (compare (getter y) (getter x));
174 newOutputList = outputList ++
175 (if any isX outputList then [] else [x]);
176 in uniqListExt { outputList = newOutputList;
177 inputList = (tail inputList);
178 inherit getter compare;
179 };
180
181 condConcat = name: list: checker:
182 if list == [] then name else
183 if checker (head list) then
184 condConcat
185 (name + (head (tail list)))
186 (tail (tail list))
187 checker
188 else condConcat
189 name (tail (tail list)) checker;
190
191 lazyGenericClosure = {startSet, operator}:
192 let
193 work = list: doneKeys: result:
194 if list == [] then
195 result
196 else
197 let x = head list; key = x.key; in
198 if elem key doneKeys then
199 work (tail list) doneKeys result
200 else
201 work (tail list ++ operator x) ([key] ++ doneKeys) ([x] ++ result);
202 in
203 work startSet [] [];
204
205 innerModifySumArgs = f: x: a: b: if b == null then (f a b) // x else
206 innerModifySumArgs f x (a // b);
207 modifySumArgs = f: x: innerModifySumArgs f x {};
208
209
210 innerClosePropagation = acc: xs:
211 if xs == []
212 then acc
213 else let y = head xs;
214 ys = tail xs;
215 in if ! isAttrs y
216 then innerClosePropagation acc ys
217 else let acc' = [y] ++ acc;
218 in innerClosePropagation
219 acc'
220 (uniqList { inputList = (maybeAttrNullable "propagatedBuildInputs" [] y)
221 ++ (maybeAttrNullable "propagatedNativeBuildInputs" [] y)
222 ++ ys;
223 acc = acc';
224 }
225 );
226
227 closePropagation = list: (uniqList {inputList = (innerClosePropagation [] list);});
228
229 # calls a function (f attr value ) for each record item. returns a list
230 mapAttrsFlatten = f: r: map (attr: f attr r.${attr}) (attrNames r);
231
232 # attribute set containing one attribute
233 nvs = name: value: listToAttrs [ (nameValuePair name value) ];
234 # adds / replaces an attribute of an attribute set
235 setAttr = set: name: v: set // (nvs name v);
236
237 # setAttrMerge (similar to mergeAttrsWithFunc but only merges the values of a particular name)
238 # setAttrMerge "a" [] { a = [2];} (x: x ++ [3]) -> { a = [2 3]; }
239 # setAttrMerge "a" [] { } (x: x ++ [3]) -> { a = [ 3]; }
240 setAttrMerge = name: default: attrs: f:
241 setAttr attrs name (f (maybeAttr name default attrs));
242
243 # Using f = a: b = b the result is similar to //
244 # merge attributes with custom function handling the case that the attribute
245 # exists in both sets
246 mergeAttrsWithFunc = f: set1: set2:
247 fold (n: set: if set ? ${n}
248 then setAttr set n (f set.${n} set2.${n})
249 else set )
250 (set2 // set1) (attrNames set2);
251
252 # merging two attribute set concatenating the values of same attribute names
253 # eg { a = 7; } { a = [ 2 3 ]; } becomes { a = [ 7 2 3 ]; }
254 mergeAttrsConcatenateValues = mergeAttrsWithFunc ( a: b: (toList a) ++ (toList b) );
255
256 # merges attributes using //, if a name exists in both attributes
257 # an error will be triggered unless its listed in mergeLists
258 # so you can mergeAttrsNoOverride { buildInputs = [a]; } { buildInputs = [a]; } {} to get
259 # { buildInputs = [a b]; }
260 # merging buildPhase doesn't really make sense. The cases will be rare where appending /prefixing will fit your needs?
261 # in these cases the first buildPhase will override the second one
262 # ! deprecated, use mergeAttrByFunc instead
263 mergeAttrsNoOverride = { mergeLists ? ["buildInputs" "propagatedBuildInputs"],
264 overrideSnd ? [ "buildPhase" ]
265 }: attrs1: attrs2:
266 fold (n: set:
267 setAttr set n ( if set ? ${n}
268 then # merge
269 if elem n mergeLists # attribute contains list, merge them by concatenating
270 then attrs2.${n} ++ attrs1.${n}
271 else if elem n overrideSnd
272 then attrs1.${n}
273 else throw "error mergeAttrsNoOverride, attribute ${n} given in both attributes - no merge func defined"
274 else attrs2.${n} # add attribute not existing in attr1
275 )) attrs1 (attrNames attrs2);
276
277
278 # example usage:
279 # mergeAttrByFunc {
280 # inherit mergeAttrBy; # defined below
281 # buildInputs = [ a b ];
282 # } {
283 # buildInputs = [ c d ];
284 # };
285 # will result in
286 # { mergeAttrsBy = [...]; buildInputs = [ a b c d ]; }
287 # is used by prepareDerivationArgs, defaultOverridableDelayableArgs and can be used when composing using
288 # foldArgs, composedArgsAndFun or applyAndFun. Example: composableDerivation in all-packages.nix
289 mergeAttrByFunc = x: y:
290 let
291 mergeAttrBy2 = { mergeAttrBy = lib.mergeAttrs; }
292 // (maybeAttr "mergeAttrBy" {} x)
293 // (maybeAttr "mergeAttrBy" {} y); in
294 fold lib.mergeAttrs {} [
295 x y
296 (mapAttrs ( a: v: # merge special names using given functions
297 if x ? ${a}
298 then if y ? ${a}
299 then v x.${a} y.${a} # both have attr, use merge func
300 else x.${a} # only x has attr
301 else y.${a} # only y has attr)
302 ) (removeAttrs mergeAttrBy2
303 # don't merge attrs which are neither in x nor y
304 (filter (a: ! x ? ${a} && ! y ? ${a})
305 (attrNames mergeAttrBy2))
306 )
307 )
308 ];
309 mergeAttrsByFuncDefaults = foldl mergeAttrByFunc { inherit mergeAttrBy; };
310 mergeAttrsByFuncDefaultsClean = list: removeAttrs (mergeAttrsByFuncDefaults list) ["mergeAttrBy"];
311
312 # sane defaults (same name as attr name so that inherit can be used)
313 mergeAttrBy = # { buildInputs = concatList; [...]; passthru = mergeAttr; [..]; }
314 listToAttrs (map (n: nameValuePair n lib.concat)
315 [ "nativeBuildInputs" "buildInputs" "propagatedBuildInputs" "configureFlags" "prePhases" "postAll" "patches" ])
316 // listToAttrs (map (n: nameValuePair n lib.mergeAttrs) [ "passthru" "meta" "cfg" "flags" ])
317 // listToAttrs (map (n: nameValuePair n (a: b: "${a}\n${b}") ) [ "preConfigure" "postInstall" ])
318 ;
319
320 # prepareDerivationArgs tries to make writing configurable derivations easier
321 # example:
322 # prepareDerivationArgs {
323 # mergeAttrBy = {
324 # myScript = x: y: x ++ "\n" ++ y;
325 # };
326 # cfg = {
327 # readlineSupport = true;
328 # };
329 # flags = {
330 # readline = {
331 # set = {
332 # configureFlags = [ "--with-compiler=${compiler}" ];
333 # buildInputs = [ compiler ];
334 # pass = { inherit compiler; READLINE=1; };
335 # assertion = compiler.dllSupport;
336 # myScript = "foo";
337 # };
338 # unset = { configureFlags = ["--without-compiler"]; };
339 # };
340 # };
341 # src = ...
342 # buildPhase = '' ... '';
343 # name = ...
344 # myScript = "bar";
345 # };
346 # if you don't have need for unset you can omit the surrounding set = { .. } attr
347 # all attrs except flags cfg and mergeAttrBy will be merged with the
348 # additional data from flags depending on config settings
349 # It's used in composableDerivation in all-packages.nix. It's also used
350 # heavily in the new python and libs implementation
351 #
352 # should we check for misspelled cfg options?
353 # TODO use args.mergeFun here as well?
354 prepareDerivationArgs = args:
355 let args2 = { cfg = {}; flags = {}; } // args;
356 flagName = name: "${name}Support";
357 cfgWithDefaults = (listToAttrs (map (n: nameValuePair (flagName n) false) (attrNames args2.flags)))
358 // args2.cfg;
359 opts = attrValues (mapAttrs (a: v:
360 let v2 = if v ? set || v ? unset then v else { set = v; };
361 n = if cfgWithDefaults.${flagName a} then "set" else "unset";
362 attr = maybeAttr n {} v2; in
363 if (maybeAttr "assertion" true attr)
364 then attr
365 else throw "assertion of flag ${a} of derivation ${args.name} failed"
366 ) args2.flags );
367 in removeAttrs
368 (mergeAttrsByFuncDefaults ([args] ++ opts ++ [{ passthru = cfgWithDefaults; }]))
369 ["flags" "cfg" "mergeAttrBy" ];
370
371
372 nixType = x:
373 if isAttrs x then
374 if x ? outPath then "derivation"
375 else "attrs"
376 else if isFunction x then "function"
377 else if isList x then "list"
378 else if x == true then "bool"
379 else if x == false then "bool"
380 else if x == null then "null"
381 else if isInt x then "int"
382 else "string";
383
384 /* deprecated:
385
386 For historical reasons, imap has an index starting at 1.
387
388 But for consistency with the rest of the library we want an index
389 starting at zero.
390 */
391 imap = imap1;
392}