diff --git a/lib/modules.nix b/lib/modules.nix
index ecf5ef3f491..daffe5224ab 100644
--- a/lib/modules.nix
+++ b/lib/modules.nix
@@ -58,6 +58,23 @@ rec {
default = check;
description = "Whether to check whether all option definitions have matching declarations.";
};
+
+ _module.freeformType = mkOption {
+ # Disallow merging for now, but could be implemented nicely with a `types.optionType`
+ type = types.nullOr (types.uniq types.attrs);
+ internal = true;
+ default = null;
+ description = ''
+ If set, merge all definitions that don't have an associated option
+ together using this type. The result then gets combined with the
+ values of all declared options to produce the final
+ config value.
+
+ If this is null, definitions without an option
+ will throw an error unless is
+ turned off.
+ '';
+ };
};
config = {
@@ -74,10 +91,29 @@ rec {
options = merged.matchedOptions;
- config = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
+ config =
+ let
+
+ # For definitions that have an associated option
+ declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
+
+ # If freeformType is set, this is for definitions that don't have an associated option
+ freeformConfig =
+ let
+ defs = map (def: {
+ file = def.file;
+ value = setAttrByPath def.prefix def.value;
+ }) merged.unmatchedDefns;
+ in declaredConfig._module.freeformType.merge prefix defs;
+
+ in if declaredConfig._module.freeformType == null then declaredConfig
+ # Because all definitions that had an associated option ended in
+ # declaredConfig, freeformConfig can only contain the non-option
+ # paths, meaning recursiveUpdate will never override any value
+ else recursiveUpdate freeformConfig declaredConfig;
checkUnmatched =
- if config._module.check && merged.unmatchedDefns != [] then
+ if config._module.check && config._module.freeformType == null && merged.unmatchedDefns != [] then
let inherit (head merged.unmatchedDefns) file prefix;
in throw "The option `${showOption prefix}' defined in `${file}' does not exist."
else null;