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 "$nixosLabel" > $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 = let hn = config.networking.hostName;
103 nn = if (hn != "") then hn else "unnamed";
104 in "nixos-system-${nn}-${config.system.nixosLabel}";
105 preferLocalBuild = true;
106 allowSubstitutes = false;
107 buildCommand = systemBuilder;
108
109 inherit (pkgs) utillinux coreutils;
110 systemd = config.systemd.package;
111
112 inherit children;
113 kernelParams = config.boot.kernelParams;
114 installBootLoader =
115 config.system.build.installBootLoader
116 or "echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2; true";
117 activationScript = config.system.activationScripts.script;
118 nixosLabel = config.system.nixosLabel;
119
120 configurationName = config.boot.loader.grub.configurationName;
121
122 # Needed by switch-to-configuration.
123 perl = "${pkgs.perl}/bin/perl -I${pkgs.perlPackages.FileSlurp}/lib/perl5/site_perl";
124 } else throw "\nFailed assertions:\n${concatStringsSep "\n" (map (x: "- ${x}") failed)}");
125
126 # Replace runtime dependencies
127 system = fold ({ oldDependency, newDependency }: drv:
128 pkgs.replaceDependency { inherit oldDependency newDependency drv; }
129 ) baseSystem config.system.replaceRuntimeDependencies;
130
131in
132
133{
134 options = {
135
136 system.build = mkOption {
137 internal = true;
138 default = {};
139 description = ''
140 Attribute set of derivations used to setup the system.
141 '';
142 };
143
144 nesting.children = mkOption {
145 default = [];
146 description = ''
147 Additional configurations to build.
148 '';
149 };
150
151 nesting.clone = mkOption {
152 default = [];
153 description = ''
154 Additional configurations to build based on the current
155 configuration which then has a lower priority.
156 '';
157 };
158
159 system.boot.loader.id = mkOption {
160 internal = true;
161 default = "";
162 description = ''
163 Id string of the used bootloader.
164 '';
165 };
166
167 system.boot.loader.kernelFile = mkOption {
168 internal = true;
169 default = pkgs.stdenv.platform.kernelTarget;
170 type = types.str;
171 description = ''
172 Name of the kernel file to be passed to the bootloader.
173 '';
174 };
175
176 system.copySystemConfiguration = mkOption {
177 type = types.bool;
178 default = false;
179 description = ''
180 If enabled, copies the NixOS configuration file
181 (usually <filename>/etc/nixos/configuration.nix</filename>)
182 and links it from the resulting system
183 (getting to <filename>/run/current-system/configuration.nix</filename>).
184 Note that only this single file is copied, even if it imports others.
185 '';
186 };
187
188 system.extraSystemBuilderCmds = mkOption {
189 type = types.lines;
190 internal = true;
191 default = "";
192 description = ''
193 This code will be added to the builder creating the system store path.
194 '';
195 };
196
197 system.extraDependencies = mkOption {
198 type = types.listOf types.package;
199 default = [];
200 description = ''
201 A list of packages that should be included in the system
202 closure but not otherwise made available to users. This is
203 primarily used by the installation tests.
204 '';
205 };
206
207 system.replaceRuntimeDependencies = mkOption {
208 default = [];
209 example = lib.literalExample "[ ({ original = pkgs.openssl; replacement = pkgs.callPackage /path/to/openssl { }; }) ]";
210 type = types.listOf (types.submodule (
211 { options, ... }: {
212 options.original = mkOption {
213 type = types.package;
214 description = "The original package to override.";
215 };
216
217 options.replacement = mkOption {
218 type = types.package;
219 description = "The replacement package.";
220 };
221 })
222 );
223 apply = map ({ original, replacement, ... }: {
224 oldDependency = original;
225 newDependency = replacement;
226 });
227 description = ''
228 List of packages to override without doing a full rebuild.
229 The original derivation and replacement derivation must have the same
230 name length, and ideally should have close-to-identical directory layout.
231 '';
232 };
233
234 };
235
236
237 config = {
238
239 system.extraSystemBuilderCmds =
240 optionalString
241 config.system.copySystemConfiguration
242 ''ln -s '${import ../../../lib/from-env.nix "NIXOS_CONFIG" <nixos-config>}' \
243 "$out/configuration.nix"
244 '';
245
246 system.build.toplevel = system;
247
248 };
249
250}