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 systemBuilder =
30 let
31 kernelPath = "${config.boot.kernelPackages.kernel}/" +
32 "${config.system.boot.loader.kernelFile}";
33 initrdPath = "${config.system.build.initialRamdisk}/" +
34 "${config.system.boot.loader.initrdFile}";
35 in ''
36 mkdir $out
37
38 # Containers don't have their own kernel or initrd. They boot
39 # directly into stage 2.
40 ${optionalString (!config.boot.isContainer) ''
41 if [ ! -f ${kernelPath} ]; then
42 echo "The bootloader cannot find the proper kernel image."
43 echo "(Expecting ${kernelPath})"
44 false
45 fi
46
47 ln -s ${kernelPath} $out/kernel
48 ln -s ${config.system.modulesTree} $out/kernel-modules
49 ${optionalString (pkgs.stdenv.hostPlatform.platform.kernelDTB or false) ''
50 ln -s ${config.boot.kernelPackages.kernel}/dtbs $out/dtbs
51 ''}
52
53 echo -n "$kernelParams" > $out/kernel-params
54
55 ln -s ${initrdPath} $out/initrd
56
57 ln -s ${config.system.build.initialRamdiskSecretAppender}/bin/append-initrd-secrets $out
58
59 ln -s ${config.hardware.firmware}/lib/firmware $out/firmware
60 ''}
61
62 echo "$activationScript" > $out/activate
63 substituteInPlace $out/activate --subst-var out
64 chmod u+x $out/activate
65 unset activationScript
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 "${pkgs.stdenv.hostPlatform.system}" > $out/system
78
79 mkdir $out/fine-tune
80 childCount=0
81 for i in $children; do
82 childCount=$(( childCount + 1 ))
83 ln -s $i $out/fine-tune/child-$childCount
84 done
85
86 mkdir $out/bin
87 export localeArchive="${config.i18n.glibcLocales}/lib/locale/locale-archive"
88 substituteAll ${./switch-to-configuration.pl} $out/bin/switch-to-configuration
89 chmod +x $out/bin/switch-to-configuration
90
91 echo -n "${toString config.system.extraDependencies}" > $out/extra-dependencies
92
93 ${config.system.extraSystemBuilderCmds}
94 '';
95
96 # Handle assertions
97
98 failed = map (x: x.message) (filter (x: !x.assertion) config.assertions);
99
100 showWarnings = res: fold (w: x: builtins.trace "[1;31mwarning: ${w}[0m" x) res config.warnings;
101
102 # Putting it all together. This builds a store path containing
103 # symlinks to the various parts of the built configuration (the
104 # kernel, systemd units, init scripts, etc.) as well as a script
105 # `switch-to-configuration' that activates the configuration and
106 # makes it bootable.
107 baseSystem = showWarnings (
108 if [] == failed then pkgs.stdenvNoCC.mkDerivation {
109 name = let hn = config.networking.hostName;
110 nn = if (hn != "") then hn else "unnamed";
111 in "nixos-system-${nn}-${config.system.nixos.label}";
112 preferLocalBuild = true;
113 allowSubstitutes = false;
114 buildCommand = systemBuilder;
115
116 inherit (pkgs) utillinux coreutils;
117 systemd = config.systemd.package;
118
119 inherit children;
120 kernelParams = config.boot.kernelParams;
121 installBootLoader =
122 config.system.build.installBootLoader
123 or "echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2; true";
124 activationScript = config.system.activationScripts.script;
125 nixosLabel = config.system.nixos.label;
126
127 configurationName = config.boot.loader.grub.configurationName;
128
129 # Needed by switch-to-configuration.
130
131 perl = "${pkgs.perl}/bin/perl " + (concatMapStringsSep " " (lib: "-I${lib}/${pkgs.perl.libPrefix}") (with pkgs.perlPackages; [ FileSlurp NetDBus XMLParser XMLTwig ]));
132 } else throw "\nFailed assertions:\n${concatStringsSep "\n" (map (x: "- ${x}") failed)}");
133
134 # Replace runtime dependencies
135 system = fold ({ oldDependency, newDependency }: drv:
136 pkgs.replaceDependency { inherit oldDependency newDependency drv; }
137 ) baseSystem config.system.replaceRuntimeDependencies;
138
139in
140
141{
142 options = {
143
144 system.build = mkOption {
145 internal = true;
146 default = {};
147 type = types.attrs;
148 description = ''
149 Attribute set of derivations used to setup the system.
150 '';
151 };
152
153 nesting.children = mkOption {
154 default = [];
155 description = ''
156 Additional configurations to build.
157 '';
158 };
159
160 nesting.clone = mkOption {
161 default = [];
162 description = ''
163 Additional configurations to build based on the current
164 configuration which then has a lower priority.
165
166 To switch to a cloned configuration (e.g. <literal>child-1</literal>)
167 at runtime, run
168
169 <programlisting>
170 # sudo /run/current-system/fine-tune/child-1/bin/switch-to-configuration test
171 </programlisting>
172 '';
173 };
174
175 system.boot.loader.id = mkOption {
176 internal = true;
177 default = "";
178 description = ''
179 Id string of the used bootloader.
180 '';
181 };
182
183 system.boot.loader.kernelFile = mkOption {
184 internal = true;
185 default = pkgs.stdenv.hostPlatform.platform.kernelTarget;
186 type = types.str;
187 description = ''
188 Name of the kernel file to be passed to the bootloader.
189 '';
190 };
191
192 system.boot.loader.initrdFile = mkOption {
193 internal = true;
194 default = "initrd";
195 type = types.str;
196 description = ''
197 Name of the initrd file to be passed to the bootloader.
198 '';
199 };
200
201 system.copySystemConfiguration = mkOption {
202 type = types.bool;
203 default = false;
204 description = ''
205 If enabled, copies the NixOS configuration file
206 (usually <filename>/etc/nixos/configuration.nix</filename>)
207 and links it from the resulting system
208 (getting to <filename>/run/current-system/configuration.nix</filename>).
209 Note that only this single file is copied, even if it imports others.
210 '';
211 };
212
213 system.extraSystemBuilderCmds = mkOption {
214 type = types.lines;
215 internal = true;
216 default = "";
217 description = ''
218 This code will be added to the builder creating the system store path.
219 '';
220 };
221
222 system.extraDependencies = mkOption {
223 type = types.listOf types.package;
224 default = [];
225 description = ''
226 A list of packages that should be included in the system
227 closure but not otherwise made available to users. This is
228 primarily used by the installation tests.
229 '';
230 };
231
232 system.replaceRuntimeDependencies = mkOption {
233 default = [];
234 example = lib.literalExample "[ ({ original = pkgs.openssl; replacement = pkgs.callPackage /path/to/openssl { }; }) ]";
235 type = types.listOf (types.submodule (
236 { ... }: {
237 options.original = mkOption {
238 type = types.package;
239 description = "The original package to override.";
240 };
241
242 options.replacement = mkOption {
243 type = types.package;
244 description = "The replacement package.";
245 };
246 })
247 );
248 apply = map ({ original, replacement, ... }: {
249 oldDependency = original;
250 newDependency = replacement;
251 });
252 description = ''
253 List of packages to override without doing a full rebuild.
254 The original derivation and replacement derivation must have the same
255 name length, and ideally should have close-to-identical directory layout.
256 '';
257 };
258
259 };
260
261
262 config = {
263
264 system.extraSystemBuilderCmds =
265 optionalString
266 config.system.copySystemConfiguration
267 ''ln -s '${import ../../../lib/from-env.nix "NIXOS_CONFIG" <nixos-config>}' \
268 "$out/configuration.nix"
269 '';
270
271 system.build.toplevel = system;
272
273 };
274
275}