···59 };
60 };
6162- closed = closeModules (modules ++ [ internalModule ]) ({ inherit config options lib; } // specialArgs);
0006364- options = mergeModules prefix (reverseList (filterModules (specialArgs.modulesPath or "") closed));
6566 # Traverse options and extract the option values into the final
67 # config set. At the same time, check whether all option
···87 result = { inherit options config; };
88 in result;
89000009091- # Filter disabled modules. Modules can be disabled allowing
92- # their implementation to be replaced.
93- filterModules = modulesPath: modules:
94- let
95- moduleKey = m: if isString m then toString modulesPath + "/" + m else toString m;
96- disabledKeys = map moduleKey (concatMap (m: m.disabledModules) modules);
97- in
98- filter (m: !(elem m.key disabledKeys)) modules;
99100- /* Close a set of modules under the ‘imports’ relation. */
101- closeModules = modules: args:
102- let
103- toClosureList = file: parentKey: imap1 (n: x:
104- if isAttrs x || isFunction x then
105- let key = "${parentKey}:anon-${toString n}"; in
106- unifyModuleSyntax file key (applyIfFunction key x args)
107- else
108- let file = toString x; key = toString x; in
109- unifyModuleSyntax file key (applyIfFunction key (import x) args));
110- in
111- builtins.genericClosure {
112- startSet = toClosureList unknownModule "" modules;
113- operator = m: toClosureList m._file m.key m.imports;
114- };
0000000000000000000000000000000000000000115116 /* Massage a module into canonical form, that is, a set consisting
117 of ‘options’, ‘config’ and ‘imports’ attributes. */
···59 };
60 };
6162+ collected = collectModules
63+ (specialArgs.modulesPath or "")
64+ (modules ++ [ internalModule ])
65+ ({ inherit config options lib; } // specialArgs);
6667+ options = mergeModules prefix (reverseList collected);
6869 # Traverse options and extract the option values into the final
70 # config set. At the same time, check whether all option
···90 result = { inherit options config; };
91 in result;
9293+ # collectModules :: (modulesPath: String) -> (modules: [ Module ]) -> (args: Attrs) -> [ Module ]
94+ #
95+ # Collects all modules recursively through `import` statements, filtering out
96+ # all modules in disabledModules.
97+ collectModules = let
9899+ # Like unifyModuleSyntax, but also imports paths and calls functions if necessary
100+ loadModule = args: fallbackFile: fallbackKey: m:
101+ if isFunction m || isAttrs m then
102+ unifyModuleSyntax fallbackFile fallbackKey (applyIfFunction fallbackKey m args)
103+ else unifyModuleSyntax (toString m) (toString m) (applyIfFunction (toString m) (import m) args);
104+105+ /*
106+ Collects all modules recursively into the form
107108+ {
109+ disabled = [ <list of disabled modules> ];
110+ # All modules of the main module list
111+ modules = [
112+ {
113+ key = <key1>;
114+ module = <module for key1>;
115+ # All modules imported by the module for key1
116+ modules = [
117+ {
118+ key = <key1-1>;
119+ module = <module for key1-1>;
120+ # All modules imported by the module for key1-1
121+ modules = [ ... ];
122+ }
123+ ...
124+ ];
125+ }
126+ ...
127+ ];
128+ }
129+ */
130+ collectStructuredModules =
131+ let
132+ collectResults = modules: {
133+ disabled = concatLists (catAttrs "disabled" modules);
134+ inherit modules;
135+ };
136+ in parentFile: parentKey: initialModules: args: collectResults (imap1 (n: x:
137+ let
138+ module = loadModule args parentFile "${parentKey}:anon-${toString n}" x;
139+ collectedImports = collectStructuredModules module._file module.key module.imports args;
140+ in {
141+ key = module.key;
142+ module = module;
143+ modules = collectedImports.modules;
144+ disabled = module.disabledModules ++ collectedImports.disabled;
145+ }) initialModules);
146+147+ # filterModules :: String -> { disabled, modules } -> [ Module ]
148+ #
149+ # Filters a structure as emitted by collectStructuredModules by removing all disabled
150+ # modules recursively. It returns the final list of unique-by-key modules
151+ filterModules = modulesPath: { disabled, modules }:
152+ let
153+ moduleKey = m: if isString m then toString modulesPath + "/" + m else toString m;
154+ disabledKeys = listToAttrs (map (k: nameValuePair (moduleKey k) null) disabled);
155+ keyFilter = filter (attrs: ! disabledKeys ? ${attrs.key});
156+ in map (attrs: attrs.module) (builtins.genericClosure {
157+ startSet = keyFilter modules;
158+ operator = attrs: keyFilter attrs.modules;
159+ });
160+161+ in modulesPath: initialModules: args:
162+ filterModules modulesPath (collectStructuredModules unknownModule "" initialModules args);
163164 /* Massage a module into canonical form, that is, a set consisting
165 of ‘options’, ‘config’ and ‘imports’ attributes. */
···6 <title>Replace Modules</title>
78 <para>
9- Modules that are imported can also be disabled. The option declarations and
10- config implementation of a disabled module will be ignored, allowing another
11 to take it's place. This can be used to import a set of modules from another
12 channel while keeping the rest of the system on a stable release.
13 </para>
···6 <title>Replace Modules</title>
78 <para>
9+ Modules that are imported can also be disabled. The option declarations,
10+ config implementation and the imports of a disabled module will be ignored, allowing another
11 to take it's place. This can be used to import a set of modules from another
12 channel while keeping the rest of the system on a stable release.
13 </para>