diff --git a/lib/options.nix b/lib/options.nix index b88af070513c..20e8fb8d5ed3 100644 --- a/lib/options.nix +++ b/lib/options.nix @@ -36,6 +36,9 @@ let inherit (lib.types) mkOptionType ; + inherit (lib.lists) + last + ; prioritySuggestion = '' Use `lib.mkForce value` or `lib.mkDefault value` to change the priority on any of these definitions. ''; @@ -107,17 +110,28 @@ rec { /* Creates an Option attribute set for an option that specifies the package a module should use for some purpose. - The package is specified as a list of strings representing its attribute path in nixpkgs. + Type: mkPackageOption :: pkgs -> (string|[string]) -> + { default? :: [string], example? :: null|string|[string], extraDescription? :: string } -> + option - Because of this, you need to pass nixpkgs itself as the first argument. + The package is specified in the third argument under `default` as a list of strings + representing its attribute path in nixpkgs (or another package set). + Because of this, you need to pass nixpkgs itself (or a subset) as the first argument. - The second argument is the name of the option, used in the description "The package to use.". + The second argument may be either a string or a list of strings. + It provides the display name of the package in the description of the generated option + (using only the last element if the passed value is a list) + and serves as the fallback value for the `default` argument. - You can also pass an example value, either a literal string or a package's attribute path. + To include extra information in the description, pass `extraDescription` to + append arbitrary text to the generated description. + You can also pass an `example` value, either a literal string or an attribute path. - You can omit the default path if the name of the option is also attribute path in nixpkgs. + The default argument can be omitted if the provided name is + an attribute of pkgs (if name is a string) or a + valid attribute path in pkgs (if name is a list). - Type: mkPackageOption :: pkgs -> string -> { default :: [string]; example :: null | string | [string]; } -> option + If you wish to explicitly provide no default, pass `null` as `default`. Example: mkPackageOption pkgs "hello" { } @@ -129,27 +143,46 @@ rec { example = "pkgs.haskell.packages.ghc92.ghc.withPackages (hkgs: [ hkgs.primes ])"; } => { _type = "option"; default = «derivation /nix/store/jxx55cxsjrf8kyh3fp2ya17q99w7541r-ghc-8.10.7.drv»; defaultText = { ... }; description = "The GHC package to use."; example = { ... }; type = { ... }; } + + Example: + mkPackageOption pkgs [ "python39Packages" "pytorch" ] { + extraDescription = "This is an example and doesn't actually do anything."; + } + => { _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 = { ... }; } + */ mkPackageOption = - # Package set (a specific version of nixpkgs) + # Package set (a specific version of nixpkgs or a subset) pkgs: # Name for the package, shown in option description name: - { default ? [ name ], example ? null }: - let default' = if !isList default then [ default ] else default; + { + # The attribute path where the default package is located + default ? name, + # A string or an attribute path to use as an example + example ? null, + # Additional text to include in the option description + extraDescription ? "", + }: + let + name' = if isList name then last name else name; + default' = if isList default then default else [ default ]; + defaultPath = concatStringsSep "." default'; + defaultValue = attrByPath default' + (throw "${defaultPath} cannot be found in pkgs") pkgs; in mkOption { + defaultText = literalExpression ("pkgs." + defaultPath); type = lib.types.package; - description = "The ${name} package to use."; - default = attrByPath default' - (throw "${concatStringsSep "." default'} cannot be found in pkgs") pkgs; - defaultText = literalExpression ("pkgs." + concatStringsSep "." default'); + description = "The ${name'} package to use." + + (if extraDescription == "" then "" else " ") + extraDescription; + ${if default != null then "default" else null} = defaultValue; ${if example != null then "example" else null} = literalExpression (if isList example then "pkgs." + concatStringsSep "." example else example); }; /* Like mkPackageOption, but emit an mdDoc description instead of DocBook. */ - mkPackageOptionMD = args: name: extra: - let option = mkPackageOption args name extra; + mkPackageOptionMD = pkgs: name: extra: + let option = mkPackageOption pkgs name extra; in option // { description = lib.mdDoc option.description; }; /* This option accepts anything, but it does not produce any result. diff --git a/nixos/doc/manual/development/option-declarations.section.md b/nixos/doc/manual/development/option-declarations.section.md index 59470bf1bc11..f6fed3e16837 100644 --- a/nixos/doc/manual/development/option-declarations.section.md +++ b/nixos/doc/manual/development/option-declarations.section.md @@ -101,11 +101,24 @@ Creates an Option attribute set for an option that specifies the package a modul **Note**: You shouldn’t necessarily make package options for all of your modules. You can always overwrite a specific package throughout nixpkgs by using [nixpkgs overlays](https://nixos.org/manual/nixpkgs/stable/#chap-overlays). -The default package is specified as a list of strings representing its attribute path in nixpkgs. Because of this, you need to pass nixpkgs itself as the first argument. +The package is specified in the third argument under `default` as a list of strings +representing its attribute path in nixpkgs (or another package set). +Because of this, you need to pass nixpkgs itself (or a subset) as the first argument. -The second argument is the name of the option, used in the description "The \ package to use.". You can also pass an example value, either a literal string or a package's attribute path. +The second argument may be either a string or a list of strings. +It provides the display name of the package in the description of the generated option +(using only the last element if the passed value is a list) +and serves as the fallback value for the `default` argument. -You can omit the default path if the name of the option is also attribute path in nixpkgs. +To include extra information in the description, pass `extraDescription` to +append arbitrary text to the generated description. +You can also pass an `example` value, either a literal string or an attribute path. + +The default argument can be omitted if the provided name is +an attribute of pkgs (if name is a string) or a +valid attribute path in pkgs (if name is a list). + +If you wish to explicitly provide no default, pass `null` as `default`. During the transition to CommonMark documentation `mkPackageOption` creates an option with a DocBook description attribute, once the transition is completed it will create a CommonMark description instead. `mkPackageOptionMD` always creates an option with a CommonMark description attribute and will be removed some time after the transition is completed. @@ -142,6 +155,21 @@ lib.mkOption { ``` ::: +::: {#ex-options-declarations-util-mkPackageOption-extraDescription .example} +```nix +mkPackageOption pkgs [ "python39Packages" "pytorch" ] { + extraDescription = "This is an example and doesn't actually do anything."; +} +# is like +lib.mkOption { + type = lib.types.package; + default = pkgs.python39Packages.pytorch; + defaultText = lib.literalExpression "pkgs.python39Packages.pytorch"; + description = "The pytorch package to use. This is an example and doesn't actually do anything."; +} +``` +::: + ## Extensible Option Types {#sec-option-declarations-eot} Extensible option types is a feature that allow to extend certain types