From 89bd18b3affc6612ad6402c05cc4d80a59efe9b8 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 28 Oct 2013 14:25:58 +0100 Subject: [PATCH] Fix manual generation --- lib/modules.nix | 35 +++++++------- lib/options.nix | 46 +++++++++--------- lib/types.nix | 49 ++++++++++++-------- nixos/modules/services/misc/nixos-manual.nix | 2 +- 4 files changed, 70 insertions(+), 62 deletions(-) diff --git a/lib/modules.nix b/lib/modules.nix index afbfda378f8..f429653d94e 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -6,14 +6,16 @@ rec { /* Evaluate a set of modules. The result is a set of two attributes: ‘options’: the nested set of all option declarations, and ‘config’: the nested set of all option values. */ - evalModules = modules: args: + evalModules = evalModules' []; + + evalModules' = prefix: modules: args: let args' = args // result; closed = closeModules modules args'; # Note: the list of modules is reversed to maintain backward # compatibility with the old module system. Not sure if this is # the most sensible policy. - options = mergeModules (reverseList closed); + options = mergeModules prefix (reverseList closed); config = yieldConfig options; yieldConfig = mapAttrs (n: v: if isOption v then v.value else yieldConfig v); result = { inherit options config; }; @@ -22,16 +24,15 @@ rec { /* Close a set of modules under the ‘imports’ relation. */ closeModules = modules: args: let - coerceToModule = n: x: + toClosureList = parent: imap (n: x: if isAttrs x || builtins.isFunction x then - unifyModuleSyntax "" "anon-${toString n}" (applyIfFunction x args) + unifyModuleSyntax parent "anon-${toString n}" (applyIfFunction x args) else - unifyModuleSyntax (toString x) (toString x) (applyIfFunction (import x) args); - toClosureList = imap (path: coerceToModule path); + unifyModuleSyntax (toString x) (toString x) (applyIfFunction (import x) args)); in builtins.genericClosure { - startSet = toClosureList modules; - operator = m: toClosureList m.imports; + startSet = toClosureList unknownModule modules; + operator = m: toClosureList m.file m.imports; }; /* Massage a module into canonical form, that is, a set consisting @@ -61,18 +62,18 @@ rec { At the same time, for each option declaration, it will merge the corresponding option definitions in all machines, returning them in the ‘value’ attribute of each option. */ - mergeModules = modules: - mergeModules' [] modules + mergeModules = prefix: modules: + mergeModules' prefix modules (concatMap (m: map (config: { inherit (m) file; inherit config; }) (pushDownProperties m.config)) modules); - mergeModules' = loc: options: configs: + mergeModules' = prefix: options: configs: 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]; + loc = prefix ++ [name]; # Get all submodules that declare ‘name’. decls = concatLists (map (m: if hasAttr name m.options @@ -95,16 +96,16 @@ rec { ) configs; in if nrOptions == length decls then - let opt = fixupOptionType loc' (mergeOptionDecls loc' decls); - in evalOptionValue loc' opt defns' + let opt = fixupOptionType loc (mergeOptionDecls loc decls); + in evalOptionValue loc opt defns' else if nrOptions != 0 then let firstOption = findFirst (m: isOption m.options) "" decls; firstNonOption = findFirst (m: !isOption m.options) "" decls; in - throw "The option `${showOption loc'}' in `${firstOption.file}' is a prefix of options in `${firstNonOption.file}'." + throw "The option `${showOption loc}' in `${firstOption.file}' is a prefix of options in `${firstNonOption.file}'." else - mergeModules' loc' decls defns; + mergeModules' loc decls defns; }) names); /* Merge multiple option declarations into a single declaration. In @@ -128,7 +129,7 @@ rec { { declarations = [opt.file] ++ res.declarations; options = if opt.options ? options then [(toList opt.options.options ++ res.options)] else []; } - ) { declarations = []; options = []; } opts; + ) { inherit loc; declarations = []; options = []; } opts; /* Merge all the definitions of an option to produce the final config value. */ diff --git a/lib/options.nix b/lib/options.nix index d94a9fad388..480837fd1cf 100644 --- a/lib/options.nix +++ b/lib/options.nix @@ -87,31 +87,28 @@ rec { # Generate documentation template from the list of option declaration like # the set generated with filterOptionSets. - optionAttrSetToDocList = attrs: - let options = collect isOption attrs; in - fold (opt: rest: - let - docOption = { - inherit (opt) name; - description = opt.description or (throw "Option ${opt.name}: No description."); - declarations = map (x: toString x.source) opt.declarations; - #definitions = map (x: toString x.source) opt.definitions; - internal = opt.internal or false; - visible = opt.visible or true; - } - // optionalAttrs (opt ? example) { example = scrubOptionValue opt.example; } - // optionalAttrs (opt ? default) { default = scrubOptionValue opt.default; } - // optionalAttrs (opt ? defaultText) { default = opt.defaultText; }; + optionAttrSetToDocList = optionAttrSetToDocList' []; - subOptions = - if opt ? options then - optionAttrSetToDocList opt.options - else - []; - in - # FIXME: expensive (O(n^2) - [ docOption ] ++ subOptions ++ rest - ) [] options; + optionAttrSetToDocList' = prefix: options: + fold (opt: rest: + let + docOption = rec { + name = showOption opt.loc; + description = opt.description or (throw "Option `${name}' has no description."); + declarations = filter (x: x != unknownModule) opt.declarations; + internal = opt.internal or false; + visible = opt.visible or true; + } + // optionalAttrs (opt ? example) { example = scrubOptionValue opt.example; } + // optionalAttrs (opt ? default) { default = scrubOptionValue opt.default; } + // optionalAttrs (opt ? defaultText) { default = opt.defaultText; }; + + subOptions = + let ss = opt.type.getSubOptions opt.loc; + in if ss != {} then optionAttrSetToDocList' opt.loc ss else []; + in + # FIXME: expensive, O(n^2) + [ docOption ] ++ subOptions ++ rest) [] (collect isOption options); /* This function recursively removes all derivation attributes from @@ -135,5 +132,6 @@ rec { /* Helper functions. */ showOption = concatStringsSep "."; + unknownModule = ""; } diff --git a/lib/types.nix b/lib/types.nix index 7cfd8e606d7..dec9d82d608 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -22,18 +22,18 @@ rec { # name (name of the type) # check (check the config value) # merge (default merge function) - # docPath (path concatenated to the option name contained in the option set) + # getSubOptions (returns sub-options for manual generation) isOptionType = isType "option-type"; mkOptionType = { name , check ? (x: true) , merge ? mergeDefaultOption , merge' ? args: merge - , docPath ? lib.id + , getSubOptions ? prefix: {} }: { _type = "option-type"; - inherit name check merge merge' docPath; + inherit name check merge merge' getSubOptions; }; @@ -99,14 +99,14 @@ rec { name = "list of ${elemType.name}s"; check = value: isList value && all elemType.check value; merge = defs: map (def: elemType.merge [def]) (concatLists defs); - docPath = path: elemType.docPath (path + ".*"); + getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["*"]); }; attrsOf = elemType: mkOptionType { name = "attribute set of ${elemType.name}s"; check = x: isAttrs x && all elemType.check (lib.attrValues x); merge = lib.zipAttrsWith (name: elemType.merge' { inherit name; }); - docPath = path: elemType.docPath (path + "."); + getSubOptions = prefix: elemType.getSubOptions (prefix ++ [""]); }; # List or attribute set of ... @@ -129,26 +129,27 @@ rec { else if isAttrs x then attrOnly.check x else false; merge = defs: attrOnly.merge (imap convertIfList defs); - docPath = path: elemType.docPath (path + "."); + getSubOptions = prefix: elemType.getSubOptions (prefix ++ [""]); }; uniq = elemType: mkOptionType { - inherit (elemType) name check docPath; + inherit (elemType) name check; merge = list: if length list == 1 then head list else throw "Multiple definitions of ${elemType.name}. Only one is allowed for this option."; + getSubOptions = elemType.getSubOptions; }; none = elemType: mkOptionType { - inherit (elemType) name check docPath; + inherit (elemType) name check; merge = list: throw "No definitions are allowed for this option."; + getSubOptions = elemType.getSubOptions; }; nullOr = elemType: mkOptionType { - inherit (elemType) docPath; name = "null or ${elemType.name}"; check = x: builtins.isNull x || elemType.check x; merge = defs: @@ -156,6 +157,7 @@ rec { else if any isNull defs then throw "Some but not all values are null." else elemType.merge defs; + getSubOptions = elemType.getSubOptions; }; functionTo = elemType: mkOptionType { @@ -163,19 +165,26 @@ rec { check = builtins.isFunction; merge = fns: args: elemType.merge (map (fn: fn args) fns); + getSubOptions = elemType.getSubOptions; }; - submodule = opts: mkOptionType rec { - name = "submodule"; - check = x: isAttrs x || builtins.isFunction x; - # FIXME: make error messages include the parent attrpath. - merge = merge' {}; - merge' = args: defs: - let - coerce = def: if builtins.isFunction def then def else { config = def; }; - modules = (toList opts) ++ map coerce defs; - in (evalModules modules args).config; - }; + submodule = opts: + let opts' = toList opts; in + mkOptionType rec { + name = "submodule"; + check = x: isAttrs x || builtins.isFunction x; + # FIXME: make error messages include the parent attrpath. + merge = merge' {}; + merge' = args: defs: + let + coerce = def: if builtins.isFunction def then def else { config = def; }; + modules = opts' ++ map coerce defs; + in (evalModules modules args).config; + getSubOptions = prefix: (evalModules' prefix opts' + # FIXME: hack to get shit to evaluate. + { name = ""; } + ).options; + }; # Obsolete alternative to configOf. It takes its option # declarations from the ‘options’ attribute of containing option diff --git a/nixos/modules/services/misc/nixos-manual.nix b/nixos/modules/services/misc/nixos-manual.nix index a593d05ee62..66d46d9114a 100644 --- a/nixos/modules/services/misc/nixos-manual.nix +++ b/nixos/modules/services/misc/nixos-manual.nix @@ -19,7 +19,7 @@ let manual = import ../../../doc/manual { inherit pkgs; revision = config.system.nixosRevision; - options = (fixMergeModules ([ versionModule ] ++ baseModules) + options = (evalModules ([ versionModule ] ++ baseModules) (removeAttrs extraArgs ["config" "options"]) // { modules = [ ]; }).options;