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