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