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, outputList ? [],
168 getter ? (x : x), compare ? (x: y: x==y)}:
169 if inputList == [] then outputList else
170 let x=head inputList;
171 isX = y: (compare (getter y) (getter x));
172 newOutputList = outputList ++
173 (if any isX outputList then [] else [x]);
174 in uniqListExt {outputList=newOutputList;
175 inputList = (tail inputList);
176 inherit getter compare;
177 };
178
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 exisits 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 does'nt 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 # merge attrs based on version key into mkDerivation args, see mergeAttrBy to learn about smart merge defaults
313 #
314 # This function is best explained by an example:
315 #
316 # {version ? "2.x"} :
317 #
318 # mkDerivation (mergeAttrsByVersion "package-name" version
319 # { # version specific settings
320 # "git" = { src = ..; preConfigre = "autogen.sh"; buildInputs = [automake autoconf libtool]; };
321 # "2.x" = { src = ..; };
322 # }
323 # { // shared settings
324 # buildInputs = [ common build inputs ];
325 # meta = { .. }
326 # }
327 # )
328 #
329 # Please note that e.g. Eelco Dolstra usually prefers having one file for
330 # each version. On the other hand there are valuable additional design goals
331 # - readability
332 # - do it once only
333 # - try to avoid duplication
334 #
335 # Marc Weber and Michael Raskin sometimes prefer keeping older
336 # versions around for testing and regression tests - as long as its cheap to
337 # do so.
338 #
339 # Very often it just happens that the "shared" code is the bigger part.
340 # Then using this function might be appropriate.
341 #
342 # Be aware that its easy to cause recompilations in all versions when using
343 # this function - also if derivations get too complex splitting into multiple
344 # files is the way to go.
345 #
346 # See misc.nix -> versionedDerivation
347 # discussion: nixpkgs: pull/310
348 mergeAttrsByVersion = name: version: attrsByVersion: base:
349 mergeAttrsByFuncDefaultsClean [ { name = "${name}-${version}"; } base (maybeAttr version (throw "bad version ${version} for ${name}") attrsByVersion)];
350
351 # sane defaults (same name as attr name so that inherit can be used)
352 mergeAttrBy = # { buildInputs = concatList; [...]; passthru = mergeAttr; [..]; }
353 listToAttrs (map (n : nameValuePair n lib.concat)
354 [ "nativeBuildInputs" "buildInputs" "propagatedBuildInputs" "configureFlags" "prePhases" "postAll" "patches" ])
355 // listToAttrs (map (n : nameValuePair n lib.mergeAttrs) [ "passthru" "meta" "cfg" "flags" ])
356 // listToAttrs (map (n : nameValuePair n (a: b: "${a}\n${b}") ) [ "preConfigure" "postInstall" ])
357 ;
358
359 # prepareDerivationArgs tries to make writing configurable derivations easier
360 # example:
361 # prepareDerivationArgs {
362 # mergeAttrBy = {
363 # myScript = x : y : x ++ "\n" ++ y;
364 # };
365 # cfg = {
366 # readlineSupport = true;
367 # };
368 # flags = {
369 # readline = {
370 # set = {
371 # configureFlags = [ "--with-compiler=${compiler}" ];
372 # buildInputs = [ compiler ];
373 # pass = { inherit compiler; READLINE=1; };
374 # assertion = compiler.dllSupport;
375 # myScript = "foo";
376 # };
377 # unset = { configureFlags = ["--without-compiler"]; };
378 # };
379 # };
380 # src = ...
381 # buildPhase = '' ... '';
382 # name = ...
383 # myScript = "bar";
384 # };
385 # if you don't have need for unset you can omit the surrounding set = { .. } attr
386 # all attrs except flags cfg and mergeAttrBy will be merged with the
387 # additional data from flags depending on config settings
388 # It's used in composableDerivation in all-packages.nix. It's also used
389 # heavily in the new python and libs implementation
390 #
391 # should we check for misspelled cfg options?
392 # TODO use args.mergeFun here as well?
393 prepareDerivationArgs = args:
394 let args2 = { cfg = {}; flags = {}; } // args;
395 flagName = name : "${name}Support";
396 cfgWithDefaults = (listToAttrs (map (n : nameValuePair (flagName n) false) (attrNames args2.flags)))
397 // args2.cfg;
398 opts = attrValues (mapAttrs (a : v :
399 let v2 = if v ? set || v ? unset then v else { set = v; };
400 n = if cfgWithDefaults.${flagName a} then "set" else "unset";
401 attr = maybeAttr n {} v2; in
402 if (maybeAttr "assertion" true attr)
403 then attr
404 else throw "assertion of flag ${a} of derivation ${args.name} failed"
405 ) args2.flags );
406 in removeAttrs
407 (mergeAttrsByFuncDefaults ([args] ++ opts ++ [{ passthru = cfgWithDefaults; }]))
408 ["flags" "cfg" "mergeAttrBy" ];
409
410
411 nixType = x:
412 if isAttrs x then
413 if x ? outPath then "derivation"
414 else "attrs"
415 else if isFunction x then "function"
416 else if isList x then "list"
417 else if x == true then "bool"
418 else if x == false then "bool"
419 else if x == null then "null"
420 else if isInt x then "int"
421 else "string";
422
423}