···539539540540 mergeModules' = prefix: options: configs:
541541 let
542542- /* byName is like foldAttrs, but will look for attributes to merge in the
543543- specified attribute name.
544544-545545- byName "foo" (module: value: ["module.hidden=${module.hidden},value=${value}"])
546546- [
547547- {
548548- hidden="baz";
549549- foo={qux="bar"; gla="flop";};
550550- }
551551- {
552552- hidden="fli";
553553- foo={qux="gne"; gli="flip";};
554554- }
555555- ]
556556- ===>
557557- {
558558- gla = [ "module.hidden=baz,value=flop" ];
559559- gli = [ "module.hidden=fli,value=flip" ];
560560- qux = [ "module.hidden=baz,value=bar" "module.hidden=fli,value=gne" ];
561561- }
562562- */
563563- byName = attr: f: modules:
564564- zipAttrsWith (n: concatLists)
565565- (map (module: let subtree = module.${attr}; in
542542+ # an attrset 'name' => list of submodules that declare ‘name’.
543543+ declsByName =
544544+ zipAttrsWith
545545+ (n: concatLists)
546546+ (map
547547+ (module: let subtree = module.options; in
566548 if !(builtins.isAttrs subtree) then
567567- throw (if attr == "config" then ''
568568- You're trying to define a value of type `${builtins.typeOf subtree}'
569569- rather than an attribute set for the option
570570- `${builtins.concatStringsSep "." prefix}'!
571571-572572- This usually happens if `${builtins.concatStringsSep "." prefix}' has option
573573- definitions inside that are not matched. Please check how to properly define
574574- this option by e.g. referring to `man 5 configuration.nix'!
575575- '' else ''
549549+ throw ''
576550 An option declaration for `${builtins.concatStringsSep "." prefix}' has type
577551 `${builtins.typeOf subtree}' rather than an attribute set.
578552 Did you mean to define this outside of `options'?
579579- '')
553553+ ''
580554 else
581581- mapAttrs (n: f module) subtree
582582- ) modules);
583583- # an attrset 'name' => list of submodules that declare ‘name’.
584584- declsByName = byName "options" (module: option:
585585- [{ inherit (module) _file; options = option; }]
586586- ) options;
555555+ mapAttrs
556556+ (n: option:
557557+ [{ inherit (module) _file; options = option; }]
558558+ )
559559+ subtree
560560+ )
561561+ options);
562562+563563+ # The root of any module definition must be an attrset.
564564+ checkedConfigs =
565565+ assert
566566+ lib.all
567567+ (c:
568568+ # TODO: I have my doubts that this error would occur when option definitions are not matched.
569569+ # The implementation of this check used to be tied to a superficially similar check for
570570+ # options, so maybe that's why this is here.
571571+ isAttrs c.config || throw ''
572572+ In module `${c.file}', you're trying to define a value of type `${builtins.typeOf c.config}'
573573+ rather than an attribute set for the option
574574+ `${builtins.concatStringsSep "." prefix}'!
575575+576576+ This usually happens if `${builtins.concatStringsSep "." prefix}' has option
577577+ definitions inside that are not matched. Please check how to properly define
578578+ this option by e.g. referring to `man 5 configuration.nix'!
579579+ ''
580580+ )
581581+ configs;
582582+ configs;
583583+587584 # an attrset 'name' => list of submodules that define ‘name’.
588588- defnsByName = byName "config" (module: value:
589589- map (config: { inherit (module) file; inherit config; }) (pushDownProperties value)
590590- ) configs;
585585+ pushedDownDefinitionsByName =
586586+ zipAttrsWith
587587+ (n: concatLists)
588588+ (map
589589+ (module:
590590+ mapAttrs
591591+ (n: value:
592592+ map (config: { inherit (module) file; inherit config; }) (pushDownProperties value)
593593+ )
594594+ module.config
595595+ )
596596+ checkedConfigs);
591597 # extract the definitions for each loc
592592- defnsByName' = byName "config" (module: value:
593593- [{ inherit (module) file; inherit value; }]
594594- ) configs;
598598+ rawDefinitionsByName =
599599+ zipAttrsWith
600600+ (n: concatLists)
601601+ (map
602602+ (module:
603603+ mapAttrs
604604+ (n: value:
605605+ [{ inherit (module) file; inherit value; }]
606606+ )
607607+ module.config
608608+ )
609609+ checkedConfigs);
595610596611 # Convert an option tree decl to a submodule option decl
597612 optionTreeToOption = decl:
···613628 # We're descending into attribute ‘name’.
614629 let
615630 loc = prefix ++ [name];
616616- defns = defnsByName.${name} or [];
617617- defns' = defnsByName'.${name} or [];
631631+ defns = pushedDownDefinitionsByName.${name} or [];
632632+ defns' = rawDefinitionsByName.${name} or [];
618633 optionDecls = filter (m: isOption m.options) decls;
619634 in
620635 if length optionDecls == length decls then
···657672 # Propagate all unmatched definitions from nested option sets
658673 mapAttrs (n: v: v.unmatchedDefns) resultsByName
659674 # Plus the definitions for the current prefix that don't have a matching option
660660- // removeAttrs defnsByName' (attrNames matchedOptions);
675675+ // removeAttrs rawDefinitionsByName (attrNames matchedOptions);
661676 in {
662677 inherit matchedOptions;
663678
+1-1
lib/tests/modules.sh
···207207## shorthandOnlyDefines config behaves as expected
208208checkConfigOutput '^true$' config.submodule.config ./declare-submoduleWith-shorthand.nix ./define-submoduleWith-shorthand.nix
209209checkConfigError 'is not of type `boolean' config.submodule.config ./declare-submoduleWith-shorthand.nix ./define-submoduleWith-noshorthand.nix
210210-checkConfigError "You're trying to define a value of type \`bool'\n\s*rather than an attribute set for the option" config.submodule.config ./declare-submoduleWith-noshorthand.nix ./define-submoduleWith-shorthand.nix
210210+checkConfigError "In module ..*define-submoduleWith-shorthand.nix., you're trying to define a value of type \`bool'\n\s*rather than an attribute set for the option" config.submodule.config ./declare-submoduleWith-noshorthand.nix ./define-submoduleWith-shorthand.nix
211211checkConfigOutput '^true$' config.submodule.config ./declare-submoduleWith-noshorthand.nix ./define-submoduleWith-noshorthand.nix
212212213213## submoduleWith should merge all modules in one swoop