diff --git a/nixos/modules/misc/nixpkgs.nix b/nixos/modules/misc/nixpkgs.nix index 02bb76cce6c5..a9e49a2a26cd 100644 --- a/nixos/modules/misc/nixpkgs.nix +++ b/nixos/modules/misc/nixpkgs.nix @@ -34,6 +34,8 @@ let ++ lib.optional (opt.localSystem.highestPrio < (lib.mkOptionDefault { }).priority) opt.localSystem ++ lib.optional (opt.crossSystem.highestPrio < (lib.mkOptionDefault { }).priority) opt.crossSystem; + _configDefinitions = opt.config.definitionsWithLocations; + defaultPkgs = if opt.hostPlatform.isDefined then let @@ -51,14 +53,15 @@ let in import ../../.. ( { - inherit (cfg) config overlays; + inherit _configDefinitions; + inherit (cfg) overlays; } // systemArgs ) else import ../../.. { + inherit _configDefinitions; inherit (cfg) - config overlays localSystem crossSystem @@ -126,13 +129,15 @@ in example = lib.literalExpression '' { allowBroken = true; allowUnfree = true; } ''; - type = lib.types.submoduleWith { - modules = [ ../../../pkgs/top-level/config.nix ]; - specialArgs = { - docPrefix = "https://nixos.org/manual/nixpkgs/unstable/"; - }; - shorthandOnlyDefinesConfig = true; + type = lib.types.deferredModuleWith { + staticModules = [ + { _module.args.docPrefix = "https://nixos.org/manual/nixpkgs/unstable/"; } + ../../../pkgs/top-level/config.nix + ]; }; + # Returns pkgs.config instead of nixpkgs.config + # This shadows the deferredModule to make it look like a submodule + apply = _: finalPkgs.config; description = '' Global configuration for Nixpkgs. The complete list of [Nixpkgs configuration options](https://nixos.org/manual/nixpkgs/unstable/#sec-config-options-reference) is in the [Nixpkgs manual section on global configuration](https://nixos.org/manual/nixpkgs/unstable/#chap-packageconfig). diff --git a/pkgs/test/config-nix-unit.nix b/pkgs/test/config-nix-unit.nix new file mode 100644 index 000000000000..cd16d5ca1c43 --- /dev/null +++ b/pkgs/test/config-nix-unit.nix @@ -0,0 +1,117 @@ +# Tests for nixpkgs config forwarding from NixOS modules. +# +# Run with: +# nix-unit pkgs/test/config-nix-unit.nix +# or +# nix-build -A tests.config-nix-unit +# +{ + nixpkgsPath ? ../.., + pkgs ? import nixpkgsPath { }, +}: +let + lib = pkgs.lib; + + # Test helper + evalNixos = + modules: + import (nixpkgsPath + "/nixos/lib/eval-config.nix") { + modules = [ { nixpkgs.hostPlatform = "x86_64-linux"; } ] ++ modules; + }; +in +{ + # Basic: a single config option is forwarded correctly. + testSingleConfigOption = { + expr = (evalNixos [ { nixpkgs.config.allowUnfree = true; } ]).config.nixpkgs.config.allowUnfree; + expected = true; + }; + + # Multiple config definitions from separate modules are merged. + testMultipleModulesMerge = { + expr = + let + eval = evalNixos [ + { nixpkgs.config.allowUnfree = true; } + { nixpkgs.config.allowBroken = true; } + ]; + in + { + inherit (eval.config.nixpkgs.config) allowUnfree allowBroken; + }; + expected = { + allowUnfree = true; + allowBroken = true; + }; + }; + + # mkForce works. Also covers other properties + testMkForce = { + expr = + (evalNixos [ + { nixpkgs.config.allowUnfree = true; } + { nixpkgs.config.allowUnfree = lib.mkForce false; } + ]).config.nixpkgs.config.allowUnfree; + expected = false; + }; + + testDefaults = { + expr = (evalNixos [ ]).config.nixpkgs.config.allowUnfree; + expected = false; + }; + + # Standalone nixpkgs (i.e. import { ... }) + testStandaloneConfig = { + expr = (import nixpkgsPath { config.allowUnfree = true; }).config.allowUnfree; + expected = true; + }; + + # Standalone nixpkgs with a function (i.e. import ({pkgs, lib, ...}: { ... }) + testStandaloneConfigFunctionPkgs = { + expr = + (import nixpkgsPath { + config = + { pkgs, lib, ... }: + { + allowUnfree = lib.isAttrs pkgs; + }; + }).config.allowUnfree; + expected = true; + }; + + # NixOS module sets nixpkgs.config as a function + testNixosConfigFunction = { + expr = + (evalNixos [ + { + nixpkgs.config = + { lib, ... }: + { + allowUnfree = lib.isFunction lib.id; + }; + } + ]).config.nixpkgs.config.allowUnfree; + expected = true; + }; + + # Passing both config and _configDefinitions is not allowed + testConfigAndDefinitionsMutuallyExclusive = { + expr = + (import nixpkgsPath { + config = { + allowUnfree = true; + }; + _configDefinitions = [ + { + file = "test"; + value = { + allowBroken = true; + }; + } + ]; + }).config.allowUnfree; + expectedError = { + type = "ThrownError"; + msg = ".*_configDefinitions.*internal.*must not be combined.*"; + }; + }; +} diff --git a/pkgs/test/default.nix b/pkgs/test/default.nix index 927078483580..cd7a1436ebf1 100644 --- a/pkgs/test/default.nix +++ b/pkgs/test/default.nix @@ -128,6 +128,21 @@ in config = callPackage ./config.nix { }; + # Technically nix-unit binds to a fixed nix version + # We have tests in lib to test the module system itself against different nix-versions + # Based on this assumption (transitivity of correctness) this test should therefore also cover all tested nix-versions + config-nix-unit = + pkgs.runCommand "config-nix-unit" + { + nativeBuildInputs = [ pkgs.nix-unit ]; + } + '' + export HOME=$TMPDIR + nix-unit --eval-store "$HOME" ${./config-nix-unit.nix} \ + --arg nixpkgsPath "${../..}" + mkdir $out + ''; + top-level = callPackage ./top-level { }; haskell = callPackage ./haskell { }; diff --git a/pkgs/top-level/config.nix b/pkgs/top-level/config.nix index 39f32645e45a..8c4438ae154d 100644 --- a/pkgs/top-level/config.nix +++ b/pkgs/top-level/config.nix @@ -9,11 +9,11 @@ { config, lib, + docPrefix ? "", ... -}@args: +}: let - docPrefix = args.docPrefix or ""; inherit (lib) literalExpression mapAttrsToList @@ -471,7 +471,7 @@ let Silence the warning for the upcoming deprecation of the `x86_64-darwin` platform in Nixpkgs 26.11. - See the [release notes](#x86_64-darwin-26.05) for more + See the [release notes](${docPrefix}#x86_64-darwin-26.05) for more information. ''; }; diff --git a/pkgs/top-level/default.nix b/pkgs/top-level/default.nix index 521745d67ec9..12fdbed08197 100644 --- a/pkgs/top-level/default.nix +++ b/pkgs/top-level/default.nix @@ -64,6 +64,11 @@ in # list it returns. stdenvStages ? import ../stdenv, + # Temporary parameter to unify nixpkgs/pkgs evaluation + # Internal, do not use this manually! + # Will be removed again within the next releases + _configDefinitions ? null, + # Ignore unexpected args. ... }@args: @@ -109,7 +114,13 @@ let then x86_64DarwinDeprecationWarning else - x: x + x: + x throwIfNot (lib.all lib.isFunction crossOverlays) + "All crossOverlays passed to nixpkgs must be functions." + ) + ( + throwIfNot (_configDefinitions == null || config0 == { }) + "The `_configDefinitions` argument is an internal interface and must not be combined with `config`." ); localSystem = lib.systems.elaborate args.localSystem; @@ -134,20 +145,24 @@ let # Allow both: # { /* the config */ } and - # { pkgs, ... } : { /* the config */ } + # { lib, pkgs, ... } : { /* the config */ } config1 = if lib.isFunction config0 then config0 { inherit lib pkgs; } else config0; configEval = lib.evalModules { modules = [ ./config.nix - ( - { options, ... }: - { - _file = "nixpkgs.config"; - config = config1; - } - ) - ]; + ] + ++ ( + if _configDefinitions != null then + map (def: lib.modules.setDefaultModuleLocation def.file def.value) _configDefinitions + else + [ + { + _file = "nixpkgs.config"; + config = config1; + } + ] + ); class = "nixpkgsConfig"; };