Merge pull request #3856 from nbp/submodule-declarations

Annotate option-set options with the file in which they are declared.
This commit is contained in:
Nicolas B. Pierron 2014-09-07 10:39:47 -07:00
commit 388b7baa73
2 changed files with 55 additions and 12 deletions

View File

@ -132,20 +132,44 @@ rec {
The exception is the options attribute, which specifies The exception is the options attribute, which specifies
sub-options. These can be specified multiple times to allow one sub-options. These can be specified multiple times to allow one
module to add sub-options to an option declared somewhere else module to add sub-options to an option declared somewhere else
(e.g. multiple modules define sub-options for fileSystems). */ (e.g. multiple modules define sub-options for fileSystems).
'loc' is the list of attribute names where the option is located.
'opts' is a list of modules. Each module has an options attribute which
correspond to the definition of 'loc' in 'opt.file'. */
mergeOptionDecls = loc: opts: mergeOptionDecls = loc: opts:
fold (opt: res: fold (opt: res:
if opt.options ? default && res ? default || if opt.options ? default && res ? default ||
opt.options ? example && res ? example || opt.options ? example && res ? example ||
opt.options ? description && res ? description || opt.options ? description && res ? description ||
opt.options ? apply && res ? apply || opt.options ? apply && res ? apply ||
opt.options ? type && res ? type # Accept to merge options which have identical types.
opt.options ? type && res ? type && opt.options.type.name != res.type.name
then then
throw "The option `${showOption loc}' in `${opt.file}' is already declared in ${showFiles res.declarations}." throw "The option `${showOption loc}' in `${opt.file}' is already declared in ${showFiles res.declarations}."
else else
opt.options // res // let
/* Add the modules of the current option to the list of modules
already collected. The options attribute except either a list of
submodules or a submodule. For each submodule, we add the file of the
current option declaration as the file use for the submodule. If the
submodule defines any filename, then we ignore the enclosing option file. */
options' = toList opt.options.options;
addModuleFile = m:
if isFunction m then args: { _file = opt.file; } // (m args)
else { _file = opt.file; } // m;
coerceOption = file: opt:
if isFunction opt then args: { _file = file; } // (opt args)
else { _file = file; options = opt; };
getSubModules = opt.options.type.getSubModules or null;
submodules =
if getSubModules != null then map addModuleFile getSubModules ++ res.options
else if opt.options ? options then map (coerceOption opt.file) options' ++ res.options
else res.options;
in opt.options // res //
{ declarations = [opt.file] ++ res.declarations; { declarations = [opt.file] ++ res.declarations;
options = if opt.options ? options then [(toList opt.options.options ++ res.options)] else []; options = submodules;
} }
) { inherit loc; declarations = []; options = []; } opts; ) { inherit loc; declarations = []; options = []; } opts;
@ -273,15 +297,12 @@ rec {
in sort compare defs'; in sort compare defs';
/* Hack for backward compatibility: convert options of type /* Hack for backward compatibility: convert options of type
optionSet to configOf. FIXME: remove eventually. */ optionSet to options of type submodule. FIXME: remove
eventually. */
fixupOptionType = loc: opt: fixupOptionType = loc: opt:
let let
options' = opt.options or options = opt.options or
(throw "Option `${showOption loc'}' has type optionSet but has no option attribute, in ${showFiles opt.declarations}."); (throw "Option `${showOption loc'}' has type optionSet but has no option attribute, in ${showFiles opt.declarations}.");
coerce = x:
if isFunction x then x
else { config, ... }: { options = x; };
options = map coerce (flatten options');
f = tp: f = tp:
if tp.name == "option set" || tp.name == "submodule" then if tp.name == "option set" || tp.name == "submodule" then
throw "The option ${showOption loc} uses submodules without a wrapping type, in ${showFiles opt.declarations}." throw "The option ${showOption loc} uses submodules without a wrapping type, in ${showFiles opt.declarations}."
@ -290,7 +311,10 @@ rec {
else if tp.name == "list of option sets" then types.listOf (types.submodule options) else if tp.name == "list of option sets" then types.listOf (types.submodule options)
else if tp.name == "null or option set" then types.nullOr (types.submodule options) else if tp.name == "null or option set" then types.nullOr (types.submodule options)
else tp; else tp;
in opt // { type = f (opt.type or types.unspecified); }; in
if opt.type.getSubModules or null == null
then opt // { type = f (opt.type or types.unspecified); }
else opt // { type = opt.type.substSubModules opt.options; options = []; };
/* Properties. */ /* Properties. */

View File

@ -33,9 +33,14 @@ rec {
, # Return a flat list of sub-options. Used to generate , # Return a flat list of sub-options. Used to generate
# documentation. # documentation.
getSubOptions ? prefix: {} getSubOptions ? prefix: {}
, # List of modules if any, or null if none.
getSubModules ? null
, # Function for building the same option type with a different list of
# modules.
substSubModules ? m: null
}: }:
{ _type = "option-type"; { _type = "option-type";
inherit name check merge getSubOptions; inherit name check merge getSubOptions getSubModules substSubModules;
}; };
@ -110,6 +115,8 @@ rec {
elemType.merge (loc ++ ["[${toString n}-${toString m}]"]) elemType.merge (loc ++ ["[${toString n}-${toString m}]"])
[{ inherit (def) file; value = def'; }]) def.value) defs); [{ inherit (def) file; value = def'; }]) def.value) defs);
getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["*"]); getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["*"]);
getSubModules = elemType.getSubModules;
substSubModules = m: listOf (elemType.substSubModules m);
}; };
attrsOf = elemType: mkOptionType { attrsOf = elemType: mkOptionType {
@ -121,6 +128,8 @@ rec {
(map (def: listToAttrs (mapAttrsToList (n: def': (map (def: listToAttrs (mapAttrsToList (n: def':
{ name = n; value = { inherit (def) file; value = def'; }; }) def.value)) defs); { name = n; value = { inherit (def) file; value = def'; }; }) def.value)) defs);
getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name>"]); getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name>"]);
getSubModules = elemType.getSubModules;
substSubModules = m: attrsOf (elemType.substSubModules m);
}; };
# List or attribute set of ... # List or attribute set of ...
@ -147,12 +156,16 @@ rec {
else false; else false;
merge = loc: defs: attrOnly.merge loc (imap convertIfList defs); merge = loc: defs: attrOnly.merge loc (imap convertIfList defs);
getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name?>"]); getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name?>"]);
getSubModules = elemType.getSubModules;
substSubModules = m: loaOf (elemType.substSubModules m);
}; };
uniq = elemType: mkOptionType { uniq = elemType: mkOptionType {
inherit (elemType) name check; inherit (elemType) name check;
merge = mergeOneOption; merge = mergeOneOption;
getSubOptions = elemType.getSubOptions; getSubOptions = elemType.getSubOptions;
getSubModules = elemType.getSubModules;
substSubModules = m: uniq (elemType.substSubModules m);
}; };
nullOr = elemType: mkOptionType { nullOr = elemType: mkOptionType {
@ -165,6 +178,8 @@ rec {
throw "The option `${showOption loc}' is defined both null and not null, in ${showFiles (getFiles defs)}." throw "The option `${showOption loc}' is defined both null and not null, in ${showFiles (getFiles defs)}."
else elemType.merge loc defs; else elemType.merge loc defs;
getSubOptions = elemType.getSubOptions; getSubOptions = elemType.getSubOptions;
getSubModules = elemType.getSubModules;
substSubModules = m: nullOr (elemType.substSubModules m);
}; };
functionTo = elemType: mkOptionType { functionTo = elemType: mkOptionType {
@ -173,6 +188,8 @@ rec {
merge = loc: defs: merge = loc: defs:
fnArgs: elemType.merge loc (map (fn: { inherit (fn) file; value = fn.value fnArgs; }) defs); fnArgs: elemType.merge loc (map (fn: { inherit (fn) file; value = fn.value fnArgs; }) defs);
getSubOptions = elemType.getSubOptions; getSubOptions = elemType.getSubOptions;
getSubModules = elemType.getSubModules;
substSubModules = m: functionTo (elemType.substSubModules m);
}; };
submodule = opts: submodule = opts:
@ -192,6 +209,8 @@ rec {
{ modules = opts'; inherit prefix; { modules = opts'; inherit prefix;
# FIXME: hack to get shit to evaluate. # FIXME: hack to get shit to evaluate.
args = { name = ""; }; }).options; args = { name = ""; }; }).options;
getSubModules = opts';
substSubModules = m: submodule m;
}; };
enum = values: mkOptionType { enum = values: mkOptionType {