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 ${optionalString (pkgs.stdenv.platform.kernelDTB or false) ''
49 ln -s ${config.boot.kernelPackages.kernel}/dtbs $out/dtbs
50 ''}
51
52 echo -n "$kernelParams" > $out/kernel-params
53
54 ln -s ${config.system.build.initialRamdisk}/initrd $out/initrd
55
56 ln -s ${config.system.build.initialRamdiskSecretAppender}/bin/append-initrd-secrets $out
57
58 ln -s ${config.hardware.firmware}/lib/firmware $out/firmware
59 ''}
60
61 echo "$activationScript" > $out/activate
62 substituteInPlace $out/activate --subst-var out
63 chmod u+x $out/activate
64 unset activationScript
65
66 cp ${config.system.build.bootStage2} $out/init
67 substituteInPlace $out/init --subst-var-by systemConfig $out
68
69 ln -s ${config.system.build.etc}/etc $out/etc
70 ln -s ${config.system.path} $out/sw
71 ln -s "$systemd" $out/systemd
72
73 echo -n "$configurationName" > $out/configuration-name
74 echo -n "systemd ${toString config.systemd.package.interfaceVersion}" > $out/init-interface-version
75 echo -n "$nixosLabel" > $out/nixos-version
76 echo -n "$system" > $out/system
77
78 mkdir $out/fine-tune
79 childCount=0
80 for i in $children; do
81 childCount=$(( childCount + 1 ))
82 ln -s $i $out/fine-tune/child-$childCount
83 done
84
85 mkdir $out/bin
86 substituteAll ${./switch-to-configuration.pl} $out/bin/switch-to-configuration
87 chmod +x $out/bin/switch-to-configuration
88
89 echo -n "${toString config.system.extraDependencies}" > $out/extra-dependencies
90
91 ${config.system.extraSystemBuilderCmds}
92 '';
93
94 # Handle assertions
95
96 failed = map (x: x.message) (filter (x: !x.assertion) config.assertions);
97
98 showWarnings = res: fold (w: x: builtins.trace "[1;31mwarning: ${w}[0m" x) res config.warnings;
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 = showWarnings (
106 if [] == failed then pkgs.stdenvNoCC.mkDerivation {
107 name = let hn = config.networking.hostName;
108 nn = if (hn != "") then hn else "unnamed";
109 in "nixos-system-${nn}-${config.system.nixosLabel}";
110 preferLocalBuild = true;
111 allowSubstitutes = false;
112 buildCommand = systemBuilder;
113
114 inherit (pkgs) utillinux coreutils;
115 systemd = config.systemd.package;
116
117 inherit children;
118 kernelParams = config.boot.kernelParams;
119 installBootLoader =
120 config.system.build.installBootLoader
121 or "echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2; true";
122 activationScript = config.system.activationScripts.script;
123 nixosLabel = config.system.nixosLabel;
124
125 configurationName = config.boot.loader.grub.configurationName;
126
127 # Needed by switch-to-configuration.
128 perl = "${pkgs.perl}/bin/perl -I${pkgs.perlPackages.FileSlurp}/lib/perl5/site_perl";
129 } else throw "\nFailed assertions:\n${concatStringsSep "\n" (map (x: "- ${x}") failed)}");
130
131 # Replace runtime dependencies
132 system = fold ({ oldDependency, newDependency }: drv:
133 pkgs.replaceDependency { inherit oldDependency newDependency drv; }
134 ) baseSystem config.system.replaceRuntimeDependencies;
135
136in
137
138{
139 options = {
140
141 system.build = mkOption {
142 internal = true;
143 default = {};
144 type = types.attrs;
145 description = ''
146 Attribute set of derivations used to setup the system.
147 '';
148 };
149
150 nesting.children = mkOption {
151 default = [];
152 description = ''
153 Additional configurations to build.
154 '';
155 };
156
157 nesting.clone = mkOption {
158 default = [];
159 description = ''
160 Additional configurations to build based on the current
161 configuration which then has a lower priority.
162 '';
163 };
164
165 system.boot.loader.id = mkOption {
166 internal = true;
167 default = "";
168 description = ''
169 Id string of the used bootloader.
170 '';
171 };
172
173 system.boot.loader.kernelFile = mkOption {
174 internal = true;
175 default = pkgs.stdenv.platform.kernelTarget;
176 type = types.str;
177 description = ''
178 Name of the kernel file to be passed to the bootloader.
179 '';
180 };
181
182 system.copySystemConfiguration = mkOption {
183 type = types.bool;
184 default = false;
185 description = ''
186 If enabled, copies the NixOS configuration file
187 (usually <filename>/etc/nixos/configuration.nix</filename>)
188 and links it from the resulting system
189 (getting to <filename>/run/current-system/configuration.nix</filename>).
190 Note that only this single file is copied, even if it imports others.
191 '';
192 };
193
194 system.extraSystemBuilderCmds = mkOption {
195 type = types.lines;
196 internal = true;
197 default = "";
198 description = ''
199 This code will be added to the builder creating the system store path.
200 '';
201 };
202
203 system.extraDependencies = mkOption {
204 type = types.listOf types.package;
205 default = [];
206 description = ''
207 A list of packages that should be included in the system
208 closure but not otherwise made available to users. This is
209 primarily used by the installation tests.
210 '';
211 };
212
213 system.replaceRuntimeDependencies = mkOption {
214 default = [];
215 example = lib.literalExample "[ ({ original = pkgs.openssl; replacement = pkgs.callPackage /path/to/openssl { }; }) ]";
216 type = types.listOf (types.submodule (
217 { options, ... }: {
218 options.original = mkOption {
219 type = types.package;
220 description = "The original package to override.";
221 };
222
223 options.replacement = mkOption {
224 type = types.package;
225 description = "The replacement package.";
226 };
227 })
228 );
229 apply = map ({ original, replacement, ... }: {
230 oldDependency = original;
231 newDependency = replacement;
232 });
233 description = ''
234 List of packages to override without doing a full rebuild.
235 The original derivation and replacement derivation must have the same
236 name length, and ideally should have close-to-identical directory layout.
237 '';
238 };
239
240 };
241
242
243 config = {
244
245 system.extraSystemBuilderCmds =
246 optionalString
247 config.system.copySystemConfiguration
248 ''ln -s '${import ../../../lib/from-env.nix "NIXOS_CONFIG" <nixos-config>}' \
249 "$out/configuration.nix"
250 '';
251
252 system.build.toplevel = system;
253
254 };
255
256}