diff --git a/lib/modules.nix b/lib/modules.nix index 31134c66276..1d428311cd1 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 0e2b6515e16..783e07cdc72 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 {