1{ config, lib, pkgs, modules, baseModules, specialArgs, ... }:
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 children = mapAttrs (childName: childConfig:
15 (import ../../../lib/eval-config.nix {
16 inherit lib baseModules specialArgs;
17 system = config.nixpkgs.initialSystem;
18 modules =
19 (optionals childConfig.inheritParentConfig modules)
20 ++ [ ./no-clone.nix ]
21 ++ [ childConfig.configuration ];
22 }).config.system.build.toplevel
23 ) config.specialisation;
24
25 systemBuilder =
26 let
27 kernelPath = "${config.boot.kernelPackages.kernel}/" +
28 "${config.system.boot.loader.kernelFile}";
29 initrdPath = "${config.system.build.initialRamdisk}/" +
30 "${config.system.boot.loader.initrdFile}";
31 in ''
32 mkdir $out
33
34 # Containers don't have their own kernel or initrd. They boot
35 # directly into stage 2.
36 ${optionalString (!config.boot.isContainer) ''
37 if [ ! -f ${kernelPath} ]; then
38 echo "The bootloader cannot find the proper kernel image."
39 echo "(Expecting ${kernelPath})"
40 false
41 fi
42
43 ln -s ${kernelPath} $out/kernel
44 ln -s ${config.system.modulesTree} $out/kernel-modules
45 ${optionalString (config.hardware.deviceTree.package != null) ''
46 ln -s ${config.hardware.deviceTree.package} $out/dtbs
47 ''}
48
49 echo -n "$kernelParams" > $out/kernel-params
50
51 ln -s ${initrdPath} $out/initrd
52
53 ln -s ${config.system.build.initialRamdiskSecretAppender}/bin/append-initrd-secrets $out
54
55 ln -s ${config.hardware.firmware}/lib/firmware $out/firmware
56 ''}
57
58 echo "$activationScript" > $out/activate
59 echo "$dryActivationScript" > $out/dry-activate
60 substituteInPlace $out/activate --subst-var out
61 substituteInPlace $out/dry-activate --subst-var out
62 chmod u+x $out/activate $out/dry-activate
63 unset activationScript dryActivationScript
64 ${pkgs.stdenv.shell} -n $out/activate
65 ${pkgs.stdenv.shell} -n $out/dry-activate
66
67 cp ${config.system.build.bootStage2} $out/init
68 substituteInPlace $out/init --subst-var-by systemConfig $out
69
70 ln -s ${config.system.build.etc}/etc $out/etc
71 ln -s ${config.system.path} $out/sw
72 ln -s "$systemd" $out/systemd
73
74 echo -n "$configurationName" > $out/configuration-name
75 echo -n "systemd ${toString config.systemd.package.interfaceVersion}" > $out/init-interface-version
76 echo -n "$nixosLabel" > $out/nixos-version
77 echo -n "${config.boot.kernelPackages.stdenv.hostPlatform.system}" > $out/system
78
79 mkdir $out/specialisation
80 ${concatStringsSep "\n"
81 (mapAttrsToList (name: path: "ln -s ${path} $out/specialisation/${name}") children)}
82
83 mkdir $out/bin
84 export localeArchive="${config.i18n.glibcLocales}/lib/locale/locale-archive"
85 substituteAll ${./switch-to-configuration.pl} $out/bin/switch-to-configuration
86 chmod +x $out/bin/switch-to-configuration
87 ${optionalString (pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) ''
88 if ! output=$($perl/bin/perl -c $out/bin/switch-to-configuration 2>&1); then
89 echo "switch-to-configuration syntax is not valid:"
90 echo "$output"
91 exit 1
92 fi
93 ''}
94
95 echo -n "${toString config.system.extraDependencies}" > $out/extra-dependencies
96
97 ${config.system.extraSystemBuilderCmds}
98 '';
99
100 # Putting it all together. This builds a store path containing
101 # symlinks to the various parts of the built configuration (the
102 # kernel, systemd units, init scripts, etc.) as well as a script
103 # `switch-to-configuration' that activates the configuration and
104 # makes it bootable.
105 baseSystem = pkgs.stdenvNoCC.mkDerivation {
106 name = "nixos-system-${config.system.name}-${config.system.nixos.label}";
107 preferLocalBuild = true;
108 allowSubstitutes = false;
109 buildCommand = systemBuilder;
110
111 inherit (pkgs) coreutils;
112 systemd = config.systemd.package;
113 shell = "${pkgs.bash}/bin/sh";
114 su = "${pkgs.shadow.su}/bin/su";
115 utillinux = pkgs.util-linux;
116
117 kernelParams = config.boot.kernelParams;
118 installBootLoader =
119 config.system.build.installBootLoader
120 or "echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2; true";
121 activationScript = config.system.activationScripts.script;
122 dryActivationScript = config.system.dryActivationScript;
123 nixosLabel = config.system.nixos.label;
124
125 configurationName = config.boot.loader.grub.configurationName;
126
127 # Needed by switch-to-configuration.
128 perl = pkgs.perl.withPackages (p: with p; [ FileSlurp NetDBus XMLParser XMLTwig ]);
129 };
130
131 # Handle assertions and warnings
132
133 failedAssertions = map (x: x.message) (filter (x: !x.assertion) config.assertions);
134
135 baseSystemAssertWarn = if failedAssertions != []
136 then throw "\nFailed assertions:\n${concatStringsSep "\n" (map (x: "- ${x}") failedAssertions)}"
137 else showWarnings config.warnings baseSystem;
138
139 # Replace runtime dependencies
140 system = foldr ({ oldDependency, newDependency }: drv:
141 pkgs.replaceDependency { inherit oldDependency newDependency drv; }
142 ) baseSystemAssertWarn config.system.replaceRuntimeDependencies;
143
144in
145
146{
147 imports = [
148 (mkRemovedOptionModule [ "nesting" "clone" ] "Use `specialisation.«name» = { inheritParentConfig = true; configuration = { ... }; }` instead.")
149 (mkRemovedOptionModule [ "nesting" "children" ] "Use `specialisation.«name».configuration = { ... }` instead.")
150 ];
151
152 options = {
153
154 system.build = mkOption {
155 internal = true;
156 default = {};
157 type = types.attrs;
158 description = ''
159 Attribute set of derivations used to setup the system.
160 '';
161 };
162
163 specialisation = mkOption {
164 default = {};
165 example = lib.literalExpression "{ fewJobsManyCores.configuration = { nix.buildCores = 0; nix.maxJobs = 1; }; }";
166 description = ''
167 Additional configurations to build. If
168 <literal>inheritParentConfig</literal> is true, the system
169 will be based on the overall system configuration.
170
171 To switch to a specialised configuration
172 (e.g. <literal>fewJobsManyCores</literal>) at runtime, run:
173
174 <screen>
175 <prompt># </prompt>sudo /run/current-system/specialisation/fewJobsManyCores/bin/switch-to-configuration test
176 </screen>
177 '';
178 type = types.attrsOf (types.submodule (
179 { ... }: {
180 options.inheritParentConfig = mkOption {
181 type = types.bool;
182 default = true;
183 description = "Include the entire system's configuration. Set to false to make a completely differently configured system.";
184 };
185
186 options.configuration = mkOption {
187 default = {};
188 description = "Arbitrary NixOS configuration options.";
189 };
190 })
191 );
192 };
193
194 system.boot.loader.id = mkOption {
195 internal = true;
196 default = "";
197 description = ''
198 Id string of the used bootloader.
199 '';
200 };
201
202 system.boot.loader.kernelFile = mkOption {
203 internal = true;
204 default = pkgs.stdenv.hostPlatform.linux-kernel.target;
205 type = types.str;
206 description = ''
207 Name of the kernel file to be passed to the bootloader.
208 '';
209 };
210
211 system.boot.loader.initrdFile = mkOption {
212 internal = true;
213 default = "initrd";
214 type = types.str;
215 description = ''
216 Name of the initrd file to be passed to the bootloader.
217 '';
218 };
219
220 system.copySystemConfiguration = mkOption {
221 type = types.bool;
222 default = false;
223 description = ''
224 If enabled, copies the NixOS configuration file
225 (usually <filename>/etc/nixos/configuration.nix</filename>)
226 and links it from the resulting system
227 (getting to <filename>/run/current-system/configuration.nix</filename>).
228 Note that only this single file is copied, even if it imports others.
229 '';
230 };
231
232 system.extraSystemBuilderCmds = mkOption {
233 type = types.lines;
234 internal = true;
235 default = "";
236 description = ''
237 This code will be added to the builder creating the system store path.
238 '';
239 };
240
241 system.extraDependencies = mkOption {
242 type = types.listOf types.package;
243 default = [];
244 description = ''
245 A list of packages that should be included in the system
246 closure but not otherwise made available to users. This is
247 primarily used by the installation tests.
248 '';
249 };
250
251 system.replaceRuntimeDependencies = mkOption {
252 default = [];
253 example = lib.literalExpression "[ ({ original = pkgs.openssl; replacement = pkgs.callPackage /path/to/openssl { }; }) ]";
254 type = types.listOf (types.submodule (
255 { ... }: {
256 options.original = mkOption {
257 type = types.package;
258 description = "The original package to override.";
259 };
260
261 options.replacement = mkOption {
262 type = types.package;
263 description = "The replacement package.";
264 };
265 })
266 );
267 apply = map ({ original, replacement, ... }: {
268 oldDependency = original;
269 newDependency = replacement;
270 });
271 description = ''
272 List of packages to override without doing a full rebuild.
273 The original derivation and replacement derivation must have the same
274 name length, and ideally should have close-to-identical directory layout.
275 '';
276 };
277
278 system.name = mkOption {
279 type = types.str;
280 default =
281 if config.networking.hostName == ""
282 then "unnamed"
283 else config.networking.hostName;
284 defaultText = literalExpression ''
285 if config.networking.hostName == ""
286 then "unnamed"
287 else config.networking.hostName;
288 '';
289 description = ''
290 The name of the system used in the <option>system.build.toplevel</option> derivation.
291 </para><para>
292 That derivation has the following name:
293 <literal>"nixos-system-''${config.system.name}-''${config.system.nixos.label}"</literal>
294 '';
295 };
296
297 };
298
299
300 config = {
301
302 system.extraSystemBuilderCmds =
303 optionalString
304 config.system.copySystemConfiguration
305 ''ln -s '${import ../../../lib/from-env.nix "NIXOS_CONFIG" <nixos-config>}' \
306 "$out/configuration.nix"
307 '';
308
309 system.build.toplevel = system;
310
311 };
312
313}