diff --git a/doc/manual/development.xml b/doc/manual/development.xml index 9d0b42c4349..3887904a92e 100644 --- a/doc/manual/development.xml +++ b/doc/manual/development.xml @@ -7,42 +7,209 @@ NixOS. + +
Extending NixOS -A unique syntax is used to express all system, hardware, computer and - service configurations. This syntax helps for reading and writing new - configuration files. It is coming with some extra strategies defined in - NixPkgs which are used to merge and evaluate all configuration files. +NixOS is based on a modular system for declarative configuration. + This system combines multiple modules to produce one + configuration. One of the module which compose your computer + configuration is /etc/nixos/configuration.nix. Other + modules are available under NixOS modules + directory - A configuration file is the same as your own computer - configuration. +A module is a file which handles one specific part of the + configuration. This part of the configuration could correspond to an + hardware, a service, network settings, or preferences. A module + configuration does not have to handle everything from scratch, it can base + its configuration on other configurations provided by other modules. Thus + a module can define options to setup its + configuration, and it can also declare options to be + fed by other modules. -Usual configuration file + + +A module is a file which contains a Nix + expression. This expression should be either an expression which gets + evaluated into an attribute set or a function which returns an attribute + set. + +When the expression is a function, it should expect only one argument + which is an attribute set containing an attribute + named config and another attribute + named pkgs. The config attribute + contains the result of the merge of all modules. This attribute is + evaluated lazily, such as any Nix expression. For more details on how + options are merged, see the details in . + The pkgs attribute + contains nixpkgs attribute set of packages. This + attribute is necessary for declaring options. + +Usual module content -{config, modulesPath, pkgs, ...}: +{config, pkgs, ...}: + +{ + imports = [ + + ]; + + options = { + + }; + + config = { + + }; +} + + + Illustrates + a module skeleton. + + + + This line makes the current Nix expression a function. This + line can be omitted if there is no reference to pkgs + and config inside the module. + + + + This list is used to enumerate path to other modules which are + declaring options used by the current module. In NixOS, default modules + are listed in the file modules/module-list.nix. + The default modules don't need to be added in the import list. + + + + This attribute set contains an attribute set of option + declaration. + + + + This attribute set contains an attribute set of option + definitions. If the module does not have any imported + modules or any option declarations, then this attribute set can be used + in place of its parent attribute set. This is a common case for simple + modules such + as /etc/nixos/configuration.nix. + + + + + + + +A module defines a configuration which would be + interpreted by other modules. To define a configuration, a module needs + to provide option definitions. An option definition is a simple + attribute assignment. + +Option definitions are made in a declarative manner. Without + properties, options will always be defined with the same value. To + introduce more flexibility in the system, option definitions are guarded + by properties. + +Properties are means to introduce conditional values inside option + definitions. This conditional values can be distinguished in two + categories. The condition which are local to the current configuration + and conditions which are dependent on others configurations. Local + properties are mkIf, mkAlways + and mkAssert. Global properties + are mkOverride, mkDefault + and mkOrder. + +mkIf is used to remove the option definitions which + are below it if the condition is evaluated to + false. mkAssert expects the condition to be evaluated + to true otherwise it raises an error message. mkAlways + is used to ignore all the mkIf + and mkAssert which have been made + previously. mkAlways and mkAssert + are often used together to set an option value and to ensure that it has + not been masked by another one. + +mkOverride is used to mask previous definitions if + the current value has a lower mask number. The mask value is 100 (default) + for any option definition which does not use this property. + Thus, mkDefault is just a short-cut with a higher mask + (1000) than the default mask value. This means that a module can set an + option definition as a preference, and still let another module defining + it with a different value without using any property. + +mkOrder is used to sort definitions based on the + rank number. The rank number will sort all options definitions before + giving the sorted list of option definition to the merge function defined + in the option declaration. A lower rank will move the definition to the + beginning and a higher rank will move the option toward the end. The + default rank is 100. + + + +A module may declare options which are used by + other module to change the configuration provided by the current module. + Changes to the option definitions are made with properties which are using + values extracted from the result of the merge of all modules + (the config argument). + +The config argument reproduce the same hierarchy of + all options declared in all modules. For each option, the result of the + option is available, it is either the default value or the merge of all + definitions of the option. + +Options are declared with the + function pkgs.lib.mkOption. This function expects an + attribute set which at least provides a description. A default value, an + example, a type, a merge function and a post-process function can be + added. + +Types are used to provide a merge strategy for options and to ensure + the type of each option definitions. They are defined + in pkgs.lib.types. + +The merge function expects a list of option definitions and merge + them to obtain one result of the same type. + +The post-process function (named apply) takes the + result of the merge or of the default value, and produce an output which + could have a different type than the type expected by the option. + + + +Locate Module Example + +{config, pkgs, ...}: + +with pkgs.lib; let - inherit (pkgs.lib) mkOption mkIf mkThenElse types; - - cfg = config.services.locate; + cfg = config.services.locate; locatedb = "/var/cache/locatedb"; logfile = "/var/log/updatedb"; - cmd = "root updatedb --localuser=nobody --output=${locatedb} > ${logfile}"; + cmd =''root updatedb --localuser=nobody --output=${locatedb} > ${logfile}''; + + mkCheck = x: + mkIf cfg.enable ( + mkAssert config.services.cron.enable '' + The cron daemon is not enabled, required by services.locate.enable. + '' + x + ) in { - imports = [ - (modulesPath + /services/scheduling/cron.nix) + imports = [ + /etc/nixos/nixos/modules/services/scheduling/cron.nix ]; - options = { + options = { services.locate = { enable = mkOption { default = false; example = true; - type = types.bool; + type = with types; bool; description = '' If enabled, NixOS will periodically update the database of files used by the locate command. @@ -51,7 +218,7 @@ in period = mkOption { default = "15 02 * * *"; - type = types.uniq type.string; + type = with types; uniq string; description = '' This option defines (in the format used by cron) when the locate database is updated. @@ -61,165 +228,30 @@ in }; }; - config = mkIf cfg.enable { + config = mkCheck { services.cron = { - systemCronJobs = mkThenElse { - thenPart = "${cfg.period} root ${cmd}"; - elsePart = ""; - }; + enable = mkAlways cfg.enable; + systemCronJobs = "${cfg.period} root ${cmd}"; }; }; } - shows the configuration - file for the locate service which uses cron to update the - database at some dates which can be defined by the user. This nix - expression is coming - from upstart-jobs/cron/locate.nix. It shows a simple - example of a service that can be either distributed on many computer that - are using the same configuration or to shared with the community. This - file is divided in two with the interface and the implementation. Both - the interface and the implementation declare a configuration - set. - - - - - - This line declares the arguments of the configuration file. You - can omit this line if there is no reference to pkgs - and config inside the configuration file. - - The argument pkgs refers to NixPkgs and allow - you to access all attributes contained inside it. In this - example pkgs is used to retrieve common functions to - ease the writing of configuration files - like mkOption, mkIf - and mkThenElse. - - The argument config corresponds to the whole - NixOS configuration. This is a set which is build by merging all - configuration files imported to set up the system. Thus all options - declared are contained inside this variable. In this - example config is used to retrieve the status of - the enable flag. The important point of this - argument is that it contains either the result of the merge of different - settings or the default value, therefore you cannot assume - that is always false - because it may have been defined in another configuration file. - - - - - - This line is used to import a functions that are useful for - writing this configuration file. - - - - - - The variable options is - a configuration set which is only used to declare - options with the function mkOption imported - from pkgs/lib/default.nix. Options may contained - any attribute but only the following have a special - meaning: default, example, - description, merge - and apply. - - The merge attribute is used to merge all values - defined in all configuration files and this function return a value - which has the same type as the default value. If the merge function is - not defined, then a default function - (pkgs.lib.mergeDefaultOption) is used to merge - values. The merge attribute is a function which - expect two arguments: the location of the option and the list of values - which have to be merged. - - The apply attribute is a function used to - process the option. Thus the value return - by would be - the result of the apply function called with either - the default value or the result of - the merge function. - - - - - - This line is a common trick used to reduce the amount of - writing. In this case cfg is just a sugar over - - - - - - - This line is used to declare a special IF - statement. If you had put a usual IF statement - here, with the same condition, then you will get an infinite loop. The - reason is that your condition ask for the value of the - option but in order to - get this value you have to evaluate all configuration sets including the - configuration set contained inside your file. - - To remove this extra complexity, mkIf has been - introduced to get rid of possible infinite loop and to factor your - writing. - - - - - - The attribute imports contains a list of other - module location. These modules should provide option declarations for - the current module in order to be evaluated safely. - - When a dependence on a NixOS module has to be made, then you - should use the argument modulesPath to prefix the - location of your NixOS repository. - - - - - - The attribute config, which should not be - confused with the argument of the same name, contains a set of option - definitions defined by this module. When there is - neither imports nor options, then - this attribute set can be return without any enclosing. This feature - allow you to write your configuration.nix as a - module which does not contains any option declarations. - - As mkIf does not need - any then part or else part, - then you can specify one on each option definition with the - function mkThenElse. - - To avoid a lot of mkThenElse with empty - else part, a sugar has been added to infer the - corresponding empty-value of your option when the - function mkThenElse is not used. - - If your then part - and else part are identical, then you should use - the function mkAlways to ignore the condition. - - If you need to add another condition, then you can add mkIf to on - the appropriate location. Thus the then part will - only be used if all conditions declared with mkIf - are satisfied. - - - - - - + illustrates a module which handles + the regular update of the database which index all files on the file + system. This modules has option definitions to rely on the cron service + to run the command at predefined dates. In addition, this modules + provides option declarations to enable the indexing and to use different + period of time to run the indexing. Properties are used to prevent + ambiguous definitions of option (enable locate service and disable cron + services) and to ensure that no options would be defined if the locate + service is not enabled.
+ + +
Building specific parts of NixOS @@ -271,6 +303,8 @@ config.system.build
+ +
Building your own NixOS CD @@ -323,6 +357,8 @@ $ ./result/bin/nixos-install
+ +
Testing the <literal>initrd</literal>