从Nix模块选项部分引用pkgs.system时的无限递归



以下是构建nixos配置时无限递归错误的最小再现器:

(import <nixpkgs/nixos>) {
configuration = { pkgs, ... }: {
options = builtins.trace "Building a system with system ${pkgs.system}" {};
};
system = "x86_64-linux";
}

当评估时,它失败如下,除非删除对pkgs.system的引用:

$ nix-build 
error: infinite recursion encountered
at /Users/charles/.nix-defexpr/channels/nixpkgs/lib/modules.nix:496:28:
495|         builtins.addErrorContext (context name)
496|           (args.${name} or config._module.args.${name})
|                            ^
497|       ) (lib.functionArgs f);

如果我们查看nixos/lib/eval-config.nix:33的实现,我们会发现为system参数传递的值在pkgs中被设置为可重写的默认值。这是否意味着我们要到评估过程的后期才能访问它?

(在真实世界的用例中,我正在内省一个薄片——研究someFlake.packages.${pkgs.system},以找到要为其生成配置选项的包。(

这已经被交叉发布到NixOS话语;看见https://discourse.nixos.org/t/accessing-target-system-when-building-options-for-a-module/

为了让模块系统构建配置,它需要知道存在哪些configoptions项,至少到了生成配置的根属性集所需的程度。

循环如下:

  1. 评估config中的属性名称
  2. 评估options的属性名称
  3. 评估pkgs(您的代码(
  4. 评估config._module.args.pkgs(模块参数的定义(
  5. 评估config中的属性名称(循环(

可以通过删除或减少对pkgs的依赖来破坏它。例如,您可以定义您的";动态的";选项为type = attrsOf foo,而不是将薄片中的每个项目枚举为单独的选项。

另一个潜在的解决方案是将选项定义移动到子模块中。attrsOf (submodule x)中没有attrsOf的子模块通常是无用的,但它可能会创建一个必要的间接方法,将动态pkgs相关的options与具有pkgs的模块固定点分离。

(import <nixpkgs/nixos>) {
configuration = { pkgs, lib, ... }: {
options.foo = lib.mkOption {
type = lib.types.submodule {
options = builtins.trace "Building a system with system ${pkgs.system}" { };
};
default = { };
};
};
system = "x86_64-linux";
}
nix-repl> config.foo
trace: Building a system with system x86_64-linux
{ }

作为避免递归不可行的情况的替代方法,可以在调用nixos/lib/eval-config.nix时使用specialArgs来传递无法通过模块系统覆盖的最终值:

let
configuration = { pkgs, forcedSystem, ... }: {
options = builtins.trace "Building a system with system ${forcedSystem}" {};
};
in
(import <nixpkgs/nixos/lib/eval-config.nix>) {
modules = [ configuration ];
system = "x86_64-linux";
specialArgs = { forcedSystem = "x86_64-linux"; };
}

相关内容

  • 没有找到相关文章

最新更新