nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1/**
2 Module System option handling.
3*/
4{ lib }:
5
6let
7 inherit (lib)
8 all
9 collect
10 concatLists
11 concatMap
12 concatMapStringsSep
13 filter
14 foldl'
15 head
16 tail
17 isAttrs
18 isBool
19 isDerivation
20 isFunction
21 isInt
22 isList
23 isString
24 length
25 mapAttrs
26 optional
27 optionals
28 take
29 ;
30 inherit (lib.attrsets)
31 attrByPath
32 optionalAttrs
33 showAttrPath
34 ;
35 inherit (lib.strings)
36 concatMapStrings
37 concatStringsSep
38 ;
39 inherit (lib.types)
40 mkOptionType
41 ;
42 inherit (lib.lists)
43 last
44 toList
45 ;
46 prioritySuggestion = ''
47 Use `lib.mkForce value` or `lib.mkDefault value` to change the priority on any of these definitions.
48 '';
49in
50rec {
51
52 /**
53 Returns true when the given argument `a` is an option
54
55 # Inputs
56
57 `a`
58 : Any value to check whether it is an option
59
60 # Examples
61 :::{.example}
62 ## `lib.options.isOption` usage example
63
64 ```nix
65 isOption 1 // => false
66 isOption (mkOption {}) // => true
67 ```
68
69 :::
70
71 # Type
72
73 ```
74 isOption :: Any -> Bool
75 ```
76 */
77 isOption = lib.isType "option";
78
79 /**
80 Creates an Option declaration for use with the module system.
81
82 # Inputs
83
84 Attribute set
85 : containing none or some of the following attributes.
86
87 `default`
88 : Optional default value used when no definition is given in the configuration.
89
90 `defaultText`
91 : Substitute for documenting the `default`, if evaluating the default value during documentation rendering is not possible.
92 : Can be any nix value that evaluates.
93 : Usage with `lib.literalMD` or `lib.literalExpression` is supported
94
95 `example`
96 : Optional example value used in the manual.
97 : Can be any nix value that evaluates.
98 : Usage with `lib.literalMD` or `lib.literalExpression` is supported
99
100 `description`
101 : Optional string describing the option. This is required if option documentation is generated.
102
103 `relatedPackages`
104 : Optional related packages used in the manual (see `genRelatedPackages` in `../nixos/lib/make-options-doc/default.nix`).
105
106 `type`
107 : Optional option type, providing type-checking and value merging.
108
109 `apply`
110 : Optional function that converts the option value to something else.
111
112 `internal`
113 : Optional boolean indicating whether the option is for NixOS developers only.
114
115 `visible`
116 : Optional, whether the option and/or sub-options show up in the manual.
117 Use false to hide the option and any sub-options from submodules.
118 Use "shallow" to hide only sub-options.
119 Use "transparent" to hide this option, but not its sub-options.
120 Default: true.
121
122 `readOnly`
123 : Optional boolean indicating whether the option can be set only once.
124
125 # Examples
126 :::{.example}
127 ## `lib.options.mkOption` usage example
128
129 ```nix
130 mkOption { }
131 # => Empty option; type = types.anything
132
133 mkOption { default = "foo"; }
134 # => Same as above, with a default value
135 ```
136
137 :::
138 */
139 mkOption =
140 {
141 default ? null,
142 defaultText ? null,
143 example ? null,
144 description ? null,
145 relatedPackages ? null,
146 type ? null,
147 apply ? null,
148 internal ? null,
149 visible ? null,
150 readOnly ? null,
151 }@attrs:
152 attrs // { _type = "option"; };
153
154 /**
155 Creates an option declaration with a default value of `false`, and can be defined to `true`.
156
157 # Inputs
158
159 `name`
160
161 : Name for the created option
162
163 # Examples
164 :::{.example}
165 ## `lib.options.mkEnableOption` usage example
166
167 ```nix
168 # module
169 let
170 eval = lib.evalModules {
171 modules = [
172 {
173 options.foo.enable = mkEnableOption "foo";
174
175 config.foo.enable = true;
176 }
177 ];
178 };
179 in
180 eval.config
181 => { foo.enable = true; }
182 ```
183
184 :::
185 */
186 mkEnableOption =
187 name:
188 mkOption {
189 default = false;
190 example = true;
191 description = "Whether to enable ${name}.";
192 type = lib.types.bool;
193 };
194
195 /**
196 Creates an Option attribute set for an option that specifies the
197 package a module should use for some purpose.
198
199 The package is specified in the third argument under `default` as a list of strings
200 representing its attribute path in nixpkgs (or another package set).
201 Because of this, you need to pass nixpkgs itself (usually `pkgs` in a module;
202 alternatively to nixpkgs itself, another package set) as the first argument.
203
204 If you pass another package set you should set the `pkgsText` option.
205 This option is used to display the expression for the package set. It is `"pkgs"` by default.
206 If your expression is complex you should parenthesize it, as the `pkgsText` argument
207 is usually immediately followed by an attribute lookup (`.`).
208
209 The second argument may be either a string or a list of strings.
210 It provides the display name of the package in the description of the generated option
211 (using only the last element if the passed value is a list)
212 and serves as the fallback value for the `default` argument.
213
214 To include extra information in the description, pass `extraDescription` to
215 append arbitrary text to the generated description.
216
217 You can also pass an `example` value, either a literal string or an attribute path.
218
219 The `default` argument can be omitted if the provided name is
220 an attribute of pkgs (if `name` is a string) or a valid attribute path in pkgs (if `name` is a list).
221 You can also set `default` to just a string in which case it is interpreted as an attribute name
222 (a singleton attribute path, if you will).
223
224 If you wish to explicitly provide no default, pass `null` as `default`.
225
226 If you want users to be able to set no package, pass `nullable = true`.
227 In this mode a `default = null` will not be interpreted as no default and is interpreted literally.
228
229 # Inputs
230
231 `pkgs`
232
233 : Package set (an instantiation of nixpkgs such as pkgs in modules or another package set)
234
235 `name`
236
237 : Name for the package, shown in option description
238
239 Structured function argument
240 : Attribute set containing the following attributes.
241
242 `nullable`
243 : Optional whether the package can be null, for example to disable installing a package altogether. Default: `false`
244
245 `default`
246 : Optional attribute path where the default package is located. Default: `name`
247 If omitted will be copied from `name`
248
249 `example`
250 : Optional string or an attribute path to use as an example. Default: `null`
251
252 `extraDescription`
253 : Optional additional text to include in the option description. Default: `""`
254
255 `pkgsText`
256 : Optional representation of the package set passed as pkgs. Default: `"pkgs"`
257
258 # Type
259
260 ```
261 mkPackageOption :: Pkgs -> (String | [String]) -> { nullable? :: Bool; default? :: String | [String]; example? :: Null | String | [String]; extraDescription? :: String; pkgsText? :: String; } -> Option
262 ```
263
264 # Examples
265 :::{.example}
266 ## `lib.options.mkPackageOption` usage example
267
268 ```nix
269 mkPackageOption pkgs "hello" { }
270 => { ...; default = pkgs.hello; defaultText = literalExpression "pkgs.hello"; description = "The hello package to use."; type = package; }
271
272 mkPackageOption pkgs "GHC" {
273 default = [ "ghc" ];
274 example = "pkgs.haskellPackages.ghc.withPackages (hkgs: [ hkgs.primes ])";
275 }
276 => { ...; default = pkgs.ghc; defaultText = literalExpression "pkgs.ghc"; description = "The GHC package to use."; example = literalExpression "pkgs.haskellPackages.ghc.withPackages (hkgs: [ hkgs.primes ])"; type = package; }
277
278 mkPackageOption pkgs [ "python3Packages" "pytorch" ] {
279 extraDescription = "This is an example and doesn't actually do anything.";
280 }
281 => { ...; default = pkgs.python3Packages.pytorch; defaultText = literalExpression "pkgs.python3Packages.pytorch"; description = "The pytorch package to use. This is an example and doesn't actually do anything."; type = package; }
282
283 mkPackageOption pkgs "nushell" {
284 nullable = true;
285 }
286 => { ...; default = pkgs.nushell; defaultText = literalExpression "pkgs.nushell"; description = "The nushell package to use."; type = nullOr package; }
287
288 mkPackageOption pkgs "coreutils" {
289 default = null;
290 }
291 => { ...; description = "The coreutils package to use."; type = package; }
292
293 mkPackageOption pkgs "dbus" {
294 nullable = true;
295 default = null;
296 }
297 => { ...; default = null; description = "The dbus package to use."; type = nullOr package; }
298
299 mkPackageOption pkgs.javaPackages "OpenJFX" {
300 default = "openjfx20";
301 pkgsText = "pkgs.javaPackages";
302 }
303 => { ...; default = pkgs.javaPackages.openjfx20; defaultText = literalExpression "pkgs.javaPackages.openjfx20"; description = "The OpenJFX package to use."; type = package; }
304 ```
305
306 :::
307 */
308 mkPackageOption =
309 pkgs: name:
310 {
311 nullable ? false,
312 default ? name,
313 example ? null,
314 extraDescription ? "",
315 pkgsText ? "pkgs",
316 }:
317 let
318 name' = if isList name then last name else name;
319 default' = toList default;
320 defaultText = showAttrPath default';
321 defaultValue = attrByPath default' (throw "${defaultText} cannot be found in ${pkgsText}") pkgs;
322 defaults =
323 if default != null then
324 {
325 default = defaultValue;
326 defaultText = literalExpression "${pkgsText}.${defaultText}";
327 }
328 else
329 optionalAttrs nullable {
330 default = null;
331 };
332 in
333 mkOption (
334 defaults
335 // {
336 description =
337 "The ${name'} package to use." + (if extraDescription == "" then "" else " ") + extraDescription;
338 type = with lib.types; (if nullable then nullOr else lib.id) package;
339 }
340 // optionalAttrs (example != null) {
341 example = literalExpression (
342 if isList example then "${pkgsText}.${showAttrPath example}" else example
343 );
344 }
345 );
346
347 /**
348 This option accepts arbitrary definitions, but it does not produce an option value.
349
350 This is useful for sharing a module across different module sets
351 without having to implement similar features as long as the
352 values of the options are not accessed.
353
354 # Inputs
355
356 `attrs`
357
358 : Attribute set whose attributes override the argument to `mkOption`.
359 */
360 mkSinkUndeclaredOptions =
361 attrs:
362 mkOption (
363 {
364 internal = true;
365 visible = false;
366 default = false;
367 description = "Sink for option definitions.";
368 type = mkOptionType {
369 name = "sink";
370 check = x: true;
371 merge = loc: defs: false;
372 };
373 apply = x: throw "Option value is not readable because the option is not declared.";
374 }
375 // attrs
376 );
377
378 /**
379 A merge function that merges multiple definitions of an option into a single value
380
381 :::{.caution}
382 This function is used as the default merge operation in `lib.types.mkOptionType`. In most cases, explicit usage of this function is unnecessary.
383 :::
384
385 # Inputs
386
387 `loc`
388 : location of the option in the configuration as a list of strings.
389
390 e.g. `["boot" "loader "grub" "enable"]`
391
392 `defs`
393 : list of definition values and locations.
394
395 e.g. `[ { file = "/foo.nix"; value = 1; } { file = "/bar.nix"; value = 2 } ]`
396
397 # Example
398 :::{.example}
399 ## `lib.options.mergeDefaultOption` usage example
400
401 ```nix
402 myType = mkOptionType {
403 name = "myType";
404 merge = mergeDefaultOption; # <- This line is redundant. It is the default already.
405 };
406 ```
407
408 :::
409
410 # Merge behavior
411
412 Merging requires all definition values to have the same type.
413
414 - If all definitions are booleans, the result of a `foldl'` with the `or` operation is returned.
415 - If all definitions are strings, they are concatenated. (`lib.concatStrings`)
416 - If all definitions are integers and all are equal, the first one is returned.
417 - If all definitions are lists, they are concatenated. (`++`)
418 - If all definitions are attribute sets, they are merged. (`lib.mergeAttrs`)
419 - If all definitions are functions, the first function is applied to the result of the second function. (`f -> x: f x`)
420 - Otherwise, an error is thrown.
421 */
422 mergeDefaultOption =
423 loc: defs:
424 let
425 list = getValues defs;
426 in
427 if length list == 1 then
428 head list
429 else if all isFunction list then
430 x: mergeDefaultOption loc (map (f: f x) list)
431 else if all isList list then
432 concatLists list
433 else if all isAttrs list then
434 foldl' lib.mergeAttrs { } list
435 else if all isBool list then
436 foldl' lib."or" false list
437 else if all isString list then
438 lib.concatStrings list
439 else if all isInt list && all (x: x == head list) list then
440 head list
441 else
442 throw "Cannot merge definitions of `${showOption loc}'. Definition values:${showDefs defs}";
443
444 /**
445 Require a single definition.
446
447 ::: {.warning}
448 Does not perform nested checks, as this does not run the merge function!
449 :::
450 */
451 mergeOneOption = mergeUniqueOption { message = ""; };
452
453 /**
454 Require a single definition.
455
456 ::: {.note}
457 When the type is not checked completely by check, pass a merge function for further checking (of sub-attributes, etc).
458 :::
459
460 # Inputs
461
462 `loc`
463
464 : 2\. Function argument
465
466 `defs`
467
468 : 3\. Function argument
469 */
470 mergeUniqueOption =
471 args@{
472 message,
473 # WARNING: the default merge function assumes that the definition is a valid (option) value. You MUST pass a merge function if the return value needs to be
474 # - type checked beyond what .check does (which should be very little; only on the value head; not attribute values, etc)
475 # - if you want attribute values to be checked, or list items
476 # - if you want coercedTo-like behavior to work
477 merge ? loc: defs: (head defs).value,
478 }:
479 loc: defs:
480 if length defs == 1 then
481 merge loc defs
482 else
483 assert length defs > 1;
484 throw "The option `${showOption loc}' is defined multiple times while it's expected to be unique.\n${message}\nDefinition values:${showDefs defs}\n${prioritySuggestion}";
485
486 /**
487 "Merge" option definitions by checking that they all have the same value.
488
489 # Inputs
490
491 `loc`
492
493 : 1\. Function argument
494
495 `defs`
496
497 : 2\. Function argument
498 */
499 mergeEqualOption =
500 loc: defs:
501 if defs == [ ] then
502 abort "This case should never happen."
503 # Returns early if we only have one element
504 # This also makes it work for functions, because the foldl' below would try
505 # to compare the first element with itself, which is false for functions
506 else if length defs == 1 then
507 (head defs).value
508 else
509 (foldl' (
510 first: def:
511 if def.value != first.value then
512 throw "The option `${showOption loc}' has conflicting definition values:${
513 showDefs [
514 first
515 def
516 ]
517 }\n${prioritySuggestion}"
518 else
519 first
520 ) (head defs) (tail defs)).value;
521
522 /**
523 Extracts values of all `value` keys of the given list.
524
525 # Type
526
527 ```
528 getValues :: [{ value :: a; ... }] -> [a]
529 ```
530
531 # Examples
532 :::{.example}
533 ## `getValues` usage example
534
535 ```nix
536 getValues [ { value = 1; } { value = 2; } ] // => [ 1 2 ]
537 getValues [ ] // => [ ]
538 ```
539
540 :::
541 */
542 getValues = map (x: x.value);
543
544 /**
545 Extracts values of all `file` keys of the given list
546
547 # Type
548
549 ```
550 getFiles :: [{ file :: a; ... }] -> [a]
551 ```
552
553 # Examples
554 :::{.example}
555 ## `getFiles` usage example
556
557 ```nix
558 getFiles [ { file = "file1"; } { file = "file2"; } ] // => [ "file1" "file2" ]
559 getFiles [ ] // => [ ]
560 ```
561
562 :::
563 */
564 getFiles = map (x: x.file);
565
566 # Generate documentation template from the list of option declaration like
567 # the set generated with filterOptionSets.
568 optionAttrSetToDocList = optionAttrSetToDocList' [ ];
569
570 optionAttrSetToDocList' =
571 _: options:
572 concatMap (
573 opt:
574 let
575 name = showOption opt.loc;
576 visible = opt.visible or true;
577 docOption = {
578 loc = opt.loc;
579 inherit name;
580 description = opt.description or null;
581 declarations = filter (x: x != unknownModule) opt.declarations;
582 internal = opt.internal or false;
583 visible = if isBool visible then visible else visible == "shallow";
584 readOnly = opt.readOnly or false;
585 type = opt.type.description or "unspecified";
586 }
587 // optionalAttrs (opt ? example) {
588 example = builtins.addErrorContext "while evaluating the example of option `${name}`" (
589 renderOptionValue opt.example
590 );
591 }
592 // optionalAttrs (opt ? defaultText || opt ? default) {
593 default = builtins.addErrorContext "while evaluating the ${
594 if opt ? defaultText then "defaultText" else "default value"
595 } of option `${name}`" (renderOptionValue (opt.defaultText or opt.default));
596 }
597 // optionalAttrs (opt ? relatedPackages && opt.relatedPackages != null) {
598 inherit (opt) relatedPackages;
599 };
600
601 subOptions =
602 let
603 ss = opt.type.getSubOptions opt.loc;
604 in
605 if ss != { } then optionAttrSetToDocList' opt.loc ss else [ ];
606 subOptionsVisible = if isBool visible then visible else visible == "transparent";
607 in
608 # To find infinite recursion in NixOS option docs:
609 # builtins.trace opt.loc
610 [ docOption ] ++ optionals subOptionsVisible subOptions
611 ) (collect isOption options);
612
613 /**
614 This function recursively removes all derivation attributes from
615 `x` except for the `name` attribute.
616
617 This is to make the generation of `options.xml` much more
618 efficient: the XML representation of derivations is very large
619 (on the order of megabytes) and is not actually used by the
620 manual generator.
621
622 This function was made obsolete by `renderOptionValue` and is kept for
623 compatibility with out-of-tree code.
624
625 # Inputs
626
627 `x`
628
629 : 1\. Function argument
630 */
631 scrubOptionValue =
632 x:
633 if isDerivation x then
634 {
635 type = "derivation";
636 drvPath = x.name;
637 outPath = x.name;
638 name = x.name;
639 }
640 else if isList x then
641 map scrubOptionValue x
642 else if isAttrs x then
643 mapAttrs (n: v: scrubOptionValue v) (removeAttrs x [ "_args" ])
644 else
645 x;
646
647 /**
648 Ensures that the given option value (default or example) is a `_type`d string
649 by rendering Nix values to `literalExpression`s.
650
651 # Inputs
652
653 `v`
654
655 : 1\. Function argument
656 */
657 renderOptionValue =
658 v:
659 if v ? _type && v ? text then
660 v
661 else
662 literalExpression (
663 lib.generators.toPretty {
664 multiline = true;
665 allowPrettyValues = true;
666 } v
667 );
668
669 /**
670 For use in the `defaultText` and `example` option attributes. Causes the
671 given string to be rendered verbatim in the documentation as Nix code. This
672 is necessary for complex values, e.g. functions, or values that depend on
673 other values or packages.
674
675 # Examples
676 :::{.example}
677 ## `literalExpression` usage example
678
679 ```nix
680 llvmPackages = mkOption {
681 type = types.str;
682 description = ''
683 Version of llvm packages to use for
684 this module
685 '';
686 example = literalExpression ''
687 llvmPackages = pkgs.llvmPackages_20;
688 '';
689 };
690 ```
691
692 :::
693
694 # Inputs
695
696 `text`
697
698 : The text to render as a Nix expression
699 */
700 literalExpression =
701 text:
702 if !isString text then
703 throw "literalExpression expects a string."
704 else
705 {
706 _type = "literalExpression";
707 inherit text;
708 };
709
710 /**
711 For use in the `defaultText` and `example` option attributes. Causes the
712 given string to be rendered verbatim in the documentation as a code
713 block with the language bassed on the provided input tag.
714
715 If you wish to render Nix code, please see `literalExpression`.
716
717 # Examples
718 :::{.example}
719 ## `literalCode` usage example
720
721 ```nix
722 myPythonScript = mkOption {
723 type = types.str;
724 description = ''
725 Example python script used by a module
726 '';
727 example = literalCode "python" ''
728 print("Hello world!")
729 '';
730 };
731 ```
732
733 :::
734
735 # Inputs
736
737 `languageTag`
738
739 : The language tag to use when producing the code block (i.e. `js`, `rs`, etc).
740
741 `text`
742
743 : The text to render as a Nix expression
744 */
745 literalCode =
746 languageTag: text:
747 lib.literalMD ''
748 ```${languageTag}
749 ${text}
750 ```
751 '';
752
753 /**
754 For use in the `defaultText` and `example` option attributes. Causes the
755 given MD text to be inserted verbatim in the documentation, for when
756 a `literalExpression` would be too hard to read.
757
758 # Inputs
759
760 `text`
761
762 : 1\. Function argument
763 */
764 literalMD =
765 text:
766 if !isString text then
767 throw "literalMD expects a string."
768 else
769 {
770 _type = "literalMD";
771 inherit text;
772 };
773
774 # Helper functions.
775
776 /**
777 Convert an option, described as a list of the option parts to a
778 human-readable version.
779
780 # Inputs
781
782 `parts`
783
784 : 1\. Function argument
785
786 # Examples
787 :::{.example}
788 ## `showOption` usage example
789
790 ```nix
791 (showOption ["foo" "bar" "baz"]) == "foo.bar.baz"
792 (showOption ["foo" "bar.baz" "tux"]) == "foo.\"bar.baz\".tux"
793 (showOption ["windowManager" "2bwm" "enable"]) == "windowManager.\"2bwm\".enable"
794
795 Placeholders will not be quoted as they are not actual values:
796 (showOption ["foo" "*" "bar"]) == "foo.*.bar"
797 (showOption ["foo" "<name>" "bar"]) == "foo.<name>.bar"
798 (showOption ["foo" "<myPlaceholder>" "bar"]) == "foo.<myPlaceholder>.bar"
799 ```
800
801 :::
802 */
803 showOption =
804 parts:
805 let
806 # If the part is a named placeholder of the form "<...>" don't escape it.
807 # It may cause misleading escaping if somebody uses literally "<...>" in their option names.
808 # This is the trade-off to allow for placeholders in option names.
809 isNamedPlaceholder = builtins.match "<(.*)>";
810 escapeOptionPart =
811 part:
812 if part == "*" || isNamedPlaceholder part != null then
813 part
814 else
815 lib.strings.escapeNixIdentifier part;
816 in
817 (concatStringsSep ".") (map escapeOptionPart parts);
818 showFiles = files: concatStringsSep " and " (map (f: "`${f}'") files);
819
820 showDefs =
821 defs:
822 concatMapStrings (
823 def:
824 let
825 # Pretty print the value for display, if successful
826 prettyEval = builtins.tryEval (
827 lib.generators.toPretty { } (
828 lib.generators.withRecursion {
829 depthLimit = 10;
830 throwOnDepthLimit = false;
831 } def.value
832 )
833 );
834 # Split it into its lines
835 lines = filter (v: !isList v) (builtins.split "\n" prettyEval.value);
836 # Only display the first 5 lines, and indent them for better visibility
837 value = concatStringsSep "\n " (take 5 lines ++ optional (length lines > 5) "...");
838 result =
839 # Don't print any value if evaluating the value strictly fails
840 if !prettyEval.success then
841 ""
842 # Put it on a new line if it consists of multiple
843 else if length lines > 1 then
844 ":\n " + value
845 else
846 ": " + value;
847 in
848 "\n- In `${def.file}'${result}"
849 ) defs;
850
851 /**
852 Pretty prints all option definition locations
853
854 # Inputs
855
856 `option`
857 : The option to pretty print
858
859 # Examples
860 :::{.example}
861 ## `lib.options.showOptionWithDefLocs` usage example
862
863 ```nix
864 showOptionWithDefLocs { loc = ["x" "y" ]; files = [ "foo.nix" "bar.nix" ]; }
865 "x.y, with values defined in:\n - foo.nix\n - bar.nix\n"
866 ```
867
868 ```nix
869 nix-repl> eval = lib.evalModules {
870 modules = [
871 {
872 options = {
873 foo = lib.mkEnableOption "foo";
874 };
875 }
876 ];
877 }
878
879 nix-repl> lib.options.showOptionWithDefLocs eval.options.foo
880 "foo, with values defined in:\n - <unknown-file>\n"
881 ```
882
883 :::
884
885 # Type
886
887 ```
888 showOptionWithDefLocs :: { files :: [String]; loc :: [String]; ... } -> String
889 ```
890 */
891 showOptionWithDefLocs = opt: ''
892 ${showOption opt.loc}, with values defined in:
893 ${concatMapStringsSep "\n" (defFile: " - ${defFile}") opt.files}
894 '';
895
896 unknownModule = "<unknown-file>";
897
898}