1 # Nixpkgs/NixOS option handling.
11 /* Returns true when the given argument is an option
13 Type: isOption :: a -> bool
16 isOption 1 // => false
17 isOption (mkOption {}) // => true
19 isOption = lib.isType "option";
21 /* Creates an Option attribute set. mkOption accepts an attribute set with the following keys:
23 All keys default to `null` when not given.
26 mkOption { } // => { _type = "option"; }
27 mkOption { defaultText = "foo"; } // => { _type = "option"; defaultText = "foo"; }
31 # Default value used when no definition is given in the configuration.
33 # Textual representation of the default, for the manual.
35 # Example value used in the manual.
37 # String describing the option.
39 # Related packages used in the manual (see `genRelatedPackages` in ../nixos/lib/make-options-doc/default.nix).
40 relatedPackages ? null,
41 # Option type, providing type-checking and value merging.
43 # Function that converts the option value to something else.
45 # Whether the option is for NixOS developers only.
47 # Whether the option shows up in the manual.
49 # Whether the option can be set only once
51 # Deprecated, used by types.optionSet.
54 attrs // { _type = "option"; };
56 /* Creates an Option attribute set for a boolean value option i.e an
57 option to be toggled on or off:
61 => { _type = "option"; default = false; description = "Whether to enable foo."; example = true; type = { ... }; }
64 # Name for the created option
68 description = "Whether to enable ${name}.";
69 type = lib.types.bool;
72 /* This option accepts anything, but it does not produce any result.
74 This is useful for sharing a module across different module sets
75 without having to implement similar features as long as the
76 values of the options are not accessed. */
77 mkSinkUndeclaredOptions = attrs: mkOption ({
81 description = "Sink for option definitions.";
85 merge = loc: defs: false;
87 apply = x: throw "Option value is not readable because the option is not declared.";
90 mergeDefaultOption = loc: defs:
91 let list = getValues defs; in
92 if length list == 1 then head list
93 else if all isFunction list then x: mergeDefaultOption loc (map (f: f x) list)
94 else if all isList list then concatLists list
95 else if all isAttrs list then foldl' lib.mergeAttrs {} list
96 else if all isBool list then foldl' lib.or false list
97 else if all isString list then lib.concatStrings list
98 else if all isInt list && all (x: x == head list) list then head list
99 else throw "Cannot merge definitions of `${showOption loc}' given in ${showFiles (getFiles defs)}.";
101 mergeOneOption = loc: defs:
102 if defs == [] then abort "This case should never happen."
103 else if length defs != 1 then
104 throw "The unique option `${showOption loc}' is defined multiple times, in:\n - ${concatStringsSep "\n - " (getFiles defs)}."
105 else (head defs).value;
107 /* "Merge" option definitions by checking that they all have the same value. */
108 mergeEqualOption = loc: defs:
109 if defs == [] then abort "This case should never happen."
110 else foldl' (val: def:
111 if def.value != val then
112 throw "The option `${showOption loc}' has conflicting definitions, in ${showFiles (getFiles defs)}."
114 val) (head defs).value defs;
116 /* Extracts values of all "value" keys of the given list.
118 Type: getValues :: [ { value :: a } ] -> [a]
121 getValues [ { value = 1; } { value = 2; } ] // => [ 1 2 ]
122 getValues [ ] // => [ ]
124 getValues = map (x: x.value);
126 /* Extracts values of all "file" keys of the given list
128 Type: getFiles :: [ { file :: a } ] -> [a]
131 getFiles [ { file = "file1"; } { file = "file2"; } ] // => [ "file1" "file2" ]
132 getFiles [ ] // => [ ]
134 getFiles = map (x: x.file);
136 # Generate documentation template from the list of option declaration like
137 # the set generated with filterOptionSets.
138 optionAttrSetToDocList = optionAttrSetToDocList' [];
140 optionAttrSetToDocList' = prefix: options:
145 name = showOption opt.loc;
146 description = opt.description or (lib.warn "Option `${name}' has no description." "This option has no description.");
147 declarations = filter (x: x != unknownModule) opt.declarations;
148 internal = opt.internal or false;
149 visible = opt.visible or true;
150 readOnly = opt.readOnly or false;
151 type = opt.type.description or null;
153 // optionalAttrs (opt ? example) { example = scrubOptionValue opt.example; }
154 // optionalAttrs (opt ? default) { default = scrubOptionValue opt.default; }
155 // optionalAttrs (opt ? defaultText) { default = opt.defaultText; }
156 // optionalAttrs (opt ? relatedPackages && opt.relatedPackages != null) { inherit (opt) relatedPackages; };
159 let ss = opt.type.getSubOptions opt.loc;
160 in if ss != {} then optionAttrSetToDocList' opt.loc ss else [];
162 [ docOption ] ++ optionals docOption.visible subOptions) (collect isOption options);
165 /* This function recursively removes all derivation attributes from
166 `x` except for the `name` attribute.
168 This is to make the generation of `options.xml` much more
169 efficient: the XML representation of derivations is very large
170 (on the order of megabytes) and is not actually used by the
173 scrubOptionValue = x:
174 if isDerivation x then
175 { type = "derivation"; drvPath = x.name; outPath = x.name; name = x.name; }
176 else if isList x then map scrubOptionValue x
177 else if isAttrs x then mapAttrs (n: v: scrubOptionValue v) (removeAttrs x ["_args"])
181 /* For use in the `example` option attribute. It causes the given
182 text to be included verbatim in documentation. This is necessary
183 for example values that are not simple values, e.g., functions.
185 literalExample = text: { _type = "literalExample"; inherit text; };
189 /* Convert an option, described as a list of the option parts in to a
190 safe, human readable version.
193 (showOption ["foo" "bar" "baz"]) == "foo.bar.baz"
194 (showOption ["foo" "bar.baz" "tux"]) == "foo.bar.baz.tux"
196 Placeholders will not be quoted as they are not actual values:
197 (showOption ["foo" "*" "bar"]) == "foo.*.bar"
198 (showOption ["foo" "<name>" "bar"]) == "foo.<name>.bar"
200 Unlike attributes, options can also start with numbers:
201 (showOption ["windowManager" "2bwm" "enable"]) == "windowManager.2bwm.enable"
203 showOption = parts: let
204 escapeOptionPart = part:
206 escaped = lib.strings.escapeNixString part;
207 in if escaped == "\"${part}\""
210 in (concatStringsSep ".") (map escapeOptionPart parts);
211 showFiles = files: concatStringsSep " and " (map (f: "`${f}'") files);
212 unknownModule = "<unknown-file>";