From 9255c48a06913a064da4e103e6b7c1d94fb4fb90 Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Thu, 1 May 2014 17:29:30 -0400 Subject: [PATCH 01/17] Move property processing, type checking, and merge code into a function This makes the relationship between property types clearer, and more importantly will let option types parameterized by other option types reuse the code for delegated type checking and merging. --- lib/modules.nix | 58 ++++++++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/lib/modules.nix b/lib/modules.nix index d0b8f90e5ce6..8bf8016b431f 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -106,12 +106,9 @@ rec { else [] ) configs); nrOptions = count (m: isOption m.options) decls; - # Process mkMerge and mkIf properties. - defns' = concatMap (m: - if m.config ? ${name} - then map (m': { inherit (m) file; value = m'; }) (dischargeProperties m.config.${name}) - else [] - ) configs; + # Extract the definitions for this loc + defns' = map (m: { inherit (m) file; value = m.config.${name}; }) + (filter (m: m.config ? ${name}) configs); in if nrOptions == length decls then let opt = fixupOptionType loc (mergeOptionDecls loc decls); @@ -177,27 +174,17 @@ rec { config value. */ evalOptionValue = loc: opt: defs: let - # Process mkOverride properties, adding in the default - # value specified in the option declaration (if any). - defsFinal' = filterOverrides - ((if opt ? default then [{ file = head opt.declarations; value = mkOptionDefault opt.default; }] else []) ++ defs); - # Sort mkOrder properties. - defsFinal = - # Avoid sorting if we don't have to. - if any (def: def.value._type or "" == "order") defsFinal' - then sortProperties defsFinal' - else defsFinal'; + # Add in the default value for this option, if any. + defs' = (optional (opt ? default) + { file = head opt.declarations; value = mkOptionDefault opt.default; }) ++ defs; + # Handle properties, check types, and merge everything together + inherit (mergeDefinitions loc opt.type defs') defsFinal mergedValue; files = map (def: def.file) defsFinal; - # Type-check the remaining definitions, and merge them if - # possible. merged = if defsFinal == [] then throw "The option `${showOption loc}' is used but not defined." else - fold (def: res: - if opt.type.check def.value then res - else throw "The option value `${showOption loc}' in `${def.file}' is not a ${opt.type.name}.") - (opt.type.merge loc defsFinal) defsFinal; + mergedValue; # Finally, apply the ‘apply’ function to the merged # value. This allows options to yield a value computed # from the definitions. @@ -209,6 +196,33 @@ rec { inherit files; }; + # Merge definitions of a value of a given type + mergeDefinitions = loc: type: defs: rec { + defsFinal = + let + # Process mkMerge and mkIf properties + discharged = concatMap (m: + map (value: { inherit (m) file; inherit value; }) (dischargeProperties m.value) + ) defs; + + # Process mkOverride properties + overridden = filterOverrides discharged; + + # Sort mkOrder properties + sorted = + # Avoid sorting if we don't have to. + if any (def: def.value._type or "" == "order") overridden + then sortProperties overridden + else overridden; + in sorted; + + # Type-check the remaining definitions, and merge them + mergedValue = fold (def: res: + if type.check def.value then res + else throw "The option value `${showOption loc}' in `${def.file}' is not a ${type.name}.") + (type.merge loc defsFinal) defsFinal; + }; + /* Given a config set, expand mkMerge properties, and push down the other properties into the children. The result is a list of config sets that do not have properties at top-level. For From e4bc2592f3c5fa2f05484e7258f99ebb0507d304 Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Thu, 1 May 2014 17:58:16 -0400 Subject: [PATCH 02/17] types.listOf: Use mergeDefinitions to handle each element This simplifies typechecking and allows properties to be used inside the lists --- lib/types.nix | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/types.nix b/lib/types.nix index 1e7abf36535f..58fb05b9f0e5 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -7,7 +7,9 @@ with import ./options.nix; with import ./trivial.nix; with import ./strings.nix; -rec { +let + inherit (import ./modules.nix) mergeDefinitions; +in rec { isType = type: x: (x._type or "") == type; @@ -109,11 +111,14 @@ rec { listOf = elemType: mkOptionType { name = "list of ${elemType.name}s"; - check = value: isList value && all elemType.check value; + check = isList; merge = loc: defs: - concatLists (imap (n: def: imap (m: def': - elemType.merge (loc ++ ["[${toString n}-${toString m}]"]) - [{ inherit (def) file; value = def'; }]) def.value) defs); + map (x: x.value) (filter (x: x ? value) (concatLists (imap (n: def: imap (m: def': + let + inherit (mergeDefinitions (loc ++ ["[definition ${toString n}-entry ${toString m}]"]) + elemType [{ inherit (def) file; value = def'; }] + ) defsFinal mergedValue; + in if defsFinal == [] then {} else { value = mergedValue; }) def.value) defs))); getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["*"]); getSubModules = elemType.getSubModules; substSubModules = m: listOf (elemType.substSubModules m); From 8737d1783f8a8e0c219ec00dd995e8851fd65b3b Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Thu, 1 May 2014 18:13:06 -0400 Subject: [PATCH 03/17] types.attrsOf: Use mergeDefinitions to handle each element This simplifes typechecking and allows properties to be used inside of the attribute sets. This fixes the empty synergy-client and synergy-server services previously generated on systems with synergy disabled. --- lib/types.nix | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/types.nix b/lib/types.nix index 58fb05b9f0e5..a5b9d59e30ff 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -126,12 +126,16 @@ in rec { attrsOf = elemType: mkOptionType { name = "attribute set of ${elemType.name}s"; - check = x: isAttrs x && all elemType.check (attrValues x); + check = isAttrs; merge = loc: defs: - zipAttrsWith (name: elemType.merge (loc ++ [name])) + mapAttrs (n: v: v.value) (filterAttrs (n: v: v ? value) (zipAttrsWith (name: defs: + let + inherit (mergeDefinitions (loc ++ [name]) elemType defs) + defsFinal mergedValue; + in if defsFinal == [] then {} else { value = mergedValue; }) # Push down position info. (map (def: listToAttrs (mapAttrsToList (n: def': - { name = n; value = { inherit (def) file; value = def'; }; }) def.value)) defs); + { name = n; value = { inherit (def) file; value = def'; }; }) def.value)) defs))); getSubOptions = prefix: elemType.getSubOptions (prefix ++ [""]); getSubModules = elemType.getSubModules; substSubModules = m: attrsOf (elemType.substSubModules m); From 4f5c6330c9f3df2533daf33ecaf0c52420979674 Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Thu, 1 May 2014 20:17:03 -0400 Subject: [PATCH 04/17] types.loaOf: Update check function This symplifies typechecking and allows properties to be used inside the function body. It also makes possible checking the type of the result. --- lib/types.nix | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/types.nix b/lib/types.nix index a5b9d59e30ff..4d336c1d9466 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -159,10 +159,7 @@ in rec { attrOnly = attrsOf elemType; in mkOptionType { name = "list or attribute set of ${elemType.name}s"; - check = x: - if isList x then listOnly.check x - else if isAttrs x then attrOnly.check x - else false; + check = x: isList x || isAttrs x; merge = loc: defs: attrOnly.merge loc (imap convertIfList defs); getSubOptions = prefix: elemType.getSubOptions (prefix ++ [""]); getSubModules = elemType.getSubModules; From 1d62ad474694c0717017c2c8aa79909a890407b5 Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Mon, 5 May 2014 15:18:53 -0400 Subject: [PATCH 05/17] modules.nix: Generate the extra argument set from the configuration This allows for module arguments to be handled modularly, in particular allowing the nixpkgs module to handle the nixpkgs import internally. This creates the __internal option namespace, which should only be added to by the module system itself. --- lib/modules.nix | 33 ++++++++++++++++++-- nixos/modules/security/pam.nix | 7 +++-- nixos/modules/services/misc/nixos-manual.nix | 4 +-- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/lib/modules.nix b/lib/modules.nix index 8bf8016b431f..b514544c1e08 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -12,8 +12,26 @@ rec { and ‘config’: the nested set of all option values. */ evalModules = { modules, prefix ? [], args ? {}, check ? true }: let - args' = args // { lib = import ./.; } // result; - closed = closeModules modules args'; + internalModule = { + _file = ./modules.nix; + + key = ./modules.nix; + + options = { + __internal.args = mkOption { + description = "Arguments passed to each module."; + + type = types.attrsOf types.unspecified; + + internal = true; + }; + }; + + config = { + __internal.args = args; + }; + }; + closed = closeModules (modules ++ [ internalModule ]) { inherit config options; lib = import ./.; }; # Note: the list of modules is reversed to maintain backward # compatibility with the old module system. Not sure if this is # the most sensible policy. @@ -74,7 +92,16 @@ rec { config = removeAttrs m ["key" "_file" "require" "imports"]; }; - applyIfFunction = f: arg: if isFunction f then f arg else f; + applyIfFunction = f: arg@{ config, options, lib }: if isFunction f then + let + requiredArgs = builtins.attrNames (builtins.functionArgs f); + extraArgs = builtins.listToAttrs (map (name: { + inherit name; + value = config.__internal.args.${name}; + }) requiredArgs); + in f (extraArgs // arg) + else + f; /* Merge a list of modules. This will recurse over the option declarations in all modules, combining them into a single set. diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index e81278a95d5c..631e8317cb4c 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -6,8 +6,9 @@ with lib; let + parentConfig = config; - pamOpts = args: { + pamOpts = { config, name, ... }: let cfg = config; in let config = parentConfig; in { options = { @@ -180,8 +181,8 @@ let }; - config = let cfg = args.config; in { - name = mkDefault args.name; + config = { + name = mkDefault name; setLoginUid = mkDefault cfg.startSession; limits = mkDefault config.security.pam.loginLimits; diff --git a/nixos/modules/services/misc/nixos-manual.nix b/nixos/modules/services/misc/nixos-manual.nix index c0d7885280a5..72923f2b56a0 100644 --- a/nixos/modules/services/misc/nixos-manual.nix +++ b/nixos/modules/services/misc/nixos-manual.nix @@ -3,7 +3,7 @@ # of the virtual consoles. The latter is useful for the installation # CD. -{ config, lib, pkgs, baseModules, ... } @ extraArgs: +{ config, lib, pkgs, baseModules, ... }: with lib; @@ -18,7 +18,7 @@ let eval = evalModules { modules = [ versionModule ] ++ baseModules; - args = (removeAttrs extraArgs ["config" "options"]) // { modules = [ ]; }; + args = (config.__internal.args) // { modules = [ ]; }; }; manual = import ../../../doc/manual { From e4a06f35b1d88ab98fa8b6962e7a3f802232d165 Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Mon, 5 May 2014 15:52:33 -0400 Subject: [PATCH 06/17] nixos: Don't evaluate twice to get the value of config.nixpkgs --- lib/modules.nix | 4 +-- nixos/lib/eval-config.nix | 35 ++++++++++++--------------- nixos/modules/misc/nixpkgs.nix | 4 --- nixos/modules/system/boot/systemd.nix | 2 +- 4 files changed, 18 insertions(+), 27 deletions(-) diff --git a/lib/modules.nix b/lib/modules.nix index b514544c1e08..84ca209d3671 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -12,10 +12,10 @@ rec { and ‘config’: the nested set of all option values. */ evalModules = { modules, prefix ? [], args ? {}, check ? true }: let - internalModule = { + internalModule = rec { _file = ./modules.nix; - key = ./modules.nix; + key = _file; options = { __internal.args = mkOption { diff --git a/nixos/lib/eval-config.nix b/nixos/lib/eval-config.nix index 08adcf3a0078..460f5601ae76 100644 --- a/nixos/lib/eval-config.nix +++ b/nixos/lib/eval-config.nix @@ -9,18 +9,30 @@ , modules , check ? true , prefix ? [] +, lib ? import ../../lib }: let extraArgs_ = extraArgs; pkgs_ = pkgs; system_ = system; extraModules = let e = builtins.getEnv "NIXOS_EXTRA_MODULE_PATH"; in if e == "" then [] else [(import (builtins.toPath e))]; +in + +let + pkgsModule = rec { + _file = ./eval-config.nix; + key = _file; + config = { + nixpkgs.system = lib.mkDefault system_; + }; + }; + in rec { # Merge the option definitions in all modules, forming the full # system configuration. - inherit (pkgs.lib.evalModules { + inherit (lib.evalModules { inherit prefix; - modules = modules ++ extraModules ++ baseModules; + modules = modules ++ extraModules ++ baseModules ++ [ pkgsModule ]; args = extraArgs; check = check && options.environment.checkConfigurationOptions.value; }) config options; @@ -39,30 +51,13 @@ in rec { utils = import ./utils.nix pkgs; }; - # Import Nixpkgs, allowing the NixOS option nixpkgs.config to - # specify the Nixpkgs configuration (e.g., to set package options - # such as firefox.enableGeckoMediaPlayer, or to apply global - # overrides such as changing GCC throughout the system), and the - # option nixpkgs.system to override the platform type. This is - # tricky, because we have to prevent an infinite recursion: "pkgs" - # is passed as an argument to NixOS modules, but the value of "pkgs" - # depends on config.nixpkgs.config, which we get from the modules. - # So we call ourselves here with "pkgs" explicitly set to an - # instance that doesn't depend on nixpkgs.config. pkgs = if pkgs_ != null then pkgs_ else import ./nixpkgs.nix ( let system = if nixpkgsOptions.system != "" then nixpkgsOptions.system else system_; - nixpkgsOptions = (import ./eval-config.nix { - inherit system extraArgs modules prefix; - # For efficiency, leave out most NixOS modules; they don't - # define nixpkgs.config, so it's pointless to evaluate them. - baseModules = [ ../modules/misc/nixpkgs.nix ../modules/config/no-x-libs.nix ]; - pkgs = import ./nixpkgs.nix { system = system_; config = {}; }; - check = false; - }).config.nixpkgs; + nixpkgsOptions = config.nixpkgs; in { inherit system; diff --git a/nixos/modules/misc/nixpkgs.nix b/nixos/modules/misc/nixpkgs.nix index f41c8817ba4a..cbb42c000039 100644 --- a/nixos/modules/misc/nixpkgs.nix +++ b/nixos/modules/misc/nixpkgs.nix @@ -69,8 +69,4 @@ in }; }; - - config = { - nixpkgs.system = mkDefault pkgs.stdenv.system; - }; } diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix index f853a8f6775c..f3ffda023675 100644 --- a/nixos/modules/system/boot/systemd.nix +++ b/nixos/modules/system/boot/systemd.nix @@ -1,7 +1,7 @@ { config, lib, pkgs, utils, ... }: -with lib; with utils; +with lib; with import ./systemd-unit-options.nix { inherit config lib; }; let From e3eff53037f1b7abb7a44ba72f59f20649023642 Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Mon, 5 May 2014 16:23:57 -0400 Subject: [PATCH 07/17] evalModules: Add internal option for the check argument --- lib/modules.nix | 12 +++++++++++- nixos/lib/eval-config.nix | 3 +-- nixos/modules/misc/check-config.nix | 15 --------------- nixos/modules/module-list.nix | 1 - nixos/modules/rename.nix | 2 ++ 5 files changed, 14 insertions(+), 19 deletions(-) delete mode 100644 nixos/modules/misc/check-config.nix diff --git a/lib/modules.nix b/lib/modules.nix index 84ca209d3671..1d7c7b22765e 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -25,6 +25,16 @@ rec { internal = true; }; + + __internal.check = mkOption { + description = "Whether to check whether all option definitions have matching declarations."; + + type = types.uniq types.bool; + + internal = true; + + default = check; + }; }; config = { @@ -45,7 +55,7 @@ rec { if isOption v then v.value else yieldConfig (prefix ++ [n]) v) set) ["_definedNames"]; in - if check && set ? _definedNames then + if options.__internal.check.value && set ? _definedNames then fold (m: res: fold (name: res: if set ? ${name} then res else throw "The option `${showOption (prefix ++ [name])}' defined in `${m.file}' does not exist.") diff --git a/nixos/lib/eval-config.nix b/nixos/lib/eval-config.nix index 460f5601ae76..4ee1c61f54ff 100644 --- a/nixos/lib/eval-config.nix +++ b/nixos/lib/eval-config.nix @@ -31,10 +31,9 @@ in rec { # Merge the option definitions in all modules, forming the full # system configuration. inherit (lib.evalModules { - inherit prefix; + inherit prefix check; modules = modules ++ extraModules ++ baseModules ++ [ pkgsModule ]; args = extraArgs; - check = check && options.environment.checkConfigurationOptions.value; }) config options; # These are the extra arguments passed to every module. In diff --git a/nixos/modules/misc/check-config.nix b/nixos/modules/misc/check-config.nix deleted file mode 100644 index e9803de21961..000000000000 --- a/nixos/modules/misc/check-config.nix +++ /dev/null @@ -1,15 +0,0 @@ -{ lib, ... }: - -with lib; - -{ - options = { - environment.checkConfigurationOptions = mkOption { - type = types.bool; - default = true; - description = '' - Whether to check the validity of the entire configuration. - ''; - }; - }; -} diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 23809796878f..95337168d104 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -43,7 +43,6 @@ ./installer/tools/nixos-checkout.nix ./installer/tools/tools.nix ./misc/assertions.nix - ./misc/check-config.nix ./misc/crashdump.nix ./misc/ids.nix ./misc/lib.nix diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix index e820b2cb9ce4..b898dc23522d 100644 --- a/nixos/modules/rename.nix +++ b/nixos/modules/rename.nix @@ -136,6 +136,8 @@ in zipModules ([] ++ obsolete [ "services" "mysql55" ] [ "services" "mysql" ] +++ obsolete [ "environment" "checkConfigurationOptions" ] [ "__internal" "check" ] + # Options that are obsolete and have no replacement. ++ obsolete' [ "boot" "loader" "grub" "bootDevice" ] ++ obsolete' [ "boot" "initrd" "luks" "enable" ] From 0a0a29fd0bb8329b33a0b2bb25627d3b3d9b7368 Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Mon, 5 May 2014 16:30:51 -0400 Subject: [PATCH 08/17] Add comments about the module system interface Ideally the module system could be configured pretty much completely by the contents of the modules themselves, so add comments about avoiding complicating it further and possibly removing now-redundant configurability from the existing interface. --- lib/modules.nix | 16 ++++++++++++++-- lib/types.nix | 7 ++++++- nixos/lib/eval-config.nix | 22 +++++++++++++++++----- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/lib/modules.nix b/lib/modules.nix index 1d7c7b22765e..26193b269410 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -9,8 +9,19 @@ rec { /* Evaluate a set of modules. The result is a set of two attributes: ‘options’: the nested set of all option declarations, - and ‘config’: the nested set of all option values. */ - evalModules = { modules, prefix ? [], args ? {}, check ? true }: + and ‘config’: the nested set of all option values. + !!! Please think twice before adding to this argument list! The more + that is specified here instead of in the modules themselves the harder + it is to transparently move a set of modules to be a submodule of another + config (as the proper arguments need to be replicated at each call to + evalModules) and the less declarative the module set is. */ + evalModules = { modules + , prefix ? [] + , # !!! This can be specified modularly now, can we remove it? + args ? {} + , # !!! This can be specified modularly now, can we remove it? + check ? true + }: let internalModule = rec { _file = ./modules.nix; @@ -21,6 +32,7 @@ rec { __internal.args = mkOption { description = "Arguments passed to each module."; + # !!! Should this be types.uniq types.unspecified? type = types.attrsOf types.unspecified; internal = true; diff --git a/lib/types.nix b/lib/types.nix index 4d336c1d9466..32f332ed21bb 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -200,7 +200,12 @@ in rec { let coerce = def: if isFunction def then def else { config = def; }; modules = opts' ++ map (def: { _file = def.file; imports = [(coerce def.value)]; }) defs; - in (evalModules { inherit modules; args.name = last loc; prefix = loc; }).config; + in (evalModules { + inherit modules; + # !!! See comment about args in lib/modules.nix + args.name = last loc; + prefix = loc; + }).config; getSubOptions = prefix: (evalModules { modules = opts'; inherit prefix; # FIXME: hack to get shit to evaluate. diff --git a/nixos/lib/eval-config.nix b/nixos/lib/eval-config.nix index 4ee1c61f54ff..b4b251d2581a 100644 --- a/nixos/lib/eval-config.nix +++ b/nixos/lib/eval-config.nix @@ -2,12 +2,24 @@ # configuration object (`config') from which we can retrieve option # values. -{ system ? builtins.currentSystem -, pkgs ? null -, baseModules ? import ../modules/module-list.nix -, extraArgs ? {} +# !!! Please think twice before adding to this argument list! +# Ideally eval-config.nix would be an extremely thin wrapper +# around lib.evalModules, so that modular systems that have nixos configs +# as subcomponents (e.g. the container feature, or nixops if network +# expressions are ever made modular at the top level) can just use +# types.submodule instead of using eval-config.nix +{ # !!! system can be set modularly, would be nice to remove + system ? builtins.currentSystem +, # !!! is this argument needed any more? The pkgs argument can + # be set modularly anyway. + pkgs ? null +, # !!! what do we gain by making this configurable? + baseModules ? import ../modules/module-list.nix +, # !!! See comment about args in lib/modules.nix + extraArgs ? {} , modules -, check ? true +, # !!! See comment about check in lib/modules.nix + check ? true , prefix ? [] , lib ? import ../../lib }: From f69ce50529c5108d4dababfd221652ace55264bd Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Tue, 6 May 2014 10:31:48 -0400 Subject: [PATCH 09/17] Move most extra args out of eval-config.nix --- nixos/lib/eval-config.nix | 20 +++----------------- nixos/modules/misc/extra-arguments.nix | 14 ++++++++++++++ nixos/modules/misc/nixpkgs.nix | 9 +++++++++ nixos/modules/module-list.nix | 1 + 4 files changed, 27 insertions(+), 17 deletions(-) create mode 100644 nixos/modules/misc/extra-arguments.nix diff --git a/nixos/lib/eval-config.nix b/nixos/lib/eval-config.nix index b4b251d2581a..a157ffd56951 100644 --- a/nixos/lib/eval-config.nix +++ b/nixos/lib/eval-config.nix @@ -35,6 +35,7 @@ let key = _file; config = { nixpkgs.system = lib.mkDefault system_; + __internal.args.pkgs = lib.mkIf (pkgs_ != null) (lib.mkForce pkgs_); }; }; @@ -56,23 +57,8 @@ in rec { # the 64-bit package anyway. However, it would be cleaner to respect # nixpkgs.config here. extraArgs = extraArgs_ // { - inherit pkgs modules baseModules; - modulesPath = ../modules; - pkgs_i686 = import ./nixpkgs.nix { system = "i686-linux"; config.allowUnfree = true; }; - utils = import ./utils.nix pkgs; + inherit modules baseModules; }; - pkgs = - if pkgs_ != null - then pkgs_ - else import ./nixpkgs.nix ( - let - system = if nixpkgsOptions.system != "" then nixpkgsOptions.system else system_; - nixpkgsOptions = config.nixpkgs; - in - { - inherit system; - inherit (nixpkgsOptions) config; - }); - + inherit (config.__internal.args) pkgs; } diff --git a/nixos/modules/misc/extra-arguments.nix b/nixos/modules/misc/extra-arguments.nix new file mode 100644 index 000000000000..9943a6192a69 --- /dev/null +++ b/nixos/modules/misc/extra-arguments.nix @@ -0,0 +1,14 @@ +{ lib, pkgs, config, ... }: + +{ + __internal.args = { + modulesPath = ../.; + + pkgs_i686 = import ../../lib/nixpkgs.nix { + system = "i686-linux"; + config.allowUnfree = true; + }; + + utils = import ../../lib/utils.nix pkgs; + }; +} diff --git a/nixos/modules/misc/nixpkgs.nix b/nixos/modules/misc/nixpkgs.nix index cbb42c000039..2b648c1fa4ed 100644 --- a/nixos/modules/misc/nixpkgs.nix +++ b/nixos/modules/misc/nixpkgs.nix @@ -60,6 +60,7 @@ in nixpkgs.system = mkOption { type = types.str; + default = builtins.currentSystem; description = '' Specifies the Nix platform type for which NixOS should be built. If unset, it defaults to the platform type of your host system. @@ -69,4 +70,12 @@ in }; }; + + config = { + __internal.args.pkgs = import ../../lib/nixpkgs.nix { + system = config.nixpkgs.system; + + inherit (config.nixpkgs) config; + }; + }; } diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 95337168d104..7a324a2bb306 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -44,6 +44,7 @@ ./installer/tools/tools.nix ./misc/assertions.nix ./misc/crashdump.nix + ./misc/extra-arguments.nix ./misc/ids.nix ./misc/lib.nix ./misc/locate.nix From 772b8869d28e9901370277ff3ba82c1d912482ef Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Wed, 7 May 2014 16:43:18 -0400 Subject: [PATCH 10/17] Add comment about limitation on __internal.check --- lib/modules.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/modules.nix b/lib/modules.nix index 26193b269410..9ed2917df50a 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -61,6 +61,10 @@ rec { # Traverse options and extract the option values into the final # config set. At the same time, check whether all option # definitions have matching declarations. + # !!! __internal.check's value can't depend on any other config values + # without an infinite recursion. One way around this is to make the + # 'config' passed around to the modules be unconditionally unchecked, + # and only do the check in 'result'. config = yieldConfig prefix options; yieldConfig = prefix: set: let res = removeAttrs (mapAttrs (n: v: From 3177d3765236bccd8310b86029b24673b967abf7 Mon Sep 17 00:00:00 2001 From: Shea Levy Date: Thu, 15 May 2014 01:15:39 -0400 Subject: [PATCH 11/17] Fix import using module args --- nixos/modules/virtualisation/amazon-config.nix | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/nixos/modules/virtualisation/amazon-config.nix b/nixos/modules/virtualisation/amazon-config.nix index e816ed2d183a..a27e52a8e68c 100644 --- a/nixos/modules/virtualisation/amazon-config.nix +++ b/nixos/modules/virtualisation/amazon-config.nix @@ -1,5 +1,3 @@ -{ config, pkgs, modulesPath, ... }: - { - imports = [ "${modulesPath}/virtualisation/amazon-image.nix" ]; + imports = [ ./amazon-image.nix ]; } From 9f2865515de2148dd35b720d4592783617e4e177 Mon Sep 17 00:00:00 2001 From: "Nicolas B. Pierron" Date: Wed, 11 Mar 2015 23:26:21 +0100 Subject: [PATCH 12/17] Fix infinite loop in fontconfig-ultimate.nix With the new evaluation of arguments, pkgs is now defined by the configuration, which implies that option declaration with pkgs.lib will cause an infinite loop. --- nixos/modules/config/fonts/fontconfig-ultimate.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nixos/modules/config/fonts/fontconfig-ultimate.nix b/nixos/modules/config/fonts/fontconfig-ultimate.nix index 853f253ff9bc..02568f9de51e 100644 --- a/nixos/modules/config/fonts/fontconfig-ultimate.nix +++ b/nixos/modules/config/fonts/fontconfig-ultimate.nix @@ -1,6 +1,6 @@ -{ config, pkgs, ... }: +{ config, pkgs, lib, ... }: -with pkgs.lib; +with lib; let fcBool = x: if x then "true" else "false"; in From 83dc60456e44082b4f13c2be19c5e9fbcfd57f74 Mon Sep 17 00:00:00 2001 From: "Nicolas B. Pierron" Date: Wed, 11 Mar 2015 23:30:30 +0100 Subject: [PATCH 13/17] Expose submodule arguments to builtins.functionArgs before applying the arguments. The current implementation of the ApplyIfFunction is looking at the arguments of a module to decide which arguments should be given to each module. This patch make sure that we do not wrap a submodule function in order to keep functionArgs working as expected. --- lib/modules.nix | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/lib/modules.nix b/lib/modules.nix index 9ed2917df50a..ca88b28a7791 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -87,7 +87,7 @@ rec { let toClosureList = file: parentKey: imap (n: x: if isAttrs x || isFunction x then - unifyModuleSyntax file "${parentKey}:anon-${toString n}" (applyIfFunction x args) + unifyModuleSyntax file "${parentKey}:anon-${toString n}" (unpackSubmodule applyIfFunction x args) else unifyModuleSyntax (toString x) (toString x) (applyIfFunction (import x) args)); in @@ -120,6 +120,18 @@ rec { applyIfFunction = f: arg@{ config, options, lib }: if isFunction f then let + # Module arguments are resolved in a strict manner when attribute set + # deconstruction is used. As the arguments are now defined with the + # config.__interanl.args option, the strictness used on the attribute + # set argument would cause an infinite loop, if the result of the + # option is given as argument. + # + # To work-around the strictness issue on the deconstruction of the + # attributes set argument, we create a new attribute set which is + # constructed to satisfy the expected set of attributes. Thus calling + # a module will resolve strictly the attributes used as argument but + # not their values. The values are forwarding the result of the + # evaluation of the option. requiredArgs = builtins.attrNames (builtins.functionArgs f); extraArgs = builtins.listToAttrs (map (name: { inherit name; @@ -129,6 +141,17 @@ rec { else f; + /* We have to pack and unpack submodules. We cannot wrap the expected + result of the function as we would no longer be able to list the arguments + of the submodule. (see applyIfFunction) */ + unpackSubmodule = unpack: m: args: + if isType "submodule" m then + { _file = m.file; } // (unpack m.submodule args) + else unpack m args; + + packSubmodule = file: m: + { _type = "submodule"; file = file; submodule = m; }; + /* Merge a list of modules. This will recurse over the option declarations in all modules, combining them into a single set. At the same time, for each option declaration, it will merge the @@ -206,15 +229,12 @@ rec { current option declaration as the file use for the submodule. If the submodule defines any filename, then we ignore the enclosing option file. */ options' = toList opt.options.options; - addModuleFile = m: - if isFunction m then args: { _file = opt.file; } // (m args) - else { _file = opt.file; } // m; coerceOption = file: opt: - if isFunction opt then args: { _file = file; } // (opt args) - else { _file = file; options = opt; }; + if isFunction opt then packSubmodule file opt + else packSubmodule file { options = opt; }; getSubModules = opt.options.type.getSubModules or null; submodules = - if getSubModules != null then map addModuleFile getSubModules ++ res.options + if getSubModules != null then map (packSubmodule opt.file) getSubModules ++ res.options else if opt.options ? options then map (coerceOption opt.file) options' ++ res.options else res.options; in opt.options // res // From dd4f5f6b789c7fb2cac45cb8024e87bdb13c9740 Mon Sep 17 00:00:00 2001 From: "Nicolas B. Pierron" Date: Thu, 12 Mar 2015 22:38:51 +0100 Subject: [PATCH 14/17] Rename mergeDefinitions internal steps to functions which are independent of each others. --- lib/modules.nix | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/modules.nix b/lib/modules.nix index ca88b28a7791..ad573da990f4 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -274,20 +274,21 @@ rec { defsFinal = let # Process mkMerge and mkIf properties - discharged = concatMap (m: + processIfAndMerge = defs: concatMap (m: map (value: { inherit (m) file; inherit value; }) (dischargeProperties m.value) ) defs; # Process mkOverride properties - overridden = filterOverrides discharged; + processOverride = defs: filterOverrides defs; # Sort mkOrder properties - sorted = + processOrder = defs: # Avoid sorting if we don't have to. - if any (def: def.value._type or "" == "order") overridden - then sortProperties overridden - else overridden; - in sorted; + if any (def: def.value._type or "" == "order") defs + then sortProperties defs + else defs; + in + processOrder (processOverride (processIfAndMerge defs)); # Type-check the remaining definitions, and merge them mergedValue = fold (def: res: From ed91474e9b94f06a44b7aa5bda9911292f709f54 Mon Sep 17 00:00:00 2001 From: "Nicolas B. Pierron" Date: Thu, 12 Mar 2015 23:01:47 +0100 Subject: [PATCH 15/17] Share common code for merging option definitions. This move idioms which were used in `evalOptionValue` and in the `merge` functions of `listOf` and `attrsOf` types, such that we can use a names such as `isDefined` and `optionalValue` instead or repeating identical comparisons of `defsFinal == []`. --- lib/modules.nix | 18 +++++++++++------- lib/types.nix | 22 ++++++++++------------ 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/lib/modules.nix b/lib/modules.nix index ad573da990f4..a3c3fe7fc1ae 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -250,14 +250,14 @@ rec { # Add in the default value for this option, if any. defs' = (optional (opt ? default) { file = head opt.declarations; value = mkOptionDefault opt.default; }) ++ defs; + # Handle properties, check types, and merge everything together - inherit (mergeDefinitions loc opt.type defs') defsFinal mergedValue; + inherit (mergeDefinitions loc opt.type defs') isDefined defsFinal mergedValue; files = map (def: def.file) defsFinal; merged = - if defsFinal == [] then - throw "The option `${showOption loc}' is used but not defined." - else - mergedValue; + if isDefined then mergedValue + else throw "The option `${showOption loc}' is used but not defined."; + # Finally, apply the ‘apply’ function to the merged # value. This allows options to yield a value computed # from the definitions. @@ -265,8 +265,7 @@ rec { in opt // { value = addErrorContext "while evaluating the option `${showOption loc}':" value; definitions = map (def: def.value) defsFinal; - isDefined = defsFinal != []; - inherit files; + inherit isDefined files; }; # Merge definitions of a value of a given type @@ -295,6 +294,11 @@ rec { if type.check def.value then res else throw "The option value `${showOption loc}' in `${def.file}' is not a ${type.name}.") (type.merge loc defsFinal) defsFinal; + + isDefined = defsFinal != []; + optionalValue = + if isDefined then { value = mergedValue; } + else {}; }; /* Given a config set, expand mkMerge properties, and push down the diff --git a/lib/types.nix b/lib/types.nix index 32f332ed21bb..f836b07e335f 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -6,10 +6,9 @@ with import ./attrsets.nix; with import ./options.nix; with import ./trivial.nix; with import ./strings.nix; +with {inherit (import ./modules.nix) mergeDefinitions; }; -let - inherit (import ./modules.nix) mergeDefinitions; -in rec { +rec { isType = type: x: (x._type or "") == type; @@ -114,11 +113,12 @@ in rec { check = isList; merge = loc: defs: map (x: x.value) (filter (x: x ? value) (concatLists (imap (n: def: imap (m: def': - let - inherit (mergeDefinitions (loc ++ ["[definition ${toString n}-entry ${toString m}]"]) - elemType [{ inherit (def) file; value = def'; }] - ) defsFinal mergedValue; - in if defsFinal == [] then {} else { value = mergedValue; }) def.value) defs))); + (mergeDefinitions + (loc ++ ["[definition ${toString n}-entry ${toString m}]"]) + elemType + [{ inherit (def) file; value = def'; }] + ).optionalValue + ) def.value) defs))); getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["*"]); getSubModules = elemType.getSubModules; substSubModules = m: listOf (elemType.substSubModules m); @@ -129,10 +129,8 @@ in rec { check = isAttrs; merge = loc: defs: mapAttrs (n: v: v.value) (filterAttrs (n: v: v ? value) (zipAttrsWith (name: defs: - let - inherit (mergeDefinitions (loc ++ [name]) elemType defs) - defsFinal mergedValue; - in if defsFinal == [] then {} else { value = mergedValue; }) + (mergeDefinitions (loc ++ [name]) elemType defs).optionalValue + ) # Push down position info. (map (def: listToAttrs (mapAttrsToList (n: def': { name = n; value = { inherit (def) file; value = def'; }; }) def.value)) defs))); From 05e8a48fb4af4c20134362c77ba6c5b4c6e49268 Mon Sep 17 00:00:00 2001 From: "Nicolas B. Pierron" Date: Thu, 12 Mar 2015 23:19:23 +0100 Subject: [PATCH 16/17] Document and rename internal option of modules. --- lib/modules.nix | 34 ++++++++++---------- lib/types.nix | 1 - nixos/lib/eval-config.nix | 4 +-- nixos/modules/misc/extra-arguments.nix | 2 +- nixos/modules/misc/nixpkgs.nix | 2 +- nixos/modules/rename.nix | 2 +- nixos/modules/services/misc/nixos-manual.nix | 2 +- 7 files changed, 23 insertions(+), 24 deletions(-) diff --git a/lib/modules.nix b/lib/modules.nix index a3c3fe7fc1ae..dcede0c46c63 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -17,51 +17,51 @@ rec { evalModules) and the less declarative the module set is. */ evalModules = { modules , prefix ? [] - , # !!! This can be specified modularly now, can we remove it? + , # This would be remove in the future, Prefer _module.args option instead. args ? {} - , # !!! This can be specified modularly now, can we remove it? + , # This would be remove in the future, Prefer _module.check option instead. check ? true }: let + # This internal module declare internal options under the `_module' + # attribute. These options are fragile, as they are used by the + # module system to change the interpretation of modules. internalModule = rec { _file = ./modules.nix; key = _file; options = { - __internal.args = mkOption { - description = "Arguments passed to each module."; - - # !!! Should this be types.uniq types.unspecified? + _module.args = mkOption { type = types.attrsOf types.unspecified; - internal = true; + description = "Arguments passed to each module."; }; - __internal.check = mkOption { - description = "Whether to check whether all option definitions have matching declarations."; - + _module.check = mkOption { type = types.uniq types.bool; - internal = true; - default = check; + description = "Whether to check whether all option definitions have matching declarations."; }; }; config = { - __internal.args = args; + _module.args = args; }; }; + closed = closeModules (modules ++ [ internalModule ]) { inherit config options; lib = import ./.; }; + # Note: the list of modules is reversed to maintain backward # compatibility with the old module system. Not sure if this is # the most sensible policy. options = mergeModules prefix (reverseList closed); + # Traverse options and extract the option values into the final # config set. At the same time, check whether all option # definitions have matching declarations. - # !!! __internal.check's value can't depend on any other config values + # !!! _module.check's value can't depend on any other config values # without an infinite recursion. One way around this is to make the # 'config' passed around to the modules be unconditionally unchecked, # and only do the check in 'result'. @@ -71,7 +71,7 @@ rec { if isOption v then v.value else yieldConfig (prefix ++ [n]) v) set) ["_definedNames"]; in - if options.__internal.check.value && set ? _definedNames then + if options._module.check.value && set ? _definedNames then fold (m: res: fold (name: res: if set ? ${name} then res else throw "The option `${showOption (prefix ++ [name])}' defined in `${m.file}' does not exist.") @@ -122,7 +122,7 @@ rec { let # Module arguments are resolved in a strict manner when attribute set # deconstruction is used. As the arguments are now defined with the - # config.__interanl.args option, the strictness used on the attribute + # config._module.args option, the strictness used on the attribute # set argument would cause an infinite loop, if the result of the # option is given as argument. # @@ -135,7 +135,7 @@ rec { requiredArgs = builtins.attrNames (builtins.functionArgs f); extraArgs = builtins.listToAttrs (map (name: { inherit name; - value = config.__internal.args.${name}; + value = config._module.args.${name}; }) requiredArgs); in f (extraArgs // arg) else diff --git a/lib/types.nix b/lib/types.nix index f836b07e335f..f22c76616345 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -200,7 +200,6 @@ rec { modules = opts' ++ map (def: { _file = def.file; imports = [(coerce def.value)]; }) defs; in (evalModules { inherit modules; - # !!! See comment about args in lib/modules.nix args.name = last loc; prefix = loc; }).config; diff --git a/nixos/lib/eval-config.nix b/nixos/lib/eval-config.nix index a157ffd56951..adacbd0863e3 100644 --- a/nixos/lib/eval-config.nix +++ b/nixos/lib/eval-config.nix @@ -35,7 +35,7 @@ let key = _file; config = { nixpkgs.system = lib.mkDefault system_; - __internal.args.pkgs = lib.mkIf (pkgs_ != null) (lib.mkForce pkgs_); + _module.args.pkgs = lib.mkIf (pkgs_ != null) (lib.mkForce pkgs_); }; }; @@ -60,5 +60,5 @@ in rec { inherit modules baseModules; }; - inherit (config.__internal.args) pkgs; + inherit (config._module.args) pkgs; } diff --git a/nixos/modules/misc/extra-arguments.nix b/nixos/modules/misc/extra-arguments.nix index 9943a6192a69..c2c8903546d5 100644 --- a/nixos/modules/misc/extra-arguments.nix +++ b/nixos/modules/misc/extra-arguments.nix @@ -1,7 +1,7 @@ { lib, pkgs, config, ... }: { - __internal.args = { + _module.args = { modulesPath = ../.; pkgs_i686 = import ../../lib/nixpkgs.nix { diff --git a/nixos/modules/misc/nixpkgs.nix b/nixos/modules/misc/nixpkgs.nix index 2b648c1fa4ed..395ba82f2d16 100644 --- a/nixos/modules/misc/nixpkgs.nix +++ b/nixos/modules/misc/nixpkgs.nix @@ -72,7 +72,7 @@ in }; config = { - __internal.args.pkgs = import ../../lib/nixpkgs.nix { + _module.args.pkgs = import ../../lib/nixpkgs.nix { system = config.nixpkgs.system; inherit (config.nixpkgs) config; diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix index b898dc23522d..087b903b8f49 100644 --- a/nixos/modules/rename.nix +++ b/nixos/modules/rename.nix @@ -136,7 +136,7 @@ in zipModules ([] ++ obsolete [ "services" "mysql55" ] [ "services" "mysql" ] -++ obsolete [ "environment" "checkConfigurationOptions" ] [ "__internal" "check" ] +++ obsolete [ "environment" "checkConfigurationOptions" ] [ "_module" "check" ] # Options that are obsolete and have no replacement. ++ obsolete' [ "boot" "loader" "grub" "bootDevice" ] diff --git a/nixos/modules/services/misc/nixos-manual.nix b/nixos/modules/services/misc/nixos-manual.nix index 72923f2b56a0..f73c4102cfe5 100644 --- a/nixos/modules/services/misc/nixos-manual.nix +++ b/nixos/modules/services/misc/nixos-manual.nix @@ -18,7 +18,7 @@ let eval = evalModules { modules = [ versionModule ] ++ baseModules; - args = (config.__internal.args) // { modules = [ ]; }; + args = (config._module.args) // { modules = [ ]; }; }; manual = import ../../../doc/manual { From 7f1a782d91c537eb6972b8acd83e1957a65a93e4 Mon Sep 17 00:00:00 2001 From: "Nicolas B. Pierron" Date: Mon, 16 Mar 2015 22:38:41 +0100 Subject: [PATCH 17/17] Add tests for the new module system improvements. --- lib/tests/modules.sh | 25 +++++++++++++++++++ .../modules/custom-arg-define-enable.nix | 8 ++++++ .../modules/define-if-loaOfSub-foo-enable.nix | 5 ++++ .../modules/define-loaOfSub-foo-enable-if.nix | 5 ++++ .../modules/define-loaOfSub-foo-if-enable.nix | 7 ++++++ .../modules/define-loaOfSub-if-foo-enable.nix | 7 ++++++ lib/tests/modules/define-module-check.nix | 3 +++ 7 files changed, 60 insertions(+) create mode 100644 lib/tests/modules/custom-arg-define-enable.nix create mode 100644 lib/tests/modules/define-if-loaOfSub-foo-enable.nix create mode 100644 lib/tests/modules/define-loaOfSub-foo-enable-if.nix create mode 100644 lib/tests/modules/define-loaOfSub-foo-if-enable.nix create mode 100644 lib/tests/modules/define-loaOfSub-if-foo-enable.nix create mode 100644 lib/tests/modules/define-module-check.nix diff --git a/lib/tests/modules.sh b/lib/tests/modules.sh index 58231a356369..66c6f560fbe8 100755 --- a/lib/tests/modules.sh +++ b/lib/tests/modules.sh @@ -57,13 +57,17 @@ checkConfigError() { fi } +# Check boolean option. checkConfigOutput "false" config.enable ./declare-enable.nix checkConfigError 'The option .* defined in .* does not exist.' config.enable ./define-enable.nix + +# Check mkForce without submodules. set -- config.enable ./declare-enable.nix ./define-enable.nix checkConfigOutput "true" "$@" checkConfigOutput "false" "$@" ./define-force-enable.nix checkConfigOutput "false" "$@" ./define-enable-force.nix +# Check mkForce with option and submodules. checkConfigError 'attribute .*foo.* .* not found' config.loaOfSub.foo.enable ./declare-loaOfSub-any-enable.nix checkConfigOutput 'false' config.loaOfSub.foo.enable ./declare-loaOfSub-any-enable.nix ./define-loaOfSub-foo.nix set -- config.loaOfSub.foo.enable ./declare-loaOfSub-any-enable.nix ./define-loaOfSub-foo-enable.nix @@ -73,6 +77,7 @@ checkConfigOutput 'false' "$@" ./define-loaOfSub-force-foo-enable.nix checkConfigOutput 'false' "$@" ./define-loaOfSub-foo-force-enable.nix checkConfigOutput 'false' "$@" ./define-loaOfSub-foo-enable-force.nix +# Check overriding effect of mkForce on submodule definitions. checkConfigError 'attribute .*bar.* .* not found' config.loaOfSub.bar.enable ./declare-loaOfSub-any-enable.nix ./define-loaOfSub-foo.nix checkConfigOutput 'false' config.loaOfSub.bar.enable ./declare-loaOfSub-any-enable.nix ./define-loaOfSub-foo.nix ./define-loaOfSub-bar.nix set -- config.loaOfSub.bar.enable ./declare-loaOfSub-any-enable.nix ./define-loaOfSub-foo.nix ./define-loaOfSub-bar-enable.nix @@ -82,6 +87,26 @@ checkConfigError 'attribute .*bar.* .* not found' "$@" ./define-loaOfSub-force-f checkConfigOutput 'true' "$@" ./define-loaOfSub-foo-force-enable.nix checkConfigOutput 'true' "$@" ./define-loaOfSub-foo-enable-force.nix +# Check mkIf with submodules. +checkConfigError 'attribute .*foo.* .* not found' config.loaOfSub.foo.enable ./declare-enable.nix ./declare-loaOfSub-any-enable.nix +set -- config.loaOfSub.foo.enable ./declare-enable.nix ./declare-loaOfSub-any-enable.nix +checkConfigError 'attribute .*foo.* .* not found' "$@" ./define-if-loaOfSub-foo-enable.nix +checkConfigError 'attribute .*foo.* .* not found' "$@" ./define-loaOfSub-if-foo-enable.nix +checkConfigError 'attribute .*foo.* .* not found' "$@" ./define-loaOfSub-foo-if-enable.nix +checkConfigOutput 'false' "$@" ./define-loaOfSub-foo-enable-if.nix +checkConfigOutput 'true' "$@" ./define-enable.nix ./define-if-loaOfSub-foo-enable.nix +checkConfigOutput 'true' "$@" ./define-enable.nix ./define-loaOfSub-if-foo-enable.nix +checkConfigOutput 'true' "$@" ./define-enable.nix ./define-loaOfSub-foo-if-enable.nix +checkConfigOutput 'true' "$@" ./define-enable.nix ./define-loaOfSub-foo-enable-if.nix + +# Check _module.args. +checkConfigOutput "true" config.enable ./declare-enable.nix ./custom-arg-define-enable.nix + +# Check _module.check. +set -- config.enable ./declare-enable.nix ./define-enable.nix ./define-loaOfSub-foo.nix +checkConfigError 'The option .* defined in .* does not exist.' "$@" +checkConfigOutput "true" "$@" ./define-module-check.nix + cat <