From b32252ddfa530ff67e297ff6ba9e5cb0f91a767a Mon Sep 17 00:00:00 2001 From: Eric Sagnes <eric.sagnes@gmail.com> Date: Tue, 13 Sep 2016 14:04:02 +0900 Subject: [PATCH] NixOS manual: add module option types doc (#18525) --- .../development/option-declarations.xml | 88 +--- nixos/doc/manual/development/option-types.xml | 394 ++++++++++++++++++ .../manual/development/writing-modules.xml | 1 + 3 files changed, 398 insertions(+), 85 deletions(-) create mode 100644 nixos/doc/manual/development/option-types.xml diff --git a/nixos/doc/manual/development/option-declarations.xml b/nixos/doc/manual/development/option-declarations.xml index b0689aa1d97f..7be5e9d51d46 100644 --- a/nixos/doc/manual/development/option-declarations.xml +++ b/nixos/doc/manual/development/option-declarations.xml @@ -31,9 +31,9 @@ options = { <varlistentry> <term><varname>type</varname></term> <listitem> - <para>The type of the option (see below). It may be omitted, - but that’s not advisable since it may lead to errors that are - hard to diagnose.</para> + <para>The type of the option (see <xref linkend='sec-option-types' />). + It may be omitted, but that’s not advisable since it may lead to errors + that are hard to diagnose.</para> </listitem> </varlistentry> @@ -65,86 +65,4 @@ options = { </para> -<para>Here is a non-exhaustive list of option types: - -<variablelist> - - <varlistentry> - <term><varname>types.bool</varname></term> - <listitem> - <para>A Boolean.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>types.int</varname></term> - <listitem> - <para>An integer.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>types.str</varname></term> - <listitem> - <para>A string.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>types.lines</varname></term> - <listitem> - <para>A string. If there are multiple definitions, they are - concatenated, with newline characters in between.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>types.path</varname></term> - <listitem> - <para>A path, defined as anything that, when coerced to a - string, starts with a slash. This includes derivations.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>types.package</varname></term> - <listitem> - <para>A derivation (such as <literal>pkgs.hello</literal>) or a - store path (such as - <filename>/nix/store/1ifi1cfbfs5iajmvwgrbmrnrw3a147h9-hello-2.10</filename>).</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>types.listOf</varname> <replaceable>t</replaceable></term> - <listitem> - <para>A list of elements of type <replaceable>t</replaceable> - (e.g., <literal>types.listOf types.str</literal> is a list of - strings). Multiple definitions are concatenated together.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>types.attrsOf</varname> <replaceable>t</replaceable></term> - <listitem> - <para>A set of elements of type <replaceable>t</replaceable> - (e.g., <literal>types.attrsOf types.int</literal> is a set of - name/value pairs, the values being integers).</para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>types.nullOr</varname> <replaceable>t</replaceable></term> - <listitem> - <para>Either the value <literal>null</literal> or something of - type <replaceable>t</replaceable>.</para> - </listitem> - </varlistentry> - -</variablelist> - -You can also create new types using the function -<varname>mkOptionType</varname>. See -<filename>lib/types.nix</filename> in Nixpkgs for details.</para> - </section> diff --git a/nixos/doc/manual/development/option-types.xml b/nixos/doc/manual/development/option-types.xml new file mode 100644 index 000000000000..8871b02cebf1 --- /dev/null +++ b/nixos/doc/manual/development/option-types.xml @@ -0,0 +1,394 @@ +<section xmlns="http://docbook.org/ns/docbook" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:xi="http://www.w3.org/2001/XInclude" + version="5.0" + xml:id="sec-option-types"> + +<title>Options Types</title> + + <para>Option types are a way to put constraints on the values a module option + can take. + Types are also responsible of how values are merged in case of multiple + value definitions.</para> + <section><title>Basic Types</title> + + <para>Basic types are the simplest available types in the module system. + Basic types include multiple string types that mainly differ in how + definition merging is handled.</para> + +<variablelist> + <varlistentry> + <term><varname>types.bool</varname></term> + <listitem><para>A boolean, its values can be <literal>true</literal> or + <literal>false</literal>.</para></listitem> + </varlistentry> + <varlistentry> + <term><varname>types.int</varname></term> + <listitem><para>An integer.</para></listitem> + </varlistentry> + <varlistentry> + <term><varname>types.path</varname></term> + <listitem><para>A filesystem path, defined as anything that when coerced to + a string starts with a slash. Even if derivations can be considered as + path, the more specific <literal>types.package</literal> should be + preferred.</para></listitem> + </varlistentry> + <varlistentry> + <term><varname>types.package</varname></term> + <listitem><para>A derivation or a store path.</para></listitem> + </varlistentry> +</variablelist> + +<para>String related types:</para> + +<variablelist> + <varlistentry> + <term><varname>types.str</varname></term> + <listitem><para>A string. Multiple definitions cannot be + merged.</para></listitem> + </varlistentry> + <varlistentry> + <term><varname>types.lines</varname></term> + <listitem><para>A string. Multiple definitions are concatenated with a new + line <literal>"\n"</literal>.</para></listitem> + </varlistentry> + <varlistentry> + <term><varname>types.commas</varname></term> + <listitem><para>A string. Multiple definitions are concatenated with a comma + <literal>","</literal>.</para></listitem> + </varlistentry> + <varlistentry> + <term><varname>types.envVar</varname></term> + <listitem><para>A string. Multiple definitions are concatenated with a + collon <literal>":"</literal>.</para></listitem> + </varlistentry> + <varlistentry> + <term><varname>types.separatedString</varname> + <replaceable>sep</replaceable></term> + <listitem><para>A string with a custom separator + <replaceable>sep</replaceable>, e.g. <literal>types.separatedString + "|"</literal>.</para></listitem> + </varlistentry> +</variablelist> + + </section> + + <section><title>Composed Types</title> + + <para>Composed types allow to create complex types by taking another type(s) + or value(s) as parameter(s). + It is possible to compose types multiple times, e.g. <literal>with types; + nullOr (enum [ "left" "right" ])</literal>.</para> + +<variablelist> + <varlistentry> + <term><varname>types.listOf</varname> <replaceable>t</replaceable></term> + <listitem><para>A list of <replaceable>t</replaceable> type, e.g. + <literal>types.listOf int</literal>. Multiple definitions are merged + with list concatenation.</para></listitem> + </varlistentry> + <varlistentry> + <term><varname>types.attrsOf</varname> <replaceable>t</replaceable></term> + <listitem><para>An attribute set of where all the values are of + <replaceable>t</replaceable> type. Multiple definitions result in the + joined attribute set.</para></listitem> + </varlistentry> + <varlistentry> + <term><varname>types.loaOf</varname> <replaceable>t</replaceable></term> + <listitem><para>An attribute set or a list of <replaceable>t</replaceable> + type. Multiple definitions are merged according to the + value.</para></listitem> + </varlistentry> + <varlistentry> + <term><varname>types.loeOf</varname> <replaceable>t</replaceable></term> + <listitem><para>A list or an element of <replaceable>t</replaceable> type. + Multiple definitions are merged according to the + values.</para></listitem> + </varlistentry> + <varlistentry> + <term><varname>types.nullOr</varname> <replaceable>t</replaceable></term> + <listitem><para><literal>null</literal> or type + <replaceable>t</replaceable>. Multiple definitions are merged according + to type <replaceable>t</replaceable>.</para></listitem> + </varlistentry> + <varlistentry> + <term><varname>types.uniq</varname> <replaceable>t</replaceable></term> + <listitem><para>Ensures that type <replaceable>t</replaceable> cannot be + merged. It is used to ensure option definitions are declared only + once.</para></listitem> + </varlistentry> + <varlistentry> + <term><varname>types.enum</varname> <replaceable>l</replaceable></term> + <listitem><para>One element of the list <replaceable>l</replaceable>, e.g. + <literal>types.enum [ "left" "right" ]</literal>. Multiple definitions + cannot be merged</para></listitem> + </varlistentry> + <varlistentry> + <term><varname>types.either</varname> <replaceable>t1</replaceable> + <replaceable>t2</replaceable></term> + <listitem><para>Type <replaceable>t1</replaceable> or type + <replaceable>t2</replaceable>, e.g. <literal>with types; either int + str</literal>. Multiple definitions cannot be + merged.</para></listitem> + </varlistentry> + <varlistentry> + <term><varname>types.submodule</varname> <replaceable>o</replaceable></term> + <listitem><para>A set of sub options <replaceable>o</replaceable>. + <replaceable>o</replaceable> can be an attribute set or a function + returning an attribute set. Submodules are used in composed types to + create modular options. Submodule are detailed in <xref + linkend='section-option-types-submodule' />.</para></listitem> + </varlistentry> +</variablelist> + +</section> + +<section xml:id='section-option-types-submodule'><title>Submodule</title> + + <para>Submodule is a very powerful type that defines a set of sub-options that + are handled like a separate module. + It is especially interesting when used with composed types like + <literal>attrsOf</literal> or <literal>listOf</literal>.</para> + + <para>The submodule type take a parameter <replaceable>o</replaceable>, that + should be a set, or a function returning a set with an + <literal>options</literal> key defining the sub-options. + The option set can be defined directly (<xref linkend='ex-submodule-direct' + />) or as reference (<xref linkend='ex-submodule-reference' />).</para> + + <para>Submodule option definitions are type-checked accordingly to the options + declarations. It is possible to declare submodule options inside a submodule + sub-options for even higher modularity.</para> + +<example xml:id='ex-submodule-direct'><title>Directly defined submodule</title> +<screen> +options.mod = mkOption { + name = "mod"; + description = "submodule example"; + type = with types; listOf (submodule { + options = { + foo = mkOption { + type = int; + }; + bar = mkOption { + type = str; + }; + }; + }); +};</screen></example> + +<example xml:id='ex-submodule-reference'><title>Submodule defined as a + reference</title> +<screen> +let + modOptions = { + options = { + foo = mkOption { + type = int; + }; + bar = mkOption { + type = int; + }; + }; + }; +in +options.mod = mkOption { + description = "submodule example"; + type = with types; listOf (submodule modOptions); +};</screen></example> + + +<section><title>Composed with <literal>listOf</literal></title> + + <para>When composed with <literal>listOf</literal>, submodule allows multiple + definitions of the submodule option set.</para> + +<example xml:id='ex-submodule-listof-declaration'><title>Declaration of a list + of submodules</title> +<screen> +options.mod = mkOption { + description = "submodule example"; + type = with types; listOf (submodule { + options = { + foo = mkOption { + type = int; + }; + bar = mkOption { + type = str; + }; + }; + }); +};</screen></example> + +<example xml:id='ex-submodule-listof-definition'><title>Definition of a list of + submodules</title> +<screen> +config.mod = [ + { foo = 1; bar = "one"; } + { foo = 2; bar = "two"; } +];</screen></example> + +</section> + + +<section><title>Composed with <literal>attrsOf</literal></title> + + <para>When composed with <literal>attrsOf</literal>, submodule allows multiple + named definitions of the submodule option set.</para> + +<example xml:id='ex-submodule-attrsof-declaration'><title>Declaration of + attribute sets of submodules</title> +<screen> +options.mod = mkOption { + description = "submodule example"; + type = with types; attrsOf (submodule { + options = { + foo = mkOption { + type = int; + }; + bar = mkOption { + type = str; + }; + }; + }); +};</screen></example> + +<example xml:id='ex-submodule-attrsof-definition'><title>Declaration of + attribute sets of submodules</title> +<screen> +config.mod.one = { foo = 1; bar = "one"; }; +config.mod.two = { foo = 2; bar = "two"; };</screen></example> + +</section> +</section> + +<section><title>Extending types</title> + + <para>Types are mainly characterized by their <literal>check</literal> and + <literal>merge</literal> functions.</para> + +<variablelist> + <varlistentry> + <term><varname>check</varname></term> + <listitem><para>The function to type check the value. Takes a value as + parameter and return a boolean. + It is possible to extend a type check with the + <literal>addCheck</literal> function (<xref + linkend='ex-extending-type-check-1' />), or to fully override the + check function (<xref linkend='ex-extending-type-check-2' />).</para> + +<example xml:id='ex-extending-type-check-1'><title>Adding a type check</title> +<screen> +byte = mkOption { + description = "An integer between 0 and 255."; + type = addCheck (x: x >= 0 && x <= 255) types.int; +};</screen></example> + +<example xml:id='ex-extending-type-check-2'><title>Overriding a type + check</title> +<screen> +nixThings = mkOption { + description = "words that start with 'nix'"; + type = types.str // { + check = (x: lib.hasPrefix "nix" x) + }; +};</screen></example> + </listitem> + </varlistentry> + <varlistentry> + <term><varname>merge</varname></term> + <listitem><para>Function to merge the options values when multiple values + are set. +The function takes two parameters, <literal>loc</literal> the option path as a +list of strings, and <literal>defs</literal> the list of defined values as a +list. +It is possible to override a type merge function for custom +needs.</para></listitem> + </varlistentry> +</variablelist> + +</section> + +<section><title>Custom Types</title> + +<para>Custom types can be created with the <literal>mkOptionType</literal> + function. +As type creation includes some more complex topics such as submodule handling, +it is recommended to get familiar with <filename + xlink:href="https://github.com/NixOS/nixpkgs/blob/master/lib/types.nix">types.nix</filename> +code before creating a new type.</para> + +<para>The only required parameter is <literal>name</literal>.</para> + +<variablelist> + <varlistentry> + <term><varname>name</varname></term> + <listitem><para>A string representation of the type function name, name + usually changes accordingly parameters passed to + types.</para></listitem> + </varlistentry> + <varlistentry> + <term><varname>check</varname></term> + <listitem><para>A function to type check the definition value. Takes the + definition value as a parameter and returns a boolean indicating the + type check result, <literal>true</literal> for success and + <literal>false</literal> for failure.</para></listitem> + </varlistentry> + <varlistentry> + <term><varname>merge</varname></term> + <listitem><para>A function to merge multiple definitions values. Takes two + parameters:</para> + <variablelist> + <varlistentry> + <term><replaceable>loc</replaceable></term> + <listitem><para>The option path as a list of strings, e.g. + <literal>["boot" "loader "grub" + "enable"]</literal>.</para></listitem> + </varlistentry> + <varlistentry> + <term><replaceable>defs</replaceable></term> + <listitem><para>The list of sets of defined <literal>value</literal> + and <literal>file</literal> where the value was defined, e.g. + <literal>[ { file = "/foo.nix"; value = 1; } { file = "/bar.nix"; + value = 2 } ]</literal>. The <literal>merge</literal> function + should return the merged value or throw an error in case the + values are impossible or not meant to be merged.</para></listitem> + </varlistentry> + </variablelist> + </listitem> + </varlistentry> + <varlistentry> + <term><varname>getSubOptions</varname></term> + <listitem><para>For composed types that can take a submodule as type + parameter, this function generate sub-options documentation. It takes + the current option prefix as a list and return the set of sub-options. + Usually defined in a recursive manner by adding a term to the prefix, + e.g. <literal>prefix: elemType.getSubOptions (prefix ++ + [<replaceable>"prefix"</replaceable>])</literal> where + <replaceable>"prefix"</replaceable> is the newly added + prefix.</para></listitem> + </varlistentry> + <varlistentry> + <term><varname>getSubModules</varname></term> + <listitem><para>For composed types that can take a submodule as type + parameter, this function should return the type parameters submodules. + If the type parameter is called <literal>elemType</literal>, the + function should just recursively look into submodules by returning + <literal>elemType.getSubModules;</literal>.</para></listitem> + </varlistentry> + <varlistentry> + <term><varname>substSubModules</varname></term> + <listitem><para>For composed types that can take a submodule as type + parameter, this function can be used to substitute the parameter of a + submodule type. It takes a module as parameter and return the type with + the submodule options substituted. It is usally defined as a type + function call with a recursive call to + <literal>substSubModules</literal>, e.g for a type + <literal>composedType</literal> that take an <literal>elemtype</literal> + type parameter, this function should be defined as <literal>m: + composedType (elemType.substSubModules m)</literal>.</para></listitem> + </varlistentry> +</variablelist> + +</section> +</section> diff --git a/nixos/doc/manual/development/writing-modules.xml b/nixos/doc/manual/development/writing-modules.xml index a68b122ce022..ef6920160e6d 100644 --- a/nixos/doc/manual/development/writing-modules.xml +++ b/nixos/doc/manual/development/writing-modules.xml @@ -176,6 +176,7 @@ in { </example> <xi:include href="option-declarations.xml" /> +<xi:include href="option-types.xml" /> <xi:include href="option-def.xml" /> <xi:include href="meta-attributes.xml" />