refactor: more elegant shorthand support

This commit is contained in:
Jake Hamilton 2024-06-15 02:35:37 -07:00
parent aa1c58f6ee
commit 2989fdc4fe
Signed by: jakehamilton
GPG key ID: 9762169A1B35EA68
13 changed files with 418 additions and 677 deletions

View file

@ -358,7 +358,7 @@ lib: {
in in
if builtins.isAttrs subtree if builtins.isAttrs subtree
then builtins.mapAttrs (name: f module) subtree then builtins.mapAttrs (name: f module) subtree
else builtins.throw "Value for `${builtins.concatStringsSep "." prefix} is of type `${builtins.typeOf subtree}` but an attribute set was expected." else builtins.throw "Value for `${builtins.concatStringsSep "." prefix}` is of type `${builtins.typeOf subtree}` but an attribute set was expected."
) )
modules); modules);

View file

@ -493,5 +493,113 @@ in {
}; };
in in
evaluated.config.aux.message == evaluated.config.aux.message2; evaluated.config.aux.message == evaluated.config.aux.message2;
"function submodules" = let
expected = "Hello, World!";
evaluated = lib.modules.run {
modules = [
{
options = {
aux = lib.options.create {
type = lib.types.submodules.of {
shorthand = false;
modules = [
{
options.message = lib.options.create {
type = lib.types.string;
};
}
];
};
};
};
config = {
aux = args: {
config.message = expected;
};
};
}
];
};
in
evaluated.config.aux.message == expected;
"merges submodules" = let
expected = "Hello, World!";
evaluated = lib.modules.run {
modules = [
{
options = {
aux = lib.options.create {
type = lib.types.submodules.of {
shorthand = false;
modules = [
{
options.message = lib.options.create {
type = lib.types.string;
};
}
];
};
};
};
config = {
aux = args: {
options.message2 = lib.options.create {
type = lib.types.string;
};
config.message = expected;
};
};
}
{
config = {
aux.config.message2 = expected;
};
}
];
};
in
evaluated.config.aux.message == evaluated.config.aux.message2;
"flexible shorthand" = let
expected = "Hello, World!";
evaluated = lib.modules.run {
modules = [
{
options = {
aux = lib.options.create {
type = lib.types.submodules.of {
shorthand = true;
modules = [
{
options.message = lib.options.create {
type = lib.types.string;
};
}
];
};
};
};
config = {
aux = args: {
options.message2 = lib.options.create {
type = lib.types.string;
};
config.message = expected;
};
};
}
{
config = {
aux.message2 = expected;
};
}
];
};
in
evaluated.config.aux.message == evaluated.config.aux.message2;
}; };
} }

View file

@ -756,7 +756,7 @@ lib: {
## with helpers like `lib.types.attrs.of` in order to produce more complex, ## with helpers like `lib.types.attrs.of` in order to produce more complex,
## dynamic types. ## dynamic types.
## ##
## @type { modules :: List Module, args? :: Attrs, description? :: String | Null } -> Attrs ## @type { modules :: List Module, args? :: Attrs, description? :: String | Null, shorthand? :: Bool } -> Attrs
of = settings @ { of = settings @ {
modules, modules,
args ? {}, args ? {},
@ -765,11 +765,24 @@ lib: {
}: let }: let
getModules = builtins.map ( getModules = builtins.map (
definition: definition:
if shorthand && builtins.isAttrs definition if shorthand && builtins.isAttrs definition.value
then { then let
__file__ = definition.__file__; config =
config = definition.value; definition.value.config
} or (
builtins.removeAttrs definition.value
(builtins.filter (key: key != "config") lib.modules.VALID_KEYS)
);
rest =
if definition.value ? config
then builtins.removeAttrs definition.value ["config"]
else lib.attrs.filter (name: value: builtins.elem name lib.modules.VALID_KEYS) definition.value;
in
rest
// {
__file__ = definition.__file__;
config = config;
}
else { else {
__file__ = definition.__file__; __file__ = definition.__file__;
includes = [definition.value]; includes = [definition.value];

View file

@ -8,10 +8,10 @@
}, },
"locked": { "locked": {
"dir": "foundation", "dir": "foundation",
"dirtyRev": "b3f9fe574e6ff545e4a6fe6ad5e90fc71baeece3-dirty", "dirtyRev": "aa1c58f6ee8a3ad6ad569f34477192462533c16b-dirty",
"dirtyShortRev": "b3f9fe5-dirty", "dirtyShortRev": "aa1c58f-dirty",
"lastModified": 1718366115, "lastModified": 1718411218,
"narHash": "sha256-/ktNtlY/usvE1YaathJ0f6y60cv3gQjc32SMKApsp7Y=", "narHash": "sha256-LOzQGGygC2U08zNwg1YNljjrJKJxnJ8S4RkX2v81yRw=",
"type": "git", "type": "git",
"url": "file:../?dir=foundation" "url": "file:../?dir=foundation"
}, },
@ -24,10 +24,10 @@
"lib": { "lib": {
"locked": { "locked": {
"dir": "lib", "dir": "lib",
"dirtyRev": "b3f9fe574e6ff545e4a6fe6ad5e90fc71baeece3-dirty", "dirtyRev": "aa1c58f6ee8a3ad6ad569f34477192462533c16b-dirty",
"dirtyShortRev": "b3f9fe5-dirty", "dirtyShortRev": "aa1c58f-dirty",
"lastModified": 1718366115, "lastModified": 1718411218,
"narHash": "sha256-/ktNtlY/usvE1YaathJ0f6y60cv3gQjc32SMKApsp7Y=", "narHash": "sha256-LOzQGGygC2U08zNwg1YNljjrJKJxnJ8S4RkX2v81yRw=",
"type": "git", "type": "git",
"url": "file:../?dir=lib" "url": "file:../?dir=lib"
}, },

View file

@ -26,11 +26,9 @@ in {
sorted.result; sorted.result;
system = package.platform.build; system = package.platform.build;
deps = lib'.packages.dependencies.resolve package.deps system;
in in
(builtins.trace script) builtins.derivation (
builtins.derivation (package.env package.env
// { // {
inherit (package) name; inherit (package) name;
inherit script system; inherit script system;
@ -40,7 +38,7 @@ in {
SHELL = cfg.executable; SHELL = cfg.executable;
PATH = lib.paths.bin ( PATH = lib.paths.bin (
(lib'.packages.dependencies.getPackages "build.host" deps) (lib'.packages.dependencies.getPackages package.deps.build.host)
++ [ ++ [
foundation.stage2-bash foundation.stage2-bash
foundation.stage2-coreutils foundation.stage2-coreutils
@ -67,7 +65,8 @@ in {
bash -eux $scriptPath bash -eux $scriptPath
'') '')
]; ];
}); }
);
}; };
}; };
} }

View file

@ -12,7 +12,7 @@ in {
modules = import ./modules.nix; modules = import ./modules.nix;
packages = { packages = {
example = lib'.packages.export "example.x"; example-x = config.packages.example.x;
}; };
}; };
}; };

View file

@ -1,6 +1,6 @@
{ {
config,
lib, lib,
config,
}: let }: let
in { in {
options = { options = {

View file

@ -3,12 +3,10 @@
lib, lib,
}: let }: let
lib' = config.lib; lib' = config.lib;
cfg = config.exports;
in { in {
options = { options = {
exports.packages = lib.options.create { exports.packages = lib.options.create {
type = lib.types.attrs.of (lib.types.function (lib.types.nullish lib.types.derivation)); type = lib.types.attrs.of (lib'.types.raw);
default.value = {}; default.value = {};
}; };
@ -22,17 +20,27 @@ in {
exported.packages = let exported.packages = let
all = lib.attrs.generate lib'.systems.doubles.all ( all = lib.attrs.generate lib'.systems.doubles.all (
system: let system: let
packages = all =
builtins.mapAttrs builtins.mapAttrs
(name: resolve: resolve system) (
cfg.packages; name: package: let
result = lib'.packages.build package system system;
in
result
)
config.exports.packages;
available = available =
lib.attrs.filter lib.attrs.filter
(name: package: package != null) (name: package: builtins.elem system package.meta.platforms)
packages; all;
packages =
builtins.mapAttrs
(name: package: package.package)
available;
in in
available packages
); );
available = available =

View file

@ -19,4 +19,8 @@ in {
apply = value: lib.extend (final: prev: prev.attrs.mergeRecursive prev value); apply = value: lib.extend (final: prev: prev.attrs.mergeRecursive prev value);
}; };
}; };
config = {
__module__.args.dynamic.lib' = config.lib;
};
} }

View file

@ -1,82 +1,65 @@
{ {
lib, lib,
lib',
config, config,
}: let }: {
lib' = config.lib;
in {
config = { config = {
lib.packages = { lib.packages = {
dependencies = { dependencies = {
getPackages = path: dependencies: let getPackages = dependencies: let
resolved = available =
if builtins.isList path builtins.filter
then path (dependency: !(builtins.isNull dependency))
else lib.strings.split "." path; (builtins.attrValues dependencies);
attrs = lib.attrs.select resolved {} dependencies;
in in
lib.attrs.mapToList (name: value: value.package) attrs; builtins.map (dependency: dependency.package) available;
resolve = dependencies: system:
builtins.mapAttrs
# Note that this does not correspond to the "host" and "target" platforms, but rather
# where the code is used and where it is intended to end up.
(
host: platforms:
builtins.mapAttrs
(
target: deps:
builtins.mapAttrs
(
name: dependency:
assert lib.errors.trace (dependency.namespace != null) "Namespace unknown for dependency `${name}`."; let
targeted = lib'.packages.export [dependency.namespace name] system;
in
if targeted == null
then builtins.throw "Dependency `${dependency.namespace}.${name}` does not support system `${system}`."
else targeted
)
deps
)
platforms
)
dependencies;
}; };
## Get a package from the package set by its path. The path can be either getLatest = alias: let
## a string or a list of strings that is used to access `config.packages.generic`. versions = builtins.attrNames alias.versions;
## sorted = builtins.sort (builtins.compareVersions) versions;
## @type String | List String -> Package
get = path: let
resolved =
if builtins.isList path
then path
else lib.strings.split "." path;
package = lib.attrs.selectOrThrow resolved config.packages.generic;
in in
assert lib.errors.trace (builtins.length resolved > 1) "Cannot get package without a namespace."; builtins.head sorted;
package
// { build = package: system: cross: let
namespace = lib.modules.override 99 (builtins.head resolved); resolved =
if package ? versions
then package.versions.${config.preferences.packages.version} or (lib'.packages.getLatest package)
else package;
buildDependencies = builtins.mapAttrs (name: dep: lib'.packages.build dep system cross);
result = resolved.extend ({config}: {
config = {
platform = {
build = system;
host = cross;
target = cross;
};
deps = {
build = {
only = buildDependencies resolved.deps.build.only;
build = buildDependencies resolved.deps.build.build;
host = buildDependencies resolved.deps.build.host;
target = buildDependencies resolved.deps.build.target;
};
host = {
only = buildDependencies resolved.deps.host.only;
host = buildDependencies resolved.deps.host.host;
target = buildDependencies resolved.deps.host.target;
};
target = {
only = buildDependencies resolved.deps.target.only;
target = buildDependencies resolved.deps.target.target;
};
};
package = config.builder.build config;
}; };
});
## Export a package by its path. Use this function with the `config.exports.packages.*`
## options.
##
## @type String | List String -> String -> Package
export = path: system: let
resolved =
if builtins.isList path
then path
else lib.strings.split "." path;
package = lib'.packages.get resolved;
targeted = lib.attrs.selectOrThrow resolved config.packages.targeted.${system};
in in
if builtins.elem system package.meta.platforms result.config;
then targeted.package
else null;
}; };
}; };
} }

View file

@ -1,9 +1,8 @@
{ {
lib, lib,
lib',
config, config,
}: let }: {
lib' = config.lib;
in {
config = { config = {
lib.types = { lib.types = {
license = let license = let
@ -51,58 +50,6 @@ in {
in in
lib.types.either type (lib.types.list.of type); lib.types.either type (lib.types.list.of type);
meta = lib.types.submodule {
options = {
description = lib.options.create {
description = "The description for the package.";
type = lib.types.nullish lib.types.string;
default.value = null;
};
homepage = lib.options.create {
description = "The homepage for the package.";
type = lib.types.nullish lib.types.string;
default.value = null;
};
license = lib.options.create {
description = "The license for the package.";
type = lib.types.nullish config.lib.types.license;
default.value = null;
};
free = lib.options.create {
description = "Whether the package is free.";
type = lib.types.bool;
default.value = true;
};
insecure = lib.options.create {
description = "Whether the package is insecure.";
type = lib.types.bool;
default.value = false;
};
broken = lib.options.create {
description = "Whether the package is broken.";
type = lib.types.bool;
default.value = false;
};
main = lib.options.create {
description = "The main entry point for the package.";
type = lib.types.nullish lib.types.string;
default.value = null;
};
platforms = lib.options.create {
description = "The platforms the package supports.";
type = lib.types.list.of lib.types.string;
default.value = [];
};
};
};
builder = lib.types.submodule { builder = lib.types.submodule {
freeform = lib.types.any; freeform = lib.types.any;
@ -114,523 +61,220 @@ in {
}; };
}; };
packages = { packages = lib.types.attrs.of (lib'.types.alias);
generic = lib.types.attrs.of (lib.types.submodule ({name}: {
freeform = lib.types.attrs.of (lib.types.submodule [
lib'.types.package.generic'
]);
}));
versioned = lib.types.attrs.of (lib.types.submodule ({name}: { alias = lib.types.attrs.of (lib.types.submodule {
freeform = lib.types.attrs.of (lib.types.submodule [ options = {
lib'.types.package.versioned' versions = lib.options.create {
]); description = "All available package versions.";
})); type = lib.types.attrs.of lib'.types.package;
# packages.<system>
targeted = lib.types.attrs.of (lib.types.submodule ({name}: let
system = name;
in {
# packages.<system>.<namespace>
freeform = lib.types.attrs.of (lib.types.submodule ({name}: let
namespace = name;
in {
# packages.<system>.<namespace>.<name>
freeform = lib.types.attrs.of (lib.types.submodule [
lib'.types.package.targeted'
{
config = {
namespace = lib.modules.override 99 namespace;
platform = {
build = system;
host = lib.modules.overrides.default system;
target = lib.modules.overrides.default system;
};
};
}
]);
}));
# packages.<system>.<cross>
options.cross = lib.attrs.generate lib'.systems.doubles.all (cross:
lib.options.create {
description = "A cross-compiling package set.";
default.value = {};
# packages.<system>.<cross>.<namespace>
type = lib.types.attrs.of (lib.types.submodule (
{name}: let
namespace = name;
in {
# packages.<system>.<cross>.<namespace>.<name>
freeform = lib.types.attrs.of (lib.types.submodule [
lib'.types.package.targeted'
{
config = {
namespace = lib.modules.override 99 namespace;
platform = {
build = system;
host = cross;
target = lib.modules.overrides.default cross;
};
};
}
]);
}
));
});
}));
};
dependencies = {
generic = lib.types.attrs.of (lib.types.nullish lib'.types.package.generic);
targeted = lib.types.attrs.of (lib.types.nullish lib'.types.package.targeted);
};
package = {
generic = lib.types.submodule lib'.types.package.generic';
generic' = args @ {
name ? assert false; null,
config,
}: {
freeform = lib.types.any;
options = {
namespace = lib.options.create {
description = "The namespace for the package.";
type = lib.types.nullish lib.types.string;
default.value = null;
};
pname = lib.options.create {
description = "The program name for the package";
type = lib.types.string;
default = {
text = "<name> or \"unknown\"";
value =
if args ? name
then args.name
else "unknown";
};
};
version = lib.options.create {
description = "The version for the package.";
type = lib.types.nullish lib.types.version;
default.value = null;
};
meta = lib.options.create {
description = "Metadata for the package.";
type = lib'.types.meta;
default.value = {
name = config.pname;
};
};
phases = lib.options.create {
description = "The phases for the package.";
type = lib.types.dag.of (
lib.types.either
lib.types.string
(lib.types.function lib.types.string)
);
default.value = {};
};
env = lib.options.create {
description = "The environment for the package.";
type = lib.types.attrs.of lib.types.string;
default.value = {};
};
builder = lib.options.create {
description = "The builder for the package.";
type = lib'.types.builder;
};
deps = {
build = {
only = lib.options.create {
description = "Dependencies which are only used in the build environment.";
type = lib'.types.dependencies.generic;
default.value = {};
};
build = lib.options.create {
description = "Dependencies which are created in the build environment and are executed in the build environment.";
type = lib'.types.dependencies.generic;
default.value = {};
};
host = lib.options.create {
description = "Dependencies which are created in the build environment and are executed in the host environment.";
type = lib'.types.dependencies.generic;
default.value = {};
};
target = lib.options.create {
description = "Dependencies which are created in the build environment and are executed in the target environment.";
type = lib'.types.dependencies.generic;
default.value = {};
};
};
host = {
only = lib.options.create {
description = "Dependencies which are only used in the host environment.";
type = lib'.types.dependencies.generic;
default.value = {};
};
host = lib.options.create {
description = "Dependencies which are executed in the host environment.";
type = lib'.types.dependencies.generic;
default.value = {};
};
target = lib.options.create {
description = "Dependencies which are executed in the host environment which produces code for the target environment.";
type = lib'.types.dependencies.generic;
default.value = {};
};
};
target = {
only = lib.options.create {
description = "Dependencies which are only used in the target environment.";
type = lib'.types.dependencies.generic;
default.value = {};
};
target = lib.options.create {
description = "Dependencies which are executed in the target environment.";
type = lib'.types.dependencies.generic;
default.value = {};
};
};
};
versions = lib.options.create {
description = "Available package versions.";
type = lib.types.attrs.of lib'.types.package.versioned;
default.value = {};
};
}; };
}; };
});
versioned = lib.types.submodule lib'.types.package.versioned'; dependencies = lib.types.attrs.of (lib.types.nullish (lib.types.either lib'.types.alias lib'.types.package));
versioned' = args @ { package = lib.types.submodule ({
name ? assert false; null, config,
config, meta,
}: { }: {
freeform = lib.types.any; options = {
extend = lib.options.create {
options = { description = "Extend the package's submodules with additional configuration.";
namespace = lib.options.create { type = lib.types.function lib.types.raw;
description = "The namespace for the package."; default.value = value:
type = lib.types.nullish lib.types.string; meta.extend {
default.value = null; modules =
}; if builtins.isAttrs value
then [{config = value;}]
pname = lib.options.create { else lib.lists.from.any value;
description = "The program name for the package";
type = lib.types.string;
default = {
text = "<name> or \"unknown\"";
value =
if args ? name
then args.name
else "unknown";
}; };
}; };
version = lib.options.create { name = lib.options.create {
description = "The version for the package."; description = "The name of the package.";
type = lib.types.nullish lib.types.version; type = lib.types.string;
default.value = null; default = {
}; text = "\${config.pname}-\${config.version}";
value =
meta = lib.options.create { if config.pname != null && config.version != null
type = lib'.types.meta; then "${config.pname}-${config.version}"
default.value = { else "";
name = config.pname;
};
};
phases = lib.options.create {
description = "The phases for the package.";
type = lib.types.dag.of (
lib.types.either
lib.types.string
(lib.types.function lib.types.string)
);
default.value = {};
};
env = lib.options.create {
description = "The environment for the package.";
type = lib.types.attrs.of lib.types.string;
default.value = {};
};
builder = lib.options.create {
description = "The builder for the package.";
type = lib'.types.builder;
};
deps = {
build = {
only = lib.options.create {
description = "Dependencies which are only used in the build environment.";
type = lib'.types.dependencies.generic;
default.value = {};
};
build = lib.options.create {
description = "Dependencies which are created in the build environment and are executed in the build environment.";
type = lib'.types.dependencies.generic;
default.value = {};
};
host = lib.options.create {
description = "Dependencies which are created in the build environment and are executed in the host environment.";
type = lib'.types.dependencies.generic;
default.value = {};
};
target = lib.options.create {
description = "Dependencies which are created in the build environment and are executed in the target environment.";
type = lib'.types.dependencies.generic;
default.value = {};
};
};
host = {
only = lib.options.create {
description = "Dependencies which are only used in the host environment.";
type = lib'.types.dependencies.generic;
default.value = {};
};
host = lib.options.create {
description = "Dependencies which are executed in the host environment.";
type = lib'.types.dependencies.generic;
default.value = {};
};
target = lib.options.create {
description = "Dependencies which are executed in the host environment which produces code for the target environment.";
type = lib'.types.dependencies.generic;
default.value = {};
};
};
target = {
only = lib.options.create {
description = "Dependencies which are only used in the target environment.";
type = lib'.types.dependencies.generic;
default.value = {};
};
target = lib.options.create {
description = "Dependencies which are executed in the target environment.";
type = lib'.types.dependencies.generic;
default.value = {};
};
};
};
versions = lib.options.create {
description = "Available package versions.";
type = lib.types.attrs.of lib'.types.package.versioned;
default.value = {};
}; };
}; };
};
targeted = lib.types.submodule lib'.types.package.targeted'; pname = lib.options.create {
description = "The program name for the package";
type = lib.types.nullish lib.types.string;
default.value = null;
};
targeted' = args @ { version = lib.options.create {
name ? assert false; null, description = "The version for the package.";
config, type = lib.types.nullish lib.types.version;
}: { default.value = null;
options = { };
freeform = lib.types.any;
name = lib.options.create { meta = {
description = "The name of the package."; description = lib.options.create {
type = lib.types.string; description = "The description for the package.";
default = {
text = "\${namespace}-\${pname}-\${version} or \${pname}-\${version}";
value = let
namespace =
if config.namespace == null
then ""
else "${config.namespace}-";
version =
if config.version == null
then ""
else "-${config.version}";
in "${namespace}${config.pname}${version}";
};
};
namespace = lib.options.create {
description = "The namespace for the package.";
type = lib.types.nullish lib.types.string; type = lib.types.nullish lib.types.string;
default.value = null; default.value = null;
}; };
pname = lib.options.create { homepage = lib.options.create {
description = "The program name for the package."; description = "The homepage for the package.";
type = lib.types.string; type = lib.types.nullish lib.types.string;
default = {
text = "<name> or \"unknown\"";
value =
if args ? name
then args.name
else "unknown";
};
};
version = lib.options.create {
description = "The version for the package.";
type = lib.types.nullish lib.types.version;
default.value = null; default.value = null;
}; };
meta = lib.options.create { license = lib.options.create {
description = "Metadata for the package."; description = "The license for the package.";
type = lib'.types.meta; type = lib.types.nullish lib'.types.license;
default.value = { default.value = null;
name = config.pname;
};
}; };
platform = { free = lib.options.create {
description = "Whether the package is free.";
type = lib.types.bool;
default.value = true;
};
insecure = lib.options.create {
description = "Whether the package is insecure.";
type = lib.types.bool;
default.value = false;
};
broken = lib.options.create {
description = "Whether the package is broken.";
type = lib.types.bool;
default.value = false;
};
main = lib.options.create {
description = "The main entry point for the package.";
type = lib.types.nullish lib.types.string;
default.value = null;
};
platforms = lib.options.create {
description = "The platforms the package supports.";
type = lib.types.list.of lib.types.string;
default.value = [];
};
};
platform = {
build = lib.options.create {
description = "The build platform for the package.";
type = lib.types.string;
default.value = "unknown";
};
host = lib.options.create {
description = "The host platform for the package.";
type = lib.types.string;
default.value = "unknown";
};
target = lib.options.create {
description = "The target platform for the package.";
type = lib.types.string;
default.value = "unknown";
};
};
phases = lib.options.create {
description = "The phases for the package.";
type = lib.types.dag.of (
lib.types.either
lib.types.string
(lib.types.function lib.types.string)
);
default.value = {};
};
env = lib.options.create {
description = "The environment for the package.";
type = lib.types.attrs.of lib.types.string;
default.value = {};
};
builder = lib.options.create {
description = "The builder for the package.";
type = lib'.types.builder;
};
deps = {
build = {
only = lib.options.create {
description = "Dependencies which are only used in the build environment.";
type = lib'.types.dependencies;
default.value = {};
};
build = lib.options.create { build = lib.options.create {
description = "The build platform for the package."; description = "Dependencies which are created in the build environment and are executed in the build environment.";
type = lib.types.nullish lib.types.string; type = lib'.types.dependencies;
default.value = null; default.value = {};
}; };
host = lib.options.create { host = lib.options.create {
description = "The host platform for the package."; description = "Dependencies which are created in the build environment and are executed in the host environment.";
type = lib.types.nullish lib.types.string; type = lib'.types.dependencies;
default.value = null; default.value = {};
}; };
target = lib.options.create { target = lib.options.create {
description = "The target platform for the package."; description = "Dependencies which are created in the build environment and are executed in the target environment.";
type = lib.types.nullish lib.types.string; type = lib'.types.dependencies;
default.value = null; default.value = {};
}; };
}; };
phases = lib.options.create { host = {
description = "Build phases for the package."; only = lib.options.create {
type = lib.types.dag.of ( description = "Dependencies which are only used in the host environment.";
lib.types.either type = lib'.types.dependencies;
lib.types.string default.value = {};
(lib.types.function lib.types.string)
);
default.value = {};
};
env = lib.options.create {
description = "The environment for the package.";
type = lib.types.attrs.of lib.types.string;
default.value = {};
};
builder = lib.options.create {
description = "The builder for the package.";
type = lib'.types.builder;
};
package = lib.options.create {
description = "The built derivation.";
type = lib.types.derivation;
};
deps = {
build = {
only = lib.options.create {
description = "Dependencies which are only used in the build environment.";
type = lib'.types.dependencies.targeted;
default.value = {};
};
build = lib.options.create {
description = "Dependencies which are created in the build environment and are executed in the build environment.";
type = lib'.types.dependencies.targeted;
default.value = {};
};
host = lib.options.create {
description = "Dependencies which are created in the build environment and are executed in the host environment.";
type = lib'.types.dependencies.targeted;
default.value = {};
};
target = lib.options.create {
description = "Dependencies which are created in the build environment and are executed in the target environment.";
type = lib'.types.dependencies.targeted;
default.value = {};
};
}; };
host = { host = lib.options.create {
only = lib.options.create { description = "Dependencies which are executed in the host environment.";
description = "Dependencies which are only used in the host environment."; type = lib'.types.dependencies;
type = lib'.types.dependencies.targeted; default.value = {};
default.value = {};
};
host = lib.options.create {
description = "Dependencies which are executed in the host environment.";
type = lib'.types.dependencies.targeted;
default.value = {};
};
target = lib.options.create {
description = "Dependencies which are executed in the host environment which produces code for the target environment.";
type = lib'.types.dependencies.targeted;
default.value = {};
};
}; };
target = { target = lib.options.create {
only = lib.options.create { description = "Dependencies which are executed in the host environment which produces code for the target environment.";
description = "Dependencies which are only used in the target environment."; type = lib'.types.dependencies;
type = lib'.types.dependencies.targeted; default.value = {};
default.value = {};
};
target = lib.options.create {
description = "Dependencies which are executed in the target environment.";
type = lib'.types.dependencies.targeted;
default.value = {};
};
}; };
}; };
versions = lib.options.create { target = {
description = "Available package versions."; only = lib.options.create {
type = lib.types.attrs.of lib'.types.package.versioned; description = "Dependencies which are only used in the target environment.";
default.value = {}; type = lib'.types.dependencies;
default.value = {};
};
target = lib.options.create {
description = "Dependencies which are executed in the target environment.";
type = lib'.types.dependencies;
default.value = {};
};
}; };
}; };
config = { package = lib.options.create {
package = config.builder.build config; description = "The built derivation.";
type = lib.types.derivation;
default.value = config.builder.build config;
}; };
}; };
}; });
}; };
}; };
} }

View file

@ -1,32 +1,37 @@
{ {
lib, lib,
lib',
config, config,
options, options,
}: let }: let
lib' = config.lib; builders = config.builders;
in { in {
config = { config = {
packages = { packages = {
generic = { example = {
example = { x = {
x = { versions = {
meta.platforms = ["i686-linux" "x86_64-linux"]; "latest" = {config}: {
version = "1.0.0"; config = {
meta = {
platforms = ["i686-linux" "x86_64-linux"];
};
builder = config.builders.basic; pname = "x";
version = "1.0.0";
phases = { builder = builders.basic;
build = package: ''
make --build ${package.platform.build} --host ${package.platform.host}
'';
install = lib.dag.entry.after ["build"] '' phases = {
make install DESTDIR=$out build = ''
''; make --build ${config.platform.build} --host ${config.platform.host}
}; '';
versions = { install = lib.dag.entry.after ["build"] ''
"latest" = config.packages.generic.example.x; make install DESTDIR=$out
'';
};
};
}; };
}; };
}; };

View file

@ -5,46 +5,23 @@
lib' = config.lib; lib' = config.lib;
doubles = lib'.systems.doubles.all; doubles = lib'.systems.doubles.all;
generic = config.packages.generic;
getPackages = system:
builtins.mapAttrs
(
namespace: packages:
lib.attrs.filter
(name: package: builtins.elem system package.meta.platforms)
packages
);
targeted = lib.attrs.generate lib'.systems.doubles.all (system:
getPackages system generic
// {
cross = lib.attrs.generate doubles (
host: getPackages host generic
);
});
in { in {
includes = [ includes = [
./aux/foundation.nix ./aux/foundation.nix
]; ];
options = { options = {
packages = { packages = lib.options.create {
generic = lib.options.create { description = "The package set.";
type = lib'.types.packages.generic; type = lib'.types.packages;
default.value = {};
};
targeted = lib.options.create {
type = lib'.types.packages.targeted;
};
}; };
};
config = { preferences.packages = {
packages = { version = lib.options.create {
inherit targeted; description = "The preferred package version when using aliases.";
type = lib.types.enum ["latest" "stable"];
default.value = "latest";
};
}; };
}; };
} }