From bb944b4dc80e7b2a22ac8e76355e7bf80aec6636 Mon Sep 17 00:00:00 2001 From: Nicolas Pierron Date: Fri, 29 Aug 2014 14:38:19 +0200 Subject: [PATCH 1/2] Annotate option-set options with the file in which they are declared. This modification improves NixOS manual by listing in which file, each submodule option is declared. This solve the issue that files are not reported when looking at options such as fileSystems..neededForBoot --- lib/modules.nix | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/lib/modules.nix b/lib/modules.nix index 9fe26083cfd4..31134c66276b 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -132,7 +132,12 @@ rec { The exception is the ‘options’ attribute, which specifies sub-options. These can be specified multiple times to allow one 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: fold (opt: res: if opt.options ? default && res ? default || @@ -143,9 +148,23 @@ rec { then throw "The option `${showOption loc}' in `${opt.file}' is already declared in ${showFiles res.declarations}." 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; + coerceOption = file: opt: + if isFunction opt then args: { _file = file; } // (opt args) + else args: { _file = file; options = opt; }; + submodules = + if opt.options ? options + then map (coerceOption opt.file) options' ++ res.options + else res.options; + in opt.options // res // { 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; @@ -276,12 +295,8 @@ rec { optionSet to configOf. FIXME: remove eventually. */ fixupOptionType = loc: opt: 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}."); - coerce = x: - if isFunction x then x - else { config, ... }: { options = x; }; - options = map coerce (flatten options'); f = tp: if tp.name == "option set" || tp.name == "submodule" then throw "The option ${showOption loc} uses submodules without a wrapping type, in ${showFiles opt.declarations}." From b5f0cc3cdaa70673a6229f01dc1104a272120b59 Mon Sep 17 00:00:00 2001 From: Nicolas Pierron Date: Fri, 29 Aug 2014 16:42:44 +0200 Subject: [PATCH 2/2] Merge options having the submodule type. Now we should be able to have multiple declaration of the same option as long as all declarations have the same type. If the type has a sub module, then it is merged with the submodules of other declarations, as done with option sets. In addition, the file of the option declaration is passed into the submodule, such as the documentation can display it correctly. --- lib/modules.nix | 21 +++++++++++++++------ lib/types.nix | 21 ++++++++++++++++++++- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/lib/modules.nix b/lib/modules.nix index 31134c66276b..1d428311cd19 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -144,7 +144,8 @@ rec { opt.options ? example && res ? example || opt.options ? description && res ? description || 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 throw "The option `${showOption loc}' in `${opt.file}' is already declared in ${showFiles res.declarations}." else @@ -155,12 +156,16 @@ rec { 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 args: { _file = file; options = opt; }; + else { _file = file; options = opt; }; + getSubModules = opt.options.type.getSubModules or null; submodules = - if opt.options ? options - then map (coerceOption opt.file) options' ++ res.options + 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; @@ -292,7 +297,8 @@ rec { in sort compare defs'; /* 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: let options = opt.options or @@ -305,7 +311,10 @@ rec { 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 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. */ diff --git a/lib/types.nix b/lib/types.nix index 0e2b6515e165..783e07cdc721 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -33,9 +33,14 @@ rec { , # Return a flat list of sub-options. Used to generate # documentation. 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"; - inherit name check merge getSubOptions; + inherit name check merge getSubOptions getSubModules substSubModules; }; @@ -110,6 +115,8 @@ rec { elemType.merge (loc ++ ["[${toString n}-${toString m}]"]) [{ inherit (def) file; value = def'; }]) def.value) defs); getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["*"]); + getSubModules = elemType.getSubModules; + substSubModules = m: listOf (elemType.substSubModules m); }; attrsOf = elemType: mkOptionType { @@ -121,6 +128,8 @@ rec { (map (def: listToAttrs (mapAttrsToList (n: def': { name = n; value = { inherit (def) file; value = def'; }; }) def.value)) defs); getSubOptions = prefix: elemType.getSubOptions (prefix ++ [""]); + getSubModules = elemType.getSubModules; + substSubModules = m: attrsOf (elemType.substSubModules m); }; # List or attribute set of ... @@ -147,12 +156,16 @@ rec { else false; merge = loc: defs: attrOnly.merge loc (imap convertIfList defs); getSubOptions = prefix: elemType.getSubOptions (prefix ++ [""]); + getSubModules = elemType.getSubModules; + substSubModules = m: loaOf (elemType.substSubModules m); }; uniq = elemType: mkOptionType { inherit (elemType) name check; merge = mergeOneOption; getSubOptions = elemType.getSubOptions; + getSubModules = elemType.getSubModules; + substSubModules = m: uniq (elemType.substSubModules m); }; nullOr = elemType: mkOptionType { @@ -165,6 +178,8 @@ rec { throw "The option `${showOption loc}' is defined both null and not null, in ${showFiles (getFiles defs)}." else elemType.merge loc defs; getSubOptions = elemType.getSubOptions; + getSubModules = elemType.getSubModules; + substSubModules = m: nullOr (elemType.substSubModules m); }; functionTo = elemType: mkOptionType { @@ -173,6 +188,8 @@ rec { merge = loc: defs: fnArgs: elemType.merge loc (map (fn: { inherit (fn) file; value = fn.value fnArgs; }) defs); getSubOptions = elemType.getSubOptions; + getSubModules = elemType.getSubModules; + substSubModules = m: functionTo (elemType.substSubModules m); }; submodule = opts: @@ -192,6 +209,8 @@ rec { { modules = opts'; inherit prefix; # FIXME: hack to get shit to evaluate. args = { name = ""; }; }).options; + getSubModules = opts'; + substSubModules = m: submodule m; }; enum = values: mkOptionType {