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