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