at 23.11-pre 15 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 systemBuilder = 7 let 8 kernelPath = "${config.boot.kernelPackages.kernel}/" + 9 "${config.system.boot.loader.kernelFile}"; 10 initrdPath = "${config.system.build.initialRamdisk}/" + 11 "${config.system.boot.loader.initrdFile}"; 12 in '' 13 mkdir $out 14 15 # Containers don't have their own kernel or initrd. They boot 16 # directly into stage 2. 17 ${optionalString config.boot.kernel.enable '' 18 if [ ! -f ${kernelPath} ]; then 19 echo "The bootloader cannot find the proper kernel image." 20 echo "(Expecting ${kernelPath})" 21 false 22 fi 23 24 ln -s ${kernelPath} $out/kernel 25 ln -s ${config.system.modulesTree} $out/kernel-modules 26 ${optionalString (config.hardware.deviceTree.package != null) '' 27 ln -s ${config.hardware.deviceTree.package} $out/dtbs 28 ''} 29 30 echo -n "$kernelParams" > $out/kernel-params 31 32 ln -s ${initrdPath} $out/initrd 33 34 ln -s ${config.system.build.initialRamdiskSecretAppender}/bin/append-initrd-secrets $out 35 36 ln -s ${config.hardware.firmware}/lib/firmware $out/firmware 37 ''} 38 39 echo "$activationScript" > $out/activate 40 echo "$dryActivationScript" > $out/dry-activate 41 substituteInPlace $out/activate --subst-var out 42 substituteInPlace $out/dry-activate --subst-var out 43 chmod u+x $out/activate $out/dry-activate 44 unset activationScript dryActivationScript 45 46 ${if config.boot.initrd.systemd.enable then '' 47 cp ${config.system.build.bootStage2} $out/prepare-root 48 substituteInPlace $out/prepare-root --subst-var-by systemConfig $out 49 # This must not be a symlink or the abs_path of the grub builder for the tests 50 # will resolve the symlink and we end up with a path that doesn't point to a 51 # system closure. 52 cp "$systemd/lib/systemd/systemd" $out/init 53 '' else '' 54 cp ${config.system.build.bootStage2} $out/init 55 substituteInPlace $out/init --subst-var-by systemConfig $out 56 ''} 57 58 ln -s ${config.system.build.etc}/etc $out/etc 59 ln -s ${config.system.path} $out/sw 60 ln -s "$systemd" $out/systemd 61 62 echo -n "systemd ${toString config.systemd.package.interfaceVersion}" > $out/init-interface-version 63 echo -n "$nixosLabel" > $out/nixos-version 64 echo -n "${config.boot.kernelPackages.stdenv.hostPlatform.system}" > $out/system 65 66 mkdir $out/bin 67 export localeArchive="${config.i18n.glibcLocales}/lib/locale/locale-archive" 68 export distroId=${config.system.nixos.distroId}; 69 substituteAll ${./switch-to-configuration.pl} $out/bin/switch-to-configuration 70 chmod +x $out/bin/switch-to-configuration 71 ${optionalString (pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) '' 72 if ! output=$($perl/bin/perl -c $out/bin/switch-to-configuration 2>&1); then 73 echo "switch-to-configuration syntax is not valid:" 74 echo "$output" 75 exit 1 76 fi 77 ''} 78 79 ${config.system.systemBuilderCommands} 80 81 cp "$extraDependenciesPath" "$out/extra-dependencies" 82 83 ${optionalString (!config.boot.isContainer && config.boot.bootspec.enable) '' 84 ${config.boot.bootspec.writer} 85 ${optionalString config.boot.bootspec.enableValidation 86 ''${config.boot.bootspec.validator} "$out/${config.boot.bootspec.filename}"''} 87 ''} 88 89 ${config.system.extraSystemBuilderCmds} 90 ''; 91 92 # Putting it all together. This builds a store path containing 93 # symlinks to the various parts of the built configuration (the 94 # kernel, systemd units, init scripts, etc.) as well as a script 95 # `switch-to-configuration' that activates the configuration and 96 # makes it bootable. 97 baseSystem = pkgs.stdenvNoCC.mkDerivation ({ 98 name = "nixos-system-${config.system.name}-${config.system.nixos.label}"; 99 preferLocalBuild = true; 100 allowSubstitutes = false; 101 passAsFile = [ "extraDependencies" ]; 102 buildCommand = systemBuilder; 103 104 inherit (pkgs) coreutils; 105 systemd = config.systemd.package; 106 shell = "${pkgs.bash}/bin/sh"; 107 su = "${pkgs.shadow.su}/bin/su"; 108 utillinux = pkgs.util-linux; 109 110 kernelParams = config.boot.kernelParams; 111 installBootLoader = config.system.build.installBootLoader; 112 activationScript = config.system.activationScripts.script; 113 dryActivationScript = config.system.dryActivationScript; 114 nixosLabel = config.system.nixos.label; 115 116 inherit (config.system) extraDependencies; 117 118 # Needed by switch-to-configuration. 119 perl = pkgs.perl.withPackages (p: with p; [ ConfigIniFiles FileSlurp ]); 120 } // config.system.systemBuilderArgs); 121 122 # Handle assertions and warnings 123 124 failedAssertions = map (x: x.message) (filter (x: !x.assertion) config.assertions); 125 126 baseSystemAssertWarn = if failedAssertions != [] 127 then throw "\nFailed assertions:\n${concatStringsSep "\n" (map (x: "- ${x}") failedAssertions)}" 128 else showWarnings config.warnings baseSystem; 129 130 # Replace runtime dependencies 131 system = foldr ({ oldDependency, newDependency }: drv: 132 pkgs.replaceDependency { inherit oldDependency newDependency drv; } 133 ) baseSystemAssertWarn config.system.replaceRuntimeDependencies; 134 135 systemWithBuildDeps = system.overrideAttrs (o: { 136 systemBuildClosure = pkgs.closureInfo { rootPaths = [ system.drvPath ]; }; 137 buildCommand = o.buildCommand + '' 138 ln -sn $systemBuildClosure $out/build-closure 139 ''; 140 }); 141 142in 143 144{ 145 imports = [ 146 ../build.nix 147 (mkRemovedOptionModule [ "nesting" "clone" ] "Use `specialisation.«name» = { inheritParentConfig = true; configuration = { ... }; }` instead.") 148 (mkRemovedOptionModule [ "nesting" "children" ] "Use `specialisation.«name».configuration = { ... }` instead.") 149 ]; 150 151 options = { 152 153 system.boot.loader.id = mkOption { 154 internal = true; 155 default = ""; 156 description = lib.mdDoc '' 157 Id string of the used bootloader. 158 ''; 159 }; 160 161 system.boot.loader.kernelFile = mkOption { 162 internal = true; 163 default = pkgs.stdenv.hostPlatform.linux-kernel.target; 164 defaultText = literalExpression "pkgs.stdenv.hostPlatform.linux-kernel.target"; 165 type = types.str; 166 description = lib.mdDoc '' 167 Name of the kernel file to be passed to the bootloader. 168 ''; 169 }; 170 171 system.boot.loader.initrdFile = mkOption { 172 internal = true; 173 default = "initrd"; 174 type = types.str; 175 description = lib.mdDoc '' 176 Name of the initrd file to be passed to the bootloader. 177 ''; 178 }; 179 180 system.build = { 181 installBootLoader = mkOption { 182 internal = true; 183 # "; true" => make the `$out` argument from switch-to-configuration.pl 184 # go to `true` instead of `echo`, hiding the useless path 185 # from the log. 186 default = "echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2; true"; 187 description = lib.mdDoc '' 188 A program that writes a bootloader installation script to the path passed in the first command line argument. 189 190 See `nixos/modules/system/activation/switch-to-configuration.pl`. 191 ''; 192 type = types.unique { 193 message = '' 194 Only one bootloader can be enabled at a time. This requirement has not 195 been checked until NixOS 22.05. Earlier versions defaulted to the last 196 definition. Change your configuration to enable only one bootloader. 197 ''; 198 } (types.either types.str types.package); 199 }; 200 201 toplevel = mkOption { 202 type = types.package; 203 readOnly = true; 204 description = lib.mdDoc '' 205 This option contains the store path that typically represents a NixOS system. 206 207 You can read this path in a custom deployment tool for example. 208 ''; 209 }; 210 }; 211 212 213 system.copySystemConfiguration = mkOption { 214 type = types.bool; 215 default = false; 216 description = lib.mdDoc '' 217 If enabled, copies the NixOS configuration file 218 (usually {file}`/etc/nixos/configuration.nix`) 219 and links it from the resulting system 220 (getting to {file}`/run/current-system/configuration.nix`). 221 Note that only this single file is copied, even if it imports others. 222 ''; 223 }; 224 225 system.systemBuilderCommands = mkOption { 226 type = types.lines; 227 internal = true; 228 default = ""; 229 description = '' 230 This code will be added to the builder creating the system store path. 231 ''; 232 }; 233 234 system.systemBuilderArgs = mkOption { 235 type = types.attrsOf types.unspecified; 236 internal = true; 237 default = {}; 238 description = lib.mdDoc '' 239 `lib.mkDerivation` attributes that will be passed to the top level system builder. 240 ''; 241 }; 242 243 system.forbiddenDependenciesRegex = mkOption { 244 default = ""; 245 example = "-dev$"; 246 type = types.str; 247 description = lib.mdDoc '' 248 A POSIX Extended Regular Expression that matches store paths that 249 should not appear in the system closure, with the exception of {option}`system.extraDependencies`, which is not checked. 250 ''; 251 }; 252 253 system.extraSystemBuilderCmds = mkOption { 254 type = types.lines; 255 internal = true; 256 default = ""; 257 description = lib.mdDoc '' 258 This code will be added to the builder creating the system store path. 259 ''; 260 }; 261 262 system.extraDependencies = mkOption { 263 type = types.listOf types.package; 264 default = []; 265 description = lib.mdDoc '' 266 A list of packages that should be included in the system 267 closure but generally not visible to users. 268 269 This option has also been used for build-time checks, but the 270 `system.checks` option is more appropriate for that purpose as checks 271 should not leave a trace in the built system configuration. 272 ''; 273 }; 274 275 system.checks = mkOption { 276 type = types.listOf types.package; 277 default = []; 278 description = lib.mdDoc '' 279 Packages that are added as dependencies of the system's build, usually 280 for the purpose of validating some part of the configuration. 281 282 Unlike `system.extraDependencies`, these store paths do not 283 become part of the built system configuration. 284 ''; 285 }; 286 287 system.replaceRuntimeDependencies = mkOption { 288 default = []; 289 example = lib.literalExpression "[ ({ original = pkgs.openssl; replacement = pkgs.callPackage /path/to/openssl { }; }) ]"; 290 type = types.listOf (types.submodule ( 291 { ... }: { 292 options.original = mkOption { 293 type = types.package; 294 description = lib.mdDoc "The original package to override."; 295 }; 296 297 options.replacement = mkOption { 298 type = types.package; 299 description = lib.mdDoc "The replacement package."; 300 }; 301 }) 302 ); 303 apply = map ({ original, replacement, ... }: { 304 oldDependency = original; 305 newDependency = replacement; 306 }); 307 description = lib.mdDoc '' 308 List of packages to override without doing a full rebuild. 309 The original derivation and replacement derivation must have the same 310 name length, and ideally should have close-to-identical directory layout. 311 ''; 312 }; 313 314 system.name = mkOption { 315 type = types.str; 316 default = 317 if config.networking.hostName == "" 318 then "unnamed" 319 else config.networking.hostName; 320 defaultText = literalExpression '' 321 if config.networking.hostName == "" 322 then "unnamed" 323 else config.networking.hostName; 324 ''; 325 description = lib.mdDoc '' 326 The name of the system used in the {option}`system.build.toplevel` derivation. 327 328 That derivation has the following name: 329 `"nixos-system-''${config.system.name}-''${config.system.nixos.label}"` 330 ''; 331 }; 332 333 system.includeBuildDependencies = mkOption { 334 type = types.bool; 335 default = false; 336 description = lib.mdDoc '' 337 Whether to include the build closure of the whole system in 338 its runtime closure. This can be useful for making changes 339 fully offline, as it includes all sources, patches, and 340 intermediate outputs required to build all the derivations 341 that the system depends on. 342 343 Note that this includes _all_ the derivations, down from the 344 included applications to their sources, the compilers used to 345 build them, and even the bootstrap compiler used to compile 346 the compilers. This increases the size of the system and the 347 time needed to download its dependencies drastically: a 348 minimal configuration with no extra services enabled grows 349 from ~670MiB in size to 13.5GiB, and takes proportionally 350 longer to download. 351 ''; 352 }; 353 354 }; 355 356 357 config = { 358 assertions = [ 359 { 360 assertion = config.system.copySystemConfiguration -> !lib.inPureEvalMode; 361 message = "system.copySystemConfiguration is not supported with flakes"; 362 } 363 ]; 364 365 system.extraSystemBuilderCmds = 366 optionalString 367 config.system.copySystemConfiguration 368 ''ln -s '${import ../../../lib/from-env.nix "NIXOS_CONFIG" <nixos-config>}' \ 369 "$out/configuration.nix" 370 '' + 371 optionalString 372 (config.system.forbiddenDependenciesRegex != "") 373 '' 374 if [[ $forbiddenDependenciesRegex != "" && -n $closureInfo ]]; then 375 if forbiddenPaths="$(grep -E -- "$forbiddenDependenciesRegex" $closureInfo/store-paths)"; then 376 echo -e "System closure $out contains the following disallowed paths:\n$forbiddenPaths" 377 exit 1 378 fi 379 fi 380 ''; 381 382 system.systemBuilderArgs = { 383 # Not actually used in the builder. `passedChecks` is just here to create 384 # the build dependencies. Checks are similar to build dependencies in the 385 # sense that if they fail, the system build fails. However, checks do not 386 # produce any output of value, so they are not used by the system builder. 387 # In fact, using them runs the risk of accidentally adding unneeded paths 388 # to the system closure, which defeats the purpose of the `system.checks` 389 # option, as opposed to `system.extraDependencies`. 390 passedChecks = concatStringsSep " " config.system.checks; 391 } 392 // lib.optionalAttrs (config.system.forbiddenDependenciesRegex != "") { 393 inherit (config.system) forbiddenDependenciesRegex; 394 closureInfo = pkgs.closureInfo { rootPaths = [ 395 # override to avoid infinite recursion (and to allow using extraDependencies to add forbidden dependencies) 396 (config.system.build.toplevel.overrideAttrs (_: { extraDependencies = []; closureInfo = null; })) 397 ]; }; 398 }; 399 400 401 system.build.toplevel = if config.system.includeBuildDependencies then systemWithBuildDeps else system; 402 403 }; 404 405}