at 21.11-pre 15 kB view raw
1{ lib }: 2# Operations on attribute sets. 3 4let 5 inherit (builtins) head tail length; 6 inherit (lib.trivial) and; 7 inherit (lib.strings) concatStringsSep sanitizeDerivationName; 8 inherit (lib.lists) fold concatMap concatLists; 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 if attrPath == [] then value 59 else listToAttrs 60 [ { name = head attrPath; value = setAttrByPath (tail attrPath) value; } ]; 61 62 63 /* Like `attrByPath' without a default value. If it doesn't find the 64 path it will throw. 65 66 Example: 67 x = { a = { b = 3; }; } 68 getAttrFromPath ["a" "b"] x 69 => 3 70 getAttrFromPath ["z" "z"] x 71 => error: cannot find attribute `z.z' 72 */ 73 getAttrFromPath = attrPath: set: 74 let errorMsg = "cannot find attribute `" + concatStringsSep "." attrPath + "'"; 75 in attrByPath attrPath (abort errorMsg) set; 76 77 78 /* Return the specified attributes from a set. 79 80 Example: 81 attrVals ["a" "b" "c"] as 82 => [as.a as.b as.c] 83 */ 84 attrVals = nameList: set: map (x: set.${x}) nameList; 85 86 87 /* Return the values of all attributes in the given set, sorted by 88 attribute name. 89 90 Example: 91 attrValues {c = 3; a = 1; b = 2;} 92 => [1 2 3] 93 */ 94 attrValues = builtins.attrValues or (attrs: attrVals (attrNames attrs) attrs); 95 96 97 /* Given a set of attribute names, return the set of the corresponding 98 attributes from the given set. 99 100 Example: 101 getAttrs [ "a" "b" ] { a = 1; b = 2; c = 3; } 102 => { a = 1; b = 2; } 103 */ 104 getAttrs = names: attrs: genAttrs names (name: attrs.${name}); 105 106 /* Collect each attribute named `attr' from a list of attribute 107 sets. Sets that don't contain the named attribute are ignored. 108 109 Example: 110 catAttrs "a" [{a = 1;} {b = 0;} {a = 2;}] 111 => [1 2] 112 */ 113 catAttrs = builtins.catAttrs or 114 (attr: l: concatLists (map (s: if s ? ${attr} then [s.${attr}] else []) l)); 115 116 117 /* Filter an attribute set by removing all attributes for which the 118 given predicate return false. 119 120 Example: 121 filterAttrs (n: v: n == "foo") { foo = 1; bar = 2; } 122 => { foo = 1; } 123 */ 124 filterAttrs = pred: set: 125 listToAttrs (concatMap (name: let v = set.${name}; in if pred name v then [(nameValuePair name v)] else []) (attrNames set)); 126 127 128 /* Filter an attribute set recursively by removing all attributes for 129 which the given predicate return false. 130 131 Example: 132 filterAttrsRecursive (n: v: v != null) { foo = { bar = null; }; } 133 => { foo = {}; } 134 */ 135 filterAttrsRecursive = pred: set: 136 listToAttrs ( 137 concatMap (name: 138 let v = set.${name}; in 139 if pred name v then [ 140 (nameValuePair name ( 141 if isAttrs v then filterAttrsRecursive pred v 142 else v 143 )) 144 ] else [] 145 ) (attrNames set) 146 ); 147 148 /* Apply fold functions to values grouped by key. 149 150 Example: 151 foldAttrs (n: a: [n] ++ a) [] [{ a = 2; } { a = 3; }] 152 => { a = [ 2 3 ]; } 153 */ 154 foldAttrs = op: nul: list_of_attrs: 155 fold (n: a: 156 fold (name: o: 157 o // { ${name} = op n.${name} (a.${name} or nul); } 158 ) a (attrNames n) 159 ) {} list_of_attrs; 160 161 162 /* Recursively collect sets that verify a given predicate named `pred' 163 from the set `attrs'. The recursion is stopped when the predicate is 164 verified. 165 166 Type: 167 collect :: 168 (AttrSet -> Bool) -> AttrSet -> [x] 169 170 Example: 171 collect isList { a = { b = ["b"]; }; c = [1]; } 172 => [["b"] [1]] 173 174 collect (x: x ? outPath) 175 { a = { outPath = "a/"; }; b = { outPath = "b/"; }; } 176 => [{ outPath = "a/"; } { outPath = "b/"; }] 177 */ 178 collect = pred: attrs: 179 if pred attrs then 180 [ attrs ] 181 else if isAttrs attrs then 182 concatMap (collect pred) (attrValues attrs) 183 else 184 []; 185 186 /* Return the cartesian product of attribute set value combinations. 187 188 Example: 189 cartesianProductOfSets { a = [ 1 2 ]; b = [ 10 20 ]; } 190 => [ 191 { a = 1; b = 10; } 192 { a = 1; b = 20; } 193 { a = 2; b = 10; } 194 { a = 2; b = 20; } 195 ] 196 */ 197 cartesianProductOfSets = attrsOfLists: 198 lib.foldl' (listOfAttrs: attrName: 199 concatMap (attrs: 200 map (listValue: attrs // { ${attrName} = listValue; }) attrsOfLists.${attrName} 201 ) listOfAttrs 202 ) [{}] (attrNames attrsOfLists); 203 204 205 /* Utility function that creates a {name, value} pair as expected by 206 builtins.listToAttrs. 207 208 Example: 209 nameValuePair "some" 6 210 => { name = "some"; value = 6; } 211 */ 212 nameValuePair = name: value: { inherit name value; }; 213 214 215 /* Apply a function to each element in an attribute set. The 216 function takes two arguments --- the attribute name and its value 217 --- and returns the new value for the attribute. The result is a 218 new attribute set. 219 220 Example: 221 mapAttrs (name: value: name + "-" + value) 222 { x = "foo"; y = "bar"; } 223 => { x = "x-foo"; y = "y-bar"; } 224 */ 225 mapAttrs = builtins.mapAttrs or 226 (f: set: 227 listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set))); 228 229 230 /* Like `mapAttrs', but allows the name of each attribute to be 231 changed in addition to the value. The applied function should 232 return both the new name and value as a `nameValuePair'. 233 234 Example: 235 mapAttrs' (name: value: nameValuePair ("foo_" + name) ("bar-" + value)) 236 { x = "a"; y = "b"; } 237 => { foo_x = "bar-a"; foo_y = "bar-b"; } 238 */ 239 mapAttrs' = f: set: 240 listToAttrs (map (attr: f attr set.${attr}) (attrNames set)); 241 242 243 /* Call a function for each attribute in the given set and return 244 the result in a list. 245 246 Type: 247 mapAttrsToList :: 248 (String -> a -> b) -> AttrSet -> [b] 249 250 Example: 251 mapAttrsToList (name: value: name + value) 252 { x = "a"; y = "b"; } 253 => [ "xa" "yb" ] 254 */ 255 mapAttrsToList = f: attrs: 256 map (name: f name attrs.${name}) (attrNames attrs); 257 258 259 /* Like `mapAttrs', except that it recursively applies itself to 260 attribute sets. Also, the first argument of the argument 261 function is a *list* of the names of the containing attributes. 262 263 Type: 264 mapAttrsRecursive :: 265 ([String] -> a -> b) -> AttrSet -> AttrSet 266 267 Example: 268 mapAttrsRecursive (path: value: concatStringsSep "-" (path ++ [value])) 269 { n = { a = "A"; m = { b = "B"; c = "C"; }; }; d = "D"; } 270 => { n = { a = "n-a-A"; m = { b = "n-m-b-B"; c = "n-m-c-C"; }; }; d = "d-D"; } 271 */ 272 mapAttrsRecursive = mapAttrsRecursiveCond (as: true); 273 274 275 /* Like `mapAttrsRecursive', but it takes an additional predicate 276 function that tells it whether to recursive into an attribute 277 set. If it returns false, `mapAttrsRecursiveCond' does not 278 recurse, but does apply the map function. If it returns true, it 279 does recurse, and does not apply the map function. 280 281 Type: 282 mapAttrsRecursiveCond :: 283 (AttrSet -> Bool) -> ([String] -> a -> b) -> AttrSet -> AttrSet 284 285 Example: 286 # To prevent recursing into derivations (which are attribute 287 # sets with the attribute "type" equal to "derivation"): 288 mapAttrsRecursiveCond 289 (as: !(as ? "type" && as.type == "derivation")) 290 (x: ... do something ...) 291 attrs 292 */ 293 mapAttrsRecursiveCond = cond: f: set: 294 let 295 recurse = path: set: 296 let 297 g = 298 name: value: 299 if isAttrs value && cond value 300 then recurse (path ++ [name]) value 301 else f (path ++ [name]) value; 302 in mapAttrs g set; 303 in recurse [] set; 304 305 306 /* Generate an attribute set by mapping a function over a list of 307 attribute names. 308 309 Example: 310 genAttrs [ "foo" "bar" ] (name: "x_" + name) 311 => { foo = "x_foo"; bar = "x_bar"; } 312 */ 313 genAttrs = names: f: 314 listToAttrs (map (n: nameValuePair n (f n)) names); 315 316 317 /* Check whether the argument is a derivation. Any set with 318 { type = "derivation"; } counts as a derivation. 319 320 Example: 321 nixpkgs = import <nixpkgs> {} 322 isDerivation nixpkgs.ruby 323 => true 324 isDerivation "foobar" 325 => false 326 */ 327 isDerivation = x: isAttrs x && x ? type && x.type == "derivation"; 328 329 /* Converts a store path to a fake derivation. */ 330 toDerivation = path: 331 let 332 path' = builtins.storePath path; 333 res = 334 { type = "derivation"; 335 name = sanitizeDerivationName (builtins.substring 33 (-1) (baseNameOf path')); 336 outPath = path'; 337 outputs = [ "out" ]; 338 out = res; 339 outputName = "out"; 340 }; 341 in res; 342 343 344 /* If `cond' is true, return the attribute set `as', 345 otherwise an empty attribute set. 346 347 Example: 348 optionalAttrs (true) { my = "set"; } 349 => { my = "set"; } 350 optionalAttrs (false) { my = "set"; } 351 => { } 352 */ 353 optionalAttrs = cond: as: if cond then as else {}; 354 355 356 /* Merge sets of attributes and use the function f to merge attributes 357 values. 358 359 Example: 360 zipAttrsWithNames ["a"] (name: vs: vs) [{a = "x";} {a = "y"; b = "z";}] 361 => { a = ["x" "y"]; } 362 */ 363 zipAttrsWithNames = names: f: sets: 364 listToAttrs (map (name: { 365 inherit name; 366 value = f name (catAttrs name sets); 367 }) names); 368 369 /* Implementation note: Common names appear multiple times in the list of 370 names, hopefully this does not affect the system because the maximal 371 laziness avoid computing twice the same expression and listToAttrs does 372 not care about duplicated attribute names. 373 374 Example: 375 zipAttrsWith (name: values: values) [{a = "x";} {a = "y"; b = "z";}] 376 => { a = ["x" "y"]; b = ["z"] } 377 */ 378 zipAttrsWith = f: sets: zipAttrsWithNames (concatMap attrNames sets) f sets; 379 /* Like `zipAttrsWith' with `(name: values: values)' as the function. 380 381 Example: 382 zipAttrs [{a = "x";} {a = "y"; b = "z";}] 383 => { a = ["x" "y"]; b = ["z"] } 384 */ 385 zipAttrs = zipAttrsWith (name: values: values); 386 387 /* Does the same as the update operator '//' except that attributes are 388 merged until the given predicate is verified. The predicate should 389 accept 3 arguments which are the path to reach the attribute, a part of 390 the first attribute set and a part of the second attribute set. When 391 the predicate is verified, the value of the first attribute set is 392 replaced by the value of the second attribute set. 393 394 Example: 395 recursiveUpdateUntil (path: l: r: path == ["foo"]) { 396 # first attribute set 397 foo.bar = 1; 398 foo.baz = 2; 399 bar = 3; 400 } { 401 #second attribute set 402 foo.bar = 1; 403 foo.quz = 2; 404 baz = 4; 405 } 406 407 returns: { 408 foo.bar = 1; # 'foo.*' from the second set 409 foo.quz = 2; # 410 bar = 3; # 'bar' from the first set 411 baz = 4; # 'baz' from the second set 412 } 413 414 */ 415 recursiveUpdateUntil = pred: lhs: rhs: 416 let f = attrPath: 417 zipAttrsWith (n: values: 418 let here = attrPath ++ [n]; in 419 if tail values == [] 420 || pred here (head (tail values)) (head values) then 421 head values 422 else 423 f here values 424 ); 425 in f [] [rhs lhs]; 426 427 /* A recursive variant of the update operator //. The recursion 428 stops when one of the attribute values is not an attribute set, 429 in which case the right hand side value takes precedence over the 430 left hand side value. 431 432 Example: 433 recursiveUpdate { 434 boot.loader.grub.enable = true; 435 boot.loader.grub.device = "/dev/hda"; 436 } { 437 boot.loader.grub.device = ""; 438 } 439 440 returns: { 441 boot.loader.grub.enable = true; 442 boot.loader.grub.device = ""; 443 } 444 445 */ 446 recursiveUpdate = lhs: rhs: 447 recursiveUpdateUntil (path: lhs: rhs: 448 !(isAttrs lhs && isAttrs rhs) 449 ) lhs rhs; 450 451 /* Returns true if the pattern is contained in the set. False otherwise. 452 453 Example: 454 matchAttrs { cpu = {}; } { cpu = { bits = 64; }; } 455 => true 456 */ 457 matchAttrs = pattern: attrs: assert isAttrs pattern; 458 fold and true (attrValues (zipAttrsWithNames (attrNames pattern) (n: values: 459 let pat = head values; val = head (tail values); in 460 if length values == 1 then false 461 else if isAttrs pat then isAttrs val && matchAttrs pat val 462 else pat == val 463 ) [pattern attrs])); 464 465 /* Override only the attributes that are already present in the old set 466 useful for deep-overriding. 467 468 Example: 469 overrideExisting {} { a = 1; } 470 => {} 471 overrideExisting { b = 2; } { a = 1; } 472 => { b = 2; } 473 overrideExisting { a = 3; b = 2; } { a = 1; } 474 => { a = 1; b = 2; } 475 */ 476 overrideExisting = old: new: 477 mapAttrs (name: value: new.${name} or value) old; 478 479 /* Get a package output. 480 If no output is found, fallback to `.out` and then to the default. 481 482 Example: 483 getOutput "dev" pkgs.openssl 484 => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev" 485 */ 486 getOutput = output: pkg: 487 if pkg.outputUnspecified or false 488 then pkg.${output} or pkg.out or pkg 489 else pkg; 490 491 getBin = getOutput "bin"; 492 getLib = getOutput "lib"; 493 getDev = getOutput "dev"; 494 getMan = getOutput "man"; 495 496 /* Pick the outputs of packages to place in buildInputs */ 497 chooseDevOutputs = drvs: builtins.map getDev drvs; 498 499 /* Make various Nix tools consider the contents of the resulting 500 attribute set when looking for what to build, find, etc. 501 502 This function only affects a single attribute set; it does not 503 apply itself recursively for nested attribute sets. 504 */ 505 recurseIntoAttrs = 506 attrs: attrs // { recurseForDerivations = true; }; 507 508 /* Undo the effect of recurseIntoAttrs. 509 */ 510 dontRecurseIntoAttrs = 511 attrs: attrs // { recurseForDerivations = false; }; 512 513 /*** deprecated stuff ***/ 514 515 zipWithNames = zipAttrsWithNames; 516 zip = builtins.trace 517 "lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith; 518}