Merge remote-tracking branch 'upstream/master' into lib-float
This commit is contained in:
184
lib/debug.nix
184
lib/debug.nix
@@ -1,34 +1,67 @@
|
||||
/* Collection of functions useful for debugging
|
||||
broken nix expressions.
|
||||
|
||||
* `trace`-like functions take two values, print
|
||||
the first to stderr and return the second.
|
||||
* `traceVal`-like functions take one argument
|
||||
which both printed and returned.
|
||||
* `traceSeq`-like functions fully evaluate their
|
||||
traced value before printing (not just to “weak
|
||||
head normal form” like trace does by default).
|
||||
* Functions that end in `-Fn` take an additional
|
||||
function as their first argument, which is applied
|
||||
to the traced value before it is printed.
|
||||
*/
|
||||
{ lib }:
|
||||
|
||||
let
|
||||
|
||||
inherit (builtins) trace attrNamesToStr isAttrs isList isInt
|
||||
isString isBool head substring attrNames;
|
||||
|
||||
inherit (lib) all id mapAttrsFlatten elem isFunction;
|
||||
|
||||
inherit (builtins) trace isAttrs isList isInt
|
||||
head substring attrNames;
|
||||
inherit (lib) id elem isFunction;
|
||||
in
|
||||
|
||||
rec {
|
||||
|
||||
inherit (builtins) addErrorContext;
|
||||
# -- TRACING --
|
||||
|
||||
addErrorContextToAttrs = lib.mapAttrs (a: v: lib.addErrorContext "while evaluating ${a}" v);
|
||||
/* Trace msg, but only if pred is true.
|
||||
|
||||
traceIf = p: msg: x: if p then trace msg x else x;
|
||||
Example:
|
||||
traceIf true "hello" 3
|
||||
trace: hello
|
||||
=> 3
|
||||
*/
|
||||
traceIf = pred: msg: x: if pred then trace msg x else x;
|
||||
|
||||
traceVal = x: trace x x;
|
||||
traceXMLVal = x: trace (builtins.toXML x) x;
|
||||
traceXMLValMarked = str: x: trace (str + builtins.toXML x) x;
|
||||
/* Trace the value and also return it.
|
||||
|
||||
# strict trace functions (traced structure is fully evaluated and printed)
|
||||
Example:
|
||||
traceValFn (v: "mystring ${v}") "foo"
|
||||
trace: mystring foo
|
||||
=> "foo"
|
||||
*/
|
||||
traceValFn = f: x: trace (f x) x;
|
||||
traceVal = traceValFn id;
|
||||
|
||||
/* `builtins.trace`, but the value is `builtins.deepSeq`ed first. */
|
||||
/* `builtins.trace`, but the value is `builtins.deepSeq`ed first.
|
||||
|
||||
Example:
|
||||
trace { a.b.c = 3; } null
|
||||
trace: { a = <CODE>; }
|
||||
=> null
|
||||
traceSeq { a.b.c = 3; } null
|
||||
trace: { a = { b = { c = 3; }; }; }
|
||||
=> null
|
||||
*/
|
||||
traceSeq = x: y: trace (builtins.deepSeq x x) y;
|
||||
|
||||
/* Like `traceSeq`, but only down to depth n.
|
||||
* This is very useful because lots of `traceSeq` usages
|
||||
* lead to an infinite recursion.
|
||||
/* Like `traceSeq`, but only evaluate down to depth n.
|
||||
This is very useful because lots of `traceSeq` usages
|
||||
lead to an infinite recursion.
|
||||
|
||||
Example:
|
||||
traceSeqN 2 { a.b.c = 3; } null
|
||||
trace: { a = { b = {…}; }; }
|
||||
=> null
|
||||
*/
|
||||
traceSeqN = depth: x: y: with lib;
|
||||
let snip = v: if isList v then noQuotes "[…]" v
|
||||
@@ -43,39 +76,16 @@ rec {
|
||||
in trace (generators.toPretty { allowPrettyValues = true; }
|
||||
(modify depth snip x)) y;
|
||||
|
||||
/* `traceSeq`, but the same value is traced and returned */
|
||||
traceValSeq = v: traceVal (builtins.deepSeq v v);
|
||||
/* `traceValSeq` but with fixed depth */
|
||||
traceValSeqN = depth: v: traceSeqN depth v v;
|
||||
/* A combination of `traceVal` and `traceSeq` */
|
||||
traceValSeqFn = f: v: traceVal f (builtins.deepSeq v v);
|
||||
traceValSeq = traceValSeqFn id;
|
||||
|
||||
/* A combination of `traceVal` and `traceSeqN`. */
|
||||
traceValSeqNFn = f: depth: v: traceSeqN depth (f v) v;
|
||||
traceValSeqN = traceValSeqNFn id;
|
||||
|
||||
|
||||
# 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));
|
||||
|
||||
# FIXME: rename this?
|
||||
traceValIfNot = c: x:
|
||||
if c x then true else trace (showVal x) false;
|
||||
# -- TESTING --
|
||||
|
||||
/* Evaluate a set of tests. A test is an attribute set {expr,
|
||||
expected}, denoting an expression and its expected result. The
|
||||
@@ -99,9 +109,68 @@ rec {
|
||||
# usage: { testX = allTrue [ true ]; }
|
||||
testAllTrue = expr: { inherit expr; expected = map (x: true) expr; };
|
||||
|
||||
strict = v:
|
||||
trace "Warning: strict is deprecated and will be removed in the next release"
|
||||
(builtins.seq v v);
|
||||
|
||||
# -- DEPRECATED --
|
||||
|
||||
traceShowVal = x: trace (showVal x) x;
|
||||
traceShowValMarked = str: x: trace (str + showVal x) x;
|
||||
|
||||
attrNamesToStr = a:
|
||||
trace ( "Warning: `attrNamesToStr` is deprecated "
|
||||
+ "and will be removed in the next release. "
|
||||
+ "Please use more specific concatenation "
|
||||
+ "for your uses (`lib.concat(Map)StringsSep`)." )
|
||||
(lib.concatStringsSep "; " (map (x: "${x}=") (attrNames a)));
|
||||
|
||||
showVal = with lib;
|
||||
trace ( "Warning: `showVal` is deprecated "
|
||||
+ "and will be removed in the next release, "
|
||||
+ "please use `traceSeqN`" )
|
||||
(let
|
||||
modify = v:
|
||||
let pr = f: { __pretty = f; val = v; };
|
||||
in if isDerivation v then pr
|
||||
(drv: "<δ:${drv.name}:${concatStringsSep ","
|
||||
(attrNames drv)}>")
|
||||
else if [] == v then pr (const "[]")
|
||||
else if isList v then pr (l: "[ ${go (head l)}, … ]")
|
||||
else if isAttrs v then pr
|
||||
(a: "{ ${ concatStringsSep ", " (attrNames a)} }")
|
||||
else v;
|
||||
go = x: generators.toPretty
|
||||
{ allowPrettyValues = true; }
|
||||
(modify x);
|
||||
in go);
|
||||
|
||||
traceXMLVal = x:
|
||||
trace ( "Warning: `traceXMLVal` is deprecated "
|
||||
+ "and will be removed in the next release. "
|
||||
+ "Please use `traceValFn builtins.toXML`." )
|
||||
(trace (builtins.toXML x) x);
|
||||
traceXMLValMarked = str: x:
|
||||
trace ( "Warning: `traceXMLValMarked` is deprecated "
|
||||
+ "and will be removed in the next release. "
|
||||
+ "Please use `traceValFn (x: str + builtins.toXML x)`." )
|
||||
(trace (str + builtins.toXML x) 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:
|
||||
trace ( "Warning: `traceValIfNot` is deprecated "
|
||||
+ "and will be removed in the next release. "
|
||||
+ "Please use `if/then/else` and `traceValSeq 1`.")
|
||||
(if c x then true else traceSeq (showVal x) false);
|
||||
|
||||
|
||||
addErrorContextToAttrs = attrs:
|
||||
trace ( "Warning: `addErrorContextToAttrs` is deprecated "
|
||||
+ "and will be removed in the next release. "
|
||||
+ "Please use `builtins.addErrorContext` directly." )
|
||||
(lib.mapAttrs (a: v: lib.addErrorContext "while evaluating ${a}" v) attrs);
|
||||
|
||||
# example: (traceCallXml "myfun" id 3) will output something like
|
||||
# calling myfun arg 1: 3 result: 3
|
||||
@@ -109,17 +178,20 @@ rec {
|
||||
# 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
|
||||
trace ( "Warning: `traceCallXml` is deprecated "
|
||||
+ "and will be removed in the next release. "
|
||||
+ "Please complain if you use the function regularly." )
|
||||
(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)
|
||||
traceCallXml (builtins.add 1 nr) "${str}\n arg ${builtins.toString nr} is \n ${builtins.toXML (builtins.seq arg arg)}" (expr arg)
|
||||
)
|
||||
else
|
||||
let r = strict expr;
|
||||
let r = builtins.seq expr expr;
|
||||
in trace "${str}\n result:\n${builtins.toXML r}" r
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
@@ -5,9 +5,11 @@
|
||||
*/
|
||||
let
|
||||
|
||||
callLibs = file: import file { inherit lib; };
|
||||
inherit (import ./fixed-points.nix {}) makeExtensible;
|
||||
|
||||
lib = rec {
|
||||
lib = makeExtensible (self: let
|
||||
callLibs = file: import file { lib = self; };
|
||||
in with self; {
|
||||
|
||||
# often used, or depending on very little
|
||||
trivial = callLibs ./trivial.nix;
|
||||
@@ -21,7 +23,7 @@ let
|
||||
|
||||
# packaging
|
||||
customisation = callLibs ./customisation.nix;
|
||||
maintainers = import ./maintainers-list.nix;
|
||||
maintainers = import ../maintainers/maintainer-list.nix;
|
||||
meta = callLibs ./meta.nix;
|
||||
sources = callLibs ./sources.nix;
|
||||
versions = callLibs ./versions.nix;
|
||||
@@ -56,7 +58,7 @@ let
|
||||
replaceStrings seq stringLength sub substring tail;
|
||||
inherit (trivial) id const concat or and boolToString mergeAttrs
|
||||
flip mapNullable inNixShell min max importJSON warn info
|
||||
nixpkgsVersion mod compare splitByAndCompare
|
||||
nixpkgsVersion version mod compare splitByAndCompare
|
||||
functionArgs setFunctionArgs isFunction;
|
||||
|
||||
inherit (fixedPoints) fix fix' extends composeExtensions
|
||||
@@ -72,7 +74,7 @@ let
|
||||
inherit (lists) singleton foldr fold foldl foldl' imap0 imap1
|
||||
concatMap flatten remove findSingle findFirst any all count
|
||||
optional optionals toList range partition zipListsWith zipLists
|
||||
reverseList listDfs toposort sort compareLists take drop sublist
|
||||
reverseList listDfs toposort sort naturalSort compareLists take drop sublist
|
||||
last init crossLists unique intersectLists subtractLists
|
||||
mutuallyExclusive;
|
||||
inherit (strings) concatStrings concatMapStrings concatImapStrings
|
||||
@@ -113,11 +115,12 @@ let
|
||||
unknownModule mkOption;
|
||||
inherit (types) isType setType defaultTypeMerge defaultFunctor
|
||||
isOptionType mkOptionType;
|
||||
inherit (debug) addErrorContextToAttrs traceIf traceVal
|
||||
inherit (debug) addErrorContextToAttrs traceIf traceVal traceValFn
|
||||
traceXMLVal traceXMLValMarked traceSeq traceSeqN traceValSeq
|
||||
traceValSeqN traceShowVal traceShowValMarked
|
||||
showVal traceCall traceCall2 traceCall3 traceValIfNot runTests
|
||||
testAllTrue strict traceCallXml attrNamesToStr;
|
||||
traceValSeqFn traceValSeqN traceValSeqNFn traceShowVal
|
||||
traceShowValMarked showVal traceCall traceCall2 traceCall3
|
||||
traceValIfNot runTests testAllTrue traceCallXml
|
||||
attrNamesToStr;
|
||||
inherit (misc) maybeEnv defaultMergeArg defaultMerge foldArgs
|
||||
defaultOverridableDelayableArgs composedArgsAndFun
|
||||
maybeAttrNullable maybeAttr ifEnable checkFlag getValue
|
||||
@@ -128,5 +131,5 @@ let
|
||||
mergeAttrsNoOverride mergeAttrByFunc mergeAttrsByFuncDefaults
|
||||
mergeAttrsByFuncDefaultsClean mergeAttrBy
|
||||
prepareDerivationArgs nixType imap overridableDelayableArgs;
|
||||
};
|
||||
});
|
||||
in lib
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
* They all follow a similar interface:
|
||||
* generator { config-attrs } data
|
||||
*
|
||||
* `config-attrs` are “holes” in the generators
|
||||
* with sensible default implementations that
|
||||
* can be overwritten. The default implementations
|
||||
* are mostly generators themselves, called with
|
||||
* their respective default values; they can be reused.
|
||||
*
|
||||
* Tests can be found in ./tests.nix
|
||||
* Documentation in the manual, #sec-generators
|
||||
*/
|
||||
@@ -20,6 +26,32 @@ in
|
||||
|
||||
rec {
|
||||
|
||||
## -- HELPER FUNCTIONS & DEFAULTS --
|
||||
|
||||
/* Convert a value to a sensible default string representation.
|
||||
* The builtin `toString` function has some strange defaults,
|
||||
* suitable for bash scripts but not much else.
|
||||
*/
|
||||
mkValueStringDefault = {}: v: with builtins;
|
||||
let err = t: v: abort
|
||||
("generators.mkValueStringDefault: " +
|
||||
"${t} not supported: ${toPretty {} v}");
|
||||
in if isInt v then toString v
|
||||
# we default to not quoting strings
|
||||
else if isString v then v
|
||||
# isString returns "1", which is not a good default
|
||||
else if true == v then "true"
|
||||
# here it returns to "", which is even less of a good default
|
||||
else if false == v then "false"
|
||||
else if null == v then "null"
|
||||
# if you have lists you probably want to replace this
|
||||
else if isList v then err "lists" v
|
||||
# same as for lists, might want to replace
|
||||
else if isAttrs v then err "attrsets" v
|
||||
else if isFunction v then err "functions" v
|
||||
else err "this value is" (toString v);
|
||||
|
||||
|
||||
/* Generate a line of key k and value v, separated by
|
||||
* character sep. If sep appears in k, it is escaped.
|
||||
* Helper for synaxes with different separators.
|
||||
@@ -30,11 +62,14 @@ rec {
|
||||
* > "f\:oo:bar"
|
||||
*/
|
||||
mkKeyValueDefault = {
|
||||
mkValueString ? toString
|
||||
mkValueString ? mkValueStringDefault {}
|
||||
}: sep: k: v:
|
||||
"${libStr.escape [sep] k}${sep}${mkValueString v}";
|
||||
|
||||
|
||||
## -- FILE FORMAT GENERATORS --
|
||||
|
||||
|
||||
/* Generate a key-value-style config file from an attrset.
|
||||
*
|
||||
* mkKeyValue is the same as in toINI.
|
||||
@@ -98,6 +133,7 @@ rec {
|
||||
*/
|
||||
toYAML = {}@args: toJSON args;
|
||||
|
||||
|
||||
/* Pretty print a value, akin to `builtins.trace`.
|
||||
* Should probably be a builtin as well.
|
||||
*/
|
||||
@@ -107,17 +143,13 @@ rec {
|
||||
(This means fn is type Val -> String.) */
|
||||
allowPrettyValues ? false
|
||||
}@args: v: with builtins;
|
||||
if isInt v then toString v
|
||||
else if isBool v then (if v == true then "true" else "false")
|
||||
else if isString v then "\"" + v + "\""
|
||||
else if null == v then "null"
|
||||
else if isFunction v then
|
||||
let fna = lib.functionArgs v;
|
||||
showFnas = concatStringsSep "," (libAttr.mapAttrsToList
|
||||
(name: hasDefVal: if hasDefVal then "(${name})" else name)
|
||||
fna);
|
||||
in if fna == {} then "<λ>"
|
||||
else "<λ:{${showFnas}}>"
|
||||
let isPath = v: typeOf v == "path";
|
||||
in if isInt v then toString v
|
||||
else if isString v then ''"${libStr.escape [''"''] v}"''
|
||||
else if true == v then "true"
|
||||
else if false == v then "false"
|
||||
else if null == v then "null"
|
||||
else if isPath v then toString v
|
||||
else if isList v then "[ "
|
||||
+ libStr.concatMapStringsSep " " (toPretty args) v
|
||||
+ " ]"
|
||||
@@ -126,12 +158,21 @@ rec {
|
||||
if attrNames v == [ "__pretty" "val" ] && allowPrettyValues
|
||||
then v.__pretty v.val
|
||||
# TODO: there is probably a better representation?
|
||||
else if v ? type && v.type == "derivation" then "<δ>"
|
||||
else if v ? type && v.type == "derivation" then
|
||||
"<δ:${v.name}>"
|
||||
# "<δ:${concatStringsSep "," (builtins.attrNames v)}>"
|
||||
else "{ "
|
||||
+ libStr.concatStringsSep " " (libAttr.mapAttrsToList
|
||||
(name: value:
|
||||
"${toPretty args name} = ${toPretty args value};") v)
|
||||
+ " }"
|
||||
else if isFunction v then
|
||||
let fna = lib.functionArgs v;
|
||||
showFnas = concatStringsSep "," (libAttr.mapAttrsToList
|
||||
(name: hasDefVal: if hasDefVal then "(${name})" else name)
|
||||
fna);
|
||||
in if fna == {} then "<λ>"
|
||||
else "<λ:{${showFnas}}>"
|
||||
else abort "toPretty: should never happen (v = ${v})";
|
||||
|
||||
}
|
||||
|
||||
@@ -179,6 +179,11 @@ lib.mapAttrs (n: v: v // { shortName = n; }) rec {
|
||||
fullName = "CeCILL-C Free Software License Agreement";
|
||||
};
|
||||
|
||||
cpal10 = spdx {
|
||||
spdxId = "CPAL-1.0";
|
||||
fullName = "Common Public Attribution License 1.0";
|
||||
};
|
||||
|
||||
cpl10 = spdx {
|
||||
spdxId = "CPL-1.0";
|
||||
fullName = "Common Public License 1.0";
|
||||
@@ -279,7 +284,7 @@ lib.mapAttrs (n: v: v // { shortName = n; }) rec {
|
||||
|
||||
gpl2Oss = {
|
||||
fullName = "GNU General Public License version 2 only (with OSI approved licenses linking exception)";
|
||||
url = http://www.mysql.com/about/legal/licensing/foss-exception;
|
||||
url = https://www.mysql.com/about/legal/licensing/foss-exception;
|
||||
};
|
||||
|
||||
gpl2Plus = spdx {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# General list operations.
|
||||
{ lib }:
|
||||
with lib.trivial;
|
||||
|
||||
let
|
||||
inherit (lib.strings) toInt;
|
||||
in
|
||||
rec {
|
||||
|
||||
inherit (builtins) head tail length isList elemAt concatLists filter elem genList;
|
||||
@@ -409,6 +411,25 @@ rec {
|
||||
then compareLists cmp (tail a) (tail b)
|
||||
else rel;
|
||||
|
||||
/* Sort list using "Natural sorting".
|
||||
Numeric portions of strings are sorted in numeric order.
|
||||
|
||||
Example:
|
||||
naturalSort ["disk11" "disk8" "disk100" "disk9"]
|
||||
=> ["disk8" "disk9" "disk11" "disk100"]
|
||||
naturalSort ["10.46.133.149" "10.5.16.62" "10.54.16.25"]
|
||||
=> ["10.5.16.62" "10.46.133.149" "10.54.16.25"]
|
||||
naturalSort ["v0.2" "v0.15" "v0.0.9"]
|
||||
=> [ "v0.0.9" "v0.2" "v0.15" ]
|
||||
*/
|
||||
naturalSort = lst:
|
||||
let
|
||||
vectorise = s: map (x: if isList x then toInt (head x) else x) (builtins.split "(0|[1-9][0-9]*)" s);
|
||||
prepared = map (x: [ (vectorise x) x ]) lst; # remember vectorised version for O(n) regex splits
|
||||
less = a: b: (compareLists compare (head a) (head b)) < 0;
|
||||
in
|
||||
map (x: elemAt x 1) (sort less prepared);
|
||||
|
||||
/* Return the first (at most) N elements of a list.
|
||||
|
||||
Example:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -59,7 +59,7 @@ rec {
|
||||
};
|
||||
};
|
||||
|
||||
closed = closeModules (modules ++ [ internalModule ]) ({ inherit config options; lib = import ./.; } // specialArgs);
|
||||
closed = closeModules (modules ++ [ internalModule ]) ({ inherit config options lib; } // specialArgs);
|
||||
|
||||
options = mergeModules prefix (reverseList (filterModules (specialArgs.modulesPath or "") closed));
|
||||
|
||||
@@ -159,7 +159,7 @@ rec {
|
||||
context = name: ''while evaluating the module argument `${name}' in "${key}":'';
|
||||
extraArgs = builtins.listToAttrs (map (name: {
|
||||
inherit name;
|
||||
value = addErrorContext (context name)
|
||||
value = builtins.addErrorContext (context name)
|
||||
(args.${name} or config._module.args.${name});
|
||||
}) requiredArgs);
|
||||
|
||||
@@ -309,7 +309,7 @@ rec {
|
||||
res.mergedValue;
|
||||
|
||||
in opt //
|
||||
{ value = addErrorContext "while evaluating the option `${showOption loc}':" value;
|
||||
{ value = builtins.addErrorContext "while evaluating the option `${showOption loc}':" value;
|
||||
definitions = map (def: def.value) res.defsFinal;
|
||||
files = map (def: def.file) res.defsFinal;
|
||||
inherit (res) isDefined;
|
||||
@@ -660,7 +660,7 @@ rec {
|
||||
doRename = { from, to, visible, warn, use }:
|
||||
let
|
||||
toOf = attrByPath to
|
||||
(abort "Renaming error: option `${showOption to}' does not exists.");
|
||||
(abort "Renaming error: option `${showOption to}' does not exist.");
|
||||
in
|
||||
{ config, options, ... }:
|
||||
{ options = setAttrByPath from (mkOption {
|
||||
|
||||
@@ -82,7 +82,7 @@ rec {
|
||||
=> "//bin"
|
||||
*/
|
||||
makeSearchPath = subDir: packages:
|
||||
concatStringsSep ":" (map (path: path + "/" + subDir) packages);
|
||||
concatStringsSep ":" (map (path: path + "/" + subDir) (builtins.filter (x: x != null) packages));
|
||||
|
||||
/* Construct a Unix-style search path, using given package output.
|
||||
If no output is found, fallback to `.out` and then to the default.
|
||||
@@ -437,6 +437,13 @@ rec {
|
||||
*/
|
||||
fixedWidthNumber = width: n: fixedWidthString width "0" (toString n);
|
||||
|
||||
/* Check whether a value can be coerced to a string */
|
||||
isCoercibleToString = x:
|
||||
builtins.elem (builtins.typeOf x) [ "path" "string" "null" "int" "float" "bool" ] ||
|
||||
(builtins.isList x && lib.all isCoercibleToString x) ||
|
||||
x ? outPath ||
|
||||
x ? __toString;
|
||||
|
||||
/* Check whether a value is a store path.
|
||||
|
||||
Example:
|
||||
@@ -450,7 +457,7 @@ rec {
|
||||
=> false
|
||||
*/
|
||||
isStorePath = x:
|
||||
builtins.isString x
|
||||
isCoercibleToString x
|
||||
&& builtins.substring 0 1 (toString x) == "/"
|
||||
&& dirOf (builtins.toPath x) == builtins.storeDir;
|
||||
|
||||
|
||||
@@ -45,8 +45,17 @@ rec {
|
||||
};
|
||||
# Misc boolean options
|
||||
useAndroidPrebuilt = false;
|
||||
useiOSPrebuilt = false;
|
||||
isiPhoneSimulator = false;
|
||||
} // mapAttrs (n: v: v final.parsed) inspect.predicates
|
||||
// args;
|
||||
in assert final.useAndroidPrebuilt -> final.isAndroid;
|
||||
assert lib.foldl
|
||||
(pass: { assertion, message }:
|
||||
if assertion final
|
||||
then pass
|
||||
else throw message)
|
||||
true
|
||||
(final.parsed.abi.assertions or []);
|
||||
final;
|
||||
}
|
||||
|
||||
@@ -87,16 +87,36 @@ rec {
|
||||
#
|
||||
|
||||
iphone64 = {
|
||||
config = "aarch64-apple-darwin14";
|
||||
arch = "arm64";
|
||||
libc = "libSystem";
|
||||
config = "aarch64-apple-ios";
|
||||
# config = "aarch64-apple-darwin14";
|
||||
sdkVer = "10.2";
|
||||
useiOSPrebuilt = true;
|
||||
platform = {};
|
||||
};
|
||||
|
||||
iphone32 = {
|
||||
config = "arm-apple-darwin10";
|
||||
arch = "armv7-a";
|
||||
libc = "libSystem";
|
||||
config = "armv7a-apple-ios";
|
||||
# config = "arm-apple-darwin10";
|
||||
sdkVer = "10.2";
|
||||
useiOSPrebuilt = true;
|
||||
platform = {};
|
||||
};
|
||||
|
||||
iphone64-simulator = {
|
||||
config = "x86_64-apple-ios";
|
||||
# config = "x86_64-apple-darwin14";
|
||||
sdkVer = "10.2";
|
||||
useiOSPrebuilt = true;
|
||||
isiPhoneSimulator = true;
|
||||
platform = {};
|
||||
};
|
||||
|
||||
iphone32-simulator = {
|
||||
config = "i686-apple-ios";
|
||||
# config = "i386-apple-darwin11";
|
||||
sdkVer = "10.2";
|
||||
useiOSPrebuilt = true;
|
||||
isiPhoneSimulator = true;
|
||||
platform = {};
|
||||
};
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ let
|
||||
inherit (lib.systems.inspect) patterns;
|
||||
|
||||
in rec {
|
||||
inherit (lib.systems.doubles) all mesaPlatforms;
|
||||
none = [];
|
||||
all = [ {} ]; # `{}` matches anything
|
||||
none = [];
|
||||
|
||||
arm = [ patterns.isAarch32 ];
|
||||
aarch64 = [ patterns.isAarch64 ];
|
||||
@@ -13,6 +13,7 @@ in rec {
|
||||
i686 = [ patterns.isi686 ];
|
||||
x86_64 = [ patterns.isx86_64 ];
|
||||
mips = [ patterns.isMips ];
|
||||
riscv = [ patterns.isRiscV ];
|
||||
|
||||
cygwin = [ patterns.isCygwin ];
|
||||
darwin = [ patterns.isDarwin ];
|
||||
@@ -24,4 +25,7 @@ in rec {
|
||||
netbsd = [ patterns.isNetBSD ];
|
||||
openbsd = [ patterns.isOpenBSD ];
|
||||
unix = patterns.isUnix; # Actually a list
|
||||
windows = [ patterns.isWindows ];
|
||||
|
||||
inherit (lib.systems.doubles) mesaPlatforms;
|
||||
}
|
||||
|
||||
@@ -21,9 +21,11 @@ rec {
|
||||
isLittleEndian = { cpu = { significantByte = significantBytes.littleEndian; }; };
|
||||
|
||||
isBSD = { kernel = { families = { inherit (kernelFamilies) bsd; }; }; };
|
||||
isDarwin = { kernel = { families = { inherit (kernelFamilies) darwin; }; }; };
|
||||
isUnix = [ isBSD isDarwin isLinux isSunOS isHurd isCygwin ];
|
||||
|
||||
isDarwin = { kernel = kernels.darwin; };
|
||||
isMacOS = { kernel = kernels.macos; };
|
||||
isiOS = { kernel = kernels.ios; };
|
||||
isLinux = { kernel = kernels.linux; };
|
||||
isSunOS = { kernel = kernels.solaris; };
|
||||
isFreeBSD = { kernel = kernels.freebsd; };
|
||||
@@ -38,12 +40,8 @@ rec {
|
||||
isMusl = with abis; map (a: { abi = a; }) [ musl musleabi musleabihf ];
|
||||
isUClibc = with abis; map (a: { abi = a; }) [ uclibc uclibceabi uclibceabihf ];
|
||||
|
||||
isKexecable = map (family: { kernel = kernels.linux; cpu.family = family; })
|
||||
[ "x86" "arm" "aarch64" "mips" ];
|
||||
isEfi = map (family: { cpu.family = family; })
|
||||
[ "x86" "arm" "aarch64" ];
|
||||
isSeccomputable = map (family: { kernel = kernels.linux; cpu.family = family; })
|
||||
[ "x86" "arm" "aarch64" "mips" ];
|
||||
|
||||
# Deprecated after 18.03
|
||||
isArm = isAarch32;
|
||||
|
||||
@@ -34,7 +34,7 @@ rec {
|
||||
|
||||
################################################################################
|
||||
|
||||
types.openSignifiantByte = mkOptionType {
|
||||
types.openSignificantByte = mkOptionType {
|
||||
name = "significant-byte";
|
||||
description = "Endianness";
|
||||
merge = mergeOneOption;
|
||||
@@ -42,7 +42,7 @@ rec {
|
||||
|
||||
types.significantByte = enum (attrValues significantBytes);
|
||||
|
||||
significantBytes = setTypes types.openSignifiantByte {
|
||||
significantBytes = setTypes types.openSignificantByte {
|
||||
bigEndian = {};
|
||||
littleEndian = {};
|
||||
};
|
||||
@@ -140,6 +140,7 @@ rec {
|
||||
|
||||
kernelFamilies = setTypes types.openKernelFamily {
|
||||
bsd = {};
|
||||
darwin = {};
|
||||
};
|
||||
|
||||
################################################################################
|
||||
@@ -155,7 +156,10 @@ rec {
|
||||
types.kernel = enum (attrValues kernels);
|
||||
|
||||
kernels = with execFormats; with kernelFamilies; setTypes types.openKernel {
|
||||
darwin = { execFormat = macho; families = { }; };
|
||||
# TODO(@Ericson2314): Don't want to mass-rebuild yet to keeping 'darwin' as
|
||||
# the nnormalized name for macOS.
|
||||
macos = { execFormat = macho; families = { inherit darwin; }; name = "darwin"; };
|
||||
ios = { execFormat = macho; families = { inherit darwin; }; };
|
||||
freebsd = { execFormat = elf; families = { inherit bsd; }; };
|
||||
hurd = { execFormat = elf; families = { }; };
|
||||
linux = { execFormat = elf; families = { }; };
|
||||
@@ -165,9 +169,13 @@ rec {
|
||||
solaris = { execFormat = elf; families = { }; };
|
||||
windows = { execFormat = pe; families = { }; };
|
||||
} // { # aliases
|
||||
# 'darwin' is the kernel for all of them. We choose macOS by default.
|
||||
darwin = kernels.macos;
|
||||
# TODO(@Ericson2314): Handle these Darwin version suffixes more generally.
|
||||
darwin10 = kernels.darwin;
|
||||
darwin14 = kernels.darwin;
|
||||
darwin10 = kernels.macos;
|
||||
darwin14 = kernels.macos;
|
||||
watchos = kernels.ios;
|
||||
tvos = kernels.ios;
|
||||
win32 = kernels.windows;
|
||||
};
|
||||
|
||||
@@ -191,7 +199,15 @@ rec {
|
||||
|
||||
gnueabi = { float = "soft"; };
|
||||
gnueabihf = { float = "hard"; };
|
||||
gnu = {};
|
||||
gnu = {
|
||||
assertions = [
|
||||
{ assertion = platform: !platform.isAarch32;
|
||||
message = ''
|
||||
The "gnu" ABI is ambiguous on 32-bit ARM. Use "gnueabi" or "gnueabihf" instead.
|
||||
'';
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
musleabi = { float = "soft"; };
|
||||
musleabihf = { float = "hard"; };
|
||||
@@ -206,7 +222,7 @@ rec {
|
||||
|
||||
################################################################################
|
||||
|
||||
types.system = mkOptionType {
|
||||
types.parsedPlatform = mkOptionType {
|
||||
name = "system";
|
||||
description = "fully parsed representation of llvm- or nix-style platform tuple";
|
||||
merge = mergeOneOption;
|
||||
@@ -220,7 +236,7 @@ rec {
|
||||
isSystem = isType "system";
|
||||
|
||||
mkSystem = components:
|
||||
assert types.system.check components;
|
||||
assert types.parsedPlatform.check components;
|
||||
setType "system" components;
|
||||
|
||||
mkSkeletonFromList = l: {
|
||||
@@ -266,7 +282,7 @@ rec {
|
||||
kernel = getKernel args.kernel;
|
||||
abi =
|
||||
/**/ if args ? abi then getAbi args.abi
|
||||
else if isLinux parsed then abis.gnu
|
||||
else if isLinux parsed then (if isAarch32 parsed then abis.gnueabi else abis.gnu)
|
||||
else if isWindows parsed then abis.gnu
|
||||
else abis.unknown;
|
||||
};
|
||||
@@ -276,8 +292,8 @@ rec {
|
||||
mkSystemFromString = s: mkSystemFromSkeleton (mkSkeletonFromList (lib.splitString "-" s));
|
||||
|
||||
doubleFromSystem = { cpu, vendor, kernel, abi, ... }:
|
||||
if abi == abis.cygnus
|
||||
then "${cpu.name}-cygwin"
|
||||
/**/ if abi == abis.cygnus then "${cpu.name}-cygwin"
|
||||
else if kernel.families ? darwin then "${cpu.name}-darwin"
|
||||
else "${cpu.name}-${kernel.name}";
|
||||
|
||||
tripleFromSystem = { cpu, vendor, kernel, abi, ... } @ sys: assert isSystem sys; let
|
||||
|
||||
@@ -93,6 +93,7 @@ runTests {
|
||||
"${builtins.storeDir}/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11";
|
||||
in {
|
||||
storePath = isStorePath goodPath;
|
||||
storePathDerivation = isStorePath (import ../.. {}).hello;
|
||||
storePathAppendix = isStorePath
|
||||
"${goodPath}/bin/python";
|
||||
nonAbsolute = isStorePath (concatStrings (tail (stringToCharacters goodPath)));
|
||||
@@ -106,6 +107,7 @@ runTests {
|
||||
};
|
||||
expected = {
|
||||
storePath = true;
|
||||
storePathDerivation = true;
|
||||
storePathAppendix = false;
|
||||
nonAbsolute = false;
|
||||
asPath = true;
|
||||
@@ -205,6 +207,29 @@ runTests {
|
||||
expected = ''f\:oo:bar'';
|
||||
};
|
||||
|
||||
testMkValueString = {
|
||||
expr = let
|
||||
vals = {
|
||||
int = 42;
|
||||
string = ''fo"o'';
|
||||
bool = true;
|
||||
bool2 = false;
|
||||
null = null;
|
||||
# float = 42.23; # floats are strange
|
||||
};
|
||||
in mapAttrs
|
||||
(const (generators.mkValueStringDefault {}))
|
||||
vals;
|
||||
expected = {
|
||||
int = "42";
|
||||
string = ''fo"o'';
|
||||
bool = "true";
|
||||
bool2 = "false";
|
||||
null = "null";
|
||||
# float = "42.23" true false [ "bar" ] ]'';
|
||||
};
|
||||
};
|
||||
|
||||
testToKeyValue = {
|
||||
expr = generators.toKeyValue {} {
|
||||
key = "value";
|
||||
@@ -247,6 +272,8 @@ runTests {
|
||||
"section 1" = {
|
||||
attribute1 = 5;
|
||||
x = "Me-se JarJar Binx";
|
||||
# booleans are converted verbatim by default
|
||||
boolean = false;
|
||||
};
|
||||
"foo[]" = {
|
||||
"he\\h=he" = "this is okay";
|
||||
@@ -258,6 +285,7 @@ runTests {
|
||||
|
||||
[section 1]
|
||||
attribute1=5
|
||||
boolean=false
|
||||
x=Me-se JarJar Binx
|
||||
'';
|
||||
};
|
||||
@@ -289,7 +317,8 @@ runTests {
|
||||
expr = mapAttrs (const (generators.toPretty {})) rec {
|
||||
int = 42;
|
||||
bool = true;
|
||||
string = "fnord";
|
||||
string = ''fno"rd'';
|
||||
path = /. + "/foo"; # toPath returns a string
|
||||
null_ = null;
|
||||
function = x: x;
|
||||
functionArgs = { arg ? 4, foo }: arg;
|
||||
@@ -300,13 +329,14 @@ runTests {
|
||||
expected = rec {
|
||||
int = "42";
|
||||
bool = "true";
|
||||
string = "\"fnord\"";
|
||||
string = ''"fno\"rd"'';
|
||||
path = "/foo";
|
||||
null_ = "null";
|
||||
function = "<λ>";
|
||||
functionArgs = "<λ:{(arg),foo}>";
|
||||
list = "[ 3 4 ${function} [ false ] ]";
|
||||
attrs = "{ \"foo\" = null; \"foo bar\" = \"baz\"; }";
|
||||
drv = "<δ>";
|
||||
drv = "<δ:test>";
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -136,7 +136,18 @@ checkConfigOutput "true" "$@" ./define-module-check.nix
|
||||
# Check coerced value.
|
||||
checkConfigOutput "\"42\"" config.value ./declare-coerced-value.nix
|
||||
checkConfigOutput "\"24\"" config.value ./declare-coerced-value.nix ./define-value-string.nix
|
||||
checkConfigError 'The option value .* in .* is not.*string or signed integer.*' config.value ./declare-coerced-value.nix ./define-value-list.nix
|
||||
checkConfigError 'The option value .* in .* is not.*string or signed integer convertible to it' config.value ./declare-coerced-value.nix ./define-value-list.nix
|
||||
|
||||
# Check coerced value with unsound coercion
|
||||
checkConfigOutput "12" config.value ./declare-coerced-value-unsound.nix
|
||||
checkConfigError 'The option value .* in .* is not.*8 bit signed integer.* or string convertible to it' config.value ./declare-coerced-value-unsound.nix ./define-value-string-bigint.nix
|
||||
checkConfigError 'unrecognised JSON value' config.value ./declare-coerced-value-unsound.nix ./define-value-string-arbitrary.nix
|
||||
|
||||
# Check loaOf with long list.
|
||||
checkConfigOutput "1 2 3 4 5 6 7 8 9 10" config.result ./loaOf-with-long-list.nix
|
||||
|
||||
# Check loaOf with many merges of lists.
|
||||
checkConfigOutput "1 2 3 4 5 6 7 8 9 10" config.result ./loaOf-with-many-list-merges.nix
|
||||
|
||||
cat <<EOF
|
||||
====== module tests ======
|
||||
|
||||
10
lib/tests/modules/declare-coerced-value-unsound.nix
Normal file
10
lib/tests/modules/declare-coerced-value-unsound.nix
Normal file
@@ -0,0 +1,10 @@
|
||||
{ lib, ... }:
|
||||
|
||||
{
|
||||
options = {
|
||||
value = lib.mkOption {
|
||||
default = "12";
|
||||
type = lib.types.coercedTo lib.types.str lib.toInt lib.types.ints.s8;
|
||||
};
|
||||
};
|
||||
}
|
||||
3
lib/tests/modules/define-value-string-arbitrary.nix
Normal file
3
lib/tests/modules/define-value-string-arbitrary.nix
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
value = "foobar";
|
||||
}
|
||||
3
lib/tests/modules/define-value-string-bigint.nix
Normal file
3
lib/tests/modules/define-value-string-bigint.nix
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
value = "1000";
|
||||
}
|
||||
19
lib/tests/modules/loaOf-with-long-list.nix
Normal file
19
lib/tests/modules/loaOf-with-long-list.nix
Normal file
@@ -0,0 +1,19 @@
|
||||
{ config, lib, ... }:
|
||||
|
||||
{
|
||||
options = {
|
||||
loaOfInt = lib.mkOption {
|
||||
type = lib.types.loaOf lib.types.int;
|
||||
};
|
||||
|
||||
result = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
loaOfInt = [ 1 2 3 4 5 6 7 8 9 10 ];
|
||||
|
||||
result = toString (lib.attrValues config.loaOfInt);
|
||||
};
|
||||
}
|
||||
19
lib/tests/modules/loaOf-with-many-list-merges.nix
Normal file
19
lib/tests/modules/loaOf-with-many-list-merges.nix
Normal file
@@ -0,0 +1,19 @@
|
||||
{ config, lib, ... }:
|
||||
|
||||
{
|
||||
options = {
|
||||
loaOfInt = lib.mkOption {
|
||||
type = lib.types.loaOf lib.types.int;
|
||||
};
|
||||
|
||||
result = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
loaOfInt = lib.mkMerge (map lib.singleton [ 1 2 3 4 5 6 7 8 9 10 ]);
|
||||
|
||||
result = toString (lib.attrValues config.loaOfInt);
|
||||
};
|
||||
}
|
||||
@@ -13,7 +13,6 @@ pkgs.stdenv.mkDerivation {
|
||||
export NIX_DB_DIR=$TEST_ROOT/db
|
||||
export NIX_LOCALSTATE_DIR=$TEST_ROOT/var
|
||||
export NIX_LOG_DIR=$TEST_ROOT/var/log/nix
|
||||
export NIX_MANIFESTS_DIR=$TEST_ROOT/var/nix/manifests
|
||||
export NIX_STATE_DIR=$TEST_ROOT/var/nix
|
||||
export NIX_STORE_DIR=$TEST_ROOT/store
|
||||
export PAGER=cat
|
||||
@@ -21,7 +20,7 @@ pkgs.stdenv.mkDerivation {
|
||||
nix-store --init
|
||||
|
||||
cd ${pkgs.path}/lib/tests
|
||||
./modules.sh
|
||||
bash ./modules.sh
|
||||
|
||||
[[ "$(nix-instantiate --eval --strict misc.nix)" == "[ ]" ]]
|
||||
|
||||
|
||||
@@ -58,11 +58,14 @@ rec {
|
||||
|
||||
inherit (lib.strings) fileContents;
|
||||
|
||||
release = fileContents ../.version;
|
||||
versionSuffix = let suffixFile = ../.version-suffix; in
|
||||
if pathExists suffixFile then fileContents suffixFile else "pre-git";
|
||||
|
||||
# Return the Nixpkgs version number.
|
||||
nixpkgsVersion =
|
||||
let suffixFile = ../.version-suffix; in
|
||||
fileContents ../.version
|
||||
+ (if pathExists suffixFile then fileContents suffixFile else "pre-git");
|
||||
version = release + versionSuffix;
|
||||
|
||||
nixpkgsVersion = builtins.trace "`lib.nixpkgsVersion` is deprecated, use `lib.version` instead!" version;
|
||||
|
||||
# Whether we're being called by nix-shell.
|
||||
inNixShell = builtins.getEnv "IN_NIX_SHELL" != "";
|
||||
|
||||
@@ -256,7 +256,7 @@ rec {
|
||||
functor = (defaultFunctor name) // { wrapped = elemType; };
|
||||
};
|
||||
|
||||
nonEmptyListOf = elemType:
|
||||
nonEmptyListOf = elemType:
|
||||
let list = addCheck (types.listOf elemType) (l: l != []);
|
||||
in list // { description = "non-empty " + list.description; };
|
||||
|
||||
@@ -280,15 +280,26 @@ rec {
|
||||
# List or attribute set of ...
|
||||
loaOf = elemType:
|
||||
let
|
||||
convertIfList = defIdx: def:
|
||||
convertAllLists = defs:
|
||||
let
|
||||
padWidth = stringLength (toString (length defs));
|
||||
unnamedPrefix = i: "unnamed-" + fixedWidthNumber padWidth i + ".";
|
||||
in
|
||||
imap1 (i: convertIfList (unnamedPrefix i)) defs;
|
||||
|
||||
convertIfList = unnamedPrefix: def:
|
||||
if isList def.value then
|
||||
{ inherit (def) file;
|
||||
value = listToAttrs (
|
||||
imap1 (elemIdx: elem:
|
||||
{ name = elem.name or "unnamed-${toString defIdx}.${toString elemIdx}";
|
||||
value = elem;
|
||||
}) def.value);
|
||||
}
|
||||
let
|
||||
padWidth = stringLength (toString (length def.value));
|
||||
unnamed = i: unnamedPrefix + fixedWidthNumber padWidth i;
|
||||
in
|
||||
{ inherit (def) file;
|
||||
value = listToAttrs (
|
||||
imap1 (elemIdx: elem:
|
||||
{ name = elem.name or (unnamed elemIdx);
|
||||
value = elem;
|
||||
}) def.value);
|
||||
}
|
||||
else
|
||||
def;
|
||||
listOnly = listOf elemType;
|
||||
@@ -297,7 +308,7 @@ rec {
|
||||
name = "loaOf";
|
||||
description = "list or attribute set of ${elemType.description}s";
|
||||
check = x: isList x || isAttrs x;
|
||||
merge = loc: defs: attrOnly.merge loc (imap1 convertIfList defs);
|
||||
merge = loc: defs: attrOnly.merge loc (convertAllLists defs);
|
||||
getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name?>"]);
|
||||
getSubModules = elemType.getSubModules;
|
||||
substSubModules = m: loaOf (elemType.substSubModules m);
|
||||
@@ -419,16 +430,13 @@ rec {
|
||||
assert coercedType.getSubModules == null;
|
||||
mkOptionType rec {
|
||||
name = "coercedTo";
|
||||
description = "${finalType.description} or ${coercedType.description}";
|
||||
check = x: finalType.check x || coercedType.check x;
|
||||
description = "${finalType.description} or ${coercedType.description} convertible to it";
|
||||
check = x: finalType.check x || (coercedType.check x && finalType.check (coerceFunc x));
|
||||
merge = loc: defs:
|
||||
let
|
||||
coerceVal = val:
|
||||
if finalType.check val then val
|
||||
else let
|
||||
coerced = coerceFunc val;
|
||||
in assert finalType.check coerced; coerced;
|
||||
|
||||
else coerceFunc val;
|
||||
in finalType.merge loc (map (def: def // { value = coerceVal def.value; }) defs);
|
||||
getSubOptions = finalType.getSubOptions;
|
||||
getSubModules = finalType.getSubModules;
|
||||
|
||||
Reference in New Issue
Block a user