1{ config, lib, pkgs, modules, baseModules, ... }:
2
3with lib;
4
5let
6
7
8 # This attribute is responsible for creating boot entries for
9 # child configuration. They are only (directly) accessible
10 # when the parent configuration is boot default. For example,
11 # you can provide an easy way to boot the same configuration
12 # as you use, but with another kernel
13 # !!! fix this
14 cloner = inheritParent: list:
15 map (childConfig:
16 (import ../../../lib/eval-config.nix {
17 inherit baseModules;
18 modules =
19 (optionals inheritParent modules)
20 ++ [ ./no-clone.nix ]
21 ++ [ childConfig ];
22 }).config.system.build.toplevel
23 ) list;
24
25 children =
26 cloner false config.nesting.children
27 ++ cloner true config.nesting.clone;
28
29
30 systemBuilder =
31 let
32 kernelPath = "${config.boot.kernelPackages.kernel}/" +
33 "${config.system.boot.loader.kernelFile}";
34 in ''
35 mkdir $out
36
37 # Containers don't have their own kernel or initrd. They boot
38 # directly into stage 2.
39 ${optionalString (!config.boot.isContainer) ''
40 if [ ! -f ${kernelPath} ]; then
41 echo "The bootloader cannot find the proper kernel image."
42 echo "(Expecting ${kernelPath})"
43 false
44 fi
45
46 ln -s ${kernelPath} $out/kernel
47 ln -s ${config.system.modulesTree} $out/kernel-modules
48
49 echo -n "$kernelParams" > $out/kernel-params
50
51 ln -s ${config.system.build.initialRamdisk}/initrd $out/initrd
52
53 ln -s ${config.hardware.firmware}/lib/firmware $out/firmware
54 ''}
55
56 echo "$activationScript" > $out/activate
57 substituteInPlace $out/activate --subst-var out
58 chmod u+x $out/activate
59 unset activationScript
60
61 cp ${config.system.build.bootStage2} $out/init
62 substituteInPlace $out/init --subst-var-by systemConfig $out
63
64 ln -s ${config.system.build.etc}/etc $out/etc
65 ln -s ${config.system.path} $out/sw
66 ln -s "$systemd" $out/systemd
67
68 echo -n "$configurationName" > $out/configuration-name
69 echo -n "systemd ${toString config.systemd.package.interfaceVersion}" > $out/init-interface-version
70 echo -n "$nixosVersion" > $out/nixos-version
71 echo -n "$system" > $out/system
72
73 mkdir $out/fine-tune
74 childCount=0
75 for i in $children; do
76 childCount=$(( childCount + 1 ))
77 ln -s $i $out/fine-tune/child-$childCount
78 done
79
80 mkdir $out/bin
81 substituteAll ${./switch-to-configuration.pl} $out/bin/switch-to-configuration
82 chmod +x $out/bin/switch-to-configuration
83
84 echo -n "${toString config.system.extraDependencies}" > $out/extra-dependencies
85
86 ${config.system.extraSystemBuilderCmds}
87 '';
88
89 # Handle assertions
90
91 failed = map (x: x.message) (filter (x: !x.assertion) config.assertions);
92
93 showWarnings = res: fold (w: x: builtins.trace "[1;31mwarning: ${w}[0m" x) res config.warnings;
94
95 # Putting it all together. This builds a store path containing
96 # symlinks to the various parts of the built configuration (the
97 # kernel, systemd units, init scripts, etc.) as well as a script
98 # `switch-to-configuration' that activates the configuration and
99 # makes it bootable.
100 baseSystem = showWarnings (
101 if [] == failed then pkgs.stdenv.mkDerivation {
102 name = "nixos-${config.system.nixosVersion}";
103 preferLocalBuild = true;
104 allowSubstitutes = false;
105 buildCommand = systemBuilder;
106
107 inherit (pkgs) utillinux coreutils;
108 systemd = config.systemd.package;
109
110 inherit children;
111 kernelParams = config.boot.kernelParams;
112 installBootLoader =
113 config.system.build.installBootLoader
114 or "echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2; true";
115 activationScript = config.system.activationScripts.script;
116 nixosVersion = config.system.nixosVersion;
117
118 configurationName = config.boot.loader.grub.configurationName;
119
120 # Needed by switch-to-configuration.
121 perl = "${pkgs.perl}/bin/perl -I${pkgs.perlPackages.FileSlurp}/lib/perl5/site_perl";
122 } else throw "\nFailed assertions:\n${concatStringsSep "\n" (map (x: "- ${x}") failed)}");
123
124 # Replace runtime dependencies
125 system = fold ({ oldDependency, newDependency }: drv:
126 pkgs.replaceDependency { inherit oldDependency newDependency drv; }
127 ) baseSystem config.system.replaceRuntimeDependencies;
128
129in
130
131{
132 options = {
133
134 system.build = mkOption {
135 internal = true;
136 default = {};
137 description = ''
138 Attribute set of derivations used to setup the system.
139 '';
140 };
141
142 nesting.children = mkOption {
143 default = [];
144 description = ''
145 Additional configurations to build.
146 '';
147 };
148
149 nesting.clone = mkOption {
150 default = [];
151 description = ''
152 Additional configurations to build based on the current
153 configuration which then has a lower priority.
154 '';
155 };
156
157 system.boot.loader.id = mkOption {
158 internal = true;
159 default = "";
160 description = ''
161 Id string of the used bootloader.
162 '';
163 };
164
165 system.boot.loader.kernelFile = mkOption {
166 internal = true;
167 default = pkgs.stdenv.platform.kernelTarget;
168 type = types.str;
169 description = ''
170 Name of the kernel file to be passed to the bootloader.
171 '';
172 };
173
174 system.copySystemConfiguration = mkOption {
175 type = types.bool;
176 default = false;
177 description = ''
178 If enabled, copies the NixOS configuration file
179 <literal>$NIXOS_CONFIG</literal> (usually
180 <filename>/etc/nixos/configuration.nix</filename>)
181 to the system store path.
182 '';
183 };
184
185 system.extraSystemBuilderCmds = mkOption {
186 type = types.lines;
187 internal = true;
188 default = "";
189 description = ''
190 This code will be added to the builder creating the system store path.
191 '';
192 };
193
194 system.extraDependencies = mkOption {
195 type = types.listOf types.package;
196 default = [];
197 description = ''
198 A list of packages that should be included in the system
199 closure but not otherwise made available to users. This is
200 primarily used by the installation tests.
201 '';
202 };
203
204 system.replaceRuntimeDependencies = mkOption {
205 default = [];
206 example = lib.literalExample "[ ({ original = pkgs.openssl; replacement = pkgs.callPackage /path/to/openssl { ... }; }) ]";
207 type = types.listOf (types.submodule (
208 { options, ... }: {
209 options.original = mkOption {
210 type = types.package;
211 description = "The original package to override.";
212 };
213
214 options.replacement = mkOption {
215 type = types.package;
216 description = "The replacement package.";
217 };
218 })
219 );
220 apply = map ({ original, replacement, ... }: {
221 oldDependency = original;
222 newDependency = replacement;
223 });
224 description = ''
225 List of packages to override without doing a full rebuild.
226 The original derivation and replacement derivation must have the same
227 name length, and ideally should have close-to-identical directory layout.
228 '';
229 };
230
231 };
232
233
234 config = {
235
236 system.extraSystemBuilderCmds =
237 optionalString
238 config.system.copySystemConfiguration
239 "cp ${maybeEnv "NIXOS_CONFIG" "/etc/nixos/configuration.nix"} $out";
240
241 system.build.toplevel = system;
242
243 };
244
245}