Development This chapter has some random notes on hacking on 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. A configuration file is the same as your own computer configuration. Usual configuration file {pkgs, config, ...}: ###### interface let inherit (pkgs.lib) mkOption; options = { services = { locate = { enable = mkOption { default = false; example = true; description = '' If enabled, NixOS will periodically update the database of files used by the locate command. ''; }; period = mkOption { default = "15 02 * * *"; description = '' This option defines (in the format used by cron) when the locate database is updated. The default is to update at 02:15 (at night) every day. ''; }; }; }; }; in ###### implementation let cfg = config.services.locate; inherit (pkgs.lib) mkIf mkThenElse; locatedb = "/var/cache/locatedb"; logfile = "/var/log/updatedb"; cmd = "root updatedb --localuser=nobody --output=${locatedb} > ${logfile}"; in mkIf cfg.enable { require = [ options # config.services.cron (import ../../upstart-jobs/cron.nix) ]; services = { cron = { systemCronJobs = mkThenElse { thenPart = "${cfg.period} root ${cmd}"; elsePart = ""; }; }; }; } 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 function that is 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 require is the only special option that exists. It is used to embed all option declarations that are required by your configuration file and it is also used to provide the options that you are declaring. This attribute is processed with pkgs.lib.uniqFlatten to collect all configuration files that are used by your system and it avoid the insertion of duplicated elements by comparing the configuration set codes. Currently, the file configuration.nix implicitly embeds system/options.nix. If you need a special configuration file, then you will have to add similar lines to your computer configuration. 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.
Building specific parts of NixOS $ nix-build /etc/nixos/nixos attr where attr is an attribute in /etc/nixos/nixos/default.nix. Attributes of interest include: kernel The kernel. initialRamdisk The initial ramdisk (initrd) for this configuration. bootStage1 The stage 1 (initrd) init script. bootStage2 The stage 2 init script. etc The statically computed parts of /etc. upstartJobs An attribute set containing the Upstart jobs. For instance, the sshd Upstart job can be built by doing nix-build /etc/nixos/nixos -A tests.upstartJobs.sshd.
Testing the installer Building, burning, and booting from an installation CD is rather tedious, so here is a quick way to see if the installer works properly: $ nix-build .../nixos/configuration/rescue-cd.nix -A system.nixosInstall $ dd if=/dev/zero of=diskimage seek=2G count=0 bs=1 $ yes | mke2fs -j diskimage $ mount -o loop diskimage /mnt $ ./result/bin/nixos-install
Testing the <literal>initrd</literal> A quick way to test whether the kernel and the initial ramdisk boot correctly is to use QEMU’s and options: $ nix-build /etc/nixos/nixos -A initialRamdisk -o initrd $ nix-build /etc/nixos/nixos -A kernel -o kernel $ qemu-system-x86_64 -kernel ./kernel/vmlinuz -initrd ./initrd/initrd -hda /dev/null