feat: propagating hooks and context

This commit is contained in:
Jake Hamilton 2024-07-08 23:07:59 -07:00
parent 42e69f7d43
commit 62bc2f4eee
Signed by untrusted user: jakehamilton
GPG key ID: 9762169A1B35EA68
9 changed files with 352 additions and 131 deletions

View file

@ -47,10 +47,12 @@ lib: {
in in
if sorted ? result then if sorted ? result then
{ {
result = builtins.map (value: { result = builtins.map
(value: {
name = value.name; name = value.name;
value = value.value; value = value.value;
}) sorted.result; })
sorted.result;
} }
else else
sorted; sorted;
@ -64,7 +66,8 @@ lib: {
defaults = defaults =
graph: defaults: graph: defaults:
let let
result = builtins.mapAttrs ( result = builtins.mapAttrs
(
name: entry: name: entry:
if defaults ? ${name} then if defaults ? ${name} then
if builtins.isString entry then if builtins.isString entry then
@ -81,7 +84,8 @@ lib: {
} }
else else
entry entry
) graph; )
graph;
in in
defaults // result; defaults // result;
}; };

View file

@ -23,10 +23,12 @@ lib: {
parts = lib.strings.split "(0|[1-9][0-9]*)" string; parts = lib.strings.split "(0|[1-9][0-9]*)" string;
in in
builtins.map serialize parts; builtins.map serialize parts;
prepared = builtins.map (value: [ prepared = builtins.map
(value: [
(vectorize value) (vectorize value)
value value
]) list; ])
list;
isLess = a: b: (lib.lists.compare lib.numbers.compare (builtins.head a) (builtins.head b)) < 0; isLess = a: b: (lib.lists.compare lib.numbers.compare (builtins.head a) (builtins.head b)) < 0;
in in
builtins.map (x: builtins.elemAt x 1) (builtins.sort isLess prepared); builtins.map (x: builtins.elemAt x 1) (builtins.sort isLess prepared);
@ -189,10 +191,12 @@ lib: {
list list
else else
builtins.tail ( builtins.tail (
builtins.concatMap (part: [ builtins.concatMap
(part: [
separator separator
part part
]) list ])
list
); );
## Create a list of integers from a starting number to an ending ## Create a list of integers from a starting number to an ending
@ -224,5 +228,14 @@ lib: {
filter = result: value: if builtins.elem value result then result else result ++ [ value ]; filter = result: value: if builtins.elem value result then result else result ++ [ value ];
in in
builtins.foldl' filter [ ] list; builtins.foldl' filter [ ] list;
## Flatten a list of lists into a single list.
##
## @type List (List a) -> List a
flatten = value:
if builtins.isList value then
builtins.concatMap lib.lists.flatten value
else
[ value ];
}; };
} }

View file

@ -8,10 +8,10 @@
}, },
"locked": { "locked": {
"dir": "foundation", "dir": "foundation",
"dirtyRev": "2be3111b2c0911f40b47fe0a1fb22b5f5188cf59-dirty", "dirtyRev": "42e69f7d43c0fb108ac70fde118c4a8ff6a777b0-dirty",
"dirtyShortRev": "2be3111-dirty", "dirtyShortRev": "42e69f7-dirty",
"lastModified": 1719251485, "lastModified": 1720466389,
"narHash": "sha256-9G1TPBdlQNXCZf6A66bCT9m2vhodkSF+rDtqOVuFteY=", "narHash": "sha256-Zrmbcb+42r6eZV05QbcG2znHXrOh0sbdRaEZYxV0C7A=",
"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": "2be3111b2c0911f40b47fe0a1fb22b5f5188cf59-dirty", "dirtyRev": "42e69f7d43c0fb108ac70fde118c4a8ff6a777b0-dirty",
"dirtyShortRev": "2be3111-dirty", "dirtyShortRev": "42e69f7-dirty",
"lastModified": 1719251485, "lastModified": 1720466389,
"narHash": "sha256-9G1TPBdlQNXCZf6A66bCT9m2vhodkSF+rDtqOVuFteY=", "narHash": "sha256-Zrmbcb+42r6eZV05QbcG2znHXrOh0sbdRaEZYxV0C7A=",
"type": "git", "type": "git",
"url": "file:../?dir=lib" "url": "file:../?dir=lib"
}, },

View file

@ -1,10 +1,8 @@
{ lib, config }: { config }:
let let
cfg = config.builders.basic; cfg = config.builders.basic;
lib' = config.lib; inherit (config) lib foundation;
inherit (config) foundation;
in in
{ {
config.builders = { config.builders = {
@ -14,7 +12,36 @@ in
build = build =
package: package:
let let
phases = lib.dag.apply.defaults package.phases { system = package.platform.build.double;
dependencies = lib.packages.dependencies.collect package;
context = lib.packages.context.create dependencies { };
hooks = lib.packages.hooks.create dependencies context;
cflags = builtins.concatStringsSep " " (context.build.host.cflags or [ ]);
phasesWithHooks =
let
all = lib.lists.flatten [
hooks.build.only
hooks.build.build
hooks.build.host
hooks.build.target
hooks.host.only
hooks.host.host
hooks.host.target
hooks.target.only
hooks.target.target
];
in
builtins.foldl'
(final: defaults: lib.dag.apply.defaults final defaults)
package.phases
all;
phases = lib.dag.apply.defaults phasesWithHooks {
unpack = lib.dag.entry.before [ "patch" ] ""; unpack = lib.dag.entry.before [ "patch" ] "";
patch = lib.dag.entry.between [ "unpack" ] [ "configure" ] ""; patch = lib.dag.entry.between [ "unpack" ] [ "configure" ] "";
@ -25,13 +52,12 @@ in
install = lib.dag.entry.after [ "build" ] ""; install = lib.dag.entry.after [ "build" ] "";
}; };
sorted = lib.dag.sort.topological phases; sorted = lib.dag.sort.topological phases;
script = lib.strings.concatMapSep "\n" ( script = lib.strings.concatMapSep "\n"
entry: if builtins.isFunction entry.value then entry.value package else entry.value (entry: entry.value)
) sorted.result; sorted.result;
system = package.platform.build.double;
built = builtins.derivation ( built = builtins.derivation (
package.env package.env
@ -46,7 +72,7 @@ in
PATH = PATH =
let let
bins = lib.paths.bin ( bins = lib.paths.bin (
(lib'.packages.dependencies.getPackages package.deps.build.host) (lib.packages.dependencies.get dependencies.build.host)
++ [ ++ [
foundation.stage2-bash foundation.stage2-bash
foundation.stage2-coreutils foundation.stage2-coreutils
@ -85,7 +111,8 @@ in
inherit (package) meta; inherit (package) meta;
extras = { extras = {
inherit package; inherit package;
}; phases = builtins.listToAttrs sorted.result;
} // package.extras;
}; };
}; };
}; };

View file

@ -14,6 +14,7 @@ in
packages = { packages = {
aux-a = config.packages.aux.a; aux-a = config.packages.aux.a;
aux-b = config.packages.aux.b;
# foundation-gcc-x86_64 = # foundation-gcc-x86_64 =
# (config.packages.foundation.gcc.versions."13.2.0".extend (args: { # (config.packages.foundation.gcc.versions."13.2.0".extend (args: {

View file

@ -6,18 +6,178 @@ in
config = { config = {
lib.packages = { lib.packages = {
dependencies = { dependencies = {
getPackages = get =
dependencies: dependencies:
let let
available = builtins.filter (dependency: !(builtins.isNull dependency)) ( exists = value: ! (builtins.isNull value);
builtins.attrValues dependencies available = builtins.filter exists dependencies;
);
in in
builtins.map (dependency: dependency.package) available; builtins.map (dependency: dependency.package) available;
build = build =
build': host': target': build': host': target':
builtins.mapAttrs (name: dep: lib'.packages.build dep build' host' target'); builtins.mapAttrs (name: dep: lib'.packages.build dep build' host' target');
collect = package:
let
isPropagated = name: package: package.propagate or false;
getPropagatedDependencies = target: builtins.attrValues (lib.attrs.filter isPropagated target);
process = dependencies:
let
getDeps = name: dependency:
let
deps =
{
build = {
only = getPropagatedDependencies dependency.deps.build.only ++ process dependency.deps.build.only;
build = getPropagatedDependencies dependency.deps.build.build ++ process dependency.deps.build.build;
host = getPropagatedDependencies dependency.deps.build.host ++ process dependency.deps.build.host;
target = getPropagatedDependencies dependency.deps.build.target ++ process dependency.deps.build.target;
};
host = {
only = getPropagatedDependencies dependency.deps.host.only ++ process dependency.deps.host.only;
host = getPropagatedDependencies dependency.deps.host.host ++ process dependency.deps.host.host;
target = getPropagatedDependencies dependency.deps.host.target ++ process dependency.deps.host.target;
};
target = {
only = getPropagatedDependencies dependency.deps.target.only ++ process dependency.deps.target.only;
target = getPropagatedDependencies dependency.deps.target.target ++ process dependency.deps.target.target;
};
};
in
lib.lists.flatten
[
deps.build.only
deps.build.build
deps.build.host
deps.build.target
deps.host.only
deps.host.host
deps.host.target
deps.target.only
deps.target.target
];
propagated = lib.attrs.mapToList getDeps dependencies;
in
lib.lists.flatten propagated;
in
{
build = {
only = builtins.attrValues package.deps.build.only ++ process package.deps.build.only;
build = builtins.attrValues package.deps.build.build ++ process package.deps.build.build;
host = builtins.attrValues package.deps.build.host ++ process package.deps.build.host;
target = builtins.attrValues package.deps.build.target ++ process package.deps.build.target;
};
host = {
only = builtins.attrValues package.deps.host.only ++ process package.deps.host.only;
host = builtins.attrValues package.deps.host.host ++ process package.deps.host.host;
target = builtins.attrValues package.deps.host.target ++ process package.deps.host.target;
};
target = {
only = builtins.attrValues package.deps.target.only ++ process package.deps.target.only;
target = builtins.attrValues package.deps.target.target ++ process package.deps.target.target;
};
};
};
context = {
create = collected: ctx:
let
process = path:
let
dependencies = lib.attrs.selectOrThrow path collected;
contexts = builtins.map (dependency: dependency.context or { }) dependencies;
base = ctx // {
target = builtins.concatStringsSep "." path;
};
result = lib.modules.run {
modules = builtins.map
(context: { config }: {
config = context;
})
contexts
++ [
{
freeform = lib.types.any;
options = config.packages.context.options // {
target = lib.options.create {
description = "The dependency target that is being generated.";
type = lib.types.enum [
"build.only"
"build.build"
"build.host"
"build.target"
"host.only"
"host.host"
"host.target"
"target.only"
"target.target"
];
};
};
config = base;
}
];
};
in
result.config;
in
{
build = {
only = process [ "build" "only" ];
build = process [ "build" "build" ];
host = process [ "build" "host" ];
target = process [ "build" "target" ];
};
host = {
only = process [ "host" "only" ];
host = process [ "host" "host" ];
target = process [ "host" "target" ];
};
target = {
only = process [ "target" "only" ];
target = process [ "target" "target" ];
};
};
};
hooks = {
create = collected: ctx:
let
process = path:
let
dependencies = lib.attrs.selectOrThrow path collected;
hooks = builtins.map
(dependency:
let
getHooks = dependency.hooks or (lib.fp.const { });
in
getHooks ctx)
dependencies;
in
hooks;
in
{
build = {
only = process [ "build" "only" ];
build = process [ "build" "build" ];
host = process [ "build" "host" ];
target = process [ "build" "target" ];
};
host = {
only = process [ "host" "only" ];
host = process [ "host" "host" ];
target = process [ "host" "target" ];
};
target = {
only = process [ "target" "only" ];
target = process [ "target" "target" ];
};
};
}; };
getLatest = getLatest =

View file

@ -280,6 +280,12 @@ in
}; };
}; };
extras = lib.options.create {
description = "Extra information for the package.";
type = lib.types.attrs.of lib.types.any;
default.value = { };
};
platform = { platform = {
build = lib.options.create { build = lib.options.create {
description = "The build platform for the package."; description = "The build platform for the package.";
@ -327,12 +333,6 @@ in
type = lib'.types.builder; type = lib'.types.builder;
}; };
phases = lib.options.create {
description = "The phases for the package.";
type = lib.types.dag.of lib.types.string;
default.value = { };
};
env = lib.options.create { env = lib.options.create {
description = "The environment for the package."; description = "The environment for the package.";
type = lib.types.attrs.of lib.types.string; type = lib.types.attrs.of lib.types.string;
@ -345,6 +345,30 @@ in
default.value = config.builder.build config; default.value = config.builder.build config;
}; };
phases = lib.options.create {
description = "The phases for the package.";
type = lib.types.dag.of lib.types.string;
default.value = { };
};
context = lib.options.create {
description = "The context information that the package provides.";
type = lib.types.attrs.of lib.types.raw;
default.value = { };
};
hooks = lib.options.create {
description = "The hooks that the package provides.";
type = lib.types.function (lib.types.dag.of lib.types.string);
default.value = ctx: { };
};
propagate = lib.options.create {
description = "Whether the package should propagate its hooks and context.";
type = lib.types.bool;
default.value = false;
};
deps = lib.options.create { deps = lib.options.create {
description = "The dependencies for the package."; description = "The dependencies for the package.";
type = deps build host target; type = deps build host target;

View file

@ -26,6 +26,16 @@ in
builder = builders.basic; builder = builders.basic;
context = {
cflags = [ "-I $AUX_B/include" ];
};
hooks = ctx: {
"aux:b:env" = lib.dag.entry.after
[ "unpack" ]
''export AUX_B=${config.package}'';
};
phases = { phases = {
install = '' install = ''
echo "b" > $out echo "b" > $out

View file

@ -19,14 +19,24 @@ in
type = lib.types.submodule { type = lib.types.submodule {
freeform = lib.types.packages; freeform = lib.types.packages;
options.cross = lib.attrs.generate doubles ( options = {
cross = lib.attrs.generate doubles (
system: system:
lib.options.create { lib.options.create {
description = "The cross-compiled package set for the ${system} target."; description = "The cross-compiled package set for the ${system} target.";
type = lib.types.packages; type = lib.types.packages;
default = { }; default.value = { };
} }
); );
# NOTE: We may offer a way to set default context values. For this reason we have
# nested `options` under `context` rather than using a plain option directly under `packages`.
context.options = lib.options.create {
description = "The available options for package contexts.";
default.value = { };
type = lib.types.attrs.of lib.types.option;
};
};
}; };
}; };
@ -42,9 +52,11 @@ in
}; };
}; };
config.packages.cross = lib.attrs.generate doubles ( config = {
packages.cross = lib.attrs.generate doubles (
system: system:
builtins.mapAttrs ( builtins.mapAttrs
(
namespace: namespace:
builtins.mapAttrs ( builtins.mapAttrs (
name: alias: name: alias:
@ -62,38 +74,6 @@ in
} }
]; ];
}; };
# if package != {}
# then
# (package.extend (
# {config}: {
# config = {
# platform = {
# host = lib.modules.overrides.force system;
# target = lib.modules.overrides.default system;
# };
# deps = {
# build = {
# only = setHost package.deps.build.only;
# build = setHost package.deps.build.build;
# host = setHost package.deps.build.host;
# target = setHost package.deps.build.target;
# };
# host = {
# only = setHost package.deps.host.only;
# host = setHost package.deps.host.host;
# target = setHost package.deps.host.target;
# };
# target = {
# only = setHost package.deps.target.only;
# target = setHost package.deps.target.target;
# };
# };
# };
# }
# ))
# .config
# else package;
updated = alias // { updated = alias // {
versions = builtins.mapAttrs (version: package: setHost package) alias.versions; versions = builtins.mapAttrs (version: package: setHost package) alias.versions;
@ -101,6 +81,8 @@ in
in in
updated updated
) )
) packages )
packages
); );
};
} }