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