mirror of
https://github.com/nix-community/dream2nix.git
synced 2024-11-21 21:22:23 +03:00
groups: implement global and local overrides
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`
This commit is contained in:
parent
4bd6022300
commit
752936f0c2
@ -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 {
|
||||
@ -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 = ''
|
||||
@ -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;
|
||||
}));
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
@ -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
modules/dream2nix/groups/submoduleWithNameVersion.nix
Normal file
164
modules/dream2nix/groups/submoduleWithNameVersion.nix
Normal file
@ -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
|
||||
# > and < 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
|
@ -7,6 +7,7 @@
|
||||
];
|
||||
|
||||
inherit name;
|
||||
version = "1.0.0";
|
||||
|
||||
# set options
|
||||
builtins-derivation = {
|
||||
|
@ -35,4 +35,50 @@ in {
|
||||
expr = "${config.groups.my-group.packages.hello."1.0.0".public.name}";
|
||||
expected = "hello-mod";
|
||||
};
|
||||
|
||||
groups_overrides_global = let
|
||||
config = eval {
|
||||
groups.my-group.packages.foo."1.0.0".module = {...}: fixtures.basic-derivation;
|
||||
groups.my-group.packages.bar."1.0.0".module = {...}: fixtures.basic-derivation;
|
||||
overrides = {foo = {version = lib.mkForce "2.0.0";};};
|
||||
};
|
||||
in {
|
||||
test_foo_changed = {
|
||||
expr = "${config.groups.my-group.packages.foo."1.0.0".public.version}";
|
||||
expected = "2.0.0";
|
||||
};
|
||||
test_bar_unchanged = {
|
||||
expr = "${config.groups.my-group.packages.bar."1.0.0".public.version}";
|
||||
expected = "1.0.0";
|
||||
};
|
||||
};
|
||||
|
||||
groups_overrides_local = let
|
||||
config = eval {
|
||||
groups.my-group.packages.foo."1.0.0".module = {...}: fixtures.basic-derivation;
|
||||
groups.my-group.packages.bar."1.0.0".module = {...}: fixtures.basic-derivation;
|
||||
groups.my-group.overrides = {foo = {version = lib.mkForce "2.0.0";};};
|
||||
};
|
||||
in {
|
||||
test_foo_changed = {
|
||||
expr = "${config.groups.my-group.packages.foo."1.0.0".public.version}";
|
||||
expected = "2.0.0";
|
||||
};
|
||||
test_bar_unchanged = {
|
||||
expr = "${config.groups.my-group.packages.bar."1.0.0".public.version}";
|
||||
expected = "1.0.0";
|
||||
};
|
||||
};
|
||||
|
||||
test_groups_overrides_collision = let
|
||||
config = eval {
|
||||
groups.my-group.packages.foo."1.0.0".module = {...}: fixtures.basic-derivation;
|
||||
groups.my-group.packages.bar."1.0.0".module = {...}: fixtures.basic-derivation;
|
||||
overrides = {foo = {version = lib.mkForce "2.0.0";};};
|
||||
groups.my-group.overrides = {foo = {version = lib.mkForce "3.0.0";};};
|
||||
};
|
||||
in {
|
||||
expr = "${config.groups.my-group.packages.foo."1.0.0".public.version}";
|
||||
expectedError.msg = ''The option `groups.my-group.packages.foo."1.0.0".evaluated.version' has conflicting definition values:'';
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user