diff --git a/nixos/modules/misc/nixpkgs.nix b/nixos/modules/misc/nixpkgs.nix
index 866bb3516009..132a9b68ceb4 100644
--- a/nixos/modules/misc/nixpkgs.nix
+++ b/nixos/modules/misc/nixpkgs.nix
@@ -55,9 +55,46 @@ let
check = builtins.isAttrs;
};
- defaultPkgs = import ../../.. {
- inherit (cfg) config overlays localSystem crossSystem;
- };
+ hasBuildPlatform = opt.buildPlatform.highestPrio < (mkOptionDefault {}).priority;
+ hasHostPlatform = opt.hostPlatform.isDefined;
+ hasPlatform = hasHostPlatform || hasBuildPlatform;
+
+ # Context for messages
+ hostPlatformLine = optionalString hasHostPlatform "${showOptionWithDefLocs opt.hostPlatform}";
+ buildPlatformLine = optionalString hasBuildPlatform "${showOptionWithDefLocs opt.buildPlatform}";
+ platformLines = optionalString hasPlatform ''
+ Your system configuration configures nixpkgs with platform parameters:
+ ${hostPlatformLine
+ }${buildPlatformLine
+ }'';
+
+ legacyOptionsDefined =
+ optional opt.system.isDefined opt.system
+ ++ (optional (opt.localSystem.highestPrio < (mkOptionDefault {}).priority) opt.localSystem)
+ ++ (optional (opt.crossSystem.highestPrio < (mkOptionDefault {}).priority) opt.crossSystem)
+ ;
+
+ defaultPkgs =
+ if opt.hostPlatform.isDefined
+ then
+ let isCross = cfg.buildPlatform != cfg.hostPlatform;
+ systemArgs =
+ if isCross
+ then {
+ localSystem = cfg.buildPlatform;
+ crossSystem = cfg.hostPlatform;
+ }
+ else {
+ localSystem = cfg.hostPlatform;
+ };
+ in
+ import ../../.. ({
+ inherit (cfg) config overlays;
+ } // systemArgs)
+ else
+ import ../../.. {
+ inherit (cfg) config overlays localSystem crossSystem;
+ };
finalPkgs = if opt.pkgs.isDefined then cfg.pkgs.appendOverlays cfg.overlays else defaultPkgs;
@@ -157,6 +194,46 @@ in
'';
};
+ hostPlatform = mkOption {
+ type = types.either types.str types.attrs; # TODO utilize lib.systems.parsedPlatform
+ example = { system = "aarch64-linux"; config = "aarch64-unknown-linux-gnu"; };
+ # Make sure that the final value has all fields for sake of other modules
+ # referring to this. TODO make `lib.systems` itself use the module system.
+ apply = lib.systems.elaborate;
+ defaultText = literalExpression
+ ''(import "''${nixos}/../lib").lib.systems.examples.aarch64-multiplatform'';
+ description = ''
+ Specifies the platform where the NixOS configuration will run.
+
+ To cross-compile, set also nixpkgs.buildPlatform.
+
+ Ignored when nixpkgs.pkgs is set.
+ '';
+ };
+
+ buildPlatform = mkOption {
+ type = types.either types.str types.attrs; # TODO utilize lib.systems.parsedPlatform
+ default = cfg.hostPlatform;
+ example = { system = "x86_64-linux"; config = "x86_64-unknown-linux-gnu"; };
+ # Make sure that the final value has all fields for sake of other modules
+ # referring to this.
+ apply = lib.systems.elaborate;
+ defaultText = literalExpression
+ ''config.nixpkgs.hostPlatform'';
+ description = ''
+ Specifies the platform on which NixOS should be built.
+ By default, NixOS is built on the system where it runs, but you can
+ change where it's built. Setting this option will cause NixOS to be
+ cross-compiled.
+
+ For instance, if you're doing distributed multi-platform deployment,
+ or if you're building machines, you can set this to match your
+ development system and/or build farm.
+
+ Ignored when nixpkgs.pkgs is set.
+ '';
+ };
+
localSystem = mkOption {
type = types.attrs; # TODO utilize lib.systems.parsedPlatform
default = { inherit (cfg) system; };
@@ -176,10 +253,13 @@ in
deployment, or when building virtual machines. See its
description in the Nixpkgs manual for more details.
- Ignored when nixpkgs.pkgs is set.
+ Ignored when nixpkgs.pkgs or hostPlatform is set.
'';
};
+ # TODO deprecate. "crossSystem" is a nonsense identifier, because "cross"
+ # is a relation between at least 2 systems in the context of a
+ # specific build step, not a single system.
crossSystem = mkOption {
type = types.nullOr types.attrs; # TODO utilize lib.systems.parsedPlatform
default = null;
@@ -193,7 +273,7 @@ in
should be set as null, the default. See its description in the
Nixpkgs manual for more details.
- Ignored when nixpkgs.pkgs is set.
+ Ignored when nixpkgs.pkgs or hostPlatform is set.
'';
};
@@ -216,8 +296,7 @@ in
See nixpkgs.localSystem for more information.
- Ignored when nixpkgs.localSystem is set.
- Ignored when nixpkgs.pkgs is set.
+ Ignored when nixpkgs.pkgs, nixpkgs.localSystem or nixpkgs.hostPlatform is set.
'';
};
};
@@ -240,10 +319,23 @@ in
else "nixpkgs.localSystem";
pkgsSystem = finalPkgs.stdenv.targetPlatform.system;
in {
- assertion = nixosExpectedSystem == pkgsSystem;
+ assertion = !hasPlatform -> nixosExpectedSystem == pkgsSystem;
message = "The NixOS nixpkgs.pkgs option was set to a Nixpkgs invocation that compiles to target system ${pkgsSystem} but NixOS was configured for system ${nixosExpectedSystem} via NixOS option ${nixosOption}. The NixOS system settings must match the Nixpkgs target system.";
}
)
+ {
+ assertion = hasPlatform -> legacyOptionsDefined == [];
+ message = ''
+ Your system configures nixpkgs with the platform parameter${optionalString hasBuildPlatform "s"}:
+ ${hostPlatformLine
+ }${buildPlatformLine
+ }
+ However, it also defines the legacy options:
+ ${concatMapStrings showOptionWithDefLocs legacyOptionsDefined}
+ For a future proof system configuration, we recommend to remove
+ the legacy definitions.
+ '';
+ }
];
};
diff --git a/nixos/modules/misc/nixpkgs/test.nix b/nixos/modules/misc/nixpkgs/test.nix
index ec5fab9fb4a5..9e8851707f8f 100644
--- a/nixos/modules/misc/nixpkgs/test.nix
+++ b/nixos/modules/misc/nixpkgs/test.nix
@@ -1,8 +1,63 @@
{ evalMinimalConfig, pkgs, lib, stdenv }:
+let
+ eval = mod: evalMinimalConfig {
+ imports = [ ../nixpkgs.nix mod ];
+ };
+ withHost = eval {
+ nixpkgs.hostPlatform = "aarch64-linux";
+ };
+ withHostAndBuild = eval {
+ nixpkgs.hostPlatform = "aarch64-linux";
+ nixpkgs.buildPlatform = "aarch64-darwin";
+ };
+ ambiguous = {
+ _file = "ambiguous.nix";
+ nixpkgs.hostPlatform = "aarch64-linux";
+ nixpkgs.buildPlatform = "aarch64-darwin";
+ nixpkgs.system = "x86_64-linux";
+ nixpkgs.localSystem.system = "x86_64-darwin";
+ nixpkgs.crossSystem.system = "i686-linux";
+ imports = [
+ { _file = "repeat.nix";
+ nixpkgs.hostPlatform = "aarch64-linux";
+ }
+ ];
+ };
+ getErrors = module:
+ let
+ uncheckedEval = lib.evalModules { modules = [ ../nixpkgs.nix module ]; };
+ in map (ass: ass.message) (lib.filter (ass: !ass.assertion) uncheckedEval.config.assertions);
+in
lib.recurseIntoAttrs {
invokeNixpkgsSimple =
- (evalMinimalConfig ({ config, modulesPath, ... }: {
- imports = [ (modulesPath + "/misc/nixpkgs.nix") ];
+ (eval {
nixpkgs.system = stdenv.hostPlatform.system;
- }))._module.args.pkgs.hello;
+ })._module.args.pkgs.hello;
+ assertions =
+ assert withHost._module.args.pkgs.stdenv.hostPlatform.system == "aarch64-linux";
+ assert withHost._module.args.pkgs.stdenv.buildPlatform.system == "aarch64-linux";
+ assert withHostAndBuild._module.args.pkgs.stdenv.hostPlatform.system == "aarch64-linux";
+ assert withHostAndBuild._module.args.pkgs.stdenv.buildPlatform.system == "aarch64-darwin";
+ assert builtins.trace (lib.head (getErrors ambiguous))
+ getErrors ambiguous ==
+ [''
+ Your system configures nixpkgs with the platform parameters:
+ nixpkgs.hostPlatform, with values defined in:
+ - repeat.nix
+ - ambiguous.nix
+ nixpkgs.buildPlatform, with values defined in:
+ - ambiguous.nix
+
+ However, it also defines the legacy options:
+ nixpkgs.system, with values defined in:
+ - ambiguous.nix
+ nixpkgs.localSystem, with values defined in:
+ - ambiguous.nix
+ nixpkgs.crossSystem, with values defined in:
+ - ambiguous.nix
+
+ For a future proof system configuration, we recommend to remove
+ the legacy definitions.
+ ''];
+ pkgs.emptyFile;
}