Skip to content

Commit

Permalink
groups: implement global and local overrides
Browse files Browse the repository at this point in the history
implement options:
- options.overrides
- options.groups.<name>.overrides

Having this, it should not be necessary anymore to override members of a group directly. Instead the logic can be bound to package names, either globally for all groups, or locally for a single group.

We should probably also rename :
- `options.commonModule` -> `options.overrideAll`
- `options.groups.<name>.commonModules` -> `options.groups.<name>.overrideAll`
  • Loading branch information
DavHau authored and mergify[bot] committed Oct 29, 2023
1 parent 4bd6022 commit 752936f
Show file tree
Hide file tree
Showing 5 changed files with 302 additions and 50 deletions.
120 changes: 71 additions & 49 deletions modules/dream2nix/groups/group.nix
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
{commonModule}: {
lib,
dream2nix,
{
commonModule,
globalOverrides,
}: {
config,
dream2nix,
lib,
specialArgs,
...
}: let
t = lib.types;
packageType = t.deferredModuleWith {
staticModules = [
dream2nix.modules.dream2nix.core
{_module.args = specialArgs;}
# the top-level commonModule
commonModule
# the commonModule of the current group
config.commonModule
];
};
packageType = name:
t.deferredModuleWith {
staticModules = [
{_module.args = specialArgs;}
# the top-level commonModule
commonModule
# the commonModule of the current group
config.commonModule
# the global overrides
(globalOverrides.${name} or {})
# the overrides of the current group
(config.overrides.${name} or {})
];
};
in {
options = {
commonModule = lib.mkOption {
Expand All @@ -26,10 +33,15 @@ in {
default = {};
};
overrides = lib.mkOption {
type = t.attrs;
type = t.lazyAttrsOf (t.deferredModuleWith {
staticModules = [
{_module.args = specialArgs;}
];
});
description = ''
A set of package overrides
Holds overrides for the packages in the current groups
'';
default = {};
};
packages = lib.mkOption {
description = ''
Expand All @@ -56,41 +68,51 @@ in {
```
'';
# name version options
type = t.lazyAttrsOf (t.lazyAttrsOf (t.submoduleWith {
modules = [
({config, ...}: {
options.module = lib.mkOption {
# this is a deferredModule type
type = packageType;
description = ''
The package configuration
'';
default = {};
};
options.evaluated = lib.mkOption {
type = t.submoduleWith {
modules = [config.module];
inherit specialArgs;
type = let
submoduleWithNameVersion = import ./submoduleWithNameVersion.nix {
inherit lib;
};
in
t.lazyAttrsOf (t.lazyAttrsOf (submoduleWithNameVersion {
modules = [
({
config,
name,
version,
...
}: {
options.module = lib.mkOption {
# this is a deferredModule type
type = packageType name;
description = ''
The package configuration
'';
default = {};
};
description = ''
The evaluated dream2nix package modules
'';
internal = true;
default = {};
};
options.public = lib.mkOption {
type = t.package;
description = ''
The evaluated package ready to consume
'';
readOnly = true;
default = config.evaluated.public;
defaultText = lib.literalExpression "config.evaluated.public";
};
})
];
inherit specialArgs;
}));
options.evaluated = lib.mkOption {
type = t.submoduleWith {
modules = [config.module];
inherit specialArgs;
};
description = ''
The evaluated dream2nix package modules
'';
internal = true;
default = {};
};
options.public = lib.mkOption {
type = t.package;
description = ''
The evaluated package ready to consume
'';
readOnly = true;
default = config.evaluated.public;
defaultText = lib.literalExpression "config.evaluated.public";
};
})
];
inherit specialArgs;
}));
};
};
}
21 changes: 20 additions & 1 deletion modules/dream2nix/groups/interface.nix
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
t = lib.types;
groupType = t.submoduleWith {
modules = [
(import ./group.nix {inherit (config) commonModule;})
(import ./group.nix {
inherit (config) commonModule;
globalOverrides = config.overrides;
})
];
inherit specialArgs;
};
Expand All @@ -28,5 +31,21 @@ in {
'';
default = {};
};
overrides = lib.mkOption {
type = t.lazyAttrsOf (t.deferredModuleWith {
staticModules = [
{_module.args = specialArgs;}
];
});
description = ''
Holds overrides for all packages in all groups
'';
default = {};
example = {
hello.postPatch = ''
substituteInPlace Makefile --replace /usr/local /usr
'';
};
};
};
}
164 changes: 164 additions & 0 deletions modules/dream2nix/groups/submoduleWithNameVersion.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# copied from nixpkgs with a single line added (see TODO)
{lib, ...}: let
inherit
(lib)
isAttrs
isFunction
optionalAttrs
last
mkOptionType
types
attrNames
;

inherit
(lib.types)
path
defaultFunctor
;

submoduleWith = {
modules,
specialArgs ? {},
shorthandOnlyDefinesConfig ? false,
description ? null,
class ? null,
} @ attrs: let
inherit (lib.modules) evalModules;

allModules = defs:
map (
{
value,
file,
}:
if isAttrs value && shorthandOnlyDefinesConfig
then {
_file = file;
config = value;
}
else {
_file = file;
imports = [value];
}
)
defs;

base = evalModules {
inherit class specialArgs;
modules =
[
{
# This is a work-around for the fact that some sub-modules,
# such as the one included in an attribute set, expects an "args"
# attribute to be given to the sub-module. As the option
# evaluation does not have any specific attribute name yet, we
# provide a default for the documentation and the freeform type.
#
# This is necessary as some option declaration might use the
# "name" attribute given as argument of the submodule and use it
# as the default of option declarations.
#
# We use lookalike unicode single angle quotation marks because
# of the docbook transformation the options receive. In all uses
# &gt; and &lt; wouldn't be encoded correctly so the encoded values
# would be used, and use of `<` and `>` would break the XML document.
# It shouldn't cause an issue since this is cosmetic for the manual.
_module.args.name = lib.mkOptionDefault "‹name›";
}
]
++ modules;
};

freeformType = base._module.freeformType;

name = "submodule";
in
mkOptionType {
inherit name;
description =
if description != null
then description
else freeformType.description or name;
check = x: isAttrs x || isFunction x || path.check x;
merge = loc: defs:
(base.extendModules {
modules =
[
{
# this is the only line that was added
# TODO: think about ways to upstream this
_module.args.name = lib.elemAt loc ((lib.length loc) - 2);
_module.args.version = lib.last loc;
}
]
++ allModules defs;
prefix = loc;
})
.config;
emptyValue = {value = {};};
getSubOptions = prefix:
(base.extendModules
{inherit prefix;})
.options
// optionalAttrs (freeformType != null) {
# Expose the sub options of the freeform type. Note that the option
# discovery doesn't care about the attribute name used here, so this
# is just to avoid conflicts with potential options from the submodule
_freeformOptions = freeformType.getSubOptions prefix;
};
getSubModules = modules;
substSubModules = m:
submoduleWith (attrs
// {
modules = m;
});
nestedTypes = lib.optionalAttrs (freeformType != null) {
freeformType = freeformType;
};
functor =
defaultFunctor name
// {
type = types.submoduleWith;
payload = {
inherit modules class specialArgs shorthandOnlyDefinesConfig description;
};
binOp = lhs: rhs: {
class =
# `or null` was added for backwards compatibility only. `class` is
# always set in the current version of the module system.
if lhs.class or null == null
then rhs.class or null
else if rhs.class or null == null
then lhs.class or null
else if lhs.class or null == rhs.class
then lhs.class or null
else throw "A submoduleWith option is declared multiple times with conflicting class values \"${toString lhs.class}\" and \"${toString rhs.class}\".";
modules = lhs.modules ++ rhs.modules;
specialArgs = let
intersecting = builtins.intersectAttrs lhs.specialArgs rhs.specialArgs;
in
if intersecting == {}
then lhs.specialArgs // rhs.specialArgs
else throw "A submoduleWith option is declared multiple times with the same specialArgs \"${toString (attrNames intersecting)}\"";
shorthandOnlyDefinesConfig =
if lhs.shorthandOnlyDefinesConfig == null
then rhs.shorthandOnlyDefinesConfig
else if rhs.shorthandOnlyDefinesConfig == null
then lhs.shorthandOnlyDefinesConfig
else if lhs.shorthandOnlyDefinesConfig == rhs.shorthandOnlyDefinesConfig
then lhs.shorthandOnlyDefinesConfig
else throw "A submoduleWith option is declared multiple times with conflicting shorthandOnlyDefinesConfig values";
description =
if lhs.description == null
then rhs.description
else if rhs.description == null
then lhs.description
else if lhs.description == rhs.description
then lhs.description
else throw "A submoduleWith option is declared multiple times with conflicting descriptions";
};
};
};
in
submoduleWith
1 change: 1 addition & 0 deletions tests/nix-unit/fixtures.nix
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
];

inherit name;
version = "1.0.0";

# set options
builtins-derivation = {
Expand Down
Loading

0 comments on commit 752936f

Please sign in to comment.