Compare commits

...

2 commits

Author SHA1 Message Date
isabel roses e0d98ae789
chore: add formatting commit to git-blame-ignore-revs 2024-06-30 09:17:54 +01:00
isabel roses a505937410
style: formating 2024-06-30 09:16:52 +01:00
1438 changed files with 178898 additions and 131776 deletions

2
.git-blame-ignore-revs Normal file
View file

@ -0,0 +1,2 @@
# formatting
a5059374106b6b1148a3cc6673c27ec1829380ea

View file

@ -1,4 +1,6 @@
let requiredVersion = import ./lib/minver.nix; in
let
requiredVersion = import ./lib/minver.nix;
in
if !builtins ? nixVersion || builtins.compareVersions requiredVersion builtins.nixVersion == 1 then

View file

@ -1,14 +1,18 @@
{
outputs = { self, ... }:
outputs =
{ self, ... }:
let
forAllSystems = self.lib.genAttrs self.lib.systems.flakeExposed;
in
{
lib = import ./lib;
auxPackages = forAllSystems (system:
auxPackages = forAllSystems (
system:
(
let requiredVersion = import ./lib/minver.nix; in
let
requiredVersion = import ./lib/minver.nix;
in
if !builtins ? nixVersion || builtins.compareVersions requiredVersion builtins.nixVersion == 1 then
abort ''

View file

@ -1,4 +1,5 @@
{ "\t" = 9;
{
"\t" = 9;
"\n" = 10;
"\r" = 13;
" " = 32;

View file

@ -36,10 +36,7 @@ rec {
:::
*/
# TODO(Profpatsch): add tests that check stderr
assertMsg =
pred:
msg:
pred || builtins.throw msg;
assertMsg = pred: msg: pred || builtins.throw msg;
/**
Specialized `assertMsg` for checking if `val` is one of the elements
@ -81,14 +78,10 @@ rec {
:::
*/
assertOneOf =
name:
val:
xs:
assertMsg
(lib.elem val xs)
"${name} must be one of ${
lib.generators.toPretty {} xs}, but is: ${
lib.generators.toPretty {} val}";
name: val: xs:
assertMsg (lib.elem val xs) "${name} must be one of ${lib.generators.toPretty { } xs}, but is: ${
lib.generators.toPretty { } val
}";
/**
Specialized `assertMsg` for checking if every one of `vals` is one of the elements
@ -133,12 +126,9 @@ rec {
:::
*/
assertEachOneOf =
name:
vals:
xs:
assertMsg
(lib.all (val: lib.elem val xs) vals)
"each element in ${name} must be one of ${
lib.generators.toPretty {} xs}, but is: ${
lib.generators.toPretty {} vals}";
name: vals: xs:
assertMsg (lib.all (val: lib.elem val xs) vals)
"each element in ${name} must be one of ${lib.generators.toPretty { } xs}, but is: ${
lib.generators.toPretty { } vals
}";
}

View file

@ -5,14 +5,40 @@
let
inherit (builtins) head length;
inherit (lib.trivial) isInOldestRelease mergeAttrs warn warnIf;
inherit (lib.strings) concatStringsSep concatMapStringsSep escapeNixIdentifier sanitizeDerivationName;
inherit (lib.lists) foldr foldl' concatMap elemAt all partition groupBy take foldl;
inherit (lib.trivial)
isInOldestRelease
mergeAttrs
warn
warnIf
;
inherit (lib.strings)
concatStringsSep
concatMapStringsSep
escapeNixIdentifier
sanitizeDerivationName
;
inherit (lib.lists)
foldr
foldl'
concatMap
elemAt
all
partition
groupBy
take
foldl
;
in
rec {
inherit (builtins) attrNames listToAttrs hasAttr isAttrs getAttr removeAttrs;
inherit (builtins)
attrNames
listToAttrs
hasAttr
isAttrs
getAttr
removeAttrs
;
/**
Return an attribute from nested attribute sets.
@ -25,7 +51,6 @@ rec {
(x.${f p}."example.com" or 6) == attrByPath [ (f p) "example.com" ] 6 x
```
# Inputs
`attrPath`
@ -63,19 +88,20 @@ rec {
:::
*/
attrByPath =
attrPath:
default:
set:
attrPath: default: set:
let
lenAttrPath = length attrPath;
attrByPath' = n: s: (
if n == lenAttrPath then s
else (
attrByPath' =
n: s:
(
if n == lenAttrPath then
s
else
(
let
attr = elemAt attrPath n;
in
if s ? ${attr} then attrByPath' (n + 1) s.${attr}
else default
if s ? ${attr} then attrByPath' (n + 1) s.${attr} else default
)
);
in
@ -97,7 +123,6 @@ rec {
hasAttrByPath [] x == true
```
# Inputs
`attrPath`
@ -131,17 +156,18 @@ rec {
:::
*/
hasAttrByPath =
attrPath:
e:
attrPath: e:
let
lenAttrPath = length attrPath;
hasAttrByPath' = n: s: (
n == lenAttrPath || (
hasAttrByPath' =
n: s:
(
n == lenAttrPath
|| (
let
attr = elemAt attrPath n;
in
if s ? ${attr} then hasAttrByPath' (n + 1) s.${attr}
else false
if s ? ${attr} then hasAttrByPath' (n + 1) s.${attr} else false
)
);
in
@ -164,7 +190,6 @@ rec {
hasAttrByPath (attrsets.longestValidPathPrefix p x) x == true
```
# Inputs
`attrPath`
@ -200,8 +225,7 @@ rec {
:::
*/
longestValidPathPrefix =
attrPath:
v:
attrPath: v:
let
lenAttrPath = length attrPath;
getPrefixForSetAtIndex =
@ -221,8 +245,7 @@ rec {
attr = elemAt attrPath remainingPathIndex;
in
if remainingSet ? ${attr} then
getPrefixForSetAtIndex
remainingSet.${attr} # advance from the set to the attribute value
getPrefixForSetAtIndex remainingSet.${attr} # advance from the set to the attribute value
(remainingPathIndex + 1) # advance the path
else
# The attribute doesn't exist, so we return the prefix up to the
@ -234,7 +257,6 @@ rec {
/**
Create a new attribute set with `value` set at the nested attribute location specified in `attrPath`.
# Inputs
`attrPath`
@ -263,15 +285,12 @@ rec {
:::
*/
setAttrByPath =
attrPath:
value:
attrPath: value:
let
len = length attrPath;
atDepth = n:
if n == len
then value
else { ${elemAt attrPath n} = atDepth (n + 1); };
in atDepth 0;
atDepth = n: if n == len then value else { ${elemAt attrPath n} = atDepth (n + 1); };
in
atDepth 0;
/**
Like `attrByPath`, but without a default value. If it doesn't find the
@ -285,7 +304,6 @@ rec {
x.${f p}."example.com" == getAttrByPath [ (f p) "example.com" ] x
```
# Inputs
`attrPath`
@ -317,14 +335,12 @@ rec {
:::
*/
getAttrFromPath =
attrPath:
set:
attrPath: set:
attrByPath attrPath (abort ("cannot find attribute `" + concatStringsSep "." attrPath + "'")) set;
/**
Map each attribute in the given set and merge them into a new attribute set.
# Inputs
`f`
@ -357,12 +373,7 @@ rec {
:::
*/
concatMapAttrs = f: v:
foldl' mergeAttrs { }
(attrValues
(mapAttrs f v)
);
concatMapAttrs = f: v: foldl' mergeAttrs { } (attrValues (mapAttrs f v));
/**
Update or set specific paths of an attribute set.
@ -420,13 +431,15 @@ rec {
:::
*/
updateManyAttrsByPath = let
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:
go =
prefixLength: hasValue: value: updates:
let
# Splits updates into ones on this level (split.right)
# And ones on levels further down (split.wrong)
@ -439,17 +452,21 @@ rec {
withNestedMods =
# Return the value directly if we don't have any nested modifications
if split.wrong == [ ] then
if hasValue then value
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 "
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.")
+ "path tries to access the existing value."
)
else
# If there are nested modifications, try to apply them to the value
if !hasValue then
@ -459,30 +476,33 @@ rec {
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
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 "
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.");
+ "update an attribute inside of it."
);
in
# 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;
foldl (acc: el: el.update acc) withNestedMods split.right;
in updates: value: go 0 true value updates;
in
updates: value: go 0 true value updates;
/**
Return the specified attributes from a set.
# Inputs
`nameList`
@ -510,10 +530,7 @@ rec {
:::
*/
attrVals =
nameList:
set: map (x: set.${x}) nameList;
attrVals = nameList: set: map (x: set.${x}) nameList;
/**
Return the values of all attributes in the given set, sorted by
@ -538,12 +555,10 @@ rec {
*/
attrValues = builtins.attrValues;
/**
Given a set of attribute names, return the set of the corresponding
attributes from the given set.
# Inputs
`names`
@ -571,9 +586,7 @@ rec {
:::
*/
getAttrs =
names:
attrs: genAttrs names (name: attrs.${name});
getAttrs = names: attrs: genAttrs names (name: attrs.${name});
/**
Collect each attribute named `attr` from a list of attribute
@ -608,12 +621,10 @@ rec {
*/
catAttrs = builtins.catAttrs;
/**
Filter an attribute set by removing all attributes for which the
given predicate return false.
# Inputs
`pred`
@ -642,16 +653,21 @@ rec {
:::
*/
filterAttrs =
pred:
set:
listToAttrs (concatMap (name: let v = set.${name}; in if pred name v then [(nameValuePair name v)] else []) (attrNames set));
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.
# Inputs
`pred`
@ -680,17 +696,17 @@ rec {
:::
*/
filterAttrsRecursive =
pred:
set:
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 []
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)
);
@ -704,7 +720,6 @@ rec {
There is a completely different function `lib.foldAttrs`
which has nothing to do with this function, despite the similar name.
# Inputs
`f`
@ -773,16 +788,13 @@ rec {
:::
*/
foldlAttrs = f: init: set:
foldl'
(acc: name: f acc name set.${name})
init
(attrNames set);
foldlAttrs =
f: init: set:
foldl' (acc: name: f acc name set.${name}) init (attrNames set);
/**
Apply fold functions to values grouped by key.
# Inputs
`op`
@ -815,22 +827,16 @@ rec {
:::
*/
foldAttrs =
op:
nul:
list_of_attrs:
foldr (n: a:
foldr (name: o:
o // { ${name} = op n.${name} (a.${name} or nul); }
) a (attrNames n)
op: nul: list_of_attrs:
foldr (
n: a: foldr (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.
# Inputs
`pred`
@ -863,8 +869,7 @@ rec {
:::
*/
collect =
pred:
attrs:
pred: attrs:
if pred attrs then
[ attrs ]
else if isAttrs attrs then
@ -875,7 +880,6 @@ rec {
/**
Return the cartesian product of attribute set value combinations.
# Inputs
`attrsOfLists`
@ -906,13 +910,13 @@ rec {
*/
cartesianProduct =
attrsOfLists:
foldl' (listOfAttrs: attrName:
concatMap (attrs:
map (listValue: attrs // { ${attrName} = listValue; }) attrsOfLists.${attrName}
foldl' (
listOfAttrs: attrName:
concatMap (
attrs: map (listValue: attrs // { ${attrName} = listValue; }) attrsOfLists.${attrName}
) listOfAttrs
) [ { } ] (attrNames attrsOfLists);
/**
Return the result of function f applied to the cartesian product of attribute set value combinations.
Equivalent to using cartesianProduct followed by map.
@ -943,14 +947,12 @@ rec {
```
:::
*/
mapCartesianProduct = f: attrsOfLists: map f (cartesianProduct attrsOfLists);
/**
Utility function that creates a `{name, value}` pair as expected by `builtins.listToAttrs`.
# Inputs
`name`
@ -978,11 +980,7 @@ rec {
:::
*/
nameValuePair =
name:
value:
{ inherit name value; };
nameValuePair = name: value: { inherit name value; };
/**
Apply a function to each element in an attribute set, creating a new attribute set.
@ -1017,13 +1015,11 @@ rec {
*/
mapAttrs = builtins.mapAttrs;
/**
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`.
# Inputs
`f`
@ -1052,11 +1048,7 @@ rec {
:::
*/
mapAttrs' =
f:
set:
listToAttrs (map (attr: f attr set.${attr}) (attrNames set));
mapAttrs' = f: set: listToAttrs (map (attr: f attr set.${attr}) (attrNames set));
/**
Call a function for each attribute in the given set and return
@ -1090,10 +1082,7 @@ rec {
:::
*/
mapAttrsToList =
f:
attrs:
map (name: f name attrs.${name}) (attrNames attrs);
mapAttrsToList = f: attrs: map (name: f name attrs.${name}) (attrNames attrs);
/**
Deconstruct an attrset to a list of name-value pairs as expected by [`builtins.listToAttrs`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-listToAttrs).
@ -1140,7 +1129,6 @@ rec {
*/
attrsToList = mapAttrsToList nameValuePair;
/**
Like `mapAttrs`, except that it recursively applies itself to the *leaf* attributes of a potentially-nested attribute set:
the second argument of the function will never be an attrset.
@ -1166,11 +1154,7 @@ rec {
mapAttrsRecursive :: ([String] -> a -> b) -> AttrSet -> AttrSet
```
*/
mapAttrsRecursive =
f:
set:
mapAttrsRecursiveCond (as: true) f set;
mapAttrsRecursive = f: set: mapAttrsRecursiveCond (as: true) f set;
/**
Like `mapAttrsRecursive`, but it takes an additional predicate that tells it whether to recurse into an attribute set.
@ -1196,25 +1180,21 @@ rec {
```
*/
mapAttrsRecursiveCond =
cond:
f:
set:
cond: f: set:
let
recurse = path:
mapAttrs
(name: value:
if isAttrs value && cond value
then recurse (path ++ [ name ]) value
else f (path ++ [ name ]) value);
recurse =
path:
mapAttrs (
name: value:
if isAttrs value && cond value then recurse (path ++ [ name ]) value else f (path ++ [ name ]) value
);
in
recurse [ ] set;
/**
Generate an attribute set by mapping a function over a list of
attribute names.
# Inputs
`names`
@ -1242,17 +1222,12 @@ rec {
:::
*/
genAttrs =
names:
f:
listToAttrs (map (n: nameValuePair n (f n)) names);
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.
# Inputs
`value`
@ -1279,13 +1254,11 @@ rec {
:::
*/
isDerivation =
value: value.type or null == "derivation";
isDerivation = value: value.type or null == "derivation";
/**
Converts a store path to a fake derivation.
# Inputs
`path`
@ -1302,22 +1275,21 @@ rec {
path:
let
path' = builtins.storePath path;
res =
{ type = "derivation";
res = {
type = "derivation";
name = sanitizeDerivationName (builtins.substring 33 (-1) (baseNameOf path'));
outPath = path';
outputs = [ "out" ];
out = res;
outputName = "out";
};
in res;
in
res;
/**
If `cond` is true, return the attribute set `as`,
otherwise an empty attribute set.
# Inputs
`cond`
@ -1347,17 +1319,12 @@ rec {
:::
*/
optionalAttrs =
cond:
as:
if cond then as else {};
optionalAttrs = cond: as: if cond then as else { };
/**
Merge sets of attributes and use the function `f` to merge attributes
values.
# Inputs
`names`
@ -1390,14 +1357,13 @@ rec {
:::
*/
zipAttrsWithNames =
names:
f:
sets:
listToAttrs (map (name: {
names: f: sets:
listToAttrs (
map (name: {
inherit name;
value = f name (catAttrs name sets);
}) names);
}) names
);
/**
Merge sets of attributes and use the function f to merge attribute values.
@ -1428,7 +1394,6 @@ rec {
zipAttrsWith =
builtins.zipAttrsWith or (f: sets: zipAttrsWithNames (concatMap attrNames sets) f sets);
/**
Merge sets of attributes and combine each attribute value in to a list.
@ -1459,7 +1424,6 @@ rec {
The result is the same as `foldl mergeAttrs { }`, but the performance is better for large inputs.
For n list elements, each with an attribute set containing m unique attributes, the complexity of this operation is O(nm log n).
# Inputs
`list`
@ -1485,17 +1449,18 @@ rec {
:::
*/
mergeAttrsList = list:
mergeAttrsList =
list:
let
# `binaryMerge start end` merges the elements at indices `index` of `list` such that `start <= index < end`
# Type: Int -> Int -> Attrs
binaryMerge = start: end:
binaryMerge =
start: end:
# assert start < end; # Invariant
if end - start >= 2 then
# If there's at least 2 elements, split the range in two, recurse on each part and merge the result
# The invariant is satisfied because each half will have at least 1 element
binaryMerge start (start + (end - start) / 2)
// binaryMerge (start + (end - start) / 2) end
binaryMerge start (start + (end - start) / 2) // binaryMerge (start + (end - start) / 2) end
else
# Otherwise there will be exactly 1 element due to the invariant, in which case we just return it directly
elemAt list start;
@ -1506,7 +1471,6 @@ rec {
else
binaryMerge 0 (length list);
/**
Does the same as the update operator '//' except that attributes are
merged until the given predicate is verified. The predicate should
@ -1515,7 +1479,6 @@ rec {
the predicate is satisfied, the value of the first attribute set is
replaced by the value of the second attribute set.
# Inputs
`pred`
@ -1564,20 +1527,25 @@ rec {
:::
*/
recursiveUpdateUntil =
pred:
lhs:
rhs:
let f = attrPath:
zipAttrsWith (n: values:
let here = attrPath ++ [n]; in
if length values == 1
|| pred here (elemAt values 1) (head values) then
pred: lhs: rhs:
let
f =
attrPath:
zipAttrsWith (
n: values:
let
here = attrPath ++ [ n ];
in
if length values == 1 || pred here (elemAt values 1) (head values) then
head values
else
f here values
);
in f [] [rhs lhs];
in
f [ ] [
rhs
lhs
];
/**
A recursive variant of the update operator //. The recursion
@ -1585,7 +1553,6 @@ rec {
in which case the right hand side value takes precedence over the
left hand side value.
# Inputs
`lhs`
@ -1623,17 +1590,17 @@ rec {
:::
*/
recursiveUpdate =
lhs:
rhs:
recursiveUpdateUntil (path: lhs: rhs: !(isAttrs lhs && isAttrs rhs)) lhs rhs;
lhs: rhs:
recursiveUpdateUntil (
path: lhs: rhs:
!(isAttrs lhs && isAttrs rhs)
) lhs rhs;
/**
Recurse into every attribute set of the first argument and check that:
- Each attribute path also exists in the second argument.
- If the attribute's value is not a nested attribute set, it must have the same value in the right argument.
# Inputs
`pattern`
@ -1662,30 +1629,27 @@ rec {
:::
*/
matchAttrs =
pattern:
attrs:
pattern: attrs:
assert isAttrs pattern;
all
( # Compare equality between `pattern` & `attrs`.
all (
# Compare equality between `pattern` & `attrs`.
attr:
# Missing attr, not equal.
attrs ? ${attr} && (
attrs ? ${attr}
&& (
let
lhs = pattern.${attr};
rhs = attrs.${attr};
in
# If attrset check recursively
if isAttrs lhs then isAttrs rhs && matchAttrs lhs rhs
else lhs == rhs
if isAttrs lhs then isAttrs rhs && matchAttrs lhs rhs else lhs == rhs
)
)
(attrNames pattern);
) (attrNames pattern);
/**
Override only the attributes that are already present in the old set
useful for deep-overriding.
# Inputs
`old`
@ -1717,11 +1681,7 @@ rec {
:::
*/
overrideExisting =
old:
new:
mapAttrs (name: value: new.${name} or value) old;
overrideExisting = old: new: mapAttrs (name: value: new.${name} or value) old;
/**
Turns a list of strings into a human-readable description of those
@ -1729,7 +1689,6 @@ rec {
not intended to be machine-readable.
Create a new attribute set with `value` set at the nested attribute location specified in `attrPath`.
# Inputs
`path`
@ -1757,15 +1716,12 @@ rec {
*/
showAttrPath =
path:
if path == [] then "<root attribute path>"
else concatMapStringsSep "." escapeNixIdentifier path;
if path == [ ] then "<root attribute path>" else concatMapStringsSep "." escapeNixIdentifier path;
/**
Get a package output.
If no output is found, fallback to `.out` and then to the default.
# Inputs
`output`
@ -1793,10 +1749,9 @@ rec {
:::
*/
getOutput = output: pkg:
if ! pkg ? outputSpecified || ! pkg.outputSpecified
then pkg.${output} or pkg.out or pkg
else pkg;
getOutput =
output: pkg:
if !pkg ? outputSpecified || !pkg.outputSpecified then pkg.${output} or pkg.out or pkg else pkg;
/**
Get a package's `bin` output.
@ -1827,7 +1782,6 @@ rec {
*/
getBin = getOutput "bin";
/**
Get a package's `lib` output.
If the output does not exist, fallback to `.out` and then to the default.
@ -1857,7 +1811,6 @@ rec {
*/
getLib = getOutput "lib";
/**
Get a package's `dev` output.
If the output does not exist, fallback to `.out` and then to the default.
@ -1887,7 +1840,6 @@ rec {
*/
getDev = getOutput "dev";
/**
Get a package's `man` output.
If the output does not exist, fallback to `.out` and then to the default.
@ -1941,7 +1893,6 @@ rec {
This function only affects a single attribute set; it does not
apply itself recursively for nested attribute sets.
# Inputs
`attrs`
@ -1969,14 +1920,11 @@ rec {
:::
*/
recurseIntoAttrs =
attrs:
attrs // { recurseForDerivations = true; };
recurseIntoAttrs = attrs: attrs // { recurseForDerivations = true; };
/**
Undo the effect of recurseIntoAttrs.
# Inputs
`attrs`
@ -1989,9 +1937,7 @@ rec {
dontRecurseIntoAttrs :: AttrSet -> AttrSet
```
*/
dontRecurseIntoAttrs =
attrs:
attrs // { recurseForDerivations = false; };
dontRecurseIntoAttrs = attrs: attrs // { recurseForDerivations = false; };
/**
`unionOfDisjoint x y` is equal to `x // y // z` where the
@ -1999,7 +1945,6 @@ rec {
`y`, and all values `assert` with an error message. This
operator is commutative, unlike (//).
# Inputs
`x`
@ -2016,25 +1961,25 @@ rec {
unionOfDisjoint :: AttrSet -> AttrSet -> AttrSet
```
*/
unionOfDisjoint = x: y:
unionOfDisjoint =
x: y:
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;
mask = builtins.mapAttrs (
name: value: builtins.throw "unionOfDisjoint: collision on ${name}; complete list: ${collisions}"
) intersection;
in
(x // y) // mask;
# DEPRECATED
zipWithNames = warn
"lib.zipWithNames is a deprecated alias of lib.zipAttrsWithNames." zipAttrsWithNames;
zipWithNames = warn "lib.zipWithNames is a deprecated alias of lib.zipAttrsWithNames." zipAttrsWithNames;
# DEPRECATED
zip = warn
"lib.zip is a deprecated alias of lib.zipAttrsWith." zipAttrsWith;
zip = warn "lib.zip is a deprecated alias of lib.zipAttrsWith." zipAttrsWith;
# DEPRECATED
cartesianProductOfSets = warnIf (isInOldestRelease 2405)
"lib.cartesianProductOfSets is a deprecated alias of lib.cartesianProduct." cartesianProduct;
cartesianProductOfSets = warnIf (isInOldestRelease
2405
) "lib.cartesianProductOfSets is a deprecated alias of lib.cartesianProduct." cartesianProduct;
}

View file

@ -11,7 +11,6 @@ rec {
`toGNUCommandLineShell` returns an escaped shell string.
# Inputs
`options`
@ -22,7 +21,6 @@ rec {
: 2\. Function argument
# Examples
:::{.example}
## `lib.cli.toGNUCommandLineShell` usage example
@ -60,17 +58,14 @@ rec {
:::
*/
toGNUCommandLineShell =
options: attrs: lib.escapeShellArgs (toGNUCommandLine options attrs);
toGNUCommandLineShell = options: attrs: lib.escapeShellArgs (toGNUCommandLine options attrs);
toGNUCommandLine = {
toGNUCommandLine =
{
# how to string-format the option name;
# by default one character is a short option (`-`),
# more than one characters a long option (`--`).
mkOptionName ?
k: if builtins.stringLength k == 1
then "-${k}"
else "--${k}",
mkOptionName ? k: if builtins.stringLength k == 1 then "-${k}" else "--${k}",
# how to format a boolean value to a command list;
# by default its a flag option
@ -88,16 +83,25 @@ rec {
# By default, everything is printed verbatim and complex types
# are forbidden (lists, attrsets, functions). `null` values are omitted.
mkOption ?
k: v: if v == null
then []
else [ (mkOptionName k) (lib.generators.mkValueStringDefault {} v) ]
k: v:
if v == null then
[ ]
else
[
(mkOptionName k)
(lib.generators.mkValueStringDefault { } v)
],
}:
options:
let
render = k: v:
if builtins.isBool v then mkBool k v
else if builtins.isList v then mkList k v
else mkOption k v;
render =
k: v:
if builtins.isBool v then
mkBool k v
else if builtins.isList v then
mkList k v
else
mkOption k v;
in
builtins.concatLists (lib.mapAttrsToList render options);

View file

@ -1,20 +1,42 @@
{ lib }:
let
inherit (builtins)
intersectAttrs;
inherit (builtins) intersectAttrs;
inherit (lib)
functionArgs isFunction mirrorFunctionArgs isAttrs setFunctionArgs
optionalAttrs attrNames filter elemAt concatStringsSep sortOn take length
filterAttrs optionalString flip pathIsDirectory head pipe isDerivation listToAttrs
mapAttrs seq flatten deepSeq warnIf isInOldestRelease extends
functionArgs
isFunction
mirrorFunctionArgs
isAttrs
setFunctionArgs
optionalAttrs
attrNames
filter
elemAt
concatStringsSep
sortOn
take
length
filterAttrs
optionalString
flip
pathIsDirectory
head
pipe
isDerivation
listToAttrs
mapAttrs
seq
flatten
deepSeq
warnIf
isInOldestRelease
extends
;
inherit (lib.strings) levenshtein levenshteinAtMost;
in
rec {
/**
`overrideDerivation drv f` takes a derivation (i.e., the result
of a call to the builtin function `derivation`) and returns a new
@ -40,7 +62,6 @@ rec {
You should in general prefer `drv.overrideAttrs` over this function;
see the nixpkgs manual for more information on overriding.
# Inputs
`drv`
@ -74,20 +95,21 @@ rec {
:::
*/
overrideDerivation = drv: f:
overrideDerivation =
drv: f:
let
newDrv = derivation (drv.drvAttrs // (f drv));
in flip (extendDerivation (seq drv.drvPath true)) newDrv (
{ meta = drv.meta or {};
in
flip (extendDerivation (seq drv.drvPath true)) newDrv (
{
meta = drv.meta or { };
passthru = if drv ? passthru then drv.passthru else { };
}
//
(drv.passthru or {})
//
optionalAttrs (drv ? __spliced) {
// (drv.passthru or { })
// optionalAttrs (drv ? __spliced) {
__spliced = { } // (mapAttrs (_: sDrv: overrideDerivation sDrv f) drv.__spliced);
});
}
);
/**
`makeOverridable` takes a function from attribute set to attribute set and
@ -97,7 +119,6 @@ rec {
Please refer to documentation on [`<pkg>.overrideDerivation`](#sec-pkg-overrideDerivation) to learn about `overrideDerivation` and caveats
related to its use.
# Inputs
`f`
@ -128,12 +149,14 @@ rec {
:::
*/
makeOverridable = f:
makeOverridable =
f:
let
# Creates a functor with the same arguments as f
mirrorArgs = mirrorFunctionArgs f;
in
mirrorArgs (origArgs:
mirrorArgs (
origArgs:
let
result = f origArgs;
@ -146,19 +169,19 @@ rec {
overrideResult = g: makeOverridable (mirrorArgs (args: g (f args))) origArgs;
in
if isAttrs result then
result // {
result
// {
override = overrideArgs;
overrideDerivation = fdrv: overrideResult (x: overrideDerivation x fdrv);
${if result ? overrideAttrs then "overrideAttrs" else null} = fdrv:
overrideResult (x: x.overrideAttrs fdrv);
${if result ? overrideAttrs then "overrideAttrs" else null} =
fdrv: overrideResult (x: x.overrideAttrs fdrv);
}
else if isFunction result then
# Transform the result into a functor while propagating its arguments
setFunctionArgs result (functionArgs result) // {
override = overrideArgs;
}
else result);
setFunctionArgs result (functionArgs result) // { override = overrideArgs; }
else
result
);
/**
Call the package function in the file `fn` with the required
@ -188,7 +211,6 @@ rec {
<!-- TODO: Apply "Example:" tag to the examples above -->
# Inputs
`autoArgs`
@ -209,7 +231,8 @@ rec {
callPackageWith :: AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a
```
*/
callPackageWith = autoArgs: fn: args:
callPackageWith =
autoArgs: fn: args:
let
f = if isFunction fn then fn else import fn;
fargs = functionArgs f;
@ -222,12 +245,16 @@ rec {
# wouldn't be passed to it
missingArgs =
# Filter out arguments that have a default value
(filterAttrs (name: value: ! value)
(
filterAttrs (name: value: !value)
# Filter out arguments that would be passed
(removeAttrs fargs (attrNames allArgs)));
(removeAttrs fargs (attrNames allArgs))
);
# Get a list of suggested argument names for a given missing one
getSuggestions = arg: pipe (autoArgs // args) [
getSuggestions =
arg:
pipe (autoArgs // args) [
attrNames
# Only use ones that are at most 2 edits away. While mork would work,
# levenshteinAtMost is only fast for 2 or less.
@ -240,41 +267,50 @@ rec {
(map (x: "\"" + x + "\""))
];
prettySuggestions = suggestions:
if suggestions == [] then ""
else if length suggestions == 1 then ", did you mean ${elemAt suggestions 0}?"
else ", did you mean ${concatStringsSep ", " (lib.init suggestions)} or ${lib.last suggestions}?";
prettySuggestions =
suggestions:
if suggestions == [ ] then
""
else if length suggestions == 1 then
", did you mean ${elemAt suggestions 0}?"
else
", did you mean ${concatStringsSep ", " (lib.init suggestions)} or ${lib.last suggestions}?";
errorForArg = arg:
errorForArg =
arg:
let
loc = builtins.unsafeGetAttrPos arg fargs;
# loc' can be removed once lib/minver.nix is >2.3.4, since that includes
# https://github.com/NixOS/nix/pull/3468 which makes loc be non-null
loc' = if loc != null then loc.file + ":" + toString loc.line
loc' =
if loc != null then
loc.file + ":" + toString loc.line
else if !isFunction fn then
toString fn + optionalString (pathIsDirectory fn) "/default.nix"
else "<unknown location>";
in "Function called without required argument \"${arg}\" at "
else
"<unknown location>";
in
"Function called without required argument \"${arg}\" at "
+ "${loc'}${prettySuggestions (getSuggestions arg)}";
# Only show the error for the first missing argument
error = errorForArg (head (attrNames missingArgs));
in if missingArgs == {}
then makeOverridable f allArgs
in
if missingArgs == { } then
makeOverridable f allArgs
# This needs to be an abort so it can't be caught with `builtins.tryEval`,
# which is used by nix-env and ofborg to filter out packages that don't evaluate.
# This way we're forced to fix such errors in Nixpkgs,
# which is especially relevant with allowAliases = false
else abort "lib.customisation.callPackageWith: ${error}";
else
abort "lib.customisation.callPackageWith: ${error}";
/**
Like callPackage, but for a function that returns an attribute
set of derivations. The override function is added to the
individual attributes.
# Inputs
`autoArgs`
@ -295,7 +331,8 @@ rec {
callPackagesWith :: AttrSet -> ((AttrSet -> AttrSet) | Path) -> AttrSet -> AttrSet
```
*/
callPackagesWith = autoArgs: fn: args:
callPackagesWith =
autoArgs: fn: args:
let
f = if isFunction fn then fn else import fn;
auto = intersectAttrs (functionArgs f) autoArgs;
@ -304,18 +341,19 @@ rec {
pkgs = f origArgs;
mkAttrOverridable = name: _: makeOverridable (mirrorArgs (newArgs: (f newArgs).${name})) origArgs;
in
if isDerivation pkgs then throw
("function `callPackages` was called on a *single* derivation "
if isDerivation pkgs then
throw (
"function `callPackages` was called on a *single* derivation "
+ ''"${pkgs.name or "<unknown-name>"}";''
+ " did you mean to use `callPackage` instead?")
else mapAttrs mkAttrOverridable pkgs;
+ " did you mean to use `callPackage` instead?"
)
else
mapAttrs mkAttrOverridable pkgs;
/**
Add attributes to each output of a derivation without changing
the derivation itself and check a given condition when evaluating.
# Inputs
`condition`
@ -336,21 +374,29 @@ rec {
extendDerivation :: Bool -> Any -> Derivation -> Derivation
```
*/
extendDerivation = condition: passthru: drv:
extendDerivation =
condition: passthru: drv:
let
outputs = drv.outputs or [ "out" ];
commonAttrs = drv // (listToAttrs outputsList) //
({ all = map (x: x.value) outputsList; }) // passthru;
commonAttrs =
drv // (listToAttrs outputsList) // ({ all = map (x: x.value) outputsList; }) // passthru;
outputToAttrListElement = outputName:
{ name = outputName;
value = commonAttrs // {
outputToAttrListElement = outputName: {
name = outputName;
value =
commonAttrs
// {
inherit (drv.${outputName}) type outputName;
outputSpecified = true;
drvPath = assert condition; drv.${outputName}.drvPath;
outPath = assert condition; drv.${outputName}.outPath;
} //
drvPath =
assert condition;
drv.${outputName}.drvPath;
outPath =
assert condition;
drv.${outputName}.outPath;
}
//
# TODO: give the derivation control over the outputs.
# `overrideAttrs` may not be the only attribute that needs
# updating when switching outputs.
@ -361,9 +407,15 @@ rec {
};
outputsList = map outputToAttrListElement outputs;
in commonAttrs // {
drvPath = assert condition; drv.drvPath;
outPath = assert condition; drv.outPath;
in
commonAttrs
// {
drvPath =
assert condition;
drv.drvPath;
outPath =
assert condition;
drv.outPath;
};
/**
@ -372,7 +424,6 @@ rec {
result to ensure that there are no thunks kept alive to prevent
garbage collection.
# Inputs
`drv`
@ -385,21 +436,29 @@ rec {
hydraJob :: (Derivation | Null) -> (Derivation | Null)
```
*/
hydraJob = drv:
hydraJob =
drv:
let
outputs = drv.outputs or [ "out" ];
commonAttrs =
{ inherit (drv) name system meta; inherit outputs; }
{
inherit (drv) name system meta;
inherit outputs;
}
// optionalAttrs (drv._hydraAggregate or false) {
_hydraAggregate = true;
constituents = map hydraJob (flatten drv.constituents);
}
// (listToAttrs outputsList);
makeOutput = outputName:
let output = drv.${outputName}; in
{ name = outputName;
makeOutput =
outputName:
let
output = drv.${outputName};
in
{
name = outputName;
value = commonAttrs // {
outPath = output.outPath;
drvPath = output.drvPath;
@ -411,8 +470,8 @@ rec {
outputsList = map makeOutput outputs;
drv' = (head outputsList).value;
in if drv == null then null else
deepSeq drv' drv';
in
if drv == null then null else deepSeq drv' drv';
/**
Make an attribute set (a "scope") from functions that take arguments from that same attribute set.
@ -538,23 +597,27 @@ rec {
makeScope :: (AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a) -> (AttrSet -> AttrSet) -> scope
```
*/
makeScope = newScope: f:
let self = f self // {
makeScope =
newScope: f:
let
self = f self // {
newScope = scope: newScope (self // scope);
callPackage = self.newScope { };
overrideScope = g: makeScope newScope (extends g f);
# Remove after 24.11 is released.
overrideScope' = g: warnIf (isInOldestRelease 2311)
overrideScope' =
g:
warnIf (isInOldestRelease 2311)
"`overrideScope'` (from `lib.makeScope`) has been renamed to `overrideScope`."
(makeScope newScope (extends g f));
packages = f;
};
in self;
in
self;
/**
backward compatibility with old uncurried form; deprecated
# Inputs
`splicePackages`
@ -583,9 +646,14 @@ rec {
*/
makeScopeWithSplicing =
splicePackages: newScope: otherSplices: keep: extra: f:
makeScopeWithSplicing'
{ inherit splicePackages newScope; }
{ inherit otherSplices keep extra f; };
makeScopeWithSplicing' { inherit splicePackages newScope; } {
inherit
otherSplices
keep
extra
f
;
};
/**
Like makeScope, but aims to support cross compilation. It's still ugly, but
@ -612,14 +680,13 @@ rec {
```
*/
makeScopeWithSplicing' =
{ splicePackages
, newScope
}:
{ otherSplices
{ splicePackages, newScope }:
{
otherSplices,
# Attrs from `self` which won't be spliced.
# Avoid using keep, it's only used for a python hook workaround, added in PR #104201.
# ex: `keep = (self: { inherit (self) aAttr; })`
, keep ? (_self: {})
keep ? (_self: { }),
# Additional attrs to add to the sets `callPackage`.
# When the package is from a subset (but not a subset within a package IS #211340)
# within `spliced0` it will be spliced.
@ -634,8 +701,8 @@ rec {
# nix-repl> darwin.callPackage ({ CoreFoundation }: CoreFoundation) { }
# «derivation ...CoreFoundation-11.0.0.drv»
# ```
, extra ? (_spliced0: {})
, f
extra ? (_spliced0: { }),
f,
}:
let
spliced0 = splicePackages {
@ -652,13 +719,15 @@ rec {
callPackage = newScope spliced; # == self.newScope {};
# N.B. the other stages of the package set spliced in are *not*
# overridden.
overrideScope = g: (makeScopeWithSplicing'
{ inherit splicePackages newScope; }
{ inherit otherSplices keep extra;
overrideScope =
g:
(makeScopeWithSplicing' { inherit splicePackages newScope; } {
inherit otherSplices keep extra;
f = extends g f;
});
packages = f;
};
in self;
in
self;
}

View file

@ -1,4 +1,5 @@
/* Collection of functions useful for debugging
/*
Collection of functions useful for debugging
broken nix expressions.
* `trace`-like functions take two values, print
@ -25,14 +26,16 @@ let
generators
id
mapAttrs
trace;
trace
;
in
rec {
# -- TRACING --
/* Conditionally trace the supplied message, based on a predicate.
/*
Conditionally trace the supplied message, based on a predicate.
Type: traceIf :: bool -> string -> a -> a
@ -47,9 +50,11 @@ rec {
# Message that should be traced
msg:
# Value to return
x: if pred then trace msg x else x;
x:
if pred then trace msg x else x;
/* Trace the supplied value after applying a function to it, and
/*
Trace the supplied value after applying a function to it, and
return the original value.
Type: traceValFn :: (a -> b) -> a -> a
@ -63,9 +68,11 @@ rec {
# Function to apply
f:
# Value to trace and return
x: trace (f x) x;
x:
trace (f x) x;
/* Trace the supplied value and return it.
/*
Trace the supplied value and return it.
Type: traceVal :: a -> a
@ -76,7 +83,8 @@ rec {
*/
traceVal = traceValFn id;
/* `builtins.trace`, but the value is `builtins.deepSeq`ed first.
/*
`builtins.trace`, but the value is `builtins.deepSeq`ed first.
Type: traceSeq :: a -> b -> b
@ -92,9 +100,11 @@ rec {
# The value to trace
x:
# The value to return
y: trace (builtins.deepSeq x x) y;
y:
trace (builtins.deepSeq x x) y;
/* Like `traceSeq`, but only evaluate down to depth n.
/*
Like `traceSeq`, but only evaluate down to depth n.
This is very useful because lots of `traceSeq` usages
lead to an infinite recursion.
@ -105,20 +115,36 @@ rec {
Type: traceSeqN :: Int -> a -> b -> b
*/
traceSeqN = depth: x: y:
let snip = v: if isList v then noQuotes "[]" v
else if isAttrs v then noQuotes "{}" v
else v;
noQuotes = str: v: { __pretty = const str; val = v; };
modify = n: fn: v: if (n == 0) then fn v
else if isList v then map (modify (n - 1) fn) v
else if isAttrs v then mapAttrs
(const (modify (n - 1) fn)) v
else v;
in trace (generators.toPretty { allowPrettyValues = true; }
(modify depth snip x)) y;
traceSeqN =
depth: x: y:
let
snip =
v:
if isList v then
noQuotes "[]" v
else if isAttrs v then
noQuotes "{}" v
else
v;
noQuotes = str: v: {
__pretty = const str;
val = v;
};
modify =
n: fn: v:
if (n == 0) then
fn v
else if isList v then
map (modify (n - 1) fn) v
else if isAttrs v then
mapAttrs (const (modify (n - 1) fn)) v
else
v;
in
trace (generators.toPretty { allowPrettyValues = true; } (modify depth snip x)) y;
/* A combination of `traceVal` and `traceSeq` that applies a
/*
A combination of `traceVal` and `traceSeq` that applies a
provided function to the value to be traced after `deepSeq`ing
it.
*/
@ -126,24 +152,28 @@ rec {
# Function to apply
f:
# Value to trace
v: traceValFn f (builtins.deepSeq v v);
v:
traceValFn f (builtins.deepSeq v v);
/* A combination of `traceVal` and `traceSeq`. */
# A combination of `traceVal` and `traceSeq`.
traceValSeq = traceValSeqFn id;
/* A combination of `traceVal` and `traceSeqN` that applies a
provided function to the value to be traced. */
/*
A combination of `traceVal` and `traceSeqN` that applies a
provided function to the value to be traced.
*/
traceValSeqNFn =
# Function to apply
f:
depth:
f: depth:
# Value to trace
v: traceSeqN depth (f v) v;
v:
traceSeqN depth (f v) v;
/* A combination of `traceVal` and `traceSeqN`. */
# A combination of `traceVal` and `traceSeqN`.
traceValSeqN = traceValSeqNFn id;
/* Trace the input and output of a function `f` named `name`,
/*
Trace the input and output of a function `f` named `name`,
both down to `depth`.
This is useful for adding around a function call,
@ -154,21 +184,21 @@ rec {
trace: { fn = "id"; from = { a.b = {}; }; to = { a.b = {}; }; }
=> { a.b.c = 3; }
*/
traceFnSeqN = depth: name: f: v:
let res = f v;
in lib.traceSeqN
(depth + 1)
{
traceFnSeqN =
depth: name: f: v:
let
res = f v;
in
lib.traceSeqN (depth + 1) {
fn = name;
from = v;
to = res;
}
res;
} res;
# -- TESTING --
/* Evaluates a set of tests.
/*
Evaluates a set of tests.
A test is an attribute set `{expr, expected}`,
denoting an expression and its expected result.
@ -228,19 +258,41 @@ rec {
*/
runTests =
# Tests to run
tests: concatLists (attrValues (mapAttrs (name: test:
let testsToRun = if tests ? tests then tests.tests else [];
in if (substring 0 4 name == "test" || elem name testsToRun)
tests:
concatLists (
attrValues (
mapAttrs (
name: test:
let
testsToRun = if tests ? tests then tests.tests else [ ];
in
if
(substring 0 4 name == "test" || elem name testsToRun)
&& ((testsToRun == [ ]) || elem name tests.tests)
&& (test.expr != test.expected)
then [ { inherit name; expected = test.expected; result = test.expr; } ]
else [] ) tests));
then
[
{
inherit name;
expected = test.expected;
result = test.expr;
}
]
else
[ ]
) tests
)
);
/* Create a test assuming that list elements are `true`.
/*
Create a test assuming that list elements are `true`.
Example:
{ testX = allTrue [ true ]; }
*/
testAllTrue = expr: { inherit expr; expected = map (x: true) expr; };
testAllTrue = expr: {
inherit expr;
expected = map (x: true) expr;
};
}

View file

@ -1,15 +1,19 @@
/* Library of low-level helper functions for nix expressions.
*
* Please implement (mostly) exhaustive unit tests
* for new functions in `./tests.nix`.
/*
Library of low-level helper functions for nix expressions.
Please implement (mostly) exhaustive unit tests
for new functions in `./tests.nix`.
*/
let
inherit (import ./fixed-points.nix { inherit lib; }) makeExtensible;
lib = makeExtensible (self: let
lib = makeExtensible (
self:
let
callLibs = file: import file { lib = self; };
in {
in
{
# often used, or depending on very little
trivial = callLibs ./trivial.nix;
@ -64,108 +68,433 @@ let
# linux kernel configuration
kernel = callLibs ./kernel.nix;
inherit (builtins) add addErrorContext attrNames concatLists
deepSeq elem elemAt filter genericClosure genList getAttr
hasAttr head isAttrs isBool isInt isList isPath isString length
lessThan listToAttrs pathExists readFile replaceStrings seq
stringLength sub substring tail trace;
inherit (self.trivial) id const pipe concat or and xor bitAnd bitOr bitXor
bitNot boolToString mergeAttrs flip mapNullable inNixShell isFloat min max
importJSON importTOML warn warnIf warnIfNot throwIf throwIfNot checkListOfEnum
info showWarnings nixpkgsVersion version isInOldestRelease
mod compare splitByAndCompare
functionArgs setFunctionArgs isFunction toFunction mirrorFunctionArgs
toHexString toBaseDigits inPureEvalMode;
inherit (self.fixedPoints) fix fix' converge extends composeExtensions
composeManyExtensions makeExtensible makeExtensibleWithCustomName;
inherit (self.attrsets) attrByPath hasAttrByPath setAttrByPath
getAttrFromPath attrVals attrValues getAttrs catAttrs filterAttrs
filterAttrsRecursive foldlAttrs foldAttrs collect nameValuePair mapAttrs
mapAttrs' mapAttrsToList attrsToList concatMapAttrs mapAttrsRecursive
mapAttrsRecursiveCond genAttrs isDerivation toDerivation optionalAttrs
zipAttrsWithNames zipAttrsWith zipAttrs recursiveUpdateUntil
recursiveUpdate matchAttrs mergeAttrsList overrideExisting showAttrPath getOutput
getBin getLib getDev getMan chooseDevOutputs zipWithNames zip
recurseIntoAttrs dontRecurseIntoAttrs cartesianProduct cartesianProductOfSets
mapCartesianProduct updateManyAttrsByPath;
inherit (self.lists) singleton forEach foldr fold foldl foldl' imap0 imap1
ifilter0 concatMap flatten remove findSingle findFirst any all count
optional optionals toList range replicate partition zipListsWith zipLists
reverseList listDfs toposort sort sortOn naturalSort compareLists take
drop sublist last init crossLists unique allUnique intersectLists
subtractLists mutuallyExclusive groupBy groupBy';
inherit (self.strings) concatStrings concatMapStrings concatImapStrings
intersperse concatStringsSep concatMapStringsSep
concatImapStringsSep concatLines makeSearchPath makeSearchPathOutput
makeLibraryPath makeIncludePath makeBinPath optionalString
hasInfix hasPrefix hasSuffix stringToCharacters stringAsChars escape
escapeShellArg escapeShellArgs
isStorePath isStringLike
isValidPosixName toShellVar toShellVars
escapeRegex escapeURL escapeXML replaceChars lowerChars
upperChars toLower toUpper addContextFrom splitString
removePrefix removeSuffix versionOlder versionAtLeast
getName getVersion
cmakeOptionType cmakeBool cmakeFeature
mesonOption mesonBool mesonEnable
nameFromURL enableFeature enableFeatureAs withFeature
withFeatureAs fixedWidthString fixedWidthNumber
toInt toIntBase10 readPathsFromFile fileContents;
inherit (self.stringsWithDeps) textClosureList textClosureMap
noDepEntry fullDepEntry packEntry stringAfter;
inherit (self.customisation) overrideDerivation makeOverridable
callPackageWith callPackagesWith extendDerivation hydraJob
makeScope makeScopeWithSplicing makeScopeWithSplicing';
inherit (builtins)
add
addErrorContext
attrNames
concatLists
deepSeq
elem
elemAt
filter
genericClosure
genList
getAttr
hasAttr
head
isAttrs
isBool
isInt
isList
isPath
isString
length
lessThan
listToAttrs
pathExists
readFile
replaceStrings
seq
stringLength
sub
substring
tail
trace
;
inherit (self.trivial)
id
const
pipe
concat
or
and
xor
bitAnd
bitOr
bitXor
bitNot
boolToString
mergeAttrs
flip
mapNullable
inNixShell
isFloat
min
max
importJSON
importTOML
warn
warnIf
warnIfNot
throwIf
throwIfNot
checkListOfEnum
info
showWarnings
nixpkgsVersion
version
isInOldestRelease
mod
compare
splitByAndCompare
functionArgs
setFunctionArgs
isFunction
toFunction
mirrorFunctionArgs
toHexString
toBaseDigits
inPureEvalMode
;
inherit (self.fixedPoints)
fix
fix'
converge
extends
composeExtensions
composeManyExtensions
makeExtensible
makeExtensibleWithCustomName
;
inherit (self.attrsets)
attrByPath
hasAttrByPath
setAttrByPath
getAttrFromPath
attrVals
attrValues
getAttrs
catAttrs
filterAttrs
filterAttrsRecursive
foldlAttrs
foldAttrs
collect
nameValuePair
mapAttrs
mapAttrs'
mapAttrsToList
attrsToList
concatMapAttrs
mapAttrsRecursive
mapAttrsRecursiveCond
genAttrs
isDerivation
toDerivation
optionalAttrs
zipAttrsWithNames
zipAttrsWith
zipAttrs
recursiveUpdateUntil
recursiveUpdate
matchAttrs
mergeAttrsList
overrideExisting
showAttrPath
getOutput
getBin
getLib
getDev
getMan
chooseDevOutputs
zipWithNames
zip
recurseIntoAttrs
dontRecurseIntoAttrs
cartesianProduct
cartesianProductOfSets
mapCartesianProduct
updateManyAttrsByPath
;
inherit (self.lists)
singleton
forEach
foldr
fold
foldl
foldl'
imap0
imap1
ifilter0
concatMap
flatten
remove
findSingle
findFirst
any
all
count
optional
optionals
toList
range
replicate
partition
zipListsWith
zipLists
reverseList
listDfs
toposort
sort
sortOn
naturalSort
compareLists
take
drop
sublist
last
init
crossLists
unique
allUnique
intersectLists
subtractLists
mutuallyExclusive
groupBy
groupBy'
;
inherit (self.strings)
concatStrings
concatMapStrings
concatImapStrings
intersperse
concatStringsSep
concatMapStringsSep
concatImapStringsSep
concatLines
makeSearchPath
makeSearchPathOutput
makeLibraryPath
makeIncludePath
makeBinPath
optionalString
hasInfix
hasPrefix
hasSuffix
stringToCharacters
stringAsChars
escape
escapeShellArg
escapeShellArgs
isStorePath
isStringLike
isValidPosixName
toShellVar
toShellVars
escapeRegex
escapeURL
escapeXML
replaceChars
lowerChars
upperChars
toLower
toUpper
addContextFrom
splitString
removePrefix
removeSuffix
versionOlder
versionAtLeast
getName
getVersion
cmakeOptionType
cmakeBool
cmakeFeature
mesonOption
mesonBool
mesonEnable
nameFromURL
enableFeature
enableFeatureAs
withFeature
withFeatureAs
fixedWidthString
fixedWidthNumber
toInt
toIntBase10
readPathsFromFile
fileContents
;
inherit (self.stringsWithDeps)
textClosureList
textClosureMap
noDepEntry
fullDepEntry
packEntry
stringAfter
;
inherit (self.customisation)
overrideDerivation
makeOverridable
callPackageWith
callPackagesWith
extendDerivation
hydraJob
makeScope
makeScopeWithSplicing
makeScopeWithSplicing'
;
inherit (self.derivations) lazyDerivation optionalDrvAttr;
inherit (self.meta) addMetaAttrs dontDistribute setName updateName
appendToName mapDerivationAttrset setPrio lowPrio lowPrioSet hiPrio
hiPrioSet getLicenseFromSpdxId getExe getExe';
inherit (self.filesystem) pathType pathIsDirectory pathIsRegularFile
packagesFromDirectoryRecursive;
inherit (self.sources) cleanSourceFilter
cleanSource sourceByRegex sourceFilesBySuffices
commitIdFromGitRepo cleanSourceWith pathHasContext
canCleanSource pathIsGitRepo;
inherit (self.modules) evalModules setDefaultModuleLocation
unifyModuleSyntax applyModuleArgsIfFunction mergeModules
mergeModules' mergeOptionDecls mergeDefinitions
pushDownProperties dischargeProperties filterOverrides
sortProperties fixupOptionType mkIf mkAssert mkMerge mkOverride
mkOptionDefault mkDefault mkImageMediaOverride mkForce mkVMOverride
mkFixStrictness mkOrder mkBefore mkAfter mkAliasDefinitions
mkAliasAndWrapDefinitions fixMergeModules mkRemovedOptionModule
mkRenamedOptionModule mkRenamedOptionModuleWith
mkMergedOptionModule mkChangedOptionModule
mkAliasOptionModule mkDerivedConfig doRename
mkAliasOptionModuleMD;
inherit (self.meta)
addMetaAttrs
dontDistribute
setName
updateName
appendToName
mapDerivationAttrset
setPrio
lowPrio
lowPrioSet
hiPrio
hiPrioSet
getLicenseFromSpdxId
getExe
getExe'
;
inherit (self.filesystem)
pathType
pathIsDirectory
pathIsRegularFile
packagesFromDirectoryRecursive
;
inherit (self.sources)
cleanSourceFilter
cleanSource
sourceByRegex
sourceFilesBySuffices
commitIdFromGitRepo
cleanSourceWith
pathHasContext
canCleanSource
pathIsGitRepo
;
inherit (self.modules)
evalModules
setDefaultModuleLocation
unifyModuleSyntax
applyModuleArgsIfFunction
mergeModules
mergeModules'
mergeOptionDecls
mergeDefinitions
pushDownProperties
dischargeProperties
filterOverrides
sortProperties
fixupOptionType
mkIf
mkAssert
mkMerge
mkOverride
mkOptionDefault
mkDefault
mkImageMediaOverride
mkForce
mkVMOverride
mkFixStrictness
mkOrder
mkBefore
mkAfter
mkAliasDefinitions
mkAliasAndWrapDefinitions
fixMergeModules
mkRemovedOptionModule
mkRenamedOptionModule
mkRenamedOptionModuleWith
mkMergedOptionModule
mkChangedOptionModule
mkAliasOptionModule
mkDerivedConfig
doRename
mkAliasOptionModuleMD
;
evalOptionValue = lib.warn "External use of `lib.evalOptionValue` is deprecated. If your use case isn't covered by non-deprecated functions, we'd like to know more and perhaps support your use case well, instead of providing access to these low level functions. In this case please open an issue in https://github.com/nixos/nixpkgs/issues/." self.modules.evalOptionValue;
inherit (self.options) isOption mkEnableOption mkSinkUndeclaredOptions
mergeDefaultOption mergeOneOption mergeEqualOption mergeUniqueOption
getValues getFiles
optionAttrSetToDocList optionAttrSetToDocList'
scrubOptionValue literalExpression literalExample
showOption showOptionWithDefLocs showFiles
unknownModule mkOption mkPackageOption mkPackageOptionMD
mdDoc literalMD;
inherit (self.types) isType setType defaultTypeMerge defaultFunctor
isOptionType mkOptionType;
inherit (self.asserts)
assertMsg assertOneOf;
inherit (self.debug) traceIf traceVal traceValFn
traceSeq traceSeqN traceValSeq
traceValSeqFn traceValSeqN traceValSeqNFn traceFnSeqN
runTests testAllTrue;
inherit (self.misc) maybeEnv defaultMergeArg defaultMerge foldArgs
maybeAttrNullable maybeAttr ifEnable checkFlag getValue
checkReqs uniqList uniqListExt condConcat lazyGenericClosure
innerModifySumArgs modifySumArgs innerClosePropagation
closePropagation mapAttrsFlatten nvs setAttr setAttrMerge
mergeAttrsWithFunc mergeAttrsConcatenateValues
mergeAttrsNoOverride mergeAttrByFunc mergeAttrsByFuncDefaults
mergeAttrsByFuncDefaultsClean mergeAttrBy
fakeHash fakeSha256 fakeSha512
nixType imap;
inherit (self.versions)
splitVersion;
});
in lib
inherit (self.options)
isOption
mkEnableOption
mkSinkUndeclaredOptions
mergeDefaultOption
mergeOneOption
mergeEqualOption
mergeUniqueOption
getValues
getFiles
optionAttrSetToDocList
optionAttrSetToDocList'
scrubOptionValue
literalExpression
literalExample
showOption
showOptionWithDefLocs
showFiles
unknownModule
mkOption
mkPackageOption
mkPackageOptionMD
mdDoc
literalMD
;
inherit (self.types)
isType
setType
defaultTypeMerge
defaultFunctor
isOptionType
mkOptionType
;
inherit (self.asserts) assertMsg assertOneOf;
inherit (self.debug)
traceIf
traceVal
traceValFn
traceSeq
traceSeqN
traceValSeq
traceValSeqFn
traceValSeqN
traceValSeqNFn
traceFnSeqN
runTests
testAllTrue
;
inherit (self.misc)
maybeEnv
defaultMergeArg
defaultMerge
foldArgs
maybeAttrNullable
maybeAttr
ifEnable
checkFlag
getValue
checkReqs
uniqList
uniqListExt
condConcat
lazyGenericClosure
innerModifySumArgs
modifySumArgs
innerClosePropagation
closePropagation
mapAttrsFlatten
nvs
setAttr
setAttrMerge
mergeAttrsWithFunc
mergeAttrsConcatenateValues
mergeAttrsNoOverride
mergeAttrByFunc
mergeAttrsByFuncDefaults
mergeAttrsByFuncDefaultsClean
mergeAttrBy
fakeHash
fakeSha256
fakeSha512
nixType
imap
;
inherit (self.versions) splitVersion;
}
);
in
lib

View file

@ -34,123 +34,181 @@ let
inherit (lib.attrsets) removeAttrs;
# returns default if env var is not set
maybeEnv = name: default:
let value = builtins.getEnv name; in
maybeEnv =
name: default:
let
value = builtins.getEnv name;
in
if value == "" then default else value;
defaultMergeArg = x : y: if builtins.isAttrs y then
y
else
(y x);
defaultMergeArg = x: y: if builtins.isAttrs y then y else (y x);
defaultMerge = x: y: x // (defaultMergeArg x y);
foldArgs = merger: f: init: x:
let arg = (merger init (defaultMergeArg init x));
foldArgs =
merger: f: init: x:
let
arg = (merger init (defaultMergeArg init x));
# now add the function with composed args already applied to the final attrs
base = (setAttrMerge "passthru" {} (f arg)
( z: z // {
base = (
setAttrMerge "passthru" { } (f arg) (
z:
z
// {
function = foldArgs merger f arg;
args = (attrByPath ["passthru" "args"] {} z) // x;
} ));
args =
(attrByPath [
"passthru"
"args"
] { } z)
// x;
}
)
);
withStdOverrides = base // {
override = base.passthru.function;
};
in
withStdOverrides;
# shortcut for attrByPath ["name"] default attrs
maybeAttrNullable = maybeAttr;
# shortcut for attrByPath ["name"] default attrs
maybeAttr = name: default: attrs: attrs.${name} or default;
maybeAttr =
name: default: attrs:
attrs.${name} or default;
# Return the second argument if the first one is true or the empty version
# of the second argument.
ifEnable = cond: val:
if cond then val
else if builtins.isList val then []
else if builtins.isAttrs val then {}
ifEnable =
cond: val:
if cond then
val
else if builtins.isList val then
[ ]
else if builtins.isAttrs val then
{ }
# else if builtins.isString val then ""
else if val == true || val == false then false
else null;
else if val == true || val == false then
false
else
null;
# Return true only if there is an attribute and it is true.
checkFlag = attrSet: name:
if name == "true" then true else
if name == "false" then false else
if (elem name (attrByPath ["flags"] [] attrSet)) then true else
checkFlag =
attrSet: name:
if name == "true" then
true
else if name == "false" then
false
else if (elem name (attrByPath [ "flags" ] [ ] attrSet)) then
true
else
attrByPath [ name ] false attrSet;
# Input : attrSet, [ [name default] ... ], name
# Output : its value or default.
getValue = attrSet: argList: name:
( attrByPath [name] (if checkFlag attrSet name then true else
if argList == [] then null else
let x = builtins.head argList; in
if (head x) == name then
(head (tail x))
else (getValue attrSet
(tail argList) name)) attrSet );
getValue =
attrSet: argList: name:
(attrByPath [ name ] (
if checkFlag attrSet name then
true
else if argList == [ ] then
null
else
let
x = builtins.head argList;
in
if (head x) == name then (head (tail x)) else (getValue attrSet (tail argList) name)
) attrSet);
# Input : attrSet, [[name default] ...], [ [flagname reqs..] ... ]
# Output : are reqs satisfied? It's asserted.
checkReqs = attrSet: argList: condList:
checkReqs =
attrSet: argList: condList:
(foldr and true (
map (
x:
let
name = (head x);
in
(
foldr and true
(map (x: let name = (head x); in
((checkFlag attrSet name) ->
(foldr and true
(map (y: let val=(getValue attrSet argList y); in
(val!=null) && (val!=false))
(tail x))))) condList));
(checkFlag attrSet name)
-> (foldr and true (
map (
y:
let
val = (getValue attrSet argList y);
in
(val != null) && (val != false)
) (tail x)
))
)
) condList
));
# This function has O(n^2) performance.
uniqList = { inputList, acc ? [] }:
let go = xs: acc:
if xs == []
then []
else let x = head xs;
uniqList =
{
inputList,
acc ? [ ],
}:
let
go =
xs: acc:
if xs == [ ] then
[ ]
else
let
x = head xs;
y = if elem x acc then [ ] else [ x ];
in y ++ go (tail xs) (y ++ acc);
in go inputList acc;
in
y ++ go (tail xs) (y ++ acc);
in
go inputList acc;
uniqListExt = { inputList,
uniqListExt =
{
inputList,
outputList ? [ ],
getter ? (x: x),
compare ? (x: y: x==y) }:
if inputList == [] then outputList else
let x = head inputList;
compare ? (x: y: x == y),
}:
if inputList == [ ] then
outputList
else
let
x = head inputList;
isX = y: (compare (getter y) (getter x));
newOutputList = outputList ++
(if any isX outputList then [] else [x]);
in uniqListExt { outputList = newOutputList;
newOutputList = outputList ++ (if any isX outputList then [ ] else [ x ]);
in
uniqListExt {
outputList = newOutputList;
inputList = (tail inputList);
inherit getter compare;
};
condConcat = name: list: checker:
if list == [] then name else
if checker (head list) then
condConcat
(name + (head (tail list)))
(tail (tail list))
checker
else condConcat
name (tail (tail list)) checker;
condConcat =
name: list: checker:
if list == [ ] then
name
else if checker (head list) then
condConcat (name + (head (tail list))) (tail (tail list)) checker
else
condConcat name (tail (tail list)) checker;
lazyGenericClosure = {startSet, operator}:
lazyGenericClosure =
{ startSet, operator }:
let
work = list: doneKeys: result:
work =
list: doneKeys: result:
if list == [ ] then
result
else
let x = head list; key = x.key; in
let
x = head list;
key = x.key;
in
if elem key doneKeys then
work (tail list) doneKeys result
else
@ -158,27 +216,33 @@ let
in
work startSet [ ] [ ];
innerModifySumArgs = f: x: a: b: if b == null then (f a b) // x else
innerModifySumArgs f x (a // b);
innerModifySumArgs =
f: x: a: b:
if b == null then (f a b) // x else innerModifySumArgs f x (a // b);
modifySumArgs = f: x: innerModifySumArgs f x { };
innerClosePropagation = acc: xs:
if xs == []
then acc
else let y = head xs;
innerClosePropagation =
acc: xs:
if xs == [ ] then
acc
else
let
y = head xs;
ys = tail xs;
in if ! isAttrs y
then innerClosePropagation acc ys
else let acc' = [y] ++ acc;
in innerClosePropagation
acc'
(uniqList { inputList = (maybeAttrNullable "propagatedBuildInputs" [] y)
in
if !isAttrs y then
innerClosePropagation acc ys
else
let
acc' = [ y ] ++ acc;
in
innerClosePropagation acc' (uniqList {
inputList =
(maybeAttrNullable "propagatedBuildInputs" [ ] y)
++ (maybeAttrNullable "propagatedNativeBuildInputs" [ ] y)
++ ys;
acc = acc';
}
);
});
closePropagationSlow = list: (uniqList { inputList = (innerClosePropagation [ ] list); });
@ -188,28 +252,35 @@ let
# attribute of each derivation.
# On some benchmarks, it performs up to 15 times faster than closePropagation.
# See https://github.com/NixOS/nixpkgs/pull/194391 for details.
closePropagationFast = list:
builtins.map (x: x.val) (builtins.genericClosure {
closePropagationFast =
list:
builtins.map (x: x.val) (
builtins.genericClosure {
startSet = builtins.map (x: {
key = x.outPath;
val = x;
}) (builtins.filter (x: x != null) list);
operator = item:
operator =
item:
if !builtins.isAttrs item.val then
[ ]
else
builtins.concatMap (x:
if x != null then [{
builtins.concatMap (
x:
if x != null then
[
{
key = x.outPath;
val = x;
}] else
[ ]) ((item.val.propagatedBuildInputs or [ ])
++ (item.val.propagatedNativeBuildInputs or [ ]));
});
}
]
else
[ ]
) ((item.val.propagatedBuildInputs or [ ]) ++ (item.val.propagatedNativeBuildInputs or [ ]));
}
);
closePropagation = if builtins ? genericClosure
then closePropagationFast
else closePropagationSlow;
closePropagation = if builtins ? genericClosure then closePropagationFast else closePropagationSlow;
# calls a function (f attr value ) for each record item. returns a list
mapAttrsFlatten = f: r: map (attr: f attr r.${attr}) (attrNames r);
@ -217,22 +288,25 @@ let
# attribute set containing one attribute
nvs = name: value: listToAttrs [ (nameValuePair name value) ];
# adds / replaces an attribute of an attribute set
setAttr = set: name: v: set // (nvs name v);
setAttr =
set: name: v:
set // (nvs name v);
# setAttrMerge (similar to mergeAttrsWithFunc but only merges the values of a particular name)
# setAttrMerge "a" [] { a = [2];} (x: x ++ [3]) -> { a = [2 3]; }
# setAttrMerge "a" [] { } (x: x ++ [3]) -> { a = [ 3]; }
setAttrMerge = name: default: attrs: f:
setAttrMerge =
name: default: attrs: f:
setAttr attrs name (f (maybeAttr name default attrs));
# Using f = a: b = b the result is similar to //
# merge attributes with custom function handling the case that the attribute
# exists in both sets
mergeAttrsWithFunc = f: set1: set2:
foldr (n: set: if set ? ${n}
then setAttr set n (f set.${n} set2.${n})
else set )
(set2 // set1) (attrNames set2);
mergeAttrsWithFunc =
f: set1: set2:
foldr (n: set: if set ? ${n} then setAttr set n (f set.${n} set2.${n}) else set) (set2 // set1) (
attrNames set2
);
# merging two attribute set concatenating the values of same attribute names
# eg { a = 7; } { a = [ 2 3 ]; } becomes { a = [ 7 2 3 ]; }
@ -245,20 +319,31 @@ let
# merging buildPhase doesn't really make sense. The cases will be rare where appending /prefixing will fit your needs?
# in these cases the first buildPhase will override the second one
# ! deprecated, use mergeAttrByFunc instead
mergeAttrsNoOverride = { mergeLists ? ["buildInputs" "propagatedBuildInputs"],
overrideSnd ? [ "buildPhase" ]
}: attrs1: attrs2:
foldr (n: set:
setAttr set n ( if set ? ${n}
then # merge
if elem n mergeLists # attribute contains list, merge them by concatenating
then attrs2.${n} ++ attrs1.${n}
else if elem n overrideSnd
then attrs1.${n}
else throw "error mergeAttrsNoOverride, attribute ${n} given in both attributes - no merge func defined"
else attrs2.${n} # add attribute not existing in attr1
)) attrs1 (attrNames attrs2);
mergeAttrsNoOverride =
{
mergeLists ? [
"buildInputs"
"propagatedBuildInputs"
],
overrideSnd ? [ "buildPhase" ],
}:
attrs1: attrs2:
foldr (
n: set:
setAttr set n (
if set ? ${n} then # merge
if
elem n mergeLists # attribute contains list, merge them by concatenating
then
attrs2.${n} ++ attrs1.${n}
else if elem n overrideSnd then
attrs1.${n}
else
throw "error mergeAttrsNoOverride, attribute ${n} given in both attributes - no merge func defined"
else
attrs2.${n} # add attribute not existing in attr1
)
) attrs1 (attrNames attrs2);
# example usage:
# mergeAttrByFunc {
@ -271,23 +356,31 @@ let
# { mergeAttrsBy = [...]; buildInputs = [ a b c d ]; }
# is used by defaultOverridableDelayableArgs and can be used when composing using
# foldArgs, composedArgsAndFun or applyAndFun. Example: composableDerivation in all-packages.nix
mergeAttrByFunc = x: y:
mergeAttrByFunc =
x: y:
let
mergeAttrBy2 = { mergeAttrBy = mergeAttrs; }
// (maybeAttr "mergeAttrBy" {} x)
// (maybeAttr "mergeAttrBy" {} y); in
mergeAttrBy2 = {
mergeAttrBy = mergeAttrs;
} // (maybeAttr "mergeAttrBy" { } x) // (maybeAttr "mergeAttrBy" { } y);
in
foldr mergeAttrs { } [
x y
(mapAttrs ( a: v: # merge special names using given functions
if x ? ${a}
then if y ? ${a}
then v x.${a} y.${a} # both have attr, use merge func
else x.${a} # only x has attr
else y.${a} # only y has attr)
) (removeAttrs mergeAttrBy2
x
y
(mapAttrs
(
a: v: # merge special names using given functions
if x ? ${a} then
if y ? ${a} then
v x.${a} y.${a} # both have attr, use merge func
else
x.${a} # only x has attr
else
y.${a} # only y has attr)
)
(
removeAttrs mergeAttrBy2
# don't merge attrs which are neither in x nor y
(filter (a: ! x ? ${a} && ! y ? ${a})
(attrNames mergeAttrBy2))
(filter (a: !x ? ${a} && !y ? ${a}) (attrNames mergeAttrBy2))
)
)
];
@ -296,23 +389,50 @@ let
# sane defaults (same name as attr name so that inherit can be used)
mergeAttrBy = # { buildInputs = concatList; [...]; passthru = mergeAttr; [..]; }
listToAttrs (map (n: nameValuePair n concat)
[ "nativeBuildInputs" "buildInputs" "propagatedBuildInputs" "configureFlags" "prePhases" "postAll" "patches" ])
// listToAttrs (map (n: nameValuePair n mergeAttrs) [ "passthru" "meta" "cfg" "flags" ])
// listToAttrs (map (n: nameValuePair n (a: b: "${a}\n${b}") ) [ "preConfigure" "postInstall" ])
;
listToAttrs (
map (n: nameValuePair n concat) [
"nativeBuildInputs"
"buildInputs"
"propagatedBuildInputs"
"configureFlags"
"prePhases"
"postAll"
"patches"
]
)
// listToAttrs (
map (n: nameValuePair n mergeAttrs) [
"passthru"
"meta"
"cfg"
"flags"
]
)
// listToAttrs (
map (n: nameValuePair n (a: b: "${a}\n${b}")) [
"preConfigure"
"postInstall"
]
);
nixType = x:
nixType =
x:
if isAttrs x then
if x ? outPath then "derivation"
else "attrs"
else if isFunction x then "function"
else if isList x then "list"
else if x == true then "bool"
else if x == false then "bool"
else if x == null then "null"
else if isInt x then "int"
else "string";
if x ? outPath then "derivation" else "attrs"
else if isFunction x then
"function"
else if isList x then
"list"
else if x == true then
"bool"
else if x == false then
"bool"
else if x == null then
"null"
else if isInt x then
"int"
else
"string";
/**
# Deprecated

View file

@ -1,20 +1,18 @@
{ lib }:
let
inherit (lib)
genAttrs
isString
throwIfNot
;
inherit (lib) genAttrs isString throwIfNot;
showMaybeAttrPosPre = prefix: attrName: v:
let pos = builtins.unsafeGetAttrPos attrName v;
in if pos == null then "" else "${prefix}${pos.file}:${toString pos.line}:${toString pos.column}";
showMaybeAttrPosPre =
prefix: attrName: v:
let
pos = builtins.unsafeGetAttrPos attrName v;
in
if pos == null then "" else "${prefix}${pos.file}:${toString pos.line}:${toString pos.column}";
showMaybePackagePosPre = prefix: pkg:
if pkg?meta.position && isString pkg.meta.position
then "${prefix}${pkg.meta.position}"
else "";
showMaybePackagePosPre =
prefix: pkg:
if pkg ? meta.position && isString pkg.meta.position then "${prefix}${pkg.meta.position}" else "";
in
{
/*
@ -61,35 +59,33 @@ in
(lazyDerivation { inherit derivation }).passthru
(lazyDerivation { inherit derivation }).pythonPath
*/
lazyDerivation =
args@{
# The derivation to be wrapped.
derivation
, # Optional meta attribute.
derivation,
# Optional meta attribute.
#
# While this function is primarily about derivations, it can improve
# the `meta` package attribute, which is usually specified through
# `mkDerivation`.
meta ? null
, # Optional extra values to add to the returned attrset.
meta ? null,
# Optional extra values to add to the returned attrset.
#
# This can be used for adding package attributes, such as `tests`.
passthru ? { }
, # Optional list of assumed outputs. Default: ["out"]
passthru ? { },
# Optional list of assumed outputs. Default: ["out"]
#
# This must match the set of outputs that the returned derivation has.
# You must use this when the derivation has multiple outputs.
outputs ? [ "out" ]
outputs ? [ "out" ],
}:
let
# These checks are strict in `drv` and some `drv` attributes, but the
# attrset spine returned by lazyDerivation does not depend on it.
# Instead, the individual derivation attributes do depend on it.
checked =
throwIfNot (derivation.type or null == "derivation")
"lazyDerivation: input must be a derivation."
throwIfNot (derivation.type or null == "derivation") "lazyDerivation: input must be a derivation."
throwIfNot
# NOTE: Technically we could require our outputs to be a subset of the
# actual ones, or even leave them unchecked and fail on a lazy basis.
@ -139,7 +135,13 @@ in
# A fixed set of derivation values, so that `lazyDerivation` can return
# its attrset before evaluating `derivation`.
# This must only list attributes that are available on _all_ derivations.
inherit (checked) outPath outputName drvPath name system;
inherit (checked)
outPath
outputName
drvPath
name
system
;
inherit outputs;
# The meta attribute can either be taken from the derivation, or if the
@ -149,7 +151,8 @@ in
// genAttrs outputs (outputName: checked.${outputName})
// passthru;
/* Conditionally set a derivation attribute.
/*
Conditionally set a derivation attribute.
Because `mkDerivation` sets `__ignoreNulls = true`, a derivation
attribute set to `null` will not impact the derivation output hash.
@ -173,5 +176,6 @@ in
# Condition
cond:
# Attribute value
value: if cond then value else null;
value:
if cond then value else null;
}

View file

@ -7,7 +7,11 @@
# easy proxy configuration. This is impure, but a fixed-output
# derivation like fetchurl is allowed to do so since its result is
# by definition pure.
"http_proxy" "https_proxy" "ftp_proxy" "all_proxy" "no_proxy"
"http_proxy"
"https_proxy"
"ftp_proxy"
"all_proxy"
"no_proxy"
];
}

View file

@ -57,7 +57,6 @@
If you need more file set functions,
see [this issue](https://github.com/NixOS/nixpkgs/issues/266356) to request it.
## Implicit coercion from paths to file sets {#sec-fileset-path-coercion}
All functions accepting file sets as arguments can also accept [paths](https://nixos.org/manual/nix/stable/language/values.html#type-path) as arguments.
@ -127,35 +126,20 @@ let
nixVersion
;
inherit (lib.lists)
elemAt
imap0
;
inherit (lib.lists) elemAt imap0;
inherit (lib.path)
hasPrefix
splitRoot
;
inherit (lib.path) hasPrefix splitRoot;
inherit (lib.strings)
isStringLike
versionOlder
;
inherit (lib.strings) isStringLike versionOlder;
inherit (lib.filesystem)
pathType
;
inherit (lib.filesystem) pathType;
inherit (lib.sources)
cleanSourceWith
;
inherit (lib.sources) cleanSourceWith;
inherit (lib.trivial)
isFunction
pipe
;
inherit (lib.trivial) isFunction pipe;
in {
in
{
/*
Create a file set from a path that may or may not exist:
@ -173,11 +157,9 @@ in {
path:
if !isPath path then
if isStringLike path then
throw ''
lib.fileset.maybeMissing: Argument ("${toString path}") is a string-like value, but it should be a path instead.''
throw ''lib.fileset.maybeMissing: Argument ("${toString path}") is a string-like value, but it should be a path instead.''
else
throw ''
lib.fileset.maybeMissing: Argument is of type ${typeOf path}, but it should be a path instead.''
throw ''lib.fileset.maybeMissing: Argument is of type ${typeOf path}, but it should be a path instead.''
else if !pathExists path then
_emptyWithoutBase
else
@ -220,9 +202,7 @@ in {
# and we cannot change that because of https://github.com/nix-community/nixdoc/issues/76
actualFileset = _coerce "lib.fileset.trace: Argument" fileset;
in
seq
(_printFileset actualFileset)
(x: x);
seq (_printFileset actualFileset) (x: x);
/*
Incrementally evaluate and trace a file set in a pretty way.
@ -267,8 +247,7 @@ in {
# and we cannot change that because of https://github.com/nix-community/nixdoc/issues/76
actualFileset = _coerce "lib.fileset.traceVal: Argument" fileset;
in
seq
(_printFileset actualFileset)
seq (_printFileset actualFileset)
# We could also return the original fileset argument here,
# but that would then duplicate work for consumers of the fileset, because then they have to coerce it again
actualFileset;
@ -340,7 +319,8 @@ in {
}
=> <error>
*/
toSource = {
toSource =
{
/*
(required) The local directory [path](https://nixos.org/manual/nix/stable/language/values.html#type-path) that will correspond to the root of the resulting store path.
Paths in [strings](https://nixos.org/manual/nix/stable/language/values.html#type-string), including Nix store paths, cannot be passed as `root`.
@ -361,7 +341,6 @@ in {
:::{.note}
If a directory does not recursively contain any file, it is omitted from the store path contents.
:::
*/
fileset,
}:
@ -386,8 +365,7 @@ in {
lib.fileset.toSource: `root` (${toString root}) is a string-like value, but it should be a path instead.
Paths in strings are not supported by `lib.fileset`, use `lib.sources` or derivations instead.''
else
throw ''
lib.fileset.toSource: `root` is of type ${typeOf root}, but it should be a path instead.''
throw ''lib.fileset.toSource: `root` is of type ${typeOf root}, but it should be a path instead.''
# Currently all Nix paths have the same filesystem root, but this could change in the future.
# See also ../path/README.md
else if !fileset._internalIsEmptyWithoutBase && rootFilesystemRoot != filesetFilesystemRoot then
@ -397,8 +375,7 @@ in {
`fileset`: Filesystem root is "${toString filesetFilesystemRoot}"
Different filesystem roots are not supported.''
else if !pathExists root then
throw ''
lib.fileset.toSource: `root` (${toString root}) is a path that does not exist.''
throw ''lib.fileset.toSource: `root` (${toString root}) is a path that does not exist.''
else if pathType root != "directory" then
throw ''
lib.fileset.toSource: `root` (${toString root}) is a file, but it should be a directory instead. Potential solutions:
@ -410,14 +387,12 @@ in {
- Set `root` to ${toString fileset._internalBase} or any directory higher up. This changes the layout of the resulting store path.
- Set `fileset` to a file set that cannot contain files outside the `root` (${toString root}). This could change the files included in the result.''
else
seq sourceFilter
cleanSourceWith {
seq sourceFilter cleanSourceWith {
name = "source";
src = root;
filter = sourceFilter;
};
/*
The list of file paths contained in the given file set.
@ -446,8 +421,7 @@ in {
# The file set whose file paths to return.
# This argument can also be a path,
# which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
fileset:
_toList (_coerce "lib.fileset.toList: Argument" fileset);
fileset: _toList (_coerce "lib.fileset.toList: Argument" fileset);
/*
The file set containing all files that are in either of two given file sets.
@ -479,8 +453,8 @@ in {
# This argument can also be a path,
# which gets [implicitly coerced to a file set](#sec-fileset-path-coercion).
fileset2:
_unionMany
(_coerceMany "lib.fileset.union" [
_unionMany (
_coerceMany "lib.fileset.union" [
{
context = "First argument";
value = fileset1;
@ -489,7 +463,8 @@ in {
context = "Second argument";
value = fileset2;
}
]);
]
);
/*
The file set containing all files that are in any of the given file sets.
@ -528,15 +503,16 @@ in {
# which get [implicitly coerced to file sets](#sec-fileset-path-coercion).
filesets:
if !isList filesets then
throw ''
lib.fileset.unions: Argument is of type ${typeOf filesets}, but it should be a list instead.''
throw ''lib.fileset.unions: Argument is of type ${typeOf filesets}, but it should be a list instead.''
else
pipe filesets [
# Annotate the elements with context, used by _coerceMany for better errors
(imap0 (i: el: {
(imap0 (
i: el: {
context = "Element ${toString i}";
value = el;
}))
}
))
(_coerceMany "lib.fileset.unions")
_unionMany
];
@ -576,9 +552,7 @@ in {
}
];
in
_intersection
(elemAt filesets 0)
(elemAt filesets 1);
_intersection (elemAt filesets 0) (elemAt filesets 1);
/*
The file set containing all files from the first file set that are not in the second file set.
@ -628,9 +602,7 @@ in {
}
];
in
_difference
(elemAt filesets 0)
(elemAt filesets 1);
_difference (elemAt filesets 0) (elemAt filesets 1);
/*
Filter a file set to only contain files matching some predicate.
@ -683,19 +655,16 @@ in {
# The path whose files to filter
path:
if !isFunction predicate then
throw ''
lib.fileset.fileFilter: First argument is of type ${typeOf predicate}, but it should be a function instead.''
throw ''lib.fileset.fileFilter: First argument is of type ${typeOf predicate}, but it should be a function instead.''
else if !isPath path then
if path._type or "" == "fileset" then
throw ''
lib.fileset.fileFilter: Second argument is a file set, but it should be a path instead.
If you need to filter files in a file set, use `intersection fileset (fileFilter pred ./.)` instead.''
else
throw ''
lib.fileset.fileFilter: Second argument is of type ${typeOf path}, but it should be a path instead.''
throw ''lib.fileset.fileFilter: Second argument is of type ${typeOf path}, but it should be a path instead.''
else if !pathExists path then
throw ''
lib.fileset.fileFilter: Second argument (${toString path}) is a path that does not exist.''
throw ''lib.fileset.fileFilter: Second argument (${toString path}) is a path that does not exist.''
else
_fileFilter predicate path;
@ -741,7 +710,8 @@ in {
./src
]);
*/
fromSource = source:
fromSource =
source:
let
# This function uses `._isLibCleanSourceWith`, `.origSrc` and `.filter`,
# which are technically internal to lib.sources,
@ -757,11 +727,9 @@ in {
lib.fileset.fromSource: The source origin of the argument is a string-like value ("${toString path}"), but it should be a path instead.
Sources created from paths in strings cannot be turned into file sets, use `lib.sources` or derivations instead.''
else
throw ''
lib.fileset.fromSource: The source origin of the argument is of type ${typeOf path}, but it should be a path instead.''
throw ''lib.fileset.fromSource: The source origin of the argument is of type ${typeOf path}, but it should be a path instead.''
else if !pathExists path then
throw ''
lib.fileset.fromSource: The source origin (${toString path}) of the argument is a path that does not exist.''
throw ''lib.fileset.fromSource: The source origin (${toString path}) of the argument is a path that does not exist.''
else if isFiltered then
_fromSourceFilter path source.filter
else
@ -789,12 +757,7 @@ in {
The [path](https://nixos.org/manual/nix/stable/language/values#type-path) to the working directory of a local Git repository.
This directory must contain a `.git` file or subdirectory.
*/
path:
_fromFetchGit
"gitTracked"
"argument"
path
{};
path: _fromFetchGit "gitTracked" "argument" path { };
/*
Create a file set containing all [Git-tracked files](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository) in a repository.
@ -847,14 +810,9 @@ in {
else if recurseSubmodules && versionOlder nixVersion _fetchGitSubmodulesMinver then
throw "lib.fileset.gitTrackedWith: Setting the attribute `recurseSubmodules` to `true` is only supported for Nix version ${_fetchGitSubmodulesMinver} and after, but Nix version ${nixVersion} is used."
else
_fromFetchGit
"gitTrackedWith"
"second argument"
path
_fromFetchGit "gitTrackedWith" "second argument" path
# This is the only `fetchGit` parameter that makes sense in this context.
# We can't just pass `submodules = recurseSubmodules` here because
# this would fail for Nix versions that don't support `submodules`.
(lib.optionalAttrs recurseSubmodules {
submodules = true;
});
(lib.optionalAttrs recurseSubmodules { submodules = true; });
}

View file

@ -1,4 +1,6 @@
{ lib ? import ../. }:
{
lib ? import ../.,
}:
let
inherit (builtins)
@ -23,9 +25,7 @@ let
zipAttrsWith
;
inherit (lib.filesystem)
pathType
;
inherit (lib.filesystem) pathType;
inherit (lib.lists)
all
@ -49,10 +49,7 @@ let
splitStorePath
;
inherit (lib.path.subpath)
components
join
;
inherit (lib.path.subpath) components join;
inherit (lib.strings)
isStringLike
@ -63,9 +60,7 @@ let
versionAtLeast
;
inherit (lib.trivial)
inPureEvalMode
;
inherit (lib.trivial) inPureEvalMode;
in
# Rare case of justified usage of rec:
# - This file is internal, so the return value doesn't matter, no need to make things overridable
@ -87,7 +82,8 @@ rec {
let
parts = splitRoot filesetV0._internalBase;
in
filesetV0 // {
filesetV0
// {
_internalVersion = 1;
_internalBaseRoot = parts.root;
_internalBaseComponents = components parts.subpath;
@ -98,15 +94,14 @@ rec {
(
filesetV1:
# This change is backwards compatible (but not forwards compatible, so we still need a new version)
filesetV1 // {
_internalVersion = 2;
}
filesetV1 // { _internalVersion = 2; }
)
# Convert v2 into v3: filesetTree's now have a representation for an empty file set without a base path
(
filesetV2:
filesetV2 // {
filesetV2
// {
# All v1 file sets are not the new empty file set
_internalIsEmptyWithoutBase = false;
_internalVersion = 3;
@ -136,7 +131,8 @@ rec {
# Create a fileset, see ./README.md#fileset
# Type: path -> filesetTree -> fileset
_create = base: tree:
_create =
base: tree:
let
# Decompose the base into its components
# See ../path/README.md for why we're not just using `toString`
@ -162,7 +158,8 @@ rec {
# Coerce a value to a fileset, erroring when the value cannot be coerced.
# The string gives the context for error messages.
# Type: String -> (fileset | Path) -> fileset
_coerce = context: value:
_coerce =
context: value:
if value._type or "" == "fileset" then
if value._internalVersion > _currentVersion then
throw ''
@ -173,7 +170,9 @@ rec {
else if value._internalVersion < _currentVersion then
let
# Get all the migration functions necessary to convert from the old to the current version
migrationsToApply = sublist value._internalVersion (_currentVersion - value._internalVersion) migrations;
migrationsToApply = sublist value._internalVersion (
_currentVersion - value._internalVersion
) migrations;
in
foldl' (value: migration: migration value) value migrationsToApply
else
@ -189,8 +188,7 @@ rec {
${context} ("${toString value}") is a string-like value, but it should be a file set or a path instead.
Paths represented as strings are not supported by `lib.fileset`, use `lib.sources` or derivations instead.''
else
throw ''
${context} is of type ${typeOf value}, but it should be a file set or a path instead.''
throw ''${context} is of type ${typeOf value}, but it should be a file set or a path instead.''
else if !pathExists value then
throw ''
${context} (${toString value}) is a path that does not exist.
@ -201,11 +199,10 @@ rec {
# Coerce many values to filesets, erroring when any value cannot be coerced,
# or if the filesystem root of the values doesn't match.
# Type: String -> [ { context :: String, value :: fileset | Path } ] -> [ fileset ]
_coerceMany = functionContext: list:
_coerceMany =
functionContext: list:
let
filesets = map ({ context, value }:
_coerce "${functionContext}: ${context}" value
) list;
filesets = map ({ context, value }: _coerce "${functionContext}: ${context}" value) list;
# Find the first value with a base, there may be none!
firstWithBase = findFirst (fileset: !fileset._internalIsEmptyWithoutBase) null filesets;
@ -213,10 +210,10 @@ rec {
firstBaseRoot = firstWithBase._internalBaseRoot;
# Finds the first element with a filesystem root different than the first element, if any
differentIndex = findFirstIndex (fileset:
differentIndex = findFirstIndex (
fileset:
# The empty value without a base doesn't have a base path
! fileset._internalIsEmptyWithoutBase
&& firstBaseRoot != fileset._internalBaseRoot
!fileset._internalIsEmptyWithoutBase && firstBaseRoot != fileset._internalBaseRoot
) null filesets;
in
# Only evaluates `differentIndex` if there are any elements with a base
@ -231,7 +228,8 @@ rec {
# Create a file set from a path.
# Type: Path -> fileset
_singleton = path:
_singleton =
path:
let
type = pathType path;
in
@ -244,21 +242,18 @@ rec {
# "default.nix" = <type>;
# }
# See ./README.md#single-files
_create (dirOf path)
{
${baseNameOf path} = type;
};
_create (dirOf path) { ${baseNameOf path} = type; };
# Expand a directory representation to an equivalent one in attribute set form.
# All directory entries are included in the result.
# Type: Path -> filesetTree -> { <name> = filesetTree; }
_directoryEntries = path: value:
_directoryEntries =
path: value:
if value == "directory" then
readDir path
else
# Set all entries not present to null
mapAttrs (name: value: null) (readDir path)
// value;
mapAttrs (name: value: null) (readDir path) // value;
/*
A normalisation of a filesetTree suitable filtering with `builtins.path`:
@ -271,7 +266,8 @@ rec {
Type: Path -> filesetTree -> filesetTree
*/
_normaliseTreeFilter = path: tree:
_normaliseTreeFilter =
path: tree:
if tree == "directory" || isAttrs tree then
let
entries = _directoryEntries path tree;
@ -301,7 +297,8 @@ rec {
Type: Path -> filesetTree -> filesetTree (with "emptyDir"'s)
*/
_normaliseTreeMinimal = path: tree:
_normaliseTreeMinimal =
path: tree:
if tree == "directory" || isAttrs tree then
let
entries = _directoryEntries path tree;
@ -334,9 +331,11 @@ rec {
# Trace a filesetTree in a pretty way when the resulting value is evaluated.
# This can handle both normal filesetTree's, and ones returned from _normaliseTreeMinimal
# Type: Path -> filesetTree (with "emptyDir"'s) -> Null
_printMinimalTree = base: tree:
_printMinimalTree =
base: tree:
let
treeSuffix = tree:
treeSuffix =
tree:
if isAttrs tree then
""
else if tree == "directory" then
@ -349,14 +348,15 @@ rec {
" (${tree})";
# Only for attribute set trees
traceTreeAttrs = prevLine: indent: tree:
foldl' (prevLine: name:
traceTreeAttrs =
prevLine: indent: tree:
foldl' (
prevLine: name:
let
subtree = tree.${name};
# Evaluating this prints the line for this subtree
thisLine =
trace "${indent}- ${name}${treeSuffix subtree}" prevLine;
thisLine = trace "${indent}- ${name}${treeSuffix subtree}" prevLine;
in
if subtree == null || subtree == "emptyDir" then
# Don't print anything at all if this subtree is empty
@ -378,24 +378,24 @@ rec {
else
trace "${toString base}${treeSuffix tree}" null;
in
if isAttrs tree then
traceTreeAttrs firstLine "" tree
else
firstLine;
if isAttrs tree then traceTreeAttrs firstLine "" tree else firstLine;
# Pretty-print a file set in a pretty way when the resulting value is evaluated
# Type: fileset -> Null
_printFileset = fileset:
_printFileset =
fileset:
if fileset._internalIsEmptyWithoutBase then
trace "(empty)" null
else
_printMinimalTree fileset._internalBase
(_normaliseTreeMinimal fileset._internalBase fileset._internalTree);
_printMinimalTree fileset._internalBase (
_normaliseTreeMinimal fileset._internalBase fileset._internalTree
);
# Turn a fileset into a source filter function suitable for `builtins.path`
# Only directories recursively containing at least one files are recursed into
# Type: fileset -> (String -> String -> Bool)
_toSourceFilter = fileset:
_toSourceFilter =
fileset:
let
# Simplify the tree, necessary to make sure all empty directories are null
# which has the effect that they aren't included in the result
@ -414,9 +414,11 @@ rec {
# Check whether a list of path components under the base path exists in the tree.
# This function is called often, so it should be fast.
# Type: [ String ] -> Bool
inTree = components:
inTree =
components:
let
recurse = index: localTree:
recurse =
index: localTree:
if isAttrs localTree then
# We have an attribute set, meaning this is a directory with at least one file
if index >= length components then
@ -431,7 +433,8 @@ rec {
# If it's not an attribute set it can only be either null (in which case it's not included)
# or a string ("directory" or "regular", etc.) in which case it's included
localTree != null;
in recurse 0 tree;
in
recurse 0 tree;
# Filter suited when there's no files
empty = _: _: false;
@ -483,16 +486,14 @@ rec {
# Special case because the code below assumes that the _internalBase is always included in the result
# which shouldn't be done when we have no files at all in the base
# This also forces the tree before returning the filter, leads to earlier error messages
if fileset._internalIsEmptyWithoutBase || tree == null then
empty
else
nonEmpty;
if fileset._internalIsEmptyWithoutBase || tree == null then empty else nonEmpty;
# Turn a builtins.filterSource-based source filter on a root path into a file set
# containing only files included by the filter.
# The filter is lazily called as necessary to determine whether paths are included
# Type: Path -> (String -> String -> Bool) -> fileset
_fromSourceFilter = root: sourceFilter:
_fromSourceFilter =
root: sourceFilter:
let
# During the recursion we need to track both:
# - The path value such that we can safely call `readDir` on it
@ -503,7 +504,8 @@ rec {
# which is a fairly expensive operation
# Create a file set from a directory entry
fromDirEntry = path: pathString: type:
fromDirEntry =
path: pathString: type:
# The filter needs to run on the path as a string
if !sourceFilter pathString type then
null
@ -513,7 +515,8 @@ rec {
type;
# Create a file set from a directory
fromDir = path: pathString:
fromDir =
path: pathString:
mapAttrs
# This looks a bit funny, but we need both the path-based and the path string-based values
(name: fromDirEntry (path + "/${name}") (pathString + "/${name}"))
@ -536,20 +539,17 @@ rec {
else
# Direct files are always included by builtins.path without calling the filter
# But we need to lift up the base path to its parent to satisfy the base path invariant
_create (dirOf root)
{
${baseNameOf root} = rootPathType;
};
_create (dirOf root) { ${baseNameOf root} = rootPathType; };
# Turns a file set into the list of file paths it includes.
# Type: fileset -> [ Path ]
_toList = fileset:
_toList =
fileset:
let
recurse = path: tree:
recurse =
path: tree:
if isAttrs tree then
concatLists (mapAttrsToList (name: value:
recurse (path + "/${name}") value
) tree)
concatLists (mapAttrsToList (name: value: recurse (path + "/${name}") value) tree)
else if tree == "directory" then
recurse path (readDir path)
else if tree == null then
@ -565,9 +565,11 @@ rec {
# Transforms the filesetTree of a file set to a shorter base path, e.g.
# _shortenTreeBase [ "foo" ] (_create /foo/bar null)
# => { bar = null; }
_shortenTreeBase = targetBaseComponents: fileset:
_shortenTreeBase =
targetBaseComponents: fileset:
let
recurse = index:
recurse =
index:
# If we haven't reached the required depth yet
if index < length fileset._internalBaseComponents then
# Create an attribute set and recurse as the value, this can be lazily evaluated this way
@ -581,9 +583,11 @@ rec {
# Transforms the filesetTree of a file set to a longer base path, e.g.
# _lengthenTreeBase [ "foo" "bar" ] (_create /foo { bar.baz = "regular"; })
# => { baz = "regular"; }
_lengthenTreeBase = targetBaseComponents: fileset:
_lengthenTreeBase =
targetBaseComponents: fileset:
let
recurse = index: tree:
recurse =
index: tree:
# If the filesetTree is an attribute set and we haven't reached the required depth yet
if isAttrs tree && index < length targetBaseComponents then
# Recurse with the tree under the right component (which might not exist)
@ -602,7 +606,8 @@ rec {
# Computes the union of a list of filesets.
# The filesets must already be coerced and validated to be in the same filesystem root
# Type: [ Fileset ] -> Fileset
_unionMany = filesets:
_unionMany =
filesets:
let
# All filesets that have a base, aka not the ones that are the empty value without a base
filesetsWithBase = filter (fileset: !fileset._internalIsEmptyWithoutBase) filesets;
@ -618,8 +623,8 @@ rec {
# A list of path components common to all base paths.
# Note that commonPrefix can only be fully evaluated,
# so this cannot cause a stack overflow due to a build-up of unevaluated thunks.
commonBaseComponents = foldl'
(components: el: commonPrefix components el._internalBaseComponents)
commonBaseComponents =
foldl' (components: el: commonPrefix components el._internalBaseComponents)
firstWithBase._internalBaseComponents
# We could also not do the `tail` here to avoid a list allocation,
# but then we'd have to pay for a potentially expensive
@ -643,15 +648,13 @@ rec {
resultTree = _unionTrees trees;
in
# If there's no values with a base, we have no files
if filesetsWithBase == [ ] then
_emptyWithoutBase
else
_create commonBase resultTree;
if filesetsWithBase == [ ] then _emptyWithoutBase else _create commonBase resultTree;
# The union of multiple filesetTree's with the same base path.
# Later elements are only evaluated if necessary.
# Type: [ filesetTree ] -> filesetTree
_unionTrees = trees:
_unionTrees =
trees:
let
stringIndex = findFirstIndex isString null trees;
withoutNull = filter (tree: tree != null) trees;
@ -671,18 +674,15 @@ rec {
# Computes the intersection of a list of filesets.
# The filesets must already be coerced and validated to be in the same filesystem root
# Type: Fileset -> Fileset -> Fileset
_intersection = fileset1: fileset2:
_intersection =
fileset1: fileset2:
let
# The common base components prefix, e.g.
# (/foo/bar, /foo/bar/baz) -> /foo/bar
# (/foo/bar, /foo/baz) -> /foo
commonBaseComponentsLength =
# TODO: Have a `lib.lists.commonPrefixLength` function such that we don't need the list allocation from commonPrefix here
length (
commonPrefix
fileset1._internalBaseComponents
fileset2._internalBaseComponents
);
length (commonPrefix fileset1._internalBaseComponents fileset2._internalBaseComponents);
# To be able to intersect filesetTree's together, they need to have the same base path.
# Base paths can be intersected by taking the longest one (if any)
@ -725,12 +725,11 @@ rec {
# The intersection of two filesetTree's with the same base path
# The second element is only evaluated as much as necessary.
# Type: filesetTree -> filesetTree -> filesetTree
_intersectTree = lhs: rhs:
_intersectTree =
lhs: rhs:
if isAttrs lhs && isAttrs rhs then
# Both sides are attribute sets, we can recurse for the attributes existing on both sides
mapAttrs
(name: _intersectTree lhs.${name})
(builtins.intersectAttrs lhs rhs)
mapAttrs (name: _intersectTree lhs.${name}) (builtins.intersectAttrs lhs rhs)
else if lhs == null || isString rhs then
# If the lhs is null, the result should also be null
# And if the rhs is the identity element
@ -743,18 +742,15 @@ rec {
# Compute the set difference between two file sets.
# The filesets must already be coerced and validated to be in the same filesystem root.
# Type: Fileset -> Fileset -> Fileset
_difference = positive: negative:
_difference =
positive: negative:
let
# The common base components prefix, e.g.
# (/foo/bar, /foo/bar/baz) -> /foo/bar
# (/foo/bar, /foo/baz) -> /foo
commonBaseComponentsLength =
# TODO: Have a `lib.lists.commonPrefixLength` function such that we don't need the list allocation from commonPrefix here
length (
commonPrefix
positive._internalBaseComponents
negative._internalBaseComponents
);
length (commonPrefix positive._internalBaseComponents negative._internalBaseComponents);
# We need filesetTree's with the same base to be able to compute the difference between them
# This here is the filesetTree from the negative file set, but for a base path that matches the positive file set.
@ -786,9 +782,7 @@ rec {
null;
resultingTree =
_differenceTree
positive._internalBase
positive._internalTree
_differenceTree positive._internalBase positive._internalTree
negativeTreeWithPositiveBase;
in
# If the first file set is empty, we can never have any files in the result
@ -805,7 +799,8 @@ rec {
# Computes the set difference of two filesetTree's
# Type: Path -> filesetTree -> filesetTree
_differenceTree = path: lhs: rhs:
_differenceTree =
path: lhs: rhs:
# If the lhs doesn't have any files, or the right hand side includes all files
if lhs == null || isString rhs then
# The result will always be empty
@ -816,17 +811,19 @@ rec {
lhs
else
# Otherwise we always have two attribute sets to recurse into
mapAttrs (name: lhsValue:
_differenceTree (path + "/${name}") lhsValue (rhs.${name} or null)
) (_directoryEntries path lhs);
mapAttrs (name: lhsValue: _differenceTree (path + "/${name}") lhsValue (rhs.${name} or null)) (
_directoryEntries path lhs
);
# Filters all files in a path based on a predicate
# Type: ({ name, type, ... } -> Bool) -> Path -> FileSet
_fileFilter = predicate: root:
_fileFilter =
predicate: root:
let
# Check the predicate for a single file
# Type: String -> String -> filesetTree
fromFile = name: type:
fromFile =
name: type:
if
predicate {
inherit name type;
@ -834,7 +831,8 @@ rec {
# To ensure forwards compatibility with more arguments being added in the future,
# adding an attribute which can't be deconstructed :)
"lib.fileset.fileFilter: The predicate function passed as the first argument must be able to handle extra attributes for future compatibility. If you're using `{ name, file, hasExt }:`, use `{ name, file, hasExt, ... }:` instead." = null;
"lib.fileset.fileFilter: The predicate function passed as the first argument must be able to handle extra attributes for future compatibility. If you're using `{ name, file, hasExt }:`, use `{ name, file, hasExt, ... }:` instead." =
null;
}
then
type
@ -843,12 +841,10 @@ rec {
# Check the predicate for all files in a directory
# Type: Path -> filesetTree
fromDir = path:
mapAttrs (name: type:
if type == "directory" then
fromDir (path + "/${name}")
else
fromFile name type
fromDir =
path:
mapAttrs (
name: type: if type == "directory" then fromDir (path + "/${name}") else fromFile name type
) (readDir path);
rootType = pathType root;
@ -857,10 +853,7 @@ rec {
_create root (fromDir root)
else
# Single files are turned into a directory containing that file or nothing.
_create (dirOf root) {
${baseNameOf root} =
fromFile (baseNameOf root) rootType;
};
_create (dirOf root) { ${baseNameOf root} = fromFile (baseNameOf root) rootType; };
# Support for `builtins.fetchGit` with `submodules = true` was introduced in 2.4
# https://github.com/NixOS/nix/commit/55cefd41d63368d4286568e2956afd535cb44018
@ -876,22 +869,21 @@ rec {
# - The store path must not include files that don't exist in the respective local path.
#
# Type: Path -> String -> FileSet
_mirrorStorePath = localPath: storePath:
_mirrorStorePath =
localPath: storePath:
let
recurse = focusedStorePath:
mapAttrs (name: type:
if type == "directory" then
recurse (focusedStorePath + "/${name}")
else
type
recurse =
focusedStorePath:
mapAttrs (
name: type: if type == "directory" then recurse (focusedStorePath + "/${name}") else type
) (builtins.readDir focusedStorePath);
in
_create localPath
(recurse storePath);
_create localPath (recurse storePath);
# Create a file set from the files included in the result of a fetchGit call
# Type: String -> String -> Path -> Attrs -> FileSet
_fromFetchGit = function: argument: path: extraFetchGitAttrs:
_fromFetchGit =
function: argument: path: extraFetchGitAttrs:
let
# The code path for when isStorePath is true
tryStorePath =
@ -922,7 +914,8 @@ rec {
# With the [lazy trees PR](https://github.com/NixOS/nix/pull/6530),
# the unnecessarily import could be avoided.
# However a simpler alternative still would be [a builtins.gitLsFiles](https://github.com/NixOS/nix/issues/2944).
fetchResult = fetchGit ({
fetchResult = fetchGit (
{
url = path;
}
# In older Nix versions, repositories were always assumed to be deep clones, which made `fetchGit` fail for shallow clones
@ -934,7 +927,8 @@ rec {
# Checking for `.git/shallow` doesn't seem worth it, especially since that's more of an implementation detail,
# and would also require more code to handle worktrees where `.git` is a file.
// optionalAttrs (versionAtLeast nixVersion _fetchGitShallowMinver) { shallow = true; }
// extraFetchGitAttrs);
// extraFetchGitAttrs
);
in
# We can identify local working directories by checking for .git,
# see https://git-scm.com/docs/gitrepository-layout#_description.

View file

@ -8,18 +8,21 @@
# }
self: super: {
path = super.path // {
splitRoot = path:
splitRoot =
path:
let
parts = super.path.splitRoot path;
components = self.path.subpath.components parts.subpath;
count = self.length components;
rootIndex = count - self.lists.findFirstIndex
(component: component == "mock-root")
(self.length components)
(self.reverseList components);
rootIndex =
count
- self.lists.findFirstIndex (component: component == "mock-root") (self.length components) (
self.reverseList components
);
root = self.path.append parts.root (self.path.subpath.join (self.take rootIndex components));
subpath = self.path.subpath.join (self.drop rootIndex components);
in {
in
{
inherit root subpath;
};
};

View file

@ -6,25 +6,13 @@
# Tested in lib/tests/filesystem.sh
let
inherit (builtins)
readDir
pathExists
toString
;
inherit (builtins) readDir pathExists toString;
inherit (lib.attrsets)
mapAttrs'
filterAttrs
;
inherit (lib.attrsets) mapAttrs' filterAttrs;
inherit (lib.filesystem)
pathType
;
inherit (lib.filesystem) pathType;
inherit (lib.strings)
hasSuffix
removeSuffix
;
inherit (lib.strings) hasSuffix removeSuffix;
in
{
@ -46,17 +34,21 @@ in
pathType =
builtins.readFileType or
# Nix <2.14 compatibility shim
(path:
if ! pathExists path
(
path:
if
!pathExists path
# Fail irrecoverably to mimic the historic behavior of this function and
# the new builtins.readFileType
then abort "lib.filesystem.pathType: Path ${toString path} does not exist."
then
abort "lib.filesystem.pathType: Path ${toString path} does not exist."
# The filesystem root is the only path where `dirOf / == /` and
# `baseNameOf /` is not valid. We can detect this and directly return
# "directory", since we know the filesystem root can't be anything else.
else if dirOf path == path
then "directory"
else (readDir (dirOf path)).${baseNameOf path}
else if dirOf path == path then
"directory"
else
(readDir (dirOf path)).${baseNameOf path}
);
/*
@ -75,8 +67,7 @@ in
pathIsDirectory /some/file.nix
=> false
*/
pathIsDirectory = path:
pathExists path && pathType path == "directory";
pathIsDirectory = path: pathExists path && pathType path == "directory";
/*
Whether a path exists and is a regular file, meaning not a symlink or any other special file type.
@ -94,8 +85,7 @@ in
pathIsRegularFile /some/file.nix
=> true
*/
pathIsRegularFile = path:
pathExists path && pathType path == "regular";
pathIsRegularFile = path: pathExists path && pathType path == "regular";
/*
A map of all haskell packages defined in the given path,
@ -107,19 +97,20 @@ in
haskellPathsInDir =
# The directory within to search
root:
let # Files in the root
let
# Files in the root
root-files = builtins.attrNames (builtins.readDir root);
# Files with their full paths
root-files-with-paths =
map (file:
{ name = file; value = root + "/${file}"; }
) root-files;
root-files-with-paths = map (file: {
name = file;
value = root + "/${file}";
}) root-files;
# Subdirectories of the root with a cabal file.
cabal-subdirs =
builtins.filter ({ name, value }:
builtins.pathExists (value + "/${name}.cabal")
cabal-subdirs = builtins.filter (
{ name, value }: builtins.pathExists (value + "/${name}.cabal")
) root-files-with-paths;
in builtins.listToAttrs cabal-subdirs;
in
builtins.listToAttrs cabal-subdirs;
/*
Find the first directory containing a file matching 'pattern'
upward from a given 'file'.
@ -132,23 +123,28 @@ in
pattern:
# The file to start searching upward from
file:
let go = path:
let files = builtins.attrNames (builtins.readDir path);
matches = builtins.filter (match: match != null)
(map (builtins.match pattern) files);
let
go =
path:
let
files = builtins.attrNames (builtins.readDir path);
matches = builtins.filter (match: match != null) (map (builtins.match pattern) files);
in
if builtins.length matches != 0
then { inherit path matches; }
else if path == /.
then null
else go (dirOf path);
if builtins.length matches != 0 then
{ inherit path matches; }
else if path == /. then
null
else
go (dirOf path);
parent = dirOf file;
isDir =
let base = baseNameOf file;
let
base = baseNameOf file;
type = (builtins.readDir parent).${base} or null;
in file == /. || type == "directory";
in go (if isDir then file else parent);
in
file == /. || type == "directory";
in
go (if isDir then file else parent);
/*
Given a directory, return a flattened list of all files within it recursively.
@ -158,12 +154,15 @@ in
listFilesRecursive =
# The path to recursively list
dir:
lib.flatten (lib.mapAttrsToList (name: type:
lib.flatten (
lib.mapAttrsToList (
name: type:
if type == "directory" then
lib.filesystem.listFilesRecursive (dir + "/${name}")
else
dir + "/${name}"
) (builtins.readDir dir));
) (builtins.readDir dir)
);
/*
Transform a directory tree containing package files suitable for
@ -263,49 +262,44 @@ in
let
# Determine if a directory entry from `readDir` indicates a package or
# directory of packages.
directoryEntryIsPackage = basename: type:
type == "directory" || hasSuffix ".nix" basename;
directoryEntryIsPackage = basename: type: type == "directory" || hasSuffix ".nix" basename;
# List directory entries that indicate packages in the given `path`.
packageDirectoryEntries = path:
filterAttrs directoryEntryIsPackage (readDir path);
packageDirectoryEntries = path: filterAttrs directoryEntryIsPackage (readDir path);
# Transform a directory entry (a `basename` and `type` pair) into a
# package.
directoryEntryToAttrPair = subdirectory: basename: type:
directoryEntryToAttrPair =
subdirectory: basename: type:
let
path = subdirectory + "/${basename}";
in
if type == "regular"
then
if type == "regular" then
{
name = removeSuffix ".nix" basename;
value = callPackage path { };
}
else
if type == "directory"
then
else if type == "directory" then
{
name = basename;
value = packagesFromDirectory path;
}
else
throw
''
throw ''
lib.filesystem.packagesFromDirectoryRecursive: Unsupported file type ${type} at path ${toString subdirectory}
'';
# Transform a directory into a package (if there's a `package.nix`) or
# set of packages (otherwise).
packagesFromDirectory = path:
packagesFromDirectory =
path:
let
defaultPackagePath = path + "/package.nix";
in
if pathExists defaultPackagePath
then callPackage defaultPackagePath { }
else mapAttrs'
(directoryEntryToAttrPair path)
(packageDirectoryEntries path);
if pathExists defaultPackagePath then
callPackage defaultPackagePath { }
else
mapAttrs' (directoryEntryToAttrPair path) (packageDirectoryEntries path);
in
packagesFromDirectory directory;
}

View file

@ -72,7 +72,12 @@ rec {
fix (self: [ 1 2 (elemAt self 0 + elemAt self 1) ])
=> [ 1 2 3 ]
*/
fix = f: let x = f x; in x;
fix =
f:
let
x = f x;
in
x;
/*
A variant of `fix` that records the original recursive attribute set in the
@ -81,7 +86,14 @@ rec {
This is useful in combination with the `extends` function to
implement deep overriding.
*/
fix' = f: let x = f x // { __unfix__ = f; }; in x;
fix' =
f:
let
x = f x // {
__unfix__ = f;
};
in
x;
/*
Return the fixpoint that `f` converges to when called iteratively, starting
@ -94,13 +106,12 @@ rec {
Type: (a -> a) -> a -> a
*/
converge = f: x:
converge =
f: x:
let
x' = f x;
in
if x' == x
then x
else converge f x';
if x' == x then x else converge f x';
/*
Extend a function using an overlay.
@ -109,7 +120,6 @@ rec {
A fixed-point function is a function which is intended to be evaluated by passing the result of itself as the argument.
This is possible due to Nix's lazy evaluation.
A fixed-point function returning an attribute set has the form
```nix
@ -259,9 +269,11 @@ rec {
*/
composeExtensions =
f: g: final: prev:
let fApplied = f final prev;
let
fApplied = f final prev;
prev' = prev // fApplied;
in fApplied // g final prev';
in
fApplied // g final prev';
/*
Compose several extending functions of the type expected by 'extends' into
@ -273,8 +285,7 @@ rec {
^final ^prev ^overrides ^final ^prev ^overrides
```
*/
composeManyExtensions =
lib.foldr (x: y: composeExtensions x y) (final: prev: {});
composeManyExtensions = lib.foldr (x: y: composeExtensions x y) (final: prev: { });
/*
Create an overridable, recursive attribute set. For example:
@ -302,8 +313,13 @@ rec {
Same as `makeExtensible` but the name of the extending attribute is
customized.
*/
makeExtensibleWithCustomName = extenderName: rattrs:
fix' (self: (rattrs self) // {
makeExtensibleWithCustomName =
extenderName: rattrs:
fix' (
self:
(rattrs self)
// {
${extenderName} = f: makeExtensibleWithCustomName extenderName (extends f rattrs);
});
}
);
}

View file

@ -13,8 +13,9 @@ finalLib: prevLib: # lib overlay
{
trivial = prevLib.trivial // {
versionSuffix =
".${finalLib.substring 0 8 (self.lastModifiedDate or "19700101")}.${self.shortRev or "dirty"}";
versionSuffix = ".${
finalLib.substring 0 8 (self.lastModifiedDate or "19700101")
}.${self.shortRev or "dirty"}";
revisionWithDefault = default: self.rev or default;
};
}

View file

@ -1,10 +1,12 @@
{
description = "Library of low-level helper functions for nix expressions.";
outputs = { self }:
outputs =
{ self }:
let
lib0 = import ./.;
in {
in
{
lib = lib0.extend (import ./flake-version-info.nix self);
};
}

View file

@ -1,17 +1,18 @@
/* Functions that generate widespread file
* formats from nix data structures.
*
* They all follow a similar interface:
* generator { config-attrs } data
*
* `config-attrs` are holes in the generators
* with sensible default implementations that
* can be overwritten. The default implementations
* are mostly generators themselves, called with
* their respective default values; they can be reused.
*
* Tests can be found in ./tests/misc.nix
* Documentation in the manual, #sec-generators
/*
Functions that generate widespread file
formats from nix data structures.
They all follow a similar interface:
generator { config-attrs } data
`config-attrs` are holes in the generators
with sensible default implementations that
can be overwritten. The default implementations
are mostly generators themselves, called with
their respective default values; they can be reused.
Tests can be found in ./tests/misc.nix
Documentation in the manual, #sec-generators
*/
{ lib }:
@ -69,212 +70,277 @@ let
## -- HELPER FUNCTIONS & DEFAULTS --
/* Convert a value to a sensible default string representation.
* The builtin `toString` function has some strange defaults,
* suitable for bash scripts but not much else.
/*
Convert a value to a sensible default string representation.
The builtin `toString` function has some strange defaults,
suitable for bash scripts but not much else.
*/
mkValueStringDefault = {}: v:
let err = t: v: abort
("generators.mkValueStringDefault: " +
"${t} not supported: ${toPretty {} v}");
in if isInt v then toString v
mkValueStringDefault =
{ }:
v:
let
err = t: v: abort ("generators.mkValueStringDefault: " + "${t} not supported: ${toPretty { } v}");
in
if isInt v then
toString v
# convert derivations to store paths
else if isDerivation v then toString v
else if isDerivation v then
toString v
# we default to not quoting strings
else if isString v then v
else if isString v then
v
# isString returns "1", which is not a good default
else if true == v then "true"
else if true == v then
"true"
# here it returns to "", which is even less of a good default
else if false == v then "false"
else if null == v then "null"
else if false == v then
"false"
else if null == v then
"null"
# if you have lists you probably want to replace this
else if isList v then err "lists" v
else if isList v then
err "lists" v
# same as for lists, might want to replace
else if isAttrs v then err "attrsets" v
else if isAttrs v then
err "attrsets" v
# functions cant be printed of course
else if isFunction v then err "functions" v
else if isFunction v then
err "functions" v
# Floats currently can't be converted to precise strings,
# condition warning on nix version once this isn't a problem anymore
# See https://github.com/NixOS/nix/pull/3480
else if isFloat v then floatToString v
else err "this value is" (toString v);
else if isFloat v then
floatToString v
else
err "this value is" (toString v);
/*
Generate a line of key k and value v, separated by
character sep. If sep appears in k, it is escaped.
Helper for synaxes with different separators.
/* Generate a line of key k and value v, separated by
* character sep. If sep appears in k, it is escaped.
* Helper for synaxes with different separators.
*
* mkValueString specifies how values should be formatted.
*
* mkKeyValueDefault {} ":" "f:oo" "bar"
* > "f\:oo:bar"
mkValueString specifies how values should be formatted.
mkKeyValueDefault {} ":" "f:oo" "bar"
> "f\:oo:bar"
*/
mkKeyValueDefault = {
mkValueString ? mkValueStringDefault {}
}: sep: k: v:
mkKeyValueDefault =
{
mkValueString ? mkValueStringDefault { },
}:
sep: k: v:
"${escape [ sep ] k}${sep}${mkValueString v}";
## -- FILE FORMAT GENERATORS --
/*
Generate a key-value-style config file from an attrset.
/* Generate a key-value-style config file from an attrset.
*
* mkKeyValue is the same as in toINI.
mkKeyValue is the same as in toINI.
*/
toKeyValue = {
toKeyValue =
{
mkKeyValue ? mkKeyValueDefault { } "=",
listsAsDuplicateKeys ? false,
indent ? ""
indent ? "",
}:
let mkLine = k: v: indent + mkKeyValue k v + "\n";
mkLines = if listsAsDuplicateKeys
then k: v: map (mkLine k) (if isList v then v else [v])
else k: v: [ (mkLine k v) ];
in attrs: concatStrings (concatLists (mapAttrsToList mkLines attrs));
let
mkLine = k: v: indent + mkKeyValue k v + "\n";
mkLines =
if listsAsDuplicateKeys then
k: v: map (mkLine k) (if isList v then v else [ v ])
else
k: v: [ (mkLine k v) ];
in
attrs: concatStrings (concatLists (mapAttrsToList mkLines attrs));
/*
Generate an INI-style config file from an
attrset of sections to an attrset of key-value pairs.
/* Generate an INI-style config file from an
* attrset of sections to an attrset of key-value pairs.
*
* generators.toINI {} {
* foo = { hi = "${pkgs.hello}"; ciao = "bar"; };
* baz = { "also, integers" = 42; };
* }
*
*> [baz]
*> also, integers=42
*>
*> [foo]
*> ciao=bar
*> hi=/nix/store/y93qql1p5ggfnaqjjqhxcw0vqw95rlz0-hello-2.10
*
* The mk* configuration attributes can generically change
* the way sections and key-value strings are generated.
*
* For more examples see the test cases in ./tests/misc.nix.
generators.toINI {} {
foo = { hi = "${pkgs.hello}"; ciao = "bar"; };
baz = { "also, integers" = 42; };
}
> [baz]
> also, integers=42
>
> [foo]
> ciao=bar
> hi=/nix/store/y93qql1p5ggfnaqjjqhxcw0vqw95rlz0-hello-2.10
The mk* configuration attributes can generically change
the way sections and key-value strings are generated.
For more examples see the test cases in ./tests/misc.nix.
*/
toINI = {
toINI =
{
# apply transformations (e.g. escapes) to section names
mkSectionName ? (name: escape [ "[" "]" ] name),
mkSectionName ? (
name:
escape [
"["
"]"
] name
),
# format a setting line from key and value
mkKeyValue ? mkKeyValueDefault { } "=",
# allow lists as values for duplicate keys
listsAsDuplicateKeys ? false
}: attrsOfAttrs:
listsAsDuplicateKeys ? false,
}:
attrsOfAttrs:
let
# map function to string for each key val
mapAttrsToStringsSep = sep: mapFn: attrs:
concatStringsSep sep
(mapAttrsToList mapFn attrs);
mkSection = sectName: sectValues: ''
mapAttrsToStringsSep =
sep: mapFn: attrs:
concatStringsSep sep (mapAttrsToList mapFn attrs);
mkSection =
sectName: sectValues:
''
[${mkSectionName sectName}]
'' + toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } sectValues;
''
+ toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } sectValues;
in
# map input to ini sections
mapAttrsToStringsSep "\n" mkSection attrsOfAttrs;
/* Generate an INI-style config file from an attrset
* specifying the global section (no header), and an
* attrset of sections to an attrset of key-value pairs.
*
* generators.toINIWithGlobalSection {} {
* globalSection = {
* someGlobalKey = "hi";
* };
* sections = {
* foo = { hi = "${pkgs.hello}"; ciao = "bar"; };
* baz = { "also, integers" = 42; };
* }
*
*> someGlobalKey=hi
*>
*> [baz]
*> also, integers=42
*>
*> [foo]
*> ciao=bar
*> hi=/nix/store/y93qql1p5ggfnaqjjqhxcw0vqw95rlz0-hello-2.10
*
* The mk* configuration attributes can generically change
* the way sections and key-value strings are generated.
*
* For more examples see the test cases in ./tests/misc.nix.
*
* If you dont need a global section, you can also use
* `generators.toINI` directly, which only takes
* the part in `sections`.
/*
Generate an INI-style config file from an attrset
specifying the global section (no header), and an
attrset of sections to an attrset of key-value pairs.
generators.toINIWithGlobalSection {} {
globalSection = {
someGlobalKey = "hi";
};
sections = {
foo = { hi = "${pkgs.hello}"; ciao = "bar"; };
baz = { "also, integers" = 42; };
}
> someGlobalKey=hi
>
> [baz]
> also, integers=42
>
> [foo]
> ciao=bar
> hi=/nix/store/y93qql1p5ggfnaqjjqhxcw0vqw95rlz0-hello-2.10
The mk* configuration attributes can generically change
the way sections and key-value strings are generated.
For more examples see the test cases in ./tests/misc.nix.
If you dont need a global section, you can also use
`generators.toINI` directly, which only takes
the part in `sections`.
*/
toINIWithGlobalSection = {
toINIWithGlobalSection =
{
# apply transformations (e.g. escapes) to section names
mkSectionName ? (name: escape [ "[" "]" ] name),
mkSectionName ? (
name:
escape [
"["
"]"
] name
),
# format a setting line from key and value
mkKeyValue ? mkKeyValueDefault { } "=",
# allow lists as values for duplicate keys
listsAsDuplicateKeys ? false
}: { globalSection, sections ? {} }:
( if globalSection == {}
then ""
else (toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } globalSection)
+ "\n")
listsAsDuplicateKeys ? false,
}:
{
globalSection,
sections ? { },
}:
(
if globalSection == { } then
""
else
(toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } globalSection) + "\n"
)
+ (toINI { inherit mkSectionName mkKeyValue listsAsDuplicateKeys; } sections);
/* Generate a git-config file from an attrset.
*
* It has two major differences from the regular INI format:
*
* 1. values are indented with tabs
* 2. sections can have sub-sections
*
* generators.toGitINI {
* url."ssh://git@github.com/".insteadOf = "https://github.com";
* user.name = "edolstra";
* }
*
*> [url "ssh://git@github.com/"]
*> insteadOf = "https://github.com"
*>
*> [user]
*> name = "edolstra"
/*
Generate a git-config file from an attrset.
It has two major differences from the regular INI format:
1. values are indented with tabs
2. sections can have sub-sections
generators.toGitINI {
url."ssh://git@github.com/".insteadOf = "https://github.com";
user.name = "edolstra";
}
> [url "ssh://git@github.com/"]
> insteadOf = "https://github.com"
>
> [user]
> name = "edolstra"
*/
toGitINI = attrs:
toGitINI =
attrs:
let
mkSectionName = name:
mkSectionName =
name:
let
containsQuote = hasInfix ''"'' name;
sections = splitString "." name;
section = head sections;
subsections = tail sections;
subsection = concatStringsSep "." subsections;
in if containsQuote || subsections == [ ] then
name
else
''${section} "${subsection}"'';
in
if containsQuote || subsections == [ ] then name else ''${section} "${subsection}"'';
mkValueString = v:
mkValueString =
v:
let
escapedV = ''
"${
replaceStrings [ "\n" " " ''"'' "\\" ] [ "\\n" "\\t" ''\"'' "\\\\" ] v
escapedV = ''"${
replaceStrings
[
"\n"
" "
''"''
"\\"
]
[
"\\n"
"\\t"
''\"''
"\\\\"
]
v
}"'';
in mkValueStringDefault { } (if isString v then escapedV else v);
in
mkValueStringDefault { } (if isString v then escapedV else v);
# generation for multiple ini values
mkKeyValue = k: v:
let mkKeyValue = mkKeyValueDefault { inherit mkValueString; } " = " k;
in concatStringsSep "\n" (map (kv: "\t" + mkKeyValue kv) (toList v));
mkKeyValue =
k: v:
let
mkKeyValue = mkKeyValueDefault { inherit mkValueString; } " = " k;
in
concatStringsSep "\n" (map (kv: "\t" + mkKeyValue kv) (toList v));
# converts { a.b.c = 5; } to { "a.b".c = 5; } for toINI
gitFlattenAttrs = let
recurse = path: value:
gitFlattenAttrs =
let
recurse =
path: value:
if isAttrs value && !isDerivation value then
mapAttrsToList (name: value: recurse ([ name ] ++ path) value) value
else if length path > 1 then {
${concatStringsSep "." (reverseList (tail path))}.${head path} = value;
} else {
${head path} = value;
};
in attrs: foldl recursiveUpdate { } (flatten (recurse [ ] attrs));
else if length path > 1 then
{ ${concatStringsSep "." (reverseList (tail path))}.${head path} = value; }
else
{ ${head path} = value; };
in
attrs: foldl recursiveUpdate { } (flatten (recurse [ ] attrs));
toINI_ = toINI { inherit mkKeyValue mkSectionName; };
in
@ -290,10 +356,10 @@ let
withRecursion =
{
/* If this option is not null, the given value will stop evaluating at a certain depth */
depthLimit
/* If this option is true, an error will be thrown, if a certain given depth is exceeded */
, throwOnDepthLimit ? true
# If this option is not null, the given value will stop evaluating at a certain depth
depthLimit,
# If this option is true, an error will be thrown, if a certain given depth is exceeded
throwOnDepthLimit ? true,
}:
assert isInt depthLimit;
let
@ -303,111 +369,168 @@ let
"__toString"
"__pretty"
];
stepIntoAttr = evalNext: name:
if elem name specialAttrs
then id
else evalNext;
transform = depth:
stepIntoAttr = evalNext: name: if elem name specialAttrs then id else evalNext;
transform =
depth:
if depthLimit != null && depth > depthLimit then
if throwOnDepthLimit
then throw "Exceeded maximum eval-depth limit of ${toString depthLimit} while trying to evaluate with `generators.withRecursion'!"
else const "<unevaluated>"
else id;
mapAny = depth: v:
if throwOnDepthLimit then
throw "Exceeded maximum eval-depth limit of ${toString depthLimit} while trying to evaluate with `generators.withRecursion'!"
else
const "<unevaluated>"
else
id;
mapAny =
depth: v:
let
evalNext = x: mapAny (depth + 1) (transform (depth + 1) x);
in
if isAttrs v then mapAttrs (stepIntoAttr evalNext) v
else if isList v then map evalNext v
else transform (depth + 1) v;
if isAttrs v then
mapAttrs (stepIntoAttr evalNext) v
else if isList v then
map evalNext v
else
transform (depth + 1) v;
in
mapAny 0;
/* Pretty print a value, akin to `builtins.trace`.
* Should probably be a builtin as well.
* The pretty-printed string should be suitable for rendering default values
* in the NixOS manual. In particular, it should be as close to a valid Nix expression
* as possible.
/*
Pretty print a value, akin to `builtins.trace`.
Should probably be a builtin as well.
The pretty-printed string should be suitable for rendering default values
in the NixOS manual. In particular, it should be as close to a valid Nix expression
as possible.
*/
toPretty = {
/* If this option is true, attrsets like { __pretty = fn; val = ; }
toPretty =
{
/*
If this option is true, attrsets like { __pretty = fn; val = ; }
will use fn to convert val to a pretty printed representation.
(This means fn is type Val -> String.) */
(This means fn is type Val -> String.)
*/
allowPrettyValues ? false,
/* If this option is true, the output is indented with newlines for attribute sets and lists */
# If this option is true, the output is indented with newlines for attribute sets and lists
multiline ? true,
/* Initial indentation level */
indent ? ""
# Initial indentation level
indent ? "",
}:
let
go = indent: v:
let introSpace = if multiline then "\n${indent} " else " ";
go =
indent: v:
let
introSpace = if multiline then "\n${indent} " else " ";
outroSpace = if multiline then "\n${indent}" else " ";
in if isInt v then toString v
in
if isInt v then
toString v
# toString loses precision on floats, so we use toJSON instead. This isn't perfect
# as the resulting string may not parse back as a float (e.g. 42, 1e-06), but for
# pretty-printing purposes this is acceptable.
else if isFloat v then builtins.toJSON v
else if isFloat v then
builtins.toJSON v
else if isString v then
let
lines = filter (v: !isList v) (split "\n" v);
escapeSingleline = escape [ "\\" "\"" "\${" ];
escapeMultiline = replaceStrings [ "\${" "''" ] [ "''\${" "'''" ];
escapeSingleline = escape [
"\\"
"\""
"\${"
];
escapeMultiline =
replaceStrings
[
"\${"
"''"
]
[
"''\${"
"'''"
];
singlelineResult = "\"" + concatStringsSep "\\n" (map escapeSingleline lines) + "\"";
multilineResult = let
multilineResult =
let
escapedLines = map escapeMultiline lines;
# The last line gets a special treatment: if it's empty, '' is on its own line at the "outer"
# indentation level. Otherwise, '' is appended to the last line.
lastLine = last escapedLines;
in "''" + introSpace + concatStringsSep introSpace (init escapedLines)
+ (if lastLine == "" then outroSpace else introSpace + lastLine) + "''";
in
"''"
+ introSpace
+ concatStringsSep introSpace (init escapedLines)
+ (if lastLine == "" then outroSpace else introSpace + lastLine)
+ "''";
in
if multiline && length lines > 1 then multilineResult else singlelineResult
else if true == v then "true"
else if false == v then "false"
else if null == v then "null"
else if isPath v then toString v
else if true == v then
"true"
else if false == v then
"false"
else if null == v then
"null"
else if isPath v then
toString v
else if isList v then
if v == [] then "[ ]"
else "[" + introSpace
+ concatMapStringsSep introSpace (go (indent + " ")) v
+ outroSpace + "]"
if v == [ ] then
"[ ]"
else
"[" + introSpace + concatMapStringsSep introSpace (go (indent + " ")) v + outroSpace + "]"
else if isFunction v then
let fna = functionArgs v;
showFnas = concatStringsSep ", " (mapAttrsToList
(name: hasDefVal: if hasDefVal then name + "?" else name)
fna);
in if fna == {} then "<function>"
else "<function, args: {${showFnas}}>"
let
fna = functionArgs v;
showFnas = concatStringsSep ", " (
mapAttrsToList (name: hasDefVal: if hasDefVal then name + "?" else name) fna
);
in
if fna == { } then "<function>" else "<function, args: {${showFnas}}>"
else if isAttrs v then
# apply pretty values if allowed
if allowPrettyValues && v ? __pretty && v ? val
then v.__pretty v.val
else if v == {} then "{ }"
if allowPrettyValues && v ? __pretty && v ? val then
v.__pretty v.val
else if v == { } then
"{ }"
else if v ? type && v.type == "derivation" then
"<derivation ${v.name or "???"}>"
else "{" + introSpace
+ concatStringsSep introSpace (mapAttrsToList
(name: value:
else
"{"
+ introSpace
+ concatStringsSep introSpace (
mapAttrsToList (
name: value:
"${escapeNixIdentifier name} = ${
addErrorContext "while evaluating an attribute `${name}`"
(go (indent + " ") value)
};") v)
+ outroSpace + "}"
else abort "generators.toPretty: should never happen (v = ${v})";
in go indent;
addErrorContext "while evaluating an attribute `${name}`" (go (indent + " ") value)
};"
) v
)
+ outroSpace
+ "}"
else
abort "generators.toPretty: should never happen (v = ${v})";
in
go indent;
# PLIST handling
toPlist = {}: v: let
expr = ind: x:
if x == null then "" else
if isBool x then bool ind x else
if isInt x then int ind x else
if isString x then str ind x else
if isList x then list ind x else
if isAttrs x then attrs ind x else
if isPath x then str ind (toString x) else
if isFloat x then float ind x else
toPlist =
{ }:
v:
let
expr =
ind: x:
if x == null then
""
else if isBool x then
bool ind x
else if isInt x then
int ind x
else if isString x then
str ind x
else if isList x then
list ind x
else if isAttrs x then
attrs ind x
else if isPath x then
str ind (toString x)
else if isFloat x then
float ind x
else
abort "generators.toPlist: should never happen (v = ${v})";
literal = ind: x: ind + x;
@ -422,42 +545,60 @@ let
item = ind: concatMapStringsSep "\n" (indent ind);
list = ind: x: concatStringsSep "\n" [
list =
ind: x:
concatStringsSep "\n" [
(literal ind "<array>")
(item ind x)
(literal ind "</array>")
];
attrs = ind: x: concatStringsSep "\n" [
attrs =
ind: x:
concatStringsSep "\n" [
(literal ind "<dict>")
(attr ind x)
(literal ind "</dict>")
];
attr = let attrFilter = name: value: name != "_module" && value != null;
in ind: x: concatStringsSep "\n" (flatten (mapAttrsToList
(name: value: optionals (attrFilter name value) [
attr =
let
attrFilter = name: value: name != "_module" && value != null;
in
ind: x:
concatStringsSep "\n" (
flatten (
mapAttrsToList (
name: value:
optionals (attrFilter name value) [
(key "\t${ind}" name)
(expr "\t${ind}" value)
]) x));
]
) x
)
);
in ''<?xml version="1.0" encoding="UTF-8"?>
in
''
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
${expr "" v}
</plist>'';
/* Translate a simple Nix expression to Dhall notation.
* Note that integers are translated to Integer and never
* the Natural type.
/*
Translate a simple Nix expression to Dhall notation.
Note that integers are translated to Integer and never
the Natural type.
*/
toDhall = { }@args: v:
let concatItems = concatStringsSep ", ";
in if isAttrs v then
"{ ${
concatItems (mapAttrsToList
(key: value: "${key} = ${toDhall args value}") v)
} }"
toDhall =
{ }@args:
v:
let
concatItems = concatStringsSep ", ";
in
if isAttrs v then
"{ ${concatItems (mapAttrsToList (key: value: "${key} = ${toDhall args value}") v)} }"
else if isList v then
"[ ${concatItems (map (toDhall args) v)} ]"
else if isInt v then
@ -505,14 +646,16 @@ ${expr "" v}
Type:
toLua :: AttrSet -> Any -> String
*/
toLua = {
/* If this option is true, the output is indented with newlines for attribute sets and lists */
toLua =
{
# If this option is true, the output is indented with newlines for attribute sets and lists
multiline ? true,
/* Initial indentation level */
# Initial indentation level
indent ? "",
/* Interpret as variable bindings */
# Interpret as variable bindings
asBindings ? false,
}@args: v:
}@args:
v:
let
innerIndent = "${indent} ";
introSpace = if multiline then "\n${innerIndent}" else " ";
@ -522,13 +665,16 @@ ${expr "" v}
asBindings = false;
};
concatItems = concatStringsSep ",${introSpace}";
isLuaInline = { _type ? null, ... }: _type == "lua-inline";
isLuaInline =
{
_type ? null,
...
}:
_type == "lua-inline";
generatedBindings =
assert assertMsg (badVarNames == [ ]) "Bad Lua var names: ${toPretty { } badVarNames}";
concatStrings (
mapAttrsToList (key: value: "${indent}${key} = ${toLua innerArgs value}\n") v
);
concatStrings (mapAttrsToList (key: value: "${indent}${key} = ${toLua innerArgs value}\n") v);
# https://en.wikibooks.org/wiki/Lua_Programming/variable#Variable_names
matchVarName = match "[[:alpha:]_][[:alnum:]_]*(\\.[[:alpha:]_][[:alnum:]_]*)*";
@ -541,8 +687,12 @@ ${expr "" v}
else if isInt v || isFloat v || isString v || isBool v then
toJSON v
else if isList v then
(if v == [ ] then "{}" else
"{${introSpace}${concatItems (map (value: "${toLua innerArgs value}") v)}${outroSpace}}")
(
if v == [ ] then
"{}"
else
"{${introSpace}${concatItems (map (value: "${toLua innerArgs value}") v)}${outroSpace}}"
)
else if isAttrs v then
(
if isLuaInline v then
@ -552,9 +702,9 @@ ${expr "" v}
else if isDerivation v then
''"${toString v}"''
else
"{${introSpace}${concatItems (
mapAttrsToList (key: value: "[${toJSON key}] = ${toLua innerArgs value}") v
)}${outroSpace}}"
"{${introSpace}${
concatItems (mapAttrsToList (key: value: "[${toJSON key}] = ${toLua innerArgs value}") v)
}${outroSpace}}"
)
else
abort "generators.toLua: type ${typeOf v} is unsupported";
@ -565,7 +715,10 @@ ${expr "" v}
Type:
mkLuaInline :: String -> AttrSet
*/
mkLuaInline = expr: { _type = "lua-inline"; inherit expr; };
mkLuaInline = expr: {
_type = "lua-inline";
inherit expr;
};
in
@ -588,12 +741,14 @@ in
withRecursion
;
/* Generates JSON from an arbitrary (non-function) value.
/*
Generates JSON from an arbitrary (non-function) value.
* For more information see the documentation of the builtin.
*/
toJSON = { }: toJSON;
/* YAML has been a strict superset of JSON since 1.2, so we
/*
YAML has been a strict superset of JSON since 1.2, so we
* use toJSON. Before it only had a few differences referring
* to implicit typing rules, so it should work with older
* parsers as well.

View file

@ -14,7 +14,12 @@
let
inherit (lib)
concatMapStringsSep concatStrings escape head replaceStrings;
concatMapStringsSep
concatStrings
escape
head
replaceStrings
;
mkPrimitive = t: v: {
_type = "gvariant";
@ -41,7 +46,8 @@ let
variant = "v";
};
/* Check if a value is a GVariant value
/*
Check if a value is a GVariant value
Type:
isGVariant :: Any -> Bool
@ -53,13 +59,15 @@ rec {
inherit type isGVariant;
/* Returns the GVariant value that most closely matches the given Nix value.
/*
Returns the GVariant value that most closely matches the given Nix value.
If no GVariant value can be found unambiguously then error is thrown.
Type:
mkValue :: Any -> gvariant
*/
mkValue = v:
mkValue =
v:
if builtins.isBool v then
mkBoolean v
else if builtins.isFloat v then
@ -73,7 +81,8 @@ rec {
else
throw "The GVariant type of ${v} can't be inferred.";
/* Returns the GVariant array from the given type of the elements and a Nix list.
/*
Returns the GVariant array from the given type of the elements and a Nix list.
Type:
mkArray :: [Any] -> gvariant
@ -82,19 +91,21 @@ rec {
# Creating a string array
lib.gvariant.mkArray [ "a" "b" "c" ]
*/
mkArray = elems:
mkArray =
elems:
let
vs = map mkValue (lib.throwIf (elems == [ ]) "Please create empty array with mkEmptyArray." elems);
elemType = lib.throwIfNot (lib.all (t: (head vs).type == t) (map (v: v.type) vs))
"Elements in a list should have same type."
(head vs).type;
elemType = lib.throwIfNot (lib.all (t: (head vs).type == t) (
map (v: v.type) vs
)) "Elements in a list should have same type." (head vs).type;
in
mkPrimitive (type.arrayOf elemType) vs // {
__toString = self:
"@${self.type} [${concatMapStringsSep "," toString self.value}]";
mkPrimitive (type.arrayOf elemType) vs
// {
__toString = self: "@${self.type} [${concatMapStringsSep "," toString self.value}]";
};
/* Returns the GVariant array from the given empty Nix list.
/*
Returns the GVariant array from the given empty Nix list.
Type:
mkEmptyArray :: gvariant.type -> gvariant
@ -103,12 +114,11 @@ rec {
# Creating an empty string array
lib.gvariant.mkEmptyArray (lib.gvariant.type.string)
*/
mkEmptyArray = elemType: mkPrimitive (type.arrayOf elemType) [ ] // {
__toString = self: "@${self.type} []";
};
mkEmptyArray =
elemType: mkPrimitive (type.arrayOf elemType) [ ] // { __toString = self: "@${self.type} []"; };
/* Returns the GVariant variant from the given Nix value. Variants are containers
/*
Returns the GVariant variant from the given Nix value. Variants are containers
of different GVariant type.
Type:
@ -120,13 +130,15 @@ rec {
(lib.gvariant.mkVariant (lib.gvariant.mkInt32 1))
]
*/
mkVariant = elem:
let gvarElem = mkValue elem;
in mkPrimitive type.variant gvarElem // {
__toString = self: "<${toString self.value}>";
};
mkVariant =
elem:
let
gvarElem = mkValue elem;
in
mkPrimitive type.variant gvarElem // { __toString = self: "<${toString self.value}>"; };
/* Returns the GVariant dictionary entry from the given key and value.
/*
Returns the GVariant dictionary entry from the given key and value.
Type:
mkDictionaryEntry :: String -> Any -> gvariant
@ -149,143 +161,162 @@ rec {
value' = mkValue value;
dictionaryType = type.dictionaryEntryOf name'.type value'.type;
in
mkPrimitive dictionaryType { inherit name value; } // {
mkPrimitive dictionaryType { inherit name value; }
// {
__toString = self: "@${self.type} {${name'},${value'}}";
};
/* Returns the GVariant maybe from the given element type.
/*
Returns the GVariant maybe from the given element type.
Type:
mkMaybe :: gvariant.type -> Any -> gvariant
*/
mkMaybe = elemType: elem:
mkPrimitive (type.maybeOf elemType) elem // {
__toString = self:
if self.value == null then
"@${self.type} nothing"
else
"just ${toString self.value}";
mkMaybe =
elemType: elem:
mkPrimitive (type.maybeOf elemType) elem
// {
__toString =
self: if self.value == null then "@${self.type} nothing" else "just ${toString self.value}";
};
/* Returns the GVariant nothing from the given element type.
/*
Returns the GVariant nothing from the given element type.
Type:
mkNothing :: gvariant.type -> gvariant
*/
mkNothing = elemType: mkMaybe elemType null;
/* Returns the GVariant just from the given Nix value.
/*
Returns the GVariant just from the given Nix value.
Type:
mkJust :: Any -> gvariant
*/
mkJust = elem: let gvarElem = mkValue elem; in mkMaybe gvarElem.type gvarElem;
mkJust =
elem:
let
gvarElem = mkValue elem;
in
mkMaybe gvarElem.type gvarElem;
/* Returns the GVariant tuple from the given Nix list.
/*
Returns the GVariant tuple from the given Nix list.
Type:
mkTuple :: [Any] -> gvariant
*/
mkTuple = elems:
mkTuple =
elems:
let
gvarElems = map mkValue elems;
tupleType = type.tupleOf (map (e: e.type) gvarElems);
in
mkPrimitive tupleType gvarElems // {
__toString = self:
"@${self.type} (${concatMapStringsSep "," toString self.value})";
mkPrimitive tupleType gvarElems
// {
__toString = self: "@${self.type} (${concatMapStringsSep "," toString self.value})";
};
/* Returns the GVariant boolean from the given Nix bool value.
/*
Returns the GVariant boolean from the given Nix bool value.
Type:
mkBoolean :: Bool -> gvariant
*/
mkBoolean = v:
mkPrimitive type.boolean v // {
__toString = self: if self.value then "true" else "false";
};
mkBoolean =
v: mkPrimitive type.boolean v // { __toString = self: if self.value then "true" else "false"; };
/* Returns the GVariant string from the given Nix string value.
/*
Returns the GVariant string from the given Nix string value.
Type:
mkString :: String -> gvariant
*/
mkString = v:
let sanitize = s: replaceStrings [ "\n" ] [ "\\n" ] (escape [ "'" "\\" ] s);
in mkPrimitive type.string v // {
__toString = self: "'${sanitize self.value}'";
};
mkString =
v:
let
sanitize =
s:
replaceStrings [ "\n" ] [ "\\n" ] (
escape [
"'"
"\\"
] s
);
in
mkPrimitive type.string v // { __toString = self: "'${sanitize self.value}'"; };
/* Returns the GVariant object path from the given Nix string value.
/*
Returns the GVariant object path from the given Nix string value.
Type:
mkObjectpath :: String -> gvariant
*/
mkObjectpath = v:
mkPrimitive type.string v // {
__toString = self: "objectpath '${escape [ "'" ] self.value}'";
};
mkObjectpath =
v: mkPrimitive type.string v // { __toString = self: "objectpath '${escape [ "'" ] self.value}'"; };
/* Returns the GVariant uchar from the given Nix int value.
/*
Returns the GVariant uchar from the given Nix int value.
Type:
mkUchar :: Int -> gvariant
*/
mkUchar = mkPrimitive type.uchar;
/* Returns the GVariant int16 from the given Nix int value.
/*
Returns the GVariant int16 from the given Nix int value.
Type:
mkInt16 :: Int -> gvariant
*/
mkInt16 = mkPrimitive type.int16;
/* Returns the GVariant uint16 from the given Nix int value.
/*
Returns the GVariant uint16 from the given Nix int value.
Type:
mkUint16 :: Int -> gvariant
*/
mkUint16 = mkPrimitive type.uint16;
/* Returns the GVariant int32 from the given Nix int value.
/*
Returns the GVariant int32 from the given Nix int value.
Type:
mkInt32 :: Int -> gvariant
*/
mkInt32 = v:
mkPrimitive type.int32 v // {
__toString = self: toString self.value;
};
mkInt32 = v: mkPrimitive type.int32 v // { __toString = self: toString self.value; };
/* Returns the GVariant uint32 from the given Nix int value.
/*
Returns the GVariant uint32 from the given Nix int value.
Type:
mkUint32 :: Int -> gvariant
*/
mkUint32 = mkPrimitive type.uint32;
/* Returns the GVariant int64 from the given Nix int value.
/*
Returns the GVariant int64 from the given Nix int value.
Type:
mkInt64 :: Int -> gvariant
*/
mkInt64 = mkPrimitive type.int64;
/* Returns the GVariant uint64 from the given Nix int value.
/*
Returns the GVariant uint64 from the given Nix int value.
Type:
mkUint64 :: Int -> gvariant
*/
mkUint64 = mkPrimitive type.uint64;
/* Returns the GVariant double from the given Nix float value.
/*
Returns the GVariant double from the given Nix float value.
Type:
mkDouble :: Float -> gvariant
*/
mkDouble = v:
mkPrimitive type.double v // {
__toString = self: toString self.value;
};
mkDouble = v: mkPrimitive type.double v // { __toString = self: toString self.value; };
}

View file

@ -5,17 +5,29 @@ let
in
{
# Keeping these around in case we decide to change this horrible implementation :)
option = x:
x // { optional = true; };
yes = { tristate = "y"; optional = false; };
no = { tristate = "n"; optional = false; };
module = { tristate = "m"; optional = false; };
unset = { tristate = null; optional = false; };
freeform = x: { freeform = x; optional = false; };
option = x: x // { optional = true; };
yes = {
tristate = "y";
optional = false;
};
no = {
tristate = "n";
optional = false;
};
module = {
tristate = "m";
optional = false;
};
unset = {
tristate = null;
optional = false;
};
freeform = x: {
freeform = x;
optional = false;
};
# Common patterns/legacy used in common-config/hardened/config.nix
whenHelpers = version: {

View file

@ -1,28 +1,41 @@
{ lib }:
lib.mapAttrs (lname: lset: let
lib.mapAttrs
(
lname: lset:
let
defaultLicense = {
shortName = lname;
free = true; # Most of our licenses are Free, explicitly declare unfree additions as such!
deprecated = false;
};
mkLicense = licenseDeclaration: let
mkLicense =
licenseDeclaration:
let
applyDefaults = license: defaultLicense // license;
applySpdx = license:
if license ? spdxId
then license // { url = "https://spdx.org/licenses/${license.spdxId}.html"; }
else license;
applySpdx =
license:
if license ? spdxId then
license // { url = "https://spdx.org/licenses/${license.spdxId}.html"; }
else
license;
applyRedistributable = license: { redistributable = license.free; } // license;
in lib.pipe licenseDeclaration [
in
lib.pipe licenseDeclaration [
applyDefaults
applySpdx
applyRedistributable
];
in mkLicense lset) ({
/* License identifiers from spdx.org where possible.
* If you cannot find your license here, then look for a similar license or
* add it to this list. The URL mentioned above is a good source for inspiration.
in
mkLicense lset
)
(
{
/*
License identifiers from spdx.org where possible.
If you cannot find your license here, then look for a similar license or
add it to this list. The URL mentioned above is a good source for inspiration.
*/
abstyles = {
@ -33,7 +46,7 @@ in mkLicense lset) ({
acsl14 = {
fullName = "Anti-Capitalist Software License v1.4";
url = "https://anticapitalist.software/";
/* restrictions on corporations apply for both use and redistribution */
# restrictions on corporations apply for both use and redistribution
free = false;
redistributable = false;
};
@ -1270,7 +1283,8 @@ in mkLicense lset) ({
spdxId = "XSkat";
fullName = "XSkat License";
};
} // {
}
// {
# TODO: remove legacy aliases
apsl10 = {
# deprecated for consistency with `apple-psl20`; use `apple-psl10`
@ -1309,4 +1323,5 @@ in mkLicense lset) ({
fullName = "GNU Lesser General Public License v3.0";
deprecated = true;
};
})
}
)

View file

@ -4,12 +4,29 @@
{ lib }:
let
inherit (lib.strings) toInt;
inherit (lib.trivial) compare min id warn pipe;
inherit (lib.trivial)
compare
min
id
warn
pipe
;
inherit (lib.attrsets) mapAttrs;
in
rec {
inherit (builtins) head tail length isList elemAt concatLists filter elem genList map;
inherit (builtins)
head
tail
length
isList
elemAt
concatLists
filter
elem
genList
map
;
/**
Create a list consisting of a single element. `singleton x` is
@ -81,7 +98,6 @@ rec {
`list` with `nul` as the starting value, i.e.,
`foldr op nul [x_1 x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))`.
# Inputs
`op`
@ -118,14 +134,13 @@ rec {
:::
*/
foldr = op: nul: list:
foldr =
op: nul: list:
let
len = length list;
fold' = n:
if n == len
then nul
else op (elemAt list n) (fold' (n + 1));
in fold' 0;
fold' = n: if n == len then nul else op (elemAt list n) (fold' (n + 1));
in
fold' 0;
/**
`fold` is an alias of `foldr` for historic reasons
@ -133,7 +148,6 @@ rec {
# FIXME(Profpatsch): deprecate?
fold = foldr;
/**
left fold, like `foldr`, but from the left:
@ -175,13 +189,12 @@ rec {
:::
*/
foldl = op: nul: list:
foldl =
op: nul: list:
let
foldl' = n:
if n == -1
then nul
else op (foldl' (n - 1)) (elemAt list n);
in foldl' (length list - 1);
foldl' = n: if n == -1 then nul else op (foldl' (n - 1)) (elemAt list n);
in
foldl' (length list - 1);
/**
Reduce a list by applying a binary operator from left to right,
@ -260,13 +273,11 @@ rec {
:::
*/
foldl' =
op:
acc:
op: acc:
# The builtin `foldl'` is a bit lazier than one might expect.
# See https://github.com/NixOS/nix/pull/7158.
# In particular, the initial accumulator value is not forced before the first iteration starts.
builtins.seq acc
(builtins.foldl' op acc);
builtins.seq acc (builtins.foldl' op acc);
/**
Map with index starting from 0
@ -303,7 +314,6 @@ rec {
/**
Map with index starting from 1
# Inputs
`f`
@ -373,12 +383,9 @@ rec {
:::
*/
ifilter0 =
ipred:
input:
ipred: input:
map (idx: elemAt input idx) (
filter (idx: ipred idx (elemAt input idx)) (
genList (x: x) (length input)
)
filter (idx: ipred idx (elemAt input idx)) (genList (x: x) (length input))
);
/**
@ -407,14 +414,12 @@ rec {
Flatten the argument into a single list; that is, nested lists are
spliced into the top-level lists.
# Inputs
`x`
: 1\. Function argument
# Examples
:::{.example}
## `lib.lists.flatten` usage example
@ -428,15 +433,11 @@ rec {
:::
*/
flatten = x:
if isList x
then concatMap (y: flatten y) x
else [x];
flatten = x: if isList x then concatMap (y: flatten y) x else [ x ];
/**
Remove elements equal to 'e' from a list. Useful for buildInputs.
# Inputs
`e`
@ -464,8 +465,7 @@ rec {
:::
*/
remove =
e: filter (x: x != e);
remove = e: filter (x: x != e);
/**
Find the sole element in the list matching the specified
@ -474,7 +474,6 @@ rec {
Returns `default` if no such element exists, or
`multiple` if there are multiple matching elements.
# Inputs
`pred`
@ -515,14 +514,17 @@ rec {
:::
*/
findSingle =
pred:
default:
multiple:
list:
let found = filter pred list; len = length found;
in if len == 0 then default
else if len != 1 then multiple
else head found;
pred: default: multiple: list:
let
found = filter pred list;
len = length found;
in
if len == 0 then
default
else if len != 1 then
multiple
else
head found;
/**
Find the first index in the list matching the specified
@ -562,9 +564,7 @@ rec {
:::
*/
findFirstIndex =
pred:
default:
list:
pred: default: list:
let
# A naive recursive implementation would be much simpler, but
# would also overflow the evaluator stack. We use `foldl'` as a workaround
@ -579,7 +579,8 @@ rec {
# - if index >= 0 then pred (elemAt list index) and all elements before (elemAt list index) didn't satisfy pred
#
# We start with index -1 and the 0'th element of the list, which satisfies the invariant
resultIndex = foldl' (index: el:
resultIndex = foldl' (
index: el:
if index < 0 then
# No match yet before the current index, we need to check the element
if pred el then
@ -593,10 +594,7 @@ rec {
index
) (-1) list;
in
if resultIndex < 0 then
default
else
resultIndex;
if resultIndex < 0 then default else resultIndex;
/**
Find the first element in the list matching the specified
@ -636,16 +634,11 @@ rec {
:::
*/
findFirst =
pred:
default:
list:
pred: default: list:
let
index = findFirstIndex pred null list;
in
if index == null then
default
else
elemAt list index;
if index == null then default else elemAt list index;
/**
Return true if function `pred` returns true for at least one
@ -744,8 +737,7 @@ rec {
:::
*/
count =
pred: foldl' (c: x: if pred x then c + 1 else c) 0;
count = pred: foldl' (c: x: if pred x then c + 1 else c) 0;
/**
Return a singleton list or an empty list, depending on a boolean
@ -815,10 +807,7 @@ rec {
:::
*/
optionals =
cond:
elems: if cond then elems else [];
optionals = cond: elems: if cond then elems else [ ];
/**
If argument is a list, return it; else, wrap it in a singleton
@ -878,13 +867,7 @@ rec {
:::
*/
range =
first:
last:
if first > last then
[]
else
genList (n: first + n) (last - first + 1);
range = first: last: if first > last then [ ] else genList (n: first + n) (last - first + 1);
/**
Return a list with `n` copies of an element.
@ -976,7 +959,6 @@ rec {
: 4\. Function argument
# Examples
:::{.example}
## `lib.lists.groupBy'` usage example
@ -1001,15 +983,21 @@ rec {
:::
*/
groupBy' = op: nul: pred: lst: mapAttrs (name: foldl op nul) (groupBy pred lst);
groupBy' =
op: nul: pred: lst:
mapAttrs (name: foldl op nul) (groupBy pred lst);
groupBy = builtins.groupBy or (
pred: foldl' (r: e:
groupBy =
builtins.groupBy or (
pred:
foldl' (
r: e:
let
key = pred e;
in
r // { ${key} = (r.${key} or [ ]) ++ [ e ]; }
) {});
) { }
);
/**
Merges two lists of the same size together. If the sizes aren't the same
@ -1048,11 +1036,8 @@ rec {
:::
*/
zipListsWith =
f:
fst:
snd:
genList
(n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd));
f: fst: snd:
genList (n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd));
/**
Merges two lists of the same size together. If the sizes aren't the same
@ -1113,8 +1098,12 @@ rec {
:::
*/
reverseList = xs:
let l = length xs; in genList (n: elemAt xs (l - n - 1)) l;
reverseList =
xs:
let
l = length xs;
in
genList (n: elemAt xs (l - n - 1)) l;
/**
Depth-First Search (DFS) for lists `list != []`.
@ -1122,7 +1111,6 @@ rec {
`before a b == true` means that `b` depends on `a` (there's an
edge from `b` to `a`).
# Inputs
`stopOnCycles`
@ -1137,7 +1125,6 @@ rec {
: 3\. Function argument
# Examples
:::{.example}
## `lib.lists.listDfs` usage example
@ -1158,22 +1145,32 @@ rec {
:::
*/
listDfs = stopOnCycles: before: list:
listDfs =
stopOnCycles: before: list:
let
dfs' = us: visited: rest:
dfs' =
us: visited: rest:
let
c = filter (x: before x us) visited;
b = partition (x: before x us) rest;
in if stopOnCycles && (length c > 0)
then { cycle = us; loops = c; inherit visited rest; }
else if length b.right == 0
then # nothing is before us
{ minimal = us; inherit visited rest; }
else # grab the first one before us and continue
dfs' (head b.right)
([ us ] ++ visited)
(tail b.right ++ b.wrong);
in dfs' (head list) [] (tail list);
in
if stopOnCycles && (length c > 0) then
{
cycle = us;
loops = c;
inherit visited rest;
}
else if length b.right == 0 then
# nothing is before us
{
minimal = us;
inherit visited rest;
}
else
# grab the first one before us and continue
dfs' (head b.right) ([ us ] ++ visited) (tail b.right ++ b.wrong);
in
dfs' (head list) [ ] (tail list);
/**
Sort a list based on a partial ordering using DFS. This
@ -1183,7 +1180,6 @@ rec {
`before a b == true` means that `b` should be after `a`
in the result.
# Inputs
`before`
@ -1194,7 +1190,6 @@ rec {
: 2\. Function argument
# Examples
:::{.example}
## `lib.lists.toposort` usage example
@ -1215,23 +1210,27 @@ rec {
:::
*/
toposort = before: list:
toposort =
before: list:
let
dfsthis = listDfs true before list;
toporest = toposort before (dfsthis.visited ++ dfsthis.rest);
in
if length list < 2
then # finish
if length list < 2 then
# finish
{ result = list; }
else if dfsthis ? cycle
then # there's a cycle, starting from the current vertex, return it
{ cycle = reverseList ([ dfsthis.cycle ] ++ dfsthis.visited);
inherit (dfsthis) loops; }
else if toporest ? cycle
then # there's a cycle somewhere else in the graph, return it
else if dfsthis ? cycle then
# there's a cycle, starting from the current vertex, return it
{
cycle = reverseList ([ dfsthis.cycle ] ++ dfsthis.visited);
inherit (dfsthis) loops;
}
else if toporest ? cycle then
# there's a cycle somewhere else in the graph, return it
toporest
# Slow, but short. Can be made a bit faster with an explicit stack.
else # there are no cycles
else
# there are no cycles
{ result = [ dfsthis.minimal ] ++ toporest.result; };
/**
@ -1288,7 +1287,6 @@ rec {
sortOn f == sort (p: q: f p < f q)
```
# Inputs
`f`
@ -1316,18 +1314,22 @@ rec {
:::
*/
sortOn = f: list:
sortOn =
f: list:
let
# Heterogenous list as pair may be ugly, but requires minimal allocations.
pairs = map (x: [(f x) x]) list;
pairs = map (x: [
(f x)
x
]) list;
in
map
(x: builtins.elemAt x 1)
(sort
map (x: builtins.elemAt x 1) (
sort
# Compare the first element of the pairs
# Do not factor out the `<`, to avoid calls in hot code; duplicate instead.
(a: b: head a < head b)
pairs);
pairs
);
/**
Compare two lists element-by-element.
@ -1346,7 +1348,6 @@ rec {
: 3\. Function argument
# Examples
:::{.example}
## `lib.lists.compareLists` usage example
@ -1364,30 +1365,28 @@ rec {
:::
*/
compareLists = cmp: a: b:
if a == []
then if b == []
then 0
else -1
else if b == []
then 1
else let rel = cmp (head a) (head b); in
if rel == 0
then compareLists cmp (tail a) (tail b)
else rel;
compareLists =
cmp: a: b:
if a == [ ] then
if b == [ ] then 0 else -1
else if b == [ ] then
1
else
let
rel = cmp (head a) (head b);
in
if rel == 0 then compareLists cmp (tail a) (tail b) else rel;
/**
Sort list using "Natural sorting".
Numeric portions of strings are sorted in numeric order.
# Inputs
`lst`
: 1\. Function argument
# Examples
:::{.example}
## `lib.lists.naturalSort` usage example
@ -1403,10 +1402,14 @@ rec {
:::
*/
naturalSort = lst:
naturalSort =
lst:
let
vectorise = s: map (x: if isList x then toInt (head x) else x) (builtins.split "(0|[1-9][0-9]*)" s);
prepared = map (x: [ (vectorise x) x ]) lst; # remember vectorised version for O(n) regex splits
prepared = map (x: [
(vectorise x)
x
]) lst; # remember vectorised version for O(n) regex splits
less = a: b: (compareLists compare (head a) (head b)) < 0;
in
map (x: elemAt x 1) (sort less prepared);
@ -1414,7 +1417,6 @@ rec {
/**
Return the first (at most) N elements of a list.
# Inputs
`count`
@ -1444,13 +1446,11 @@ rec {
:::
*/
take =
count: sublist 0 count;
take = count: sublist 0 count;
/**
Remove the first (at most) N elements of a list.
# Inputs
`count`
@ -1480,14 +1480,11 @@ rec {
:::
*/
drop =
count:
list: sublist count (length list) list;
drop = count: list: sublist count (length list) list;
/**
Whether the first list is a prefix of the second list.
# Inputs
`list1`
@ -1517,10 +1514,7 @@ rec {
:::
*/
hasPrefix =
list1:
list2:
take (length list1) list2 == list1;
hasPrefix = list1: list2: take (length list1) list2 == list1;
/**
Remove the first list as a prefix from the second list.
@ -1556,8 +1550,7 @@ rec {
:::
*/
removePrefix =
list1:
list2:
list1: list2:
if hasPrefix list1 list2 then
drop (length list1) list2
else
@ -1601,20 +1594,22 @@ rec {
:::
*/
sublist =
start:
count:
list:
let len = length list; in
genList
(n: elemAt list (n + start))
(if start >= len then 0
else if start + count > len then len - start
else count);
start: count: list:
let
len = length list;
in
genList (n: elemAt list (n + start)) (
if start >= len then
0
else if start + count > len then
len - start
else
count
);
/**
The common prefix of two lists.
# Inputs
`list1`
@ -1647,8 +1642,7 @@ rec {
:::
*/
commonPrefix =
list1:
list2:
list1: list2:
let
# Zip the lists together into a list of booleans whether each element matches
matchings = zipListsWith (fst: snd: fst != snd) list1 list2;
@ -1665,7 +1659,6 @@ rec {
This function throws an error if the list is empty.
# Inputs
`list`
@ -1689,7 +1682,8 @@ rec {
:::
*/
last = list:
last =
list:
assert lib.assertMsg (list != [ ]) "lists.last: list must not be empty!";
elemAt list (length list - 1);
@ -1698,7 +1692,6 @@ rec {
This function throws an error if the list is empty.
# Inputs
`list`
@ -1722,15 +1715,14 @@ rec {
:::
*/
init = list:
init =
list:
assert lib.assertMsg (list != [ ]) "lists.init: list must not be empty!";
take (length list - 1) list;
/**
Return the image of the cross product of some lists by a function.
# Examples
:::{.example}
## `lib.lists.crossLists` usage example
@ -1748,8 +1740,8 @@ rec {
```
:::
*/
crossLists = warn
''lib.crossLists is deprecated, use lib.mapCartesianProduct instead.
crossLists = warn ''
lib.crossLists is deprecated, use lib.mapCartesianProduct instead.
For example, the following function call:
@ -1760,13 +1752,11 @@ rec {
nix-repl> lib.mapCartesianProduct ({x,y}: x+y) { x = [1 2]; y = [3 4]; }
[ 4 5 5 6 ]
''
(f: foldl (fs: args: concatMap (f: map f args) fs) [f]);
'' (f: foldl (fs: args: concatMap (f: map f args) fs) [ f ]);
/**
Remove duplicate elements from the `list`. O(n^2) complexity.
# Inputs
`list`
@ -1795,7 +1785,6 @@ rec {
/**
Check if list contains only unique elements. O(n^2) complexity.
# Inputs
`list`
@ -1823,7 +1812,6 @@ rec {
*/
allUnique = list: (length (unique list) == length list);
/**
Intersects list 'list1' and another list (`list2`).
@ -1839,7 +1827,6 @@ rec {
: Second list
# Examples
:::{.example}
## `lib.lists.intersectLists` usage example
@ -1868,7 +1855,6 @@ rec {
: Second list
# Examples
:::{.example}
## `lib.lists.subtractLists` usage example

View file

@ -1,80 +1,91 @@
/* Some functions for manipulating meta attributes, as well as the
name attribute. */
/*
Some functions for manipulating meta attributes, as well as the
name attribute.
*/
{ lib }:
let
inherit (lib) matchAttrs any all isDerivation getBin assertMsg;
inherit (lib)
matchAttrs
any
all
isDerivation
getBin
assertMsg
;
inherit (builtins) isString match typeOf;
in
rec {
/* Add to or override the meta attributes of the given
/*
Add to or override the meta attributes of the given
derivation.
Example:
addMetaAttrs {description = "Bla blah";} somePkg
*/
addMetaAttrs = newAttrs: drv:
drv // { meta = (drv.meta or {}) // newAttrs; };
addMetaAttrs = newAttrs: drv: drv // { meta = (drv.meta or { }) // newAttrs; };
/* Disable Hydra builds of given derivation.
*/
# Disable Hydra builds of given derivation.
dontDistribute = drv: addMetaAttrs { hydraPlatforms = [ ]; } drv;
/* Change the symbolic name of a package for presentation purposes
/*
Change the symbolic name of a package for presentation purposes
(i.e., so that nix-env users can tell them apart).
*/
setName = name: drv: drv // { inherit name; };
/* Like `setName`, but takes the previous name as an argument.
/*
Like `setName`, but takes the previous name as an argument.
Example:
updateName (oldName: oldName + "-experimental") somePkg
*/
updateName = updater: drv: drv // { name = updater (drv.name); };
/* Append a suffix to the name of a package (before the version
part). */
appendToName = suffix: updateName (name:
let x = builtins.parseDrvName name; in "${x.name}-${suffix}-${x.version}");
/* Apply a function to each derivation and only to derivations in an attrset.
/*
Append a suffix to the name of a package (before the version
part).
*/
mapDerivationAttrset = f: set: lib.mapAttrs (name: pkg: if lib.isDerivation pkg then (f pkg) else pkg) set;
appendToName =
suffix:
updateName (
name:
let
x = builtins.parseDrvName name;
in
"${x.name}-${suffix}-${x.version}"
);
/* Set the nix-env priority of the package.
*/
# Apply a function to each derivation and only to derivations in an attrset.
mapDerivationAttrset =
f: set: lib.mapAttrs (name: pkg: if lib.isDerivation pkg then (f pkg) else pkg) set;
# Set the nix-env priority of the package.
setPrio = priority: addMetaAttrs { inherit priority; };
/* Decrease the nix-env priority of the package, i.e., other
/*
Decrease the nix-env priority of the package, i.e., other
versions/variants of the package will be preferred.
*/
lowPrio = setPrio 10;
/* Apply lowPrio to an attrset with derivations
*/
# Apply lowPrio to an attrset with derivations
lowPrioSet = set: mapDerivationAttrset lowPrio set;
/* Increase the nix-env priority of the package, i.e., this
/*
Increase the nix-env priority of the package, i.e., this
version/variant of the package will be preferred.
*/
hiPrio = setPrio (-10);
/* Apply hiPrio to an attrset with derivations
*/
# Apply hiPrio to an attrset with derivations
hiPrioSet = set: mapDerivationAttrset hiPrio set;
/* Check to see if a platform is matched by the given `meta.platforms`
/*
Check to see if a platform is matched by the given `meta.platforms`
element.
A `meta.platform` pattern is either
@ -92,7 +103,9 @@ rec {
lib.meta.platformMatch { system = "aarch64-darwin"; } "aarch64-darwin"
=> true
*/
platformMatch = platform: elem: (
platformMatch =
platform: elem:
(
# Check with simple string comparison if elem was a string.
#
# The majority of comparisons done with this function will be against meta.platforms
@ -100,15 +113,17 @@ rec {
#
# Avoiding an attrset allocation results in significant performance gains (~2-30) across the board in OfBorg
# because this is a hot path for nixpkgs.
if isString elem then platform ? system && elem == platform.system
else matchAttrs (
if isString elem then
platform ? system && elem == platform.system
else
matchAttrs (
# Normalize platform attrset.
if elem ? parsed then elem
else { parsed = elem; }
if elem ? parsed then elem else { parsed = elem; }
) platform
);
/* Check if a package is available on a given platform.
/*
Check if a package is available on a given platform.
A package is available on a platform if both
@ -121,11 +136,13 @@ rec {
lib.meta.availableOn { system = "aarch64-darwin"; } pkg.zsh
=> true
*/
availableOn = platform: pkg:
((!pkg?meta.platforms) || any (platformMatch platform) pkg.meta.platforms) &&
all (elem: !platformMatch platform elem) (pkg.meta.badPlatforms or []);
availableOn =
platform: pkg:
((!pkg ? meta.platforms) || any (platformMatch platform) pkg.meta.platforms)
&& all (elem: !platformMatch platform elem) (pkg.meta.badPlatforms or [ ]);
/* Get the corresponding attribute in lib.licenses
/*
Get the corresponding attribute in lib.licenses
from the SPDX ID.
For SPDX IDs, see
https://spdx.org/licenses
@ -144,15 +161,23 @@ rec {
*/
getLicenseFromSpdxId =
let
spdxLicenses = lib.mapAttrs (id: ls: assert lib.length ls == 1; builtins.head ls)
spdxLicenses =
lib.mapAttrs
(
id: ls:
assert lib.length ls == 1;
builtins.head ls
)
(lib.groupBy (l: lib.toLower l.spdxId) (lib.filter (l: l ? spdxId) (lib.attrValues lib.licenses)));
in licstr:
spdxLicenses.${ lib.toLower licstr } or (
lib.warn "getLicenseFromSpdxId: No license matches the given SPDX ID: ${licstr}"
{ shortName = licstr; }
);
in
licstr:
spdxLicenses.${lib.toLower licstr}
or (lib.warn "getLicenseFromSpdxId: No license matches the given SPDX ID: ${licstr}" {
shortName = licstr;
});
/* Get the path to the main program of a package based on meta.mainProgram
/*
Get the path to the main program of a package based on meta.mainProgram
Type: getExe :: package -> string
@ -162,14 +187,22 @@ rec {
getExe pkgs.mustache-go
=> "/nix/store/am9ml4f4ywvivxnkiaqwr0hyxka1xjsf-mustache-go-1.3.0/bin/mustache"
*/
getExe = x: getExe' x (x.meta.mainProgram or (
getExe =
x:
getExe' x (
x.meta.mainProgram or (
# This could be turned into an error when 23.05 is at end of life
lib.warn "getExe: Package ${lib.strings.escapeNixIdentifier x.meta.name or x.pname or x.name} does not have the meta.mainProgram attribute. We'll assume that the main program has the same name for now, but this behavior is deprecated, because it leads to surprising errors when the assumption does not hold. If the package has a main program, please set `meta.mainProgram` in its definition to make this warning go away. Otherwise, if the package does not have a main program, or if you don't control its definition, use getExe' to specify the name to the program, such as lib.getExe' foo \"bar\"."
lib.warn
"getExe: Package ${
lib.strings.escapeNixIdentifier x.meta.name or x.pname or x.name
} does not have the meta.mainProgram attribute. We'll assume that the main program has the same name for now, but this behavior is deprecated, because it leads to surprising errors when the assumption does not hold. If the package has a main program, please set `meta.mainProgram` in its definition to make this warning go away. Otherwise, if the package does not have a main program, or if you don't control its definition, use getExe' to specify the name to the program, such as lib.getExe' foo \"bar\"."
lib.getName
x
));
)
);
/* Get the path of a program of a derivation.
/*
Get the path of a program of a derivation.
Type: getExe' :: derivation -> string -> string
Example:
@ -178,7 +211,8 @@ rec {
getExe' pkgs.imagemagick "convert"
=> "/nix/store/5rs48jamq7k6sal98ymj9l4k2bnwq515-imagemagick-7.1.1-15/bin/convert"
*/
getExe' = x: y:
getExe' =
x: y:
assert assertMsg (isDerivation x)
"lib.meta.getExe': The first argument is of type ${typeOf x}, but it should be a derivation instead.";
assert assertMsg (isString y)

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
/* Nixpkgs/NixOS option handling. */
# Nixpkgs/NixOS option handling.
{ lib }:
let
@ -25,27 +25,18 @@ let
optionals
take
;
inherit (lib.attrsets)
attrByPath
optionalAttrs
;
inherit (lib.strings)
concatMapStrings
concatStringsSep
;
inherit (lib.types)
mkOptionType
;
inherit (lib.lists)
last
;
inherit (lib.attrsets) attrByPath optionalAttrs;
inherit (lib.strings) concatMapStrings concatStringsSep;
inherit (lib.types) mkOptionType;
inherit (lib.lists) last;
prioritySuggestion = ''
Use `lib.mkForce value` or `lib.mkDefault value` to change the priority on any of these definitions.
'';
in
rec {
/* Returns true when the given argument is an option
/*
Returns true when the given argument is an option
Type: isOption :: a -> bool
@ -55,7 +46,8 @@ rec {
*/
isOption = lib.isType "option";
/* Creates an Option attribute set. mkOption accepts an attribute set with the following keys:
/*
Creates an Option attribute set. mkOption accepts an attribute set with the following keys:
All keys default to `null` when not given.
@ -88,7 +80,8 @@ rec {
}@attrs:
attrs // { _type = "option"; };
/* Creates an Option attribute set for a boolean value option i.e an
/*
Creates an Option attribute set for a boolean value option i.e an
option to be toggled on or off:
Example:
@ -97,14 +90,16 @@ rec {
*/
mkEnableOption =
# Name for the created option
name: mkOption {
name:
mkOption {
default = false;
example = true;
description = "Whether to enable ${name}.";
type = lib.types.bool;
};
/* Creates an Option attribute set for an option that specifies the
/*
Creates an Option attribute set for an option that specifies the
package a module should use for some purpose.
The package is specified in the third argument under `default` as a list of strings
@ -197,40 +192,53 @@ rec {
# Additional text to include in the option description (may be omitted)
extraDescription ? "",
# Representation of the package set passed as pkgs (defaults to `"pkgs"`)
pkgsText ? "pkgs"
pkgsText ? "pkgs",
}:
let
name' = if isList name then last name else name;
default' = if isList default then default else [ default ];
defaultText = concatStringsSep "." default';
defaultValue = attrByPath default'
(throw "${defaultText} cannot be found in ${pkgsText}") pkgs;
defaults = if default != null then {
defaultValue = attrByPath default' (throw "${defaultText} cannot be found in ${pkgsText}") pkgs;
defaults =
if default != null then
{
default = defaultValue;
defaultText = literalExpression ("${pkgsText}." + defaultText);
} else optionalAttrs nullable {
default = null;
};
in mkOption (defaults // {
description = "The ${name'} package to use."
+ (if extraDescription == "" then "" else " ") + extraDescription;
}
else
optionalAttrs nullable { default = null; };
in
mkOption (
defaults
// {
description =
"The ${name'} package to use." + (if extraDescription == "" then "" else " ") + extraDescription;
type = with lib.types; (if nullable then nullOr else lib.id) package;
} // optionalAttrs (example != null) {
example = literalExpression
(if isList example then "${pkgsText}." + concatStringsSep "." example else example);
});
}
// optionalAttrs (example != null) {
example = literalExpression (
if isList example then "${pkgsText}." + concatStringsSep "." example else example
);
}
);
/* Alias of mkPackageOption. Previously used to create options with markdown
/*
Alias of mkPackageOption. Previously used to create options with markdown
documentation, which is no longer required.
*/
mkPackageOptionMD = mkPackageOption;
/* This option accepts anything, but it does not produce any result.
/*
This option accepts anything, but it does not produce any result.
This is useful for sharing a module across different module sets
without having to implement similar features as long as the
values of the options are not accessed. */
mkSinkUndeclaredOptions = attrs: mkOption ({
values of the options are not accessed.
*/
mkSinkUndeclaredOptions =
attrs:
mkOption (
{
internal = true;
visible = false;
default = false;
@ -241,18 +249,31 @@ rec {
merge = loc: defs: false;
};
apply = x: throw "Option value is not readable because the option is not declared.";
} // attrs);
}
// attrs
);
mergeDefaultOption = loc: defs:
let list = getValues defs; in
if length list == 1 then head list
else if all isFunction list then x: mergeDefaultOption loc (map (f: f x) list)
else if all isList list then concatLists list
else if all isAttrs list then foldl' lib.mergeAttrs {} list
else if all isBool list then foldl' lib.or false list
else if all isString list then lib.concatStrings list
else if all isInt list && all (x: x == head list) list then head list
else throw "Cannot merge definitions of `${showOption loc}'. Definition values:${showDefs defs}";
mergeDefaultOption =
loc: defs:
let
list = getValues defs;
in
if length list == 1 then
head list
else if all isFunction list then
x: mergeDefaultOption loc (map (f: f x) list)
else if all isList list then
concatLists list
else if all isAttrs list then
foldl' lib.mergeAttrs { } list
else if all isBool list then
foldl' lib.or false list
else if all isString list then
lib.concatStrings list
else if all isInt list && all (x: x == head list) list then
head list
else
throw "Cannot merge definitions of `${showOption loc}'. Definition values:${showDefs defs}";
/*
Require a single definition.
@ -266,34 +287,48 @@ rec {
NOTE: When the type is not checked completely by check, pass a merge function for further checking (of sub-attributes, etc).
*/
mergeUniqueOption = args@{
mergeUniqueOption =
args@{
message,
# WARNING: the default merge function assumes that the definition is a valid (option) value. You MUST pass a merge function if the return value needs to be
# - type checked beyond what .check does (which should be very litte; only on the value head; not attribute values, etc)
# - if you want attribute values to be checked, or list items
# - if you want coercedTo-like behavior to work
merge ? loc: defs: (head defs).value }:
merge ? loc: defs: (head defs).value,
}:
loc: defs:
if length defs == 1
then merge loc defs
if length defs == 1 then
merge loc defs
else
assert length defs > 1;
throw "The option `${showOption loc}' is defined multiple times while it's expected to be unique.\n${message}\nDefinition values:${showDefs defs}\n${prioritySuggestion}";
/* "Merge" option definitions by checking that they all have the same value. */
mergeEqualOption = loc: defs:
if defs == [] then abort "This case should never happen."
# "Merge" option definitions by checking that they all have the same value.
mergeEqualOption =
loc: defs:
if defs == [ ] then
abort "This case should never happen."
# Return early if we only have one element
# This also makes it work for functions, because the foldl' below would try
# to compare the first element with itself, which is false for functions
else if length defs == 1 then (head defs).value
else (foldl' (first: def:
if def.value != first.value then
throw "The option `${showOption loc}' has conflicting definition values:${showDefs [ first def ]}\n${prioritySuggestion}"
else if length defs == 1 then
(head defs).value
else
first) (head defs) (tail defs)).value;
(foldl' (
first: def:
if def.value != first.value then
throw "The option `${showOption loc}' has conflicting definition values:${
showDefs [
first
def
]
}\n${prioritySuggestion}"
else
first
) (head defs) (tail defs)).value;
/* Extracts values of all "value" keys of the given list.
/*
Extracts values of all "value" keys of the given list.
Type: getValues :: [ { value :: a; } ] -> [a]
@ -303,7 +338,8 @@ rec {
*/
getValues = map (x: x.value);
/* Extracts values of all "file" keys of the given list
/*
Extracts values of all "file" keys of the given list
Type: getFiles :: [ { file :: a; } ] -> [a]
@ -317,48 +353,51 @@ rec {
# the set generated with filterOptionSets.
optionAttrSetToDocList = optionAttrSetToDocList' [ ];
optionAttrSetToDocList' = _: options:
concatMap (opt:
optionAttrSetToDocList' =
_: options:
concatMap (
opt:
let
name = showOption opt.loc;
docOption = {
docOption =
{
loc = opt.loc;
inherit name;
description = opt.description or null;
declarations = filter (x: x != unknownModule) opt.declarations;
internal = opt.internal or false;
visible =
if (opt?visible && opt.visible == "shallow")
then true
else opt.visible or true;
visible = if (opt ? visible && opt.visible == "shallow") then true else opt.visible or true;
readOnly = opt.readOnly or false;
type = opt.type.description or "unspecified";
}
// optionalAttrs (opt ? example) {
example =
builtins.addErrorContext "while evaluating the example of option `${name}`" (
example = builtins.addErrorContext "while evaluating the example of option `${name}`" (
renderOptionValue opt.example
);
}
// optionalAttrs (opt ? defaultText || opt ? default) {
default =
builtins.addErrorContext "while evaluating the ${if opt?defaultText then "defaultText" else "default value"} of option `${name}`" (
renderOptionValue (opt.defaultText or opt.default)
);
default = builtins.addErrorContext "while evaluating the ${
if opt ? defaultText then "defaultText" else "default value"
} of option `${name}`" (renderOptionValue (opt.defaultText or opt.default));
}
// optionalAttrs (opt ? relatedPackages && opt.relatedPackages != null) { inherit (opt) relatedPackages; };
// optionalAttrs (opt ? relatedPackages && opt.relatedPackages != null) {
inherit (opt) relatedPackages;
};
subOptions =
let ss = opt.type.getSubOptions opt.loc;
in if ss != {} then optionAttrSetToDocList' opt.loc ss else [];
let
ss = opt.type.getSubOptions opt.loc;
in
if ss != { } then optionAttrSetToDocList' opt.loc ss else [ ];
subOptionsVisible = docOption.visible && opt.visible or null != "shallow";
in
# To find infinite recursion in NixOS option docs:
# builtins.trace opt.loc
[ docOption ] ++ optionals subOptionsVisible subOptions) (collect isOption options);
[ docOption ] ++ optionals subOptionsVisible subOptions
) (collect isOption options);
/* This function recursively removes all derivation attributes from
/*
This function recursively removes all derivation attributes from
`x` except for the `name` attribute.
This is to make the generation of `options.xml` much more
@ -369,54 +408,83 @@ rec {
This function was made obsolete by renderOptionValue and is kept for
compatibility with out-of-tree code.
*/
scrubOptionValue = x:
scrubOptionValue =
x:
if isDerivation x then
{ type = "derivation"; drvPath = x.name; outPath = x.name; name = x.name; }
else if isList x then map scrubOptionValue x
else if isAttrs x then mapAttrs (n: v: scrubOptionValue v) (removeAttrs x ["_args"])
else x;
{
type = "derivation";
drvPath = x.name;
outPath = x.name;
name = x.name;
}
else if isList x then
map scrubOptionValue x
else if isAttrs x then
mapAttrs (n: v: scrubOptionValue v) (removeAttrs x [ "_args" ])
else
x;
/* Ensures that the given option value (default or example) is a `_type`d string
/*
Ensures that the given option value (default or example) is a `_type`d string
by rendering Nix values to `literalExpression`s.
*/
renderOptionValue = v:
if v ? _type && v ? text then v
else literalExpression (lib.generators.toPretty {
renderOptionValue =
v:
if v ? _type && v ? text then
v
else
literalExpression (
lib.generators.toPretty {
multiline = true;
allowPrettyValues = true;
} v);
} v
);
/* For use in the `defaultText` and `example` option attributes. Causes the
/*
For use in the `defaultText` and `example` option attributes. Causes the
given string to be rendered verbatim in the documentation as Nix code. This
is necessary for complex values, e.g. functions, or values that depend on
other values or packages.
*/
literalExpression = text:
if ! isString text then throw "literalExpression expects a string."
else { _type = "literalExpression"; inherit text; };
literalExpression =
text:
if !isString text then
throw "literalExpression expects a string."
else
{
_type = "literalExpression";
inherit text;
};
literalExample = lib.warn "lib.literalExample is deprecated, use lib.literalExpression instead, or use lib.literalMD for a non-Nix description." literalExpression;
/* Transition marker for documentation that's already migrated to markdown
/*
Transition marker for documentation that's already migrated to markdown
syntax. Has been a no-op for some while and been removed from nixpkgs.
Kept here to alert downstream users who may not be aware of the migration's
completion that it should be removed from modules.
*/
mdDoc = lib.warn "lib.mdDoc will be removed from nixpkgs in 24.11. Option descriptions are now in Markdown by default; you can remove any remaining uses of lib.mdDoc.";
/* For use in the `defaultText` and `example` option attributes. Causes the
/*
For use in the `defaultText` and `example` option attributes. Causes the
given MD text to be inserted verbatim in the documentation, for when
a `literalExpression` would be too hard to read.
*/
literalMD = text:
if ! isString text then throw "literalMD expects a string."
else { _type = "literalMD"; inherit text; };
literalMD =
text:
if !isString text then
throw "literalMD expects a string."
else
{
_type = "literalMD";
inherit text;
};
# Helper functions.
/* Convert an option, described as a list of the option parts to a
/*
Convert an option, described as a list of the option parts to a
human-readable version.
Example:
@ -428,8 +496,11 @@ rec {
(showOption ["foo" "*" "bar"]) == "foo.*.bar"
(showOption ["foo" "<name>" "bar"]) == "foo.<name>.bar"
*/
showOption = parts: let
escapeOptionPart = part:
showOption =
parts:
let
escapeOptionPart =
part:
let
# We assume that these are "special values" and not real configuration data.
# If it is real configuration data, it is rendered incorrectly.
@ -438,29 +509,41 @@ rec {
"*" # listOf (submodule {})
"<function body>" # functionTo
];
in if builtins.elem part specialIdentifiers
then part
else lib.strings.escapeNixIdentifier part;
in (concatStringsSep ".") (map escapeOptionPart parts);
in
if builtins.elem part specialIdentifiers then part else lib.strings.escapeNixIdentifier part;
in
(concatStringsSep ".") (map escapeOptionPart parts);
showFiles = files: concatStringsSep " and " (map (f: "`${f}'") files);
showDefs = defs: concatMapStrings (def:
showDefs =
defs:
concatMapStrings (
def:
let
# Pretty print the value for display, if successful
prettyEval = builtins.tryEval
(lib.generators.toPretty { }
(lib.generators.withRecursion { depthLimit = 10; throwOnDepthLimit = false; } def.value));
prettyEval = builtins.tryEval (
lib.generators.toPretty { } (
lib.generators.withRecursion {
depthLimit = 10;
throwOnDepthLimit = false;
} def.value
)
);
# Split it into its lines
lines = filter (v: !isList v) (builtins.split "\n" prettyEval.value);
# Only display the first 5 lines, and indent them for better visibility
value = concatStringsSep "\n " (take 5 lines ++ optional (length lines > 5) "...");
result =
# Don't print any value if evaluating the value strictly fails
if ! prettyEval.success then ""
if !prettyEval.success then
""
# Put it on a new line if it consists of multiple
else if length lines > 1 then ":\n " + value
else ": " + value;
in "\n- In `${def.file}'${result}"
else if length lines > 1 then
":\n " + value
else
": " + value;
in
"\n- In `${def.file}'${result}"
) defs;
showOptionWithDefLocs = opt: ''

View file

@ -1,4 +1,4 @@
/* Functions for working with path values. */
# Functions for working with path values.
# See ./README.md for internal docs
{ lib }:
let
@ -27,21 +27,15 @@ let
listHasPrefix = lib.lists.hasPrefix;
inherit (lib.strings)
concatStringsSep
substring
;
inherit (lib.strings) concatStringsSep substring;
inherit (lib.asserts)
assertMsg
;
inherit (lib.asserts) assertMsg;
inherit (lib.path.subpath)
isValid
;
inherit (lib.path.subpath) isValid;
# Return the reason why a subpath is invalid, or `null` if it's valid
subpathInvalidReason = value:
subpathInvalidReason =
value:
if !isString value then
"The given value is of type ${builtins.typeOf value}, but a string was expected"
else if value == "" then
@ -51,11 +45,13 @@ let
# We don't support ".." components, see ./path.md#parent-directory
else if match "(.*/)?\\.\\.(/.*)?" value != null then
"The given string \"${value}\" contains a `..` component, which is not allowed in subpaths"
else null;
else
null;
# Split and normalise a relative path string into its components.
# Error for ".." components and doesn't include "." components
splitRelPath = path:
splitRelPath =
path:
let
# Split the string into its parts using regex for efficiency. This regex
# matches patterns like "/", "/./", "/././", with arbitrarily many "/"s
@ -89,21 +85,26 @@ let
# Special case of a single "." path component. Such a case leaves a
# componentCount of -1 due to the skipStart/skipEnd not verifying that
# they don't refer to the same character
if path == "." then []
if path == "." then
[ ]
# Generate the result list directly. This is more efficient than a
# combination of `filter`, `init` and `tail`, because here we don't
# allocate any intermediate lists
else genList (index:
else
genList (
index:
# To get to the element we need to add the number of parts we skip and
# multiply by two due to the interleaved layout of `parts`
elemAt parts ((skipStart + index) * 2)
) componentCount;
# Join relative path components together
joinRelPath = components:
joinRelPath =
components:
# Always return relative paths with `./` as a prefix (./path.md#leading-dots-for-relative-paths)
"./" +
"./"
+
# An empty string is not a valid relative path, so we need to return a `.` when we have no components
(if components == [ ] then "." else concatStringsSep "/" components);
@ -117,11 +118,18 @@ let
# because it can distinguish different filesystem roots
deconstructPath =
let
recurse = components: base:
recurse =
components: base:
# If the parent of a path is the path itself, then it's a filesystem root
if base == dirOf base then { root = base; inherit components; }
else recurse ([ (baseNameOf base) ] ++ components) (dirOf base);
in recurse [];
if base == dirOf base then
{
root = base;
inherit components;
}
else
recurse ([ (baseNameOf base) ] ++ components) (dirOf base);
in
recurse [ ];
# The components of the store directory, typically [ "nix" "store" ]
storeDirComponents = splitRelPath ("./" + storeDir);
@ -132,7 +140,8 @@ let
#
# Whether path components have a store path as a prefix, according to
# https://nixos.org/manual/nix/stable/store/store-path.html#store-path.
componentsHaveStorePathPrefix = components:
componentsHaveStorePathPrefix =
components:
# path starts with the store directory (typically /nix/store)
listHasPrefix storeDirComponents components
# is not the store directory itself, meaning there's at least one extra component
@ -145,7 +154,9 @@ let
# We care more about discerning store path-ness on realistic values. Making it airtight would be fragile and slow.
&& match ".{32}-.+" (elemAt components storeDirLength) != null;
in /* No rec! Add dependencies on this file at the top. */ {
in
# No rec! Add dependencies on this file at the top.
{
/*
Append a subpath string to a path.
@ -194,8 +205,8 @@ in /* No rec! Add dependencies on this file at the top. */ {
path:
# The subpath string to append
subpath:
assert assertMsg (isPath path) ''
lib.path.append: The first argument is of type ${builtins.typeOf path}, but a path was expected'';
assert assertMsg (isPath path)
''lib.path.append: The first argument is of type ${builtins.typeOf path}, but a path was expected'';
assert assertMsg (isValid subpath) ''
lib.path.append: Second argument is not a valid subpath string:
${subpathInvalidReason subpath}'';
@ -225,25 +236,23 @@ in /* No rec! Add dependencies on this file at the top. */ {
*/
hasPrefix =
path1:
assert assertMsg
(isPath path1)
assert assertMsg (isPath path1)
"lib.path.hasPrefix: First argument is of type ${typeOf path1}, but a path was expected";
let
path1Deconstructed = deconstructPath path1;
in
path2:
assert assertMsg
(isPath path2)
assert assertMsg (isPath path2)
"lib.path.hasPrefix: Second argument is of type ${typeOf path2}, but a path was expected";
let
path2Deconstructed = deconstructPath path2;
in
assert assertMsg
(path1Deconstructed.root == path2Deconstructed.root) ''
assert assertMsg (path1Deconstructed.root == path2Deconstructed.root) ''
lib.path.hasPrefix: Filesystem roots must be the same for both paths, but paths with different roots were given:
first argument: "${toString path1}" with root "${toString path1Deconstructed.root}"
second argument: "${toString path2}" with root "${toString path2Deconstructed.root}"'';
take (length path1Deconstructed.components) path2Deconstructed.components == path1Deconstructed.components;
take (length path1Deconstructed.components) path2Deconstructed.components
== path1Deconstructed.components;
/*
Remove the first path as a component-wise prefix from the second path.
@ -270,16 +279,14 @@ in /* No rec! Add dependencies on this file at the top. */ {
*/
removePrefix =
path1:
assert assertMsg
(isPath path1)
assert assertMsg (isPath path1)
"lib.path.removePrefix: First argument is of type ${typeOf path1}, but a path was expected.";
let
path1Deconstructed = deconstructPath path1;
path1Length = length path1Deconstructed.components;
in
path2:
assert assertMsg
(isPath path2)
assert assertMsg (isPath path2)
"lib.path.removePrefix: Second argument is of type ${typeOf path2}, but a path was expected.";
let
path2Deconstructed = deconstructPath path2;
@ -288,11 +295,9 @@ in /* No rec! Add dependencies on this file at the top. */ {
if success then
drop path1Length path2Deconstructed.components
else
throw ''
lib.path.removePrefix: The first path argument "${toString path1}" is not a component-wise prefix of the second path argument "${toString path2}".'';
throw ''lib.path.removePrefix: The first path argument "${toString path1}" is not a component-wise prefix of the second path argument "${toString path2}".'';
in
assert assertMsg
(path1Deconstructed.root == path2Deconstructed.root) ''
assert assertMsg (path1Deconstructed.root == path2Deconstructed.root) ''
lib.path.removePrefix: Filesystem roots must be the same for both paths, but paths with different roots were given:
first argument: "${toString path1}" with root "${toString path1Deconstructed.root}"
second argument: "${toString path2}" with root "${toString path2Deconstructed.root}"'';
@ -336,12 +341,12 @@ in /* No rec! Add dependencies on this file at the top. */ {
splitRoot =
# The path to split the root off of
path:
assert assertMsg
(isPath path)
assert assertMsg (isPath path)
"lib.path.splitRoot: Argument is of type ${typeOf path}, but a path was expected";
let
deconstructed = deconstructPath path;
in {
in
{
root = deconstructed.root;
subpath = joinRelPath deconstructed.components;
};
@ -387,12 +392,12 @@ in /* No rec! Add dependencies on this file at the top. */ {
hasStorePathPrefix /nix/store/nvl9ic0pj1fpyln3zaqrf4cclbqdfn1j-foo.drv
=> true
*/
hasStorePathPrefix = path:
hasStorePathPrefix =
path:
let
deconstructed = deconstructPath path;
in
assert assertMsg
(isPath path)
assert assertMsg (isPath path)
"lib.path.hasStorePathPrefix: Argument is of type ${typeOf path}, but a path was expected";
assert assertMsg
# This function likely breaks or needs adjustment if used with other filesystem roots, if they ever get implemented.
@ -446,9 +451,7 @@ in /* No rec! Add dependencies on this file at the top. */ {
*/
subpath.isValid =
# The value to check
value:
subpathInvalidReason value == null;
value: subpathInvalidReason value == null;
/*
Join subpath strings together using `/`, returning a normalised subpath string.
@ -511,16 +514,18 @@ in /* No rec! Add dependencies on this file at the top. */ {
# The list of subpaths to join together
subpaths:
# Fast in case all paths are valid
if all isValid subpaths
then joinRelPath (concatMap splitRelPath subpaths)
if all isValid subpaths then
joinRelPath (concatMap splitRelPath subpaths)
else
# Otherwise we take our time to gather more info for a better error message
# Strictly go through each path, throwing on the first invalid one
# Tracks the list index in the fold accumulator
foldl' (i: path:
if isValid path
then i + 1
else throw ''
foldl' (
i: path:
if isValid path then
i + 1
else
throw ''
lib.path.subpath.join: Element at index ${toString i} is not a valid subpath string:
${subpathInvalidReason path}''
) 0 subpaths;

View file

@ -12,14 +12,16 @@
seed ? null,
}:
pkgs.runCommand "lib-path-tests" {
nativeBuildInputs = [
nixVersions.stable
] ++ (with pkgs; [
pkgs.runCommand "lib-path-tests"
{
nativeBuildInputs =
[ nixVersions.stable ]
++ (with pkgs; [
jq
bc
]);
} ''
}
''
# Needed to make Nix evaluation work
export TEST_ROOT=$(pwd)/test-tmp
export NIX_BUILD_HOOK=

View file

@ -16,14 +16,15 @@ let
lib = import libpath;
# read each file into a string
strings = map (name:
builtins.readFile (dir + "/${name}")
) (builtins.attrNames (builtins.readDir dir));
strings = map (name: builtins.readFile (dir + "/${name}")) (
builtins.attrNames (builtins.readDir dir)
);
inherit (lib.path.subpath) normalise isValid;
inherit (lib.asserts) assertMsg;
normaliseAndCheck = str:
normaliseAndCheck =
str:
let
originalValid = isValid str;
@ -34,27 +35,26 @@ let
absConcatNormalised = /. + ("/" + tryOnce.value);
in
# Check the lib.path.subpath.normalise property to only error on invalid subpaths
assert assertMsg
(originalValid -> tryOnce.success)
"Even though string \"${str}\" is valid as a subpath, the normalisation for it failed";
assert assertMsg
(! originalValid -> ! tryOnce.success)
"Even though string \"${str}\" is invalid as a subpath, the normalisation for it succeeded";
assert assertMsg (
originalValid -> tryOnce.success
) "Even though string \"${str}\" is valid as a subpath, the normalisation for it failed";
assert assertMsg (
!originalValid -> !tryOnce.success
) "Even though string \"${str}\" is invalid as a subpath, the normalisation for it succeeded";
# Check normalisation idempotency
assert assertMsg
(originalValid -> tryTwice.success)
"For valid subpath \"${str}\", the normalisation \"${tryOnce.value}\" was not a valid subpath";
assert assertMsg
(originalValid -> tryOnce.value == tryTwice.value)
assert assertMsg (
originalValid -> tryTwice.success
) "For valid subpath \"${str}\", the normalisation \"${tryOnce.value}\" was not a valid subpath";
assert assertMsg (originalValid -> tryOnce.value == tryTwice.value)
"For valid subpath \"${str}\", normalising it once gives \"${tryOnce.value}\" but normalising it twice gives a different result: \"${tryTwice.value}\"";
# Check that normalisation doesn't change a string when appended to an absolute Nix path value
assert assertMsg
(originalValid -> absConcatOrig == absConcatNormalised)
assert assertMsg (originalValid -> absConcatOrig == absConcatNormalised)
"For valid subpath \"${str}\", appending to an absolute Nix path value gives \"${absConcatOrig}\", but appending the normalised result \"${tryOnce.value}\" gives a different value \"${absConcatNormalised}\"";
# Return an empty string when failed
if tryOnce.success then tryOnce.value else "";
in lib.genAttrs strings normaliseAndCheck
in
lib.genAttrs strings normaliseAndCheck

View file

@ -3,7 +3,14 @@
{ libpath }:
let
lib = import libpath;
inherit (lib.path) hasPrefix removePrefix append splitRoot hasStorePathPrefix subpath;
inherit (lib.path)
hasPrefix
removePrefix
append
splitRoot
hasStorePathPrefix
subpath
;
# This is not allowed generally, but we're in the tests here, so we'll allow ourselves.
storeDirPath = /. + builtins.storeDir;
@ -79,15 +86,24 @@ let
testSplitRootExample1 = {
expr = splitRoot /foo/bar;
expected = { root = /.; subpath = "./foo/bar"; };
expected = {
root = /.;
subpath = "./foo/bar";
};
};
testSplitRootExample2 = {
expr = splitRoot /.;
expected = { root = /.; subpath = "./."; };
expected = {
root = /.;
subpath = "./.";
};
};
testSplitRootExample3 = {
expr = splitRoot /foo/../bar;
expected = { root = /.; subpath = "./bar"; };
expected = {
root = /.;
subpath = "./bar";
};
};
testSplitRootExample4 = {
expr = (builtins.tryEval (splitRoot "/foo/bar")).success;
@ -111,7 +127,9 @@ let
expected = false;
};
testHasStorePathPrefixExample5 = {
expr = hasStorePathPrefix (storeDirPath + "/.links/10gg8k3rmbw8p7gszarbk7qyd9jwxhcfq9i6s5i0qikx8alkk4hq");
expr = hasStorePathPrefix (
storeDirPath + "/.links/10gg8k3rmbw8p7gszarbk7qyd9jwxhcfq9i6s5i0qikx8alkk4hq"
);
expected = false;
};
testHasStorePathPrefixExample6 = {
@ -188,11 +206,18 @@ let
# Test examples from the lib.path.subpath.join documentation
testSubpathJoinExample1 = {
expr = subpath.join [ "foo" "bar/baz" ];
expr = subpath.join [
"foo"
"bar/baz"
];
expected = "./foo/bar/baz";
};
testSubpathJoinExample2 = {
expr = subpath.join [ "./foo" "." "bar//./baz/" ];
expr = subpath.join [
"./foo"
"."
"bar//./baz/"
];
expected = "./foo/bar/baz";
};
testSubpathJoinExample3 = {
@ -273,7 +298,11 @@ let
};
testSubpathComponentsExample2 = {
expr = subpath.components "./foo//bar/./baz/";
expected = [ "foo" "bar" "baz" ];
expected = [
"foo"
"bar"
"baz"
];
};
testSubpathComponentsExample3 = {
expr = (builtins.tryEval (subpath.components "/foo")).success;
@ -281,5 +310,7 @@ let
};
};
in
if cases == [] then "Unit tests successful"
else throw "Path unit tests failed: ${lib.generators.toPretty {} cases}"
if cases == [ ] then
"Unit tests successful"
else
throw "Path unit tests failed: ${lib.generators.toPretty { } cases}"

View file

@ -5,7 +5,8 @@ let
shortName = tname;
isSource = false;
};
in lib.mapAttrs (tname: tset: defaultSourceType tname // tset) {
in
lib.mapAttrs (tname: tset: defaultSourceType tname // tset) {
fromSource = {
isSource = true;

View file

@ -1,41 +1,47 @@
/* Functions for copying sources to the Nix store. */
# Functions for copying sources to the Nix store.
{ lib }:
# Tested in lib/tests/sources.sh
let
inherit (builtins)
match
split
storeDir
;
inherit (builtins) match split storeDir;
inherit (lib)
boolToString
filter
isString
readFile
;
inherit (lib.filesystem)
pathIsRegularFile
;
inherit (lib.filesystem) pathIsRegularFile;
/*
A basic filter for `cleanSourceWith` that removes
directories of version control system, backup files (*~)
and some generated files.
*/
cleanSourceFilter = name: type: let baseName = baseNameOf (toString name); in ! (
cleanSourceFilter =
name: type:
let
baseName = baseNameOf (toString name);
in
!(
# Filter out version control software files/directories
(baseName == ".git" || type == "directory" && (baseName == ".svn" || baseName == "CVS" || baseName == ".hg")) ||
(
baseName == ".git"
|| type == "directory" && (baseName == ".svn" || baseName == "CVS" || baseName == ".hg")
)
||
# Filter out editor backup / swap files.
lib.hasSuffix "~" baseName ||
match "^\\.sw[a-z]$" baseName != null ||
match "^\\..*\\.sw[a-z]$" baseName != null ||
lib.hasSuffix "~" baseName
|| match "^\\.sw[a-z]$" baseName != null
|| match "^\\..*\\.sw[a-z]$" baseName != null
||
# Filter out generates files.
lib.hasSuffix ".o" baseName ||
lib.hasSuffix ".so" baseName ||
lib.hasSuffix ".o" baseName
|| lib.hasSuffix ".so" baseName
||
# Filter out nix-build result symlinks
(type == "symlink" && lib.hasPrefix "result" baseName) ||
(type == "symlink" && lib.hasPrefix "result" baseName)
||
# Filter out sockets and other types of files we can't have in the store.
(type == "unknown")
);
@ -46,7 +52,12 @@ let
Example:
cleanSource ./.
*/
cleanSource = src: cleanSourceWith { filter = cleanSourceFilter; inherit src; };
cleanSource =
src:
cleanSourceWith {
filter = cleanSourceFilter;
inherit src;
};
/*
Like `builtins.filterSource`, except it will compose with itself,
@ -65,7 +76,6 @@ let
builtins.filterSource f (builtins.filterSource g ./.)
# Fails!
*/
cleanSourceWith =
{
@ -80,11 +90,12 @@ let
filter ? _path: _type: true,
# Optional name to use as part of the store path.
# This defaults to `src.name` or otherwise `"source"`.
name ? null
name ? null,
}:
let
orig = toSourceAttributes src;
in fromSourceAttributes {
in
fromSourceAttributes {
inherit (orig) origSrc;
filter = path: type: filter path type && orig.filter path type;
name = if name != null then name else orig.name;
@ -102,14 +113,17 @@ let
attrs = toSourceAttributes src;
in
fromSourceAttributes (
attrs // {
filter = path: type:
attrs
// {
filter =
path: type:
let
r = attrs.filter path type;
in
builtins.trace "${attrs.name}.filter ${path} = ${boolToString r}" r;
}
) // {
)
// {
satisfiesSubpathInvariant = src ? satisfiesSubpathInvariant && src.satisfiesSubpathInvariant;
};
@ -118,14 +132,20 @@ let
Example: src = sourceByRegex ./my-subproject [".*\.py$" "^database.sql$"]
*/
sourceByRegex = src: regexes:
sourceByRegex =
src: regexes:
let
isFiltered = src ? _isLibCleanSourceWith;
origSrc = if isFiltered then src.origSrc else src;
in lib.cleanSourceWith {
filter = (path: type:
let relPath = lib.removePrefix (toString origSrc + "/") (toString path);
in lib.any (re: match re relPath != null) regexes);
in
lib.cleanSourceWith {
filter = (
path: type:
let
relPath = lib.removePrefix (toString origSrc + "/") (toString path);
in
lib.any (re: match re relPath != null) regexes
);
inherit src;
};
@ -145,10 +165,15 @@ let
src:
# A list of file suffix strings
exts:
let filter = name: type:
let base = baseNameOf (toString name);
in type == "directory" || lib.any (ext: lib.hasSuffix ext base) exts;
in cleanSourceWith { inherit filter src; };
let
filter =
name: type:
let
base = baseNameOf (toString name);
in
type == "directory" || lib.any (ext: lib.hasSuffix ext base) exts;
in
cleanSourceWith { inherit filter src; };
pathIsGitRepo = path: (_commitIdFromGitRepoOrError path) ? value;
@ -157,9 +182,12 @@ let
Example: commitIdFromGitRepo <nixpkgs/.git>
*/
commitIdFromGitRepo = path:
let commitIdOrError = _commitIdFromGitRepoOrError path;
in commitIdOrError.value or (throw commitIdOrError.error);
commitIdFromGitRepo =
path:
let
commitIdOrError = _commitIdFromGitRepoOrError path;
in
commitIdOrError.value or (throw commitIdOrError.error);
# Get the commit id of a git repo.
@ -168,55 +196,68 @@ let
# Example: commitIdFromGitRepo <nixpkgs/.git>
# not exported, used for commitIdFromGitRepo
_commitIdFromGitRepoOrError =
let readCommitFromFile = file: path:
let fileName = path + "/${file}";
let
readCommitFromFile =
file: path:
let
fileName = path + "/${file}";
packedRefsName = path + "/packed-refs";
absolutePath = base: path:
if lib.hasPrefix "/" path
then path
else toString (/. + "${base}/${path}");
in if pathIsRegularFile path
absolutePath =
base: path: if lib.hasPrefix "/" path then path else toString (/. + "${base}/${path}");
in
if
pathIsRegularFile path
# Resolve git worktrees. See gitrepository-layout(5)
then
let m = match "^gitdir: (.*)$" (lib.fileContents path);
in if m == null
then { error = "File contains no gitdir reference: " + path; }
let
m = match "^gitdir: (.*)$" (lib.fileContents path);
in
if m == null then
{ error = "File contains no gitdir reference: " + path; }
else
let gitDir = absolutePath (dirOf path) (lib.head m);
commonDir'' = if pathIsRegularFile "${gitDir}/commondir"
then lib.fileContents "${gitDir}/commondir"
else gitDir;
let
gitDir = absolutePath (dirOf path) (lib.head m);
commonDir'' =
if pathIsRegularFile "${gitDir}/commondir" then lib.fileContents "${gitDir}/commondir" else gitDir;
commonDir' = lib.removeSuffix "/" commonDir'';
commonDir = absolutePath gitDir commonDir';
refFile = lib.removePrefix "${commonDir}/" "${gitDir}/${file}";
in readCommitFromFile refFile commonDir
in
readCommitFromFile refFile commonDir
else if pathIsRegularFile fileName
else if
pathIsRegularFile fileName
# Sometimes git stores the commitId directly in the file but
# sometimes it stores something like: «ref: refs/heads/branch-name»
then
let fileContent = lib.fileContents fileName;
let
fileContent = lib.fileContents fileName;
matchRef = match "^ref: (.*)$" fileContent;
in if matchRef == null
then { value = fileContent; }
else readCommitFromFile (lib.head matchRef) path
in
if matchRef == null then { value = fileContent; } else readCommitFromFile (lib.head matchRef) path
else if pathIsRegularFile packedRefsName
else if
pathIsRegularFile packedRefsName
# Sometimes, the file isn't there at all and has been packed away in the
# packed-refs file, so we have to grep through it:
then
let fileContent = readFile packedRefsName;
let
fileContent = readFile packedRefsName;
matchRef = match "([a-z0-9]+) ${file}";
isRef = s: isString s && (matchRef s) != null;
# there is a bug in libstdc++ leading to stackoverflow for long strings:
# https://github.com/NixOS/nix/issues/2147#issuecomment-659868795
refs = filter isRef (split "\n" fileContent);
in if refs == []
then { error = "Could not find " + file + " in " + packedRefsName; }
else { value = lib.head (matchRef (lib.head refs)); }
in
if refs == [ ] then
{ error = "Could not find " + file + " in " + packedRefsName; }
else
{ value = lib.head (matchRef (lib.head refs)); }
else { error = "Not a .git directory: " + toString path; };
in readCommitFromFile "HEAD";
else
{ error = "Not a .git directory: " + toString path; };
in
readCommitFromFile "HEAD";
pathHasContext = builtins.hasContext or (lib.hasPrefix storeDir);
@ -233,7 +274,8 @@ let
# like class of objects in the wild.
# (Existing ones being: paths, strings, sources and x//{outPath})
# So instead of exposing internals, we build a library of combinator functions.
toSourceAttributes = src:
toSourceAttributes =
src:
let
isFiltered = src ? _isLibCleanSourceWith;
in
@ -247,24 +289,35 @@ let
# fromSourceAttributes : SourceAttrs -> Source
#
# Inverse of toSourceAttributes for Source objects.
fromSourceAttributes = { origSrc, filter, name }:
fromSourceAttributes =
{
origSrc,
filter,
name,
}:
{
_isLibCleanSourceWith = true;
inherit origSrc filter name;
outPath = builtins.path { inherit filter name; path = origSrc; };
outPath = builtins.path {
inherit filter name;
path = origSrc;
};
};
in {
in
{
pathType = lib.warnIf (lib.isInOldestRelease 2305)
"lib.sources.pathType has been moved to lib.filesystem.pathType."
lib.filesystem.pathType;
pathType = lib.warnIf (lib.isInOldestRelease
2305
) "lib.sources.pathType has been moved to lib.filesystem.pathType." lib.filesystem.pathType;
pathIsDirectory = lib.warnIf (lib.isInOldestRelease 2305)
pathIsDirectory =
lib.warnIf (lib.isInOldestRelease 2305)
"lib.sources.pathIsDirectory has been moved to lib.filesystem.pathIsDirectory."
lib.filesystem.pathIsDirectory;
pathIsRegularFile = lib.warnIf (lib.isInOldestRelease 2305)
pathIsRegularFile =
lib.warnIf (lib.isInOldestRelease 2305)
"lib.sources.pathIsRegularFile has been moved to lib.filesystem.pathIsRegularFile."
lib.filesystem.pathIsRegularFile;

View file

@ -52,32 +52,63 @@ let
in
rec {
/* !!! The interface of this function is kind of messed up, since
/*
!!! The interface of this function is kind of messed up, since
it's way too overloaded and almost but not quite computes a
topological sort of the depstrings. */
topological sort of the depstrings.
*/
textClosureList = predefined: arg:
textClosureList =
predefined: arg:
let
f = done: todo:
if todo == [] then {result = []; inherit done;}
f =
done: todo:
if todo == [ ] then
{
result = [ ];
inherit done;
}
else
let entry = head todo; in
let
entry = head todo;
in
if isAttrs entry then
let x = f done entry.deps;
let
x = f done entry.deps;
y = f x.done (tail todo);
in { result = x.result ++ [entry.text] ++ y.result;
in
{
result = x.result ++ [ entry.text ] ++ y.result;
done = y.done;
}
else if done ? ${entry} then f done (tail todo)
else f (done // listToAttrs [{name = entry; value = 1;}]) ([predefined.${entry}] ++ tail todo);
in (f {} arg).result;
else if done ? ${entry} then
f done (tail todo)
else
f (
done
// listToAttrs [
{
name = entry;
value = 1;
}
]
) ([ predefined.${entry} ] ++ tail todo);
in
(f { } arg).result;
textClosureMap = f: predefined: names:
textClosureMap =
f: predefined: names:
concatStringsSep "\n" (map f (textClosureList predefined names));
noDepEntry = text: {inherit text; deps = [];};
noDepEntry = text: {
inherit text;
deps = [ ];
};
fullDepEntry = text: deps: { inherit text deps; };
packEntry = deps: {inherit deps; text="";};
packEntry = deps: {
inherit deps;
text = "";
};
stringAfter = deps: text: { inherit text deps; };

File diff suppressed because it is too large Load diff

View file

@ -7,36 +7,284 @@ rec {
# Spec: https://gitlab.com/x86-psABIs/x86-64-ABI/
default = [ ];
x86-64 = [ ];
x86-64-v2 = [ "sse3" "ssse3" "sse4_1" "sse4_2" ];
x86-64-v3 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "avx" "avx2" "fma" ];
x86-64-v4 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "avx" "avx2" "avx512" "fma" ];
x86-64-v2 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
];
x86-64-v3 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"avx"
"avx2"
"fma"
];
x86-64-v4 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"avx"
"avx2"
"avx512"
"fma"
];
# x86_64 Intel
nehalem = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" ];
westmere = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" ];
sandybridge = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" ];
ivybridge = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" ];
haswell = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "fma" ];
broadwell = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "fma" ];
skylake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "fma" ];
skylake-avx512 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
cannonlake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
icelake-client = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
icelake-server = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
cascadelake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
cooperlake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
tigerlake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
alderlake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "fma" ];
nehalem = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
];
westmere = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
];
sandybridge = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
];
ivybridge = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
];
haswell = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"fma"
];
broadwell = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"fma"
];
skylake = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"fma"
];
skylake-avx512 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
cannonlake = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
icelake-client = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
icelake-server = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
cascadelake = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
cooperlake = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
tigerlake = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
alderlake = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"fma"
];
# x86_64 AMD
btver1 = [ "sse3" "ssse3" "sse4_1" "sse4_2" ];
btver2 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" ];
bdver1 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "fma" "fma4" ];
bdver2 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "fma" "fma4" ];
bdver3 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "fma" "fma4" ];
bdver4 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "avx2" "fma" "fma4" ];
znver1 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "avx2" "fma" ];
znver2 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "avx2" "fma" ];
znver3 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "avx2" "fma" ];
znver4 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "avx2" "avx512" "fma" ];
btver1 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
];
btver2 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
];
bdver1 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"sse4a"
"aes"
"avx"
"fma"
"fma4"
];
bdver2 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"sse4a"
"aes"
"avx"
"fma"
"fma4"
];
bdver3 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"sse4a"
"aes"
"avx"
"fma"
"fma4"
];
bdver4 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"sse4a"
"aes"
"avx"
"avx2"
"fma"
"fma4"
];
znver1 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"sse4a"
"aes"
"avx"
"avx2"
"fma"
];
znver2 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"sse4a"
"aes"
"avx"
"avx2"
"fma"
];
znver3 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"sse4a"
"aes"
"avx"
"avx2"
"fma"
];
znver4 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"sse4a"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
# other
armv5te = [ ];
armv6 = [ ];
@ -62,11 +310,25 @@ rec {
sandybridge = [ "westmere" ] ++ inferiors.westmere;
ivybridge = [ "sandybridge" ] ++ inferiors.sandybridge;
haswell = lib.unique ([ "ivybridge" "x86-64-v3" ] ++ inferiors.ivybridge ++ inferiors.x86-64-v3);
haswell = lib.unique (
[
"ivybridge"
"x86-64-v3"
]
++ inferiors.ivybridge
++ inferiors.x86-64-v3
);
broadwell = [ "haswell" ] ++ inferiors.haswell;
skylake = [ "broadwell" ] ++ inferiors.broadwell;
skylake-avx512 = lib.unique ([ "skylake" "x86-64-v4" ] ++ inferiors.skylake ++ inferiors.x86-64-v4);
skylake-avx512 = lib.unique (
[
"skylake"
"x86-64-v4"
]
++ inferiors.skylake
++ inferiors.x86-64-v4
);
cannonlake = [ "skylake-avx512" ] ++ inferiors.skylake-avx512;
icelake-client = [ "cannonlake" ] ++ inferiors.cannonlake;
icelake-server = [ "icelake-client" ] ++ inferiors.icelake-client;
@ -107,7 +369,14 @@ rec {
znver1 = [ "skylake" ] ++ inferiors.skylake; # Includes haswell and x86-64-v3
znver2 = [ "znver1" ] ++ inferiors.znver1;
znver3 = [ "znver2" ] ++ inferiors.znver2;
znver4 = lib.unique ([ "znver3" "x86-64-v4" ] ++ inferiors.znver3 ++ inferiors.x86-64-v4);
znver4 = lib.unique (
[
"znver3"
"x86-64-v4"
]
++ inferiors.znver3
++ inferiors.x86-64-v4
);
# other
armv5te = [ ];
@ -118,9 +387,11 @@ rec {
loongson2f = [ ];
};
predicates = let
predicates =
let
featureSupport = feature: x: builtins.elem feature features.${x} or [ ];
in {
in
{
sse3Support = featureSupport "sse3";
ssse3Support = featureSupport "ssse3";
sse4_1Support = featureSupport "sse4_1";

View file

@ -42,8 +42,10 @@ let
both arguments have been `elaborate`-d.
*/
equals =
let removeFunctions = a: filterAttrs (_: v: !isFunction v) a;
in a: b: removeFunctions a == removeFunctions b;
let
removeFunctions = a: filterAttrs (_: v: !isFunction v) a;
in
a: b: removeFunctions a == removeFunctions b;
/**
List of all Nix system doubles the nixpkgs flake will expose the package set
@ -61,42 +63,63 @@ let
# `parsed` is inferred from args, both because there are two options with one
# clearly preferred, and to prevent cycles. A simpler fixed point where the RHS
# always just used `final.*` would fail on both counts.
elaborate = args': let
args = if isString args' then { system = args'; }
else args';
elaborate =
args':
let
args = if isString args' then { system = args'; } else args';
# TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL.
rust = args.rust or args.rustc or { };
final = {
final =
{
# Prefer to parse `config` as it is strictly more informative.
parsed = parse.mkSystemFromString (if args ? config then args.config else args.system);
# Either of these can be losslessly-extracted from `parsed` iff parsing succeeds.
system = parse.doubleFromSystem final.parsed;
config = parse.tripleFromSystem final.parsed;
# Determine whether we can execute binaries built for the provided platform.
canExecute = platform:
final.isAndroid == platform.isAndroid &&
parse.isCompatible final.parsed.cpu platform.parsed.cpu
canExecute =
platform:
final.isAndroid == platform.isAndroid
&& parse.isCompatible final.parsed.cpu platform.parsed.cpu
&& final.parsed.kernel == platform.parsed.kernel;
isCompatible = _: throw "2022-05-23: isCompatible has been removed in favor of canExecute, refer to the 22.11 changelog for details";
isCompatible =
_:
throw "2022-05-23: isCompatible has been removed in favor of canExecute, refer to the 22.11 changelog for details";
# Derived meta-data
libc =
/**/ if final.isDarwin then "libSystem"
else if final.isMinGW then "msvcrt"
else if final.isWasi then "wasilibc"
else if final.isRedox then "relibc"
else if final.isMusl then "musl"
else if final.isUClibc then "uclibc"
else if final.isAndroid then "bionic"
else if final.isLinux /* default */ then "glibc"
else if final.isFreeBSD then "fblibc"
else if final.isNetBSD then "nblibc"
else if final.isAvr then "avrlibc"
else if final.isGhcjs then null
else if final.isNone then "newlib"
if final.isDarwin then
"libSystem"
else if final.isMinGW then
"msvcrt"
else if final.isWasi then
"wasilibc"
else if final.isRedox then
"relibc"
else if final.isMusl then
"musl"
else if final.isUClibc then
"uclibc"
else if final.isAndroid then
"bionic"
else if
final.isLinux # default
then
"glibc"
else if final.isFreeBSD then
"fblibc"
else if final.isNetBSD then
"nblibc"
else if final.isAvr then
"avrlibc"
else if final.isGhcjs then
null
else if final.isNone then
"newlib"
# TODO(@Ericson2314) think more about other operating systems
else "native/impure";
else
"native/impure";
# Choose what linker we wish to use by default. Someday we might also
# choose the C compiler, runtime library, C++ standard library, etc. in
# this way, nice and orthogonally, and deprecate `useLLVM`. But due to
@ -104,34 +127,36 @@ let
# independently, so we are just doing `linker` and keeping `useLLVM` for
# now.
linker =
/**/ if final.useLLVM or false then "lld"
else if final.isDarwin then "cctools"
if final.useLLVM or false then
"lld"
else if final.isDarwin then
"cctools"
# "bfd" and "gold" both come from GNU binutils. The existence of Gold
# is why we use the more obscure "bfd" and not "binutils" for this
# choice.
else "bfd";
else
"bfd";
# The standard lib directory name that non-nixpkgs binaries distributed
# for this platform normally assume.
libDir = if final.isLinux then
if final.isx86_64 || final.isMips64 || final.isPower64
then "lib64"
else "lib"
else null;
extensions = optionalAttrs final.hasSharedLibraries {
libDir =
if final.isLinux then
if final.isx86_64 || final.isMips64 || final.isPower64 then "lib64" else "lib"
else
null;
extensions =
optionalAttrs final.hasSharedLibraries {
sharedLibrary =
if final.isDarwin then ".dylib"
else if final.isWindows then ".dll"
else ".so";
} // {
staticLibrary =
/**/ if final.isWindows then ".lib"
else ".a";
library =
/**/ if final.isStatic then final.extensions.staticLibrary
else final.extensions.sharedLibrary;
executable =
/**/ if final.isWindows then ".exe"
else "";
if final.isDarwin then
".dylib"
else if final.isWindows then
".dll"
else
".so";
}
// {
staticLibrary = if final.isWindows then ".lib" else ".a";
library = if final.isStatic then final.extensions.staticLibrary else final.extensions.sharedLibrary;
executable = if final.isWindows then ".exe" else "";
};
# Misc boolean options
useAndroidPrebuilt = false;
@ -140,7 +165,8 @@ let
# Output from uname
uname = {
# uname -s
system = {
system =
{
linux = "Linux";
windows = "Windows";
darwin = "Darwin";
@ -150,17 +176,19 @@ let
wasi = "Wasi";
redox = "Redox";
genode = "Genode";
}.${final.parsed.kernel.name} or null;
}
.${final.parsed.kernel.name} or null;
# uname -m
processor =
if final.isPower64
then "ppc64${optionalString final.isLittleEndian "le"}"
else if final.isPower
then "ppc${optionalString final.isLittleEndian "le"}"
else if final.isMips64
then "mips64" # endianness is *not* included on mips64
else final.parsed.cpu.name;
if final.isPower64 then
"ppc64${optionalString final.isLittleEndian "le"}"
else if final.isPower then
"ppc${optionalString final.isLittleEndian "le"}"
else if final.isMips64 then
"mips64" # endianness is *not* included on mips64
else
final.parsed.cpu.name;
# uname -r
release = null;
@ -172,11 +200,21 @@ let
# will still build on/for those platforms with --enable-shared, but simply
# omit any `.so` build products such as libgcc_s.so. When that happens,
# it causes hard-to-troubleshoot build failures.
hasSharedLibraries = with final;
(isAndroid || isGnu || isMusl # Linux (allows multiple libcs)
|| isDarwin || isSunOS || isOpenBSD || isFreeBSD || isNetBSD # BSDs
|| isCygwin || isMinGW # Windows
) && !isStatic;
hasSharedLibraries =
with final;
(
isAndroid
|| isGnu
|| isMusl # Linux (allows multiple libcs)
|| isDarwin
|| isSunOS
|| isOpenBSD
|| isFreeBSD
|| isNetBSD # BSDs
|| isCygwin
|| isMinGW # Windows
)
&& !isStatic;
# The difference between `isStatic` and `hasSharedLibraries` is mainly the
# addition of the `staticMarker` (see make-derivation.nix). Some
@ -188,73 +226,115 @@ let
# Just a guess, based on `system`
inherit
({
(
{
linux-kernel = args.linux-kernel or { };
gcc = args.gcc or { };
} // platforms.select final)
linux-kernel gcc;
}
// platforms.select final
)
linux-kernel
gcc
;
# TODO: remove after 23.05 is EOL, with an error pointing to the rust.* attrs.
rustc = args.rustc or { };
linuxArch =
if final.isAarch32 then "arm"
else if final.isAarch64 then "arm64"
else if final.isx86_32 then "i386"
else if final.isx86_64 then "x86_64"
if final.isAarch32 then
"arm"
else if final.isAarch64 then
"arm64"
else if final.isx86_32 then
"i386"
else if final.isx86_64 then
"x86_64"
# linux kernel does not distinguish microblaze/microblazeel
else if final.isMicroBlaze then "microblaze"
else if final.isMips32 then "mips"
else if final.isMips64 then "mips" # linux kernel does not distinguish mips32/mips64
else if final.isPower then "powerpc"
else if final.isRiscV then "riscv"
else if final.isS390 then "s390"
else if final.isLoongArch64 then "loongarch"
else final.parsed.cpu.name;
else if final.isMicroBlaze then
"microblaze"
else if final.isMips32 then
"mips"
else if final.isMips64 then
"mips" # linux kernel does not distinguish mips32/mips64
else if final.isPower then
"powerpc"
else if final.isRiscV then
"riscv"
else if final.isS390 then
"s390"
else if final.isLoongArch64 then
"loongarch"
else
final.parsed.cpu.name;
# https://source.denx.de/u-boot/u-boot/-/blob/9bfb567e5f1bfe7de8eb41f8c6d00f49d2b9a426/common/image.c#L81-106
ubootArch =
if final.isx86_32 then "x86" # not i386
else if final.isMips64 then "mips64" # uboot *does* distinguish between mips32/mips64
else final.linuxArch; # other cases appear to agree with linuxArch
if final.isx86_32 then
"x86" # not i386
else if final.isMips64 then
"mips64" # uboot *does* distinguish between mips32/mips64
else
final.linuxArch; # other cases appear to agree with linuxArch
qemuArch =
if final.isAarch32 then "arm"
else if final.isS390 && !final.isS390x then null
else if final.isx86_64 then "x86_64"
else if final.isx86 then "i386"
else if final.isMips64n32 then "mipsn32${optionalString final.isLittleEndian "el"}"
else if final.isMips64 then "mips64${optionalString final.isLittleEndian "el"}"
else final.uname.processor;
if final.isAarch32 then
"arm"
else if final.isS390 && !final.isS390x then
null
else if final.isx86_64 then
"x86_64"
else if final.isx86 then
"i386"
else if final.isMips64n32 then
"mipsn32${optionalString final.isLittleEndian "el"}"
else if final.isMips64 then
"mips64${optionalString final.isLittleEndian "el"}"
else
final.uname.processor;
# Name used by UEFI for architectures.
efiArch =
if final.isx86_32 then "ia32"
else if final.isx86_64 then "x64"
else if final.isAarch32 then "arm"
else if final.isAarch64 then "aa64"
else final.parsed.cpu.name;
if final.isx86_32 then
"ia32"
else if final.isx86_64 then
"x64"
else if final.isAarch32 then
"arm"
else if final.isAarch64 then
"aa64"
else
final.parsed.cpu.name;
darwinArch = {
darwinArch =
{
armv7a = "armv7";
aarch64 = "arm64";
}.${final.parsed.cpu.name} or final.parsed.cpu.name;
}
.${final.parsed.cpu.name} or final.parsed.cpu.name;
darwinPlatform =
if final.isMacOS then "macos"
else if final.isiOS then "ios"
else null;
if final.isMacOS then
"macos"
else if final.isiOS then
"ios"
else
null;
# The canonical name for this attribute is darwinSdkVersion, but some
# platforms define the old name "sdkVer".
darwinSdkVersion = final.sdkVer or (if final.isAarch64 then "11.0" else "10.12");
darwinMinVersion = final.darwinSdkVersion;
darwinMinVersionVariable =
if final.isMacOS then "MACOSX_DEPLOYMENT_TARGET"
else if final.isiOS then "IPHONEOS_DEPLOYMENT_TARGET"
else null;
} // (
if final.isMacOS then
"MACOSX_DEPLOYMENT_TARGET"
else if final.isiOS then
"IPHONEOS_DEPLOYMENT_TARGET"
else
null;
}
// (
let
selectEmulator = pkgs:
selectEmulator =
pkgs:
let
qemu-user = pkgs.qemu.override {
smartcardSupport = false;
@ -277,28 +357,35 @@ let
};
wine = (pkgs.winePackagesFor "wine${toString final.parsed.cpu.bits}").minimal;
in
if pkgs.stdenv.hostPlatform.canExecute final
then "${pkgs.runtimeShell} -c '\"$@\"' --"
else if final.isWindows
then "${wine}/bin/wine${optionalString (final.parsed.cpu.bits == 64) "64"}"
else if final.isLinux && pkgs.stdenv.hostPlatform.isLinux && final.qemuArch != null
then "${qemu-user}/bin/qemu-${final.qemuArch}"
else if final.isWasi
then "${pkgs.wasmtime}/bin/wasmtime"
else if final.isMmix
then "${pkgs.mmixware}/bin/mmix"
else null;
in {
if pkgs.stdenv.hostPlatform.canExecute final then
"${pkgs.runtimeShell} -c '\"$@\"' --"
else if final.isWindows then
"${wine}/bin/wine${optionalString (final.parsed.cpu.bits == 64) "64"}"
else if final.isLinux && pkgs.stdenv.hostPlatform.isLinux && final.qemuArch != null then
"${qemu-user}/bin/qemu-${final.qemuArch}"
else if final.isWasi then
"${pkgs.wasmtime}/bin/wasmtime"
else if final.isMmix then
"${pkgs.mmixware}/bin/mmix"
else
null;
in
{
emulatorAvailable = pkgs: (selectEmulator pkgs) != null;
emulator = pkgs:
if (final.emulatorAvailable pkgs)
then selectEmulator pkgs
else throw "Don't know how to run ${final.config} executables.";
emulator =
pkgs:
if (final.emulatorAvailable pkgs) then
selectEmulator pkgs
else
throw "Don't know how to run ${final.config} executables.";
}) // mapAttrs (n: v: v final.parsed) inspect.predicates
}
)
// mapAttrs (n: v: v final.parsed) inspect.predicates
// mapAttrs (n: v: v final.gcc.arch or "default") architectures.predicates
// args // {
// args
// {
rust = rust // {
# Once args.rustc.platform.target-family is deprecated and
# removed, there will no longer be any need to modify any
@ -308,23 +395,31 @@ let
platform = rust.platform or { } // {
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch
arch =
/**/ if rust ? platform then rust.platform.arch
else if final.isAarch32 then "arm"
else if final.isMips64 then "mips64" # never add "el" suffix
else if final.isPower64 then "powerpc64" # never add "le" suffix
else final.parsed.cpu.name;
if rust ? platform then
rust.platform.arch
else if final.isAarch32 then
"arm"
else if final.isMips64 then
"mips64" # never add "el" suffix
else if final.isPower64 then
"powerpc64" # never add "le" suffix
else
final.parsed.cpu.name;
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_os
os =
/**/ if rust ? platform then rust.platform.os or "none"
else if final.isDarwin then "macos"
else final.parsed.kernel.name;
if rust ? platform then
rust.platform.os or "none"
else if final.isDarwin then
"macos"
else
final.parsed.kernel.name;
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_family
target-family =
/**/ if args ? rust.platform.target-family then args.rust.platform.target-family
else if args ? rustc.platform.target-family
then
if args ? rust.platform.target-family then
args.rust.platform.target-family
else if args ? rustc.platform.target-family then
(
# Since https://github.com/rust-lang/rust/pull/84072
# `target-family` is a list instead of single value.
@ -333,71 +428,79 @@ let
in
if isList f then f else [ f ]
)
else optional final.isUnix "unix"
++ optional final.isWindows "windows";
else
optional final.isUnix "unix" ++ optional final.isWindows "windows";
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_vendor
vendor = let
vendor =
let
inherit (final.parsed) vendor;
in rust.platform.vendor or {
in
rust.platform.vendor or {
"w64" = "pc";
}.${vendor.name} or vendor.name;
}
.${vendor.name} or vendor.name;
};
# The name of the rust target, even if it is custom. Adjustments are
# because rust has slightly different naming conventions than we do.
rustcTarget = let
rustcTarget =
let
inherit (final.parsed) cpu kernel abi;
cpu_ = rust.platform.arch or {
cpu_ =
rust.platform.arch or {
"armv7a" = "armv7";
"armv7l" = "armv7";
"armv6l" = "arm";
"armv5tel" = "armv5te";
"riscv64" = "riscv64gc";
}.${cpu.name} or cpu.name;
}
.${cpu.name} or cpu.name;
vendor_ = final.rust.platform.vendor;
in
# TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL.
in args.rust.rustcTarget or args.rustc.config
args.rust.rustcTarget or args.rustc.config
or "${cpu_}-${vendor_}-${kernel.name}${optionalString (abi.name != "unknown") "-${abi.name}"}";
# The name of the rust target if it is standard, or the json file
# containing the custom target spec.
rustcTargetSpec = rust.rustcTargetSpec or (
/**/ if rust ? platform
then builtins.toFile (final.rust.rustcTarget + ".json") (toJSON rust.platform)
else final.rust.rustcTarget);
rustcTargetSpec =
rust.rustcTargetSpec or (
if rust ? platform then
builtins.toFile (final.rust.rustcTarget + ".json") (toJSON rust.platform)
else
final.rust.rustcTarget
);
# The name of the rust target if it is standard, or the
# basename of the file containing the custom target spec,
# without the .json extension.
#
# This is the name used by Cargo for target subdirectories.
cargoShortTarget =
removeSuffix ".json" (baseNameOf "${final.rust.rustcTargetSpec}");
cargoShortTarget = removeSuffix ".json" (baseNameOf "${final.rust.rustcTargetSpec}");
# When used as part of an environment variable name, triples are
# uppercased and have all hyphens replaced by underscores:
#
# https://github.com/rust-lang/cargo/pull/9169
# https://github.com/rust-lang/cargo/issues/8285#issuecomment-634202431
cargoEnvVarTarget =
replaceStrings ["-"] ["_"]
(toUpper final.rust.cargoShortTarget);
cargoEnvVarTarget = replaceStrings [ "-" ] [ "_" ] (toUpper final.rust.cargoShortTarget);
# True if the target is no_std
# https://github.com/rust-lang/rust/blob/2e44c17c12cec45b6a682b1e53a04ac5b5fcc9d2/src/bootstrap/config.rs#L415-L421
isNoStdTarget =
any (t: hasInfix t final.rust.rustcTarget) ["-none" "nvptx" "switch" "-uefi"];
isNoStdTarget = any (t: hasInfix t final.rust.rustcTarget) [
"-none"
"nvptx"
"switch"
"-uefi"
];
};
};
in assert final.useAndroidPrebuilt -> final.isAndroid;
assert foldl
(pass: { assertion, message }:
if assertion final
then pass
else throw message)
true
(final.parsed.abi.assertions or []);
in
assert final.useAndroidPrebuilt -> final.isAndroid;
assert foldl (pass: { assertion, message }: if assertion final then pass else throw message) true (
final.parsed.abi.assertions or [ ]
);
final;
in

View file

@ -7,16 +7,23 @@ let
all = [
# Cygwin
"i686-cygwin" "x86_64-cygwin"
"i686-cygwin"
"x86_64-cygwin"
# Darwin
"x86_64-darwin" "i686-darwin" "aarch64-darwin" "armv7a-darwin"
"x86_64-darwin"
"i686-darwin"
"aarch64-darwin"
"armv7a-darwin"
# FreeBSD
"i686-freebsd13" "x86_64-freebsd13"
"i686-freebsd13"
"x86_64-freebsd13"
# Genode
"aarch64-genode" "i686-genode" "x86_64-genode"
"aarch64-genode"
"i686-genode"
"x86_64-genode"
# illumos
"x86_64-solaris"
@ -25,44 +32,90 @@ let
"javascript-ghcjs"
# Linux
"aarch64-linux" "armv5tel-linux" "armv6l-linux" "armv7a-linux"
"armv7l-linux" "i686-linux" "loongarch64-linux" "m68k-linux" "microblaze-linux"
"microblazeel-linux" "mips-linux" "mips64-linux" "mips64el-linux"
"mipsel-linux" "powerpc64-linux" "powerpc64le-linux" "riscv32-linux"
"riscv64-linux" "s390-linux" "s390x-linux" "x86_64-linux"
"aarch64-linux"
"armv5tel-linux"
"armv6l-linux"
"armv7a-linux"
"armv7l-linux"
"i686-linux"
"loongarch64-linux"
"m68k-linux"
"microblaze-linux"
"microblazeel-linux"
"mips-linux"
"mips64-linux"
"mips64el-linux"
"mipsel-linux"
"powerpc64-linux"
"powerpc64le-linux"
"riscv32-linux"
"riscv64-linux"
"s390-linux"
"s390x-linux"
"x86_64-linux"
# MMIXware
"mmix-mmixware"
# NetBSD
"aarch64-netbsd" "armv6l-netbsd" "armv7a-netbsd" "armv7l-netbsd"
"i686-netbsd" "m68k-netbsd" "mipsel-netbsd" "powerpc-netbsd"
"riscv32-netbsd" "riscv64-netbsd" "x86_64-netbsd"
"aarch64-netbsd"
"armv6l-netbsd"
"armv7a-netbsd"
"armv7l-netbsd"
"i686-netbsd"
"m68k-netbsd"
"mipsel-netbsd"
"powerpc-netbsd"
"riscv32-netbsd"
"riscv64-netbsd"
"x86_64-netbsd"
# none
"aarch64_be-none" "aarch64-none" "arm-none" "armv6l-none" "avr-none" "i686-none"
"microblaze-none" "microblazeel-none" "mips-none" "mips64-none" "msp430-none" "or1k-none" "m68k-none"
"powerpc-none" "powerpcle-none" "riscv32-none" "riscv64-none" "rx-none"
"s390-none" "s390x-none" "vc4-none" "x86_64-none"
"aarch64_be-none"
"aarch64-none"
"arm-none"
"armv6l-none"
"avr-none"
"i686-none"
"microblaze-none"
"microblazeel-none"
"mips-none"
"mips64-none"
"msp430-none"
"or1k-none"
"m68k-none"
"powerpc-none"
"powerpcle-none"
"riscv32-none"
"riscv64-none"
"rx-none"
"s390-none"
"s390x-none"
"vc4-none"
"x86_64-none"
# OpenBSD
"i686-openbsd" "x86_64-openbsd"
"i686-openbsd"
"x86_64-openbsd"
# Redox
"x86_64-redox"
# WASI
"wasm64-wasi" "wasm32-wasi"
"wasm64-wasi"
"wasm32-wasi"
# Windows
"x86_64-windows" "i686-windows"
"x86_64-windows"
"i686-windows"
];
allParsed = map parse.mkSystemFromString all;
filterDoubles = f: map parse.doubleFromSystem (lists.filter f allParsed);
in {
in
{
inherit all;
none = [ ];
@ -96,13 +149,35 @@ in {
darwin = filterDoubles predicates.isDarwin;
freebsd = filterDoubles predicates.isFreeBSD;
# Should be better, but MinGW is unclear.
gnu = filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnu; })
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnueabi; })
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnueabihf; })
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnuabin32; })
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnuabi64; })
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnuabielfv1; })
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnuabielfv2; });
gnu =
filterDoubles (matchAttrs {
kernel = parse.kernels.linux;
abi = parse.abis.gnu;
})
++ filterDoubles (matchAttrs {
kernel = parse.kernels.linux;
abi = parse.abis.gnueabi;
})
++ filterDoubles (matchAttrs {
kernel = parse.kernels.linux;
abi = parse.abis.gnueabihf;
})
++ filterDoubles (matchAttrs {
kernel = parse.kernels.linux;
abi = parse.abis.gnuabin32;
})
++ filterDoubles (matchAttrs {
kernel = parse.kernels.linux;
abi = parse.abis.gnuabi64;
})
++ filterDoubles (matchAttrs {
kernel = parse.kernels.linux;
abi = parse.abis.gnuabielfv1;
})
++ filterDoubles (matchAttrs {
kernel = parse.kernels.linux;
abi = parse.abis.gnuabielfv2;
});
illumos = filterDoubles predicates.isSunOS;
linux = filterDoubles predicates.isLinux;
netbsd = filterDoubles predicates.isNetBSD;
@ -115,5 +190,18 @@ in {
embedded = filterDoubles predicates.isNone;
mesaPlatforms = ["i686-linux" "x86_64-linux" "x86_64-darwin" "armv5tel-linux" "armv6l-linux" "armv7l-linux" "armv7a-linux" "aarch64-linux" "powerpc64-linux" "powerpc64le-linux" "aarch64-darwin" "riscv64-linux"];
mesaPlatforms = [
"i686-linux"
"x86_64-linux"
"x86_64-darwin"
"armv5tel-linux"
"armv6l-linux"
"armv7l-linux"
"armv7a-linux"
"aarch64-linux"
"powerpc64-linux"
"powerpc64le-linux"
"aarch64-darwin"
"riscv64-linux"
];
}

View file

@ -5,9 +5,7 @@
let
platforms = import ./platforms.nix { inherit lib; };
riscv = bits: {
config = "riscv${bits}-unknown-linux-gnu";
};
riscv = bits: { config = "riscv${bits}-unknown-linux-gnu"; };
in
rec {
@ -26,7 +24,9 @@ rec {
};
ppc64-musl = {
config = "powerpc64-unknown-linux-musl";
gcc = { abi = "elfv2"; };
gcc = {
abi = "elfv2";
};
};
sheevaplug = {
@ -95,16 +95,28 @@ rec {
} // platforms.fuloong2f_n32;
# can execute on 32bit chip
mips-linux-gnu = { config = "mips-unknown-linux-gnu"; } // platforms.gcc_mips32r2_o32;
mipsel-linux-gnu = { config = "mipsel-unknown-linux-gnu"; } // platforms.gcc_mips32r2_o32;
mips-linux-gnu = {
config = "mips-unknown-linux-gnu";
} // platforms.gcc_mips32r2_o32;
mipsel-linux-gnu = {
config = "mipsel-unknown-linux-gnu";
} // platforms.gcc_mips32r2_o32;
# require 64bit chip (for more registers, 64-bit floating point, 64-bit "long long") but use 32bit pointers
mips64-linux-gnuabin32 = { config = "mips64-unknown-linux-gnuabin32"; } // platforms.gcc_mips64r2_n32;
mips64el-linux-gnuabin32 = { config = "mips64el-unknown-linux-gnuabin32"; } // platforms.gcc_mips64r2_n32;
mips64-linux-gnuabin32 = {
config = "mips64-unknown-linux-gnuabin32";
} // platforms.gcc_mips64r2_n32;
mips64el-linux-gnuabin32 = {
config = "mips64el-unknown-linux-gnuabin32";
} // platforms.gcc_mips64r2_n32;
# 64bit pointers
mips64-linux-gnuabi64 = { config = "mips64-unknown-linux-gnuabi64"; } // platforms.gcc_mips64r2_64;
mips64el-linux-gnuabi64 = { config = "mips64el-unknown-linux-gnuabi64"; } // platforms.gcc_mips64r2_64;
mips64-linux-gnuabi64 = {
config = "mips64-unknown-linux-gnuabi64";
} // platforms.gcc_mips64r2_64;
mips64el-linux-gnuabi64 = {
config = "mips64el-unknown-linux-gnuabi64";
} // platforms.gcc_mips64r2_64;
muslpi = raspberryPi // {
config = "armv6l-unknown-linux-musleabihf";
@ -114,12 +126,20 @@ rec {
config = "aarch64-unknown-linux-musl";
};
gnu64 = { config = "x86_64-unknown-linux-gnu"; };
gnu64 = {
config = "x86_64-unknown-linux-gnu";
};
gnu64_simplekernel = gnu64 // platforms.pc_simplekernel; # see test/cross/default.nix
gnu32 = { config = "i686-unknown-linux-gnu"; };
gnu32 = {
config = "i686-unknown-linux-gnu";
};
musl64 = { config = "x86_64-unknown-linux-musl"; };
musl32 = { config = "i686-unknown-linux-musl"; };
musl64 = {
config = "x86_64-unknown-linux-musl";
};
musl32 = {
config = "i686-unknown-linux-musl";
};
riscv64 = riscv "64";
riscv32 = riscv "32";

View file

@ -38,123 +38,434 @@ rec {
# `lib.attrsets.matchAttrs`, which requires a match on *all* attributes of
# the product.
isi686 = { cpu = cpuTypes.i686; };
isx86_32 = { cpu = { family = "x86"; bits = 32; }; };
isx86_64 = { cpu = { family = "x86"; bits = 64; }; };
isPower = { cpu = { family = "power"; }; };
isPower64 = { cpu = { family = "power"; bits = 64; }; };
isi686 = {
cpu = cpuTypes.i686;
};
isx86_32 = {
cpu = {
family = "x86";
bits = 32;
};
};
isx86_64 = {
cpu = {
family = "x86";
bits = 64;
};
};
isPower = {
cpu = {
family = "power";
};
};
isPower64 = {
cpu = {
family = "power";
bits = 64;
};
};
# This ABI is the default in NixOS PowerPC64 BE, but not on mainline GCC,
# so it sometimes causes issues in certain packages that makes the wrong
# assumption on the used ABI.
isAbiElfv2 = [
{ abi = { abi = "elfv2"; }; }
{ abi = { name = "musl"; }; cpu = { family = "power"; bits = 64; }; }
{
abi = {
abi = "elfv2";
};
}
{
abi = {
name = "musl";
};
cpu = {
family = "power";
bits = 64;
};
}
];
isx86 = { cpu = { family = "x86"; }; };
isAarch32 = { cpu = { family = "arm"; bits = 32; }; };
isArmv7 = map ({ arch, ... }: { cpu = { inherit arch; }; })
(filter (cpu: hasPrefix "armv7" cpu.arch or "")
(attrValues cpuTypes));
isAarch64 = { cpu = { family = "arm"; bits = 64; }; };
isAarch = { cpu = { family = "arm"; }; };
isMicroBlaze = { cpu = { family = "microblaze"; }; };
isMips = { cpu = { family = "mips"; }; };
isMips32 = { cpu = { family = "mips"; bits = 32; }; };
isMips64 = { cpu = { family = "mips"; bits = 64; }; };
isMips64n32 = { cpu = { family = "mips"; bits = 64; }; abi = { abi = "n32"; }; };
isMips64n64 = { cpu = { family = "mips"; bits = 64; }; abi = { abi = "64"; }; };
isMmix = { cpu = { family = "mmix"; }; };
isRiscV = { cpu = { family = "riscv"; }; };
isRiscV32 = { cpu = { family = "riscv"; bits = 32; }; };
isRiscV64 = { cpu = { family = "riscv"; bits = 64; }; };
isRx = { cpu = { family = "rx"; }; };
isSparc = { cpu = { family = "sparc"; }; };
isSparc64 = { cpu = { family = "sparc"; bits = 64; }; };
isWasm = { cpu = { family = "wasm"; }; };
isMsp430 = { cpu = { family = "msp430"; }; };
isVc4 = { cpu = { family = "vc4"; }; };
isAvr = { cpu = { family = "avr"; }; };
isAlpha = { cpu = { family = "alpha"; }; };
isOr1k = { cpu = { family = "or1k"; }; };
isM68k = { cpu = { family = "m68k"; }; };
isS390 = { cpu = { family = "s390"; }; };
isS390x = { cpu = { family = "s390"; bits = 64; }; };
isLoongArch64 = { cpu = { family = "loongarch"; bits = 64; }; };
isJavaScript = { cpu = cpuTypes.javascript; };
isx86 = {
cpu = {
family = "x86";
};
};
isAarch32 = {
cpu = {
family = "arm";
bits = 32;
};
};
isArmv7 = map (
{ arch, ... }:
{
cpu = {
inherit arch;
};
}
) (filter (cpu: hasPrefix "armv7" cpu.arch or "") (attrValues cpuTypes));
isAarch64 = {
cpu = {
family = "arm";
bits = 64;
};
};
isAarch = {
cpu = {
family = "arm";
};
};
isMicroBlaze = {
cpu = {
family = "microblaze";
};
};
isMips = {
cpu = {
family = "mips";
};
};
isMips32 = {
cpu = {
family = "mips";
bits = 32;
};
};
isMips64 = {
cpu = {
family = "mips";
bits = 64;
};
};
isMips64n32 = {
cpu = {
family = "mips";
bits = 64;
};
abi = {
abi = "n32";
};
};
isMips64n64 = {
cpu = {
family = "mips";
bits = 64;
};
abi = {
abi = "64";
};
};
isMmix = {
cpu = {
family = "mmix";
};
};
isRiscV = {
cpu = {
family = "riscv";
};
};
isRiscV32 = {
cpu = {
family = "riscv";
bits = 32;
};
};
isRiscV64 = {
cpu = {
family = "riscv";
bits = 64;
};
};
isRx = {
cpu = {
family = "rx";
};
};
isSparc = {
cpu = {
family = "sparc";
};
};
isSparc64 = {
cpu = {
family = "sparc";
bits = 64;
};
};
isWasm = {
cpu = {
family = "wasm";
};
};
isMsp430 = {
cpu = {
family = "msp430";
};
};
isVc4 = {
cpu = {
family = "vc4";
};
};
isAvr = {
cpu = {
family = "avr";
};
};
isAlpha = {
cpu = {
family = "alpha";
};
};
isOr1k = {
cpu = {
family = "or1k";
};
};
isM68k = {
cpu = {
family = "m68k";
};
};
isS390 = {
cpu = {
family = "s390";
};
};
isS390x = {
cpu = {
family = "s390";
bits = 64;
};
};
isLoongArch64 = {
cpu = {
family = "loongarch";
bits = 64;
};
};
isJavaScript = {
cpu = cpuTypes.javascript;
};
is32bit = { cpu = { bits = 32; }; };
is64bit = { cpu = { bits = 64; }; };
isILP32 = [ { cpu = { family = "wasm"; bits = 32; }; } ] ++
map (a: { abi = { abi = a; }; }) [ "n32" "ilp32" "x32" ];
isBigEndian = { cpu = { significantByte = significantBytes.bigEndian; }; };
isLittleEndian = { cpu = { significantByte = significantBytes.littleEndian; }; };
is32bit = {
cpu = {
bits = 32;
};
};
is64bit = {
cpu = {
bits = 64;
};
};
isILP32 =
[
{
cpu = {
family = "wasm";
bits = 32;
};
}
]
++ map
(a: {
abi = {
abi = a;
};
})
[
"n32"
"ilp32"
"x32"
];
isBigEndian = {
cpu = {
significantByte = significantBytes.bigEndian;
};
};
isLittleEndian = {
cpu = {
significantByte = significantBytes.littleEndian;
};
};
isBSD = { kernel = { families = { inherit (kernelFamilies) bsd; }; }; };
isDarwin = { kernel = { families = { inherit (kernelFamilies) darwin; }; }; };
isUnix = [ isBSD isDarwin isLinux isSunOS isCygwin isRedox ];
isBSD = {
kernel = {
families = {
inherit (kernelFamilies) bsd;
};
};
};
isDarwin = {
kernel = {
families = {
inherit (kernelFamilies) darwin;
};
};
};
isUnix = [
isBSD
isDarwin
isLinux
isSunOS
isCygwin
isRedox
];
isMacOS = { kernel = kernels.macos; };
isiOS = { kernel = kernels.ios; };
isLinux = { kernel = kernels.linux; };
isSunOS = { kernel = kernels.solaris; };
isFreeBSD = { kernel = { name = "freebsd"; }; };
isNetBSD = { kernel = kernels.netbsd; };
isOpenBSD = { kernel = kernels.openbsd; };
isWindows = { kernel = kernels.windows; };
isCygwin = { kernel = kernels.windows; abi = abis.cygnus; };
isMinGW = { kernel = kernels.windows; abi = abis.gnu; };
isWasi = { kernel = kernels.wasi; };
isRedox = { kernel = kernels.redox; };
isGhcjs = { kernel = kernels.ghcjs; };
isGenode = { kernel = kernels.genode; };
isNone = { kernel = kernels.none; };
isMacOS = {
kernel = kernels.macos;
};
isiOS = {
kernel = kernels.ios;
};
isLinux = {
kernel = kernels.linux;
};
isSunOS = {
kernel = kernels.solaris;
};
isFreeBSD = {
kernel = {
name = "freebsd";
};
};
isNetBSD = {
kernel = kernels.netbsd;
};
isOpenBSD = {
kernel = kernels.openbsd;
};
isWindows = {
kernel = kernels.windows;
};
isCygwin = {
kernel = kernels.windows;
abi = abis.cygnus;
};
isMinGW = {
kernel = kernels.windows;
abi = abis.gnu;
};
isWasi = {
kernel = kernels.wasi;
};
isRedox = {
kernel = kernels.redox;
};
isGhcjs = {
kernel = kernels.ghcjs;
};
isGenode = {
kernel = kernels.genode;
};
isNone = {
kernel = kernels.none;
};
isAndroid = [ { abi = abis.android; } { abi = abis.androideabi; } ];
isGnu = with abis; map (a: { abi = a; }) [ gnuabi64 gnuabin32 gnu gnueabi gnueabihf gnuabielfv1 gnuabielfv2 ];
isMusl = with abis; map (a: { abi = a; }) [ musl musleabi musleabihf muslabin32 muslabi64 ];
isUClibc = with abis; map (a: { abi = a; }) [ uclibc uclibceabi uclibceabihf ];
isAndroid = [
{ abi = abis.android; }
{ abi = abis.androideabi; }
];
isGnu =
with abis;
map (a: { abi = a; }) [
gnuabi64
gnuabin32
gnu
gnueabi
gnueabihf
gnuabielfv1
gnuabielfv2
];
isMusl =
with abis;
map (a: { abi = a; }) [
musl
musleabi
musleabihf
muslabin32
muslabi64
];
isUClibc =
with abis;
map (a: { abi = a; }) [
uclibc
uclibceabi
uclibceabihf
];
isEfi = [
{ cpu = { family = "arm"; version = "6"; }; }
{ cpu = { family = "arm"; version = "7"; }; }
{ cpu = { family = "arm"; version = "8"; }; }
{ cpu = { family = "riscv"; }; }
{ cpu = { family = "x86"; }; }
{
cpu = {
family = "arm";
version = "6";
};
}
{
cpu = {
family = "arm";
version = "7";
};
}
{
cpu = {
family = "arm";
version = "8";
};
}
{
cpu = {
family = "riscv";
};
}
{
cpu = {
family = "x86";
};
}
];
isElf = { kernel.execFormat = execFormats.elf; };
isMacho = { kernel.execFormat = execFormats.macho; };
isElf = {
kernel.execFormat = execFormats.elf;
};
isMacho = {
kernel.execFormat = execFormats.macho;
};
};
# given two patterns, return a pattern which is their logical AND.
# Since a pattern is a list-of-disjuncts, this needs to
patternLogicalAnd = pat1_: pat2_:
patternLogicalAnd =
pat1_: pat2_:
let
# patterns can be either a list or a (bare) singleton; turn
# them into singletons for uniform handling
pat1 = toList pat1_;
pat2 = toList pat2_;
in
concatMap (attr1:
map (attr2:
recursiveUpdateUntil
(path: subattr1: subattr2:
if (builtins.intersectAttrs subattr1 subattr2) == {} || subattr1 == subattr2
then true
else throw ''
concatMap (
attr1:
map (
attr2:
recursiveUpdateUntil (
path: subattr1: subattr2:
if (builtins.intersectAttrs subattr1 subattr2) == { } || subattr1 == subattr2 then
true
else
throw ''
pattern conflict at path ${toString path}:
${toJSON subattr1}
${toJSON subattr2}
'')
attr1
attr2
)
pat2)
pat1;
''
) attr1 attr2
) pat2
) pat1;
matchAnyAttrs = patterns:
if isList patterns then attrs: any (pattern: matchAttrs pattern attrs) patterns
else matchAttrs patterns;
matchAnyAttrs =
patterns:
if isList patterns then
attrs: any (pattern: matchAttrs pattern attrs) patterns
else
matchAttrs patterns;
predicates = mapAttrs (_: matchAnyAttrs) patterns;
@ -164,6 +475,8 @@ rec {
# apply only to the `parsed` field.
platformPatterns = mapAttrs (_: p: { parsed = { }; } // p) {
isStatic = { isStatic = true; };
isStatic = {
isStatic = true;
};
};
}

View file

@ -55,19 +55,23 @@ let
types
;
setTypes = type:
mapAttrs (name: value:
setTypes =
type:
mapAttrs (
name: value:
assert type.check value;
setType type.name ({ inherit name; } // value));
setType type.name ({ inherit name; } // value)
);
# gnu-config will ignore the portion of a triple matching the
# regex `e?abi.*$` when determining the validity of a triple. In
# other words, `i386-linuxabichickenlips` is a valid triple.
removeAbiSuffix = x:
let found = match "(.*)e?abi.*" x;
in if found == null
then x
else elemAt found 0;
removeAbiSuffix =
x:
let
found = match "(.*)e?abi.*" x;
in
if found == null then x else elemAt found 0;
in
@ -91,7 +95,13 @@ rec {
################################################################################
# Reasonable power of 2
types.bitWidth = enum [ 8 16 32 64 128 ];
types.bitWidth = enum [
8
16
32
64
128
];
################################################################################
@ -99,87 +109,307 @@ rec {
name = "cpu-type";
description = "instruction set architecture name and information";
merge = mergeOneOption;
check = x: types.bitWidth.check x.bits
&& (if 8 < x.bits
then types.significantByte.check x.significantByte
else !(x ? significantByte));
check =
x:
types.bitWidth.check x.bits
&& (if 8 < x.bits then types.significantByte.check x.significantByte else !(x ? significantByte));
};
types.cpuType = enum (attrValues cpuTypes);
cpuTypes = let inherit (significantBytes) bigEndian littleEndian; in setTypes types.openCpuType {
arm = { bits = 32; significantByte = littleEndian; family = "arm"; };
armv5tel = { bits = 32; significantByte = littleEndian; family = "arm"; version = "5"; arch = "armv5t"; };
armv6m = { bits = 32; significantByte = littleEndian; family = "arm"; version = "6"; arch = "armv6-m"; };
armv6l = { bits = 32; significantByte = littleEndian; family = "arm"; version = "6"; arch = "armv6"; };
armv7a = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; arch = "armv7-a"; };
armv7r = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; arch = "armv7-r"; };
armv7m = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; arch = "armv7-m"; };
armv7l = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; arch = "armv7"; };
armv8a = { bits = 32; significantByte = littleEndian; family = "arm"; version = "8"; arch = "armv8-a"; };
armv8r = { bits = 32; significantByte = littleEndian; family = "arm"; version = "8"; arch = "armv8-a"; };
armv8m = { bits = 32; significantByte = littleEndian; family = "arm"; version = "8"; arch = "armv8-m"; };
aarch64 = { bits = 64; significantByte = littleEndian; family = "arm"; version = "8"; arch = "armv8-a"; };
aarch64_be = { bits = 64; significantByte = bigEndian; family = "arm"; version = "8"; arch = "armv8-a"; };
cpuTypes =
let
inherit (significantBytes) bigEndian littleEndian;
in
setTypes types.openCpuType {
arm = {
bits = 32;
significantByte = littleEndian;
family = "arm";
};
armv5tel = {
bits = 32;
significantByte = littleEndian;
family = "arm";
version = "5";
arch = "armv5t";
};
armv6m = {
bits = 32;
significantByte = littleEndian;
family = "arm";
version = "6";
arch = "armv6-m";
};
armv6l = {
bits = 32;
significantByte = littleEndian;
family = "arm";
version = "6";
arch = "armv6";
};
armv7a = {
bits = 32;
significantByte = littleEndian;
family = "arm";
version = "7";
arch = "armv7-a";
};
armv7r = {
bits = 32;
significantByte = littleEndian;
family = "arm";
version = "7";
arch = "armv7-r";
};
armv7m = {
bits = 32;
significantByte = littleEndian;
family = "arm";
version = "7";
arch = "armv7-m";
};
armv7l = {
bits = 32;
significantByte = littleEndian;
family = "arm";
version = "7";
arch = "armv7";
};
armv8a = {
bits = 32;
significantByte = littleEndian;
family = "arm";
version = "8";
arch = "armv8-a";
};
armv8r = {
bits = 32;
significantByte = littleEndian;
family = "arm";
version = "8";
arch = "armv8-a";
};
armv8m = {
bits = 32;
significantByte = littleEndian;
family = "arm";
version = "8";
arch = "armv8-m";
};
aarch64 = {
bits = 64;
significantByte = littleEndian;
family = "arm";
version = "8";
arch = "armv8-a";
};
aarch64_be = {
bits = 64;
significantByte = bigEndian;
family = "arm";
version = "8";
arch = "armv8-a";
};
i386 = { bits = 32; significantByte = littleEndian; family = "x86"; arch = "i386"; };
i486 = { bits = 32; significantByte = littleEndian; family = "x86"; arch = "i486"; };
i586 = { bits = 32; significantByte = littleEndian; family = "x86"; arch = "i586"; };
i686 = { bits = 32; significantByte = littleEndian; family = "x86"; arch = "i686"; };
x86_64 = { bits = 64; significantByte = littleEndian; family = "x86"; arch = "x86-64"; };
i386 = {
bits = 32;
significantByte = littleEndian;
family = "x86";
arch = "i386";
};
i486 = {
bits = 32;
significantByte = littleEndian;
family = "x86";
arch = "i486";
};
i586 = {
bits = 32;
significantByte = littleEndian;
family = "x86";
arch = "i586";
};
i686 = {
bits = 32;
significantByte = littleEndian;
family = "x86";
arch = "i686";
};
x86_64 = {
bits = 64;
significantByte = littleEndian;
family = "x86";
arch = "x86-64";
};
microblaze = { bits = 32; significantByte = bigEndian; family = "microblaze"; };
microblazeel = { bits = 32; significantByte = littleEndian; family = "microblaze"; };
microblaze = {
bits = 32;
significantByte = bigEndian;
family = "microblaze";
};
microblazeel = {
bits = 32;
significantByte = littleEndian;
family = "microblaze";
};
mips = { bits = 32; significantByte = bigEndian; family = "mips"; };
mipsel = { bits = 32; significantByte = littleEndian; family = "mips"; };
mips64 = { bits = 64; significantByte = bigEndian; family = "mips"; };
mips64el = { bits = 64; significantByte = littleEndian; family = "mips"; };
mips = {
bits = 32;
significantByte = bigEndian;
family = "mips";
};
mipsel = {
bits = 32;
significantByte = littleEndian;
family = "mips";
};
mips64 = {
bits = 64;
significantByte = bigEndian;
family = "mips";
};
mips64el = {
bits = 64;
significantByte = littleEndian;
family = "mips";
};
mmix = { bits = 64; significantByte = bigEndian; family = "mmix"; };
mmix = {
bits = 64;
significantByte = bigEndian;
family = "mmix";
};
m68k = { bits = 32; significantByte = bigEndian; family = "m68k"; };
m68k = {
bits = 32;
significantByte = bigEndian;
family = "m68k";
};
powerpc = { bits = 32; significantByte = bigEndian; family = "power"; };
powerpc64 = { bits = 64; significantByte = bigEndian; family = "power"; };
powerpc64le = { bits = 64; significantByte = littleEndian; family = "power"; };
powerpcle = { bits = 32; significantByte = littleEndian; family = "power"; };
powerpc = {
bits = 32;
significantByte = bigEndian;
family = "power";
};
powerpc64 = {
bits = 64;
significantByte = bigEndian;
family = "power";
};
powerpc64le = {
bits = 64;
significantByte = littleEndian;
family = "power";
};
powerpcle = {
bits = 32;
significantByte = littleEndian;
family = "power";
};
riscv32 = { bits = 32; significantByte = littleEndian; family = "riscv"; };
riscv64 = { bits = 64; significantByte = littleEndian; family = "riscv"; };
riscv32 = {
bits = 32;
significantByte = littleEndian;
family = "riscv";
};
riscv64 = {
bits = 64;
significantByte = littleEndian;
family = "riscv";
};
s390 = { bits = 32; significantByte = bigEndian; family = "s390"; };
s390x = { bits = 64; significantByte = bigEndian; family = "s390"; };
s390 = {
bits = 32;
significantByte = bigEndian;
family = "s390";
};
s390x = {
bits = 64;
significantByte = bigEndian;
family = "s390";
};
sparc = { bits = 32; significantByte = bigEndian; family = "sparc"; };
sparc64 = { bits = 64; significantByte = bigEndian; family = "sparc"; };
sparc = {
bits = 32;
significantByte = bigEndian;
family = "sparc";
};
sparc64 = {
bits = 64;
significantByte = bigEndian;
family = "sparc";
};
wasm32 = { bits = 32; significantByte = littleEndian; family = "wasm"; };
wasm64 = { bits = 64; significantByte = littleEndian; family = "wasm"; };
wasm32 = {
bits = 32;
significantByte = littleEndian;
family = "wasm";
};
wasm64 = {
bits = 64;
significantByte = littleEndian;
family = "wasm";
};
alpha = { bits = 64; significantByte = littleEndian; family = "alpha"; };
alpha = {
bits = 64;
significantByte = littleEndian;
family = "alpha";
};
rx = { bits = 32; significantByte = littleEndian; family = "rx"; };
msp430 = { bits = 16; significantByte = littleEndian; family = "msp430"; };
avr = { bits = 8; family = "avr"; };
rx = {
bits = 32;
significantByte = littleEndian;
family = "rx";
};
msp430 = {
bits = 16;
significantByte = littleEndian;
family = "msp430";
};
avr = {
bits = 8;
family = "avr";
};
vc4 = { bits = 32; significantByte = littleEndian; family = "vc4"; };
vc4 = {
bits = 32;
significantByte = littleEndian;
family = "vc4";
};
or1k = { bits = 32; significantByte = bigEndian; family = "or1k"; };
or1k = {
bits = 32;
significantByte = bigEndian;
family = "or1k";
};
loongarch64 = { bits = 64; significantByte = littleEndian; family = "loongarch"; };
loongarch64 = {
bits = 64;
significantByte = littleEndian;
family = "loongarch";
};
javascript = { bits = 32; significantByte = littleEndian; family = "javascript"; };
javascript = {
bits = 32;
significantByte = littleEndian;
family = "javascript";
};
};
# GNU build systems assume that older NetBSD architectures are using a.out.
gnuNetBSDDefaultExecFormat = cpu:
if (cpu.family == "arm" && cpu.bits == 32) ||
(cpu.family == "sparc" && cpu.bits == 32) ||
(cpu.family == "m68k" && cpu.bits == 32) ||
(cpu.family == "x86" && cpu.bits == 32)
then execFormats.aout
else execFormats.elf;
gnuNetBSDDefaultExecFormat =
cpu:
if
(cpu.family == "arm" && cpu.bits == 32)
|| (cpu.family == "sparc" && cpu.bits == 32)
|| (cpu.family == "m68k" && cpu.bits == 32)
|| (cpu.family == "x86" && cpu.bits == 32)
then
execFormats.aout
else
execFormats.elf;
# Determine when two CPUs are compatible with each other. That is,
# can code built for system B run on system A? For that to happen,
@ -197,7 +427,10 @@ rec {
# Note: Since 22.11 the archs of a mode switching CPU are no longer considered
# pairwise compatible. Mode switching implies that binaries built for A
# and B respectively can't be executed at the same time.
isCompatible = with cpuTypes; a: b: any id [
isCompatible =
with cpuTypes;
a: b:
any id [
# x86
(b == i386 && isCompatible a i486)
(b == i486 && isCompatible a i586)
@ -312,37 +545,109 @@ rec {
name = "kernel";
description = "kernel name and information";
merge = mergeOneOption;
check = x: types.execFormat.check x.execFormat
&& all types.kernelFamily.check (attrValues x.families);
check =
x: types.execFormat.check x.execFormat && all types.kernelFamily.check (attrValues x.families);
};
types.kernel = enum (attrValues kernels);
kernels = let
inherit (execFormats) elf pe wasm unknown macho;
kernels =
let
inherit (execFormats)
elf
pe
wasm
unknown
macho
;
inherit (kernelFamilies) bsd darwin;
in setTypes types.openKernel {
in
setTypes types.openKernel {
# TODO(@Ericson2314): Don't want to mass-rebuild yet to keeping 'darwin' as
# the normalized name for macOS.
macos = { execFormat = macho; families = { inherit darwin; }; name = "darwin"; };
ios = { execFormat = macho; families = { inherit darwin; }; };
macos = {
execFormat = macho;
families = {
inherit darwin;
};
name = "darwin";
};
ios = {
execFormat = macho;
families = {
inherit darwin;
};
};
# A tricky thing about FreeBSD is that there is no stable ABI across
# versions. That means that putting in the version as part of the
# config string is paramount.
freebsd12 = { execFormat = elf; families = { inherit bsd; }; name = "freebsd"; version = 12; };
freebsd13 = { execFormat = elf; families = { inherit bsd; }; name = "freebsd"; version = 13; };
linux = { execFormat = elf; families = { }; };
netbsd = { execFormat = elf; families = { inherit bsd; }; };
none = { execFormat = unknown; families = { }; };
openbsd = { execFormat = elf; families = { inherit bsd; }; };
solaris = { execFormat = elf; families = { }; };
wasi = { execFormat = wasm; families = { }; };
redox = { execFormat = elf; families = { }; };
windows = { execFormat = pe; families = { }; };
ghcjs = { execFormat = unknown; families = { }; };
genode = { execFormat = elf; families = { }; };
mmixware = { execFormat = unknown; families = { }; };
} // { # aliases
freebsd12 = {
execFormat = elf;
families = {
inherit bsd;
};
name = "freebsd";
version = 12;
};
freebsd13 = {
execFormat = elf;
families = {
inherit bsd;
};
name = "freebsd";
version = 13;
};
linux = {
execFormat = elf;
families = { };
};
netbsd = {
execFormat = elf;
families = {
inherit bsd;
};
};
none = {
execFormat = unknown;
families = { };
};
openbsd = {
execFormat = elf;
families = {
inherit bsd;
};
};
solaris = {
execFormat = elf;
families = { };
};
wasi = {
execFormat = wasm;
families = { };
};
redox = {
execFormat = elf;
families = { };
};
windows = {
execFormat = pe;
families = { };
};
ghcjs = {
execFormat = unknown;
families = { };
};
genode = {
execFormat = elf;
families = { };
};
mmixware = {
execFormat = unknown;
families = { };
};
}
// {
# aliases
# 'darwin' is the kernel for all of them. We choose macOS by default.
darwin = kernels.macos;
watchos = kernels.ios;
@ -367,8 +672,12 @@ rec {
# Note: eabi is specific to ARM and PowerPC.
# On PowerPC, this corresponds to PPCEABI.
# On ARM, this corresponds to ARMEABI.
eabi = { float = "soft"; };
eabihf = { float = "hard"; };
eabi = {
float = "soft";
};
eabihf = {
float = "hard";
};
# Other architectures should use ELF in embedded situations.
elf = { };
@ -376,7 +685,8 @@ rec {
androideabi = { };
android = {
assertions = [
{ assertion = platform: !platform.isAarch32;
{
assertion = platform: !platform.isAarch32;
message = ''
The "android" ABI is not for 32-bit ARM. Use "androideabi" instead.
'';
@ -384,40 +694,66 @@ rec {
];
};
gnueabi = { float = "soft"; };
gnueabihf = { float = "hard"; };
gnueabi = {
float = "soft";
};
gnueabihf = {
float = "hard";
};
gnu = {
assertions = [
{ assertion = platform: !platform.isAarch32;
{
assertion = platform: !platform.isAarch32;
message = ''
The "gnu" ABI is ambiguous on 32-bit ARM. Use "gnueabi" or "gnueabihf" instead.
'';
}
{ assertion = platform: !(platform.isPower64 && platform.isBigEndian);
{
assertion = platform: !(platform.isPower64 && platform.isBigEndian);
message = ''
The "gnu" ABI is ambiguous on big-endian 64-bit PowerPC. Use "gnuabielfv2" or "gnuabielfv1" instead.
'';
}
];
};
gnuabi64 = { abi = "64"; };
muslabi64 = { abi = "64"; };
gnuabi64 = {
abi = "64";
};
muslabi64 = {
abi = "64";
};
# NOTE: abi=n32 requires a 64-bit MIPS chip! That is not a typo.
# It is basically the 64-bit abi with 32-bit pointers. Details:
# https://www.linux-mips.org/pub/linux/mips/doc/ABI/MIPS-N32-ABI-Handbook.pdf
gnuabin32 = { abi = "n32"; };
muslabin32 = { abi = "n32"; };
gnuabin32 = {
abi = "n32";
};
muslabin32 = {
abi = "n32";
};
gnuabielfv2 = { abi = "elfv2"; };
gnuabielfv1 = { abi = "elfv1"; };
gnuabielfv2 = {
abi = "elfv2";
};
gnuabielfv1 = {
abi = "elfv1";
};
musleabi = { float = "soft"; };
musleabihf = { float = "hard"; };
musleabi = {
float = "soft";
};
musleabihf = {
float = "hard";
};
musl = { };
uclibceabi = { float = "soft"; };
uclibceabihf = { float = "hard"; };
uclibceabi = {
float = "soft";
};
uclibceabihf = {
float = "hard";
};
uclibc = { };
unknown = { };
@ -429,7 +765,13 @@ rec {
name = "system";
description = "fully parsed representation of llvm- or nix-style platform tuple";
merge = mergeOneOption;
check = { cpu, vendor, kernel, abi }:
check =
{
cpu,
vendor,
kernel,
abi,
}:
types.cpuType.check cpu
&& types.vendor.check vendor
&& types.kernel.check kernel
@ -438,63 +780,120 @@ rec {
isSystem = isType "system";
mkSystem = components:
mkSystem =
components:
assert types.parsedPlatform.check components;
setType "system" components;
mkSkeletonFromList = l: {
"1" = if elemAt l 0 == "avr"
then { cpu = elemAt l 0; kernel = "none"; abi = "unknown"; }
else throw "Target specification with 1 components is ambiguous";
mkSkeletonFromList =
l:
{
"1" =
if elemAt l 0 == "avr" then
{
cpu = elemAt l 0;
kernel = "none";
abi = "unknown";
}
else
throw "Target specification with 1 components is ambiguous";
"2" = # We only do 2-part hacks for things Nix already supports
if elemAt l 1 == "cygwin"
then { cpu = elemAt l 0; kernel = "windows"; abi = "cygnus"; }
if elemAt l 1 == "cygwin" then
{
cpu = elemAt l 0;
kernel = "windows";
abi = "cygnus";
}
# MSVC ought to be the default ABI so this case isn't needed. But then it
# becomes difficult to handle the gnu* variants for Aarch32 correctly for
# minGW. So it's easier to make gnu* the default for the MinGW, but
# hack-in MSVC for the non-MinGW case right here.
else if elemAt l 1 == "windows"
then { cpu = elemAt l 0; kernel = "windows"; abi = "msvc"; }
else if (elemAt l 1) == "elf"
then { cpu = elemAt l 0; vendor = "unknown"; kernel = "none"; abi = elemAt l 1; }
else { cpu = elemAt l 0; kernel = elemAt l 1; };
else if elemAt l 1 == "windows" then
{
cpu = elemAt l 0;
kernel = "windows";
abi = "msvc";
}
else if (elemAt l 1) == "elf" then
{
cpu = elemAt l 0;
vendor = "unknown";
kernel = "none";
abi = elemAt l 1;
}
else
{
cpu = elemAt l 0;
kernel = elemAt l 1;
};
"3" =
# cpu-kernel-environment
if elemAt l 1 == "linux" ||
elem (elemAt l 2) ["eabi" "eabihf" "elf" "gnu"]
then {
if
elemAt l 1 == "linux"
|| elem (elemAt l 2) [
"eabi"
"eabihf"
"elf"
"gnu"
]
then
{
cpu = elemAt l 0;
kernel = elemAt l 1;
abi = elemAt l 2;
vendor = "unknown";
}
# cpu-vendor-os
else if elemAt l 1 == "apple" ||
elem (elemAt l 2) [ "wasi" "redox" "mmixware" "ghcjs" "mingw32" ] ||
hasPrefix "freebsd" (elemAt l 2) ||
hasPrefix "netbsd" (elemAt l 2) ||
hasPrefix "genode" (elemAt l 2)
then {
else if
elemAt l 1 == "apple"
|| elem (elemAt l 2) [
"wasi"
"redox"
"mmixware"
"ghcjs"
"mingw32"
]
|| hasPrefix "freebsd" (elemAt l 2)
|| hasPrefix "netbsd" (elemAt l 2)
|| hasPrefix "genode" (elemAt l 2)
then
{
cpu = elemAt l 0;
vendor = elemAt l 1;
kernel = if elemAt l 2 == "mingw32"
then "windows" # autotools breaks on -gnu for window
else elemAt l 2;
kernel =
if elemAt l 2 == "mingw32" then
"windows" # autotools breaks on -gnu for window
else
elemAt l 2;
}
else throw "Target specification with 3 components is ambiguous";
"4" = { cpu = elemAt l 0; vendor = elemAt l 1; kernel = elemAt l 2; abi = elemAt l 3; };
}.${toString (length l)}
else
throw "Target specification with 3 components is ambiguous";
"4" = {
cpu = elemAt l 0;
vendor = elemAt l 1;
kernel = elemAt l 2;
abi = elemAt l 3;
};
}
.${toString (length l)}
or (throw "system string has invalid number of hyphen-separated components");
# This should revert the job done by config.guess from the gcc compiler.
mkSystemFromSkeleton = { cpu
, # Optional, but fallback too complex for here.
mkSystemFromSkeleton =
{
cpu,
# Optional, but fallback too complex for here.
# Inferred below instead.
vendor ? assert false; null
, kernel
, # Also inferred below
abi ? assert false; null
} @ args: let
vendor ?
assert false;
null,
kernel,
# Also inferred below
abi ?
assert false;
null,
}@args:
let
getCpu = name: cpuTypes.${name} or (throw "Unknown CPU type: ${name}");
getVendor = name: vendors.${name} or (throw "Unknown vendor: ${name}");
getKernel = name: kernels.${name} or (throw "Unknown kernel: ${name}");
@ -503,45 +902,73 @@ rec {
parsed = {
cpu = getCpu args.cpu;
vendor =
/**/ if args ? vendor then getVendor args.vendor
else if isDarwin parsed then vendors.apple
else if isWindows parsed then vendors.pc
else vendors.unknown;
kernel = if hasPrefix "darwin" args.kernel then getKernel "darwin"
else if hasPrefix "netbsd" args.kernel then getKernel "netbsd"
else getKernel (removeAbiSuffix args.kernel);
if args ? vendor then
getVendor args.vendor
else if isDarwin parsed then
vendors.apple
else if isWindows parsed then
vendors.pc
else
vendors.unknown;
kernel =
if hasPrefix "darwin" args.kernel then
getKernel "darwin"
else if hasPrefix "netbsd" args.kernel then
getKernel "netbsd"
else
getKernel (removeAbiSuffix args.kernel);
abi =
/**/ if args ? abi then getAbi args.abi
if args ? abi then
getAbi args.abi
else if isLinux parsed || isWindows parsed then
if isAarch32 parsed then
if versionAtLeast (parsed.cpu.version or "0") "6"
then abis.gnueabihf
else abis.gnueabi
if versionAtLeast (parsed.cpu.version or "0") "6" then abis.gnueabihf else abis.gnueabi
# Default ppc64 BE to ELFv2
else if isPower64 parsed && isBigEndian parsed then abis.gnuabielfv2
else abis.gnu
else abis.unknown;
else if isPower64 parsed && isBigEndian parsed then
abis.gnuabielfv2
else
abis.gnu
else
abis.unknown;
};
in mkSystem parsed;
in
mkSystem parsed;
mkSystemFromString = s: mkSystemFromSkeleton (mkSkeletonFromList (splitString "-" s));
kernelName = kernel:
kernel.name + toString (kernel.version or "");
kernelName = kernel: kernel.name + toString (kernel.version or "");
doubleFromSystem = { cpu, kernel, abi, ... }:
/**/ if abi == abis.cygnus then "${cpu.name}-cygwin"
else if kernel.families ? darwin then "${cpu.name}-darwin"
else "${cpu.name}-${kernelName kernel}";
doubleFromSystem =
{
cpu,
kernel,
abi,
...
}:
if abi == abis.cygnus then
"${cpu.name}-cygwin"
else if kernel.families ? darwin then
"${cpu.name}-darwin"
else
"${cpu.name}-${kernelName kernel}";
tripleFromSystem = { cpu, vendor, kernel, abi, ... } @ sys: assert isSystem sys; let
optExecFormat =
optionalString (kernel.name == "netbsd" &&
gnuNetBSDDefaultExecFormat cpu != kernel.execFormat)
kernel.execFormat.name;
tripleFromSystem =
{
cpu,
vendor,
kernel,
abi,
...
}@sys:
assert isSystem sys;
let
optExecFormat = optionalString (
kernel.name == "netbsd" && gnuNetBSDDefaultExecFormat cpu != kernel.execFormat
) kernel.execFormat.name;
optAbi = optionalString (abi != abis.unknown) "-${abi.name}";
in "${cpu.name}-${vendor.name}-${kernelName kernel}${optExecFormat}${optAbi}";
in
"${cpu.name}-${vendor.name}-${kernelName kernel}${optExecFormat}${optAbi}";
################################################################################

View file

@ -18,9 +18,7 @@ rec {
};
};
pc_simplekernel = lib.recursiveUpdate pc {
linux-kernel.autoModules = false;
};
pc_simplekernel = lib.recursiveUpdate pc { linux-kernel.autoModules = false; };
powernv = {
linux-kernel = {
@ -490,12 +488,42 @@ rec {
};
# can execute on 32bit chip
gcc_mips32r2_o32 = { gcc = { arch = "mips32r2"; abi = "32"; }; };
gcc_mips32r6_o32 = { gcc = { arch = "mips32r6"; abi = "32"; }; };
gcc_mips64r2_n32 = { gcc = { arch = "mips64r2"; abi = "n32"; }; };
gcc_mips64r6_n32 = { gcc = { arch = "mips64r6"; abi = "n32"; }; };
gcc_mips64r2_64 = { gcc = { arch = "mips64r2"; abi = "64"; }; };
gcc_mips64r6_64 = { gcc = { arch = "mips64r6"; abi = "64"; }; };
gcc_mips32r2_o32 = {
gcc = {
arch = "mips32r2";
abi = "32";
};
};
gcc_mips32r6_o32 = {
gcc = {
arch = "mips32r6";
abi = "32";
};
};
gcc_mips64r2_n32 = {
gcc = {
arch = "mips64r2";
abi = "n32";
};
};
gcc_mips64r6_n32 = {
gcc = {
arch = "mips64r6";
abi = "n32";
};
};
gcc_mips64r2_64 = {
gcc = {
arch = "mips64r2";
abi = "64";
};
};
gcc_mips64r6_64 = {
gcc = {
arch = "mips64r6";
abi = "64";
};
};
# based on:
# https://www.mail-archive.com/qemu-discuss@nongnu.org/msg05179.html
@ -546,27 +574,38 @@ rec {
# This function takes a minimally-valid "platform" and returns an
# attrset containing zero or more additional attrs which should be
# included in the platform in order to further elaborate it.
select = platform:
select =
platform:
# x86
/**/ if platform.isx86 then pc
if platform.isx86 then
pc
# ARM
else if platform.isAarch32 then let
else if platform.isAarch32 then
let
version = platform.parsed.cpu.version or null;
in if version == null then pc
else if lib.versionOlder version "6" then sheevaplug
else if lib.versionOlder version "7" then raspberrypi
else armv7l-hf-multiplatform
in
if version == null then
pc
else if lib.versionOlder version "6" then
sheevaplug
else if lib.versionOlder version "7" then
raspberrypi
else
armv7l-hf-multiplatform
else if platform.isAarch64 then
if platform.isDarwin then apple-m1
else aarch64-multiplatform
if platform.isDarwin then apple-m1 else aarch64-multiplatform
else if platform.isRiscV then riscv-multiplatform
else if platform.isRiscV then
riscv-multiplatform
else if platform.parsed.cpu == lib.systems.parse.cpuTypes.mipsel then (import ./examples.nix { inherit lib; }).mipsel-linux-gnu
else if platform.parsed.cpu == lib.systems.parse.cpuTypes.mipsel then
(import ./examples.nix { inherit lib; }).mipsel-linux-gnu
else if platform.parsed.cpu == lib.systems.parse.cpuTypes.powerpc64le then powernv
else if platform.parsed.cpu == lib.systems.parse.cpuTypes.powerpc64le then
powernv
else { };
else
{ };
}

View file

@ -1,7 +1,10 @@
# Throws an error if any of our lib tests fail.
let tests = [ "misc" "systems" ];
let
tests = [
"misc"
"systems"
];
all = builtins.concatLists (map (f: import (./. + "/${f}.nix")) tests);
in if all == []
then null
else throw (builtins.toJSON all)
in
if all == [ ] then null else throw (builtins.toJSON all)

View file

@ -1,11 +1,10 @@
{ lib, ... }:
let
inherit (lib) types;
in {
in
{
options = {
name = lib.mkOption {
type = types.str;
};
name = lib.mkOption { type = types.str; };
email = lib.mkOption {
type = types.nullOr types.str;
default = null;
@ -23,9 +22,7 @@ in {
default = null;
};
keys = lib.mkOption {
type = types.listOf (types.submodule {
options.fingerprint = lib.mkOption { type = types.str; };
});
type = types.listOf (types.submodule { options.fingerprint = lib.mkOption { type = types.str; }; });
default = [ ];
};
};

View file

@ -1,16 +1,23 @@
# to run these tests (and the others)
# nix-build nixpkgs/lib/tests/release.nix
# These tests should stay in sync with the comment in maintainers/maintainers-list.nix
{ # The pkgs used for dependencies for the testing itself
pkgs ? import ../.. {}
, lib ? pkgs.lib
{
# The pkgs used for dependencies for the testing itself
pkgs ? import ../.. { },
lib ? pkgs.lib,
}:
let
checkMaintainer = handle: uncheckedAttrs:
checkMaintainer =
handle: uncheckedAttrs:
let
prefix = [ "lib" "maintainers" handle ];
checkedAttrs = (lib.modules.evalModules {
prefix = [
"lib"
"maintainers"
handle
];
checkedAttrs =
(lib.modules.evalModules {
inherit prefix;
modules = [
./maintainer-module.nix
@ -21,7 +28,8 @@ let
];
}).config;
checks = lib.optional (checkedAttrs.github != null && checkedAttrs.githubId == null) ''
checks =
lib.optional (checkedAttrs.github != null && checkedAttrs.githubId == null) ''
echo ${lib.escapeShellArg (lib.showOption prefix)}': If `github` is specified, `githubId` must be too.'
# Calling this too often would hit non-authenticated API limits, but this
# shouldn't happen since such errors will get fixed rather quickly
@ -29,25 +37,40 @@ let
id=$(jq -r '.id' <<< "$info")
echo "The GitHub ID for GitHub user ${checkedAttrs.github} is $id:"
echo -e " githubId = $id;\n"
'' ++ lib.optional (checkedAttrs.email == null && checkedAttrs.github == null && checkedAttrs.matrix == null) ''
''
++
lib.optional
(checkedAttrs.email == null && checkedAttrs.github == null && checkedAttrs.matrix == null)
''
echo ${lib.escapeShellArg (lib.showOption prefix)}': At least one of `email`, `github` or `matrix` must be specified, so that users know how to reach you.'
'' ++ lib.optional (checkedAttrs.email != null && lib.hasSuffix "noreply.github.com" checkedAttrs.email) ''
''
++
lib.optional (checkedAttrs.email != null && lib.hasSuffix "noreply.github.com" checkedAttrs.email)
''
echo ${lib.escapeShellArg (lib.showOption prefix)}': If an email address is given, it should allow people to reach you. If you do not want that, you can just provide `github` or `matrix` instead.'
'';
in lib.deepSeq checkedAttrs checks;
in
lib.deepSeq checkedAttrs checks;
missingGithubIds = lib.concatLists (lib.mapAttrsToList checkMaintainer lib.maintainers);
success = pkgs.runCommand "checked-maintainers-success" { } ">$out";
failure = pkgs.runCommand "checked-maintainers-failure" {
nativeBuildInputs = [ pkgs.curl pkgs.jq ];
failure =
pkgs.runCommand "checked-maintainers-failure"
{
nativeBuildInputs = [
pkgs.curl
pkgs.jq
];
outputHash = "sha256:${lib.fakeSha256}";
outputHAlgo = "sha256";
outputHashMode = "flat";
SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
} ''
}
''
${lib.concatStringsSep "\n" missingGithubIds}
exit 1
'';
in if missingGithubIds == [] then success else failure
in
if missingGithubIds == [ ] then success else failure

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,9 @@
{ lib, ... }: {
options.dummy = lib.mkOption { type = lib.types.anything; default = {}; };
{ lib, ... }:
{
options.dummy = lib.mkOption {
type = lib.types.anything;
default = { };
};
freeformType =
let
a = lib.types.attrsOf (lib.types.submodule { options.bar = lib.mkOption { }; });
@ -7,8 +11,6 @@
# modifying types like this breaks type merging.
# This test makes sure that type merging is not performed when only a single declaration exists.
# Don't modify types in practice!
a // {
merge = loc: defs: { freeformItems = a.merge loc defs; };
};
a // { merge = loc: defs: { freeformItems = a.merge loc defs; }; };
config.foo.bar = "ok";
}

View file

@ -46,14 +46,16 @@ in
# Disable the aliased option with a high priority so it
# should override the next import.
( { config, lib, ... }:
(
{ config, lib, ... }:
{
enableAlias = mkForce false;
}
)
# Enable the normal (non-aliased) option.
( { config, lib, ... }:
(
{ config, lib, ... }:
{
enable = true;
}

View file

@ -46,14 +46,16 @@ in
# Disable the aliased option, but with a default (low) priority so it
# should be able to be overridden by the next import.
( { config, lib, ... }:
(
{ config, lib, ... }:
{
enableAlias = mkDefault false;
}
)
# Enable the normal (non-aliased) option.
( { config, lib, ... }:
(
{ config, lib, ... }:
{
enable = true;
}

View file

@ -1,7 +1,6 @@
{ lib, config, ... }: {
options.conditionalWorks = lib.mkOption {
default = ! config.value ? foo;
};
{ lib, config, ... }:
{
options.conditionalWorks = lib.mkOption { default = !config.value ? foo; };
config.value.foo = lib.mkIf false "should not be defined";
}

View file

@ -1,7 +1,6 @@
{ lib, config, ... }: {
options.isLazy = lib.mkOption {
default = ! config.value ? foo;
};
{ lib, config, ... }:
{
options.isLazy = lib.mkOption { default = !config.value ? foo; };
config.value.bar = throw "is not lazy";
}

View file

@ -1,14 +1,24 @@
{ lib, ... }: {
{ lib, ... }:
{
options.value = lib.mkOption {
type = lib.types.lazyAttrsOf lib.types.boolByOr;
};
options.value = lib.mkOption { type = lib.types.lazyAttrsOf lib.types.boolByOr; };
config.value = {
falseFalse = lib.mkMerge [ false false ];
trueFalse = lib.mkMerge [ true false ];
falseTrue = lib.mkMerge [ false true ];
trueTrue = lib.mkMerge [ true true ];
falseFalse = lib.mkMerge [
false
false
];
trueFalse = lib.mkMerge [
true
false
];
falseTrue = lib.mkMerge [
false
true
];
trueTrue = lib.mkMerge [
true
true
];
};
}

View file

@ -1,4 +1,5 @@
{ lib, ... }: {
{ lib, ... }:
{
options = {
sub = {
nixosOk = lib.mkOption {
@ -40,16 +41,12 @@
];
config = {
_module.freeformType = lib.types.anything;
ok =
lib.evalModules {
ok = lib.evalModules {
class = "nixos";
modules = [
./module-class-is-nixos.nix
];
modules = [ ./module-class-is-nixos.nix ];
};
fail =
lib.evalModules {
fail = lib.evalModules {
class = "nixos";
modules = [
./module-class-is-nixos.nix
@ -57,12 +54,12 @@
];
};
fail-anon =
lib.evalModules {
fail-anon = lib.evalModules {
class = "nixos";
modules = [
./module-class-is-nixos.nix
{ _file = "foo.nix#darwinModules.default";
{
_file = "foo.nix#darwinModules.default";
_class = "darwin";
config = { };
imports = [ ];
@ -70,7 +67,11 @@
];
};
sub.nixosOk = { _class = "nixos"; };
sub.nixosFail = { imports = [ ./module-class-is-darwin.nix ]; };
sub.nixosOk = {
_class = "nixos";
};
sub.nixosFail = {
imports = [ ./module-class-is-darwin.nix ];
};
};
}

View file

@ -1,24 +1,22 @@
{ lib, options, ... }:
let discardPositions = lib.mapAttrs (k: v: v);
let
discardPositions = lib.mapAttrs (k: v: v);
in
# unsafeGetAttrPos is unspecified best-effort behavior, so we only want to consider this test on an evaluator that satisfies some basic assumptions about this function.
assert builtins.unsafeGetAttrPos "a" { a = true; } != null;
assert builtins.unsafeGetAttrPos "a" (discardPositions { a = true; }) == null;
assert
builtins.unsafeGetAttrPos "a" (discardPositions {
a = true;
}) == null;
{
imports = [
{
options.imported.line10 = lib.mkOption {
type = lib.types.int;
};
options.imported.line10 = lib.mkOption { type = lib.types.int; };
# Simulates various patterns of generating modules such as
# programs.firefox.nativeMessagingHosts.ff2mpv. We don't expect to get
# line numbers for these, but we can fall back on knowing the file.
options.generated = discardPositions {
line18 = lib.mkOption {
type = lib.types.int;
};
};
options.generated = discardPositions { line18 = lib.mkOption { type = lib.types.int; }; };
options.submoduleLine34.extraOptLine23 = lib.mkOption {
default = 1;
@ -27,23 +25,25 @@ assert builtins.unsafeGetAttrPos "a" (discardPositions { a = true; }) == null;
}
];
options.nested.nestedLine30 = lib.mkOption {
type = lib.types.int;
};
options.nested.nestedLine30 = lib.mkOption { type = lib.types.int; };
options.submoduleLine34 = lib.mkOption {
default = { };
type = lib.types.submoduleWith {
modules = [
({ options, ... }: {
(
{ options, ... }:
{
options.submodDeclLine39 = lib.mkOption { };
})
}
)
{ freeformType = with lib.types; lazyAttrsOf (uniq unspecified); }
];
};
};
config = {
submoduleLine34.submodDeclLine39 = (options.submoduleLine34.type.getSubOptions [ ]).submodDeclLine39.declarationPositions;
submoduleLine34.submodDeclLine39 =
(options.submoduleLine34.type.getSubOptions [ ]).submodDeclLine39.declarationPositions;
};
}

View file

@ -1,8 +1,8 @@
{ lib, ... }:
let
deathtrapArgs = lib.mapAttrs
(k: _: throw "The module system is too strict, accessing an unused option's ${k} mkOption-attribute.")
(lib.functionArgs lib.mkOption);
deathtrapArgs = lib.mapAttrs (
k: _: throw "The module system is too strict, accessing an unused option's ${k} mkOption-attribute."
) (lib.functionArgs lib.mkOption);
in
{
options.value = lib.mkOption {

View file

@ -1,7 +1,9 @@
{ lib, ... }:
let
submod = { ... }: {
submod =
{ ... }:
{
options = {
enable = lib.mkOption {
default = false;

View file

@ -12,7 +12,5 @@ in
};
# config-dependent options: won't recommend, but useful for making this test parameterized
options.shorthandOnlyDefinesConfig = mkOption {
default = false;
};
options.shorthandOnlyDefinesConfig = mkOption { default = false; };
}

View file

@ -1,5 +1,4 @@
{ lib, ... }: {
options.value = lib.mkOption {
type = lib.types.either lib.types.int lib.types.str;
};
{ lib, ... }:
{
options.value = lib.mkOption { type = lib.types.either lib.types.int lib.types.str; };
}

View file

@ -2,8 +2,6 @@
{
options = {
value = lib.mkOption {
type = lib.types.ints.between (-21) 43;
};
value = lib.mkOption { type = lib.types.ints.between (-21) 43; };
};
}

View file

@ -2,8 +2,6 @@
{
options.set = {
value = lib.mkOption {
type = lib.types.ints.positive;
};
value = lib.mkOption { type = lib.types.ints.positive; };
};
}

View file

@ -2,8 +2,6 @@
{
options = {
value = lib.mkOption {
type = lib.types.ints.positive;
};
value = lib.mkOption { type = lib.types.ints.positive; };
};
}

View file

@ -2,8 +2,6 @@
{
options = {
value = lib.mkOption {
type = lib.types.ints.unsigned;
};
value = lib.mkOption { type = lib.types.ints.unsigned; };
};
}

View file

@ -1,4 +1,5 @@
{ lib, ... }: {
{ lib, ... }:
{
options.value = lib.mkOption {
type = lib.types.lazyAttrsOf (lib.types.str // { emptyValue.value = "empty"; });
default = { };

View file

@ -1,19 +1,17 @@
{ lib, ... }: let
{ lib, ... }:
let
pkgs.hello = {
type = "derivation";
pname = "hello";
};
in {
in
{
options = {
package = lib.mkPackageOption pkgs "hello" { };
namedPackage = lib.mkPackageOption pkgs "Hello" {
default = [ "hello" ];
};
namedPackage = lib.mkPackageOption pkgs "Hello" { default = [ "hello" ]; };
namedPackageSingletonDefault = lib.mkPackageOption pkgs "Hello" {
default = "hello";
};
namedPackageSingletonDefault = lib.mkPackageOption pkgs "Hello" { default = "hello"; };
pathPackage = lib.mkPackageOption pkgs [ "hello" ] { };
@ -21,33 +19,31 @@ in {
example = "pkgs.hello.override { stdenv = pkgs.clangStdenv; }";
};
packageWithPathExample = lib.mkPackageOption pkgs "hello" {
example = [ "hello" ];
};
packageWithPathExample = lib.mkPackageOption pkgs "hello" { example = [ "hello" ]; };
packageWithExtraDescription = lib.mkPackageOption pkgs "hello" {
extraDescription = "Example extra description.";
};
undefinedPackage = lib.mkPackageOption pkgs "hello" {
default = null;
};
undefinedPackage = lib.mkPackageOption pkgs "hello" { default = null; };
nullablePackage = lib.mkPackageOption pkgs "hello" {
nullable = true;
default = null;
};
nullablePackageWithDefault = lib.mkPackageOption pkgs "hello" {
nullable = true;
};
nullablePackageWithDefault = lib.mkPackageOption pkgs "hello" { nullable = true; };
packageWithPkgsText = lib.mkPackageOption pkgs "hello" {
pkgsText = "myPkgs";
};
packageWithPkgsText = lib.mkPackageOption pkgs "hello" { pkgsText = "myPkgs"; };
packageFromOtherSet = let myPkgs = {
hello = pkgs.hello // { pname = "hello-other"; };
}; in lib.mkPackageOption myPkgs "hello" { };
packageFromOtherSet =
let
myPkgs = {
hello = pkgs.hello // {
pname = "hello-other";
};
};
in
lib.mkPackageOption myPkgs "hello" { };
};
}

View file

@ -1,4 +1,5 @@
{ lib, ... }: {
{ lib, ... }:
{
options.value = lib.mkOption {
type = lib.types.oneOf [
lib.types.int

View file

@ -3,7 +3,9 @@
{
options.set = lib.mkOption {
default = { };
example = { a = 1; };
example = {
a = 1;
};
type = lib.types.attrsOf lib.types.int;
description = ''
Some descriptive text

View file

@ -1,6 +1,8 @@
{ lib, ... }: {
{ lib, ... }:
{
options.submodule = lib.mkOption {
inherit (lib.evalModules {
inherit
(lib.evalModules {
modules = [
{
options.inner = lib.mkOption {
@ -9,17 +11,22 @@
};
}
];
}) type;
})
type
;
default = { };
};
config.submodule = lib.mkMerge [
({ lib, ... }: {
(
{ lib, ... }:
{
options.outer = lib.mkOption {
type = lib.types.bool;
default = false;
};
})
}
)
{
inner = true;
outer = true;

View file

@ -1,4 +1,5 @@
{ lib, ... }: {
{ lib, ... }:
{
options.submodule = lib.mkOption {
type = lib.types.submoduleWith {
modules = [
@ -14,12 +15,15 @@
};
config.submodule = lib.mkMerge [
({ lib, ... }: {
(
{ lib, ... }:
{
options.outer = lib.mkOption {
type = lib.types.bool;
default = false;
};
})
}
)
{
inner = true;
outer = true;

View file

@ -1,13 +1,13 @@
{ lib, ... }: let
{ lib, ... }:
let
sub.options.config = lib.mkOption {
type = lib.types.bool;
default = false;
};
in {
in
{
options.submodule = lib.mkOption {
type = lib.types.submoduleWith {
modules = [ sub ];
};
type = lib.types.submoduleWith { modules = [ sub ]; };
default = { };
};
}

View file

@ -1,10 +1,7 @@
{ lib, ... }: {
{ lib, ... }:
{
options.submodule = lib.mkOption {
type = lib.types.submoduleWith {
modules = [
./declare-enable.nix
];
};
type = lib.types.submoduleWith { modules = [ ./declare-enable.nix ]; };
default = { };
};

View file

@ -1,9 +1,11 @@
{ lib, ... }: let
{ lib, ... }:
let
sub.options.config = lib.mkOption {
type = lib.types.bool;
default = false;
};
in {
in
{
options.submodule = lib.mkOption {
type = lib.types.submoduleWith {
modules = [ sub ];

View file

@ -1,12 +1,14 @@
{ lib, ... }: {
{ lib, ... }:
{
options.submodule = lib.mkOption {
type = lib.types.submoduleWith {
modules = [
({ lib, ... }: {
options.foo = lib.mkOption {
default = lib.foo;
};
})
(
{ lib, ... }:
{
options.foo = lib.mkOption { default = lib.foo; };
}
)
];
specialArgs.lib = lib // {
foo = "foo";

View file

@ -1,5 +1,6 @@
{ lib, moduleType, ... }:
let inherit (lib) mkOption types;
let
inherit (lib) mkOption types;
in
{
options.variants = mkOption {

View file

@ -1,8 +1,15 @@
{ lib ? import ../.., modules ? [] }:
{
lib ? import ../..,
modules ? [ ],
}:
{
inherit (lib.evalModules {
inherit
(lib.evalModules {
inherit modules;
specialArgs.modulesPath = ./.;
}) config options;
})
config
options
;
}

View file

@ -1,19 +1,28 @@
{ config, lib, ... }:
let
inherit (lib) types mkOption setDefaultModuleLocation evalModules;
inherit (types) deferredModule lazyAttrsOf submodule str raw enum;
inherit (lib)
types
mkOption
setDefaultModuleLocation
evalModules
;
inherit (types)
deferredModule
lazyAttrsOf
submodule
str
raw
enum
;
in
{
options = {
deferred = mkOption {
type = deferredModule;
};
result = mkOption {
default = (evalModules { modules = [ config.deferred ]; }).config.result;
};
deferred = mkOption { type = deferredModule; };
result = mkOption { default = (evalModules { modules = [ config.deferred ]; }).config.result; };
};
config = {
deferred = { ... }:
deferred =
{ ... }:
# this should be an attrset, so this fails
true;
};

View file

@ -1,7 +1,14 @@
{ lib, ... }:
let
inherit (lib) types mkOption setDefaultModuleLocation;
inherit (types) deferredModule lazyAttrsOf submodule str raw enum;
inherit (types)
deferredModule
lazyAttrsOf
submodule
str
raw
enum
;
in
{
imports = [
@ -9,10 +16,14 @@ in
# - nodes.<name>
# - default
# where all nodes include the default
({ config, ... }: {
(
{ config, ... }:
{
_file = "generic.nix";
options.nodes = mkOption {
type = lazyAttrsOf (submodule { imports = [ config.default ]; });
type = lazyAttrsOf (submodule {
imports = [ config.default ];
});
default = { };
};
options.default = mkOption {
@ -22,12 +33,18 @@ in
Module that is included in all nodes.
'';
};
})
}
)
{
_file = "default-1.nix";
default = { config, ... }: {
options.settingsDict = lib.mkOption { type = lazyAttrsOf str; default = {}; };
default =
{ config, ... }:
{
options.settingsDict = lib.mkOption {
type = lazyAttrsOf str;
default = { };
};
options.bottom = lib.mkOption { type = enum [ ]; };
};
}
@ -49,7 +66,9 @@ in
{
_file = "nodes-foo-c-is-a.nix";
nodes.foo = { config, ... }: {
nodes.foo =
{ config, ... }:
{
settingsDict.c = config.settingsDict.a;
};
}

View file

@ -1,3 +1 @@
{
attrsOfSub.bar.enable = true;
}
{ attrsOfSub.bar.enable = true; }

View file

@ -1,3 +1 @@
{
attrsOfSub.bar = {};
}
{ attrsOfSub.bar = { }; }

View file

@ -1,3 +1 @@
{
attrsOfSub.foo.enable = true;
}
{ attrsOfSub.foo.enable = true; }

View file

@ -1,7 +1,5 @@
{ lib, ... }:
{
attrsOfSub.foo = lib.mkForce {
enable = false;
};
attrsOfSub.foo = lib.mkForce { enable = false; };
}

View file

@ -1,7 +1,5 @@
{ config, lib, ... }:
{
attrsOfSub.foo = lib.mkIf config.enable {
enable = true;
};
attrsOfSub.foo = lib.mkIf config.enable { enable = true; };
}

View file

@ -1,3 +1 @@
{
attrsOfSub.foo = {};
}
{ attrsOfSub.foo = { }; }

View file

@ -1,7 +1,5 @@
{ lib, ... }:
{
attrsOfSub = lib.mkForce {
foo.enable = false;
};
attrsOfSub = lib.mkForce { foo.enable = false; };
}

View file

@ -1,7 +1,5 @@
{ config, lib, ... }:
{
attrsOfSub = lib.mkIf config.enable {
foo.enable = true;
};
attrsOfSub = lib.mkIf config.enable { foo.enable = true; };
}

View file

@ -1,3 +1 @@
{
config.enable = abort "oops";
}
{ config.enable = abort "oops"; }

View file

@ -1,3 +1 @@
{
config.enable = throw "oops";
}
{ config.enable = throw "oops"; }

View file

@ -1,3 +1 @@
{
enable = true;
}
{ enable = true; }

View file

@ -1,5 +1,3 @@
{ lib, ... }:
lib.mkForce {
attrsOfSub.foo.enable = false;
}
lib.mkForce { attrsOfSub.foo.enable = false; }

View file

@ -1,5 +1,3 @@
{ lib, ... }:
lib.mkForce {
enable = false;
}
lib.mkForce { enable = false; }

View file

@ -1,15 +1,24 @@
{ config, ... }: {
class = { "just" = "data"; };
{ config, ... }:
{
class = {
"just" = "data";
};
a = "one";
b = "two";
meta = "meta";
_module.args.result =
let r = builtins.removeAttrs config [ "_module" ];
in builtins.trace (builtins.deepSeq r r) (r == {
let
r = builtins.removeAttrs config [ "_module" ];
in
builtins.trace (builtins.deepSeq r r) (
r == {
a = "one";
b = "two";
class = { "just" = "data"; };
class = {
"just" = "data";
};
meta = "meta";
});
}
);
}

View file

@ -1,5 +1,3 @@
{ config, lib, ... }:
lib.mkIf config.enable {
attrsOfSub.foo.enable = true;
}
lib.mkIf config.enable { attrsOfSub.foo.enable = true; }

View file

@ -1,3 +1 @@
{
_module.check = false;
}
{ _module.check = false; }

View file

@ -5,12 +5,11 @@
{
# Always defined, but the value depends on the presence of an option.
config.set = {
config.set =
{
value = if options ? set.enable then 360 else 7;
}
# Only define if possible.
// lib.optionalAttrs (options ? set.enable) {
enable = true;
};
// lib.optionalAttrs (options ? set.enable) { enable = true; };
}

View file

@ -5,12 +5,11 @@
{
# Always defined, but the value depends on the presence of an option.
config = {
config =
{
value = if options ? enable then 360 else 7;
}
# Only define if possible.
// lib.optionalAttrs (options ? enable) {
enable = true;
};
// lib.optionalAttrs (options ? enable) { enable = true; };
}

View file

@ -1,3 +1,4 @@
{ config, ... }: {
{ config, ... }:
{
settingsDict.a = config.settingsDict.b;
}

View file

@ -1,3 +1 @@
{
submodule.config.config = true;
}
{ submodule.config.config = true; }

View file

@ -1,3 +1 @@
{
submodule.config = true;
}
{ submodule.config = true; }

View file

@ -1,3 +1 @@
{
value = -23;
}
{ value = -23; }

View file

@ -1,3 +1 @@
{
value = 42;
}
{ value = 42; }

Some files were not shown because too many files have changed in this diff Show more