at 24.11-pre 13 kB view raw
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}