471 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			471 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
| { lib }:
 | ||
| # Operations on attribute sets.
 | ||
| 
 | ||
| let
 | ||
|   inherit (builtins) head tail length;
 | ||
|   inherit (lib.trivial) and;
 | ||
|   inherit (lib.strings) concatStringsSep;
 | ||
|   inherit (lib.lists) fold concatMap concatLists;
 | ||
| in
 | ||
| 
 | ||
| rec {
 | ||
|   inherit (builtins) attrNames listToAttrs hasAttr isAttrs getAttr;
 | ||
| 
 | ||
| 
 | ||
|   /* Return an attribute from nested attribute sets.
 | ||
| 
 | ||
|      Example:
 | ||
|        x = { a = { b = 3; }; }
 | ||
|        attrByPath ["a" "b"] 6 x
 | ||
|        => 3
 | ||
|        attrByPath ["z" "z"] 6 x
 | ||
|        => 6
 | ||
|   */
 | ||
|   attrByPath = attrPath: default: e:
 | ||
|     let attr = head attrPath;
 | ||
|     in
 | ||
|       if attrPath == [] then e
 | ||
|       else if e ? ${attr}
 | ||
|       then attrByPath (tail attrPath) default e.${attr}
 | ||
|       else default;
 | ||
| 
 | ||
|   /* Return if an attribute from nested attribute set exists.
 | ||
| 
 | ||
|      Example:
 | ||
|        x = { a = { b = 3; }; }
 | ||
|        hasAttrByPath ["a" "b"] x
 | ||
|        => true
 | ||
|        hasAttrByPath ["z" "z"] x
 | ||
|        => false
 | ||
| 
 | ||
|   */
 | ||
|   hasAttrByPath = attrPath: e:
 | ||
|     let attr = head attrPath;
 | ||
|     in
 | ||
|       if attrPath == [] then true
 | ||
|       else if e ? ${attr}
 | ||
|       then hasAttrByPath (tail attrPath) e.${attr}
 | ||
|       else false;
 | ||
| 
 | ||
| 
 | ||
|   /* Return nested attribute set in which an attribute is set.
 | ||
| 
 | ||
|      Example:
 | ||
|        setAttrByPath ["a" "b"] 3
 | ||
|        => { a = { b = 3; }; }
 | ||
|   */
 | ||
|   setAttrByPath = attrPath: value:
 | ||
|     if attrPath == [] then value
 | ||
|     else listToAttrs
 | ||
|       [ { name = head attrPath; value = setAttrByPath (tail attrPath) value; } ];
 | ||
| 
 | ||
| 
 | ||
|   /* Like `getAttrPath' without a default value. If it doesn't find the
 | ||
|      path it will throw.
 | ||
| 
 | ||
|      Example:
 | ||
|        x = { a = { b = 3; }; }
 | ||
|        getAttrFromPath ["a" "b"] x
 | ||
|        => 3
 | ||
|        getAttrFromPath ["z" "z"] x
 | ||
|        => error: cannot find attribute `z.z'
 | ||
|   */
 | ||
|   getAttrFromPath = attrPath: set:
 | ||
|     let errorMsg = "cannot find attribute `" + concatStringsSep "." attrPath + "'";
 | ||
|     in attrByPath attrPath (abort errorMsg) set;
 | ||
| 
 | ||
| 
 | ||
|   /* Return the specified attributes from a set.
 | ||
| 
 | ||
|      Example:
 | ||
|        attrVals ["a" "b" "c"] as
 | ||
|        => [as.a as.b as.c]
 | ||
|   */
 | ||
|   attrVals = nameList: set: map (x: set.${x}) nameList;
 | ||
| 
 | ||
| 
 | ||
|   /* Return the values of all attributes in the given set, sorted by
 | ||
|      attribute name.
 | ||
| 
 | ||
|      Example:
 | ||
|        attrValues {c = 3; a = 1; b = 2;}
 | ||
|        => [1 2 3]
 | ||
|   */
 | ||
|   attrValues = builtins.attrValues or (attrs: attrVals (attrNames attrs) attrs);
 | ||
| 
 | ||
| 
 | ||
|   /* Collect each attribute named `attr' from a list of attribute
 | ||
|      sets.  Sets that don't contain the named attribute are ignored.
 | ||
| 
 | ||
|      Example:
 | ||
|        catAttrs "a" [{a = 1;} {b = 0;} {a = 2;}]
 | ||
|        => [1 2]
 | ||
|   */
 | ||
|   catAttrs = builtins.catAttrs or
 | ||
|     (attr: l: concatLists (map (s: if s ? ${attr} then [s.${attr}] else []) l));
 | ||
| 
 | ||
| 
 | ||
|   /* Filter an attribute set by removing all attributes for which the
 | ||
|      given predicate return false.
 | ||
| 
 | ||
|      Example:
 | ||
|        filterAttrs (n: v: n == "foo") { foo = 1; bar = 2; }
 | ||
|        => { foo = 1; }
 | ||
|   */
 | ||
|   filterAttrs = pred: set:
 | ||
|     listToAttrs (concatMap (name: let v = set.${name}; in if pred name v then [(nameValuePair name v)] else []) (attrNames set));
 | ||
| 
 | ||
| 
 | ||
|   /* Filter an attribute set recursively by removing all attributes for
 | ||
|      which the given predicate return false.
 | ||
| 
 | ||
|      Example:
 | ||
|        filterAttrsRecursive (n: v: v != null) { foo = { bar = null; }; }
 | ||
|        => { foo = {}; }
 | ||
|   */
 | ||
|   filterAttrsRecursive = pred: set:
 | ||
|     listToAttrs (
 | ||
|       concatMap (name:
 | ||
|         let v = set.${name}; in
 | ||
|         if pred name v then [
 | ||
|           (nameValuePair name (
 | ||
|             if isAttrs v then filterAttrsRecursive pred v
 | ||
|             else v
 | ||
|           ))
 | ||
|         ] else []
 | ||
|       ) (attrNames set)
 | ||
|     );
 | ||
| 
 | ||
|   /* Apply fold functions to values grouped by key.
 | ||
| 
 | ||
|      Example:
 | ||
|        foldAttrs (n: a: [n] ++ a) [] [{ a = 2; } { a = 3; }]
 | ||
|        => { a = [ 2 3 ]; }
 | ||
|   */
 | ||
|   foldAttrs = op: nul: list_of_attrs:
 | ||
|     fold (n: a:
 | ||
|         fold (name: o:
 | ||
|           o // { ${name} = op n.${name} (a.${name} or nul); }
 | ||
|         ) a (attrNames n)
 | ||
|     ) {} list_of_attrs;
 | ||
| 
 | ||
| 
 | ||
|   /* Recursively collect sets that verify a given predicate named `pred'
 | ||
|      from the set `attrs'.  The recursion is stopped when the predicate is
 | ||
|      verified.
 | ||
| 
 | ||
|      Type:
 | ||
|        collect ::
 | ||
|          (AttrSet -> Bool) -> AttrSet -> [x]
 | ||
| 
 | ||
|      Example:
 | ||
|        collect isList { a = { b = ["b"]; }; c = [1]; }
 | ||
|        => [["b"] [1]]
 | ||
| 
 | ||
|        collect (x: x ? outPath)
 | ||
|           { a = { outPath = "a/"; }; b = { outPath = "b/"; }; }
 | ||
|        => [{ outPath = "a/"; } { outPath = "b/"; }]
 | ||
|   */
 | ||
|   collect = pred: attrs:
 | ||
|     if pred attrs then
 | ||
|       [ attrs ]
 | ||
|     else if isAttrs attrs then
 | ||
|       concatMap (collect pred) (attrValues attrs)
 | ||
|     else
 | ||
|       [];
 | ||
| 
 | ||
| 
 | ||
|   /* Utility function that creates a {name, value} pair as expected by
 | ||
|      builtins.listToAttrs.
 | ||
| 
 | ||
|      Example:
 | ||
|        nameValuePair "some" 6
 | ||
|        => { name = "some"; value = 6; }
 | ||
|   */
 | ||
|   nameValuePair = name: value: { inherit name value; };
 | ||
| 
 | ||
| 
 | ||
|   /* Apply a function to each element in an attribute set.  The
 | ||
|      function takes two arguments --- the attribute name and its value
 | ||
|      --- and returns the new value for the attribute.  The result is a
 | ||
|      new attribute set.
 | ||
| 
 | ||
|      Example:
 | ||
|        mapAttrs (name: value: name + "-" + value)
 | ||
|           { x = "foo"; y = "bar"; }
 | ||
|        => { x = "x-foo"; y = "y-bar"; }
 | ||
|   */
 | ||
|   mapAttrs = builtins.mapAttrs or
 | ||
|     (f: set:
 | ||
|       listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set)));
 | ||
| 
 | ||
| 
 | ||
|   /* Like `mapAttrs', but allows the name of each attribute to be
 | ||
|      changed in addition to the value.  The applied function should
 | ||
|      return both the new name and value as a `nameValuePair'.
 | ||
| 
 | ||
|      Example:
 | ||
|        mapAttrs' (name: value: nameValuePair ("foo_" + name) ("bar-" + value))
 | ||
|           { x = "a"; y = "b"; }
 | ||
|        => { foo_x = "bar-a"; foo_y = "bar-b"; }
 | ||
|   */
 | ||
|   mapAttrs' = f: set:
 | ||
|     listToAttrs (map (attr: f attr set.${attr}) (attrNames set));
 | ||
| 
 | ||
| 
 | ||
|   /* Call a function for each attribute in the given set and return
 | ||
|      the result in a list.
 | ||
| 
 | ||
|      Example:
 | ||
|        mapAttrsToList (name: value: name + value)
 | ||
|           { x = "a"; y = "b"; }
 | ||
|        => [ "xa" "yb" ]
 | ||
|   */
 | ||
|   mapAttrsToList = f: attrs:
 | ||
|     map (name: f name attrs.${name}) (attrNames attrs);
 | ||
| 
 | ||
| 
 | ||
|   /* Like `mapAttrs', except that it recursively applies itself to
 | ||
|      attribute sets.  Also, the first argument of the argument
 | ||
|      function is a *list* of the names of the containing attributes.
 | ||
| 
 | ||
|      Type:
 | ||
|        mapAttrsRecursive ::
 | ||
|          ([String] -> a -> b) -> AttrSet -> AttrSet
 | ||
| 
 | ||
|      Example:
 | ||
|        mapAttrsRecursive (path: value: concatStringsSep "-" (path ++ [value]))
 | ||
|          { n = { a = "A"; m = { b = "B"; c = "C"; }; }; d = "D"; }
 | ||
|        => { n = { a = "n-a-A"; m = { b = "n-m-b-B"; c = "n-m-c-C"; }; }; d = "d-D"; }
 | ||
|   */
 | ||
|   mapAttrsRecursive = mapAttrsRecursiveCond (as: true);
 | ||
| 
 | ||
| 
 | ||
|   /* Like `mapAttrsRecursive', but it takes an additional predicate
 | ||
|      function that tells it whether to recursive into an attribute
 | ||
|      set.  If it returns false, `mapAttrsRecursiveCond' does not
 | ||
|      recurse, but does apply the map function.  It is returns true, it
 | ||
|      does recurse, and does not apply the map function.
 | ||
| 
 | ||
|      Type:
 | ||
|        mapAttrsRecursiveCond ::
 | ||
|          (AttrSet -> Bool) -> ([String] -> a -> b) -> AttrSet -> AttrSet
 | ||
| 
 | ||
|      Example:
 | ||
|        # To prevent recursing into derivations (which are attribute
 | ||
|        # sets with the attribute "type" equal to "derivation"):
 | ||
|        mapAttrsRecursiveCond
 | ||
|          (as: !(as ? "type" && as.type == "derivation"))
 | ||
|          (x: ... do something ...)
 | ||
|          attrs
 | ||
|   */
 | ||
|   mapAttrsRecursiveCond = cond: f: set:
 | ||
|     let
 | ||
|       recurse = path: set:
 | ||
|         let
 | ||
|           g =
 | ||
|             name: value:
 | ||
|             if isAttrs value && cond value
 | ||
|               then recurse (path ++ [name]) value
 | ||
|               else f (path ++ [name]) value;
 | ||
|         in mapAttrs g set;
 | ||
|     in recurse [] set;
 | ||
| 
 | ||
| 
 | ||
|   /* Generate an attribute set by mapping a function over a list of
 | ||
|      attribute names.
 | ||
| 
 | ||
|      Example:
 | ||
|        genAttrs [ "foo" "bar" ] (name: "x_" + name)
 | ||
|        => { foo = "x_foo"; bar = "x_bar"; }
 | ||
|   */
 | ||
|   genAttrs = names: f:
 | ||
|     listToAttrs (map (n: nameValuePair n (f n)) names);
 | ||
| 
 | ||
| 
 | ||
|   /* Check whether the argument is a derivation. Any set with
 | ||
|      { type = "derivation"; } counts as a derivation.
 | ||
| 
 | ||
|      Example:
 | ||
|        nixpkgs = import <nixpkgs> {}
 | ||
|        isDerivation nixpkgs.ruby
 | ||
|        => true
 | ||
|        isDerivation "foobar"
 | ||
|        => false
 | ||
|   */
 | ||
|   isDerivation = x: isAttrs x && x ? type && x.type == "derivation";
 | ||
| 
 | ||
|   /* Converts a store path to a fake derivation. */
 | ||
|   toDerivation = path:
 | ||
|     let
 | ||
|       path' = builtins.storePath path;
 | ||
|       res =
 | ||
|         { type = "derivation";
 | ||
|           name = builtins.unsafeDiscardStringContext (builtins.substring 33 (-1) (baseNameOf path'));
 | ||
|           outPath = path';
 | ||
|           outputs = [ "out" ];
 | ||
|           out = res;
 | ||
|           outputName = "out";
 | ||
|         };
 | ||
|     in res;
 | ||
| 
 | ||
| 
 | ||
|   /* If `cond' is true, return the attribute set `as',
 | ||
|      otherwise an empty attribute set.
 | ||
| 
 | ||
|      Example:
 | ||
|        optionalAttrs (true) { my = "set"; }
 | ||
|        => { my = "set"; }
 | ||
|        optionalAttrs (false) { my = "set"; }
 | ||
|        => { }
 | ||
|   */
 | ||
|   optionalAttrs = cond: as: if cond then as else {};
 | ||
| 
 | ||
| 
 | ||
|   /* Merge sets of attributes and use the function f to merge attributes
 | ||
|      values.
 | ||
| 
 | ||
|      Example:
 | ||
|        zipAttrsWithNames ["a"] (name: vs: vs) [{a = "x";} {a = "y"; b = "z";}]
 | ||
|        => { a = ["x" "y"]; }
 | ||
|   */
 | ||
|   zipAttrsWithNames = names: f: sets:
 | ||
|     listToAttrs (map (name: {
 | ||
|       inherit name;
 | ||
|       value = f name (catAttrs name sets);
 | ||
|     }) names);
 | ||
| 
 | ||
|   /* Implementation note: Common names  appear multiple times in the list of
 | ||
|      names, hopefully this does not affect the system because the maximal
 | ||
|      laziness avoid computing twice the same expression and listToAttrs does
 | ||
|      not care about duplicated attribute names.
 | ||
| 
 | ||
|      Example:
 | ||
|        zipAttrsWith (name: values: values) [{a = "x";} {a = "y"; b = "z";}]
 | ||
|        => { a = ["x" "y"]; b = ["z"] }
 | ||
|   */
 | ||
|   zipAttrsWith = f: sets: zipAttrsWithNames (concatMap attrNames sets) f sets;
 | ||
|   /* Like `zipAttrsWith' with `(name: values: value)' as the function.
 | ||
| 
 | ||
|     Example:
 | ||
|       zipAttrs [{a = "x";} {a = "y"; b = "z";}]
 | ||
|       => { a = ["x" "y"]; b = ["z"] }
 | ||
|   */
 | ||
|   zipAttrs = zipAttrsWith (name: values: values);
 | ||
| 
 | ||
|   /* Does the same as the update operator '//' except that attributes are
 | ||
|      merged until the given predicate is verified.  The predicate should
 | ||
|      accept 3 arguments which are the path to reach the attribute, a part of
 | ||
|      the first attribute set and a part of the second attribute set.  When
 | ||
|      the predicate is verified, the value of the first attribute set is
 | ||
|      replaced by the value of the second attribute set.
 | ||
| 
 | ||
|      Example:
 | ||
|        recursiveUpdateUntil (path: l: r: path == ["foo"]) {
 | ||
|          # first attribute set
 | ||
|          foo.bar = 1;
 | ||
|          foo.baz = 2;
 | ||
|          bar = 3;
 | ||
|        } {
 | ||
|          #second attribute set
 | ||
|          foo.bar = 1;
 | ||
|          foo.quz = 2;
 | ||
|          baz = 4;
 | ||
|        }
 | ||
| 
 | ||
|        returns: {
 | ||
|          foo.bar = 1; # 'foo.*' from the second set
 | ||
|          foo.quz = 2; #
 | ||
|          bar = 3;     # 'bar' from the first set
 | ||
|          baz = 4;     # 'baz' from the second set
 | ||
|        }
 | ||
| 
 | ||
|      */
 | ||
|   recursiveUpdateUntil = pred: lhs: rhs:
 | ||
|     let f = attrPath:
 | ||
|       zipAttrsWith (n: values:
 | ||
|         let here = attrPath ++ [n]; in
 | ||
|         if tail values == []
 | ||
|         || pred here (head (tail values)) (head values) then
 | ||
|           head values
 | ||
|         else
 | ||
|           f here values
 | ||
|       );
 | ||
|     in f [] [rhs lhs];
 | ||
| 
 | ||
|   /* A recursive variant of the update operator ‘//’.  The recursion
 | ||
|      stops when one of the attribute values is not an attribute set,
 | ||
|      in which case the right hand side value takes precedence over the
 | ||
|      left hand side value.
 | ||
| 
 | ||
|      Example:
 | ||
|        recursiveUpdate {
 | ||
|          boot.loader.grub.enable = true;
 | ||
|          boot.loader.grub.device = "/dev/hda";
 | ||
|        } {
 | ||
|          boot.loader.grub.device = "";
 | ||
|        }
 | ||
| 
 | ||
|        returns: {
 | ||
|          boot.loader.grub.enable = true;
 | ||
|          boot.loader.grub.device = "";
 | ||
|        }
 | ||
| 
 | ||
|      */
 | ||
|   recursiveUpdate = lhs: rhs:
 | ||
|     recursiveUpdateUntil (path: lhs: rhs:
 | ||
|       !(isAttrs lhs && isAttrs rhs)
 | ||
|     ) lhs rhs;
 | ||
| 
 | ||
|   /* Returns true if the pattern is contained in the set. False otherwise.
 | ||
| 
 | ||
|      Example:
 | ||
|        matchAttrs { cpu = {}; } { cpu = { bits = 64; }; }
 | ||
|        => true
 | ||
|    */
 | ||
|   matchAttrs = pattern: attrs: assert isAttrs pattern;
 | ||
|     fold and true (attrValues (zipAttrsWithNames (attrNames pattern) (n: values:
 | ||
|       let pat = head values; val = head (tail values); in
 | ||
|       if length values == 1 then false
 | ||
|       else if isAttrs pat then isAttrs val && matchAttrs pat val
 | ||
|       else pat == val
 | ||
|     ) [pattern attrs]));
 | ||
| 
 | ||
|   /* Override only the attributes that are already present in the old set
 | ||
|     useful for deep-overriding.
 | ||
| 
 | ||
|     Example:
 | ||
|       x = { a = { b = 4; c = 3; }; }
 | ||
|       overrideExisting x { a = { b = 6; d = 2; }; }
 | ||
|       => { a = { b = 6; d = 2; }; }
 | ||
|   */
 | ||
|   overrideExisting = old: new:
 | ||
|     mapAttrs (name: value: new.${name} or value) old;
 | ||
| 
 | ||
|   /* Get a package output.
 | ||
|      If no output is found, fallback to `.out` and then to the default.
 | ||
| 
 | ||
|      Example:
 | ||
|        getOutput "dev" pkgs.openssl
 | ||
|        => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev"
 | ||
|   */
 | ||
|   getOutput = output: pkg:
 | ||
|     if pkg.outputUnspecified or false
 | ||
|       then pkg.${output} or pkg.out or pkg
 | ||
|       else pkg;
 | ||
| 
 | ||
|   getBin = getOutput "bin";
 | ||
|   getLib = getOutput "lib";
 | ||
|   getDev = getOutput "dev";
 | ||
| 
 | ||
|   /* Pick the outputs of packages to place in buildInputs */
 | ||
|   chooseDevOutputs = drvs: builtins.map getDev drvs;
 | ||
| 
 | ||
|   /*** deprecated stuff ***/
 | ||
| 
 | ||
|   zipWithNames = zipAttrsWithNames;
 | ||
|   zip = builtins.trace
 | ||
|     "lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith;
 | ||
| 
 | ||
| }
 | 
