1{ lib }:
2
3let
4 inherit (lib)
5 and
6 any
7 attrByPath
8 attrNames
9 compare
10 concat
11 concatMap
12 elem
13 filter
14 foldl
15 foldr
16 genericClosure
17 head
18 imap1
19 init
20 isAttrs
21 isFunction
22 isInt
23 isList
24 lists
25 listToAttrs
26 mapAttrs
27 mergeAttrs
28 meta
29 nameValuePair
30 tail
31 toList
32 ;
33
34 inherit (lib.attrsets) removeAttrs;
35
36 # returns default if env var is not set
37 maybeEnv = name: default:
38 let value = builtins.getEnv name; in
39 if value == "" then default else value;
40
41 defaultMergeArg = x : y: if builtins.isAttrs y then
42 y
43 else
44 (y x);
45 defaultMerge = x: y: x // (defaultMergeArg x y);
46 foldArgs = merger: f: init: x:
47 let arg = (merger init (defaultMergeArg init x));
48 # now add the function with composed args already applied to the final attrs
49 base = (setAttrMerge "passthru" {} (f arg)
50 ( z: z // {
51 function = foldArgs merger f arg;
52 args = (attrByPath ["passthru" "args"] {} z) // x;
53 } ));
54 withStdOverrides = base // {
55 override = base.passthru.function;
56 };
57 in
58 withStdOverrides;
59
60
61 # shortcut for attrByPath ["name"] default attrs
62 maybeAttrNullable = maybeAttr;
63
64 # shortcut for attrByPath ["name"] default attrs
65 maybeAttr = name: default: attrs: attrs.${name} or default;
66
67
68 # Return the second argument if the first one is true or the empty version
69 # of the second argument.
70 ifEnable = cond: val:
71 if cond then val
72 else if builtins.isList val then []
73 else if builtins.isAttrs val then {}
74 # else if builtins.isString val then ""
75 else if val == true || val == false then false
76 else null;
77
78
79 # Return true only if there is an attribute and it is true.
80 checkFlag = attrSet: name:
81 if name == "true" then true else
82 if name == "false" then false else
83 if (elem name (attrByPath ["flags"] [] attrSet)) then true else
84 attrByPath [name] false attrSet ;
85
86
87 # Input : attrSet, [ [name default] ... ], name
88 # Output : its value or default.
89 getValue = attrSet: argList: name:
90 ( attrByPath [name] (if checkFlag attrSet name then true else
91 if argList == [] then null else
92 let x = builtins.head argList; in
93 if (head x) == name then
94 (head (tail x))
95 else (getValue attrSet
96 (tail argList) name)) attrSet );
97
98
99 # Input : attrSet, [[name default] ...], [ [flagname reqs..] ... ]
100 # Output : are reqs satisfied? It's asserted.
101 checkReqs = attrSet: argList: condList:
102 (
103 foldr and true
104 (map (x: let name = (head x); in
105
106 ((checkFlag attrSet name) ->
107 (foldr and true
108 (map (y: let val=(getValue attrSet argList y); in
109 (val!=null) && (val!=false))
110 (tail x))))) condList));
111
112
113 # This function has O(n^2) performance.
114 uniqList = { inputList, acc ? [] }:
115 let go = xs: acc:
116 if xs == []
117 then []
118 else let x = head xs;
119 y = if elem x acc then [] else [x];
120 in y ++ go (tail xs) (y ++ acc);
121 in go inputList acc;
122
123 uniqListExt = { inputList,
124 outputList ? [],
125 getter ? (x: x),
126 compare ? (x: y: x==y) }:
127 if inputList == [] then outputList else
128 let x = head inputList;
129 isX = y: (compare (getter y) (getter x));
130 newOutputList = outputList ++
131 (if any isX outputList then [] else [x]);
132 in uniqListExt { outputList = newOutputList;
133 inputList = (tail inputList);
134 inherit getter compare;
135 };
136
137 condConcat = name: list: checker:
138 if list == [] then name else
139 if checker (head list) then
140 condConcat
141 (name + (head (tail list)))
142 (tail (tail list))
143 checker
144 else condConcat
145 name (tail (tail list)) checker;
146
147 lazyGenericClosure = {startSet, operator}:
148 let
149 work = list: doneKeys: result:
150 if list == [] then
151 result
152 else
153 let x = head list; key = x.key; in
154 if elem key doneKeys then
155 work (tail list) doneKeys result
156 else
157 work (tail list ++ operator x) ([key] ++ doneKeys) ([x] ++ result);
158 in
159 work startSet [] [];
160
161 innerModifySumArgs = f: x: a: b: if b == null then (f a b) // x else
162 innerModifySumArgs f x (a // b);
163 modifySumArgs = f: x: innerModifySumArgs f x {};
164
165
166 innerClosePropagation = acc: xs:
167 if xs == []
168 then acc
169 else let y = head xs;
170 ys = tail xs;
171 in if ! isAttrs y
172 then innerClosePropagation acc ys
173 else let acc' = [y] ++ acc;
174 in innerClosePropagation
175 acc'
176 (uniqList { inputList = (maybeAttrNullable "propagatedBuildInputs" [] y)
177 ++ (maybeAttrNullable "propagatedNativeBuildInputs" [] y)
178 ++ ys;
179 acc = acc';
180 }
181 );
182
183 closePropagationSlow = list: (uniqList {inputList = (innerClosePropagation [] list);});
184
185 # This is an optimisation of closePropagation which avoids the O(n^2) behavior
186 # Using a list of derivations, it generates the full closure of the propagatedXXXBuildInputs
187 # The ordering / sorting / comparison is done based on the `outPath`
188 # attribute of each derivation.
189 # On some benchmarks, it performs up to 15 times faster than closePropagation.
190 # See https://github.com/NixOS/nixpkgs/pull/194391 for details.
191 closePropagationFast = list:
192 builtins.map (x: x.val) (builtins.genericClosure {
193 startSet = builtins.map (x: {
194 key = x.outPath;
195 val = x;
196 }) (builtins.filter (x: x != null) list);
197 operator = item:
198 if !builtins.isAttrs item.val then
199 [ ]
200 else
201 builtins.concatMap (x:
202 if x != null then [{
203 key = x.outPath;
204 val = x;
205 }] else
206 [ ]) ((item.val.propagatedBuildInputs or [ ])
207 ++ (item.val.propagatedNativeBuildInputs or [ ]));
208 });
209
210 closePropagation = if builtins ? genericClosure
211 then closePropagationFast
212 else closePropagationSlow;
213
214 # calls a function (f attr value ) for each record item. returns a list
215 mapAttrsFlatten = f: r: map (attr: f attr r.${attr}) (attrNames r);
216
217 # attribute set containing one attribute
218 nvs = name: value: listToAttrs [ (nameValuePair name value) ];
219 # adds / replaces an attribute of an attribute set
220 setAttr = set: name: v: set // (nvs name v);
221
222 # setAttrMerge (similar to mergeAttrsWithFunc but only merges the values of a particular name)
223 # setAttrMerge "a" [] { a = [2];} (x: x ++ [3]) -> { a = [2 3]; }
224 # setAttrMerge "a" [] { } (x: x ++ [3]) -> { a = [ 3]; }
225 setAttrMerge = name: default: attrs: f:
226 setAttr attrs name (f (maybeAttr name default attrs));
227
228 # Using f = a: b = b the result is similar to //
229 # merge attributes with custom function handling the case that the attribute
230 # exists in both sets
231 mergeAttrsWithFunc = f: set1: set2:
232 foldr (n: set: if set ? ${n}
233 then setAttr set n (f set.${n} set2.${n})
234 else set )
235 (set2 // set1) (attrNames set2);
236
237 # merging two attribute set concatenating the values of same attribute names
238 # eg { a = 7; } { a = [ 2 3 ]; } becomes { a = [ 7 2 3 ]; }
239 mergeAttrsConcatenateValues = mergeAttrsWithFunc ( a: b: (toList a) ++ (toList b) );
240
241 # merges attributes using //, if a name exists in both attributes
242 # an error will be triggered unless its listed in mergeLists
243 # so you can mergeAttrsNoOverride { buildInputs = [a]; } { buildInputs = [a]; } {} to get
244 # { buildInputs = [a b]; }
245 # merging buildPhase doesn't really make sense. The cases will be rare where appending /prefixing will fit your needs?
246 # in these cases the first buildPhase will override the second one
247 # ! deprecated, use mergeAttrByFunc instead
248 mergeAttrsNoOverride = { mergeLists ? ["buildInputs" "propagatedBuildInputs"],
249 overrideSnd ? [ "buildPhase" ]
250 }: attrs1: attrs2:
251 foldr (n: set:
252 setAttr set n ( if set ? ${n}
253 then # merge
254 if elem n mergeLists # attribute contains list, merge them by concatenating
255 then attrs2.${n} ++ attrs1.${n}
256 else if elem n overrideSnd
257 then attrs1.${n}
258 else throw "error mergeAttrsNoOverride, attribute ${n} given in both attributes - no merge func defined"
259 else attrs2.${n} # add attribute not existing in attr1
260 )) attrs1 (attrNames attrs2);
261
262
263 # example usage:
264 # mergeAttrByFunc {
265 # inherit mergeAttrBy; # defined below
266 # buildInputs = [ a b ];
267 # } {
268 # buildInputs = [ c d ];
269 # };
270 # will result in
271 # { mergeAttrsBy = [...]; buildInputs = [ a b c d ]; }
272 # is used by defaultOverridableDelayableArgs and can be used when composing using
273 # foldArgs, composedArgsAndFun or applyAndFun. Example: composableDerivation in all-packages.nix
274 mergeAttrByFunc = x: y:
275 let
276 mergeAttrBy2 = { mergeAttrBy = mergeAttrs; }
277 // (maybeAttr "mergeAttrBy" {} x)
278 // (maybeAttr "mergeAttrBy" {} y); in
279 foldr mergeAttrs {} [
280 x y
281 (mapAttrs ( a: v: # merge special names using given functions
282 if x ? ${a}
283 then if y ? ${a}
284 then v x.${a} y.${a} # both have attr, use merge func
285 else x.${a} # only x has attr
286 else y.${a} # only y has attr)
287 ) (removeAttrs mergeAttrBy2
288 # don't merge attrs which are neither in x nor y
289 (filter (a: ! x ? ${a} && ! y ? ${a})
290 (attrNames mergeAttrBy2))
291 )
292 )
293 ];
294 mergeAttrsByFuncDefaults = foldl mergeAttrByFunc { inherit mergeAttrBy; };
295 mergeAttrsByFuncDefaultsClean = list: removeAttrs (mergeAttrsByFuncDefaults list) ["mergeAttrBy"];
296
297 # sane defaults (same name as attr name so that inherit can be used)
298 mergeAttrBy = # { buildInputs = concatList; [...]; passthru = mergeAttr; [..]; }
299 listToAttrs (map (n: nameValuePair n concat)
300 [ "nativeBuildInputs" "buildInputs" "propagatedBuildInputs" "configureFlags" "prePhases" "postAll" "patches" ])
301 // listToAttrs (map (n: nameValuePair n mergeAttrs) [ "passthru" "meta" "cfg" "flags" ])
302 // listToAttrs (map (n: nameValuePair n (a: b: "${a}\n${b}") ) [ "preConfigure" "postInstall" ])
303 ;
304
305 nixType = x:
306 if isAttrs x then
307 if x ? outPath then "derivation"
308 else "attrs"
309 else if isFunction x then "function"
310 else if isList x then "list"
311 else if x == true then "bool"
312 else if x == false then "bool"
313 else if x == null then "null"
314 else if isInt x then "int"
315 else "string";
316
317 /**
318 # Deprecated
319
320 For historical reasons, imap has an index starting at 1.
321
322 But for consistency with the rest of the library we want an index
323 starting at zero.
324 */
325 imap = imap1;
326
327 # Fake hashes. Can be used as hash placeholders, when computing hash ahead isn't trivial
328 fakeHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
329 fakeSha256 = "0000000000000000000000000000000000000000000000000000000000000000";
330 fakeSha512 = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
331
332in
333
334# Everything in this attrset is the public interface of the file.
335{
336 inherit
337 checkFlag
338 checkReqs
339 closePropagation
340 closePropagationFast
341 closePropagationSlow
342 condConcat
343 defaultMerge
344 defaultMergeArg
345 fakeHash
346 fakeSha256
347 fakeSha512
348 foldArgs
349 getValue
350 ifEnable
351 imap
352 innerClosePropagation
353 innerModifySumArgs
354 lazyGenericClosure
355 mapAttrsFlatten
356 maybeAttr
357 maybeAttrNullable
358 maybeEnv
359 mergeAttrBy
360 mergeAttrByFunc
361 mergeAttrsByFuncDefaults
362 mergeAttrsByFuncDefaultsClean
363 mergeAttrsConcatenateValues
364 mergeAttrsNoOverride
365 mergeAttrsWithFunc
366 modifySumArgs
367 nixType
368 nvs
369 setAttr
370 setAttrMerge
371 uniqList
372 uniqListExt
373 ;
374}