DevelopmentThis chapter has some random notes on hacking on
NixOS.Extending NixOSA 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:
kernelThe kernel.initialRamdiskThe initial ramdisk (initrd) for this configuration.bootStage1The stage 1 (initrd) init script.bootStage2The stage 2 init script.etcThe statically computed parts of /etc.upstartJobsAn 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 installerBuilding, 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-installTesting the initrdA 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