Move pkgs/lib/ to lib/
This commit is contained in:
@@ -0,0 +1,329 @@
|
||||
# Operations on attribute sets.
|
||||
|
||||
with {
|
||||
inherit (builtins) head tail isString;
|
||||
inherit (import ./trivial.nix) or;
|
||||
inherit (import ./default.nix) fold;
|
||||
inherit (import ./strings.nix) concatStringsSep;
|
||||
inherit (import ./lists.nix) concatMap concatLists all deepSeqList;
|
||||
inherit (import ./misc.nix) maybeAttr;
|
||||
};
|
||||
|
||||
rec {
|
||||
inherit (builtins) attrNames listToAttrs hasAttr isAttrs getAttr;
|
||||
|
||||
|
||||
/* Return an attribute from nested attribute sets. For instance
|
||||
["x" "y"] applied to some set e returns e.x.y, if it exists. The
|
||||
default value is returned otherwise. */
|
||||
attrByPath = attrPath: default: e:
|
||||
let attr = head attrPath;
|
||||
in
|
||||
if attrPath == [] then e
|
||||
else if builtins ? hasAttr && hasAttr attr e
|
||||
then attrByPath (tail attrPath) default (getAttr attr e)
|
||||
else default;
|
||||
|
||||
|
||||
/* Return nested attribute set in which an attribute is set. For instance
|
||||
["x" "y"] applied with some value v returns `x.y = v;' */
|
||||
setAttrByPath = attrPath: value:
|
||||
if attrPath == [] then value
|
||||
else listToAttrs [(
|
||||
nameValuePair (head attrPath) (setAttrByPath (tail attrPath) value)
|
||||
)];
|
||||
|
||||
|
||||
getAttrFromPath = attrPath: set:
|
||||
let errorMsg = "cannot find attribute `" + concatStringsSep "." attrPath + "'";
|
||||
in attrByPath attrPath (abort errorMsg) set;
|
||||
|
||||
|
||||
/* Return the specified attributes from a set.
|
||||
|
||||
Example:
|
||||
attrVals ["a" "b" "c"] as
|
||||
=> [as.a as.b as.c]
|
||||
*/
|
||||
attrVals = nameList: set:
|
||||
map (x: getAttr x set) nameList;
|
||||
|
||||
|
||||
/* Return the values of all attributes in the given set, sorted by
|
||||
attribute name.
|
||||
|
||||
Example:
|
||||
attrValues {c = 3; a = 1; b = 2;}
|
||||
=> [1 2 3]
|
||||
*/
|
||||
attrValues = attrs: attrVals (attrNames attrs) attrs;
|
||||
|
||||
|
||||
/* Collect each attribute named `attr' from a list of attribute
|
||||
sets. Sets that don't contain the named attribute are ignored.
|
||||
|
||||
Example:
|
||||
catAttrs "a" [{a = 1;} {b = 0;} {a = 2;}]
|
||||
=> [1 2]
|
||||
*/
|
||||
catAttrs = attr: l: concatLists (map (s: if hasAttr attr s then [(getAttr attr s)] else []) l);
|
||||
|
||||
|
||||
/* Filter an attribute set by removing all attributes for which the
|
||||
given predicate return false.
|
||||
|
||||
Example:
|
||||
filterAttrs (n: v: n == "foo") { foo = 1; bar = 2; }
|
||||
=> { foo = 1; }
|
||||
*/
|
||||
filterAttrs = pred: set:
|
||||
listToAttrs (fold (n: ys: let v = getAttr n set; in if pred n v then [(nameValuePair n v)] ++ ys else ys) [] (attrNames set));
|
||||
|
||||
|
||||
/* foldAttrs: apply fold functions to values grouped by key. Eg accumulate values as list:
|
||||
foldAttrs (n: a: [n] ++ a) [] [{ a = 2; } { a = 3; }]
|
||||
=> { a = [ 2 3 ]; }
|
||||
*/
|
||||
foldAttrs = op: nul: list_of_attrs:
|
||||
fold (n: a:
|
||||
fold (name: o:
|
||||
o // (listToAttrs [{inherit name; value = op (getAttr name n) (maybeAttr name nul a); }])
|
||||
) a (attrNames n)
|
||||
) {} list_of_attrs;
|
||||
|
||||
|
||||
/* Recursively collect sets that verify a given predicate named `pred'
|
||||
from the set `attrs'. The recursion is stopped when the predicate is
|
||||
verified.
|
||||
|
||||
Type:
|
||||
collect ::
|
||||
(AttrSet -> Bool) -> AttrSet -> AttrSet
|
||||
|
||||
Example:
|
||||
collect builtins.isList { a = { b = ["b"]; }; c = [1]; }
|
||||
=> [["b"] [1]]
|
||||
|
||||
collect (x: x ? outPath)
|
||||
{ a = { outPath = "a/"; }; b = { outPath = "b/"; }; }
|
||||
=> [{ outPath = "a/"; } { outPath = "b/"; }]
|
||||
*/
|
||||
collect = pred: attrs:
|
||||
if pred attrs then
|
||||
[ attrs ]
|
||||
else if builtins.isAttrs attrs then
|
||||
concatMap (collect pred) (attrValues attrs)
|
||||
else
|
||||
[];
|
||||
|
||||
|
||||
/* Utility function that creates a {name, value} pair as expected by
|
||||
builtins.listToAttrs. */
|
||||
nameValuePair = name: value: { inherit name value; };
|
||||
|
||||
|
||||
/* Apply a function to each element in an attribute set. The
|
||||
function takes two arguments --- the attribute name and its value
|
||||
--- and returns the new value for the attribute. The result is a
|
||||
new attribute set.
|
||||
|
||||
Example:
|
||||
mapAttrs (name: value: name + "-" + value)
|
||||
{ x = "foo"; y = "bar"; }
|
||||
=> { x = "x-foo"; y = "y-bar"; }
|
||||
*/
|
||||
mapAttrs = f: set:
|
||||
listToAttrs (map (attr: nameValuePair attr (f attr (getAttr attr set))) (attrNames set));
|
||||
|
||||
|
||||
/* Like `mapAttrs', but allows the name of each attribute to be
|
||||
changed in addition to the value. The applied function should
|
||||
return both the new name and value as a `nameValuePair'.
|
||||
|
||||
Example:
|
||||
mapAttrs' (name: value: nameValuePair ("foo_" + name) ("bar-" + value))
|
||||
{ x = "a"; y = "b"; }
|
||||
=> { foo_x = "bar-a"; foo_y = "bar-b"; }
|
||||
*/
|
||||
mapAttrs' = f: set:
|
||||
listToAttrs (map (attr: f attr (getAttr attr set)) (attrNames set));
|
||||
|
||||
|
||||
/* Call a function for each attribute in the given set and return
|
||||
the result in a list.
|
||||
|
||||
Example:
|
||||
mapAttrsToList (name: value: name + value)
|
||||
{ x = "a"; y = "b"; }
|
||||
=> [ "xa" "yb" ]
|
||||
*/
|
||||
mapAttrsToList = f: attrs:
|
||||
map (name: f name (getAttr name attrs)) (attrNames attrs);
|
||||
|
||||
|
||||
/* Like `mapAttrs', except that it recursively applies itself to
|
||||
attribute sets. Also, the first argument of the argument
|
||||
function is a *list* of the names of the containing attributes.
|
||||
|
||||
Type:
|
||||
mapAttrsRecursive ::
|
||||
([String] -> a -> b) -> AttrSet -> AttrSet
|
||||
|
||||
Example:
|
||||
mapAttrsRecursive (path: value: concatStringsSep "-" (path ++ [value]))
|
||||
{ n = { a = "A"; m = { b = "B"; c = "C"; }; }; d = "D"; }
|
||||
=> { n = { a = "n-a-A"; m = { b = "n-m-b-B"; c = "n-m-c-C"; }; }; d = "d-D"; }
|
||||
*/
|
||||
mapAttrsRecursive = mapAttrsRecursiveCond (as: true);
|
||||
|
||||
|
||||
/* Like `mapAttrsRecursive', but it takes an additional predicate
|
||||
function that tells it whether to recursive into an attribute
|
||||
set. If it returns false, `mapAttrsRecursiveCond' does not
|
||||
recurse, but does apply the map function. It is returns true, it
|
||||
does recurse, and does not apply the map function.
|
||||
|
||||
Type:
|
||||
mapAttrsRecursiveCond ::
|
||||
(AttrSet -> Bool) -> ([String] -> a -> b) -> AttrSet -> AttrSet
|
||||
|
||||
Example:
|
||||
# To prevent recursing into derivations (which are attribute
|
||||
# sets with the attribute "type" equal to "derivation"):
|
||||
mapAttrsRecursiveCond
|
||||
(as: !(as ? "type" && as.type == "derivation"))
|
||||
(x: ... do something ...)
|
||||
attrs
|
||||
*/
|
||||
mapAttrsRecursiveCond = cond: f: set:
|
||||
let
|
||||
recurse = path: set:
|
||||
let
|
||||
g =
|
||||
name: value:
|
||||
if isAttrs value && cond value
|
||||
then recurse (path ++ [name]) value
|
||||
else f (path ++ [name]) value;
|
||||
in mapAttrs g set;
|
||||
in recurse [] set;
|
||||
|
||||
|
||||
/* Generate an attribute set by mapping a function over a list of
|
||||
attribute names.
|
||||
|
||||
Example:
|
||||
genAttrs [ "foo" "bar" ] (name: "x_" + name)
|
||||
=> { foo = "x_foo"; bar = "x_bar"; }
|
||||
*/
|
||||
genAttrs = names: f:
|
||||
listToAttrs (map (n: nameValuePair n (f n)) names);
|
||||
|
||||
|
||||
/* Check whether the argument is a derivation. */
|
||||
isDerivation = x: isAttrs x && x ? type && x.type == "derivation";
|
||||
|
||||
|
||||
/* If the Boolean `cond' is true, return the attribute set `as',
|
||||
otherwise an empty attribute set. */
|
||||
optionalAttrs = cond: as: if cond then as else {};
|
||||
|
||||
|
||||
/* Merge sets of attributes and use the function f to merge attributes
|
||||
values. */
|
||||
zipAttrsWithNames = names: f: sets:
|
||||
listToAttrs (map (name: {
|
||||
inherit name;
|
||||
value = f name (catAttrs name sets);
|
||||
}) names);
|
||||
|
||||
# implentation note: Common names appear multiple times in the list of
|
||||
# names, hopefully this does not affect the system because the maximal
|
||||
# laziness avoid computing twice the same expression and listToAttrs does
|
||||
# not care about duplicated attribute names.
|
||||
zipAttrsWith = f: sets: zipWithNames (concatMap attrNames sets) f sets;
|
||||
|
||||
zipAttrs = zipAttrsWith (name: values: values);
|
||||
|
||||
/* backward compatibility */
|
||||
zipWithNames = zipAttrsWithNames;
|
||||
zip = builtins.trace "lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith;
|
||||
|
||||
|
||||
/* Does the same as the update operator '//' except that attributes are
|
||||
merged until the given pedicate is verified. The predicate should
|
||||
accept 3 arguments which are the path to reach the attribute, a part of
|
||||
the first attribute set and a part of the second attribute set. When
|
||||
the predicate is verified, the value of the first attribute set is
|
||||
replaced by the value of the second attribute set.
|
||||
|
||||
Example:
|
||||
recursiveUpdateUntil (path: l: r: path == ["foo"]) {
|
||||
# first attribute set
|
||||
foo.bar = 1;
|
||||
foo.baz = 2;
|
||||
bar = 3;
|
||||
} {
|
||||
#second attribute set
|
||||
foo.bar = 1;
|
||||
foo.quz = 2;
|
||||
baz = 4;
|
||||
}
|
||||
|
||||
returns: {
|
||||
foo.bar = 1; # 'foo.*' from the second set
|
||||
foo.quz = 2; #
|
||||
bar = 3; # 'bar' from the first set
|
||||
baz = 4; # 'baz' from the second set
|
||||
}
|
||||
|
||||
*/
|
||||
recursiveUpdateUntil = pred: lhs: rhs:
|
||||
let f = attrPath:
|
||||
zipAttrsWith (n: values:
|
||||
if tail values == []
|
||||
|| pred attrPath (head (tail values)) (head values) then
|
||||
head values
|
||||
else
|
||||
f (attrPath ++ [n]) values
|
||||
);
|
||||
in f [] [rhs lhs];
|
||||
|
||||
/* A recursive variant of the update operator ‘//’. The recusion
|
||||
stops when one of the attribute values is not an attribute set,
|
||||
in which case the right hand side value takes precedence over the
|
||||
left hand side value.
|
||||
|
||||
Example:
|
||||
recursiveUpdate {
|
||||
boot.loader.grub.enable = true;
|
||||
boot.loader.grub.device = "/dev/hda";
|
||||
} {
|
||||
boot.loader.grub.device = "";
|
||||
}
|
||||
|
||||
returns: {
|
||||
boot.loader.grub.enable = true;
|
||||
boot.loader.grub.device = "";
|
||||
}
|
||||
|
||||
*/
|
||||
recursiveUpdate = lhs: rhs:
|
||||
recursiveUpdateUntil (path: lhs: rhs:
|
||||
!(isAttrs lhs && isAttrs rhs)
|
||||
) lhs rhs;
|
||||
|
||||
matchAttrs = pattern: attrs:
|
||||
fold or false (attrValues (zipAttrsWithNames (attrNames pattern) (n: values:
|
||||
let pat = head values; val = head (tail values); in
|
||||
if length values == 1 then false
|
||||
else if isAttrs pat then isAttrs val && matchAttrs head values
|
||||
else pat == val
|
||||
) [pattern attrs]));
|
||||
|
||||
# override only the attributes that are already present in the old set
|
||||
# useful for deep-overriding
|
||||
overrideExisting = old: new:
|
||||
old // listToAttrs (map (attr: nameValuePair attr (attrByPath [attr] (getAttr attr old) new)) (attrNames old));
|
||||
|
||||
deepSeqAttrs = x: y: deepSeqList (attrValues x) y;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
{lib, pkgs} :
|
||||
let inherit (lib) nv nvs; in
|
||||
{
|
||||
# see for example:
|
||||
# - development/interpreters/php_configurable/default.nix
|
||||
# - .. search composableDerivation in all-packages.nix ..
|
||||
#
|
||||
# You should be able to override anything you like easily
|
||||
# grep the mailinglist by title "python proposal" (dec 08)
|
||||
# -> http://mail.cs.uu.nl/pipermail/nix-dev/2008-December/001571.html
|
||||
# to see why this got complicated when using all its features
|
||||
# TODO add newer example using new syntax (kernel derivation proposal -> mailinglist)
|
||||
composableDerivation = {
|
||||
mkDerivation ? pkgs.stdenv.mkDerivation,
|
||||
|
||||
# list of functions to be applied before defaultOverridableDelayableArgs removes removeAttrs names
|
||||
# prepareDerivationArgs handles derivation configurations
|
||||
applyPreTidy ? [ lib.prepareDerivationArgs ],
|
||||
|
||||
# consider adding addtional elements by derivation.merge { removeAttrs = ["elem"]; };
|
||||
removeAttrs ? ["cfg" "flags"]
|
||||
|
||||
}: (lib.defaultOverridableDelayableArgs ( a: mkDerivation a)
|
||||
{
|
||||
inherit applyPreTidy removeAttrs;
|
||||
}).merge;
|
||||
|
||||
# some utility functions
|
||||
# use this function to generate flag attrs for prepareDerivationArgs
|
||||
# E nable D isable F eature
|
||||
edf = {name, feat ? name, enable ? {}, disable ? {} , value ? ""}:
|
||||
nvs name {
|
||||
set = {
|
||||
configureFlags = ["--enable-${feat}${if value == "" then "" else "="}${value}"];
|
||||
} // enable;
|
||||
unset = {
|
||||
configureFlags = ["--disable-${feat}"];
|
||||
} // disable;
|
||||
};
|
||||
|
||||
# same for --with and --without-
|
||||
# W ith or W ithout F eature
|
||||
wwf = {name, feat ? name, enable ? {}, disable ? {}, value ? ""}:
|
||||
nvs name {
|
||||
set = enable // {
|
||||
configureFlags = ["--with-${feat}${if value == "" then "" else "="}${value}"]
|
||||
++ lib.maybeAttr "configureFlags" [] enable;
|
||||
};
|
||||
unset = disable // {
|
||||
configureFlags = ["--without-${feat}"]
|
||||
++ lib.maybeAttr "configureFlags" [] disable;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
let lib = import ./default.nix;
|
||||
inherit (builtins) getAttr attrNames isFunction;
|
||||
|
||||
in
|
||||
|
||||
rec {
|
||||
|
||||
|
||||
/* `overrideDerivation drv f' takes a derivation (i.e., the result
|
||||
of a call to the builtin function `derivation') and returns a new
|
||||
derivation in which the attributes of the original are overriden
|
||||
according to the function `f'. The function `f' is called with
|
||||
the original derivation attributes.
|
||||
|
||||
`overrideDerivation' allows certain "ad-hoc" customisation
|
||||
scenarios (e.g. in ~/.nixpkgs/config.nix). For instance, if you
|
||||
want to "patch" the derivation returned by a package function in
|
||||
Nixpkgs to build another version than what the function itself
|
||||
provides, you can do something like this:
|
||||
|
||||
mySed = overrideDerivation pkgs.gnused (oldAttrs: {
|
||||
name = "sed-4.2.2-pre";
|
||||
src = fetchurl {
|
||||
url = ftp://alpha.gnu.org/gnu/sed/sed-4.2.2-pre.tar.bz2;
|
||||
sha256 = "11nq06d131y4wmf3drm0yk502d2xc6n5qy82cg88rb9nqd2lj41k";
|
||||
};
|
||||
patches = [];
|
||||
});
|
||||
|
||||
For another application, see build-support/vm, where this
|
||||
function is used to build arbitrary derivations inside a QEMU
|
||||
virtual machine. */
|
||||
|
||||
overrideDerivation = drv: f:
|
||||
let
|
||||
newDrv = derivation (drv.drvAttrs // (f drv));
|
||||
in addPassthru newDrv (
|
||||
{ meta = drv.meta or {};
|
||||
passthru = if drv ? passthru then drv.passthru else {};
|
||||
}
|
||||
//
|
||||
(drv.passthru or {})
|
||||
//
|
||||
(if (drv ? crossDrv && drv ? nativeDrv)
|
||||
then {
|
||||
crossDrv = overrideDerivation drv.crossDrv f;
|
||||
nativeDrv = overrideDerivation drv.nativeDrv f;
|
||||
}
|
||||
else { }));
|
||||
|
||||
|
||||
# usage: (you can use override multiple times)
|
||||
# let d = makeOverridable stdenv.mkDerivation { name = ..; buildInputs; }
|
||||
# noBuildInputs = d.override { buildInputs = []; }
|
||||
# additionalBuildInputs = d.override ( args : args // { buildInputs = args.buildInputs ++ [ additional ]; } )
|
||||
makeOverridable = f: origArgs:
|
||||
let
|
||||
ff = f origArgs;
|
||||
in
|
||||
if builtins.isAttrs ff then (ff //
|
||||
{ override = newArgs:
|
||||
makeOverridable f (origArgs // (if builtins.isFunction newArgs then newArgs origArgs else newArgs));
|
||||
deepOverride = newArgs:
|
||||
makeOverridable f (lib.overrideExisting (lib.mapAttrs (deepOverrider newArgs) origArgs) newArgs);
|
||||
})
|
||||
else ff;
|
||||
|
||||
deepOverrider = newArgs: name: x: if builtins.isAttrs x then (
|
||||
if x ? deepOverride then (x.deepOverride newArgs) else
|
||||
if x ? override then (x.override newArgs) else
|
||||
x) else x;
|
||||
|
||||
|
||||
/* Call the package function in the file `fn' with the required
|
||||
arguments automatically. The function is called with the
|
||||
arguments `args', but any missing arguments are obtained from
|
||||
`autoArgs'. This function is intended to be partially
|
||||
parameterised, e.g.,
|
||||
|
||||
callPackage = callPackageWith pkgs;
|
||||
pkgs = {
|
||||
libfoo = callPackage ./foo.nix { };
|
||||
libbar = callPackage ./bar.nix { };
|
||||
};
|
||||
|
||||
If the `libbar' function expects an argument named `libfoo', it is
|
||||
automatically passed as an argument. Overrides or missing
|
||||
arguments can be supplied in `args', e.g.
|
||||
|
||||
libbar = callPackage ./bar.nix {
|
||||
libfoo = null;
|
||||
enableX11 = true;
|
||||
};
|
||||
*/
|
||||
callPackageWith = autoArgs: fn: args:
|
||||
let f = if builtins.isFunction fn then fn else import fn; in
|
||||
makeOverridable f ((builtins.intersectAttrs (builtins.functionArgs f) autoArgs) // args);
|
||||
|
||||
/* Add attributes to each output of a derivation without changing the derivation itself */
|
||||
addPassthru = drv: passthru:
|
||||
let
|
||||
outputs = drv.outputs or [ "out" ];
|
||||
|
||||
commonAttrs = drv // (builtins.listToAttrs outputsList) //
|
||||
({ all = map (x: x.value) outputsList; }) // passthru;
|
||||
|
||||
outputToAttrListElement = outputName:
|
||||
{ name = outputName;
|
||||
value = commonAttrs // {
|
||||
inherit (builtins.getAttr outputName drv) outPath drvPath type outputName;
|
||||
};
|
||||
};
|
||||
|
||||
outputsList = map outputToAttrListElement outputs;
|
||||
in builtins.getAttr drv.outputName commonAttrs;
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
let lib = import ./default.nix;
|
||||
|
||||
inherit (builtins) trace attrNamesToStr isAttrs isFunction isList isInt
|
||||
isString isBool head substring attrNames;
|
||||
|
||||
inherit (lib) all id mapAttrsFlatten elem;
|
||||
|
||||
in
|
||||
|
||||
rec {
|
||||
|
||||
|
||||
# Wrapper aroung the primop `addErrorContext', which shouldn't used
|
||||
# directly. It evaluates and returns `val', but if an evaluation
|
||||
# error occurs, the text in `msg' is added to the error context
|
||||
# (stack trace) printed by Nix.
|
||||
addErrorContext =
|
||||
if builtins ? addErrorContext
|
||||
then builtins.addErrorContext
|
||||
else msg: val: val;
|
||||
|
||||
addErrorContextToAttrs = lib.mapAttrs (a : v : lib.addErrorContext "while evaluating ${a}" v);
|
||||
|
||||
|
||||
traceVal = if builtins ? trace then x: (builtins.trace x x) else x: x;
|
||||
traceXMLVal = if builtins ? trace then x: (builtins.trace (builtins.toXML x) x) else x: x;
|
||||
traceXMLValMarked = str: if builtins ? trace then x: (builtins.trace ( str + builtins.toXML x) x) else x: x;
|
||||
|
||||
# this can help debug your code as well - designed to not produce thousands of lines
|
||||
traceShowVal = x : trace (showVal x) x;
|
||||
traceShowValMarked = str: x: trace (str + showVal x) x;
|
||||
attrNamesToStr = a : lib.concatStringsSep "; " (map (x : "${x}=") (attrNames a));
|
||||
showVal = x :
|
||||
if isAttrs x then
|
||||
if x ? outPath then "x is a derivation, name ${if x ? name then x.name else "<no name>"}, { ${attrNamesToStr x} }"
|
||||
else "x is attr set { ${attrNamesToStr x} }"
|
||||
else if isFunction x then "x is a function"
|
||||
else if x == [] then "x is an empty list"
|
||||
else if isList x then "x is a list, first element is: ${showVal (head x)}"
|
||||
else if x == true then "x is boolean true"
|
||||
else if x == false then "x is boolean false"
|
||||
else if x == null then "x is null"
|
||||
else if isInt x then "x is an integer `${toString x}'"
|
||||
else if isString x then "x is a string `${substring 0 50 x}...'"
|
||||
else "x is probably a path `${substring 0 50 (toString x)}...'";
|
||||
|
||||
# trace the arguments passed to function and its result
|
||||
# maybe rewrite these functions in a traceCallXml like style. Then one function is enough
|
||||
traceCall = n : f : a : let t = n2 : x : traceShowValMarked "${n} ${n2}:" x; in t "result" (f (t "arg 1" a));
|
||||
traceCall2 = n : f : a : b : let t = n2 : x : traceShowValMarked "${n} ${n2}:" x; in t "result" (f (t "arg 1" a) (t "arg 2" b));
|
||||
traceCall3 = n : f : a : b : c : let t = n2 : x : traceShowValMarked "${n} ${n2}:" x; in t "result" (f (t "arg 1" a) (t "arg 2" b) (t "arg 3" c));
|
||||
|
||||
traceValIfNot = c: x:
|
||||
if c x then true else trace (showVal x) false;
|
||||
|
||||
/* Evaluate a set of tests. A test is an attribute set {expr,
|
||||
expected}, denoting an expression and its expected result. The
|
||||
result is a list of failed tests, each represented as {name,
|
||||
expected, actual}, denoting the attribute name of the failing
|
||||
test and its expected and actual results. Used for regression
|
||||
testing of the functions in lib; see tests.nix for an example.
|
||||
Only tests having names starting with "test" are run.
|
||||
Add attr { tests = ["testName"]; } to run these test only
|
||||
*/
|
||||
runTests = tests: lib.concatLists (lib.attrValues (lib.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));
|
||||
|
||||
# create a test assuming that list elements are true
|
||||
# usage: { testX = allTrue [ true ]; }
|
||||
testAllTrue = expr : { inherit expr; expected = map (x: true) expr; };
|
||||
|
||||
# evaluate everything once so that errors will occur earlier
|
||||
# hacky: traverse attrs by adding a dummy
|
||||
# ignores functions (should this behavior change?) See strictf
|
||||
#
|
||||
# Note: This should be a primop! Something like seq of haskell would be nice to
|
||||
# have as well. It's used fore debugging only anyway
|
||||
strict = x :
|
||||
let
|
||||
traverse = x :
|
||||
if isString x then true
|
||||
else if isAttrs x then
|
||||
if x ? outPath then true
|
||||
else all id (mapAttrsFlatten (n: traverse) x)
|
||||
else if isList x then
|
||||
all id (map traverse x)
|
||||
else if isBool x then true
|
||||
else if isFunction x then true
|
||||
else if isInt x then true
|
||||
else if x == null then true
|
||||
else true; # a (store) path?
|
||||
in if traverse x then x else throw "else never reached";
|
||||
|
||||
# example: (traceCallXml "myfun" id 3) will output something like
|
||||
# calling myfun arg 1: 3 result: 3
|
||||
# this forces deep evaluation of all arguments and the result!
|
||||
# note: if result doesn't evaluate you'll get no trace at all (FIXME)
|
||||
# args should be printed in any case
|
||||
traceCallXml = a:
|
||||
if !isInt a then
|
||||
traceCallXml 1 "calling ${a}\n"
|
||||
else
|
||||
let nr = a;
|
||||
in (str: expr:
|
||||
if isFunction expr then
|
||||
(arg:
|
||||
traceCallXml (builtins.add 1 nr) "${str}\n arg ${builtins.toString nr} is \n ${builtins.toXML (strict arg)}" (expr arg)
|
||||
)
|
||||
else
|
||||
let r = strict expr;
|
||||
in builtins.trace "${str}\n result:\n${builtins.toXML r}" r
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
let
|
||||
|
||||
trivial = import ./trivial.nix;
|
||||
lists = import ./lists.nix;
|
||||
strings = import ./strings.nix;
|
||||
stringsWithDeps = import ./strings-with-deps.nix;
|
||||
attrsets = import ./attrsets.nix;
|
||||
sources = import ./sources.nix;
|
||||
modules = import ./modules.nix;
|
||||
options = import ./options.nix;
|
||||
properties = import ./properties.nix;
|
||||
types = import ./types.nix;
|
||||
meta = import ./meta.nix;
|
||||
debug = import ./debug.nix;
|
||||
misc = import ./misc.nix;
|
||||
maintainers = import ./maintainers.nix;
|
||||
platforms = import ./platforms.nix;
|
||||
systems = import ./systems.nix;
|
||||
customisation = import ./customisation.nix;
|
||||
licenses = import ./licenses.nix;
|
||||
|
||||
in
|
||||
{ inherit trivial lists strings stringsWithDeps attrsets sources options
|
||||
properties modules types meta debug maintainers licenses platforms systems;
|
||||
}
|
||||
# !!! don't include everything at top-level; perhaps only the most
|
||||
# commonly used functions.
|
||||
// trivial // lists // strings // stringsWithDeps // attrsets // sources
|
||||
// properties // options // types // meta // debug // misc // modules
|
||||
// systems
|
||||
// customisation
|
||||
@@ -0,0 +1,235 @@
|
||||
{
|
||||
/* License identifiers loosely based on: http://fedoraproject.org/wiki/Licensing
|
||||
* 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.
|
||||
*/
|
||||
|
||||
artistic2 = {
|
||||
shortName = "Artistic 2.0";
|
||||
fullName = "Artistic 2.0";
|
||||
url = "http://opensource.org/licenses/artistic-license-2.0.php";
|
||||
};
|
||||
|
||||
agpl3 = {
|
||||
shortName = "AGPLv3";
|
||||
fullName = "GNU Affero General Public License version 3 only";
|
||||
url = https://www.gnu.org/licenses/agpl.html;
|
||||
};
|
||||
|
||||
agpl3Plus = {
|
||||
shortName = "AGPLv3+";
|
||||
fullName = "GNU Affero General Public License version 3 or later";
|
||||
url = https://www.gnu.org/licenses/agpl.html;
|
||||
};
|
||||
|
||||
amd = {
|
||||
shortName = "amd";
|
||||
fullName = "AMD License Agreement";
|
||||
url = "http://developer.amd.com/amd-license-agreement/";
|
||||
};
|
||||
|
||||
amdadl = {
|
||||
shortName = "amd-adl";
|
||||
fullName = "amd-adl license";
|
||||
url = "http://sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/licenses/AMD-ADL?revision=1.1";
|
||||
};
|
||||
|
||||
# Apple Public Source License 2.0;
|
||||
# http://opensource.org/licenses/APSL-2.0
|
||||
apsl20 = "APSL 2.0";
|
||||
|
||||
asl20 = {
|
||||
shortName = "ASL2.0";
|
||||
fullName = "Apache Software License 2.0";
|
||||
url = http://www.apache.org/licenses/LICENSE-2.0;
|
||||
};
|
||||
|
||||
boost = {
|
||||
shortName = "boost";
|
||||
fullName = "Boost Software License";
|
||||
url = http://www.boost.org/LICENSE_1_0.txt;
|
||||
};
|
||||
|
||||
bsd2 = {
|
||||
shortName = "BSD-2";
|
||||
fullName = "BSD license (2 clause)";
|
||||
url = http://opensource.org/licenses/BSD-2-Clause;
|
||||
};
|
||||
|
||||
bsd3 = {
|
||||
shortName = "BSD-3";
|
||||
fullName = "BSD license (3 clause)";
|
||||
url = http://opensource.org/licenses/BSD-3-Clause;
|
||||
};
|
||||
|
||||
bsdOriginal = {
|
||||
shortName = "BSD-original";
|
||||
fullName = "Original BSD license with advertising clause";
|
||||
url = https://fedoraproject.org/wiki/Licensing/BSD;
|
||||
};
|
||||
|
||||
cddl = {
|
||||
shortName = "CDDL";
|
||||
fullName = "Common Development Distribution License ";
|
||||
url = http://www.opensolaris.org/os/licensing/cddllicense.txt;
|
||||
};
|
||||
|
||||
cpl10 = {
|
||||
shortName = "CPL 1.0";
|
||||
fullName = "Common Public License version 1.0";
|
||||
url = http://www.eclipse.org/legal/cpl-v10.html;
|
||||
};
|
||||
|
||||
epl10 = {
|
||||
shortName = "EPL 1.0";
|
||||
fullName = "Eclipse Public License version 1.0";
|
||||
url = http://www.eclipse.org/legal/epl-v10.html;
|
||||
};
|
||||
|
||||
gpl2 = "GPLv2";
|
||||
|
||||
gpl2Oss = {
|
||||
shortName = "GPLv2+OSS";
|
||||
fullName = "GNU General Public License version 2 only (with OSI approved licenses linking exception)";
|
||||
url = http://www.mysql.com/about/legal/licensing/foss-exception;
|
||||
};
|
||||
|
||||
# GNU General Public License version 2 or later;
|
||||
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
gpl2Plus = "GPLv2+";
|
||||
|
||||
gpl3 = {
|
||||
shortName = "GPLv3";
|
||||
fullName = "GNU General Public License version 3 only";
|
||||
url = http://www.fsf.org/licensing/licenses/gpl.html;
|
||||
};
|
||||
|
||||
gpl3Plus = {
|
||||
shortName = "GPLv3+";
|
||||
fullName = "GNU General Public License version 3 or later";
|
||||
url = http://www.fsf.org/licensing/licenses/gpl.html;
|
||||
};
|
||||
|
||||
gpl3ClasspathPlus = {
|
||||
shortName = "GPLv3+classpath+";
|
||||
fullName = "GNU General Public License version 3 or later (with Classpath exception)";
|
||||
url = https://fedoraproject.org/wiki/Licensing/GPL_Classpath_Exception;
|
||||
};
|
||||
|
||||
isc = {
|
||||
shortName = "ISC";
|
||||
fullName = "Internet Systems Consortium License";
|
||||
url = http://www.opensource.org/licenses/ISC;
|
||||
};
|
||||
|
||||
ipl10 = {
|
||||
shortName = "IPL 1.0";
|
||||
fullName = "IBM Public License Version 1.0";
|
||||
url = http://www.ibm.com/developerworks/opensource/library/os-i18n2/os-ipl.html;
|
||||
};
|
||||
|
||||
ijg = {
|
||||
shortName = "IJG";
|
||||
fullName = "Independent JPEG Group License";
|
||||
url = https://fedoraproject.org/wiki/Licensing/IJG;
|
||||
};
|
||||
|
||||
libtiff = {
|
||||
shortName = "libtiff";
|
||||
fullName = "libtiff license";
|
||||
url = https://fedoraproject.org/wiki/Licensing/libtiff;
|
||||
};
|
||||
|
||||
lgpl2 = "LGPLv2";
|
||||
|
||||
lgpl2Plus = {
|
||||
shortName = "LGPLv2+";
|
||||
fullName = "GNU Library General Public License version 2 or later";
|
||||
url = http://www.gnu.org/licenses/old-licenses/lgpl-2.0.html;
|
||||
};
|
||||
|
||||
lgpl21 = "LGPLv2.1";
|
||||
|
||||
lgpl21Plus = {
|
||||
shortName = "LGPLv2.1+";
|
||||
fullName = "GNU Lesser General Public License version 2.1 or later";
|
||||
url = http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html;
|
||||
};
|
||||
|
||||
llgpl21 = {
|
||||
shortName = "LLGPLv2.1";
|
||||
fullName = "Lisp LGPL; GNU Lesser General Public License version 2.1 with Franz Inc. preamble for clarification of LGPL terms in context of Lisp";
|
||||
url = http://opensource.franz.com/preamble.html;
|
||||
};
|
||||
|
||||
lgpl3 = {
|
||||
shortName = "LGPLv3";
|
||||
fullName = "GNU Lesser General Public License version 3 only";
|
||||
url = http://www.fsf.org/licensing/licenses/lgpl.html;
|
||||
};
|
||||
|
||||
lgpl3Plus = {
|
||||
shortName = "LGPLv3+";
|
||||
fullName = "GNU Lesser General Public License version 3 or later";
|
||||
url = http://www.fsf.org/licensing/licenses/lgpl.html;
|
||||
};
|
||||
|
||||
mit = {
|
||||
shortName = "MIT";
|
||||
fullName = "MIT/X11 license";
|
||||
url = http://www.opensource.org/licenses/mit-license.php;
|
||||
};
|
||||
|
||||
mpl11 = {
|
||||
shortName = "MPL1.1";
|
||||
fullName = "Mozilla Public License version 1.1";
|
||||
url = http://www.mozilla.org/MPL/MPL-1.1.html;
|
||||
};
|
||||
|
||||
openssl = {
|
||||
shortName = "openssl";
|
||||
fullName = "OpenSSL license";
|
||||
url = http://www.openssl.org/source/license.html;
|
||||
};
|
||||
|
||||
publicDomain = {
|
||||
shortName = "Public Domain";
|
||||
fullname = "Public Domain";
|
||||
};
|
||||
|
||||
psfl = {
|
||||
shortName = "PSFL";
|
||||
fullName = "Python Software Foundation License";
|
||||
url = http://docs.python.org/license.html;
|
||||
};
|
||||
|
||||
tcltk = {
|
||||
shortName = "Tcl/Tk";
|
||||
fullName = "Tcl/Tk license";
|
||||
url = http://www.tcl.tk/software/tcltk/license.html;
|
||||
};
|
||||
|
||||
unfree = "unfree";
|
||||
|
||||
unfreeRedistributable = "unfree-redistributable";
|
||||
|
||||
unfreeRedistributableFirmware = "unfree-redistributable-firmware";
|
||||
|
||||
zlib = {
|
||||
shortName = "zlib";
|
||||
fullName = "zlib license";
|
||||
url = http://www.gzip.org/zlib/zlib_license.html;
|
||||
};
|
||||
|
||||
zpt20 = {
|
||||
shortName = "ZPT2.0";
|
||||
fullName = "Zope Public License 2.0";
|
||||
url = "http://old.zope.org/Resources/License/ZPL-2.0";
|
||||
};
|
||||
|
||||
zpt21 = {
|
||||
shortName = "ZPT2.1";
|
||||
fullName = "Zope Public License 2.1";
|
||||
url = "http://old.zope.org/Resources/License/ZPL-2.1";
|
||||
};
|
||||
}
|
||||
+235
@@ -0,0 +1,235 @@
|
||||
# General list operations.
|
||||
let
|
||||
inherit (import ./trivial.nix) deepSeq;
|
||||
|
||||
inc = builtins.add 1;
|
||||
|
||||
dec = n: builtins.sub n 1;
|
||||
|
||||
inherit (builtins) elemAt;
|
||||
in rec {
|
||||
inherit (builtins) head tail length isList add sub lessThan;
|
||||
|
||||
|
||||
# Create a list consisting of a single element. `singleton x' is
|
||||
# sometimes more convenient with respect to indentation than `[x]'
|
||||
# when x spans multiple lines.
|
||||
singleton = x: [x];
|
||||
|
||||
|
||||
# "Fold" a binary function `op' between successive elements of
|
||||
# `list' with `nul' as the starting value, i.e., `fold op nul [x_1
|
||||
# x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))'. (This is
|
||||
# Haskell's foldr).
|
||||
fold = op: nul: list:
|
||||
let
|
||||
len = length list;
|
||||
fold' = n:
|
||||
if n == len
|
||||
then nul
|
||||
else op (elemAt list n) (fold' (inc n));
|
||||
in fold' 0;
|
||||
|
||||
# Left fold: `fold op nul [x_1 x_2 ... x_n] == op (... (op (op nul
|
||||
# x_1) x_2) ... x_n)'.
|
||||
foldl = op: nul: list:
|
||||
let
|
||||
len = length list;
|
||||
foldl' = n:
|
||||
if n == minus1
|
||||
then nul
|
||||
else op (foldl' (dec n)) (elemAt list n);
|
||||
in foldl' (dec (length list));
|
||||
|
||||
minus1 = dec 0;
|
||||
|
||||
|
||||
# map with index: `imap (i: v: "${v}-${toString i}") ["a" "b"] ==
|
||||
# ["a-1" "b-2"]'
|
||||
imap = f: list:
|
||||
let
|
||||
len = length list;
|
||||
imap' = n:
|
||||
if n == len
|
||||
then []
|
||||
else [ (f (inc n) (elemAt list n)) ] ++ imap' (inc n);
|
||||
in imap' 0;
|
||||
|
||||
|
||||
# Concatenate a list of lists.
|
||||
concatLists = builtins.concatLists or (fold (x: y: x ++ y) []);
|
||||
|
||||
|
||||
# Map and concatenate the result.
|
||||
concatMap = f: list: concatLists (map f list);
|
||||
|
||||
|
||||
# Flatten the argument into a single list; that is, nested lists are
|
||||
# spliced into the top-level lists. E.g., `flatten [1 [2 [3] 4] 5]
|
||||
# == [1 2 3 4 5]' and `flatten 1 == [1]'.
|
||||
flatten = x:
|
||||
if isList x
|
||||
then fold (x: y: (flatten x) ++ y) [] x
|
||||
else [x];
|
||||
|
||||
|
||||
# Filter a list using a predicate; that is, return a list containing
|
||||
# every element from `list' for which `pred' returns true.
|
||||
filter =
|
||||
builtins.filter or
|
||||
(pred: list:
|
||||
fold (x: y: if pred x then [x] ++ y else y) [] list);
|
||||
|
||||
|
||||
# Remove elements equal to 'e' from a list. Useful for buildInputs.
|
||||
remove = e: filter (x: x != e);
|
||||
|
||||
|
||||
# Return true if `list' has an element `x'.
|
||||
elem =
|
||||
builtins.elem or
|
||||
(x: list: fold (a: bs: x == a || bs) false list);
|
||||
|
||||
|
||||
# Find the sole element in the list matching the specified
|
||||
# predicate, returns `default' if no such element exists, or
|
||||
# `multiple' if there are multiple matching elements.
|
||||
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;
|
||||
|
||||
|
||||
# Find the first element in the list matching the specified
|
||||
# predicate or returns `default' if no such element exists.
|
||||
findFirst = pred: default: list:
|
||||
let found = filter pred list;
|
||||
in if found == [] then default else head found;
|
||||
|
||||
|
||||
# Return true iff function `pred' returns true for at least element
|
||||
# of `list'.
|
||||
any = pred: fold (x: y: if pred x then true else y) false;
|
||||
|
||||
|
||||
# Return true iff function `pred' returns true for all elements of
|
||||
# `list'.
|
||||
all = pred: fold (x: y: if pred x then y else false) true;
|
||||
|
||||
|
||||
# Return a singleton list or an empty list, depending on a boolean
|
||||
# value. Useful when building lists with optional elements
|
||||
# (e.g. `++ optional (system == "i686-linux") flashplayer').
|
||||
optional = cond: elem: if cond then [elem] else [];
|
||||
|
||||
|
||||
# Return a list or an empty list, dependening on a boolean value.
|
||||
optionals = cond: elems: if cond then elems else [];
|
||||
|
||||
|
||||
# If argument is a list, return it; else, wrap it in a singleton
|
||||
# list. If you're using this, you should almost certainly
|
||||
# reconsider if there isn't a more "well-typed" approach.
|
||||
toList = x: if builtins.isList x then x else [x];
|
||||
|
||||
|
||||
# Return a list of integers from `first' up to and including `last'.
|
||||
range = first: last:
|
||||
if builtins.lessThan last first
|
||||
then []
|
||||
else [first] ++ range (builtins.add first 1) last;
|
||||
|
||||
|
||||
# Partition the elements of a list in two lists, `right' and
|
||||
# `wrong', depending on the evaluation of a predicate.
|
||||
partition = pred:
|
||||
fold (h: t:
|
||||
if pred h
|
||||
then { right = [h] ++ t.right; wrong = t.wrong; }
|
||||
else { right = t.right; wrong = [h] ++ t.wrong; }
|
||||
) { right = []; wrong = []; };
|
||||
|
||||
|
||||
zipListsWith = f: fst: snd:
|
||||
let
|
||||
len1 = length fst;
|
||||
len2 = length snd;
|
||||
len = if builtins.lessThan len1 len2 then len1 else len2;
|
||||
zipListsWith' = n:
|
||||
if n != len then
|
||||
[ (f (elemAt fst n) (elemAt snd n)) ]
|
||||
++ zipListsWith' (inc n)
|
||||
else [];
|
||||
in zipListsWith' 0;
|
||||
|
||||
zipLists = zipListsWith (fst: snd: { inherit fst snd; });
|
||||
|
||||
|
||||
# Reverse the order of the elements of a list.
|
||||
reverseList = fold (e: acc: acc ++ [ e ]) [];
|
||||
|
||||
# Sort a list based on a comparator function which compares two
|
||||
# elements and returns true if the first argument is strictly below
|
||||
# the second argument. The returned list is sorted in an increasing
|
||||
# order. The implementation does a quick-sort.
|
||||
sort = strictLess: list:
|
||||
let
|
||||
len = length list;
|
||||
first = head list;
|
||||
pivot' = n: acc@{ left, right }: let el = elemAt list n; next = pivot' (inc n); in
|
||||
if n == len
|
||||
then acc
|
||||
else if strictLess first el
|
||||
then next { inherit left; right = [ el ] ++ right; }
|
||||
else
|
||||
next { left = [ el ] ++ left; inherit right; };
|
||||
pivot = pivot' 1 { left = []; right = []; };
|
||||
in
|
||||
if lessThan len 2 then list
|
||||
else (sort strictLess pivot.left) ++ [ first ] ++ (sort strictLess pivot.right);
|
||||
|
||||
|
||||
# Return the first (at most) N elements of a list.
|
||||
take = count: list:
|
||||
let
|
||||
len = length list;
|
||||
take' = n:
|
||||
if n == len || n == count
|
||||
then []
|
||||
else
|
||||
[ (elemAt list n) ] ++ take' (inc n);
|
||||
in take' 0;
|
||||
|
||||
|
||||
# Remove the first (at most) N elements of a list.
|
||||
drop = count: list:
|
||||
let
|
||||
len = length list;
|
||||
drop' = n:
|
||||
if n == minus1 || lessThan n count
|
||||
then []
|
||||
else
|
||||
drop' (dec n) ++ [ (elemAt list n) ];
|
||||
in drop' (dec len);
|
||||
|
||||
|
||||
last = list:
|
||||
assert list != []; elemAt list (dec (length list));
|
||||
|
||||
|
||||
# Zip two lists together.
|
||||
zipTwoLists = xs: ys:
|
||||
let
|
||||
len1 = length xs;
|
||||
len2 = length ys;
|
||||
len = if lessThan len1 len2 then len1 else len2;
|
||||
zipTwoLists' = n:
|
||||
if n != len then
|
||||
[ { first = elemAt xs n; second = elemAt ys n; } ]
|
||||
++ zipTwoLists' (inc n)
|
||||
else [];
|
||||
in zipTwoLists' 0;
|
||||
|
||||
deepSeqList = xs: y: if any (x: deepSeq x false) xs then y else y;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/* -*- coding: utf-8; -*- */
|
||||
|
||||
{
|
||||
/* Add your name and email address here. Keep the list
|
||||
alphabetically sorted. */
|
||||
|
||||
aforemny = "Alexander Foremny <alexanderforemny@googlemail.com>";
|
||||
algorith = "Dries Van Daele <dries_van_daele@telenet.be>";
|
||||
all = "Nix Committers <nix-commits@lists.science.uu.nl>";
|
||||
amiddelk = "Arie Middelkoop <amiddelk@gmail.com>";
|
||||
amorsillo = "Andrew Morsillo <andrew.morsillo@gmail.com>";
|
||||
andres = "Andres Loeh <ksnixos@andres-loeh.de>";
|
||||
antono = "Antono Vasiljev <self@antono.info>";
|
||||
astsmtl = "Alexander Tsamutali <astsmtl@yandex.ru>";
|
||||
aszlig = "aszlig <aszlig@redmoonstudios.org>";
|
||||
bbenoist = "Baptist BENOIST <return_0@live.com>";
|
||||
bjg = "Brian Gough <bjg@gnu.org>";
|
||||
bjornfor = "Bjørn Forsman <bjorn.forsman@gmail.com>";
|
||||
bluescreen303 = "Mathijs Kwik <mathijs@bluescreen303.nl>";
|
||||
bodil = "Bodil Stokke <nix@bodil.org>";
|
||||
chaoflow = "Florian Friesdorf <flo@chaoflow.net>";
|
||||
coconnor = "Corey O'Connor <coreyoconnor@gmail.com>";
|
||||
coroa = "Jonas Hörsch <jonas@chaoflow.net>";
|
||||
edwtjo = "Edward Tjörnhammar <ed@cflags.cc>";
|
||||
eelco = "Eelco Dolstra <eelco.dolstra@logicblox.com>";
|
||||
ertes = "Ertugrul Söylemez <es@ertes.de>";
|
||||
garbas = "Rok Garbas <rok@garbas.si>";
|
||||
goibhniu = "Cillian de Róiste <cillian.deroiste@gmail.com>";
|
||||
guibert = "David Guibert <david.guibert@gmail.com>";
|
||||
iElectric = "Domen Kozar <domen@dev.si>";
|
||||
iyzsong = "Song Wenwu <iyzsong@gmail.com>";
|
||||
jcumming = "Jack Cummings <jack@mudshark.org>";
|
||||
kkallio = "Karn Kallio <tierpluspluslists@gmail.com>";
|
||||
lovek323 = "Jason O'Conal <jason@oconal.id.au>";
|
||||
ludo = "Ludovic Courtès <ludo@gnu.org>";
|
||||
marcweber = "Marc Weber <marco-oweber@gmx.de>";
|
||||
modulistic = "Pablo Costa <modulistic@gmail.com>";
|
||||
mornfall = "Petr Ročkai <me@mornfall.net>";
|
||||
ocharles = "Oliver Charles <ollie@ocharles.org.uk>";
|
||||
offline = "Jaka Hudoklin <jakahudoklin@gmail.com>";
|
||||
orbitz = "Malcolm Matalka <mmatalka@gmail.com>";
|
||||
page = "Carles Pagès <page@cubata.homelinux.net>";
|
||||
phreedom = "Evgeny Egorochkin <phreedom@yandex.ru>";
|
||||
pierron = "Nicolas B. Pierron <nixos@nbp.name>";
|
||||
piotr = "Piotr Pietraszkiewicz <ppietrasa@gmail.com>";
|
||||
pSub = "Pascal Wittmann <mail@pascal-wittmann.de>";
|
||||
qknight = "Joachim Schiele <js@lastlog.de>";
|
||||
raskin = "Michael Raskin <7c6f434c@mail.ru>";
|
||||
rickynils = "Rickard Nilsson <rickynils@gmail.com>";
|
||||
rob = "Rob Vermaas <rob.vermaas@gmail.com>";
|
||||
roconnor = "Russell O'Connor <roconnor@theorem.ca>";
|
||||
sander = "Sander van der Burg <s.vanderburg@tudelft.nl>";
|
||||
shlevy = "Shea Levy <shea@shealevy.com>";
|
||||
simons = "Peter Simons <simons@cryp.to>";
|
||||
smironov = "Sergey Mironov <ierton@gmail.com>";
|
||||
thammers = "Tobias Hammerschmidt <jawr@gmx.de>";
|
||||
the-kenny = "Moritz Ulrich <moritz@tarn-vedra.de>";
|
||||
urkud = "Yury G. Kudryashov <urkud+nix@ya.ru>";
|
||||
vcunat = "Vladimír Čunát <vcunat@gmail.com>";
|
||||
viric = "Lluís Batlle i Rossell <viric@viric.name>";
|
||||
vizanto = "Danny Wilson <danny@prime.vc>";
|
||||
winden = "Antonio Vargas Gonzalez <windenntw@gmail.com>";
|
||||
z77z = "Marco Maggesi <maggesi@math.unifi.it>";
|
||||
zef = "Zef Hemel <zef@zef.me>";
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/* Some functions for manipulating meta attributes, as well as the
|
||||
name attribute. */
|
||||
|
||||
rec {
|
||||
|
||||
|
||||
/* Add to or override the meta attributes of the given
|
||||
derivation.
|
||||
|
||||
Example:
|
||||
addMetaAttrs {description = "Bla blah";} somePkg
|
||||
*/
|
||||
addMetaAttrs = newAttrs: drv:
|
||||
drv // { meta = (if drv ? meta then drv.meta else {}) // newAttrs; };
|
||||
|
||||
|
||||
/* 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.
|
||||
|
||||
Example:
|
||||
updateName (oldName: oldName + "-experimental") somePkg
|
||||
*/
|
||||
updateName = updater: drv: drv // {name = updater (drv.name);};
|
||||
|
||||
|
||||
/* Append a suffix to the name of a package. !!! the suffix should
|
||||
really be appended *before* the version, at least most of the
|
||||
time.
|
||||
*/
|
||||
appendToName = suffix: updateName (name: "${name}-${suffix}");
|
||||
|
||||
|
||||
/* Decrease the nix-env priority of the package, i.e., other
|
||||
versions/variants of the package will be preferred.
|
||||
*/
|
||||
lowPrio = drv: addMetaAttrs { priority = "10"; } drv;
|
||||
|
||||
/* Increase the nix-env priority of the package, i.e., this
|
||||
version/variant of the package will be preferred.
|
||||
*/
|
||||
hiPrio = drv: addMetaAttrs { priority = "-10"; } drv;
|
||||
|
||||
}
|
||||
+431
@@ -0,0 +1,431 @@
|
||||
let lib = import ./default.nix;
|
||||
inherit (builtins) isFunction hasAttr getAttr head tail isList isAttrs isInt attrNames;
|
||||
|
||||
in
|
||||
|
||||
with import ./lists.nix;
|
||||
with import ./attrsets.nix;
|
||||
with import ./strings.nix;
|
||||
|
||||
rec {
|
||||
|
||||
# returns default if env var is not set
|
||||
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);
|
||||
defaultMerge = x: y: x // (defaultMergeArg x y);
|
||||
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 // rec {
|
||||
function = foldArgs merger f arg;
|
||||
args = (lib.attrByPath ["passthru" "args"] {} z) // x;
|
||||
} ));
|
||||
withStdOverrides = base // {
|
||||
override = base.passthru.function;
|
||||
deepOverride = a : (base.passthru.function ((lib.mapAttrs (lib.deepOverrider a) base.passthru.args) // a));
|
||||
} ;
|
||||
in
|
||||
withStdOverrides;
|
||||
|
||||
|
||||
# predecessors: proposed replacement for applyAndFun (which has a bug cause it merges twice)
|
||||
# the naming "overridableDelayableArgs" tries to express that you can
|
||||
# - override attr values which have been supplied earlier
|
||||
# - use attr values before they have been supplied by accessing the fix point
|
||||
# name "fixed"
|
||||
# f: the (delayed overridden) arguments are applied to this
|
||||
#
|
||||
# initial: initial attrs arguments and settings. see defaultOverridableDelayableArgs
|
||||
#
|
||||
# returns: f applied to the arguments // special attributes attrs
|
||||
# a) merge: merge applied args with new args. Wether an argument is overridden depends on the merge settings
|
||||
# b) replace: this let's you replace and remove names no matter which merge function has been set
|
||||
#
|
||||
# examples: see test cases "res" below;
|
||||
overridableDelayableArgs =
|
||||
f : # the function applied to the arguments
|
||||
initial : # you pass attrs, the functions below are passing a function taking the fix argument
|
||||
let
|
||||
takeFixed = if isFunction initial then initial else (fixed : initial); # transform initial to an expression always taking the fixed argument
|
||||
tidy = args :
|
||||
let # apply all functions given in "applyPreTidy" in sequence
|
||||
applyPreTidyFun = fold ( n : a : x : n ( a x ) ) lib.id (maybeAttr "applyPreTidy" [] args);
|
||||
in removeAttrs (applyPreTidyFun args) ( ["applyPreTidy"] ++ (maybeAttr "removeAttrs" [] args) ); # tidy up args before applying them
|
||||
fun = n : x :
|
||||
let newArgs = fixed :
|
||||
let args = takeFixed fixed;
|
||||
mergeFun = getAttr n args;
|
||||
in if isAttrs x then (mergeFun args x)
|
||||
else assert isFunction x;
|
||||
mergeFun args (x ( args // { inherit fixed; }));
|
||||
in overridableDelayableArgs f newArgs;
|
||||
in
|
||||
(f (tidy (lib.fix takeFixed))) // {
|
||||
merge = fun "mergeFun";
|
||||
replace = fun "keepFun";
|
||||
};
|
||||
defaultOverridableDelayableArgs = f :
|
||||
let defaults = {
|
||||
mergeFun = mergeAttrByFunc; # default merge function. merge strategie (concatenate lists, strings) is given by mergeAttrBy
|
||||
keepFun = a : b : { inherit (a) removeAttrs mergeFun keepFun mergeAttrBy; } // b; # even when using replace preserve these values
|
||||
applyPreTidy = []; # list of functions applied to args before args are tidied up (usage case : prepareDerivationArgs)
|
||||
mergeAttrBy = mergeAttrBy // {
|
||||
applyPreTidy = a : b : a ++ b;
|
||||
removeAttrs = a : b: a ++ b;
|
||||
};
|
||||
removeAttrs = ["mergeFun" "keepFun" "mergeAttrBy" "removeAttrs" "fixed" ]; # before applying the arguments to the function make sure these names are gone
|
||||
};
|
||||
in (overridableDelayableArgs f defaults).merge;
|
||||
|
||||
|
||||
|
||||
# rec { # an example of how composedArgsAndFun can be used
|
||||
# a = composedArgsAndFun (x : x) { a = ["2"]; meta = { d = "bar";}; };
|
||||
# # meta.d will be lost ! It's your task to preserve it (eg using a merge function)
|
||||
# b = a.passthru.function { a = [ "3" ]; meta = { d2 = "bar2";}; };
|
||||
# # instead of passing/ overriding values you can use a merge function:
|
||||
# c = b.passthru.function ( x: { a = x.a ++ ["4"]; }); # consider using (maybeAttr "a" [] x)
|
||||
# }
|
||||
# result:
|
||||
# {
|
||||
# a = { a = ["2"]; meta = { d = "bar"; }; passthru = { function = .. }; };
|
||||
# b = { a = ["3"]; meta = { d2 = "bar2"; }; passthru = { function = .. }; };
|
||||
# c = { a = ["3" "4"]; meta = { d2 = "bar2"; }; passthru = { function = .. }; };
|
||||
# # c2 is equal to c
|
||||
# }
|
||||
composedArgsAndFun = f: foldArgs defaultMerge f {};
|
||||
|
||||
|
||||
# shortcut for attrByPath ["name"] default attrs
|
||||
maybeAttrNullable = name: default: attrs:
|
||||
if attrs == null then default else
|
||||
if __hasAttr name attrs then (__getAttr name attrs) else default;
|
||||
|
||||
# shortcut for attrByPath ["name"] default attrs
|
||||
maybeAttr = name: default: attrs:
|
||||
if __hasAttr name attrs then (__getAttr name attrs) else 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 {}
|
||||
# else if builtins.isString val then ""
|
||||
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
|
||||
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 );
|
||||
|
||||
|
||||
# Input : attrSet, [[name default] ...], [ [flagname reqs..] ... ]
|
||||
# Output : are reqs satisfied? It's asserted.
|
||||
checkReqs = attrSet : argList : condList :
|
||||
(
|
||||
fold lib.and true
|
||||
(map (x: let name = (head x) ; in
|
||||
|
||||
((checkFlag attrSet name) ->
|
||||
(fold lib.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;
|
||||
y = if elem x acc then [] else [x];
|
||||
in y ++ go (tail xs) (y ++ acc);
|
||||
in go inputList acc;
|
||||
|
||||
uniqListExt = {inputList, outputList ? [],
|
||||
getter ? (x : x), 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;
|
||||
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;
|
||||
|
||||
lazyGenericClosure = {startSet, operator}:
|
||||
let
|
||||
work = list: doneKeys: result:
|
||||
if list == [] then
|
||||
result
|
||||
else
|
||||
let x = head list; key = x.key; in
|
||||
if elem key doneKeys then
|
||||
work (tail list) doneKeys result
|
||||
else
|
||||
work (tail list ++ operator x) ([key] ++ doneKeys) ([x] ++ result);
|
||||
in
|
||||
work startSet [] [];
|
||||
|
||||
genericClosure =
|
||||
if builtins ? genericClosure then builtins.genericClosure
|
||||
else lazyGenericClosure;
|
||||
|
||||
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;
|
||||
ys = tail xs;
|
||||
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';
|
||||
}
|
||||
);
|
||||
|
||||
closePropagation = list: (uniqList {inputList = (innerClosePropagation [] list);});
|
||||
|
||||
# calls a function (f attr value ) for each record item. returns a list
|
||||
mapAttrsFlatten = f : r : map (attr: f attr (builtins.getAttr attr r) ) (attrNames r);
|
||||
|
||||
# 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);
|
||||
|
||||
# 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 :
|
||||
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 :
|
||||
fold (n: set : if (__hasAttr n set)
|
||||
then setAttr set n (f (__getAttr n set) (__getAttr n set2))
|
||||
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 ]; }
|
||||
mergeAttrsConcatenateValues = mergeAttrsWithFunc ( a : b : (toList a) ++ (toList b) );
|
||||
|
||||
# merges attributes using //, if a name exisits in both attributes
|
||||
# an error will be triggered unless its listed in mergeLists
|
||||
# so you can mergeAttrsNoOverride { buildInputs = [a]; } { buildInputs = [a]; } {} to get
|
||||
# { buildInputs = [a b]; }
|
||||
# merging buildPhase does'nt 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 :
|
||||
fold (n: set :
|
||||
setAttr set n ( if (__hasAttr n set)
|
||||
then # merge
|
||||
if elem n mergeLists # attribute contains list, merge them by concatenating
|
||||
then (__getAttr n attrs2) ++ (__getAttr n attrs1)
|
||||
else if elem n overrideSnd
|
||||
then __getAttr n attrs1
|
||||
else throw "error mergeAttrsNoOverride, attribute ${n} given in both attributes - no merge func defined"
|
||||
else __getAttr n attrs2 # add attribute not existing in attr1
|
||||
)) attrs1 (__attrNames attrs2);
|
||||
|
||||
|
||||
# example usage:
|
||||
# mergeAttrByFunc {
|
||||
# inherit mergeAttrBy; # defined below
|
||||
# buildInputs = [ a b ];
|
||||
# } {
|
||||
# buildInputs = [ c d ];
|
||||
# };
|
||||
# will result in
|
||||
# { mergeAttrsBy = [...]; buildInputs = [ a b c d ]; }
|
||||
# is used by prepareDerivationArgs, defaultOverridableDelayableArgs and can be used when composing using
|
||||
# foldArgs, composedArgsAndFun or applyAndFun. Example: composableDerivation in all-packages.nix
|
||||
mergeAttrByFunc = x : y :
|
||||
let
|
||||
mergeAttrBy2 = { mergeAttrBy=lib.mergeAttrs; }
|
||||
// (maybeAttr "mergeAttrBy" {} x)
|
||||
// (maybeAttr "mergeAttrBy" {} y); in
|
||||
fold lib.mergeAttrs {} [
|
||||
x y
|
||||
(mapAttrs ( a : v : # merge special names using given functions
|
||||
if (hasAttr a x)
|
||||
then if (hasAttr a y)
|
||||
then v (getAttr a x) (getAttr a y) # both have attr, use merge func
|
||||
else (getAttr a x) # only x has attr
|
||||
else (getAttr a y) # only y has attr)
|
||||
) (removeAttrs mergeAttrBy2
|
||||
# don't merge attrs which are neither in x nor y
|
||||
(filter (a : (! hasAttr a x) && (! hasAttr a y) )
|
||||
(attrNames mergeAttrBy2))
|
||||
)
|
||||
)
|
||||
];
|
||||
mergeAttrsByFuncDefaults = foldl mergeAttrByFunc { inherit mergeAttrBy; };
|
||||
mergeAttrsByFuncDefaultsClean = list: removeAttrs (mergeAttrsByFuncDefaults list) ["mergeAttrBy"];
|
||||
|
||||
# merge attrs based on version key into mkDerivation args, see mergeAttrBy to learn about smart merge defaults
|
||||
#
|
||||
# This function is best explained by an example:
|
||||
#
|
||||
# {version ? "2.x"} :
|
||||
#
|
||||
# mkDerivation (mergeAttrsByVersion "package-name" version
|
||||
# { # version specific settings
|
||||
# "git" = { src = ..; preConfigre = "autogen.sh"; buildInputs = [automake autoconf libtool]; };
|
||||
# "2.x" = { src = ..; };
|
||||
# }
|
||||
# { // shared settings
|
||||
# buildInputs = [ common build inputs ];
|
||||
# meta = { .. }
|
||||
# }
|
||||
# )
|
||||
#
|
||||
# Please note that e.g. Eelco Dolstra usually prefers having one file for
|
||||
# each version. On the other hand there are valuable additional design goals
|
||||
# - readability
|
||||
# - do it once only
|
||||
# - try to avoid duplication
|
||||
#
|
||||
# Marc Weber and Michael Raskin sometimes prefer keeping older
|
||||
# versions around for testing and regression tests - as long as its cheap to
|
||||
# do so.
|
||||
#
|
||||
# Very often it just happens that the "shared" code is the bigger part.
|
||||
# Then using this function might be appropriate.
|
||||
#
|
||||
# Be aware that its easy to cause recompilations in all versions when using
|
||||
# this function - also if derivations get too complex splitting into multiple
|
||||
# files is the way to go.
|
||||
#
|
||||
# See misc.nix -> versionedDerivation
|
||||
# discussion: nixpkgs: pull/310
|
||||
mergeAttrsByVersion = name: version: attrsByVersion: base:
|
||||
mergeAttrsByFuncDefaultsClean [ { name = "${name}-${version}"; } base (maybeAttr version (throw "bad version ${version} for ${name}") attrsByVersion)];
|
||||
|
||||
# sane defaults (same name as attr name so that inherit can be used)
|
||||
mergeAttrBy = # { buildInputs = concatList; [...]; passthru = mergeAttr; [..]; }
|
||||
listToAttrs (map (n : nameValuePair n lib.concat)
|
||||
[ "nativeBuildInputs" "buildInputs" "propagatedBuildInputs" "configureFlags" "prePhases" "postAll" "patches" ])
|
||||
// listToAttrs (map (n : nameValuePair n lib.mergeAttrs) [ "passthru" "meta" "cfg" "flags" ])
|
||||
// listToAttrs (map (n : nameValuePair n (a: b: "${a}\n${b}") ) [ "preConfigure" "postInstall" ])
|
||||
;
|
||||
|
||||
# prepareDerivationArgs tries to make writing configurable derivations easier
|
||||
# example:
|
||||
# prepareDerivationArgs {
|
||||
# mergeAttrBy = {
|
||||
# myScript = x : y : x ++ "\n" ++ y;
|
||||
# };
|
||||
# cfg = {
|
||||
# readlineSupport = true;
|
||||
# };
|
||||
# flags = {
|
||||
# readline = {
|
||||
# set = {
|
||||
# configureFlags = [ "--with-compiler=${compiler}" ];
|
||||
# buildInputs = [ compiler ];
|
||||
# pass = { inherit compiler; READLINE=1; };
|
||||
# assertion = compiler.dllSupport;
|
||||
# myScript = "foo";
|
||||
# };
|
||||
# unset = { configureFlags = ["--without-compiler"]; };
|
||||
# };
|
||||
# };
|
||||
# src = ...
|
||||
# buildPhase = '' ... '';
|
||||
# name = ...
|
||||
# myScript = "bar";
|
||||
# };
|
||||
# if you don't have need for unset you can omit the surrounding set = { .. } attr
|
||||
# all attrs except flags cfg and mergeAttrBy will be merged with the
|
||||
# additional data from flags depending on config settings
|
||||
# It's used in composableDerivation in all-packages.nix. It's also used
|
||||
# heavily in the new python and libs implementation
|
||||
#
|
||||
# should we check for misspelled cfg options?
|
||||
# TODO use args.mergeFun here as well?
|
||||
prepareDerivationArgs = args:
|
||||
let args2 = { cfg = {}; flags = {}; } // args;
|
||||
flagName = name : "${name}Support";
|
||||
cfgWithDefaults = (listToAttrs (map (n : nameValuePair (flagName n) false) (attrNames args2.flags)))
|
||||
// args2.cfg;
|
||||
opts = attrValues (mapAttrs (a : v :
|
||||
let v2 = if v ? set || v ? unset then v else { set = v; };
|
||||
n = if (getAttr (flagName a) cfgWithDefaults) then "set" else "unset";
|
||||
attr = maybeAttr n {} v2; in
|
||||
if (maybeAttr "assertion" true attr)
|
||||
then attr
|
||||
else throw "assertion of flag ${a} of derivation ${args.name} failed"
|
||||
) args2.flags );
|
||||
in removeAttrs
|
||||
(mergeAttrsByFuncDefaults ([args] ++ opts ++ [{ passthru = cfgWithDefaults; }]))
|
||||
["flags" "cfg" "mergeAttrBy" ];
|
||||
|
||||
|
||||
nixType = x:
|
||||
if isAttrs x then
|
||||
if x ? outPath then "derivation"
|
||||
else "aattrs"
|
||||
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";
|
||||
|
||||
}
|
||||
+380
@@ -0,0 +1,380 @@
|
||||
# NixOS module handling.
|
||||
|
||||
let lib = import ./default.nix; in
|
||||
|
||||
with { inherit (builtins) head; };
|
||||
with import ./trivial.nix;
|
||||
with import ./lists.nix;
|
||||
with import ./misc.nix;
|
||||
with import ./attrsets.nix;
|
||||
with import ./options.nix;
|
||||
with import ./properties.nix;
|
||||
|
||||
rec {
|
||||
|
||||
# Unfortunately this can also be a string.
|
||||
isPath = x: !(
|
||||
builtins.isFunction x
|
||||
|| builtins.isAttrs x
|
||||
|| builtins.isInt x
|
||||
|| builtins.isBool x
|
||||
|| builtins.isList x
|
||||
);
|
||||
|
||||
|
||||
importIfPath = path:
|
||||
if isPath path then
|
||||
import path
|
||||
else
|
||||
path;
|
||||
|
||||
|
||||
applyIfFunction = f: arg:
|
||||
if builtins.isFunction f then
|
||||
f arg
|
||||
else
|
||||
f;
|
||||
|
||||
|
||||
isModule = m:
|
||||
(m ? config && isAttrs m.config && ! isOption m.config)
|
||||
|| (m ? options && isAttrs m.options && ! isOption m.options);
|
||||
|
||||
|
||||
# Convert module to a set which has imports / options and config
|
||||
# attributes.
|
||||
unifyModuleSyntax = m:
|
||||
let
|
||||
delayedModule = delayProperties m;
|
||||
|
||||
getImports =
|
||||
toList (rmProperties (delayedModule.require or []));
|
||||
getImportedPaths = filter isPath getImports;
|
||||
getImportedSets = filter (x: !isPath x) getImports;
|
||||
|
||||
getConfig =
|
||||
removeAttrs delayedModule ["require" "key" "imports"];
|
||||
|
||||
in
|
||||
if isModule m then
|
||||
{ key = "<unknown location>"; } // m
|
||||
else
|
||||
{ key = "<unknown location>";
|
||||
imports = (m.imports or []) ++ getImportedPaths;
|
||||
config = getConfig;
|
||||
} // (
|
||||
if getImportedSets != [] then
|
||||
assert length getImportedSets == 1;
|
||||
{ options = head getImportedSets; }
|
||||
else
|
||||
{}
|
||||
);
|
||||
|
||||
|
||||
unifyOptionModule = {key ? "<unknown location>"}: name: index: m: (args:
|
||||
let
|
||||
module = lib.applyIfFunction m args;
|
||||
key_ = rec {
|
||||
file = key;
|
||||
option = name;
|
||||
number = index;
|
||||
outPath = key;
|
||||
};
|
||||
in if lib.isModule module then
|
||||
{ key = key_; } // module
|
||||
else
|
||||
{ key = key_; options = module; }
|
||||
);
|
||||
|
||||
|
||||
moduleClosure = initModules: args:
|
||||
let
|
||||
moduleImport = origin: index: m:
|
||||
let m' = applyIfFunction (importIfPath m) args;
|
||||
in (unifyModuleSyntax m') // {
|
||||
# used by generic closure to avoid duplicated imports.
|
||||
key =
|
||||
if isPath m then m
|
||||
else m'.key or (newModuleName origin index);
|
||||
};
|
||||
|
||||
getImports = m: m.imports or [];
|
||||
|
||||
newModuleName = origin: index:
|
||||
"${origin.key}:<import-${toString index}>";
|
||||
|
||||
topLevel = {
|
||||
key = "<top-level>";
|
||||
};
|
||||
|
||||
in
|
||||
(lazyGenericClosure {
|
||||
startSet = imap (moduleImport topLevel) initModules;
|
||||
operator = m: imap (moduleImport m) (getImports m);
|
||||
});
|
||||
|
||||
|
||||
moduleApply = funs: module:
|
||||
lib.mapAttrs (name: value:
|
||||
if builtins.hasAttr name funs then
|
||||
let fun = lib.getAttr name funs; in
|
||||
fun value
|
||||
else
|
||||
value
|
||||
) module;
|
||||
|
||||
|
||||
# Handle mkMerge function left behind after a delay property.
|
||||
moduleFlattenMerge = module:
|
||||
if module ? config &&
|
||||
isProperty module.config &&
|
||||
isMerge module.config.property
|
||||
then
|
||||
(map (cfg: { key = module.key; config = cfg; }) module.config.content)
|
||||
++ [ (module // { config = {}; }) ]
|
||||
else
|
||||
[ module ];
|
||||
|
||||
|
||||
# Handle mkMerge attributes which are left behind by previous delay
|
||||
# properties and convert them into a list of modules. Delay properties
|
||||
# inside the config attribute of a module and create a second module if a
|
||||
# mkMerge attribute was left behind.
|
||||
#
|
||||
# Module -> [ Module ]
|
||||
delayModule = module:
|
||||
map (moduleApply { config = delayProperties; }) (moduleFlattenMerge module);
|
||||
|
||||
|
||||
evalDefinitions = opt: values:
|
||||
if opt.type.delayOnGlobalEval or false then
|
||||
map (delayPropertiesWithIter opt.type.iter opt.name)
|
||||
(evalLocalProperties values)
|
||||
else
|
||||
evalProperties values;
|
||||
|
||||
|
||||
selectModule = name: m:
|
||||
{ inherit (m) key;
|
||||
} // (
|
||||
if m ? options && builtins.hasAttr name m.options then
|
||||
{ options = lib.getAttr name m.options; }
|
||||
else {}
|
||||
) // (
|
||||
if m ? config && builtins.hasAttr name m.config then
|
||||
{ config = lib.getAttr name m.config; }
|
||||
else {}
|
||||
);
|
||||
|
||||
filterModules = name: modules:
|
||||
filter (m: m ? config || m ? options) (
|
||||
map (selectModule name) modules
|
||||
);
|
||||
|
||||
|
||||
modulesNames = modules:
|
||||
lib.concatMap (m: []
|
||||
++ optionals (m ? options) (lib.attrNames m.options)
|
||||
++ optionals (m ? config) (lib.attrNames m.config)
|
||||
) modules;
|
||||
|
||||
|
||||
moduleZip = funs: modules:
|
||||
lib.mapAttrs (name: fun:
|
||||
fun (catAttrs name modules)
|
||||
) funs;
|
||||
|
||||
|
||||
moduleMerge = path: modules:
|
||||
let modules_ = modules; in
|
||||
let
|
||||
addName = name:
|
||||
if path == "" then name else path + "." + name;
|
||||
|
||||
modules = concatLists (map delayModule modules_);
|
||||
|
||||
modulesOf = name: filterModules name modules;
|
||||
declarationsOf = name: filter (m: m ? options) (modulesOf name);
|
||||
definitionsOf = name: filter (m: m ? config ) (modulesOf name);
|
||||
|
||||
recurseInto = name:
|
||||
moduleMerge (addName name) (modulesOf name);
|
||||
|
||||
recurseForOption = name: modules: args:
|
||||
moduleMerge name (
|
||||
moduleClosure modules args
|
||||
);
|
||||
|
||||
errorSource = modules:
|
||||
"The error may come from the following files:\n" + (
|
||||
lib.concatStringsSep "\n" (
|
||||
map (m:
|
||||
if m ? key then toString m.key else "<unknown location>"
|
||||
) modules
|
||||
)
|
||||
);
|
||||
|
||||
eol = "\n";
|
||||
|
||||
allNames = modulesNames modules;
|
||||
|
||||
getResults = m:
|
||||
let fetchResult = s: mapAttrs (n: v: v.result) s; in {
|
||||
options = fetchResult m.options;
|
||||
config = fetchResult m.config;
|
||||
};
|
||||
|
||||
endRecursion = { options = {}; config = {}; };
|
||||
|
||||
in if modules == [] then endRecursion else
|
||||
getResults (fix (crossResults: moduleZip {
|
||||
options = lib.zipWithNames allNames (name: values: rec {
|
||||
config = lib.getAttr name crossResults.config;
|
||||
|
||||
declarations = declarationsOf name;
|
||||
declarationSources =
|
||||
map (m: {
|
||||
source = m.key;
|
||||
}) declarations;
|
||||
|
||||
hasOptions = values != [];
|
||||
isOption = any lib.isOption values;
|
||||
|
||||
decls = # add location to sub-module options.
|
||||
map (m:
|
||||
mapSubOptions
|
||||
(unifyOptionModule {inherit (m) key;} name)
|
||||
m.options
|
||||
) declarations;
|
||||
|
||||
decl =
|
||||
lib.addErrorContext "${eol
|
||||
}while enhancing option `${addName name}':${eol
|
||||
}${errorSource declarations}${eol
|
||||
}" (
|
||||
addOptionMakeUp
|
||||
{ name = addName name; recurseInto = recurseForOption; }
|
||||
(mergeOptionDecls decls)
|
||||
);
|
||||
|
||||
value = decl // (with config; {
|
||||
inherit (config) isNotDefined;
|
||||
isDefined = ! isNotDefined;
|
||||
declarations = declarationSources;
|
||||
definitions = definitionSources;
|
||||
config = strictResult;
|
||||
});
|
||||
|
||||
recurse = (recurseInto name).options;
|
||||
|
||||
result =
|
||||
if isOption then value
|
||||
else if !hasOptions then {}
|
||||
else if all isAttrs values then recurse
|
||||
else
|
||||
throw "${eol
|
||||
}Unexpected type where option declarations are expected.${eol
|
||||
}${errorSource declarations}${eol
|
||||
}";
|
||||
|
||||
});
|
||||
|
||||
config = lib.zipWithNames allNames (name: values_: rec {
|
||||
option = lib.getAttr name crossResults.options;
|
||||
|
||||
definitions = definitionsOf name;
|
||||
definitionSources =
|
||||
map (m: {
|
||||
source = m.key;
|
||||
value = m.config;
|
||||
}) definitions;
|
||||
|
||||
values = values_ ++
|
||||
optionals (option.isOption && option.decl ? extraConfigs)
|
||||
option.decl.extraConfigs;
|
||||
|
||||
defs = evalDefinitions option.decl values;
|
||||
|
||||
isNotDefined = defs == [];
|
||||
|
||||
value =
|
||||
lib.addErrorContext "${eol
|
||||
}while evaluating the option `${addName name}':${eol
|
||||
}${errorSource (modulesOf name)}${eol
|
||||
}" (
|
||||
let opt = option.decl; in
|
||||
opt.apply (
|
||||
if isNotDefined then
|
||||
opt.default or (throw "Option `${addName name}' not defined and does not have a default value.")
|
||||
else opt.merge defs
|
||||
)
|
||||
);
|
||||
|
||||
strictResult = builtins.tryEval (builtins.toXML value);
|
||||
|
||||
recurse = (recurseInto name).config;
|
||||
|
||||
configIsAnOption = v: isOption (rmProperties v);
|
||||
errConfigIsAnOption =
|
||||
let badModules = filter (m: configIsAnOption m.config) definitions; in
|
||||
"${eol
|
||||
}Option ${addName name} is defined in the configuration section.${eol
|
||||
}${errorSource badModules}${eol
|
||||
}";
|
||||
|
||||
errDefinedWithoutDeclaration =
|
||||
let badModules = definitions; in
|
||||
"${eol
|
||||
}Option '${addName name}' defined without option declaration.${eol
|
||||
}${errorSource badModules}${eol
|
||||
}";
|
||||
|
||||
result =
|
||||
if option.isOption then value
|
||||
else if !option.hasOptions then throw errDefinedWithoutDeclaration
|
||||
else if any configIsAnOption values then throw errConfigIsAnOption
|
||||
else if all isAttrs values then recurse
|
||||
# plain value during the traversal
|
||||
else throw errDefinedWithoutDeclaration;
|
||||
|
||||
});
|
||||
} modules));
|
||||
|
||||
|
||||
fixMergeModules = initModules: {...}@args:
|
||||
lib.fix (result:
|
||||
# This trick avoids an infinite loop because names of attribute
|
||||
# are know and it is not required to evaluate the result of
|
||||
# moduleMerge to know which attributes are present as arguments.
|
||||
let module = { inherit (result) options config; }; in
|
||||
moduleMerge "" (
|
||||
moduleClosure initModules (module // args)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
# Visit all definitions to raise errors related to undeclared options.
|
||||
checkModule = path: {config, options, ...}@m:
|
||||
let
|
||||
eol = "\n";
|
||||
addName = name:
|
||||
if path == "" then name else path + "." + name;
|
||||
in
|
||||
if lib.isOption options then
|
||||
if options ? options then
|
||||
options.type.fold
|
||||
(cfg: res: res && checkModule (options.type.docPath path) cfg._args)
|
||||
true config
|
||||
else
|
||||
true
|
||||
else if isAttrs options && lib.attrNames m.options != [] then
|
||||
all (name:
|
||||
lib.addErrorContext "${eol
|
||||
}while checking the attribute `${addName name}':${eol
|
||||
}" (checkModule (addName name) (selectModule name m))
|
||||
) (lib.attrNames m.config)
|
||||
else
|
||||
builtins.trace "try to evaluate config ${lib.showVal config}."
|
||||
false;
|
||||
|
||||
}
|
||||
+315
@@ -0,0 +1,315 @@
|
||||
# Nixpkgs/NixOS option handling.
|
||||
|
||||
let lib = import ./default.nix; in
|
||||
|
||||
with { inherit (builtins) head length; };
|
||||
with import ./trivial.nix;
|
||||
with import ./lists.nix;
|
||||
with import ./misc.nix;
|
||||
with import ./attrsets.nix;
|
||||
with import ./properties.nix;
|
||||
|
||||
rec {
|
||||
|
||||
inherit (lib) isType;
|
||||
|
||||
|
||||
isOption = isType "option";
|
||||
mkOption = attrs: attrs // {
|
||||
_type = "option";
|
||||
# name (this is the name of the attributem it is automatically generated by the traversal)
|
||||
# default (value used when no definition exists)
|
||||
# example (documentation)
|
||||
# description (documentation)
|
||||
# type (option type, provide a default merge function and ensure type correctness)
|
||||
# merge (function used to merge definitions into one definition: [ /type/ ] -> /type/)
|
||||
# apply (convert the option value to ease the manipulation of the option result)
|
||||
# options (set of sub-options declarations & definitions)
|
||||
# extraConfigs (list of possible configurations)
|
||||
};
|
||||
|
||||
mkEnableOption = name: mkOption {
|
||||
default = false;
|
||||
example = true;
|
||||
description = "Whether to enable ${name}";
|
||||
type = lib.types.bool;
|
||||
};
|
||||
|
||||
mapSubOptions = f: opt:
|
||||
if opt ? options then
|
||||
opt // {
|
||||
options = imap f (toList opt.options);
|
||||
}
|
||||
else
|
||||
opt;
|
||||
|
||||
# Make the option declaration more user-friendly by adding default
|
||||
# settings and some verifications based on the declaration content (like
|
||||
# type correctness).
|
||||
addOptionMakeUp = {name, recurseInto}: decl:
|
||||
let
|
||||
init = {
|
||||
inherit name;
|
||||
merge = mergeDefaultOption;
|
||||
apply = lib.id;
|
||||
};
|
||||
|
||||
functionsFromType = opt:
|
||||
opt // (builtins.intersectAttrs { merge = 1; check = 1; } (decl.type or {}));
|
||||
|
||||
addDeclaration = opt: opt // decl;
|
||||
|
||||
ensureMergeInputType = opt:
|
||||
if opt ? check then
|
||||
opt // {
|
||||
merge = list:
|
||||
if all opt.check list then
|
||||
opt.merge list
|
||||
else
|
||||
throw "One of option ${name} values has a bad type.";
|
||||
}
|
||||
else opt;
|
||||
|
||||
checkDefault = opt:
|
||||
if opt ? check && opt ? default then
|
||||
opt // {
|
||||
default =
|
||||
if opt.check opt.default then
|
||||
opt.default
|
||||
else
|
||||
throw "The default value of option ${name} has a bad type.";
|
||||
}
|
||||
else opt;
|
||||
|
||||
handleOptionSets = opt:
|
||||
if opt ? type && opt.type.hasOptions then
|
||||
let
|
||||
# Evaluate sub-modules.
|
||||
subModuleMerge = path: vals:
|
||||
lib.fix (args:
|
||||
let
|
||||
result = recurseInto path (opt.options ++ imap (index: v: args: {
|
||||
key = rec {
|
||||
#!!! Would be nice if we had the file the val was from
|
||||
option = path;
|
||||
number = index;
|
||||
outPath = "option ${option} config number ${toString number}";
|
||||
};
|
||||
} // (lib.applyIfFunction v args)) (toList vals)) args;
|
||||
name = lib.removePrefix (opt.name + ".") path;
|
||||
extraArgs = opt.extraArgs or {};
|
||||
individualExtraArgs = opt.individualExtraArgs or {};
|
||||
in {
|
||||
inherit (result) config options;
|
||||
inherit name;
|
||||
} //
|
||||
(opt.extraArgs or {}) //
|
||||
(if hasAttr name individualExtraArgs then getAttr name individualExtraArgs else {})
|
||||
);
|
||||
|
||||
# Add _options in sub-modules to make it viewable from other
|
||||
# modules.
|
||||
subModuleMergeConfig = path: vals:
|
||||
let result = subModuleMerge path vals; in
|
||||
{ _args = result; } // result.config;
|
||||
|
||||
in
|
||||
opt // {
|
||||
merge = list:
|
||||
opt.type.iter
|
||||
subModuleMergeConfig
|
||||
opt.name
|
||||
(opt.merge list);
|
||||
options =
|
||||
let path = opt.type.docPath opt.name; in
|
||||
(subModuleMerge path []).options;
|
||||
}
|
||||
else
|
||||
opt;
|
||||
in
|
||||
foldl (opt: f: f opt) init [
|
||||
# default settings
|
||||
functionsFromType
|
||||
|
||||
# user settings
|
||||
addDeclaration
|
||||
|
||||
# override settings
|
||||
ensureMergeInputType
|
||||
checkDefault
|
||||
handleOptionSets
|
||||
];
|
||||
|
||||
# Merge a list of options containning different field. This is useful to
|
||||
# separate the merge & apply fields from the interface.
|
||||
mergeOptionDecls = opts:
|
||||
if opts == [] then {}
|
||||
else if length opts == 1 then
|
||||
let opt = head opts; in
|
||||
if opt ? options then
|
||||
opt // { options = toList opt.options; }
|
||||
else
|
||||
opt
|
||||
else
|
||||
fold (opt1: opt2:
|
||||
lib.addErrorContext "opt1 = ${lib.showVal opt1}\nopt2 = ${lib.showVal opt2}" (
|
||||
# You cannot merge if two options have the same field.
|
||||
assert opt1 ? default -> ! opt2 ? default;
|
||||
assert opt1 ? example -> ! opt2 ? example;
|
||||
assert opt1 ? description -> ! opt2 ? description;
|
||||
assert opt1 ? merge -> ! opt2 ? merge;
|
||||
assert opt1 ? apply -> ! opt2 ? apply;
|
||||
assert opt1 ? type -> ! opt2 ? type;
|
||||
opt1 // opt2
|
||||
// optionalAttrs (opt1 ? options || opt2 ? options) {
|
||||
options =
|
||||
(toList (opt1.options or []))
|
||||
++ (toList (opt2.options or []));
|
||||
}
|
||||
// optionalAttrs (opt1 ? extraConfigs || opt2 ? extraConfigs) {
|
||||
extraConfigs = opt1.extraConfigs or [] ++ opt2.extraConfigs or [];
|
||||
}
|
||||
// optionalAttrs (opt1 ? extraArgs || opt2 ? extraArgs) {
|
||||
extraArgs = opt1.extraArgs or {} // opt2.extraArgs or {};
|
||||
}
|
||||
// optionalAttrs (opt1 ? individualExtraArgs || opt2 ? individualExtraArgs) {
|
||||
individualExtraArgs = zipAttrsWith (name: values:
|
||||
if length values == 1 then head values else (head values // (head (tail values)))
|
||||
) [ (opt1.individualExtraArgs or {}) (opt2.individualExtraArgs or {}) ];
|
||||
}
|
||||
)) {} opts;
|
||||
|
||||
|
||||
# !!! This function will be removed because this can be done with the
|
||||
# multiple option declarations.
|
||||
addDefaultOptionValues = defs: opts: opts //
|
||||
builtins.listToAttrs (map (defName:
|
||||
{ name = defName;
|
||||
value =
|
||||
let
|
||||
defValue = builtins.getAttr defName defs;
|
||||
optValue = builtins.getAttr defName opts;
|
||||
in
|
||||
if isOption defValue
|
||||
then
|
||||
# `defValue' is an option.
|
||||
if hasAttr defName opts
|
||||
then builtins.getAttr defName opts
|
||||
else defValue.default
|
||||
else
|
||||
# `defValue' is an attribute set containing options.
|
||||
# So recurse.
|
||||
if hasAttr defName opts && isAttrs optValue
|
||||
then addDefaultOptionValues defValue optValue
|
||||
else addDefaultOptionValues defValue {};
|
||||
}
|
||||
) (attrNames defs));
|
||||
|
||||
mergeDefaultOption = list:
|
||||
if length list == 1 then head list
|
||||
else if all builtins.isFunction list then x: mergeDefaultOption (map (f: f x) list)
|
||||
else if all isList list then concatLists list
|
||||
else if all isAttrs list then fold lib.mergeAttrs {} list
|
||||
else if all builtins.isBool list then fold lib.or false list
|
||||
else if all builtins.isString list then lib.concatStrings list
|
||||
else if all builtins.isInt list && all (x: x == head list) list
|
||||
then head list
|
||||
else throw "Cannot merge values.";
|
||||
|
||||
mergeTypedOption = typeName: predicate: merge: list:
|
||||
if all predicate list then merge list
|
||||
else throw "Expect a ${typeName}.";
|
||||
|
||||
mergeEnableOption = mergeTypedOption "boolean"
|
||||
(x: true == x || false == x) (fold lib.or false);
|
||||
|
||||
mergeListOption = mergeTypedOption "list" isList concatLists;
|
||||
|
||||
mergeStringOption = mergeTypedOption "string"
|
||||
(x: if builtins ? isString then builtins.isString x else x + "")
|
||||
lib.concatStrings;
|
||||
|
||||
mergeOneOption = list:
|
||||
if list == [] then abort "This case should never happen."
|
||||
else if length list != 1 then throw "Multiple definitions. Only one is allowed for this option."
|
||||
else head list;
|
||||
|
||||
|
||||
fixableMergeFun = merge: f: config:
|
||||
merge (
|
||||
# generate the list of option sets.
|
||||
f config
|
||||
);
|
||||
|
||||
fixableMergeModules = merge: initModules: {...}@args: config:
|
||||
fixableMergeFun merge (config:
|
||||
lib.moduleClosure initModules (args // { inherit config; })
|
||||
) config;
|
||||
|
||||
|
||||
fixableDefinitionsOf = initModules: {...}@args:
|
||||
fixableMergeModules (modules: (lib.moduleMerge "" modules).config) initModules args;
|
||||
|
||||
fixableDeclarationsOf = initModules: {...}@args:
|
||||
fixableMergeModules (modules: (lib.moduleMerge "" modules).options) initModules args;
|
||||
|
||||
definitionsOf = initModules: {...}@args:
|
||||
(lib.fix (module:
|
||||
fixableMergeModules (lib.moduleMerge "") initModules args module.config
|
||||
)).config;
|
||||
|
||||
declarationsOf = initModules: {...}@args:
|
||||
(lib.fix (module:
|
||||
fixableMergeModules (lib.moduleMerge "") initModules args module.config
|
||||
)).options;
|
||||
|
||||
|
||||
# Generate documentation template from the list of option declaration like
|
||||
# the set generated with filterOptionSets.
|
||||
optionAttrSetToDocList = ignore: newOptionAttrSetToDocList;
|
||||
newOptionAttrSetToDocList = attrs:
|
||||
let options = collect isOption attrs; in
|
||||
fold (opt: rest:
|
||||
let
|
||||
docOption = {
|
||||
inherit (opt) name;
|
||||
description = if opt ? description then opt.description else
|
||||
throw "Option ${opt.name}: No description.";
|
||||
|
||||
declarations = map (x: toString x.source) opt.declarations;
|
||||
#definitions = map (x: toString x.source) opt.definitions;
|
||||
}
|
||||
// optionalAttrs (opt ? example) { example = scrubOptionValue opt.example; }
|
||||
// optionalAttrs (opt ? default) { default = scrubOptionValue opt.default; }
|
||||
// optionalAttrs (opt ? defaultText) { default = opt.defaultText; };
|
||||
|
||||
subOptions =
|
||||
if opt ? options then
|
||||
newOptionAttrSetToDocList opt.options
|
||||
else
|
||||
[];
|
||||
in
|
||||
[ docOption ] ++ subOptions ++ rest
|
||||
) [] options;
|
||||
|
||||
|
||||
/* 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 efficient: the XML
|
||||
representation of derivations is very large (on the order of
|
||||
megabytes) and is not actually used by the manual generator. */
|
||||
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;
|
||||
|
||||
|
||||
/* For use in the ‘example’ option attribute. It causes the given
|
||||
text to be included verbatim in documentation. This is necessary
|
||||
for example values that are not simple values, e.g.,
|
||||
functions. */
|
||||
literalExample = text: { _type = "literalExample"; inherit text; };
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
let lists = import ./lists.nix; in
|
||||
|
||||
rec {
|
||||
gnu = linux; /* ++ hurd ++ kfreebsd ++ ... */
|
||||
linux = ["i686-linux" "x86_64-linux" "powerpc-linux" "armv5tel-linux" "armv7l-linux" "mips64el-linux"];
|
||||
darwin = ["x86_64-darwin"];
|
||||
freebsd = ["i686-freebsd" "x86_64-freebsd" "powerpc-freebsd"];
|
||||
openbsd = ["i686-openbsd" "x86_64-openbsd"];
|
||||
netbsd = ["i686-netbsd" "x86_64-netbsd"];
|
||||
cygwin = ["i686-cygwin"];
|
||||
unix = linux ++ darwin ++ freebsd ++ openbsd;
|
||||
all = linux ++ darwin ++ cygwin ++ freebsd ++ openbsd;
|
||||
none = [];
|
||||
allBut = platform: lists.filter (x: platform != x) all;
|
||||
mesaPlatforms = ["i686-linux" "x86_64-linux" "x86_64-darwin" "armv5tel-linux" "armv6l-linux"];
|
||||
}
|
||||
@@ -0,0 +1,464 @@
|
||||
# Nixpkgs/NixOS properties. Generalize the problem of delayable (not yet
|
||||
# evaluable) properties like mkIf.
|
||||
|
||||
let lib = import ./default.nix; in
|
||||
|
||||
with { inherit (builtins) head tail; };
|
||||
with import ./trivial.nix;
|
||||
with import ./lists.nix;
|
||||
with import ./misc.nix;
|
||||
with import ./attrsets.nix;
|
||||
|
||||
rec {
|
||||
|
||||
inherit (lib) isType;
|
||||
|
||||
# Tell that nothing is defined. When properties are evaluated, this type
|
||||
# is used to remove an entry. Thus if your property evaluation semantic
|
||||
# implies that you have to mute the content of an attribute, then your
|
||||
# property should produce this value.
|
||||
isNotdef = isType "notdef";
|
||||
mkNotdef = {_type = "notdef";};
|
||||
|
||||
# General property type, it has a property attribute and a content
|
||||
# attribute. The property attribute refers to an attribute set which
|
||||
# contains a _type attribute and a list of functions which are used to
|
||||
# evaluate this property. The content attribute is used to stack properties
|
||||
# on top of each other.
|
||||
#
|
||||
# The optional functions which may be contained in the property attribute
|
||||
# are:
|
||||
# - onDelay: run on a copied property.
|
||||
# - onGlobalDelay: run on all copied properties.
|
||||
# - onEval: run on an evaluated property.
|
||||
# - onGlobalEval: run on a list of property stack on top of their values.
|
||||
isProperty = isType "property";
|
||||
mkProperty = p@{property, content, ...}: p // {
|
||||
_type = "property";
|
||||
};
|
||||
|
||||
# Go through the stack of properties and apply the function `op' on all
|
||||
# property and call the function `nul' on the final value which is not a
|
||||
# property. The stack is traversed in reversed order. The `op' function
|
||||
# should expect a property with a content which have been modified.
|
||||
#
|
||||
# Warning: The `op' function expects only one argument in order to avoid
|
||||
# calls to mkProperties as the argument is already a valid property which
|
||||
# contains the result of the folding inside the content attribute.
|
||||
foldProperty = op: nul: attrs:
|
||||
if isProperty attrs then
|
||||
op (attrs // {
|
||||
content = foldProperty op nul attrs.content;
|
||||
})
|
||||
else
|
||||
nul attrs;
|
||||
|
||||
# Simple function which can be used as the `op' argument of the
|
||||
# foldProperty function. Properties that you don't want to handle can be
|
||||
# ignored with the `id' function. `isSearched' is a function which should
|
||||
# check the type of a property and return a boolean value. `thenFun' and
|
||||
# `elseFun' are functions which behave as the `op' argument of the
|
||||
# foldProperty function.
|
||||
foldFilter = isSearched: thenFun: elseFun: attrs:
|
||||
if isSearched attrs.property then
|
||||
thenFun attrs
|
||||
else
|
||||
elseFun attrs;
|
||||
|
||||
|
||||
# Move properties from the current attribute set to the attribute
|
||||
# contained in this attribute set. This trigger property handlers called
|
||||
# `onDelay' and `onGlobalDelay'.
|
||||
delayPropertiesWithIter = iter: path: attrs:
|
||||
let cleanAttrs = rmProperties attrs; in
|
||||
if isProperty attrs then
|
||||
iter (a: v:
|
||||
lib.addErrorContext "while moving properties on the attribute `${a}':" (
|
||||
triggerPropertiesGlobalDelay a (
|
||||
triggerPropertiesDelay a (
|
||||
copyProperties attrs v
|
||||
)))) path cleanAttrs
|
||||
else
|
||||
attrs;
|
||||
|
||||
delayProperties = # implicit attrs argument.
|
||||
let
|
||||
# mapAttrs except that it also recurse into potential mkMerge
|
||||
# functions. This may cause a strictness issue because looking the
|
||||
# type of a string implies evaluating it.
|
||||
iter = fun: path: value:
|
||||
lib.mapAttrs (attr: val:
|
||||
if isProperty val && isMerge val.property then
|
||||
val // { content = map (fun attr) val.content; }
|
||||
else
|
||||
fun attr val
|
||||
) value;
|
||||
in
|
||||
delayPropertiesWithIter iter "";
|
||||
|
||||
# Call onDelay functions.
|
||||
triggerPropertiesDelay = name: attrs:
|
||||
let
|
||||
callOnDelay = p@{property, ...}:
|
||||
if property ? onDelay then
|
||||
property.onDelay name p
|
||||
else
|
||||
p;
|
||||
in
|
||||
foldProperty callOnDelay id attrs;
|
||||
|
||||
# Call onGlobalDelay functions.
|
||||
triggerPropertiesGlobalDelay = name: attrs:
|
||||
let
|
||||
globalDelayFuns = uniqListExt {
|
||||
getter = property: property._type;
|
||||
inputList = foldProperty (p@{property, content, ...}:
|
||||
if property ? onGlobalDelay then
|
||||
[ property ] ++ content
|
||||
else
|
||||
content
|
||||
) (a: []) attrs;
|
||||
};
|
||||
|
||||
callOnGlobalDelay = property: content:
|
||||
property.onGlobalDelay name content;
|
||||
in
|
||||
fold callOnGlobalDelay attrs globalDelayFuns;
|
||||
|
||||
# Expect a list of values which may have properties and return the same
|
||||
# list of values where all properties have been evaluated and where all
|
||||
# ignored values are removed. This trigger property handlers called
|
||||
# `onEval' and `onGlobalEval'.
|
||||
evalProperties = valList:
|
||||
if valList != [] then
|
||||
filter (x: !isNotdef x) (
|
||||
triggerPropertiesGlobalEval (
|
||||
evalLocalProperties valList
|
||||
)
|
||||
)
|
||||
else
|
||||
valList;
|
||||
|
||||
evalLocalProperties = valList:
|
||||
filter (x: !isNotdef x) (
|
||||
map triggerPropertiesEval valList
|
||||
);
|
||||
|
||||
# Call onEval function
|
||||
triggerPropertiesEval = val:
|
||||
foldProperty (p@{property, ...}:
|
||||
if property ? onEval then
|
||||
property.onEval p
|
||||
else
|
||||
p
|
||||
) id val;
|
||||
|
||||
# Call onGlobalEval function
|
||||
triggerPropertiesGlobalEval = valList:
|
||||
let
|
||||
globalEvalFuns = uniqListExt {
|
||||
getter = property: property._type;
|
||||
inputList =
|
||||
fold (attrs: list:
|
||||
foldProperty (p@{property, content, ...}:
|
||||
if property ? onGlobalEval then
|
||||
[ property ] ++ content
|
||||
else
|
||||
content
|
||||
) (a: list) attrs
|
||||
) [] valList;
|
||||
};
|
||||
|
||||
callOnGlobalEval = property: valList: property.onGlobalEval valList;
|
||||
in
|
||||
fold callOnGlobalEval valList globalEvalFuns;
|
||||
|
||||
# Remove all properties on top of a value and return the value.
|
||||
rmProperties =
|
||||
foldProperty (p@{content, ...}: content) id;
|
||||
|
||||
# Copy properties defined on a value on another value.
|
||||
copyProperties = attrs: newAttrs:
|
||||
foldProperty id (x: newAttrs) attrs;
|
||||
|
||||
/* Merge. */
|
||||
|
||||
# Create "merge" statement which is skipped by the delayProperty function
|
||||
# and interpreted by the underlying system using properties (modules).
|
||||
|
||||
# Create a "Merge" property which only contains a condition.
|
||||
isMerge = isType "merge";
|
||||
mkMerge = content: mkProperty {
|
||||
property = {
|
||||
_type = "merge";
|
||||
onDelay = name: val: throw "mkMerge is not the first of the list of properties.";
|
||||
onEval = val: throw "mkMerge is not allowed on option definitions.";
|
||||
};
|
||||
inherit content;
|
||||
};
|
||||
|
||||
/* If. ThenElse. Always. */
|
||||
|
||||
# create "if" statement that can be delayed on sets until a "then-else" or
|
||||
# "always" set is reached. When an always set is reached the condition
|
||||
# is ignore.
|
||||
|
||||
# Create a "If" property which only contains a condition.
|
||||
isIf = isType "if";
|
||||
mkIf = condition: content: mkProperty {
|
||||
property = {
|
||||
_type = "if";
|
||||
onGlobalDelay = onIfGlobalDelay;
|
||||
onEval = onIfEval;
|
||||
inherit condition;
|
||||
};
|
||||
inherit content;
|
||||
};
|
||||
|
||||
mkAssert = assertion: message: content:
|
||||
mkIf
|
||||
(if assertion then true else throw "\nFailed assertion: ${message}")
|
||||
content;
|
||||
|
||||
# Evaluate the "If" statements when either "ThenElse" or "Always"
|
||||
# statement is encountered. Otherwise it removes multiple If statements and
|
||||
# replaces them by one "If" statement where the condition is the list of all
|
||||
# conditions joined with a "and" operation.
|
||||
onIfGlobalDelay = name: content:
|
||||
let
|
||||
# extract if statements and non-if statements and repectively put them
|
||||
# in the attribute list and attrs.
|
||||
ifProps =
|
||||
foldProperty
|
||||
(foldFilter (p: isIf p)
|
||||
# then, push the condition inside the list list
|
||||
(p@{property, content, ...}:
|
||||
{ inherit (content) attrs;
|
||||
list = [property] ++ content.list;
|
||||
}
|
||||
)
|
||||
# otherwise, add the propertie.
|
||||
(p@{property, content, ...}:
|
||||
{ inherit (content) list;
|
||||
attrs = p // { content = content.attrs; };
|
||||
}
|
||||
)
|
||||
)
|
||||
(attrs: { list = []; inherit attrs; })
|
||||
content;
|
||||
|
||||
# compute the list of if statements.
|
||||
evalIf = content: condition: list:
|
||||
if list == [] then
|
||||
mkIf condition content
|
||||
else
|
||||
let p = head list; in
|
||||
evalIf content (condition && p.condition) (tail list);
|
||||
in
|
||||
evalIf ifProps.attrs true ifProps.list;
|
||||
|
||||
# Evaluate the condition of the "If" statement to either get the value or
|
||||
# to ignore the value.
|
||||
onIfEval = p@{property, content, ...}:
|
||||
if property.condition then
|
||||
content
|
||||
else
|
||||
mkNotdef;
|
||||
|
||||
/* mkOverride */
|
||||
|
||||
# Create an "Override" statement which allow the user to define
|
||||
# priorities between values. The default priority is 100. The lowest
|
||||
# priorities are kept. The template argument must reproduce the same
|
||||
# attribute set hierarchy to override leaves of the hierarchy.
|
||||
isOverride = isType "override";
|
||||
mkOverrideTemplate = priority: template: content: mkProperty {
|
||||
property = {
|
||||
_type = "override";
|
||||
onDelay = onOverrideDelay;
|
||||
onGlobalEval = onOverrideGlobalEval;
|
||||
inherit priority template;
|
||||
};
|
||||
inherit content;
|
||||
};
|
||||
|
||||
# Like mkOverrideTemplate, but without the template argument.
|
||||
mkOverride = priority: content: mkOverrideTemplate priority {} content;
|
||||
|
||||
# Sugar to override the default value of the option by making a new
|
||||
# default value based on the configuration.
|
||||
mkDefaultValue = mkOverride 1000;
|
||||
mkDefault = mkOverride 1000;
|
||||
mkForce = mkOverride 50;
|
||||
mkStrict = mkOverride 0;
|
||||
|
||||
# Make the template traversal in function of the property traversal. If
|
||||
# the template define a non-empty attribute set, then the property is
|
||||
# copied only on all mentionned attributes inside the template.
|
||||
# Otherwise, the property is kept on all sub-attribute definitions.
|
||||
onOverrideDelay = name: p@{property, content, ...}:
|
||||
let inherit (property) template; in
|
||||
if isAttrs template && template != {} then
|
||||
if hasAttr name template then
|
||||
p // {
|
||||
property = p.property // {
|
||||
template = builtins.getAttr name template;
|
||||
};
|
||||
}
|
||||
# Do not override the attribute \name\
|
||||
else
|
||||
content
|
||||
# Override values defined inside the attribute \name\.
|
||||
else
|
||||
p;
|
||||
|
||||
# Keep values having lowest priority numbers only throwing away those having
|
||||
# a higher priority assigned.
|
||||
onOverrideGlobalEval = valList:
|
||||
let
|
||||
defaultPrio = 100;
|
||||
|
||||
inherit (builtins) lessThan;
|
||||
|
||||
getPrioVal =
|
||||
foldProperty
|
||||
(foldFilter isOverride
|
||||
(p@{property, content, ...}:
|
||||
if content ? priority && lessThan content.priority property.priority then
|
||||
content
|
||||
else
|
||||
content // {
|
||||
inherit (property) priority;
|
||||
}
|
||||
)
|
||||
(p@{property, content, ...}:
|
||||
content // {
|
||||
value = p // { content = content.value; };
|
||||
}
|
||||
)
|
||||
) (value: { inherit value; });
|
||||
|
||||
addDefaultPrio = x:
|
||||
if x ? priority then x
|
||||
else x // { priority = defaultPrio; };
|
||||
|
||||
prioValList = map (x: addDefaultPrio (getPrioVal x)) valList;
|
||||
|
||||
higherPrio =
|
||||
if prioValList == [] then
|
||||
defaultPrio
|
||||
else
|
||||
fold (x: min:
|
||||
if lessThan x.priority min then
|
||||
x.priority
|
||||
else
|
||||
min
|
||||
) (head prioValList).priority (tail prioValList);
|
||||
in
|
||||
map (x:
|
||||
if x.priority == higherPrio then
|
||||
x.value
|
||||
else
|
||||
mkNotdef
|
||||
) prioValList;
|
||||
|
||||
/* mkOrder */
|
||||
|
||||
# Order definitions based on there index value. This property is useful
|
||||
# when the result of the merge function depends on the order on the
|
||||
# initial list. (e.g. concatStrings) Definitions are ordered based on
|
||||
# their rank. The lowest ranked definition would be the first to element
|
||||
# of the list used by the merge function. And the highest ranked
|
||||
# definition would be the last. Definitions which does not have any rank
|
||||
# value have the default rank of 100.
|
||||
isOrder = isType "order";
|
||||
mkOrder = rank: content: mkProperty {
|
||||
property = {
|
||||
_type = "order";
|
||||
onGlobalEval = onOrderGlobalEval;
|
||||
inherit rank;
|
||||
};
|
||||
inherit content;
|
||||
};
|
||||
|
||||
mkHeader = mkOrder 10;
|
||||
mkFooter = mkOrder 1000;
|
||||
|
||||
# Fetch the rank of each definition (add the default rank is none) and
|
||||
# sort them based on their ranking.
|
||||
onOrderGlobalEval = valList:
|
||||
let
|
||||
defaultRank = 100;
|
||||
|
||||
inherit (builtins) lessThan;
|
||||
|
||||
getRankVal =
|
||||
foldProperty
|
||||
(foldFilter isOrder
|
||||
(p@{property, content, ...}:
|
||||
if content ? rank then
|
||||
content
|
||||
else
|
||||
content // {
|
||||
inherit (property) rank;
|
||||
}
|
||||
)
|
||||
(p@{property, content, ...}:
|
||||
content // {
|
||||
value = p // { content = content.value; };
|
||||
}
|
||||
)
|
||||
) (value: { inherit value; });
|
||||
|
||||
addDefaultRank = x:
|
||||
if x ? rank then x
|
||||
else x // { rank = defaultRank; };
|
||||
|
||||
rankValList = map (x: addDefaultRank (getRankVal x)) valList;
|
||||
|
||||
cmp = x: y:
|
||||
builtins.lessThan x.rank y.rank;
|
||||
in
|
||||
map (x: x.value) (sort cmp rankValList);
|
||||
|
||||
/* mkFixStrictness */
|
||||
|
||||
# This is a hack used to restore laziness on some option definitions.
|
||||
# Some option definitions are evaluated when they are not used. This
|
||||
# error is caused by the strictness of type checking builtins. Builtins
|
||||
# like 'isAttrs' are too strict because they have to evaluate their
|
||||
# arguments to check if the type is correct. This evaluation, cause the
|
||||
# strictness of properties.
|
||||
#
|
||||
# Properties can be stacked on top of each other. The stackability of
|
||||
# properties on top of the option definition is nice for user manipulation
|
||||
# but require to check if the content of the property is not another
|
||||
# property. Such testing implies to verify if this is an attribute set
|
||||
# and if it possess the type 'property'. (see isProperty & typeOf/isType)
|
||||
#
|
||||
# To avoid strict evaluation of option definitions, 'mkFixStrictness' is
|
||||
# introduced. This property protects an option definition by replacing
|
||||
# the base of the stack of properties by 'mkNotDef', when this property is
|
||||
# evaluated it returns the original definition.
|
||||
#
|
||||
# This property is useful over any elements which depends on options which
|
||||
# are raising errors when they get evaluated without the proper settings.
|
||||
#
|
||||
# Plain list and attribute set are lazy structures, which means that the
|
||||
# container gets evaluated but not the content. Thus, using this property
|
||||
# on top of plain list or attribute set is pointless.
|
||||
#
|
||||
# This is a Hack, you should avoid it!
|
||||
|
||||
# This property has a long name because you should avoid it.
|
||||
isFixStrictness = attrs: (typeOf attrs) == "fix-strictness";
|
||||
mkFixStrictness = value:
|
||||
mkProperty {
|
||||
property = {
|
||||
_type = "fix-strictness";
|
||||
onEval = p: value;
|
||||
};
|
||||
content = mkNotdef;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
# Functions for copying sources to the Nix store.
|
||||
|
||||
let lib = import ./default.nix; in
|
||||
|
||||
rec {
|
||||
|
||||
|
||||
# Bring in a path as a source, filtering out all Subversion and CVS
|
||||
# directories, as well as backup files (*~).
|
||||
cleanSource =
|
||||
let filter = name: type: let baseName = baseNameOf (toString name); in ! (
|
||||
# Filter out Subversion and CVS directories.
|
||||
(type == "directory" && (baseName == ".git" || baseName == ".svn" || baseName == "CVS")) ||
|
||||
# Filter out backup files.
|
||||
(lib.hasSuffix "~" baseName)
|
||||
);
|
||||
in src: builtins.filterSource filter src;
|
||||
|
||||
|
||||
# Get all files ending with the specified suffices from the given
|
||||
# directory. E.g. `sourceFilesBySuffices ./dir [".xml" ".c"]'.
|
||||
sourceFilesBySuffices = path: exts:
|
||||
let filter = name: type:
|
||||
let base = baseNameOf (toString name);
|
||||
in type != "directory" && lib.any (ext: lib.hasSuffix ext base) exts;
|
||||
in builtins.filterSource filter path;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
Usage:
|
||||
|
||||
You define you custom builder script by adding all build steps to a list.
|
||||
for example:
|
||||
builder = writeScript "fsg-4.4-builder"
|
||||
(textClosure [doUnpack addInputs preBuild doMake installPhase doForceShare]);
|
||||
|
||||
a step is defined by noDepEntry, fullDepEntry or packEntry.
|
||||
To ensure that prerequisite are met those are added before the task itself by
|
||||
textClosureDupList. Duplicated items are removed again.
|
||||
|
||||
See trace/nixpkgs/trunk/pkgs/top-level/builder-defs.nix for some predefined build steps
|
||||
|
||||
Attention:
|
||||
|
||||
let
|
||||
pkgs = (import /etc/nixos/nixpkgs/pkgs/top-level/all-packages.nix) {};
|
||||
in let
|
||||
inherit (pkgs.stringsWithDeps) fullDepEntry packEntry noDepEntry textClosureMap;
|
||||
inherit (pkgs.lib) id;
|
||||
|
||||
nameA = noDepEntry "Text a";
|
||||
nameB = fullDepEntry "Text b" ["nameA"];
|
||||
nameC = fullDepEntry "Text c" ["nameA"];
|
||||
|
||||
stages = {
|
||||
nameHeader = noDepEntry "#! /bin/sh \n";
|
||||
inherit nameA nameB nameC;
|
||||
};
|
||||
in
|
||||
textClosureMap id stages
|
||||
[ "nameHeader" "nameA" "nameB" "nameC"
|
||||
nameC # <- added twice. add a dep entry if you know that it will be added once only [1]
|
||||
"nameB" # <- this will not be added again because the attr name (reference) is used
|
||||
]
|
||||
|
||||
# result: Str("#! /bin/sh \n\nText a\nText b\nText c\nText c",[])
|
||||
|
||||
[1] maybe this behaviour should be removed to keep things simple (?)
|
||||
*/
|
||||
|
||||
with import ./lists.nix;
|
||||
with import ./attrsets.nix;
|
||||
with import ./strings.nix;
|
||||
|
||||
rec {
|
||||
|
||||
/* !!! 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. */
|
||||
|
||||
textClosureList = predefined: arg:
|
||||
let
|
||||
f = done: todo:
|
||||
if todo == [] then {result = []; inherit done;}
|
||||
else
|
||||
let entry = head todo; in
|
||||
if isAttrs entry then
|
||||
let x = f done entry.deps;
|
||||
y = f x.done (tail todo);
|
||||
in { result = x.result ++ [entry.text] ++ y.result;
|
||||
done = y.done;
|
||||
}
|
||||
else if hasAttr entry done then f done (tail todo)
|
||||
else f (done // listToAttrs [{name = entry; value = 1;}]) ([(builtins.getAttr entry predefined)] ++ tail todo);
|
||||
in (f {} arg).result;
|
||||
|
||||
textClosureMap = f: predefined: names:
|
||||
concatStringsSep "\n" (map f (textClosureList predefined names));
|
||||
|
||||
noDepEntry = text: {inherit text; deps = [];};
|
||||
fullDepEntry = text: deps: {inherit text deps;};
|
||||
packEntry = deps: {inherit deps; text="";};
|
||||
|
||||
stringAfter = deps: text: { inherit text deps; };
|
||||
|
||||
}
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
/* String manipulation functions. */
|
||||
|
||||
let lib = import ./default.nix;
|
||||
|
||||
inherit (builtins) add sub lessThan length;
|
||||
|
||||
in
|
||||
|
||||
rec {
|
||||
inherit (builtins) stringLength substring head tail;
|
||||
|
||||
|
||||
# Concatenate a list of strings.
|
||||
concatStrings = lib.fold (x: y: x + y) "";
|
||||
|
||||
|
||||
# Map a function over a list and concatenate the resulting strings.
|
||||
concatMapStrings = f: list: concatStrings (map f list);
|
||||
concatImapStrings = f: list: concatStrings (lib.imap f list);
|
||||
|
||||
|
||||
# Place an element between each element of a list, e.g.,
|
||||
# `intersperse "," ["a" "b" "c"]' returns ["a" "," "b" "," "c"].
|
||||
intersperse = separator: list:
|
||||
if list == [] || length list == 1
|
||||
then list
|
||||
else [(head list) separator]
|
||||
++ (intersperse separator (tail list));
|
||||
|
||||
|
||||
# Concatenate a list of strings with a separator between each element, e.g.
|
||||
# concatStringsSep " " ["foo" "bar" "xyzzy"] == "foo bar xyzzy"
|
||||
concatStringsSep = separator: list:
|
||||
concatStrings (intersperse separator list);
|
||||
|
||||
|
||||
# Construct a Unix-style search path consisting of each `subDir"
|
||||
# directory of the given list of packages. For example,
|
||||
# `makeSearchPath "bin" ["x" "y" "z"]' returns "x/bin:y/bin:z/bin".
|
||||
makeSearchPath = subDir: packages:
|
||||
concatStringsSep ":" (map (path: path + "/" + subDir) packages);
|
||||
|
||||
|
||||
# Construct a library search path (such as RPATH) containing the
|
||||
# libraries for a set of packages, e.g. "${pkg1}/lib:${pkg2}/lib:...".
|
||||
makeLibraryPath = makeSearchPath "lib";
|
||||
|
||||
|
||||
# Idem for Perl search paths.
|
||||
makePerlPath = makeSearchPath "lib/perl5/site_perl";
|
||||
|
||||
|
||||
# Dependening on the boolean `cond', return either the given string
|
||||
# or the empty string.
|
||||
optionalString = cond: string: if cond then string else "";
|
||||
|
||||
|
||||
# Determine whether a filename ends in the given suffix.
|
||||
hasSuffix = ext: fileName:
|
||||
let lenFileName = stringLength fileName;
|
||||
lenExt = stringLength ext;
|
||||
in !(lessThan lenFileName lenExt) &&
|
||||
substring (sub lenFileName lenExt) lenFileName fileName == ext;
|
||||
|
||||
|
||||
# Convert a string to a list of characters (i.e. singleton strings).
|
||||
# For instance, "abc" becomes ["a" "b" "c"]. This allows you to,
|
||||
# e.g., map a function over each character. However, note that this
|
||||
# will likely be horribly inefficient; Nix is not a general purpose
|
||||
# programming language. Complex string manipulations should, if
|
||||
# appropriate, be done in a derivation.
|
||||
stringToCharacters = s: let l = stringLength s; in
|
||||
if l == 0
|
||||
then []
|
||||
else map (p: substring p 1 s) (lib.range 0 (sub l 1));
|
||||
|
||||
|
||||
# Manipulate a string charcater by character and replace them by strings
|
||||
# before concatenating the results.
|
||||
stringAsChars = f: s:
|
||||
concatStrings (
|
||||
map f (stringToCharacters s)
|
||||
);
|
||||
|
||||
|
||||
# same as vim escape function.
|
||||
# Each character contained in list is prefixed by "\"
|
||||
escape = list : string :
|
||||
stringAsChars (c: if lib.elem c list then "\\${c}" else c) string;
|
||||
|
||||
|
||||
# still ugly slow. But more correct now
|
||||
# [] for zsh
|
||||
escapeShellArg = lib.escape (stringToCharacters "\\ ';$`()|<>\t*[]");
|
||||
|
||||
|
||||
# replace characters by their substitutes. This function is equivalent to
|
||||
# the `tr' command except that one character can be replace by multiple
|
||||
# ones. e.g.,
|
||||
# replaceChars ["<" ">"] ["<" ">"] "<foo>" returns "<foo>".
|
||||
replaceChars = del: new: s:
|
||||
let
|
||||
subst = c:
|
||||
(lib.fold
|
||||
(sub: res: if sub.fst == c then sub else res)
|
||||
{fst = c; snd = c;} (lib.zipLists del new)
|
||||
).snd;
|
||||
in
|
||||
stringAsChars subst s;
|
||||
|
||||
|
||||
# Case conversion utilities
|
||||
lowerChars = stringToCharacters "abcdefghijklmnopqrstuvwxyz";
|
||||
upperChars = stringToCharacters "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
toLower = replaceChars upperChars lowerChars;
|
||||
toUpper = replaceChars lowerChars upperChars;
|
||||
|
||||
|
||||
# Compares strings not requiring context equality
|
||||
# Obviously, a workaround but works on all Nix versions
|
||||
eqStrings = a: b: (a+(substring 0 0 b)) == ((substring 0 0 a)+b);
|
||||
|
||||
|
||||
# Cut a string with a separator and produces a list of strings which were
|
||||
# separated by this separator. e.g.,
|
||||
# `splitString "." "foo.bar.baz"' returns ["foo" "bar" "baz"].
|
||||
splitString = sep: s:
|
||||
let
|
||||
sepLen = stringLength sep;
|
||||
sLen = stringLength s;
|
||||
lastSearch = sub sLen sepLen;
|
||||
startWithSep = startAt:
|
||||
substring startAt sepLen s == sep;
|
||||
|
||||
recurse = index: startAt:
|
||||
let cutUntil = i: [(substring startAt (sub i startAt) s)]; in
|
||||
if lessThan index lastSearch then
|
||||
if startWithSep index then
|
||||
let restartAt = add index sepLen; in
|
||||
cutUntil index ++ recurse restartAt restartAt
|
||||
else
|
||||
recurse (add index 1) startAt
|
||||
else
|
||||
cutUntil sLen;
|
||||
in
|
||||
recurse 0 0;
|
||||
|
||||
|
||||
# return the suffix of the second argument if the first argument match its
|
||||
# prefix. e.g.,
|
||||
# `removePrefix "foo." "foo.bar.baz"' returns "bar.baz".
|
||||
removePrefix = pre: s:
|
||||
let
|
||||
preLen = stringLength pre;
|
||||
sLen = stringLength s;
|
||||
in
|
||||
if pre == substring 0 preLen s then
|
||||
substring preLen (sub sLen preLen) s
|
||||
else
|
||||
s;
|
||||
|
||||
# Return true iff string v1 denotes a version older than v2.
|
||||
versionOlder = v1: v2: builtins.compareVersions v2 v1 == 1;
|
||||
|
||||
|
||||
# Return true iff string v1 denotes a version equal to or newer than v2.
|
||||
versionAtLeast = v1: v2: !versionOlder v1 v2;
|
||||
|
||||
|
||||
# Get the version of the specified derivation, as specified in its
|
||||
# ‘name’ attribute.
|
||||
getVersion = drv: (builtins.parseDrvName drv.name).version;
|
||||
|
||||
|
||||
# Extract name with version from URL. Ask for separator which is
|
||||
# supposed to start extension
|
||||
nameFromURL = url: sep: let
|
||||
components = splitString "/" url;
|
||||
filename = lib.last components;
|
||||
name = builtins.head (splitString sep filename);
|
||||
in
|
||||
assert ! eqStrings name filename;
|
||||
name;
|
||||
|
||||
|
||||
# Create an --{enable,disable}-<feat> string that can be passed to
|
||||
# standard GNU Autoconf scripts.
|
||||
enableFeature = enable: feat: "--${if enable then "enable" else "disable"}-${feat}";
|
||||
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
# Define the list of system with their properties. Only systems tested for
|
||||
# Nixpkgs are listed below
|
||||
|
||||
with import ./lists.nix;
|
||||
with import ./types.nix;
|
||||
with import ./attrsets.nix;
|
||||
|
||||
let
|
||||
lib = import ./default.nix;
|
||||
setTypes = type:
|
||||
mapAttrs (name: value:
|
||||
setType type ({inherit name;} // value)
|
||||
);
|
||||
in
|
||||
|
||||
rec {
|
||||
|
||||
isSignificantByte = isType "significant-byte";
|
||||
significantBytes = setTypes "significant-byte" {
|
||||
bigEndian = {};
|
||||
littleEndian = {};
|
||||
};
|
||||
|
||||
|
||||
isCpuType = x: typeOf x == "cpu-type"
|
||||
&& elem x.bits [8 16 32 64 128]
|
||||
&& (builtins.lessThan 8 x.bits -> isSignificantByte x.significantByte);
|
||||
|
||||
cpuTypes = with significantBytes;
|
||||
setTypes "cpu-type" {
|
||||
arm = { bits = 32; significantByte = littleEndian; };
|
||||
armv5tel = { bits = 32; significantByte = littleEndian; };
|
||||
armv7l = { bits = 32; significantByte = littleEndian; };
|
||||
i686 = { bits = 32; significantByte = littleEndian; };
|
||||
powerpc = { bits = 32; significantByte = bigEndian; };
|
||||
x86_64 = { bits = 64; significantByte = littleEndian; };
|
||||
};
|
||||
|
||||
|
||||
isExecFormat = isType "exec-format";
|
||||
execFormats = setTypes "exec-format" {
|
||||
aout = {}; # a.out
|
||||
elf = {};
|
||||
macho = {};
|
||||
pe = {};
|
||||
unknow = {};
|
||||
};
|
||||
|
||||
|
||||
isKernel = isType "kernel";
|
||||
kernels = with execFormats;
|
||||
setTypes "kernel" {
|
||||
cygwin = { execFormat = pe; };
|
||||
darwin = { execFormat = macho; };
|
||||
freebsd = { execFormat = elf; };
|
||||
linux = { execFormat = elf; };
|
||||
netbsd = { execFormat = elf; };
|
||||
none = { execFormat = unknow; };
|
||||
openbsd = { execFormat = elf; };
|
||||
win32 = { execFormat = pe; };
|
||||
};
|
||||
|
||||
|
||||
isArchitecture = isType "architecture";
|
||||
architectures = setTypes "architecture" {
|
||||
apple = {};
|
||||
pc = {};
|
||||
unknow = {};
|
||||
};
|
||||
|
||||
|
||||
isSystem = x: typeOf x == "system"
|
||||
&& isCpuType x.cpu
|
||||
&& isArchitecture x.arch
|
||||
&& isKernel x.kernel;
|
||||
|
||||
mkSystem = {
|
||||
cpu ? cpuTypes.i686,
|
||||
arch ? architectures.pc,
|
||||
kernel ? kernels.linux,
|
||||
name ? "${cpu.name}-${arch.name}-${kernel.name}"
|
||||
}: setType "system" {
|
||||
inherit name cpu arch kernel;
|
||||
};
|
||||
|
||||
|
||||
isDarwin = matchAttrs { kernel = kernels.darwin; };
|
||||
isLinux = matchAttrs { kernel = kernels.linux; };
|
||||
isi686 = matchAttrs { cpu = cpuTypes.i686; };
|
||||
is64Bit = matchAttrs { cpu = { bits = 64; }; };
|
||||
|
||||
|
||||
# This should revert the job done by config.guess from the gcc compiler.
|
||||
mkSystemFromString = s: let
|
||||
l = lib.splitString "-" s;
|
||||
|
||||
getCpu = name:
|
||||
attrByPath [name] (throw "Unknow cpuType `${name}'.")
|
||||
cpuTypes;
|
||||
getArch = name:
|
||||
attrByPath [name] (throw "Unknow architecture `${name}'.")
|
||||
architectures;
|
||||
getKernel = name:
|
||||
attrByPath [name] (throw "Unknow kernel `${name}'.")
|
||||
kernels;
|
||||
|
||||
system =
|
||||
if builtins.length l == 2 then
|
||||
mkSystem rec {
|
||||
name = s;
|
||||
cpu = getCpu (head l);
|
||||
arch =
|
||||
if isDarwin system
|
||||
then architectures.apple
|
||||
else architectures.pc;
|
||||
kernel = getKernel (head (tail l));
|
||||
}
|
||||
else
|
||||
mkSystem {
|
||||
name = s;
|
||||
cpu = getCpu (head l);
|
||||
arch = getArch (head (tail l));
|
||||
kernel = getKernel (head (tail (tail l)));
|
||||
};
|
||||
in assert isSystem system; system;
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
let inherit (builtins) add; in
|
||||
with import ./default.nix;
|
||||
|
||||
runTests {
|
||||
|
||||
testId = {
|
||||
expr = id 1;
|
||||
expected = 1;
|
||||
};
|
||||
|
||||
testConst = {
|
||||
expr = const 2 3;
|
||||
expected = 2;
|
||||
};
|
||||
|
||||
/*
|
||||
testOr = {
|
||||
expr = or true false;
|
||||
expected = true;
|
||||
};
|
||||
*/
|
||||
|
||||
testAnd = {
|
||||
expr = and true false;
|
||||
expected = false;
|
||||
};
|
||||
|
||||
testFix = {
|
||||
expr = fix (x: {a = if x ? a then "a" else "b";});
|
||||
expected = {a = "a";};
|
||||
};
|
||||
|
||||
testConcatMapStrings = {
|
||||
expr = concatMapStrings (x: x + ";") ["a" "b" "c"];
|
||||
expected = "a;b;c;";
|
||||
};
|
||||
|
||||
testConcatStringsSep = {
|
||||
expr = concatStringsSep "," ["a" "b" "c"];
|
||||
expected = "a,b,c";
|
||||
};
|
||||
|
||||
testFilter = {
|
||||
expr = filter (x: x != "a") ["a" "b" "c" "a"];
|
||||
expected = ["b" "c"];
|
||||
};
|
||||
|
||||
testFold = {
|
||||
expr = fold (builtins.add) 0 (range 0 100);
|
||||
expected = 5050;
|
||||
};
|
||||
|
||||
testTake = testAllTrue [
|
||||
([] == (take 0 [ 1 2 3 ]))
|
||||
([1] == (take 1 [ 1 2 3 ]))
|
||||
([ 1 2 ] == (take 2 [ 1 2 3 ]))
|
||||
([ 1 2 3 ] == (take 3 [ 1 2 3 ]))
|
||||
([ 1 2 3 ] == (take 4 [ 1 2 3 ]))
|
||||
];
|
||||
|
||||
testFoldAttrs = {
|
||||
expr = foldAttrs (n: a: [n] ++ a) [] [
|
||||
{ a = 2; b = 7; }
|
||||
{ a = 3; c = 8; }
|
||||
];
|
||||
expected = { a = [ 2 3 ]; b = [7]; c = [8];};
|
||||
};
|
||||
|
||||
testOverridableDelayableArgsTest = {
|
||||
expr =
|
||||
let res1 = defaultOverridableDelayableArgs id {};
|
||||
res2 = defaultOverridableDelayableArgs id { a = 7; };
|
||||
res3 = let x = defaultOverridableDelayableArgs id { a = 7; };
|
||||
in (x.merge) { b = 10; };
|
||||
res4 = let x = defaultOverridableDelayableArgs id { a = 7; };
|
||||
in (x.merge) ( x: { b = 10; });
|
||||
res5 = let x = defaultOverridableDelayableArgs id { a = 7; };
|
||||
in (x.merge) ( x: { a = add x.a 3; });
|
||||
res6 = let x = defaultOverridableDelayableArgs id { a = 7; mergeAttrBy = { a = add; }; };
|
||||
y = x.merge {};
|
||||
in (y.merge) { a = 10; };
|
||||
|
||||
resRem7 = res6.replace (a : removeAttrs a ["a"]);
|
||||
|
||||
resReplace6 = let x = defaultOverridableDelayableArgs id { a = 7; mergeAttrBy = { a = add; }; };
|
||||
x2 = x.merge { a = 20; }; # now we have 27
|
||||
in (x2.replace) { a = 10; }; # and override the value by 10
|
||||
|
||||
# fixed tests (delayed args): (when using them add some comments, please)
|
||||
resFixed1 =
|
||||
let x = defaultOverridableDelayableArgs id ( x : { a = 7; c = x.fixed.b; });
|
||||
y = x.merge (x : { name = "name-${builtins.toString x.fixed.c}"; });
|
||||
in (y.merge) { b = 10; };
|
||||
strip = attrs : removeAttrs attrs ["merge" "replace"];
|
||||
in all id
|
||||
[ ((strip res1) == { })
|
||||
((strip res2) == { a = 7; })
|
||||
((strip res3) == { a = 7; b = 10; })
|
||||
((strip res4) == { a = 7; b = 10; })
|
||||
((strip res5) == { a = 10; })
|
||||
((strip res6) == { a = 17; })
|
||||
((strip resRem7) == {})
|
||||
((strip resFixed1) == { a = 7; b = 10; c =10; name = "name-10"; })
|
||||
];
|
||||
expected = true;
|
||||
};
|
||||
|
||||
testSort = {
|
||||
expr = sort builtins.lessThan [ 40 2 30 42 ];
|
||||
expected = [2 30 40 42];
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
with {
|
||||
inherit (import ./lists.nix) deepSeqList;
|
||||
inherit (import ./attrsets.nix) deepSeqAttrs;
|
||||
};
|
||||
|
||||
rec {
|
||||
|
||||
# Identity function.
|
||||
id = x: x;
|
||||
|
||||
# Constant function.
|
||||
const = x: y: x;
|
||||
|
||||
# Named versions corresponding to some builtin operators.
|
||||
concat = x: y: x ++ y;
|
||||
or = x: y: x || y;
|
||||
and = x: y: x && y;
|
||||
mergeAttrs = x: y: x // y;
|
||||
|
||||
# Take a function and evaluate it with its own returned value.
|
||||
fix = f: let result = f result; in result;
|
||||
|
||||
# Flip the order of the arguments of a binary function.
|
||||
flip = f: a: b: f b a;
|
||||
|
||||
# `seq x y' evaluates x, then returns y. That is, it forces strict
|
||||
# evaluation of its first argument.
|
||||
seq = x: y: if x == null then y else y;
|
||||
|
||||
# Like `seq', but recurses into lists and attribute sets to force evaluation
|
||||
# of all list elements/attributes.
|
||||
deepSeq = x: y:
|
||||
if builtins.isList x
|
||||
then deepSeqList x y
|
||||
else if builtins.isAttrs x
|
||||
then deepSeqAttrs x y
|
||||
else seq x y;
|
||||
}
|
||||
+226
@@ -0,0 +1,226 @@
|
||||
# Definitions related to run-time type checking. Used in particular
|
||||
# to type-check NixOS configurations.
|
||||
|
||||
let lib = import ./default.nix; in
|
||||
|
||||
with import ./lists.nix;
|
||||
with import ./attrsets.nix;
|
||||
with import ./options.nix;
|
||||
with import ./trivial.nix;
|
||||
|
||||
rec {
|
||||
|
||||
isType = type: x: (x._type or "") == type;
|
||||
hasType = x: isAttrs x && x ? _type;
|
||||
typeOf = x: x._type or "";
|
||||
|
||||
setType = typeName: value: value // {
|
||||
_type = typeName;
|
||||
};
|
||||
|
||||
|
||||
# name (name of the type)
|
||||
# check (check the config value. Before returning false it should trace the bad value eg using traceValIfNot)
|
||||
# merge (default merge function)
|
||||
# iter (iterate on all elements contained in this type)
|
||||
# fold (fold all elements contained in this type)
|
||||
# hasOptions (boolean: whatever this option contains an option set)
|
||||
# delayOnGlobalEval (boolean: should properties go through the evaluation of this option)
|
||||
# docPath (path concatenated to the option name contained in the option set)
|
||||
isOptionType = isType "option-type";
|
||||
mkOptionType =
|
||||
{ name
|
||||
, check ? (x: true)
|
||||
, merge ? mergeDefaultOption
|
||||
# Handle complex structure types.
|
||||
, iter ? (f: path: v: f path v)
|
||||
, fold ? (op: nul: v: op v nul)
|
||||
, docPath ? lib.id
|
||||
# If the type can contains option sets.
|
||||
, hasOptions ? false
|
||||
, delayOnGlobalEval ? false
|
||||
}:
|
||||
|
||||
{ _type = "option-type";
|
||||
inherit name check merge iter fold docPath hasOptions delayOnGlobalEval;
|
||||
};
|
||||
|
||||
|
||||
types = rec {
|
||||
|
||||
bool = mkOptionType {
|
||||
name = "boolean";
|
||||
check = lib.traceValIfNot builtins.isBool;
|
||||
merge = fold lib.or false;
|
||||
};
|
||||
|
||||
int = mkOptionType {
|
||||
name = "integer";
|
||||
check = lib.traceValIfNot builtins.isInt;
|
||||
};
|
||||
|
||||
string = mkOptionType {
|
||||
name = "string";
|
||||
check = lib.traceValIfNot builtins.isString;
|
||||
merge = lib.concatStrings;
|
||||
};
|
||||
|
||||
# Like ‘string’, but add newlines between every value. Useful for
|
||||
# configuration file contents.
|
||||
lines = mkOptionType {
|
||||
name = "string";
|
||||
check = lib.traceValIfNot builtins.isString;
|
||||
merge = lib.concatStringsSep "\n";
|
||||
};
|
||||
|
||||
envVar = mkOptionType {
|
||||
name = "environment variable";
|
||||
inherit (string) check;
|
||||
merge = lib.concatStringsSep ":";
|
||||
};
|
||||
|
||||
attrs = mkOptionType {
|
||||
name = "attribute set";
|
||||
check = lib.traceValIfNot isAttrs;
|
||||
merge = fold lib.mergeAttrs {};
|
||||
};
|
||||
|
||||
# derivation is a reserved keyword.
|
||||
package = mkOptionType {
|
||||
name = "derivation";
|
||||
check = lib.traceValIfNot isDerivation;
|
||||
};
|
||||
|
||||
path = mkOptionType {
|
||||
name = "path";
|
||||
# Hacky: there is no ‘isPath’ primop.
|
||||
check = lib.traceValIfNot (x: builtins.unsafeDiscardStringContext (builtins.substring 0 1 (toString x)) == "/");
|
||||
};
|
||||
|
||||
# drop this in the future:
|
||||
list = builtins.trace "types.list is deprecated, use types.listOf instead" types.listOf;
|
||||
|
||||
listOf = elemType: mkOptionType {
|
||||
name = "list of ${elemType.name}s";
|
||||
check = value: lib.traceValIfNot isList value && all elemType.check value;
|
||||
merge = concatLists;
|
||||
iter = f: path: list: map (elemType.iter f (path + ".*")) list;
|
||||
fold = op: nul: list: lib.fold (e: l: elemType.fold op l e) nul list;
|
||||
docPath = path: elemType.docPath (path + ".*");
|
||||
inherit (elemType) hasOptions;
|
||||
|
||||
# You cannot define multiple configurations of one entity, therefore
|
||||
# no reason justify to delay properties inside list elements.
|
||||
delayOnGlobalEval = false;
|
||||
};
|
||||
|
||||
attrsOf = elemType: mkOptionType {
|
||||
name = "attribute set of ${elemType.name}s";
|
||||
check = x: lib.traceValIfNot isAttrs x
|
||||
&& all elemType.check (lib.attrValues x);
|
||||
merge = lib.zipAttrsWith (name: elemType.merge);
|
||||
iter = f: path: set: lib.mapAttrs (name: elemType.iter f (path + "." + name)) set;
|
||||
fold = op: nul: set: fold (e: l: elemType.fold op l e) nul (lib.attrValues set);
|
||||
docPath = path: elemType.docPath (path + ".<name>");
|
||||
inherit (elemType) hasOptions delayOnGlobalEval;
|
||||
};
|
||||
|
||||
# List or attribute set of ...
|
||||
loaOf = elemType:
|
||||
let
|
||||
convertIfList = defIdx: def:
|
||||
if isList def then
|
||||
listToAttrs (
|
||||
flip imap def (elemIdx: elem:
|
||||
nameValuePair "unnamed-${toString defIdx}.${toString elemIdx}" elem))
|
||||
else
|
||||
def;
|
||||
listOnly = listOf elemType;
|
||||
attrOnly = attrsOf elemType;
|
||||
|
||||
in mkOptionType {
|
||||
name = "list or attribute set of ${elemType.name}s";
|
||||
check = x:
|
||||
if isList x then listOnly.check x
|
||||
else if isAttrs x then attrOnly.check x
|
||||
else lib.traceValIfNot (x: false) x;
|
||||
## The merge function returns an attribute set
|
||||
merge = defs:
|
||||
attrOnly.merge (imap convertIfList defs);
|
||||
iter = f: path: def:
|
||||
if isList def then listOnly.iter f path def
|
||||
else if isAttrs def then attrOnly.iter f path def
|
||||
else throw "Unexpected value";
|
||||
fold = op: nul: def:
|
||||
if isList def then listOnly.fold op nul def
|
||||
else if isAttrs def then attrOnly.fold op nul def
|
||||
else throw "Unexpected value";
|
||||
|
||||
docPath = path: elemType.docPath (path + ".<name?>");
|
||||
inherit (elemType) hasOptions delayOnGlobalEval;
|
||||
}
|
||||
;
|
||||
|
||||
uniq = elemType: mkOptionType {
|
||||
inherit (elemType) name check iter fold docPath hasOptions;
|
||||
merge = list:
|
||||
if length list == 1 then
|
||||
head list
|
||||
else
|
||||
throw "Multiple definitions of ${elemType.name}. Only one is allowed for this option.";
|
||||
};
|
||||
|
||||
none = elemType: mkOptionType {
|
||||
inherit (elemType) name check iter fold docPath hasOptions;
|
||||
merge = list:
|
||||
throw "No definitions are allowed for this option.";
|
||||
};
|
||||
|
||||
nullOr = elemType: mkOptionType {
|
||||
inherit (elemType) name merge docPath hasOptions;
|
||||
check = x: builtins.isNull x || elemType.check x;
|
||||
iter = f: path: v: if v == null then v else elemType.iter f path v;
|
||||
fold = op: nul: v: if v == null then nul else elemType.fold op nul v;
|
||||
};
|
||||
|
||||
functionTo = elemType: mkOptionType {
|
||||
name = "function that evaluates to a(n) ${elemType.name}";
|
||||
check = lib.traceValIfNot builtins.isFunction;
|
||||
merge = fns:
|
||||
args: elemType.merge (map (fn: fn args) fns);
|
||||
# These are guesses, I don't fully understand iter, fold, delayOnGlobalEval
|
||||
iter = f: path: v:
|
||||
args: elemType.iter f path (v args);
|
||||
fold = op: nul: v:
|
||||
args: elemType.fold op nul (v args);
|
||||
inherit (elemType) delayOnGlobalEval;
|
||||
hasOptions = false;
|
||||
};
|
||||
|
||||
# usually used with listOf, attrsOf, loaOf like this:
|
||||
# users = mkOption {
|
||||
# type = loaOf optionSet;
|
||||
#
|
||||
# # you can omit the list if there is one element only
|
||||
# options = [ {
|
||||
# name = mkOption {
|
||||
# description = "name of the user"
|
||||
# ...
|
||||
# };
|
||||
# # more options here
|
||||
# } { more options } ];
|
||||
# }
|
||||
# TODO: !!! document passing options as an argument to optionSet,
|
||||
# deprecate the current approach.
|
||||
optionSet = mkOptionType {
|
||||
name = "option set";
|
||||
# merge is done in "options.nix > addOptionMakeUp > handleOptionSets"
|
||||
merge = lib.id;
|
||||
check = x: isAttrs x || builtins.isFunction x;
|
||||
hasOptions = true;
|
||||
delayOnGlobalEval = true;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user