telegraf: 1.27.0 -> 1.27.1
[NixPkgs.git] / lib / options.nix
blobc42bc1e6c67e3b8639f43f37c64a19bcf6b83a34
1 # Nixpkgs/NixOS option handling.
2 { lib }:
4 let
5   inherit (lib)
6     all
7     collect
8     concatLists
9     concatMap
10     concatMapStringsSep
11     filter
12     foldl'
13     head
14     tail
15     isAttrs
16     isBool
17     isDerivation
18     isFunction
19     isInt
20     isList
21     isString
22     length
23     mapAttrs
24     optional
25     optionals
26     take
27     ;
28   inherit (lib.attrsets)
29     attrByPath
30     optionalAttrs
31     ;
32   inherit (lib.strings)
33     concatMapStrings
34     concatStringsSep
35     ;
36   inherit (lib.types)
37     mkOptionType
38     ;
39   inherit (lib.lists)
40     last
41     ;
42   prioritySuggestion = ''
43    Use `lib.mkForce value` or `lib.mkDefault value` to change the priority on any of these definitions.
44   '';
46 rec {
48   /* Returns true when the given argument is an option
50      Type: isOption :: a -> bool
52      Example:
53        isOption 1             // => false
54        isOption (mkOption {}) // => true
55   */
56   isOption = lib.isType "option";
58   /* Creates an Option attribute set. mkOption accepts an attribute set with the following keys:
60      All keys default to `null` when not given.
62      Example:
63        mkOption { }  // => { _type = "option"; }
64        mkOption { default = "foo"; } // => { _type = "option"; default = "foo"; }
65   */
66   mkOption =
67     {
68     # Default value used when no definition is given in the configuration.
69     default ? null,
70     # Textual representation of the default, for the manual.
71     defaultText ? null,
72     # Example value used in the manual.
73     example ? null,
74     # String describing the option.
75     description ? null,
76     # Related packages used in the manual (see `genRelatedPackages` in ../nixos/lib/make-options-doc/default.nix).
77     relatedPackages ? null,
78     # Option type, providing type-checking and value merging.
79     type ? null,
80     # Function that converts the option value to something else.
81     apply ? null,
82     # Whether the option is for NixOS developers only.
83     internal ? null,
84     # Whether the option shows up in the manual. Default: true. Use false to hide the option and any sub-options from submodules. Use "shallow" to hide only sub-options.
85     visible ? null,
86     # Whether the option can be set only once
87     readOnly ? null,
88     } @ attrs:
89     attrs // { _type = "option"; };
91   /* Creates an Option attribute set for a boolean value option i.e an
92      option to be toggled on or off:
94      Example:
95        mkEnableOption "foo"
96        => { _type = "option"; default = false; description = "Whether to enable foo."; example = true; type = { ... }; }
97   */
98   mkEnableOption =
99     # Name for the created option
100     name: mkOption {
101     default = false;
102     example = true;
103     description = "Whether to enable ${name}.";
104     type = lib.types.bool;
105   };
107   /* Creates an Option attribute set for an option that specifies the
108      package a module should use for some purpose.
110      The package is specified in the third argument under `default` as a list of strings
111      representing its attribute path in nixpkgs (or another package set).
112      Because of this, you need to pass nixpkgs itself (or a subset) as the first argument.
114      The second argument may be either a string or a list of strings.
115      It provides the display name of the package in the description of the generated option
116      (using only the last element if the passed value is a list)
117      and serves as the fallback value for the `default` argument.
119      To include extra information in the description, pass `extraDescription` to
120      append arbitrary text to the generated description.
121      You can also pass an `example` value, either a literal string or an attribute path.
123      The default argument can be omitted if the provided name is
124      an attribute of pkgs (if name is a string) or a
125      valid attribute path in pkgs (if name is a list).
127      If you wish to explicitly provide no default, pass `null` as `default`.
129      Type: mkPackageOption :: pkgs -> (string|[string]) -> { default? :: [string], example? :: null|string|[string], extraDescription? :: string } -> option
131      Example:
132        mkPackageOption pkgs "hello" { }
133        => { _type = "option"; default = «derivation /nix/store/3r2vg51hlxj3cx5vscp0vkv60bqxkaq0-hello-2.10.drv»; defaultText = { ... }; description = "The hello package to use."; type = { ... }; }
135      Example:
136        mkPackageOption pkgs "GHC" {
137          default = [ "ghc" ];
138          example = "pkgs.haskell.packages.ghc92.ghc.withPackages (hkgs: [ hkgs.primes ])";
139        }
140        => { _type = "option"; default = «derivation /nix/store/jxx55cxsjrf8kyh3fp2ya17q99w7541r-ghc-8.10.7.drv»; defaultText = { ... }; description = "The GHC package to use."; example = { ... }; type = { ... }; }
142      Example:
143        mkPackageOption pkgs [ "python39Packages" "pytorch" ] {
144          extraDescription = "This is an example and doesn't actually do anything.";
145        }
146        => { _type = "option"; default = «derivation /nix/store/gvqgsnc4fif9whvwd9ppa568yxbkmvk8-python3.9-pytorch-1.10.2.drv»; defaultText = { ... }; description = "The pytorch package to use. This is an example and doesn't actually do anything."; type = { ... }; }
148   */
149   mkPackageOption =
150     # Package set (a specific version of nixpkgs or a subset)
151     pkgs:
152       # Name for the package, shown in option description
153       name:
154       {
155         # Whether the package can be null, for example to disable installing a package altogether.
156         nullable ? false,
157         # The attribute path where the default package is located (may be omitted)
158         default ? name,
159         # A string or an attribute path to use as an example (may be omitted)
160         example ? null,
161         # Additional text to include in the option description (may be omitted)
162         extraDescription ? "",
163       }:
164       let
165         name' = if isList name then last name else name;
166       in mkOption ({
167         type = with lib.types; (if nullable then nullOr else lib.id) package;
168         description = "The ${name'} package to use."
169           + (if extraDescription == "" then "" else " ") + extraDescription;
170       } // (if default != null then let
171         default' = if isList default then default else [ default ];
172         defaultPath = concatStringsSep "." default';
173         defaultValue = attrByPath default'
174           (throw "${defaultPath} cannot be found in pkgs") pkgs;
175       in {
176         default = defaultValue;
177         defaultText = literalExpression ("pkgs." + defaultPath);
178       } else if nullable then {
179         default = null;
180       } else { }) // lib.optionalAttrs (example != null) {
181         example = literalExpression
182           (if isList example then "pkgs." + concatStringsSep "." example else example);
183       });
185   /* Alias of mkPackageOption. Previously used to create options with markdown
186      documentation, which is no longer required.
187   */
188   mkPackageOptionMD = mkPackageOption;
190   /* This option accepts anything, but it does not produce any result.
192      This is useful for sharing a module across different module sets
193      without having to implement similar features as long as the
194      values of the options are not accessed. */
195   mkSinkUndeclaredOptions = attrs: mkOption ({
196     internal = true;
197     visible = false;
198     default = false;
199     description = "Sink for option definitions.";
200     type = mkOptionType {
201       name = "sink";
202       check = x: true;
203       merge = loc: defs: false;
204     };
205     apply = x: throw "Option value is not readable because the option is not declared.";
206   } // attrs);
208   mergeDefaultOption = loc: defs:
209     let list = getValues defs; in
210     if length list == 1 then head list
211     else if all isFunction list then x: mergeDefaultOption loc (map (f: f x) list)
212     else if all isList list then concatLists list
213     else if all isAttrs list then foldl' lib.mergeAttrs {} list
214     else if all isBool list then foldl' lib.or false list
215     else if all isString list then lib.concatStrings list
216     else if all isInt list && all (x: x == head list) list then head list
217     else throw "Cannot merge definitions of `${showOption loc}'. Definition values:${showDefs defs}";
219   mergeOneOption = mergeUniqueOption { message = ""; };
221   mergeUniqueOption = { message }: loc: defs:
222     if length defs == 1
223     then (head defs).value
224     else assert length defs > 1;
225       throw "The option `${showOption loc}' is defined multiple times while it's expected to be unique.\n${message}\nDefinition values:${showDefs defs}\n${prioritySuggestion}";
227   /* "Merge" option definitions by checking that they all have the same value. */
228   mergeEqualOption = loc: defs:
229     if defs == [] then abort "This case should never happen."
230     # Return early if we only have one element
231     # This also makes it work for functions, because the foldl' below would try
232     # to compare the first element with itself, which is false for functions
233     else if length defs == 1 then (head defs).value
234     else (foldl' (first: def:
235       if def.value != first.value then
236         throw "The option `${showOption loc}' has conflicting definition values:${showDefs [ first def ]}\n${prioritySuggestion}"
237       else
238         first) (head defs) (tail defs)).value;
240   /* Extracts values of all "value" keys of the given list.
242      Type: getValues :: [ { value :: a; } ] -> [a]
244      Example:
245        getValues [ { value = 1; } { value = 2; } ] // => [ 1 2 ]
246        getValues [ ]                               // => [ ]
247   */
248   getValues = map (x: x.value);
250   /* Extracts values of all "file" keys of the given list
252      Type: getFiles :: [ { file :: a; } ] -> [a]
254      Example:
255        getFiles [ { file = "file1"; } { file = "file2"; } ] // => [ "file1" "file2" ]
256        getFiles [ ]                                         // => [ ]
257   */
258   getFiles = map (x: x.file);
260   # Generate documentation template from the list of option declaration like
261   # the set generated with filterOptionSets.
262   optionAttrSetToDocList = optionAttrSetToDocList' [];
264   optionAttrSetToDocList' = _: options:
265     concatMap (opt:
266       let
267         name = showOption opt.loc;
268         docOption = {
269           loc = opt.loc;
270           inherit name;
271           description = opt.description or null;
272           declarations = filter (x: x != unknownModule) opt.declarations;
273           internal = opt.internal or false;
274           visible =
275             if (opt?visible && opt.visible == "shallow")
276             then true
277             else opt.visible or true;
278           readOnly = opt.readOnly or false;
279           type = opt.type.description or "unspecified";
280         }
281         // optionalAttrs (opt ? example) {
282           example =
283             builtins.addErrorContext "while evaluating the example of option `${name}`" (
284               renderOptionValue opt.example
285             );
286         }
287         // optionalAttrs (opt ? defaultText || opt ? default) {
288           default =
289             builtins.addErrorContext "while evaluating the ${if opt?defaultText then "defaultText" else "default value"} of option `${name}`" (
290               renderOptionValue (opt.defaultText or opt.default)
291             );
292         }
293         // optionalAttrs (opt ? relatedPackages && opt.relatedPackages != null) { inherit (opt) relatedPackages; };
295         subOptions =
296           let ss = opt.type.getSubOptions opt.loc;
297           in if ss != {} then optionAttrSetToDocList' opt.loc ss else [];
298         subOptionsVisible = docOption.visible && opt.visible or null != "shallow";
299       in
300         # To find infinite recursion in NixOS option docs:
301         # builtins.trace opt.loc
302         [ docOption ] ++ optionals subOptionsVisible subOptions) (collect isOption options);
305   /* This function recursively removes all derivation attributes from
306      `x` except for the `name` attribute.
308      This is to make the generation of `options.xml` much more
309      efficient: the XML representation of derivations is very large
310      (on the order of megabytes) and is not actually used by the
311      manual generator.
313      This function was made obsolete by renderOptionValue and is kept for
314      compatibility with out-of-tree code.
315   */
316   scrubOptionValue = x:
317     if isDerivation x then
318       { type = "derivation"; drvPath = x.name; outPath = x.name; name = x.name; }
319     else if isList x then map scrubOptionValue x
320     else if isAttrs x then mapAttrs (n: v: scrubOptionValue v) (removeAttrs x ["_args"])
321     else x;
324   /* Ensures that the given option value (default or example) is a `_type`d string
325      by rendering Nix values to `literalExpression`s.
326   */
327   renderOptionValue = v:
328     if v ? _type && v ? text then v
329     else literalExpression (lib.generators.toPretty {
330       multiline = true;
331       allowPrettyValues = true;
332     } v);
335   /* For use in the `defaultText` and `example` option attributes. Causes the
336      given string to be rendered verbatim in the documentation as Nix code. This
337      is necessary for complex values, e.g. functions, or values that depend on
338      other values or packages.
339   */
340   literalExpression = text:
341     if ! isString text then throw "literalExpression expects a string."
342     else { _type = "literalExpression"; inherit text; };
344   literalExample = lib.warn "literalExample is deprecated, use literalExpression instead, or use literalMD for a non-Nix description." literalExpression;
346   /* Transition marker for documentation that's already migrated to markdown
347      syntax. This is a no-op and no longer needed.
348   */
349   mdDoc = lib.id;
351   /* For use in the `defaultText` and `example` option attributes. Causes the
352      given MD text to be inserted verbatim in the documentation, for when
353      a `literalExpression` would be too hard to read.
354   */
355   literalMD = text:
356     if ! isString text then throw "literalMD expects a string."
357     else { _type = "literalMD"; inherit text; };
359   # Helper functions.
361   /* Convert an option, described as a list of the option parts to a
362      human-readable version.
364      Example:
365        (showOption ["foo" "bar" "baz"]) == "foo.bar.baz"
366        (showOption ["foo" "bar.baz" "tux"]) == "foo.\"bar.baz\".tux"
367        (showOption ["windowManager" "2bwm" "enable"]) == "windowManager.\"2bwm\".enable"
369      Placeholders will not be quoted as they are not actual values:
370        (showOption ["foo" "*" "bar"]) == "foo.*.bar"
371        (showOption ["foo" "<name>" "bar"]) == "foo.<name>.bar"
372   */
373   showOption = parts: let
374     escapeOptionPart = part:
375       let
376         # We assume that these are "special values" and not real configuration data.
377         # If it is real configuration data, it is rendered incorrectly.
378         specialIdentifiers = [
379           "<name>"          # attrsOf (submodule {})
380           "*"               # listOf (submodule {})
381           "<function body>" # functionTo
382         ];
383       in if builtins.elem part specialIdentifiers
384          then part
385          else lib.strings.escapeNixIdentifier part;
386     in (concatStringsSep ".") (map escapeOptionPart parts);
387   showFiles = files: concatStringsSep " and " (map (f: "`${f}'") files);
389   showDefs = defs: concatMapStrings (def:
390     let
391       # Pretty print the value for display, if successful
392       prettyEval = builtins.tryEval
393         (lib.generators.toPretty { }
394           (lib.generators.withRecursion { depthLimit = 10; throwOnDepthLimit = false; } def.value));
395       # Split it into its lines
396       lines = filter (v: ! isList v) (builtins.split "\n" prettyEval.value);
397       # Only display the first 5 lines, and indent them for better visibility
398       value = concatStringsSep "\n    " (take 5 lines ++ optional (length lines > 5) "...");
399       result =
400         # Don't print any value if evaluating the value strictly fails
401         if ! prettyEval.success then ""
402         # Put it on a new line if it consists of multiple
403         else if length lines > 1 then ":\n    " + value
404         else ": " + value;
405     in "\n- In `${def.file}'${result}"
406   ) defs;
408   showOptionWithDefLocs = opt: ''
409       ${showOption opt.loc}, with values defined in:
410       ${concatMapStringsSep "\n" (defFile: "  - ${defFile}") opt.files}
411     '';
413   unknownModule = "<unknown-file>";