@ -24,9 +24,9 @@ rec {
let
coerceToModule = n : x :
if isAttrs x || builtins . isFunction x then
unifyModuleSyntax " a n o n - ${ toString n } " ( applyIfFunction x args )
unifyModuleSyntax " < u n k n o w n - f i l e > " " an o n - ${ toString n } " ( applyIfFunction x args )
else
unifyModuleSyntax ( toString x ) ( applyIfFunction ( import x ) args ) ;
unifyModuleSyntax ( toString x ) ( toString x ) ( applyIfFunction ( import x ) args ) ;
toClosureList = imap ( path : coerceToModule path ) ;
in
builtins . genericClosure {
@ -36,19 +36,19 @@ rec {
/* M a s s a g e a m o d u l e i n t o c a n o n i c a l f o r m , t h a t i s , a s e t c o n s i s t i n g
of ‘ options ’ , ‘ config ’ and ‘ imports ’ attributes . * /
unifyModuleSyntax = key : m :
unifyModuleSyntax = file : key : m :
if m ? config || m ? options || m ? imports then
let badAttrs = removeAttrs m [ " i m p o r t s " " o p t i o n s " " c o n f i g " ] ; in
if badAttrs != { } then
throw " M o d u l e ` ${ key } ' h a s a n u n s u p p o r t e d a t t r i b u t e ` ${ head ( attrNames badAttrs ) } ' . ${ builtins . toXML m } "
else
{ inherit key ;
{ inherit file key ;
imports = m . imports or [ ] ;
options = m . options or { } ;
config = m . config or { } ;
}
else
{ inherit key ;
{ inherit file key ;
imports = m . require or [ ] ;
options = { } ;
config = m ;
@ -62,19 +62,47 @@ rec {
corresponding option definitions in all machines , returning them
in the ‘ value ’ attribute of each option . * /
mergeModules = modules :
mergeModules' [ ] ( map ( m : m . options ) modules ) ( concatMap ( m : pushDownProperties m . config ) modules ) ;
mergeModules' [ ] modules
( concatMap ( m : map ( config : { inherit ( m ) file ; inherit config ; } ) ( pushDownProperties m . config ) ) modules ) ;
mergeModules' = loc : options : configs :
zipAttrsWith ( name : vals :
let loc' = loc ++ [ name ] ; in
if all isOption vals then
let opt = fixupOptionType loc' ( mergeOptionDecls loc' vals ) ;
in evalOptionValue loc' opt ( catAttrs name configs )
else if any isOption vals then
throw " T h e r e a r e o p t i o n s w i t h t h e p r e f i x ` ${ showOption loc' } ' , w h i c h i s i t s e l f a n o p t i o n . "
else
mergeModules' loc' vals ( concatMap pushDownProperties ( catAttrs name configs ) )
) options ;
let names = concatMap ( m : attrNames m . options ) options ;
in listToAttrs ( map ( name : {
# We're descending into attribute ‘name’.
inherit name ;
value =
let
loc' = loc ++ [ name ] ;
# Get all submodules that declare ‘name’.
decls = concatLists ( map ( m :
optional ( hasAttr name m . options )
{ inherit ( m ) file ; options = getAttr name m . options ; }
) options ) ;
# Get all submodules that define ‘name’.
defns = concatLists ( map ( m :
optionals ( hasAttr name m . config )
( map ( config : { inherit ( m ) file ; inherit config ; } )
( pushDownProperties ( getAttr name m . config ) ) )
) configs ) ;
nrOptions = count ( m : isOption m . options ) decls ;
defns2 = concatMap ( m :
optional ( hasAttr name m . config )
{ inherit ( m ) file ; config = getAttr name m . config ; }
) configs ;
in
if nrOptions == length decls then
let opt = fixupOptionType loc' ( mergeOptionDecls loc' decls ) ;
in evalOptionValue loc' opt defns2
else if nrOptions != 0 then
let
firstOption = findFirst ( m : isOption m . options ) " " decls ;
firstNonOption = findFirst ( m : ! isOption m . options ) " " decls ;
in
throw " T h e o p t i o n ` ${ showOption loc' } ' i n ` ${ firstOption . file } ' i s a p r e f i x o f o p t i o n s i n ` ${ firstNonOption . file } ' . "
else
mergeModules' loc' decls defns ;
} ) names ) ;
/* M e r g e m u l t i p l e o p t i o n d e c l a r a t i o n s i n t o a s i n g l e d e c l a r a t i o n . I n
general , there should be only one declaration of each option .
@ -83,41 +111,41 @@ rec {
module to add sub-options to an option declared somewhere else
( e . g . multiple modules define sub-options for ‘ fileSystems ’ ) . * /
mergeOptionDecls = loc : opts :
fold ( opt1 : opt2 :
if opt1 ? default && opt2 ? default ||
opt1 ? example && opt2 ? example ||
opt1 ? description && opt2 ? description ||
opt1 ? merge && opt2 ? merge ||
opt1 ? apply && opt2 ? apply ||
opt1 ? type && opt2 ? type
fold ( opt : res :
if opt . options ? default && res ? default ||
opt . options ? example && res ? example ||
opt . options ? description && res ? description ||
opt . options ? merge && res ? merge ||
opt . options ? apply && res ? apply ||
opt . options ? type && res ? type
then
throw " C o n f l i c t i n g d e c l a r a t i o n s o f t h e o p t i o n ` ${ showOption loc } ' . "
throw " T h e o p t i o n ` ${ showOption loc } ' i n ` ${ opt . file } ' i s a l r e a d y d e c l a r e d i n ${ concatStringsSep " a n d " ( map ( d : " ` ${ d } ' " ) res . declarations ) } . "
else
opt1 // opt2
// optionalAttrs ( opt1 ? options && opt2 ? options )
{ options = [ opt1 . options opt2 . options ] ; }
) { } opts ;
opt . options // res //
{ declarations = [ opt . file ] ++ res . declarations ;
options = optionals ( opt . options ? options ) ( toList opt . options . options ++ res . options ) ;
}
) { declarations = [ ] ; options = [ ] ; } opts ;
/* M e r g e a l l t h e d e f i n i t i o n s o f a n o p t i o n t o p r o d u c e t h e f i n a l
config value . * /
evalOptionValue = loc : opt : def s:
evalOptionValue = loc : opt : cfg s:
let
# Process mkMerge and mkIf properties.
defs' = concatMap dischargeProperties def s;
defs' = concatMap ( m : map ( config : { inherit ( m ) file ; inherit config ; } ) ( dischargeProperties m . config ) ) cfg s;
# Process mkOverride properties, adding in the default
# value specified in the option declaration (if any).
defsFinal = filterOverrides ( optional ( opt ? default ) ( mkOptionDefault opt . default ) ++ defs' ) ;
# Type-check the remaining definitions, and merge them
# if possible.
defsFinal = filterOverrides ( optional ( opt ? default ) ( { file = head opt . declarations ; config = mkOptionDefault opt . default ; } ) ++ defs' ) ;
# Type-check the remaining definitions, and merge them if
# possible.
merged =
if defsFinal == [ ] then
throw " T h e o p t i o n ` ${ showOption loc } ' i s u s e d b u t n o t d e f i n e d . "
else
if all opt . type . check defsFinal then
opt . type . merge defsFinal
#throw "The option `${showOption loc}' has multiple values (with no way to merge them)."
else
throw " A v a l u e o f t h e o p t i o n ` ${ showOption loc } ' h a s a b a d t y p e . " ;
fold ( def : res :
if opt . type . check def . config then res
else throw " T h e o p t i o n v a l u e ` ${ showOption loc } ' i n ` ${ def . file } ' i s n o t a ${ opt . type . name } . " )
( opt . type . merge ( map ( m : m . config ) defsFinal ) ) defsFinal ;
# Finally, apply the ‘apply’ function to the merged
# value. This allows options to yield a value computed
# from the definitions.
@ -178,17 +206,27 @@ rec {
is , . numerically lowest ) priority , and strip the mkOverride
properties . For example ,
[ ( mkOverride 10 " a " ) ( mkOverride 20 " b " ) " z " ( mkOverride 10 " d " ) ]
[ { file = " / 1 " ; config = mkOverride 10 " a " ; }
{ file = " / 2 " ; config = mkOverride 20 " b " ; }
{ file = " / 3 " ; config = " z " ; }
{ file = " / 4 " ; config = mkOverride 10 " d " ; }
]
yields
[ { file = " / 1 " ; config = " a " ; }
{ file = " / 4 " ; config = " d " ; }
]
yields ‘ [ " a " " d " ] ’ . Note that " z " has the default priority 100 .
Note that " z " has the default priority 100 .
* /
filterOverrides = defs :
let
defaultPrio = 100 ;
getPrio = def : if def . _type or " " == " o v e r r i d e " then def . priority else defaultPrio ;
getPrio = def : if def . config . _type or " " == " o v e r r i d e " then def . config . priority else defaultPrio ;
min = x : y : if x < y then x else y ;
highestPrio = fold ( def : prio : min ( getPrio def ) prio ) 9999 defs ;
strip = def : if def . _type or " " == " o v e r r i d e " then def . content else def ;
strip = def : if def . config . _type or " " == " o v e r r i d e " then def // { config = def . config . content ; } else def ;
in concatMap ( def : if getPrio def == highestPrio then [ ( strip def ) ] else [ ] ) defs ;
/* H a c k f o r b a c k w a r d c o m p a t i b i l i t y : c o n v e r t o p t i o n s o f t y p e