nix-community / home-manager

Manage a user environment using Nix [maintainer=@rycee]
https://nix-community.github.io/home-manager/
MIT License
7.01k stars 1.81k forks source link

Using _module.args with flakes #1642

Closed badly-drawn-wizards closed 3 years ago

badly-drawn-wizards commented 3 years ago

I am trying to pass flake inputs into home-manager modules using _module.args, but it is resulting in infinite recursion, likely due to it not being in the arguments at all.

Here is the configuration used: https://github.com/badly-drawn-wizards/dotfiles/tree/8165aca4ff7bf685a41bac830b36cd7473830c90/nixos

Here is the site where I attempt to use _module.args, I have tried other varients mentioned in the issue linked below: https://github.com/badly-drawn-wizards/dotfiles/blob/8165aca4ff7bf685a41bac830b36cd7473830c90/nixos/home-manager/default.nix

Here is where I attempt to use the inputs argument in a home-manager module: https://github.com/badly-drawn-wizards/dotfiles/blob/8165aca4ff7bf685a41bac830b36cd7473830c90/nixos/home-manager/emacs.nix if it is not forced, then no infinite recursion occurs.

Requested by @berbiche in https://github.com/nix-community/home-manager/issues/1538#issuecomment-738949861

berbiche commented 3 years ago

Can you remove the call to builtins.seq? This will force the evaluation of all your inputs (assuming inputs is defined as outputs = inputs @ { ... }), which also includes the special self IIRC. Evaluating self will cause recursion.

badly-drawn-wizards commented 3 years ago

Removing the seq with no reference to inputs will build. Uncommenting

  imports = [inputs.nix-doom-emacs.hmModule];

causes the another (possibly the same) infinite recursion.

berbiche commented 3 years ago

I looked into my own configuration (because I also use nix-doom-emacs) and I'm not sure what would cause the infinite recursion. Could you share the full log?

badly-drawn-wizards commented 3 years ago
error: --- EvalError ------------------------------------------------------------------------------------------------------------------------------------------------------------------- nix
at: (303:28) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   302|         value = builtins.addErrorContext (context name)
   303|           (args.${name} or config._module.args.${name});
      |                            ^
   304|       }) requiredArgs);

infinite recursion encountered
---------------------------------------------------------------------------------------- show-trace ----------------------------------------------------------------------------------------
trace: while evaluating the module argument `inputs' in "/nix/store/ssl4k3wakkqgprz8ajidl86lnqzh6bf8-source/nixos/home-manager/emacs.nix":
trace: while evaluating 'isFunction'
at: (339:16) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/trivial.nix

   338|   */
   339|   isFunction = f: builtins.isFunction f ||
      |                ^
   340|     (f ? __functor && isFunction (f.__functor f));

trace: from call site
at: (189:12) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   188|       loadModule = args: fallbackFile: fallbackKey: m:
   189|         if isFunction m || isAttrs m then
      |            ^
   190|           unifyModuleSyntax fallbackFile fallbackKey (applyIfFunction fallbackKey m args)

trace: while evaluating 'loadModule'
at: (188:53) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   187|       # Like unifyModuleSyntax, but also imports paths and calls functions if necessary
   188|       loadModule = args: fallbackFile: fallbackKey: m:
      |                                                     ^
   189|         if isFunction m || isAttrs m then

trace: from call site
at: (226:22) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   225|           let
   226|             module = loadModule args parentFile "${parentKey}:anon-${toString n}" x;
      |                      ^
   227|             collectedImports = collectStructuredModules module._file module.key module.imports args;

trace: while evaluating the attribute 'disabled'
at: (221:13) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   220|           collectResults = modules: {
   221|             disabled = concatLists (catAttrs "disabled" modules);
      |             ^
   222|             inherit modules;

trace: while evaluating the attribute 'disabled'
at: (221:13) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   220|           collectResults = modules: {
   221|             disabled = concatLists (catAttrs "disabled" modules);
      |             ^
   222|             inherit modules;

trace: while evaluating the attribute 'disabled'
at: (221:13) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   220|           collectResults = modules: {
   221|             disabled = concatLists (catAttrs "disabled" modules);
      |             ^
   222|             inherit modules;

trace: while evaluating anonymous lambda
at: (243:31) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   242|           disabledKeys = map moduleKey disabled;
   243|           keyFilter = filter (attrs: ! elem attrs.key disabledKeys);
      |                               ^
   244|         in map (attrs: attrs.module) (builtins.genericClosure {

trace: from call site
trace: while evaluating 'filterModules'
at: (239:36) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   238|       # modules recursively. It returns the final list of unique-by-key modules
   239|       filterModules = modulesPath: { disabled, modules }:
      |                                    ^
   240|         let

trace: from call site
at: (250:7) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   249|     in modulesPath: initialModules: args:
   250|       filterModules modulesPath (collectStructuredModules unknownModule "" initialModules args);
      |       ^
   251| 

trace: while evaluating anonymous lambda
at: (249:37) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   248| 
   249|     in modulesPath: initialModules: args:
      |                                     ^
   250|       filterModules modulesPath (collectStructuredModules unknownModule "" initialModules args);

trace: from call site
at: (127:25) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   126|       merged =
   127|         let collected = collectModules
      |                         ^
   128|           (specialArgs.modulesPath or "")

trace: while evaluating 'reverseList'
at: (393:17) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/lists.nix

   392|   */
   393|   reverseList = xs:
      |                 ^
   394|     let l = length xs; in genList (n: elemAt xs (l - n - 1)) l;

trace: from call site
at: (131:33) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   130|           ({ inherit lib options config; } // specialArgs);
   131|         in mergeModules prefix (reverseList collected);
      |                                 ^
   132| 

trace: while evaluating 'byName'
at: (362:25) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   361|       */
   362|       byName = attr: f: modules:
      |                         ^
   363|         foldl' (acc: module:

trace: from call site
at: (370:21) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   369|       # an attrset 'name' => list of submodules that declare ‘name’.
   370|       declsByName = byName "options" (module: option:
      |                     ^
   371|           [{ inherit (module) _file; options = option; }]

trace: while evaluating 'flip'
at: (138:16) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/trivial.nix

   137|   */
   138|   flip = f: a: b: f b a;
      |                ^
   139| 

trace: from call site
at: (382:23) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   381| 
   382|       resultsByName = flip mapAttrs declsByName (name: decls:
      |                       ^
   383|         # We're descending into attribute ‘name’.

trace: while evaluating the attribute 'matchedOptions'
at: (414:14) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   413|     in {
   414|       inherit matchedOptions;
      |              ^
   415| 

trace: while evaluating 'recurse'
at: (273:23) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/attrsets.nix

   272|     let
   273|       recurse = path: set:
      |                       ^
   274|         let

trace: from call site
at: (281:8) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/attrsets.nix

   280|         in mapAttrs g set;
   281|     in recurse [] set;
      |        ^
   282| 

trace: while evaluating 'mapAttrsRecursiveCond'
at: (271:36) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/attrsets.nix

   270|   */
   271|   mapAttrsRecursiveCond = cond: f: set:
      |                                    ^
   272|     let

trace: from call site
at: (139:28) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   138|           # For definitions that have an associated option
   139|           declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
      |                            ^
   140| 

trace: while evaluating 'evalModules'
at: (62:17) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

    61|      evalModules) and the less declarative the module set is. */
    62|   evalModules = { modules
      |                 ^
    63|                 , prefix ? []

trace: from call site
at: (492:12) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/types.nix

   491|         merge = loc: defs:
   492|           (evalModules {
      |            ^
   493|             modules = allModules defs;

trace: while evaluating 'merge'
at: (491:22) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/types.nix

   490|         check = x: isAttrs x || isFunction x || path.check x;
   491|         merge = loc: defs:
      |                      ^
   492|           (evalModules {

trace: from call site
at: (546:59) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   545|       if isDefined then
   546|         if all (def: type.check def.value) defsFinal then type.merge loc defsFinal
      |                                                           ^
   547|         else let allInvalid = filter (def: ! type.check def.value) defsFinal;

trace: while evaluating the attribute 'value'
at: (557:27) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   556|     optionalValue =
   557|       if isDefined then { value = mergedValue; }
      |                           ^
   558|       else {};

trace: while evaluating anonymous lambda
at: (382:22) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/types.nix

   381|       merge = loc: defs:
   382|         mapAttrs (n: v: v.value) (filterAttrs (n: v: v ? value) (zipAttrsWith (name: defs:
      |                      ^
   383|             (mergeDefinitions (loc ++ [name]) elemType defs).optionalValue

trace: from call site
trace: while evaluating the attribute 'reuben'
trace: while evaluating 'flip'
at: (138:16) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/trivial.nix

   137|   */
   138|   flip = f: a: b: f b a;
      |                ^
   139| 

trace: from call site
at: (85:7) in file: /nix/store/fq9gz540q9z2w2ikv5gbglsf16kxard9-source/nixos/default.nix

    84|     assertions = flatten (flip mapAttrsToList cfg.users (user: config:
    85|       flip map config.assertions (assertion: {
      |       ^
    86|         inherit (assertion) assertion;

trace: while evaluating anonymous lambda
at: (84:64) in file: /nix/store/fq9gz540q9z2w2ikv5gbglsf16kxard9-source/nixos/default.nix

    83| 
    84|     assertions = flatten (flip mapAttrsToList cfg.users (user: config:
      |                                                                ^
    85|       flip map config.assertions (assertion: {

trace: from call site
at: (234:16) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/attrsets.nix

   233|   mapAttrsToList = f: attrs:
   234|     map (name: f name attrs.${name}) (attrNames attrs);
      |                ^
   235| 

trace: while evaluating anonymous lambda
at: (234:10) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/attrsets.nix

   233|   mapAttrsToList = f: attrs:
   234|     map (name: f name attrs.${name}) (attrNames attrs);
      |          ^
   235| 

trace: from call site
trace: while evaluating 'flatten'
at: (137:13) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/lists.nix

   136|   */
   137|   flatten = x:
      |             ^
   138|     if isList x

trace: from call site
at: (139:24) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/lists.nix

   138|     if isList x
   139|     then concatMap (y: flatten y) x
      |                        ^
   140|     else [x];

trace: while evaluating anonymous lambda
at: (139:21) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/lists.nix

   138|     if isList x
   139|     then concatMap (y: flatten y) x
      |                     ^
   140|     else [x];

trace: from call site
at: (139:10) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/lists.nix

   138|     if isList x
   139|     then concatMap (y: flatten y) x
      |          ^
   140|     else [x];

trace: while evaluating 'flatten'
at: (137:13) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/lists.nix

   136|   */
   137|   flatten = x:
      |             ^
   138|     if isList x

trace: from call site
at: (84:18) in file: /nix/store/fq9gz540q9z2w2ikv5gbglsf16kxard9-source/nixos/default.nix

    83| 
    84|     assertions = flatten (flip mapAttrsToList cfg.users (user: config:
      |                  ^
    85|       flip map config.assertions (assertion: {

trace: while evaluating the attribute 'content'
at: (680:14) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   679|     { _type = "if";
   680|       inherit condition content;
      |              ^
   681|     };

trace: while evaluating 'dischargeProperties'
at: (596:25) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   595|   */
   596|   dischargeProperties = def:
      |                         ^
   597|     if def._type or "" == "merge" then

trace: from call site
at: (602:11) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   601|         if def.condition then
   602|           dischargeProperties def.content
      |           ^
   603|         else

trace: while evaluating 'dischargeProperties'
at: (596:25) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   595|   */
   596|   dischargeProperties = def:
      |                         ^
   597|     if def._type or "" == "merge" then

trace: from call site
at: (525:137) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   524|         defs' = concatMap (m:
   525|           map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
      |                                                                                                                                         ^
   526|         ) defs;

trace: while evaluating definitions from `/nix/store/ssl4k3wakkqgprz8ajidl86lnqzh6bf8-source/nixos/home-manager':
trace: while evaluating anonymous lambda
at: (524:28) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   523|         # Process mkMerge and mkIf properties.
   524|         defs' = concatMap (m:
      |                            ^
   525|           map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))

trace: from call site
at: (524:17) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   523|         # Process mkMerge and mkIf properties.
   524|         defs' = concatMap (m:
      |                 ^
   525|           map (value: { inherit (m) file; inherit value; }) (builtins.addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))

trace: while evaluating the attribute 'values'
at: (637:7) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   636|     in {
   637|       values = concatMap (def: if getPrio def == highestPrio then [(strip def)] else []) defs;
      |       ^
   638|       inherit highestPrio;

trace: while evaluating the attribute 'values'
at: (538:9) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   537|       in {
   538|         values = defs''';
      |         ^
   539|         inherit (defs'') highestPrio;

trace: while evaluating the attribute 'mergedValue'
at: (544:5) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   543|     # Type-check the remaining definitions, and merge them. Or throw if no definitions.
   544|     mergedValue =
      |     ^
   545|       if isDefined then

trace: while evaluating the option `assertions':
trace: while evaluating the attribute 'value'
at: (512:9) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   511|     in warnDeprecation opt //
   512|       { value = builtins.addErrorContext "while evaluating the option `${showOption loc}':" value;
      |         ^
   513|         inherit (res.defsFinal') highestPrio;

trace: while evaluating anonymous lambda
at: (139:72) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/modules.nix

   138|           # For definitions that have an associated option
   139|           declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
      |                                                                        ^
   140| 

trace: from call site
at: (279:20) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/attrsets.nix

   278|               then recurse (path ++ [name]) value
   279|               else f (path ++ [name]) value;
      |                    ^
   280|         in mapAttrs g set;

trace: while evaluating 'g'
at: (276:19) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/attrsets.nix

   275|           g =
   276|             name: value:
      |                   ^
   277|             if isAttrs value && cond value

trace: from call site
trace: while evaluating the attribute 'assertions'
trace: while evaluating 'fold''
at: (55:15) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/lists.nix

    54|       len = length list;
    55|       fold' = n:
      |               ^
    56|         if n == len

trace: from call site
at: (59:8) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/lists.nix

    58|         else op (elemAt list n) (fold' (n + 1));
    59|     in fold' 0;
      |        ^
    60| 

trace: while evaluating 'foldr'
at: (52:20) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/lib/lists.nix

    51|   */
    52|   foldr = op: nul: list:
      |                    ^
    53|     let

trace: from call site
at: (129:12) in file: /nix/store/n8gjar46rfb7bx74fw1kpp0p668dqplf-source/nixos/modules/system/activation/top-level.nix

   128|   # Replace runtime dependencies
   129|   system = fold ({ oldDependency, newDependency }: drv:
      |            ^
   130|       pkgs.replaceDependency { inherit oldDependency newDependency drv; }
badly-drawn-wizards commented 3 years ago

I suspect for some reason the _module.args is not taking effect. If I would use something like

{{...}@lolDoesNotExist, ...}: {}

as a home-manager module it would give the same error.

berbiche commented 3 years ago

I think the issue will be resolved by passing the inputs directly to home-manager within your flake.nix instead of taking it from the specialArgs of NixOS modules.

{
  outputs = inputs: {
    nixosConfigurations.noobnoob = inputs.nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      specialArgs = {
        inherit inputs;
      };
      modules = [
        ./configuration.nix
        # The next two lines
        ./home-manager.nix
        ({ ... }: {
          home-manager.user.your-user.config = {
            _module.args.inputs = inputs;
          };
        })
      ];
    };
  };
}
badly-drawn-wizards commented 3 years ago

It doesn't seem to work. Got same error. Pushed the changes.

berbiche commented 3 years ago

Do you still have the same exact log?

Also your ./doom should be ../doom in the configuration you pass to nix-doom-emacs.

rycee commented 3 years ago

The

imports = [inputs.nix-doom-emacs.hmModule];

line won't work since imports must be evaluated before finding the config fixpoint. But _module.args.inputs = inputs is an option assignment in config, so to evaluate inputs.nix-doom-emacs.hmModule you must first have the config fixpoint. In other words, you have a circular dependency.

The devious thing is that the imports field looks like an option in this case but it is actually not. A module

{ inputs, ... }:

{
  imports = [inputs.nix-doom-emacs.hmModule];
  home.sessionVariables.EDITOR = "em";
}

will be converted to something like

{ input, ... }:
{
  imports = [inputs.nix-doom-emacs.hmModule];
  options = {};
  config = {
    home.sessionVariables.EDITOR = "em";
  };
}

and input is safe to use inside the config field, but not the imports one.

badly-drawn-wizards commented 3 years ago

Thanks, by moving inputs out of home-manager modules, I fixed my problem.