nixos/docs/option-declarations: Document mkEnableOption and mkPackageOption

This is a squashed commit. These are the original commit messages:

lib/option: Improve comment

better comment

Update documentation

Updated nixos/doc/manual/development/options-declarations.md with info on mkEnableOption and mkPackageOption.
Updated the comment on mkEnableOption in lib/options.nix

remove trailing whitespace

nixos/doc/option-declarations: Update IDs & formatting

nixos/docs/option-declarations: Escape angle brackets

Build DB from MD

(Amended) Fix typo
Co-authored-by: pennae <82953136+pennae@users.noreply.github.com>

(Amended) Build DB from MD (again)
This commit is contained in:
Anselm Schüler 2022-01-21 10:41:34 +01:00
parent fdf7ede344
commit c008b3d100
3 changed files with 320 additions and 118 deletions
lib
nixos/doc/manual

View File

@ -100,23 +100,48 @@ rec {
type = lib.types.bool; type = lib.types.bool;
}; };
/* Creaties an Option attribute set for an option that specifies the /* Creates an Option attribute set for an option that specifies the
package a module should use. package a module should use for some purpose.
The argument default is an attribute set path in pkgs. Type: mkPackageOption :: pkgs -> string -> { default :: [string], example :: null | string | [string] } -> option
The 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 second argument is the name of the option, used in the description "The <name> package to use.".
You can also pass an example value, either a literal string or a package's attribute path.
You can omit the default path if the name of the option is also attribute path in nixpkgs.
Example:
mkPackageOption pkgs "hello" { }
=> { _type = "option"; default = «derivation /nix/store/3r2vg51hlxj3cx5vscp0vkv60bqxkaq0-hello-2.10.drv»; defaultText = { ... }; description = "The hello package to use."; type = { ... }; }
Example:
mkPackageOption pkgs "GHC" {
default = [ "ghc" ];
example = "pkgs.haskell.package.ghc921.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 = { ... }; }
*/ */
mkPackageOption = pkgs: name: mkPackageOption =
{ default ? [ name ], example ? null }: # Package set (a specific version of nixpkgs)
let default' = if !isList default then [ default ] else default; pkgs:
in mkOption { # Name for the package, shown in option description
type = lib.types.package; name:
description = "The ${name} package to use."; { default ? [ name ], example ? null }:
default = attrByPath default' let default' = if !isList default then [ default ] else default;
(throw "${concatStringsSep "." default'} cannot be found in pkgs") pkgs; in mkOption {
defaultText = literalExpression ("pkgs." + concatStringsSep "." default'); type = lib.types.package;
${if example != null then "example" else null} = literalExpression description = "The ${name} package to use.";
(if isList example then "pkgs." + concatStringsSep "." example else example); default = attrByPath default'
}; (throw "${concatStringsSep "." default'} cannot be found in pkgs") pkgs;
defaultText = literalExpression ("pkgs." + concatStringsSep "." default');
${if example != null then "example" else null} = literalExpression
(if isList example then "pkgs." + concatStringsSep "." example else example);
};
/* This option accepts anything, but it does not produce any result. /* This option accepts anything, but it does not produce any result.

View File

@ -57,6 +57,80 @@ The function `mkOption` accepts the following arguments.
: A textual description of the option, in DocBook format, that will be : A textual description of the option, in DocBook format, that will be
included in the NixOS manual. included in the NixOS manual.
## Utility functions for common option patterns {#sec-option-declarations-util}
### `mkEnableOption` {#sec-option-declarations-util-mkEnableOption}
Creates an Option attribute set for a boolean value option i.e an
option to be toggled on or off.
This function takes a single string argument, the name of the thing to be toggled.
The option's description is "Whether to enable \<name\>.".
For example:
::: {#ex-options-declarations-util-mkEnableOption-magic .example}
```nix
lib.mkEnableOption "magic"
# is like
lib.mkOption {
type = lib.types.bool;
default = false;
example = true;
description = "Whether to enable magic.";
}
```
### `mkPackageOption` {#sec-option-declarations-util-mkPackageOption}
Usage:
```nix
mkPackageOption pkgs "name" { default = [ "path" "in" "pkgs" ]; example = "literal example"; }
```
Creates an Option attribute set for an option that specifies the package a module should use for some purpose.
**Note**: You shouldnt 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 second argument is the name of the option, used in the description "The \<name\> package to use.". You can also pass an example value, either a literal string or a package's attribute path.
You can omit the default path if the name of the option is also attribute path in nixpkgs.
::: {#ex-options-declarations-util-mkPackageOption .title}
Examples:
::: {#ex-options-declarations-util-mkPackageOption-hello .example}
```nix
lib.mkPackageOption pkgs "hello" { }
# is like
lib.mkOption {
type = lib.types.package;
default = pkgs.hello;
defaultText = lib.literalExpression "pkgs.hello";
description = "The hello package to use.";
}
```
::: {#ex-options-declarations-util-mkPackageOption-ghc .example}
```nix
lib.mkPackageOption pkgs "GHC" {
default = [ "ghc" ];
example = "pkgs.haskell.package.ghc921.ghc.withPackages (hkgs: [ hkgs.primes ])";
}
# is like
lib.mkOption {
type = lib.types.package;
default = pkgs.ghc;
defaultText = lib.literalExpression "pkgs.ghc";
example = lib.literalExpression "pkgs.haskell.package.ghc921.ghc.withPackages (hkgs: [ hkgs.primes ])";
description = "The GHC package to use.";
}
```
## Extensible Option Types {#sec-option-declarations-eot} ## Extensible Option Types {#sec-option-declarations-eot}
Extensible option types is a feature that allow to extend certain types Extensible option types is a feature that allow to extend certain types

View File

@ -97,125 +97,228 @@ options = {
</listitem> </listitem>
</varlistentry> </varlistentry>
</variablelist> </variablelist>
<section xml:id="sec-option-declarations-eot"> <section xml:id="sec-option-declarations-util">
<title>Extensible Option Types</title> <title>Utility functions for common option patterns</title>
<para> <section xml:id="sec-option-declarations-util-mkEnableOption">
Extensible option types is a feature that allow to extend certain <title><literal>mkEnableOption</literal></title>
types declaration through multiple module files. This feature only <para>
work with a restricted set of types, namely Creates an Option attribute set for a boolean value option i.e
<literal>enum</literal> and <literal>submodules</literal> and any an option to be toggled on or off.
composed forms of them. </para>
</para> <para>
<para> This function takes a single string argument, the name of the
Extensible option types can be used for <literal>enum</literal> thing to be toggled.
options that affects multiple modules, or as an alternative to </para>
related <literal>enable</literal> options. <para>
</para> The options description is <quote>Whether to enable
<para> &lt;name&gt;.</quote>.
As an example, we will take the case of display managers. There is </para>
a central display manager module for generic display manager <para>
options and a module file per display manager backend (sddm, gdm For example:
...). </para>
</para> <anchor xml:id="ex-options-declarations-util-mkEnableOption-magic" />
<para> <programlisting language="bash">
There are two approach to this module structure: lib.mkEnableOption &quot;magic&quot;
</para> # is like
<itemizedlist> lib.mkOption {
<listitem> type = lib.types.bool;
default = false;
example = true;
description = &quot;Whether to enable magic.&quot;;
}
</programlisting>
<section xml:id="sec-option-declarations-util-mkPackageOption">
<title><literal>mkPackageOption</literal></title>
<para> <para>
Managing the display managers independently by adding an Usage:
enable option to every display manager module backend. (NixOS)
</para> </para>
</listitem> <programlisting language="bash">
<listitem> mkPackageOption pkgs &quot;name&quot; { default = [ &quot;path&quot; &quot;in&quot; &quot;pkgs&quot; ]; example = &quot;literal example&quot;; }
</programlisting>
<para> <para>
Managing the display managers in the central module by adding Creates an Option attribute set for an option that specifies
an option to select which display manager backend to use. the package a module should use for some purpose.
</para> </para>
</listitem> <para>
</itemizedlist> <emphasis role="strong">Note</emphasis>: You shouldnt
<para> necessarily make package options for all of your modules. You
Both approaches have problems. can always overwrite a specific package throughout nixpkgs by
</para> using
<para> <link xlink:href="https://nixos.org/manual/nixpkgs/stable/#chap-overlays">nixpkgs
Making backends independent can quickly become hard to manage. For overlays</link>.
display managers, there can be only one enabled at a time, but the </para>
type system can not enforce this restriction as there is no <para>
relation between each backend <literal>enable</literal> option. As The default package is specified as a list of strings
a result, this restriction has to be done explicitely by adding representing its attribute path in nixpkgs. Because of this,
assertions in each display manager backend module. you need to pass nixpkgs itself as the first argument.
</para> </para>
<para> <para>
On the other hand, managing the display managers backends in the The second argument is the name of the option, used in the
central module will require to change the central module option description <quote>The &lt;name&gt; package to use.</quote>.
every time a new backend is added or removed. You can also pass an example value, either a literal string or
</para> a packages attribute path.
<para> </para>
By using extensible option types, it is possible to create a <para>
placeholder option in the central module You can omit the default path if the name of the option is
(<link linkend="ex-option-declaration-eot-service">Example: also attribute path in nixpkgs.
Extensible type placeholder in the service module</link>), and to </para>
extend it in each backend module <anchor xml:id="ex-options-declarations-util-mkPackageOption" />
(<link linkend="ex-option-declaration-eot-backend-gdm">Example: <para>
Extending Examples:
<literal>services.xserver.displayManager.enable</literal> in the </para>
<literal>gdm</literal> module</link>, <anchor xml:id="ex-options-declarations-util-mkPackageOption-hello" />
<link linkend="ex-option-declaration-eot-backend-sddm">Example: <programlisting language="bash">
Extending lib.mkPackageOption pkgs &quot;hello&quot; { }
<literal>services.xserver.displayManager.enable</literal> in the # is like
<literal>sddm</literal> module</link>). lib.mkOption {
</para> type = lib.types.package;
<para> default = pkgs.hello;
As a result, <literal>displayManager.enable</literal> option defaultText = lib.literalExpression &quot;pkgs.hello&quot;;
values can be added without changing the main service module file description = &quot;The hello package to use.&quot;;
and the type system automatically enforce that there can only be a }
single display manager enabled. </programlisting>
</para> <anchor xml:id="ex-options-declarations-util-mkPackageOption-ghc" />
<anchor xml:id="ex-option-declaration-eot-service" /> <programlisting language="bash">
<para> lib.mkPackageOption pkgs &quot;GHC&quot; {
<emphasis role="strong">Example: Extensible type placeholder in default = [ &quot;ghc&quot; ];
the service module</emphasis> example = &quot;pkgs.haskell.package.ghc921.ghc.withPackages (hkgs: [ hkgs.primes ])&quot;;
</para> }
<programlisting language="bash"> # is like
lib.mkOption {
type = lib.types.package;
default = pkgs.ghc;
defaultText = lib.literalExpression &quot;pkgs.ghc&quot;;
example = lib.literalExpression &quot;pkgs.haskell.package.ghc921.ghc.withPackages (hkgs: [ hkgs.primes ])&quot;;
description = &quot;The GHC package to use.&quot;;
}
</programlisting>
<section xml:id="sec-option-declarations-eot">
<title>Extensible Option Types</title>
<para>
Extensible option types is a feature that allow to extend
certain types declaration through multiple module files.
This feature only work with a restricted set of types,
namely <literal>enum</literal> and
<literal>submodules</literal> and any composed forms of
them.
</para>
<para>
Extensible option types can be used for
<literal>enum</literal> options that affects multiple
modules, or as an alternative to related
<literal>enable</literal> options.
</para>
<para>
As an example, we will take the case of display managers.
There is a central display manager module for generic
display manager options and a module file per display
manager backend (sddm, gdm ...).
</para>
<para>
There are two approach to this module structure:
</para>
<itemizedlist>
<listitem>
<para>
Managing the display managers independently by adding an
enable option to every display manager module backend.
(NixOS)
</para>
</listitem>
<listitem>
<para>
Managing the display managers in the central module by
adding an option to select which display manager backend
to use.
</para>
</listitem>
</itemizedlist>
<para>
Both approaches have problems.
</para>
<para>
Making backends independent can quickly become hard to
manage. For display managers, there can be only one enabled
at a time, but the type system can not enforce this
restriction as there is no relation between each backend
<literal>enable</literal> option. As a result, this
restriction has to be done explicitely by adding assertions
in each display manager backend module.
</para>
<para>
On the other hand, managing the display managers backends in
the central module will require to change the central module
option every time a new backend is added or removed.
</para>
<para>
By using extensible option types, it is possible to create a
placeholder option in the central module
(<link linkend="ex-option-declaration-eot-service">Example:
Extensible type placeholder in the service module</link>),
and to extend it in each backend module
(<link linkend="ex-option-declaration-eot-backend-gdm">Example:
Extending
<literal>services.xserver.displayManager.enable</literal> in
the <literal>gdm</literal> module</link>,
<link linkend="ex-option-declaration-eot-backend-sddm">Example:
Extending
<literal>services.xserver.displayManager.enable</literal> in
the <literal>sddm</literal> module</link>).
</para>
<para>
As a result, <literal>displayManager.enable</literal> option
values can be added without changing the main service module
file and the type system automatically enforce that there
can only be a single display manager enabled.
</para>
<anchor xml:id="ex-option-declaration-eot-service" />
<para>
<emphasis role="strong">Example: Extensible type placeholder
in the service module</emphasis>
</para>
<programlisting language="bash">
services.xserver.displayManager.enable = mkOption { services.xserver.displayManager.enable = mkOption {
description = &quot;Display manager to use&quot;; description = &quot;Display manager to use&quot;;
type = with types; nullOr (enum [ ]); type = with types; nullOr (enum [ ]);
}; };
</programlisting> </programlisting>
<anchor xml:id="ex-option-declaration-eot-backend-gdm" /> <anchor xml:id="ex-option-declaration-eot-backend-gdm" />
<para> <para>
<emphasis role="strong">Example: Extending <emphasis role="strong">Example: Extending
<literal>services.xserver.displayManager.enable</literal> in the <literal>services.xserver.displayManager.enable</literal> in
<literal>gdm</literal> module</emphasis> the <literal>gdm</literal> module</emphasis>
</para> </para>
<programlisting language="bash"> <programlisting language="bash">
services.xserver.displayManager.enable = mkOption { services.xserver.displayManager.enable = mkOption {
type = with types; nullOr (enum [ &quot;gdm&quot; ]); type = with types; nullOr (enum [ &quot;gdm&quot; ]);
}; };
</programlisting> </programlisting>
<anchor xml:id="ex-option-declaration-eot-backend-sddm" /> <anchor xml:id="ex-option-declaration-eot-backend-sddm" />
<para> <para>
<emphasis role="strong">Example: Extending <emphasis role="strong">Example: Extending
<literal>services.xserver.displayManager.enable</literal> in the <literal>services.xserver.displayManager.enable</literal> in
<literal>sddm</literal> module</emphasis> the <literal>sddm</literal> module</emphasis>
</para> </para>
<programlisting language="bash"> <programlisting language="bash">
services.xserver.displayManager.enable = mkOption { services.xserver.displayManager.enable = mkOption {
type = with types; nullOr (enum [ &quot;sddm&quot; ]); type = with types; nullOr (enum [ &quot;sddm&quot; ]);
}; };
</programlisting> </programlisting>
<para> <para>
The placeholder declaration is a standard The placeholder declaration is a standard
<literal>mkOption</literal> declaration, but it is important that <literal>mkOption</literal> declaration, but it is important
extensible option declarations only use the that extensible option declarations only use the
<literal>type</literal> argument. <literal>type</literal> argument.
</para> </para>
<para> <para>
Extensible option types work with any of the composed variants of Extensible option types work with any of the composed
<literal>enum</literal> such as variants of <literal>enum</literal> such as
<literal>with types; nullOr (enum [ &quot;foo&quot; &quot;bar&quot; ])</literal> <literal>with types; nullOr (enum [ &quot;foo&quot; &quot;bar&quot; ])</literal>
or or
<literal>with types; listOf (enum [ &quot;foo&quot; &quot;bar&quot; ])</literal>. <literal>with types; listOf (enum [ &quot;foo&quot; &quot;bar&quot; ])</literal>.
</para> </para>
</section>
</section>
</section>
</section> </section>
</section> </section>