Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
1{ lib }: 2# Operations on attribute sets. 3 4let 5 inherit (builtins) head tail length; 6 inherit (lib.trivial) id; 7 inherit (lib.strings) concatStringsSep concatMapStringsSep escapeNixIdentifier sanitizeDerivationName; 8 inherit (lib.lists) foldr foldl' concatMap concatLists elemAt all partition groupBy take foldl; 9in 10 11rec { 12 inherit (builtins) attrNames listToAttrs hasAttr isAttrs getAttr; 13 14 15 /* Return an attribute from nested attribute sets. 16 17 Example: 18 x = { a = { b = 3; }; } 19 attrByPath ["a" "b"] 6 x 20 => 3 21 attrByPath ["z" "z"] 6 x 22 => 6 23 */ 24 attrByPath = attrPath: default: e: 25 let attr = head attrPath; 26 in 27 if attrPath == [] then e 28 else if e ? ${attr} 29 then attrByPath (tail attrPath) default e.${attr} 30 else default; 31 32 /* Return if an attribute from nested attribute set exists. 33 34 Example: 35 x = { a = { b = 3; }; } 36 hasAttrByPath ["a" "b"] x 37 => true 38 hasAttrByPath ["z" "z"] x 39 => false 40 41 */ 42 hasAttrByPath = attrPath: e: 43 let attr = head attrPath; 44 in 45 if attrPath == [] then true 46 else if e ? ${attr} 47 then hasAttrByPath (tail attrPath) e.${attr} 48 else false; 49 50 51 /* Return nested attribute set in which an attribute is set. 52 53 Example: 54 setAttrByPath ["a" "b"] 3 55 => { a = { b = 3; }; } 56 */ 57 setAttrByPath = attrPath: value: 58 let 59 len = length attrPath; 60 atDepth = n: 61 if n == len 62 then value 63 else { ${elemAt attrPath n} = atDepth (n + 1); }; 64 in atDepth 0; 65 66 /* Like `attrByPath' without a default value. If it doesn't find the 67 path it will throw. 68 69 Example: 70 x = { a = { b = 3; }; } 71 getAttrFromPath ["a" "b"] x 72 => 3 73 getAttrFromPath ["z" "z"] x 74 => error: cannot find attribute `z.z' 75 */ 76 getAttrFromPath = attrPath: 77 let errorMsg = "cannot find attribute `" + concatStringsSep "." attrPath + "'"; 78 in attrByPath attrPath (abort errorMsg); 79 80 81 /* Update or set specific paths of an attribute set. 82 83 Takes a list of updates to apply and an attribute set to apply them to, 84 and returns the attribute set with the updates applied. Updates are 85 represented as { path = ...; update = ...; } values, where `path` is a 86 list of strings representing the attribute path that should be updated, 87 and `update` is a function that takes the old value at that attribute path 88 as an argument and returns the new 89 value it should be. 90 91 Properties: 92 - Updates to deeper attribute paths are applied before updates to more 93 shallow attribute paths 94 - Multiple updates to the same attribute path are applied in the order 95 they appear in the update list 96 - If any but the last `path` element leads into a value that is not an 97 attribute set, an error is thrown 98 - If there is an update for an attribute path that doesn't exist, 99 accessing the argument in the update function causes an error, but 100 intermediate attribute sets are implicitly created as needed 101 102 Example: 103 updateManyAttrsByPath [ 104 { 105 path = [ "a" "b" ]; 106 update = old: { d = old.c; }; 107 } 108 { 109 path = [ "a" "b" "c" ]; 110 update = old: old + 1; 111 } 112 { 113 path = [ "x" "y" ]; 114 update = old: "xy"; 115 } 116 ] { a.b.c = 0; } 117 => { a = { b = { d = 1; }; }; x = { y = "xy"; }; } 118 */ 119 updateManyAttrsByPath = let 120 # When recursing into attributes, instead of updating the `path` of each 121 # update using `tail`, which needs to allocate an entirely new list, 122 # we just pass a prefix length to use and make sure to only look at the 123 # path without the prefix length, so that we can reuse the original list 124 # entries. 125 go = prefixLength: hasValue: value: updates: 126 let 127 # Splits updates into ones on this level (split.right) 128 # And ones on levels further down (split.wrong) 129 split = partition (el: length el.path == prefixLength) updates; 130 131 # Groups updates on further down levels into the attributes they modify 132 nested = groupBy (el: elemAt el.path prefixLength) split.wrong; 133 134 # Applies only nested modification to the input value 135 withNestedMods = 136 # Return the value directly if we don't have any nested modifications 137 if split.wrong == [] then 138 if hasValue then value 139 else 140 # Throw an error if there is no value. This `head` call here is 141 # safe, but only in this branch since `go` could only be called 142 # with `hasValue == false` for nested updates, in which case 143 # it's also always called with at least one update 144 let updatePath = (head split.right).path; in 145 throw 146 ( "updateManyAttrsByPath: Path '${showAttrPath updatePath}' does " 147 + "not exist in the given value, but the first update to this " 148 + "path tries to access the existing value.") 149 else 150 # If there are nested modifications, try to apply them to the value 151 if ! hasValue then 152 # But if we don't have a value, just use an empty attribute set 153 # as the value, but simplify the code a bit 154 mapAttrs (name: go (prefixLength + 1) false null) nested 155 else if isAttrs value then 156 # If we do have a value and it's an attribute set, override it 157 # with the nested modifications 158 value // 159 mapAttrs (name: go (prefixLength + 1) (value ? ${name}) value.${name}) nested 160 else 161 # However if it's not an attribute set, we can't apply the nested 162 # modifications, throw an error 163 let updatePath = (head split.wrong).path; in 164 throw 165 ( "updateManyAttrsByPath: Path '${showAttrPath updatePath}' needs to " 166 + "be updated, but path '${showAttrPath (take prefixLength updatePath)}' " 167 + "of the given value is not an attribute set, so we can't " 168 + "update an attribute inside of it."); 169 170 # We get the final result by applying all the updates on this level 171 # after having applied all the nested updates 172 # We use foldl instead of foldl' so that in case of multiple updates, 173 # intermediate values aren't evaluated if not needed 174 in foldl (acc: el: el.update acc) withNestedMods split.right; 175 176 in updates: value: go 0 true value updates; 177 178 /* Return the specified attributes from a set. 179 180 Example: 181 attrVals ["a" "b" "c"] as 182 => [as.a as.b as.c] 183 */ 184 attrVals = nameList: set: map (x: set.${x}) nameList; 185 186 187 /* Return the values of all attributes in the given set, sorted by 188 attribute name. 189 190 Example: 191 attrValues {c = 3; a = 1; b = 2;} 192 => [1 2 3] 193 */ 194 attrValues = builtins.attrValues or (attrs: attrVals (attrNames attrs) attrs); 195 196 197 /* Given a set of attribute names, return the set of the corresponding 198 attributes from the given set. 199 200 Example: 201 getAttrs [ "a" "b" ] { a = 1; b = 2; c = 3; } 202 => { a = 1; b = 2; } 203 */ 204 getAttrs = names: attrs: genAttrs names (name: attrs.${name}); 205 206 /* Collect each attribute named `attr' from a list of attribute 207 sets. Sets that don't contain the named attribute are ignored. 208 209 Example: 210 catAttrs "a" [{a = 1;} {b = 0;} {a = 2;}] 211 => [1 2] 212 */ 213 catAttrs = builtins.catAttrs or 214 (attr: l: concatLists (map (s: if s ? ${attr} then [s.${attr}] else []) l)); 215 216 217 /* Filter an attribute set by removing all attributes for which the 218 given predicate return false. 219 220 Example: 221 filterAttrs (n: v: n == "foo") { foo = 1; bar = 2; } 222 => { foo = 1; } 223 */ 224 filterAttrs = pred: set: 225 listToAttrs (concatMap (name: let v = set.${name}; in if pred name v then [(nameValuePair name v)] else []) (attrNames set)); 226 227 228 /* Filter an attribute set recursively by removing all attributes for 229 which the given predicate return false. 230 231 Example: 232 filterAttrsRecursive (n: v: v != null) { foo = { bar = null; }; } 233 => { foo = {}; } 234 */ 235 filterAttrsRecursive = pred: set: 236 listToAttrs ( 237 concatMap (name: 238 let v = set.${name}; in 239 if pred name v then [ 240 (nameValuePair name ( 241 if isAttrs v then filterAttrsRecursive pred v 242 else v 243 )) 244 ] else [] 245 ) (attrNames set) 246 ); 247 248 /* Apply fold functions to values grouped by key. 249 250 Example: 251 foldAttrs (item: acc: [item] ++ acc) [] [{ a = 2; } { a = 3; }] 252 => { a = [ 2 3 ]; } 253 */ 254 foldAttrs = op: nul: 255 foldr (n: a: 256 foldr (name: o: 257 o // { ${name} = op n.${name} (a.${name} or nul); } 258 ) a (attrNames n) 259 ) {}; 260 261 262 /* Recursively collect sets that verify a given predicate named `pred' 263 from the set `attrs'. The recursion is stopped when the predicate is 264 verified. 265 266 Type: 267 collect :: 268 (AttrSet -> Bool) -> AttrSet -> [x] 269 270 Example: 271 collect isList { a = { b = ["b"]; }; c = [1]; } 272 => [["b"] [1]] 273 274 collect (x: x ? outPath) 275 { a = { outPath = "a/"; }; b = { outPath = "b/"; }; } 276 => [{ outPath = "a/"; } { outPath = "b/"; }] 277 */ 278 collect = pred: attrs: 279 if pred attrs then 280 [ attrs ] 281 else if isAttrs attrs then 282 concatMap (collect pred) (attrValues attrs) 283 else 284 []; 285 286 /* Return the cartesian product of attribute set value combinations. 287 288 Example: 289 cartesianProductOfSets { a = [ 1 2 ]; b = [ 10 20 ]; } 290 => [ 291 { a = 1; b = 10; } 292 { a = 1; b = 20; } 293 { a = 2; b = 10; } 294 { a = 2; b = 20; } 295 ] 296 */ 297 cartesianProductOfSets = attrsOfLists: 298 foldl' (listOfAttrs: attrName: 299 concatMap (attrs: 300 map (listValue: attrs // { ${attrName} = listValue; }) attrsOfLists.${attrName} 301 ) listOfAttrs 302 ) [{}] (attrNames attrsOfLists); 303 304 305 /* Utility function that creates a {name, value} pair as expected by 306 builtins.listToAttrs. 307 308 Example: 309 nameValuePair "some" 6 310 => { name = "some"; value = 6; } 311 */ 312 nameValuePair = name: value: { inherit name value; }; 313 314 315 /* Apply a function to each element in an attribute set. The 316 function takes two arguments --- the attribute name and its value 317 --- and returns the new value for the attribute. The result is a 318 new attribute set. 319 320 Example: 321 mapAttrs (name: value: name + "-" + value) 322 { x = "foo"; y = "bar"; } 323 => { x = "x-foo"; y = "y-bar"; } 324 */ 325 mapAttrs = builtins.mapAttrs or 326 (f: set: 327 listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set))); 328 329 330 /* Like `mapAttrs', but allows the name of each attribute to be 331 changed in addition to the value. The applied function should 332 return both the new name and value as a `nameValuePair'. 333 334 Example: 335 mapAttrs' (name: value: nameValuePair ("foo_" + name) ("bar-" + value)) 336 { x = "a"; y = "b"; } 337 => { foo_x = "bar-a"; foo_y = "bar-b"; } 338 */ 339 mapAttrs' = f: set: 340 listToAttrs (map (attr: f attr set.${attr}) (attrNames set)); 341 342 343 /* Call a function for each attribute in the given set and return 344 the result in a list. 345 346 Type: 347 mapAttrsToList :: 348 (String -> a -> b) -> AttrSet -> [b] 349 350 Example: 351 mapAttrsToList (name: value: name + value) 352 { x = "a"; y = "b"; } 353 => [ "xa" "yb" ] 354 */ 355 mapAttrsToList = f: attrs: 356 map (name: f name attrs.${name}) (attrNames attrs); 357 358 359 /* Like `mapAttrs', except that it recursively applies itself to 360 attribute sets. Also, the first argument of the argument 361 function is a *list* of the names of the containing attributes. 362 363 Type: 364 mapAttrsRecursive :: 365 ([String] -> a -> b) -> AttrSet -> AttrSet 366 367 Example: 368 mapAttrsRecursive (path: value: concatStringsSep "-" (path ++ [value])) 369 { n = { a = "A"; m = { b = "B"; c = "C"; }; }; d = "D"; } 370 => { n = { a = "n-a-A"; m = { b = "n-m-b-B"; c = "n-m-c-C"; }; }; d = "d-D"; } 371 */ 372 mapAttrsRecursive = mapAttrsRecursiveCond (as: true); 373 374 375 /* Like `mapAttrsRecursive', but it takes an additional predicate 376 function that tells it whether to recurse into an attribute 377 set. If it returns false, `mapAttrsRecursiveCond' does not 378 recurse, but does apply the map function. If it returns true, it 379 does recurse, and does not apply the map function. 380 381 Type: 382 mapAttrsRecursiveCond :: 383 (AttrSet -> Bool) -> ([String] -> a -> b) -> AttrSet -> AttrSet 384 385 Example: 386 # To prevent recursing into derivations (which are attribute 387 # sets with the attribute "type" equal to "derivation"): 388 mapAttrsRecursiveCond 389 (as: !(as ? "type" && as.type == "derivation")) 390 (x: ... do something ...) 391 attrs 392 */ 393 mapAttrsRecursiveCond = cond: f: set: 394 let 395 recurse = path: 396 let 397 g = 398 name: value: 399 if isAttrs value && cond value 400 then recurse (path ++ [name]) value 401 else f (path ++ [name]) value; 402 in mapAttrs g; 403 in recurse [] set; 404 405 406 /* Generate an attribute set by mapping a function over a list of 407 attribute names. 408 409 Example: 410 genAttrs [ "foo" "bar" ] (name: "x_" + name) 411 => { foo = "x_foo"; bar = "x_bar"; } 412 */ 413 genAttrs = names: f: 414 listToAttrs (map (n: nameValuePair n (f n)) names); 415 416 417 /* Check whether the argument is a derivation. Any set with 418 { type = "derivation"; } counts as a derivation. 419 420 Example: 421 nixpkgs = import <nixpkgs> {} 422 isDerivation nixpkgs.ruby 423 => true 424 isDerivation "foobar" 425 => false 426 */ 427 isDerivation = x: x.type or null == "derivation"; 428 429 /* Converts a store path to a fake derivation. */ 430 toDerivation = path: 431 let 432 path' = builtins.storePath path; 433 res = 434 { type = "derivation"; 435 name = sanitizeDerivationName (builtins.substring 33 (-1) (baseNameOf path')); 436 outPath = path'; 437 outputs = [ "out" ]; 438 out = res; 439 outputName = "out"; 440 }; 441 in res; 442 443 444 /* If `cond' is true, return the attribute set `as', 445 otherwise an empty attribute set. 446 447 Example: 448 optionalAttrs (true) { my = "set"; } 449 => { my = "set"; } 450 optionalAttrs (false) { my = "set"; } 451 => { } 452 */ 453 optionalAttrs = cond: as: if cond then as else {}; 454 455 456 /* Merge sets of attributes and use the function f to merge attributes 457 values. 458 459 Example: 460 zipAttrsWithNames ["a"] (name: vs: vs) [{a = "x";} {a = "y"; b = "z";}] 461 => { a = ["x" "y"]; } 462 */ 463 zipAttrsWithNames = names: f: sets: 464 listToAttrs (map (name: { 465 inherit name; 466 value = f name (catAttrs name sets); 467 }) names); 468 469 /* Implementation note: Common names appear multiple times in the list of 470 names, hopefully this does not affect the system because the maximal 471 laziness avoid computing twice the same expression and listToAttrs does 472 not care about duplicated attribute names. 473 474 Example: 475 zipAttrsWith (name: values: values) [{a = "x";} {a = "y"; b = "z";}] 476 => { a = ["x" "y"]; b = ["z"] } 477 */ 478 zipAttrsWith = 479 builtins.zipAttrsWith or (f: sets: zipAttrsWithNames (concatMap attrNames sets) f sets); 480 /* Like `zipAttrsWith' with `(name: values: values)' as the function. 481 482 Example: 483 zipAttrs [{a = "x";} {a = "y"; b = "z";}] 484 => { a = ["x" "y"]; b = ["z"] } 485 */ 486 zipAttrs = zipAttrsWith (name: values: values); 487 488 /* Does the same as the update operator '//' except that attributes are 489 merged until the given predicate is verified. The predicate should 490 accept 3 arguments which are the path to reach the attribute, a part of 491 the first attribute set and a part of the second attribute set. When 492 the predicate is verified, the value of the first attribute set is 493 replaced by the value of the second attribute set. 494 495 Example: 496 recursiveUpdateUntil (path: l: r: path == ["foo"]) { 497 # first attribute set 498 foo.bar = 1; 499 foo.baz = 2; 500 bar = 3; 501 } { 502 #second attribute set 503 foo.bar = 1; 504 foo.quz = 2; 505 baz = 4; 506 } 507 508 returns: { 509 foo.bar = 1; # 'foo.*' from the second set 510 foo.quz = 2; # 511 bar = 3; # 'bar' from the first set 512 baz = 4; # 'baz' from the second set 513 } 514 515 */ 516 recursiveUpdateUntil = pred: lhs: rhs: 517 let f = attrPath: 518 zipAttrsWith (n: values: 519 let here = attrPath ++ [n]; in 520 if length values == 1 521 || pred here (elemAt values 1) (head values) then 522 head values 523 else 524 f here values 525 ); 526 in f [] [rhs lhs]; 527 528 /* A recursive variant of the update operator //. The recursion 529 stops when one of the attribute values is not an attribute set, 530 in which case the right hand side value takes precedence over the 531 left hand side value. 532 533 Example: 534 recursiveUpdate { 535 boot.loader.grub.enable = true; 536 boot.loader.grub.device = "/dev/hda"; 537 } { 538 boot.loader.grub.device = ""; 539 } 540 541 returns: { 542 boot.loader.grub.enable = true; 543 boot.loader.grub.device = ""; 544 } 545 546 */ 547 recursiveUpdate = recursiveUpdateUntil (path: lhs: rhs: !(isAttrs lhs && isAttrs rhs)); 548 549 /* Returns true if the pattern is contained in the set. False otherwise. 550 551 Example: 552 matchAttrs { cpu = {}; } { cpu = { bits = 64; }; } 553 => true 554 */ 555 matchAttrs = pattern: attrs: assert isAttrs pattern; 556 all id (attrValues (zipAttrsWithNames (attrNames pattern) (n: values: 557 let pat = head values; val = elemAt values 1; in 558 if length values == 1 then false 559 else if isAttrs pat then isAttrs val && matchAttrs pat val 560 else pat == val 561 ) [pattern attrs])); 562 563 /* Override only the attributes that are already present in the old set 564 useful for deep-overriding. 565 566 Example: 567 overrideExisting {} { a = 1; } 568 => {} 569 overrideExisting { b = 2; } { a = 1; } 570 => { b = 2; } 571 overrideExisting { a = 3; b = 2; } { a = 1; } 572 => { a = 1; b = 2; } 573 */ 574 overrideExisting = old: new: 575 mapAttrs (name: value: new.${name} or value) old; 576 577 /* Turns a list of strings into a human-readable description of those 578 strings represented as an attribute path. The result of this function is 579 not intended to be machine-readable. 580 581 Example: 582 showAttrPath [ "foo" "10" "bar" ] 583 => "foo.\"10\".bar" 584 showAttrPath [] 585 => "<root attribute path>" 586 */ 587 showAttrPath = path: 588 if path == [] then "<root attribute path>" 589 else concatMapStringsSep "." escapeNixIdentifier path; 590 591 /* Get a package output. 592 If no output is found, fallback to `.out` and then to the default. 593 594 Example: 595 getOutput "dev" pkgs.openssl 596 => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev" 597 */ 598 getOutput = output: pkg: 599 if ! pkg ? outputSpecified || ! pkg.outputSpecified 600 then pkg.${output} or pkg.out or pkg 601 else pkg; 602 603 getBin = getOutput "bin"; 604 getLib = getOutput "lib"; 605 getDev = getOutput "dev"; 606 getMan = getOutput "man"; 607 608 /* Pick the outputs of packages to place in buildInputs */ 609 chooseDevOutputs = drvs: builtins.map getDev drvs; 610 611 /* Make various Nix tools consider the contents of the resulting 612 attribute set when looking for what to build, find, etc. 613 614 This function only affects a single attribute set; it does not 615 apply itself recursively for nested attribute sets. 616 */ 617 recurseIntoAttrs = 618 attrs: attrs // { recurseForDerivations = true; }; 619 620 /* Undo the effect of recurseIntoAttrs. 621 */ 622 dontRecurseIntoAttrs = 623 attrs: attrs // { recurseForDerivations = false; }; 624 625 /*** deprecated stuff ***/ 626 627 zipWithNames = zipAttrsWithNames; 628 zip = builtins.trace 629 "lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith; 630}