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 closePropagation = list: (uniqList {inputList = (innerClosePropagation [] list);});
161
162 # calls a function (f attr value ) for each record item. returns a list
163 mapAttrsFlatten = f: r: map (attr: f attr r.${attr}) (attrNames r);
164
165 # attribute set containing one attribute
166 nvs = name: value: listToAttrs [ (nameValuePair name value) ];
167 # adds / replaces an attribute of an attribute set
168 setAttr = set: name: v: set // (nvs name v);
169
170 # setAttrMerge (similar to mergeAttrsWithFunc but only merges the values of a particular name)
171 # setAttrMerge "a" [] { a = [2];} (x: x ++ [3]) -> { a = [2 3]; }
172 # setAttrMerge "a" [] { } (x: x ++ [3]) -> { a = [ 3]; }
173 setAttrMerge = name: default: attrs: f:
174 setAttr attrs name (f (maybeAttr name default attrs));
175
176 # Using f = a: b = b the result is similar to //
177 # merge attributes with custom function handling the case that the attribute
178 # exists in both sets
179 mergeAttrsWithFunc = f: set1: set2:
180 foldr (n: set: if set ? ${n}
181 then setAttr set n (f set.${n} set2.${n})
182 else set )
183 (set2 // set1) (attrNames set2);
184
185 # merging two attribute set concatenating the values of same attribute names
186 # eg { a = 7; } { a = [ 2 3 ]; } becomes { a = [ 7 2 3 ]; }
187 mergeAttrsConcatenateValues = mergeAttrsWithFunc ( a: b: (toList a) ++ (toList b) );
188
189 # merges attributes using //, if a name exists in both attributes
190 # an error will be triggered unless its listed in mergeLists
191 # so you can mergeAttrsNoOverride { buildInputs = [a]; } { buildInputs = [a]; } {} to get
192 # { buildInputs = [a b]; }
193 # merging buildPhase doesn't really make sense. The cases will be rare where appending /prefixing will fit your needs?
194 # in these cases the first buildPhase will override the second one
195 # ! deprecated, use mergeAttrByFunc instead
196 mergeAttrsNoOverride = { mergeLists ? ["buildInputs" "propagatedBuildInputs"],
197 overrideSnd ? [ "buildPhase" ]
198 }: attrs1: attrs2:
199 foldr (n: set:
200 setAttr set n ( if set ? ${n}
201 then # merge
202 if elem n mergeLists # attribute contains list, merge them by concatenating
203 then attrs2.${n} ++ attrs1.${n}
204 else if elem n overrideSnd
205 then attrs1.${n}
206 else throw "error mergeAttrsNoOverride, attribute ${n} given in both attributes - no merge func defined"
207 else attrs2.${n} # add attribute not existing in attr1
208 )) attrs1 (attrNames attrs2);
209
210
211 # example usage:
212 # mergeAttrByFunc {
213 # inherit mergeAttrBy; # defined below
214 # buildInputs = [ a b ];
215 # } {
216 # buildInputs = [ c d ];
217 # };
218 # will result in
219 # { mergeAttrsBy = [...]; buildInputs = [ a b c d ]; }
220 # is used by defaultOverridableDelayableArgs and can be used when composing using
221 # foldArgs, composedArgsAndFun or applyAndFun. Example: composableDerivation in all-packages.nix
222 mergeAttrByFunc = x: y:
223 let
224 mergeAttrBy2 = { mergeAttrBy = lib.mergeAttrs; }
225 // (maybeAttr "mergeAttrBy" {} x)
226 // (maybeAttr "mergeAttrBy" {} y); in
227 foldr lib.mergeAttrs {} [
228 x y
229 (mapAttrs ( a: v: # merge special names using given functions
230 if x ? ${a}
231 then if y ? ${a}
232 then v x.${a} y.${a} # both have attr, use merge func
233 else x.${a} # only x has attr
234 else y.${a} # only y has attr)
235 ) (removeAttrs mergeAttrBy2
236 # don't merge attrs which are neither in x nor y
237 (filter (a: ! x ? ${a} && ! y ? ${a})
238 (attrNames mergeAttrBy2))
239 )
240 )
241 ];
242 mergeAttrsByFuncDefaults = foldl mergeAttrByFunc { inherit mergeAttrBy; };
243 mergeAttrsByFuncDefaultsClean = list: removeAttrs (mergeAttrsByFuncDefaults list) ["mergeAttrBy"];
244
245 # sane defaults (same name as attr name so that inherit can be used)
246 mergeAttrBy = # { buildInputs = concatList; [...]; passthru = mergeAttr; [..]; }
247 listToAttrs (map (n: nameValuePair n lib.concat)
248 [ "nativeBuildInputs" "buildInputs" "propagatedBuildInputs" "configureFlags" "prePhases" "postAll" "patches" ])
249 // listToAttrs (map (n: nameValuePair n lib.mergeAttrs) [ "passthru" "meta" "cfg" "flags" ])
250 // listToAttrs (map (n: nameValuePair n (a: b: "${a}\n${b}") ) [ "preConfigure" "postInstall" ])
251 ;
252
253 nixType = x:
254 if isAttrs x then
255 if x ? outPath then "derivation"
256 else "attrs"
257 else if lib.isFunction x then "function"
258 else if isList x then "list"
259 else if x == true then "bool"
260 else if x == false then "bool"
261 else if x == null then "null"
262 else if isInt x then "int"
263 else "string";
264
265 /* deprecated:
266
267 For historical reasons, imap has an index starting at 1.
268
269 But for consistency with the rest of the library we want an index
270 starting at zero.
271 */
272 imap = imap1;
273
274 # Fake hashes. Can be used as hash placeholders, when computing hash ahead isn't trivial
275 fakeHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
276 fakeSha256 = "0000000000000000000000000000000000000000000000000000000000000000";
277 fakeSha512 = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
278}