From 0abfc619bcb605299a0f3f01c1887bb65db61a6b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 31 Aug 2024 01:12:43 +0200 Subject: lib.importApply: init (#230588) * lib.modules.importApply: init Brings variables from rich scopes to modules defined in separate files. A helper for functions in files that return a module. * lib.modules.importApply: Edit doc Generally improve the quality. Notes: - Not rendered to the manual yet, so probably the syntax could be improved, but I have no way to test this now. - The docs use `arg` vs `staticArg` in the code. This is intentional, because the doc is pretty clear about the role of `arg` whereas the code exists in a context where ambiguities are more harmful. * Format--- lib/modules.nix | 53 +++++++++++++++++++++++++++++ lib/tests/modules.sh | 8 +++++ lib/tests/modules/importApply-disabling.nix | 4 +++ lib/tests/modules/importApply-function.nix | 5 +++ lib/tests/modules/importApply.nix | 5 +++ 5 files changed, 75 insertions(+) create mode 100644 lib/tests/modules/importApply-disabling.nix create mode 100644 lib/tests/modules/importApply-function.nix create mode 100644 lib/tests/modules/importApply.nix (limited to 'lib') diff --git a/lib/modules.nix b/lib/modules.nix index b9e9ca1e5d78..ef0be5b27aaf 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -1366,6 +1366,58 @@ let ]); }; + /** + `importApply file arg :: Path -> a -> Module`, where `import file :: a -> Module` + + `importApply` imports a Nix expression file much like the module system would, + after passing an extra positional argument to the function in the file. + + This function should be used when declaring a module in a file that refers to + values from a different scope, such as that in a flake. + + It solves the problems of alternative solutions: + + - While `importApply file arg` is _mostly_ equivalent to + `import file arg`, the latter returns a module without a location, + as `import` only returns the contained expression. This leads to worse + error messages. + + - Using `specialArgs` to provide arguments to all modules. This effectively + creates an incomplete module, and requires the user of the module to + manually pass the `specialArgs` to the configuration, which is error-prone, + verbose, and unnecessary. + + The nix file must contain a function that returns a module. + A module may itself be a function, so the file is often a function with two + positional arguments instead of one. See the example below. + + This function does not add support for deduplication and `disabledModules`, + although that could be achieved by wrapping the returned module and setting + the `_key` module attribute. + The reason for this omission is that the file path is not guaranteed to be + a unique identifier for the module, as two instances of the module may + reference different `arg`s in their closures. + + Example + + # lib.nix + imports = [ + (lib.modules.importApply ./module.nix { bar = bar; }) + ]; + + # module.nix + { bar }: + { lib, config, ... }: + { + options = ...; + config = ... bar ...; + } + + */ + importApply = + modulePath: staticArg: + lib.setDefaultModuleLocation modulePath (import modulePath staticArg); + /* Use this function to import a JSON file as NixOS configuration. modules.importJSON :: path -> attrs @@ -1415,6 +1467,7 @@ private // filterOverrides' fixMergeModules fixupOptionType # should be private? + importApply importJSON importTOML mergeDefinitions diff --git a/lib/tests/modules.sh b/lib/tests/modules.sh index 3301a3d987ee..09547b2e0e6c 100755 --- a/lib/tests/modules.sh +++ b/lib/tests/modules.sh @@ -247,6 +247,14 @@ checkConfigOutput '^true$' "$@" ./define-enable.nix ./define-attrsOfSub-if-foo-e checkConfigOutput '^true$' "$@" ./define-enable.nix ./define-attrsOfSub-foo-if-enable.nix checkConfigOutput '^true$' "$@" ./define-enable.nix ./define-attrsOfSub-foo-enable-if.nix +# Check importApply +checkConfigOutput '"abc"' config.value ./importApply.nix +# importApply does not set a key. +# Disabling the function file is not sufficient, because importApply can't reasonably assume that the key is unique. +# e.g. user may call it multiple times with different arguments and expect each of the module to apply. +# While this is excusable for the disabledModules aspect, it is not for the deduplication of modules. +checkConfigOutput '"abc"' config.value ./importApply-disabling.nix + # Check disabledModules with config definitions and option declarations. set -- config.enable ./define-enable.nix ./declare-enable.nix checkConfigOutput '^true$' "$@" diff --git a/lib/tests/modules/importApply-disabling.nix b/lib/tests/modules/importApply-disabling.nix new file mode 100644 index 000000000000..016036dd8af5 --- /dev/null +++ b/lib/tests/modules/importApply-disabling.nix @@ -0,0 +1,4 @@ +{ + imports = [ ./importApply.nix ]; + disabledModules = [ ./importApply-function.nix ]; +} diff --git a/lib/tests/modules/importApply-function.nix b/lib/tests/modules/importApply-function.nix new file mode 100644 index 000000000000..7c193a912d49 --- /dev/null +++ b/lib/tests/modules/importApply-function.nix @@ -0,0 +1,5 @@ +{ foo }: +{ lib, config, ... }: +{ + value = foo; +} diff --git a/lib/tests/modules/importApply.nix b/lib/tests/modules/importApply.nix new file mode 100644 index 000000000000..d3b893cb34e2 --- /dev/null +++ b/lib/tests/modules/importApply.nix @@ -0,0 +1,5 @@ +{ lib, ... }: +{ + options.value = lib.mkOption { default = 1; }; + imports = [ (lib.modules.importApply ./importApply-function.nix { foo = "abc"; }) ]; +} -- cgit 1.4.1