{ config, pkgs, modules, baseModules, ... }:
with pkgs.lib;
let
options = {
system.build = mkOption {
default = {};
description = ''
Attribute set of derivations used to setup the system.
'';
};
nesting.children = mkOption {
default = [];
description = ''
Additional configurations to build.
'';
};
nesting.clone = mkOption {
default = [];
description = ''
Additional configurations to build based on the current
configuration which is has a lower priority.
'';
};
system.boot.loader.id = mkOption {
default = "";
description = ''
Id string of the used bootloader.
'';
};
system.boot.loader.kernelFile = mkOption {
default = pkgs.stdenv.platform.kernelTarget;
description = ''
Name of the kernel file to be passed to the bootloader.
'';
};
system.copySystemConfiguration = mkOption {
default = false;
description = ''
If enabled, copies the NixOS configuration file
$NIXOS_CONFIG (usually
/etc/nixos/configuration.nix)
to the system store path.
'';
};
system.extraSystemBuilderCmds = mkOption {
default = "";
internal = true;
merge = concatStringsSep "\n";
description = ''
This code will be added to the builder creating the system store path.
'';
};
};
# This attribute is responsible for creating boot entries for
# child configuration. They are only (directly) accessible
# when the parent configuration is boot default. For example,
# you can provide an easy way to boot the same configuration
# as you use, but with another kernel
# !!! fix this
cloner = inheritParent: list: with pkgs.lib;
map (childConfig:
(import ../../../lib/eval-config.nix {
inherit baseModules;
modules =
(optionals inheritParent modules)
++ [ ./no-clone.nix ]
++ [ childConfig ];
}).config.system.build.toplevel
) list;
children =
cloner false config.nesting.children
++ cloner true config.nesting.clone;
systemBuilder =
let
kernelPath = "${config.boot.kernelPackages.kernel}/" +
"${config.system.boot.loader.kernelFile}";
in ''
mkdir $out
if [ ! -f ${kernelPath} ]; then
echo "The bootloader cannot find the proper kernel image."
echo "(Expecting ${kernelPath})"
false
fi
ln -s ${kernelPath} $out/kernel
ln -s ${config.system.modulesTree} $out/kernel-modules
if [ -n "$grub" ]; then
ln -s $grub $out/grub
fi
ln -s ${config.system.build.initialRamdisk}/initrd $out/initrd
echo "$activationScript" > $out/activate
substituteInPlace $out/activate --subst-var out
chmod u+x $out/activate
unset activationScript
cp ${config.system.build.bootStage2} $out/init
substituteInPlace $out/init --subst-var-by systemConfig $out
ln -s ${config.system.build.etc}/etc $out/etc
ln -s ${config.system.path} $out/sw
ln -s ${config.system.build.upstart} $out/upstart
ln -s ${config.hardware.firmware} $out/firmware
echo -n "$kernelParams" > $out/kernel-params
echo -n "$configurationName" > $out/configuration-name
echo -n "${toString config.system.build.upstart.interfaceVersion}" > $out/upstart-interface-version
echo -n "$nixosVersion" > $out/nixos-version
mkdir $out/fine-tune
childCount=0
for i in $children; do
childCount=$(( childCount + 1 ))
ln -s $i $out/fine-tune/child-$childCount
done
mkdir $out/bin
substituteAll ${./switch-to-configuration.sh} $out/bin/switch-to-configuration
chmod +x $out/bin/switch-to-configuration
${config.system.extraSystemBuilderCmds}
'';
# Putting it all together. This builds a store path containing
# symlinks to the various parts of the built configuration (the
# kernel, the Upstart services, the init scripts, etc.) as well as a
# script `switch-to-configuration' that activates the configuration
# and makes it bootable.
system = pkgs.stdenv.mkDerivation {
name = "nixos-${config.system.nixosVersion}";
preferLocalBuild = true;
buildCommand = systemBuilder;
inherit children;
kernelParams =
config.boot.kernelParams ++ config.boot.extraKernelParams;
menuBuilder = config.system.build.menuBuilder or "true";
initScriptBuilder = config.system.build.initScriptBuilder;
activationScript = config.system.activationScripts.script;
nixosVersion = config.system.nixosVersion;
jobs = map (j: j.name) (attrValues config.jobs);
# Pass the names of all Upstart tasks to the activation script.
tasks = attrValues (mapAttrs (n: v: if v.task then ["[${v.name}]=1"] else []) config.jobs);
# Pass the names of all Upstart jobs that shouldn't be restarted
# to the activation script.
noRestartIfChanged = attrValues (mapAttrs (n: v: if v.restartIfChanged then [] else ["[${v.name}]=1"]) config.jobs);
# Most of these are needed by grub-install.
path =
[ pkgs.coreutils
pkgs.gnused
pkgs.gnugrep
pkgs.findutils
pkgs.diffutils
config.system.build.upstart # for initctl
];
# Boot loaders
bootLoader = config.system.boot.loader.id;
grub =
if config.boot.loader.grub.enable
then config.system.build.grub
else null;
grubVersion =
if config.boot.loader.grub.enable
then (builtins.parseDrvName config.system.build.grub.name).version
else "";
grubDevices =
let
wrapQuotes = s: "\"" + s + "\"";
in map wrapQuotes config.boot.loader.grub.devices;
configurationName = config.boot.loader.grub.configurationName;
};
in {
require = [options];
system.extraSystemBuilderCmds =
optionalString
config.system.copySystemConfiguration
"cp ${maybeEnv "NIXOS_CONFIG" "/etc/nixos/configuration.nix"} $out";
system.build.toplevel = system;
}