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