lib/modules: Add class concept to check imports
This improves the error message when an incompatible module is imported.
This commit is contained in:
parent
3633bf98be
commit
b8ff2807a2
5 changed files with 69 additions and 4 deletions
|
@ -105,6 +105,10 @@ let
|
|||
# when resolving module structure (like in imports). For everything else,
|
||||
# there's _module.args. If specialArgs.modulesPath is defined it will be
|
||||
# used as the base path for disabledModules.
|
||||
#
|
||||
# `specialArgs.class`:
|
||||
# A nominal type for modules. When set and non-null, this adds a check to
|
||||
# make sure that only compatible modules are imported.
|
||||
specialArgs ? {}
|
||||
, # This would be remove in the future, Prefer _module.args option instead.
|
||||
args ? {}
|
||||
|
@ -256,6 +260,7 @@ let
|
|||
|
||||
merged =
|
||||
let collected = collectModules
|
||||
(specialArgs.class or null)
|
||||
(specialArgs.modulesPath or "")
|
||||
(regularModules ++ [ internalModule ])
|
||||
({ inherit lib options config specialArgs; } // specialArgs);
|
||||
|
@ -349,11 +354,11 @@ let
|
|||
};
|
||||
in result;
|
||||
|
||||
# collectModules :: (modulesPath: String) -> (modules: [ Module ]) -> (args: Attrs) -> [ Module ]
|
||||
# collectModules :: (class: String) -> (modulesPath: String) -> (modules: [ Module ]) -> (args: Attrs) -> [ Module ]
|
||||
#
|
||||
# Collects all modules recursively through `import` statements, filtering out
|
||||
# all modules in disabledModules.
|
||||
collectModules = let
|
||||
collectModules = class: let
|
||||
|
||||
# Like unifyModuleSyntax, but also imports paths and calls functions if necessary
|
||||
loadModule = args: fallbackFile: fallbackKey: m:
|
||||
|
@ -364,6 +369,17 @@ let
|
|||
throw "Module imports can't be nested lists. Perhaps you meant to remove one level of lists? Definitions: ${showDefs defs}"
|
||||
else unifyModuleSyntax (toString m) (toString m) (applyModuleArgsIfFunction (toString m) (import m) args);
|
||||
|
||||
checkModule =
|
||||
if class != null
|
||||
then
|
||||
m:
|
||||
if m.class != null -> m.class == class
|
||||
then m
|
||||
else
|
||||
throw "The module ${m._file or m.key} was imported into ${class} instead of ${m.class}."
|
||||
else
|
||||
m: m;
|
||||
|
||||
/*
|
||||
Collects all modules recursively into the form
|
||||
|
||||
|
@ -397,7 +413,7 @@ let
|
|||
};
|
||||
in parentFile: parentKey: initialModules: args: collectResults (imap1 (n: x:
|
||||
let
|
||||
module = loadModule args parentFile "${parentKey}:anon-${toString n}" x;
|
||||
module = checkModule (loadModule args parentFile "${parentKey}:anon-${toString n}" x);
|
||||
collectedImports = collectStructuredModules module._file module.key module.imports args;
|
||||
in {
|
||||
key = module.key;
|
||||
|
@ -461,7 +477,7 @@ let
|
|||
else config;
|
||||
in
|
||||
if m ? config || m ? options then
|
||||
let badAttrs = removeAttrs m ["_file" "key" "disabledModules" "imports" "options" "config" "meta" "freeformType"]; in
|
||||
let badAttrs = removeAttrs m ["_file" "key" "disabledModules" "imports" "options" "config" "meta" "freeformType" "class"]; in
|
||||
if badAttrs != {} then
|
||||
throw "Module `${key}' has an unsupported attribute `${head (attrNames badAttrs)}'. This is caused by introducing a top-level `config' or `options' attribute. Add configuration attributes immediately on the top level instead, or move all of them (namely: ${toString (attrNames badAttrs)}) into the explicit `config' attribute."
|
||||
else
|
||||
|
@ -471,6 +487,7 @@ let
|
|||
imports = m.imports or [];
|
||||
options = m.options or {};
|
||||
config = addFreeformType (addMeta (m.config or {}));
|
||||
class = m.class or null;
|
||||
}
|
||||
else
|
||||
# shorthand syntax
|
||||
|
@ -481,6 +498,7 @@ let
|
|||
imports = m.require or [] ++ m.imports or [];
|
||||
options = {};
|
||||
config = addFreeformType (removeAttrs m ["_file" "key" "disabledModules" "require" "imports" "freeformType"]);
|
||||
class = m.class or null;
|
||||
};
|
||||
|
||||
applyModuleArgsIfFunction = key: f: args@{ config, options, lib, ... }: if isFunction f then
|
||||
|
|
|
@ -360,6 +360,11 @@ checkConfigOutput 'ok' config.freeformItems.foo.bar ./adhoc-freeformType-survive
|
|||
# because of an `extendModules` bug, issue 168767.
|
||||
checkConfigOutput '^1$' config.sub.specialisation.value ./extendModules-168767-imports.nix
|
||||
|
||||
# Class checks
|
||||
checkConfigOutput '^{ }$' config.ok.config ./class-check.nix
|
||||
checkConfigError 'The module .*/module-class-is-darwin.nix was imported into nixos instead of darwin.' config.fail.config ./class-check.nix
|
||||
checkConfigError 'The module foo.nix#darwinModules.default was imported into nixos instead of darwin.' config.fail-anon.config ./class-check.nix
|
||||
|
||||
# doRename works when `warnings` does not exist.
|
||||
checkConfigOutput '^1234$' config.c.d.e ./doRename-basic.nix
|
||||
# doRename adds a warning.
|
||||
|
|
34
lib/tests/modules/class-check.nix
Normal file
34
lib/tests/modules/class-check.nix
Normal file
|
@ -0,0 +1,34 @@
|
|||
{ lib, ... }: {
|
||||
config = {
|
||||
_module.freeformType = lib.types.anything;
|
||||
ok =
|
||||
lib.evalModules {
|
||||
specialArgs.class = "nixos";
|
||||
modules = [
|
||||
./module-class-is-nixos.nix
|
||||
];
|
||||
};
|
||||
|
||||
fail =
|
||||
lib.evalModules {
|
||||
specialArgs.class = "nixos";
|
||||
modules = [
|
||||
./module-class-is-nixos.nix
|
||||
./module-class-is-darwin.nix
|
||||
];
|
||||
};
|
||||
|
||||
fail-anon =
|
||||
lib.evalModules {
|
||||
specialArgs.class = "nixos";
|
||||
modules = [
|
||||
./module-class-is-nixos.nix
|
||||
{ _file = "foo.nix#darwinModules.default";
|
||||
class = "darwin";
|
||||
imports = [];
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
};
|
||||
}
|
4
lib/tests/modules/module-class-is-darwin.nix
Normal file
4
lib/tests/modules/module-class-is-darwin.nix
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
class = "darwin";
|
||||
config = {};
|
||||
}
|
4
lib/tests/modules/module-class-is-nixos.nix
Normal file
4
lib/tests/modules/module-class-is-nixos.nix
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
class = "nixos";
|
||||
config = {};
|
||||
}
|
Loading…
Reference in a new issue