1 # Nixpkgs/NixOS option handling.
27 inherit (lib.attrsets)
40 /* Returns true when the given argument is an option
42 Type: isOption :: a -> bool
45 isOption 1 // => false
46 isOption (mkOption {}) // => true
48 isOption = lib.isType "option";
50 /* Creates an Option attribute set. mkOption accepts an attribute set with the following keys:
52 All keys default to `null` when not given.
55 mkOption { } // => { _type = "option"; }
56 mkOption { defaultText = "foo"; } // => { _type = "option"; defaultText = "foo"; }
60 # Default value used when no definition is given in the configuration.
62 # Textual representation of the default, for the manual.
64 # Example value used in the manual.
66 # String describing the option.
68 # Related packages used in the manual (see `genRelatedPackages` in ../nixos/lib/make-options-doc/default.nix).
69 relatedPackages ? null,
70 # Option type, providing type-checking and value merging.
72 # Function that converts the option value to something else.
74 # Whether the option is for NixOS developers only.
76 # Whether the option shows up in the manual.
78 # Whether the option can be set only once
80 # Deprecated, used by types.optionSet.
83 attrs // { _type = "option"; };
85 /* Creates an Option attribute set for a boolean value option i.e an
86 option to be toggled on or off:
90 => { _type = "option"; default = false; description = "Whether to enable foo."; example = true; type = { ... }; }
93 # Name for the created option
97 description = "Whether to enable ${name}.";
98 type = lib.types.bool;
101 /* This option accepts anything, but it does not produce any result.
103 This is useful for sharing a module across different module sets
104 without having to implement similar features as long as the
105 values of the options are not accessed. */
106 mkSinkUndeclaredOptions = attrs: mkOption ({
110 description = "Sink for option definitions.";
111 type = mkOptionType {
114 merge = loc: defs: false;
116 apply = x: throw "Option value is not readable because the option is not declared.";
119 mergeDefaultOption = loc: defs:
120 let list = getValues defs; in
121 if length list == 1 then head list
122 else if all isFunction list then x: mergeDefaultOption loc (map (f: f x) list)
123 else if all isList list then concatLists list
124 else if all isAttrs list then foldl' lib.mergeAttrs {} list
125 else if all isBool list then foldl' lib.or false list
126 else if all isString list then lib.concatStrings list
127 else if all isInt list && all (x: x == head list) list then head list
128 else throw "Cannot merge definitions of `${showOption loc}'. Definition values:${showDefs defs}";
130 mergeOneOption = loc: defs:
131 if defs == [] then abort "This case should never happen."
132 else if length defs != 1 then
133 throw "The unique option `${showOption loc}' is defined multiple times. Definition values:${showDefs defs}"
134 else (head defs).value;
136 /* "Merge" option definitions by checking that they all have the same value. */
137 mergeEqualOption = loc: defs:
138 if defs == [] then abort "This case should never happen."
139 # Return early if we only have one element
140 # This also makes it work for functions, because the foldl' below would try
141 # to compare the first element with itself, which is false for functions
142 else if length defs == 1 then (head defs).value
143 else (foldl' (first: def:
144 if def.value != first.value then
145 throw "The option `${showOption loc}' has conflicting definition values:${showDefs [ first def ]}"
147 first) (head defs) defs).value;
149 /* Extracts values of all "value" keys of the given list.
151 Type: getValues :: [ { value :: a } ] -> [a]
154 getValues [ { value = 1; } { value = 2; } ] // => [ 1 2 ]
155 getValues [ ] // => [ ]
157 getValues = map (x: x.value);
159 /* Extracts values of all "file" keys of the given list
161 Type: getFiles :: [ { file :: a } ] -> [a]
164 getFiles [ { file = "file1"; } { file = "file2"; } ] // => [ "file1" "file2" ]
165 getFiles [ ] // => [ ]
167 getFiles = map (x: x.file);
169 # Generate documentation template from the list of option declaration like
170 # the set generated with filterOptionSets.
171 optionAttrSetToDocList = optionAttrSetToDocList' [];
173 optionAttrSetToDocList' = prefix: options:
178 name = showOption opt.loc;
179 description = opt.description or (lib.warn "Option `${name}' has no description." "This option has no description.");
180 declarations = filter (x: x != unknownModule) opt.declarations;
181 internal = opt.internal or false;
182 visible = opt.visible or true;
183 readOnly = opt.readOnly or false;
184 type = opt.type.description or null;
186 // optionalAttrs (opt ? example) { example = scrubOptionValue opt.example; }
187 // optionalAttrs (opt ? default) { default = scrubOptionValue opt.default; }
188 // optionalAttrs (opt ? defaultText) { default = opt.defaultText; }
189 // optionalAttrs (opt ? relatedPackages && opt.relatedPackages != null) { inherit (opt) relatedPackages; };
192 let ss = opt.type.getSubOptions opt.loc;
193 in if ss != {} then optionAttrSetToDocList' opt.loc ss else [];
195 [ docOption ] ++ optionals docOption.visible subOptions) (collect isOption options);
198 /* This function recursively removes all derivation attributes from
199 `x` except for the `name` attribute.
201 This is to make the generation of `options.xml` much more
202 efficient: the XML representation of derivations is very large
203 (on the order of megabytes) and is not actually used by the
206 scrubOptionValue = x:
207 if isDerivation x then
208 { type = "derivation"; drvPath = x.name; outPath = x.name; name = x.name; }
209 else if isList x then map scrubOptionValue x
210 else if isAttrs x then mapAttrs (n: v: scrubOptionValue v) (removeAttrs x ["_args"])
214 /* For use in the `example` option attribute. It causes the given
215 text to be included verbatim in documentation. This is necessary
216 for example values that are not simple values, e.g., functions.
218 literalExample = text: { _type = "literalExample"; inherit text; };
222 /* Convert an option, described as a list of the option parts in to a
223 safe, human readable version.
226 (showOption ["foo" "bar" "baz"]) == "foo.bar.baz"
227 (showOption ["foo" "bar.baz" "tux"]) == "foo.bar.baz.tux"
229 Placeholders will not be quoted as they are not actual values:
230 (showOption ["foo" "*" "bar"]) == "foo.*.bar"
231 (showOption ["foo" "<name>" "bar"]) == "foo.<name>.bar"
233 Unlike attributes, options can also start with numbers:
234 (showOption ["windowManager" "2bwm" "enable"]) == "windowManager.2bwm.enable"
236 showOption = parts: let
237 escapeOptionPart = part:
239 escaped = lib.strings.escapeNixString part;
240 in if escaped == "\"${part}\""
243 in (concatStringsSep ".") (map escapeOptionPart parts);
244 showFiles = files: concatStringsSep " and " (map (f: "`${f}'") files);
246 showDefs = defs: concatMapStrings (def:
248 # Pretty print the value for display, if successful
249 prettyEval = builtins.tryEval (lib.generators.toPretty {} def.value);
250 # Split it into its lines
251 lines = filter (v: ! isList v) (builtins.split "\n" prettyEval.value);
252 # Only display the first 5 lines, and indent them for better visibility
253 value = concatStringsSep "\n " (take 5 lines ++ optional (length lines > 5) "...");
255 # Don't print any value if evaluating the value strictly fails
256 if ! prettyEval.success then ""
257 # Put it on a new line if it consists of multiple
258 else if length lines > 1 then ":\n " + value
260 in "\n- In `${def.file}'${result}"
263 unknownModule = "<unknown-file>";