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