-
-
Notifications
You must be signed in to change notification settings - Fork 15.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Apply disabledModules
recursively
#76857
Conversation
Nix doesn't have undefined behavior afaik. I think what you mean is that the output depends on details in the input that should not matter much. |
4e47c4b
to
47b9fb0
Compare
Evaluating
So this change makes it about 2.7% slower, not too bad. For more stats, see ofborg's result here: https://github.com/NixOS/nixpkgs/pull/76857/checks?check_run_id=373281930 |
I'll try to optimize it a bit further though, I think there's some potential |
Nope, the potential I saw turned out to be an anti-optimization. Apart from perhaps adding some more comments to the code itself, I think this is pretty much ready. |
lib/modules.nix
Outdated
# it out to a direct `key -> module` mapping while also filtering disabled | ||
# keys | ||
flattenKeys = keys: | ||
let included = filterAttrs (key: value: ! elem key disabledKeys) keys; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you try builtins.removeAttrs
? Unlike filterAttrs
, it's a primop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah nice, didn't even occur to me
With that change I get:
Not sure why this time the times are a bit faster than last time, the raw values are 210.574, 212.35, 205.356, 210.043, 214.301, 214.593. Not sure what's up with that 205.356 there either. With those means we get a decrease of 2.9%, so it's about the same. But it certainly can't hurt to use |
36542bd
to
fda9bf8
Compare
fda9bf8
to
5d1cccf
Compare
Moved |
5d1cccf
to
8db41d4
Compare
Using the faster with |
lib/modules.nix
Outdated
let | ||
moduleKey = m: if isString m then toString modulesPath + "/" + m else toString m; | ||
disabledKeys = map moduleKey disabled; | ||
keyFilter = filter (attrs: ! elem attrs.key disabledKeys); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will become slow when more than very few modules are disabled. Can we change this to disabledKeys ? attrs.key
by making disabledKeys
an attrset?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe it shouldn't get slower, because disabledKeys ? ${attrs.key}
seems to be linear in disabledKeys
.. See https://github.com/NixOS/nix/blob/04bbfa692f4ac506a74ae372eca486bd9b56de77/src/libexpr/attr-set.hh#L67-L73
So the elem
(also a primop) essentially implements ?
but for a list.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::lower_bound
is in fact a binary search. See the docs and impl references on https://en.cppreference.com/w/cpp/algorithm/lower_bound
8db41d4
to
41e44f6
Compare
let | ||
moduleKey = m: if isString m then toString modulesPath + "/" + m else toString m; | ||
disabledKeys = listToAttrs (map (k: nameValuePair (moduleKey k) null) disabled); | ||
keyFilter = filter (attrs: ! disabledKeys ? ${attrs.key}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@roberth Now doing this. Was able to confirm slight memory decrease with this approach for ~25 disabled modules
lib/modules.nix
Outdated
loadModule = args: fallbackFile: fallbackKey: m: | ||
if isFunction m || isAttrs m then | ||
unifyModuleSyntax fallbackFile fallbackKey (applyIfFunction fallbackKey m args) | ||
else loadModule args (toString m) (toString m) (import m); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This allows a module file to consist of a path to be imported. That doesn't seem very useful, yet introduces infinite recursion as a failure mode here. Calling unifyModuleSyntax
directly doesn't seem so bad.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, doesn't seem too bad with a bit of duplication. Did that
With this change, disabledModules applies recursively, meaning if you have a module "foo.nix" with imports = [ ./bar.nix ]; then setting disabledModules = [ "foo.nix" ]; will disable both "foo.nix" and "bar.nix", whereas previously only "foo.nix" would be disabled. This change along with NixOS#61570 allows modules to be fully disabled even when they have some `mkRenamedOption` imports.
Previously disabledModules would not be disabled for the manual
41e44f6
to
a6462a4
Compare
Thanks for the review :D |
After this change, I am unable to use store paths in my module imports. For example, something like: {
imports = [ "${sources.home-manager}/nixos" ];
...
} now fails to evaluate with:
Is this intentional? If so, is there a workaround? |
Oh thanks for the report. Turns out the changes in #76857 (comment) are causing this. I'll make a PR to fix it |
This fixes imports from the store not being possible, which was caused by NixOS#76857 E.g. such a case: imports = [ "${home-manager}/nixos" ];
See above PR which fixes this |
As uncovered by @evanjs on IRC, this introduces a new case of infinite recursion when imports have a cycle in them, e.g. a module {
imports = [ ./foo.nix ];
} now throws
While I do have a fix for this, I don't think this use-case needs to be supported as I don't know of a case where something like this is needed. To fix this, just break the cycle by not importing the same module again. So you can say that imports now have to be a DAG instead of an arbitrary graph. Fix is here in case this is needed in the future: diff --git a/lib/modules.nix b/lib/modules.nix
index e2315290ff0..82981dba73b 100644
--- a/lib/modules.nix
+++ b/lib/modules.nix
@@ -139,16 +139,22 @@ rec {
disabled = concatLists (catAttrs "disabled" modules);
inherit modules;
};
- in parentFile: parentKey: initialModules: args: collectResults (imap1 (n: x:
+ in parentPath: parentFile: parentKey: initialModules: args:
let
- module = loadModule args parentFile "${parentKey}:anon-${toString n}" x;
- collectedImports = collectStructuredModules module._file module.key module.imports args;
+ # All modules this module imports, loaded and without modules that
+ # are anywhere on the parent path to prevent cycles
+ includedModules = filter (module: ! elem module.key parentPath) (imap1 (n:
+ loadModule args parentFile "${parentKey}:anon-${toString n}"
+ ) initialModules);
+ in collectResults (map (module:
+ let
+ collectedImports = collectStructuredModules (parentPath ++ [ module.key ]) module._file module.key module.imports args;
in {
key = module.key;
module = module;
modules = collectedImports.modules;
disabled = module.disabledModules ++ collectedImports.disabled;
- }) initialModules);
+ }) includedModules);
# filterModules :: String -> { disabled, modules } -> [ Module ]
#
@@ -165,7 +171,7 @@ rec {
});
in modulesPath: initialModules: args:
- filterModules modulesPath (collectStructuredModules unknownModule "" initialModules args);
+ filterModules modulesPath (collectStructuredModules [""] unknownModule "" initialModules args);
/* Massage a module into canonical form, that is, a set consisting
of ‘options’, ‘config’ and ‘imports’ attributes. */ |
Just wanted to confirm that a dry build without @infinisil's patch does reproduce this issue ( |
This fixes imports from the store not being possible, which was caused by NixOS#76857 E.g. such a case: imports = [ "${home-manager}/nixos" ]; (cherry picked from commit e0ea5f4)
Motivation for this change
If you have a module
foo.nix
as follows:And then set
The module
bar.nix
would still be included, even thoughfoo.nix
which imports it isn't anymore. This is problematic for e.g.mkRenamedOptionModule
's, because none of them would be disabled if the module defining them was disabled, meaning all the renamed options would still apply. See #61570 for more info on this. This fixes it.To be able to implement this correctly, I had to completely restructure how the module system collects modules. While a simpler implementation would be possible, it then shows undefined behavior when e.g. two modules
foo.nix
andbar.nix
both importqux.nix
, but only one offoo.nix
/bar.nix
gets disabled. In the simpler implementation,qux.nix
would only be imported for one of them, whereas with this more correct implementation it will get imported in both cases.Ping @roberth @danbst @nbp @rycee @Profpatsch @aanderse
Things done