2017-07-29 00:05:35 +00:00
|
|
|
|
{ lib }:
|
2009-02-09 16:51:03 +00:00
|
|
|
|
# Operations on attribute sets.
|
|
|
|
|
|
2017-02-09 01:52:13 +00:00
|
|
|
|
let
|
2014-12-30 23:07:29 +00:00
|
|
|
|
inherit (builtins) head tail length;
|
2021-12-27 22:53:15 +00:00
|
|
|
|
inherit (lib.trivial) id;
|
2021-12-02 17:12:51 +00:00
|
|
|
|
inherit (lib.strings) concatStringsSep concatMapStringsSep escapeNixIdentifier sanitizeDerivationName;
|
2021-11-22 19:34:01 +00:00
|
|
|
|
inherit (lib.lists) foldr foldl' concatMap concatLists elemAt all partition groupBy take foldl;
|
2017-02-09 01:52:13 +00:00
|
|
|
|
in
|
2009-02-09 16:51:03 +00:00
|
|
|
|
|
|
|
|
|
rec {
|
2012-08-13 04:08:21 +00:00
|
|
|
|
inherit (builtins) attrNames listToAttrs hasAttr isAttrs getAttr;
|
2009-02-09 16:51:03 +00:00
|
|
|
|
|
|
|
|
|
|
2016-02-28 21:49:42 +00:00
|
|
|
|
/* Return an attribute from nested attribute sets.
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
x = { a = { b = 3; }; }
|
|
|
|
|
attrByPath ["a" "b"] 6 x
|
|
|
|
|
=> 3
|
|
|
|
|
attrByPath ["z" "z"] 6 x
|
|
|
|
|
=> 6
|
|
|
|
|
*/
|
2009-05-24 10:57:41 +00:00
|
|
|
|
attrByPath = attrPath: default: e:
|
2009-02-09 16:51:03 +00:00
|
|
|
|
let attr = head attrPath;
|
|
|
|
|
in
|
|
|
|
|
if attrPath == [] then e
|
2014-10-04 22:03:52 +00:00
|
|
|
|
else if e ? ${attr}
|
|
|
|
|
then attrByPath (tail attrPath) default e.${attr}
|
2009-02-09 16:51:03 +00:00
|
|
|
|
else default;
|
|
|
|
|
|
2015-12-04 15:17:45 +00:00
|
|
|
|
/* Return if an attribute from nested attribute set exists.
|
2016-02-28 21:49:42 +00:00
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
x = { a = { b = 3; }; }
|
|
|
|
|
hasAttrByPath ["a" "b"] x
|
|
|
|
|
=> true
|
|
|
|
|
hasAttrByPath ["z" "z"] x
|
|
|
|
|
=> false
|
|
|
|
|
|
|
|
|
|
*/
|
2015-12-04 15:17:45 +00:00
|
|
|
|
hasAttrByPath = attrPath: e:
|
|
|
|
|
let attr = head attrPath;
|
|
|
|
|
in
|
|
|
|
|
if attrPath == [] then true
|
|
|
|
|
else if e ? ${attr}
|
|
|
|
|
then hasAttrByPath (tail attrPath) e.${attr}
|
|
|
|
|
else false;
|
|
|
|
|
|
2012-10-23 13:35:48 +00:00
|
|
|
|
|
2016-02-28 21:49:42 +00:00
|
|
|
|
/* Return nested attribute set in which an attribute is set.
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
setAttrByPath ["a" "b"] 3
|
|
|
|
|
=> { a = { b = 3; }; }
|
|
|
|
|
*/
|
2009-09-28 18:22:44 +00:00
|
|
|
|
setAttrByPath = attrPath: value:
|
2021-08-20 16:24:39 +00:00
|
|
|
|
let
|
|
|
|
|
len = length attrPath;
|
|
|
|
|
atDepth = n:
|
|
|
|
|
if n == len
|
|
|
|
|
then value
|
|
|
|
|
else { ${elemAt attrPath n} = atDepth (n + 1); };
|
|
|
|
|
in atDepth 0;
|
2009-03-06 23:21:14 +00:00
|
|
|
|
|
2020-01-20 21:42:38 +00:00
|
|
|
|
/* Like `attrByPath' without a default value. If it doesn't find the
|
2016-02-28 21:49:42 +00:00
|
|
|
|
path it will throw.
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
x = { a = { b = 3; }; }
|
|
|
|
|
getAttrFromPath ["a" "b"] x
|
|
|
|
|
=> 3
|
|
|
|
|
getAttrFromPath ["z" "z"] x
|
|
|
|
|
=> error: cannot find attribute `z.z'
|
|
|
|
|
*/
|
2021-12-27 22:53:15 +00:00
|
|
|
|
getAttrFromPath = attrPath:
|
2009-03-10 15:18:38 +00:00
|
|
|
|
let errorMsg = "cannot find attribute `" + concatStringsSep "." attrPath + "'";
|
2021-12-27 22:53:15 +00:00
|
|
|
|
in attrByPath attrPath (abort errorMsg);
|
2012-10-23 13:35:48 +00:00
|
|
|
|
|
2009-03-06 23:21:14 +00:00
|
|
|
|
|
2021-11-22 19:34:01 +00:00
|
|
|
|
/* Update or set specific paths of an attribute set.
|
|
|
|
|
|
|
|
|
|
Takes a list of updates to apply and an attribute set to apply them to,
|
|
|
|
|
and returns the attribute set with the updates applied. Updates are
|
|
|
|
|
represented as { path = ...; update = ...; } values, where `path` is a
|
|
|
|
|
list of strings representing the attribute path that should be updated,
|
|
|
|
|
and `update` is a function that takes the old value at that attribute path
|
|
|
|
|
as an argument and returns the new
|
|
|
|
|
value it should be.
|
|
|
|
|
|
|
|
|
|
Properties:
|
|
|
|
|
- Updates to deeper attribute paths are applied before updates to more
|
|
|
|
|
shallow attribute paths
|
|
|
|
|
- Multiple updates to the same attribute path are applied in the order
|
|
|
|
|
they appear in the update list
|
|
|
|
|
- If any but the last `path` element leads into a value that is not an
|
|
|
|
|
attribute set, an error is thrown
|
|
|
|
|
- If there is an update for an attribute path that doesn't exist,
|
|
|
|
|
accessing the argument in the update function causes an error, but
|
|
|
|
|
intermediate attribute sets are implicitly created as needed
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
updateManyAttrsByPath [
|
|
|
|
|
{
|
|
|
|
|
path = [ "a" "b" ];
|
|
|
|
|
update = old: { d = old.c; };
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
path = [ "a" "b" "c" ];
|
|
|
|
|
update = old: old + 1;
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
path = [ "x" "y" ];
|
|
|
|
|
update = old: "xy";
|
|
|
|
|
}
|
|
|
|
|
] { a.b.c = 0; }
|
|
|
|
|
=> { a = { b = { d = 1; }; }; x = { y = "xy"; }; }
|
|
|
|
|
*/
|
|
|
|
|
updateManyAttrsByPath = let
|
|
|
|
|
# When recursing into attributes, instead of updating the `path` of each
|
|
|
|
|
# update using `tail`, which needs to allocate an entirely new list,
|
|
|
|
|
# we just pass a prefix length to use and make sure to only look at the
|
|
|
|
|
# path without the prefix length, so that we can reuse the original list
|
|
|
|
|
# entries.
|
|
|
|
|
go = prefixLength: hasValue: value: updates:
|
|
|
|
|
let
|
|
|
|
|
# Splits updates into ones on this level (split.right)
|
|
|
|
|
# And ones on levels further down (split.wrong)
|
|
|
|
|
split = partition (el: length el.path == prefixLength) updates;
|
|
|
|
|
|
|
|
|
|
# Groups updates on further down levels into the attributes they modify
|
|
|
|
|
nested = groupBy (el: elemAt el.path prefixLength) split.wrong;
|
|
|
|
|
|
|
|
|
|
# Applies only nested modification to the input value
|
|
|
|
|
withNestedMods =
|
|
|
|
|
# Return the value directly if we don't have any nested modifications
|
|
|
|
|
if split.wrong == [] then
|
|
|
|
|
if hasValue then value
|
|
|
|
|
else
|
|
|
|
|
# Throw an error if there is no value. This `head` call here is
|
|
|
|
|
# safe, but only in this branch since `go` could only be called
|
|
|
|
|
# with `hasValue == false` for nested updates, in which case
|
|
|
|
|
# it's also always called with at least one update
|
|
|
|
|
let updatePath = (head split.right).path; in
|
|
|
|
|
throw
|
|
|
|
|
( "updateManyAttrsByPath: Path '${showAttrPath updatePath}' does "
|
|
|
|
|
+ "not exist in the given value, but the first update to this "
|
|
|
|
|
+ "path tries to access the existing value.")
|
|
|
|
|
else
|
|
|
|
|
# If there are nested modifications, try to apply them to the value
|
|
|
|
|
if ! hasValue then
|
|
|
|
|
# But if we don't have a value, just use an empty attribute set
|
|
|
|
|
# as the value, but simplify the code a bit
|
|
|
|
|
mapAttrs (name: go (prefixLength + 1) false null) nested
|
|
|
|
|
else if isAttrs value then
|
|
|
|
|
# If we do have a value and it's an attribute set, override it
|
|
|
|
|
# with the nested modifications
|
|
|
|
|
value //
|
|
|
|
|
mapAttrs (name: go (prefixLength + 1) (value ? ${name}) value.${name}) nested
|
|
|
|
|
else
|
|
|
|
|
# However if it's not an attribute set, we can't apply the nested
|
|
|
|
|
# modifications, throw an error
|
|
|
|
|
let updatePath = (head split.wrong).path; in
|
|
|
|
|
throw
|
|
|
|
|
( "updateManyAttrsByPath: Path '${showAttrPath updatePath}' needs to "
|
|
|
|
|
+ "be updated, but path '${showAttrPath (take prefixLength updatePath)}' "
|
|
|
|
|
+ "of the given value is not an attribute set, so we can't "
|
|
|
|
|
+ "update an attribute inside of it.");
|
|
|
|
|
|
|
|
|
|
# We get the final result by applying all the updates on this level
|
|
|
|
|
# after having applied all the nested updates
|
|
|
|
|
# We use foldl instead of foldl' so that in case of multiple updates,
|
|
|
|
|
# intermediate values aren't evaluated if not needed
|
|
|
|
|
in foldl (acc: el: el.update acc) withNestedMods split.right;
|
|
|
|
|
|
|
|
|
|
in updates: value: go 0 true value updates;
|
|
|
|
|
|
2009-03-10 15:18:38 +00:00
|
|
|
|
/* Return the specified attributes from a set.
|
2009-03-06 23:21:14 +00:00
|
|
|
|
|
2009-03-10 15:18:38 +00:00
|
|
|
|
Example:
|
|
|
|
|
attrVals ["a" "b" "c"] as
|
|
|
|
|
=> [as.a as.b as.c]
|
|
|
|
|
*/
|
2014-10-04 22:03:52 +00:00
|
|
|
|
attrVals = nameList: set: map (x: set.${x}) nameList;
|
2009-02-09 16:51:03 +00:00
|
|
|
|
|
2009-03-10 15:18:38 +00:00
|
|
|
|
|
|
|
|
|
/* 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]
|
|
|
|
|
*/
|
2014-10-04 16:30:35 +00:00
|
|
|
|
attrValues = builtins.attrValues or (attrs: attrVals (attrNames attrs) attrs);
|
2009-03-10 15:18:38 +00:00
|
|
|
|
|
|
|
|
|
|
2018-12-11 21:18:22 +00:00
|
|
|
|
/* Given a set of attribute names, return the set of the corresponding
|
|
|
|
|
attributes from the given set.
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
getAttrs [ "a" "b" ] { a = 1; b = 2; c = 3; }
|
|
|
|
|
=> { a = 1; b = 2; }
|
|
|
|
|
*/
|
|
|
|
|
getAttrs = names: attrs: genAttrs names (name: attrs.${name});
|
|
|
|
|
|
2009-03-10 15:18:38 +00:00
|
|
|
|
/* 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]
|
|
|
|
|
*/
|
2014-10-04 16:30:35 +00:00
|
|
|
|
catAttrs = builtins.catAttrs or
|
2014-10-04 22:03:52 +00:00
|
|
|
|
(attr: l: concatLists (map (s: if s ? ${attr} then [s.${attr}] else []) l));
|
2009-03-10 15:18:38 +00:00
|
|
|
|
|
|
|
|
|
|
2012-04-05 15:37:52 +00:00
|
|
|
|
/* 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:
|
2015-07-23 14:12:25 +00:00
|
|
|
|
listToAttrs (concatMap (name: let v = set.${name}; in if pred name v then [(nameValuePair name v)] else []) (attrNames set));
|
2012-04-05 15:37:52 +00:00
|
|
|
|
|
2012-10-23 13:35:48 +00:00
|
|
|
|
|
2017-04-19 19:41:28 +00:00
|
|
|
|
/* Filter an attribute set recursively by removing all attributes for
|
2015-09-18 22:16:06 +00:00
|
|
|
|
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)
|
|
|
|
|
);
|
|
|
|
|
|
2016-02-28 21:49:42 +00:00
|
|
|
|
/* Apply fold functions to values grouped by key.
|
|
|
|
|
|
|
|
|
|
Example:
|
2022-05-10 04:52:20 +00:00
|
|
|
|
foldAttrs (item: acc: [item] ++ acc) [] [{ a = 2; } { a = 3; }]
|
2016-02-28 21:49:42 +00:00
|
|
|
|
=> { a = [ 2 3 ]; }
|
2012-08-28 12:40:24 +00:00
|
|
|
|
*/
|
2021-12-27 22:53:15 +00:00
|
|
|
|
foldAttrs = op: nul:
|
2021-01-25 06:57:48 +00:00
|
|
|
|
foldr (n: a:
|
|
|
|
|
foldr (name: o:
|
2018-08-13 23:01:08 +00:00
|
|
|
|
o // { ${name} = op n.${name} (a.${name} or nul); }
|
2012-08-28 12:40:24 +00:00
|
|
|
|
) a (attrNames n)
|
2021-12-27 22:53:15 +00:00
|
|
|
|
) {};
|
2012-04-05 15:37:52 +00:00
|
|
|
|
|
2012-10-23 13:35:48 +00:00
|
|
|
|
|
2009-06-11 16:03:33 +00:00
|
|
|
|
/* 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 ::
|
2015-10-23 11:04:10 +00:00
|
|
|
|
(AttrSet -> Bool) -> AttrSet -> [x]
|
2009-06-11 16:03:33 +00:00
|
|
|
|
|
|
|
|
|
Example:
|
2013-11-12 12:48:19 +00:00
|
|
|
|
collect isList { a = { b = ["b"]; }; c = [1]; }
|
2009-09-29 15:34:19 +00:00
|
|
|
|
=> [["b"] [1]]
|
2009-06-11 16:03:33 +00:00
|
|
|
|
|
|
|
|
|
collect (x: x ? outPath)
|
|
|
|
|
{ a = { outPath = "a/"; }; b = { outPath = "b/"; }; }
|
|
|
|
|
=> [{ outPath = "a/"; } { outPath = "b/"; }]
|
|
|
|
|
*/
|
|
|
|
|
collect = pred: attrs:
|
|
|
|
|
if pred attrs then
|
|
|
|
|
[ attrs ]
|
2013-11-12 12:50:45 +00:00
|
|
|
|
else if isAttrs attrs then
|
2009-06-11 16:03:33 +00:00
|
|
|
|
concatMap (collect pred) (attrValues attrs)
|
|
|
|
|
else
|
|
|
|
|
[];
|
|
|
|
|
|
2021-01-25 15:59:46 +00:00
|
|
|
|
/* Return the cartesian product of attribute set value combinations.
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
cartesianProductOfSets { a = [ 1 2 ]; b = [ 10 20 ]; }
|
|
|
|
|
=> [
|
|
|
|
|
{ a = 1; b = 10; }
|
|
|
|
|
{ a = 1; b = 20; }
|
|
|
|
|
{ a = 2; b = 10; }
|
|
|
|
|
{ a = 2; b = 20; }
|
|
|
|
|
]
|
|
|
|
|
*/
|
|
|
|
|
cartesianProductOfSets = attrsOfLists:
|
2021-08-20 16:24:39 +00:00
|
|
|
|
foldl' (listOfAttrs: attrName:
|
2021-01-25 15:59:46 +00:00
|
|
|
|
concatMap (attrs:
|
|
|
|
|
map (listValue: attrs // { ${attrName} = listValue; }) attrsOfLists.${attrName}
|
|
|
|
|
) listOfAttrs
|
|
|
|
|
) [{}] (attrNames attrsOfLists);
|
|
|
|
|
|
2009-06-11 16:03:33 +00:00
|
|
|
|
|
2009-03-10 15:18:38 +00:00
|
|
|
|
/* Utility function that creates a {name, value} pair as expected by
|
2016-02-28 21:49:42 +00:00
|
|
|
|
builtins.listToAttrs.
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
nameValuePair "some" 6
|
|
|
|
|
=> { name = "some"; value = 6; }
|
|
|
|
|
*/
|
2009-03-10 15:18:38 +00:00
|
|
|
|
nameValuePair = name: value: { inherit name value; };
|
|
|
|
|
|
2012-10-23 13:35:48 +00:00
|
|
|
|
|
2009-03-10 15:18:38 +00:00
|
|
|
|
/* 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)
|
2012-05-25 17:01:58 +00:00
|
|
|
|
{ x = "foo"; y = "bar"; }
|
|
|
|
|
=> { x = "x-foo"; y = "y-bar"; }
|
2009-03-10 15:18:38 +00:00
|
|
|
|
*/
|
2018-07-05 03:01:13 +00:00
|
|
|
|
mapAttrs = builtins.mapAttrs or
|
|
|
|
|
(f: set:
|
|
|
|
|
listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set)));
|
2012-05-25 17:01:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 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'.
|
2012-10-23 13:35:48 +00:00
|
|
|
|
|
2012-05-25 17:01:58 +00:00
|
|
|
|
Example:
|
|
|
|
|
mapAttrs' (name: value: nameValuePair ("foo_" + name) ("bar-" + value))
|
|
|
|
|
{ x = "a"; y = "b"; }
|
|
|
|
|
=> { foo_x = "bar-a"; foo_y = "bar-b"; }
|
|
|
|
|
*/
|
|
|
|
|
mapAttrs' = f: set:
|
2014-10-04 22:03:52 +00:00
|
|
|
|
listToAttrs (map (attr: f attr set.${attr}) (attrNames set));
|
2012-10-23 13:35:48 +00:00
|
|
|
|
|
2009-03-10 15:18:38 +00:00
|
|
|
|
|
2012-06-14 19:07:01 +00:00
|
|
|
|
/* Call a function for each attribute in the given set and return
|
|
|
|
|
the result in a list.
|
2012-10-23 13:35:48 +00:00
|
|
|
|
|
2021-05-08 11:55:11 +00:00
|
|
|
|
Type:
|
|
|
|
|
mapAttrsToList ::
|
|
|
|
|
(String -> a -> b) -> AttrSet -> [b]
|
|
|
|
|
|
2012-06-14 19:07:01 +00:00
|
|
|
|
Example:
|
|
|
|
|
mapAttrsToList (name: value: name + value)
|
|
|
|
|
{ x = "a"; y = "b"; }
|
|
|
|
|
=> [ "xa" "yb" ]
|
|
|
|
|
*/
|
|
|
|
|
mapAttrsToList = f: attrs:
|
2014-10-04 22:03:52 +00:00
|
|
|
|
map (name: f name attrs.${name}) (attrNames attrs);
|
2012-10-23 13:35:48 +00:00
|
|
|
|
|
2012-06-14 19:07:01 +00:00
|
|
|
|
|
2009-03-10 15:18:38 +00:00
|
|
|
|
/* Like `mapAttrs', except that it recursively applies itself to
|
2009-03-30 13:19:57 +00:00
|
|
|
|
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
|
2009-03-10 15:18:38 +00:00
|
|
|
|
|
|
|
|
|
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"; }
|
|
|
|
|
*/
|
2009-03-30 13:19:57 +00:00
|
|
|
|
mapAttrsRecursive = mapAttrsRecursiveCond (as: true);
|
|
|
|
|
|
2012-10-23 13:35:48 +00:00
|
|
|
|
|
2009-03-30 13:19:57 +00:00
|
|
|
|
/* Like `mapAttrsRecursive', but it takes an additional predicate
|
2022-02-09 23:15:15 +00:00
|
|
|
|
function that tells it whether to recurse into an attribute
|
2009-03-30 13:19:57 +00:00
|
|
|
|
set. If it returns false, `mapAttrsRecursiveCond' does not
|
2020-06-16 11:48:57 +00:00
|
|
|
|
recurse, but does apply the map function. If it returns true, it
|
2009-03-30 13:19:57 +00:00
|
|
|
|
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
|
2013-03-06 15:33:01 +00:00
|
|
|
|
*/
|
2009-03-30 13:19:57 +00:00
|
|
|
|
mapAttrsRecursiveCond = cond: f: set:
|
2009-03-10 15:18:38 +00:00
|
|
|
|
let
|
2021-12-27 22:53:15 +00:00
|
|
|
|
recurse = path:
|
2009-03-10 15:18:38 +00:00
|
|
|
|
let
|
|
|
|
|
g =
|
|
|
|
|
name: value:
|
2009-03-30 13:19:57 +00:00
|
|
|
|
if isAttrs value && cond value
|
|
|
|
|
then recurse (path ++ [name]) value
|
2009-03-10 15:18:38 +00:00
|
|
|
|
else f (path ++ [name]) value;
|
2021-12-27 22:53:15 +00:00
|
|
|
|
in mapAttrs g;
|
2009-03-30 13:19:57 +00:00
|
|
|
|
in recurse [] set;
|
2009-04-25 14:08:29 +00:00
|
|
|
|
|
|
|
|
|
|
2013-03-06 15:33:01 +00:00
|
|
|
|
/* 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);
|
|
|
|
|
|
|
|
|
|
|
2016-02-28 21:49:42 +00:00
|
|
|
|
/* Check whether the argument is a derivation. Any set with
|
|
|
|
|
{ type = "derivation"; } counts as a derivation.
|
2009-04-25 14:08:29 +00:00
|
|
|
|
|
2016-02-28 21:49:42 +00:00
|
|
|
|
Example:
|
|
|
|
|
nixpkgs = import <nixpkgs> {}
|
|
|
|
|
isDerivation nixpkgs.ruby
|
|
|
|
|
=> true
|
|
|
|
|
isDerivation "foobar"
|
|
|
|
|
=> false
|
|
|
|
|
*/
|
2022-03-09 14:48:30 +00:00
|
|
|
|
isDerivation = x: x.type or null == "derivation";
|
2009-09-10 10:52:51 +00:00
|
|
|
|
|
2016-02-28 21:49:42 +00:00
|
|
|
|
/* Converts a store path to a fake derivation. */
|
2015-08-06 17:55:42 +00:00
|
|
|
|
toDerivation = path:
|
2016-09-19 09:51:11 +00:00
|
|
|
|
let
|
|
|
|
|
path' = builtins.storePath path;
|
|
|
|
|
res =
|
|
|
|
|
{ type = "derivation";
|
2020-01-27 22:03:38 +00:00
|
|
|
|
name = sanitizeDerivationName (builtins.substring 33 (-1) (baseNameOf path'));
|
2016-09-19 09:51:11 +00:00
|
|
|
|
outPath = path';
|
|
|
|
|
outputs = [ "out" ];
|
|
|
|
|
out = res;
|
|
|
|
|
outputName = "out";
|
|
|
|
|
};
|
|
|
|
|
in res;
|
2015-08-06 17:55:42 +00:00
|
|
|
|
|
|
|
|
|
|
2016-02-28 21:49:42 +00:00
|
|
|
|
/* 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"; }
|
|
|
|
|
=> { }
|
|
|
|
|
*/
|
2009-09-10 10:52:51 +00:00
|
|
|
|
optionalAttrs = cond: as: if cond then as else {};
|
2009-09-29 14:57:00 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Merge sets of attributes and use the function f to merge attributes
|
2016-02-28 21:49:42 +00:00
|
|
|
|
values.
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
zipAttrsWithNames ["a"] (name: vs: vs) [{a = "x";} {a = "y"; b = "z";}]
|
|
|
|
|
=> { a = ["x" "y"]; }
|
|
|
|
|
*/
|
2009-09-29 14:57:00 +00:00
|
|
|
|
zipAttrsWithNames = names: f: sets:
|
|
|
|
|
listToAttrs (map (name: {
|
|
|
|
|
inherit name;
|
|
|
|
|
value = f name (catAttrs name sets);
|
|
|
|
|
}) names);
|
|
|
|
|
|
2021-12-27 22:16:14 +00:00
|
|
|
|
/* Implementation note: Common names appear multiple times in the list of
|
2016-02-28 21:49:42 +00:00
|
|
|
|
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"] }
|
|
|
|
|
*/
|
2021-12-25 14:20:26 +00:00
|
|
|
|
zipAttrsWith =
|
|
|
|
|
builtins.zipAttrsWith or (f: sets: zipAttrsWithNames (concatMap attrNames sets) f sets);
|
2019-09-11 17:53:44 +00:00
|
|
|
|
/* Like `zipAttrsWith' with `(name: values: values)' as the function.
|
2009-09-29 14:57:00 +00:00
|
|
|
|
|
2016-02-28 21:49:42 +00:00
|
|
|
|
Example:
|
|
|
|
|
zipAttrs [{a = "x";} {a = "y"; b = "z";}]
|
|
|
|
|
=> { a = ["x" "y"]; b = ["z"] }
|
|
|
|
|
*/
|
2009-09-29 14:57:00 +00:00
|
|
|
|
zipAttrs = zipAttrsWith (name: values: values);
|
|
|
|
|
|
|
|
|
|
/* Does the same as the update operator '//' except that attributes are
|
2017-04-19 19:41:28 +00:00
|
|
|
|
merged until the given predicate is verified. The predicate should
|
2009-11-19 16:07:47 +00:00
|
|
|
|
accept 3 arguments which are the path to reach the attribute, a part of
|
2009-09-29 14:57:00 +00:00
|
|
|
|
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
|
2012-10-23 13:35:48 +00:00
|
|
|
|
foo.quz = 2; #
|
2009-09-29 14:57:00 +00:00
|
|
|
|
bar = 3; # 'bar' from the first set
|
|
|
|
|
baz = 4; # 'baz' from the second set
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
recursiveUpdateUntil = pred: lhs: rhs:
|
|
|
|
|
let f = attrPath:
|
|
|
|
|
zipAttrsWith (n: values:
|
2018-06-06 20:38:40 +00:00
|
|
|
|
let here = attrPath ++ [n]; in
|
2021-12-27 22:16:14 +00:00
|
|
|
|
if length values == 1
|
|
|
|
|
|| pred here (elemAt values 1) (head values) then
|
2009-09-29 14:57:00 +00:00
|
|
|
|
head values
|
|
|
|
|
else
|
2018-06-06 20:38:40 +00:00
|
|
|
|
f here values
|
2009-09-29 14:57:00 +00:00
|
|
|
|
);
|
|
|
|
|
in f [] [rhs lhs];
|
|
|
|
|
|
2016-12-04 00:39:03 +00:00
|
|
|
|
/* A recursive variant of the update operator ‘//’. The recursion
|
2012-11-14 10:38:47 +00:00
|
|
|
|
stops when one of the attribute values is not an attribute set,
|
|
|
|
|
in which case the right hand side value takes precedence over the
|
2009-09-29 14:57:00 +00:00
|
|
|
|
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 = "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*/
|
2021-12-27 22:16:14 +00:00
|
|
|
|
recursiveUpdate = recursiveUpdateUntil (path: lhs: rhs: !(isAttrs lhs && isAttrs rhs));
|
2009-09-29 14:57:00 +00:00
|
|
|
|
|
2016-02-28 21:49:42 +00:00
|
|
|
|
/* Returns true if the pattern is contained in the set. False otherwise.
|
|
|
|
|
|
|
|
|
|
Example:
|
2017-04-01 19:34:38 +00:00
|
|
|
|
matchAttrs { cpu = {}; } { cpu = { bits = 64; }; }
|
2016-02-28 21:49:42 +00:00
|
|
|
|
=> true
|
|
|
|
|
*/
|
2017-04-01 19:34:38 +00:00
|
|
|
|
matchAttrs = pattern: attrs: assert isAttrs pattern;
|
2021-12-27 22:53:15 +00:00
|
|
|
|
all id (attrValues (zipAttrsWithNames (attrNames pattern) (n: values:
|
|
|
|
|
let pat = head values; val = elemAt values 1; in
|
2012-08-13 22:08:35 +00:00
|
|
|
|
if length values == 1 then false
|
2017-04-01 19:34:38 +00:00
|
|
|
|
else if isAttrs pat then isAttrs val && matchAttrs pat val
|
2012-08-13 22:08:35 +00:00
|
|
|
|
else pat == val
|
2009-11-19 17:19:39 +00:00
|
|
|
|
) [pattern attrs]));
|
|
|
|
|
|
2016-02-28 21:49:42 +00:00
|
|
|
|
/* Override only the attributes that are already present in the old set
|
|
|
|
|
useful for deep-overriding.
|
|
|
|
|
|
|
|
|
|
Example:
|
2018-09-17 21:28:47 +00:00
|
|
|
|
overrideExisting {} { a = 1; }
|
|
|
|
|
=> {}
|
|
|
|
|
overrideExisting { b = 2; } { a = 1; }
|
|
|
|
|
=> { b = 2; }
|
|
|
|
|
overrideExisting { a = 3; b = 2; } { a = 1; }
|
|
|
|
|
=> { a = 1; b = 2; }
|
2016-02-28 21:49:42 +00:00
|
|
|
|
*/
|
2010-07-08 15:31:59 +00:00
|
|
|
|
overrideExisting = old: new:
|
2018-09-07 18:59:12 +00:00
|
|
|
|
mapAttrs (name: value: new.${name} or value) old;
|
2010-07-08 15:31:59 +00:00
|
|
|
|
|
2021-12-02 17:12:51 +00:00
|
|
|
|
/* Turns a list of strings into a human-readable description of those
|
|
|
|
|
strings represented as an attribute path. The result of this function is
|
|
|
|
|
not intended to be machine-readable.
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
showAttrPath [ "foo" "10" "bar" ]
|
|
|
|
|
=> "foo.\"10\".bar"
|
|
|
|
|
showAttrPath []
|
|
|
|
|
=> "<root attribute path>"
|
|
|
|
|
*/
|
|
|
|
|
showAttrPath = path:
|
|
|
|
|
if path == [] then "<root attribute path>"
|
|
|
|
|
else concatMapStringsSep "." escapeNixIdentifier path;
|
|
|
|
|
|
2016-04-14 15:13:50 +00:00
|
|
|
|
/* 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:
|
2021-10-15 14:30:58 +00:00
|
|
|
|
if ! pkg ? outputSpecified || ! pkg.outputSpecified
|
2016-04-14 15:13:50 +00:00
|
|
|
|
then pkg.${output} or pkg.out or pkg
|
|
|
|
|
else pkg;
|
|
|
|
|
|
2016-04-14 16:00:39 +00:00
|
|
|
|
getBin = getOutput "bin";
|
|
|
|
|
getLib = getOutput "lib";
|
|
|
|
|
getDev = getOutput "dev";
|
2020-05-11 09:08:29 +00:00
|
|
|
|
getMan = getOutput "man";
|
2016-04-14 16:00:39 +00:00
|
|
|
|
|
2016-01-23 22:18:38 +00:00
|
|
|
|
/* Pick the outputs of packages to place in buildInputs */
|
2016-09-01 15:31:26 +00:00
|
|
|
|
chooseDevOutputs = drvs: builtins.map getDev drvs;
|
2016-02-28 21:49:42 +00:00
|
|
|
|
|
2019-09-11 10:57:38 +00:00
|
|
|
|
/* Make various Nix tools consider the contents of the resulting
|
|
|
|
|
attribute set when looking for what to build, find, etc.
|
2019-09-11 12:51:28 +00:00
|
|
|
|
|
|
|
|
|
This function only affects a single attribute set; it does not
|
|
|
|
|
apply itself recursively for nested attribute sets.
|
2019-09-11 10:57:38 +00:00
|
|
|
|
*/
|
|
|
|
|
recurseIntoAttrs =
|
|
|
|
|
attrs: attrs // { recurseForDerivations = true; };
|
|
|
|
|
|
2019-09-11 11:03:48 +00:00
|
|
|
|
/* Undo the effect of recurseIntoAttrs.
|
|
|
|
|
*/
|
|
|
|
|
dontRecurseIntoAttrs =
|
|
|
|
|
attrs: attrs // { recurseForDerivations = false; };
|
|
|
|
|
|
2022-09-05 23:11:50 +00:00
|
|
|
|
/* `unionOfDisjoint x y` is equal to `x // y // z` where the
|
|
|
|
|
attrnames in `z` are the intersection of the attrnames in `x` and
|
2022-09-12 19:59:16 +00:00
|
|
|
|
`y`, and all values `assert` with an error message. This
|
|
|
|
|
operator is commutative, unlike (//). */
|
2022-09-05 23:11:50 +00:00
|
|
|
|
unionOfDisjoint = x: y:
|
2022-09-25 07:09:13 +00:00
|
|
|
|
let
|
|
|
|
|
intersection = builtins.intersectAttrs x y;
|
|
|
|
|
collisions = lib.concatStringsSep " " (builtins.attrNames intersection);
|
|
|
|
|
mask = builtins.mapAttrs (name: value: builtins.throw
|
|
|
|
|
"unionOfDisjoint: collision on ${name}; complete list: ${collisions}")
|
|
|
|
|
intersection;
|
|
|
|
|
in
|
|
|
|
|
(x // y) // mask;
|
2022-09-05 23:11:50 +00:00
|
|
|
|
|
2016-02-28 21:49:42 +00:00
|
|
|
|
/*** deprecated stuff ***/
|
|
|
|
|
|
|
|
|
|
zipWithNames = zipAttrsWithNames;
|
|
|
|
|
zip = builtins.trace
|
|
|
|
|
"lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith;
|
2009-03-10 15:18:38 +00:00
|
|
|
|
}
|