1# Nixpkgs/NixOS option handling.
2
3let lib = import ./default.nix; in
4
5with import ./trivial.nix;
6with import ./lists.nix;
7with import ./attrsets.nix;
8with import ./strings.nix;
9
10rec {
11
12 isOption = lib.isType "option";
13 mkOption =
14 { default ? null # Default value used when no definition is given in the configuration.
15 , defaultText ? null # Textual representation of the default, for in the manual.
16 , example ? null # Example value used in the manual.
17 , description ? null # String describing the option.
18 , type ? null # Option type, providing type-checking and value merging.
19 , apply ? null # Function that converts the option value to something else.
20 , internal ? null # Whether the option is for NixOS developers only.
21 , visible ? null # Whether the option shows up in the manual.
22 , readOnly ? null # Whether the option can be set only once
23 , options ? null # Obsolete, used by types.optionSet.
24 } @ attrs:
25 attrs // { _type = "option"; };
26
27 mkEnableOption = name: mkOption {
28 default = false;
29 example = true;
30 description = "Whether to enable ${name}.";
31 type = lib.types.bool;
32 };
33
34 # This option accept anything, but it does not produce any result. This
35 # is useful for sharing a module across different module sets without
36 # having to implement similar features as long as the value of the options
37 # are not expected.
38 mkSinkUndeclaredOptions = attrs: mkOption ({
39 internal = true;
40 visible = false;
41 default = false;
42 description = "Sink for option definitions.";
43 type = mkOptionType {
44 name = "sink";
45 check = x: true;
46 merge = loc: defs: false;
47 };
48 apply = x: throw "Option value is not readable because the option is not declared.";
49 } // attrs);
50
51 mergeDefaultOption = loc: defs:
52 let list = getValues defs; in
53 if length list == 1 then head list
54 else if all isFunction list then x: mergeDefaultOption loc (map (f: f x) list)
55 else if all isList list then concatLists list
56 else if all isAttrs list then foldl' lib.mergeAttrs {} list
57 else if all isBool list then foldl' lib.or false list
58 else if all isString list then lib.concatStrings list
59 else if all isInt list && all (x: x == head list) list then head list
60 else throw "Cannot merge definitions of `${showOption loc}' given in ${showFiles (getFiles defs)}.";
61
62 mergeOneOption = loc: defs:
63 if defs == [] then abort "This case should never happen."
64 else if length defs != 1 then
65 throw "The unique option `${showOption loc}' is defined multiple times, in ${showFiles (getFiles defs)}."
66 else (head defs).value;
67
68 /* "Merge" option definitions by checking that they all have the same value. */
69 mergeEqualOption = loc: defs:
70 if defs == [] then abort "This case should never happen."
71 else foldl' (val: def:
72 if def.value != val then
73 throw "The option `${showOption loc}' has conflicting definitions, in ${showFiles (getFiles defs)}."
74 else
75 val) (head defs).value defs;
76
77 getValues = map (x: x.value);
78 getFiles = map (x: x.file);
79
80
81 # Generate documentation template from the list of option declaration like
82 # the set generated with filterOptionSets.
83 optionAttrSetToDocList = optionAttrSetToDocList' [];
84
85 optionAttrSetToDocList' = prefix: options:
86 concatMap (opt:
87 let
88 docOption = rec {
89 name = showOption opt.loc;
90 description = opt.description or (throw "Option `${name}' has no description.");
91 declarations = filter (x: x != unknownModule) opt.declarations;
92 internal = opt.internal or false;
93 visible = opt.visible or true;
94 readOnly = opt.readOnly or false;
95 type = opt.type.name or null;
96 }
97 // (if opt ? example then { example = scrubOptionValue opt.example; } else {})
98 // (if opt ? default then { default = scrubOptionValue opt.default; } else {})
99 // (if opt ? defaultText then { default = opt.defaultText; } else {});
100
101 subOptions =
102 let ss = opt.type.getSubOptions opt.loc;
103 in if ss != {} then optionAttrSetToDocList' opt.loc ss else [];
104 in
105 [ docOption ] ++ subOptions) (collect isOption options);
106
107
108 /* This function recursively removes all derivation attributes from
109 `x' except for the `name' attribute. This is to make the
110 generation of `options.xml' much more efficient: the XML
111 representation of derivations is very large (on the order of
112 megabytes) and is not actually used by the manual generator. */
113 scrubOptionValue = x:
114 if isDerivation x then
115 { type = "derivation"; drvPath = x.name; outPath = x.name; name = x.name; }
116 else if isList x then map scrubOptionValue x
117 else if isAttrs x then mapAttrs (n: v: scrubOptionValue v) (removeAttrs x ["_args"])
118 else x;
119
120
121 /* For use in the ‘example’ option attribute. It causes the given
122 text to be included verbatim in documentation. This is necessary
123 for example values that are not simple values, e.g.,
124 functions. */
125 literalExample = text: { _type = "literalExample"; inherit text; };
126
127
128 /* Helper functions. */
129 showOption = concatStringsSep ".";
130 showFiles = files: concatStringsSep " and " (map (f: "`${f}'") files);
131 unknownModule = "<unknown-file>";
132
133}