diff --git a/pkgs/lib/misc.nix b/pkgs/lib/misc.nix index a8f37ec277b..dc73a2f6886 100644 --- a/pkgs/lib/misc.nix +++ b/pkgs/lib/misc.nix @@ -208,20 +208,24 @@ rec { (filter (__hasAttr name) sets)); }) (concatMap builtins.attrNames sets)); - # flatten a list of elements by following the properties of the elements. - # next : return the list of following elements. - # seen : lists of elements already visited. - # default: result if 'x' is empty. - # x : list of values that have to be processed. - uniqFlatten = next: seen: default: x: - if x == [] - then default - else - let h = head x; t = tail x; n = next h; in - if elem h seen - then uniqFlatten next seen default t - else uniqFlatten next (seen ++ [h]) (default ++ [h]) (n ++ t) - ; + + lazyGenericClosure = {startSet, operator}: + let + work = list: doneKeys: result: + if list == [] then + result + else + let x = head list; key = x.key; in + if elem key doneKeys then + work (tail list) doneKeys result + else + work (tail list ++ operator x) ([key] ++ doneKeys) ([x] ++ result); + in + work startSet [] []; + + genericClosure = + if builtins ? genericClosure then builtins.genericClosure + else lazyGenericClosure; innerModifySumArgs = f: x: a: b: if b == null then (f a b) // x else innerModifySumArgs f x (a // b); diff --git a/pkgs/lib/options.nix b/pkgs/lib/options.nix index 1e3dab80bb5..ca6173ef086 100644 --- a/pkgs/lib/options.nix +++ b/pkgs/lib/options.nix @@ -78,7 +78,7 @@ rec { merge = list: decl.type.iter (path: opts: - fixMergeFun (recurseInto path) (optionConfig opts) + lib.fix (fixableMergeFun (recurseInto path) (optionConfig opts)) ) opt.name (opt.merge list); @@ -262,6 +262,12 @@ rec { || builtins.isList x ); + importIfPath = path: + if isPath path then + import path + else + path; + applyIfFunction = f: arg: if builtins.isFunction f then f arg @@ -270,106 +276,87 @@ rec { moduleClosure = initModules: args: let - moduleImport = path: - (applyIfFunction (import path) args) // { + moduleImport = m: + (applyIfFunction (importIfPath m) args) // { # used by generic closure to avoid duplicated imports. - key = path; + key = m; }; + + removeKeys = list: map (m: removeAttrs m ["key"]) list; + + getImports = m: + if m ? config || m ? options then + attrByPath ["imports"] [] m + else + toList (rmProperties (attrByPath ["require"] [] (delayProperties m))); + + getImportedPaths = m: filter isPath (getImports m); + getImportedSets = m: filter (x: !isPath x) (getImports m); + + inlineImportedSets = list: + lib.concatMap (m:[m] ++ map moduleImport (getImportedSets m)) list; in - builtins.genericClosure { + inlineImportedSets (removeKeys (lazyGenericClosure { startSet = map moduleImport initModules; - operator = m: - map moduleImport (attrByPath ["imports"] [] m); - }; + operator = m: map moduleImport (getImportedPaths m); + })); selectDeclsAndDefs = modules: lib.concatMap (m: - attrByPath ["options"] [] m - ++ attrByPath ["config"] [] m + if m ? config || m ? options then + attrByPath ["options"] [] m + ++ attrByPath ["config"] [] m + else + [ m ] ) modules; - fixMergeFun = merge: optFun: - lib.fix (config: - merge ( + + fixableMergeFun = merge: f: config: + merge ( + # remove require because this is not an option. + map (m: removeAttrs m ["require"]) ( # Delay top-level properties like mkIf map delayProperties ( # generate the list of option sets. - optFun config + f config ) ) ); - fixMergeModules = merge: initModules: {...}@args: - fixMergeFun (config: + fixableMergeModules = merge: initModules: {...}@args: config: + fixableMergeFun merge (config: + # filter the list of option sets. selectDeclsAndDefs ( + # generate the list of modules from a closure of imports/require + # attribtues. moduleClosure initModules (args // { inherit config; }) ) - ); - - fixModulesConfig = initModules: {...}@args: - fixMergeModules (mergeOptionSets "") initModules args; - - fixOptionsConfig = initModules: {...}@args: - fixMergeModules (filterOptionSets "") initModules args; + ) config; - # Evaluate a list of option sets that would be merged with the - # function "merge" which expects two arguments. The attribute named - # "require" is used to imports option declarations and bindings. - # - # * cfg[0-9]: configuration - # * cfgSet[0-9]: configuration set - # - # merge: the function used to merge options sets. - # args: is the set of packages available. (nixpkgs) - # opts: list of option sets or option set functions. - # config: result of this evaluation. - fixOptionSetsFun = merge: args: opts: config: - let - # remove possible mkIf to access the require attribute. - noImportConditions = cfgSet0: - let cfgSet1 = delayProperties cfgSet0; in - if cfgSet1 ? require then - cfgSet1 // { require = rmProperties cfgSet1.require; } - else - cfgSet1; + fixableDefinitionsOf = initModules: {...}@args: + fixableMergeModules (mergeOptionSets "") initModules args; - filenameHandler = cfg: - if isPath cfg then import cfg else cfg; + fixableDeclarationsOf = initModules: {...}@args: + fixableMergeModules (filterOptionSets "") initModules args; - argumentHandler = cfg: - applyIfFunction cfg (args // { inherit config; }); + definitionsOf = initModules: {...}@args: + lib.fix (fixableDefinitionsOf initModules args); - preprocess = cfg0: - let cfg1 = filenameHandler cfg0; - cfg2 = argumentHandler cfg1; - cfg3 = noImportConditions cfg2; - in cfg3; + declarationsOf = initModules: {...}@args: + lib.fix (fixableDeclarationsOf initModules args); - getRequire = x: toList (attrByPath ["require"] [] (preprocess x)); - getRecursiveRequire = x: - fold (cfg: l: - if isPath cfg then - [ cfg ] ++ l - else - [ cfg ] ++ (getRecursiveRequire cfg) ++ l - ) [] (getRequire x); - getRequireSets = x: filter (x: ! isPath x) (getRecursiveRequire x); - getRequirePaths = x: filter isPath (getRecursiveRequire x); - rmRequire = x: removeAttrs (preprocess x) ["require"]; + fixMergeModules = merge: initModules: {...}@args: + lib.fix (fixableMergeModules merge initModules args); - inlineRequiredSets = cfgs: - fold (cfg: l: [ cfg ] ++ (getRequireSets cfg) ++ l) [] cfgs; - in - merge "" ( - map rmRequire ( - inlineRequiredSets ((toList opts) ++ lib.uniqFlatten getRequirePaths [] [] (lib.concatMap getRequirePaths (toList opts))) - ) - ); - fixOptionSets = merge: args: opts: - lib.fix (fixOptionSetsFun merge args opts); + # old interface. + fixOptionSetsFun = merge: {...}@args: initModules: config: + fixableMergeModules (merge "") initModules args config; + + fixOptionSets = merge: args: initModules: + fixMergeModules (merge "") initModules args; # Generate documentation template from the list of option declaration like