Add a new way to handle option sets.
svn path=/nixpkgs/trunk/; revision=12505
This commit is contained in:
parent
7abbe5889f
commit
0e25bb67cf
|
@ -287,12 +287,55 @@ rec {
|
||||||
checker
|
checker
|
||||||
else condConcat
|
else condConcat
|
||||||
name (tail (tail list)) checker;
|
name (tail (tail list)) checker;
|
||||||
|
|
||||||
|
# Merge sets of attributes and use the function f to merge
|
||||||
|
# attributes values.
|
||||||
|
zip = f: sets:
|
||||||
|
builtins.listToAttrs (map (name: {
|
||||||
|
inherit name;
|
||||||
|
value =
|
||||||
|
f name
|
||||||
|
(map (__getAttr name)
|
||||||
|
(filter (__hasAttr name) sets));
|
||||||
|
}) (concatMap builtins.attrNames sets));
|
||||||
|
|
||||||
|
# divide a list in two 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 = []; };
|
||||||
|
|
||||||
|
# Take a function and evaluate it with its own returned value.
|
||||||
|
finalReference = f:
|
||||||
|
(rec { result = f result; }).result;
|
||||||
|
|
||||||
|
# flatten a list of sets returned by 'f'.
|
||||||
|
# f : function to evaluate each set.
|
||||||
|
# attr : name of the attribute which contains more values.
|
||||||
|
# default: result if 'x' is empty.
|
||||||
|
# x : list of values that have to be processed.
|
||||||
|
uniqFlattenAttr = f: attr: default: x:
|
||||||
|
if x == []
|
||||||
|
then default
|
||||||
|
else let h = f (head x); t = tail x; in
|
||||||
|
if elem h default
|
||||||
|
then uniqFlattenAttr f attr default t
|
||||||
|
else uniqFlattenAttr f attr (default ++ [h]) (toList (getAttr [attr] [] h) ++ t)
|
||||||
|
;
|
||||||
|
|
||||||
/* Options. */
|
/* Options. */
|
||||||
|
|
||||||
mkOption = attrs: attrs // {_type = "option";};
|
mkOption = attrs: attrs // {_type = "option";};
|
||||||
|
|
||||||
typeOf = x: if x ? _type then x._type else "";
|
typeOf = x: if x ? _type then x._type else "";
|
||||||
|
|
||||||
|
isOption = attrs:
|
||||||
|
__isAttrs attrs
|
||||||
|
&& attrs ? _type
|
||||||
|
&& attrs._type == "option";
|
||||||
|
|
||||||
addDefaultOptionValues = defs: opts: opts //
|
addDefaultOptionValues = defs: opts: opts //
|
||||||
builtins.listToAttrs (map (defName:
|
builtins.listToAttrs (map (defName:
|
||||||
{ name = defName;
|
{ name = defName;
|
||||||
|
@ -315,7 +358,67 @@ rec {
|
||||||
else addDefaultOptionValues defValue {};
|
else addDefaultOptionValues defValue {};
|
||||||
}
|
}
|
||||||
) (builtins.attrNames defs));
|
) (builtins.attrNames defs));
|
||||||
|
|
||||||
|
mergeDefaultOption = name: list:
|
||||||
|
if list != [] && tail list == [] then head list
|
||||||
|
else if all __isFunction list then x: mergeDefaultOption (map (f: f x) list)
|
||||||
|
else if all __isList list then concatLists list
|
||||||
|
else if all __isAttrs list then mergeAttrs list
|
||||||
|
else throw "Default merge method does not work on '${name}'.";
|
||||||
|
|
||||||
|
mergeEnableOption = name: fold logicalOR false;
|
||||||
|
|
||||||
|
mergeListOption = name: list:
|
||||||
|
if all __isList list then list
|
||||||
|
else throw "${name}: Expect a list.";
|
||||||
|
|
||||||
|
# Merge sets of options and bindings.
|
||||||
|
# noOption: function to call if no option is declared.
|
||||||
|
mergeOptionSets = noOption: path: opts:
|
||||||
|
if all __isAttrs opts then
|
||||||
|
zip (attr: opts:
|
||||||
|
let
|
||||||
|
name = if path == "" then attr else path + "." + attr;
|
||||||
|
defaultOpt = { merge = mergeDefaultOption; };
|
||||||
|
test = partition isOption opts;
|
||||||
|
in
|
||||||
|
if test.right == [] then mergeOptionSets noOption name test.wrong
|
||||||
|
else if tail test.right != [] then throw "Multiple options for '${name}'."
|
||||||
|
else if test.wrong == [] then (head test.right).default
|
||||||
|
else (defaultOpt // head test.right).merge name test.wrong
|
||||||
|
) opts
|
||||||
|
else noOption path opts;
|
||||||
|
|
||||||
|
# Keep all option declarations and add an attribute "name" inside
|
||||||
|
# each option which contains the path that has to be followed to
|
||||||
|
# access it.
|
||||||
|
filterOptionSets = path: opts:
|
||||||
|
if all __isAttrs opts then
|
||||||
|
zip (attr: opts:
|
||||||
|
let
|
||||||
|
name = if path == "" then attr else path + "." + attr;
|
||||||
|
test = partition isOption opts;
|
||||||
|
in
|
||||||
|
if test.right == []
|
||||||
|
then filterOptionSets name test.wrong
|
||||||
|
else map (x: x // { inherit name; }) test.right
|
||||||
|
) opts
|
||||||
|
else {};
|
||||||
|
|
||||||
|
# Evaluate a list of option sets that would be merged with the
|
||||||
|
# function "merge" which expects two arguments. The attribute named
|
||||||
|
# "require" is used to imports option declarations and bindings.
|
||||||
|
finalReferenceOptionSets = merge: pkgs: opts:
|
||||||
|
let optionSet = final: configFun:
|
||||||
|
if __isFunction configFun then configFun pkgs final
|
||||||
|
else configFun; # backward compatibility.
|
||||||
|
in
|
||||||
|
finalReference (final: merge ""
|
||||||
|
(map (x: removeAttrs x ["require"])
|
||||||
|
(uniqFlattenAttr (optionSet final) "require" [] (toList opts))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
optionAttrSetToDocList = (l: attrs:
|
optionAttrSetToDocList = (l: attrs:
|
||||||
(if (getAttr ["_type"] "" attrs) == "option" then
|
(if (getAttr ["_type"] "" attrs) == "option" then
|
||||||
[({
|
[({
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
# sets of small configurations:
|
||||||
|
# Each configuration
|
||||||
|
rec {
|
||||||
|
# has 2 arguments pkgs and this.
|
||||||
|
configA = pkgs: this: {
|
||||||
|
# Can depends on other configuration
|
||||||
|
require = configB;
|
||||||
|
|
||||||
|
# Defines new options
|
||||||
|
optionA = pkgs.lib.mkOption {
|
||||||
|
# With default values
|
||||||
|
default = false;
|
||||||
|
# And merging functions.
|
||||||
|
merge = pkgs.lib.mergeEnableOption;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Add a new definition to other options.
|
||||||
|
optionB = this.optionA;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Can be used for option header.
|
||||||
|
configB = pkgs: this: {
|
||||||
|
# Can depends on more than one configuration.
|
||||||
|
require = [ configC configD ];
|
||||||
|
|
||||||
|
optionB = pkgs.lib.mkOption {
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Is not obliged to define other options.
|
||||||
|
};
|
||||||
|
|
||||||
|
configC = pkgs: this: {
|
||||||
|
require = [ configA ];
|
||||||
|
|
||||||
|
optionC = pkgs.lib.mkOption {
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Use the default value if it is not overwritten.
|
||||||
|
optionA = this.optionC;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Can also be used as option configuration only.
|
||||||
|
# without any arguments (backward compatibility)
|
||||||
|
configD = {
|
||||||
|
# Is not forced to specify the require attribute.
|
||||||
|
|
||||||
|
# Is not force to make new options.
|
||||||
|
optionA = true;
|
||||||
|
optionD = false;
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
let
|
||||||
|
pkgs = import ../../top-level/all-packages.nix {};
|
||||||
|
config = import ./declare.nix;
|
||||||
|
in
|
||||||
|
with (pkgs.lib);
|
||||||
|
|
||||||
|
finalReferenceOptionSets
|
||||||
|
filterOptionSets
|
||||||
|
pkgs
|
||||||
|
# List of main configurations.
|
||||||
|
[ config.configB config.configC ]
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<expr>
|
||||||
|
<attrs>
|
||||||
|
<attr name="optionA">
|
||||||
|
<list>
|
||||||
|
<attrs>
|
||||||
|
<attr name="_type">
|
||||||
|
<string value="option" />
|
||||||
|
</attr>
|
||||||
|
<attr name="default">
|
||||||
|
<bool value="false" />
|
||||||
|
</attr>
|
||||||
|
<attr name="merge">
|
||||||
|
<unevaluated />
|
||||||
|
</attr>
|
||||||
|
<attr name="name">
|
||||||
|
<string value="optionA" />
|
||||||
|
</attr>
|
||||||
|
</attrs>
|
||||||
|
</list>
|
||||||
|
</attr>
|
||||||
|
<attr name="optionB">
|
||||||
|
<list>
|
||||||
|
<attrs>
|
||||||
|
<attr name="_type">
|
||||||
|
<string value="option" />
|
||||||
|
</attr>
|
||||||
|
<attr name="default">
|
||||||
|
<bool value="false" />
|
||||||
|
</attr>
|
||||||
|
<attr name="name">
|
||||||
|
<string value="optionB" />
|
||||||
|
</attr>
|
||||||
|
</attrs>
|
||||||
|
</list>
|
||||||
|
</attr>
|
||||||
|
<attr name="optionC">
|
||||||
|
<list>
|
||||||
|
<attrs>
|
||||||
|
<attr name="_type">
|
||||||
|
<string value="option" />
|
||||||
|
</attr>
|
||||||
|
<attr name="default">
|
||||||
|
<bool value="false" />
|
||||||
|
</attr>
|
||||||
|
<attr name="name">
|
||||||
|
<string value="optionC" />
|
||||||
|
</attr>
|
||||||
|
</attrs>
|
||||||
|
</list>
|
||||||
|
</attr>
|
||||||
|
<attr name="optionD">
|
||||||
|
<attrs>
|
||||||
|
</attrs>
|
||||||
|
</attr>
|
||||||
|
</attrs>
|
||||||
|
</expr>
|
|
@ -0,0 +1,15 @@
|
||||||
|
let
|
||||||
|
pkgs = import ../../top-level/all-packages.nix {};
|
||||||
|
config = import ./declare.nix;
|
||||||
|
|
||||||
|
# Define the handler of unbound options.
|
||||||
|
noOption = name: values:
|
||||||
|
builtins.trace "Attribute named '${name}' does not match any option declaration." values;
|
||||||
|
in
|
||||||
|
with (pkgs.lib);
|
||||||
|
|
||||||
|
finalReferenceOptionSets
|
||||||
|
(mergeOptionSets noOption)
|
||||||
|
pkgs
|
||||||
|
# List of main configurations.
|
||||||
|
[ config.configB config.configC ]
|
|
@ -0,0 +1,20 @@
|
||||||
|
trace: Str("Attribute named 'optionD' does not match any option declaration.",[])
|
||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<expr>
|
||||||
|
<attrs>
|
||||||
|
<attr name="optionA">
|
||||||
|
<bool value="true" />
|
||||||
|
</attr>
|
||||||
|
<attr name="optionB">
|
||||||
|
<bool value="true" />
|
||||||
|
</attr>
|
||||||
|
<attr name="optionC">
|
||||||
|
<bool value="false" />
|
||||||
|
</attr>
|
||||||
|
<attr name="optionD">
|
||||||
|
<list>
|
||||||
|
<bool value="false" />
|
||||||
|
</list>
|
||||||
|
</attr>
|
||||||
|
</attrs>
|
||||||
|
</expr>
|
|
@ -0,0 +1,9 @@
|
||||||
|
#! /bin/sh -e
|
||||||
|
|
||||||
|
echo 1>&2 "Test: Merge of option bindings."
|
||||||
|
nix-instantiate merge.nix --eval-only --strict --xml >& merge.out
|
||||||
|
diff merge.ref merge.out
|
||||||
|
|
||||||
|
echo 1>&2 "Test: Filter of option declarations."
|
||||||
|
nix-instantiate keep.nix --eval-only --strict --xml >& keep.out
|
||||||
|
diff keep.ref keep.out
|
Loading…
Reference in New Issue