nixpkgs mirror (for testing) github.com/NixOS/nixpkgs
nix
at r-updates 898 lines 25 kB view raw
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}