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: 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
name = value.name; (value: {
value = value.value; name = value.name;
}) sorted.result; value = value.value;
})
sorted.result;
} }
else else
sorted; sorted;
@ -64,24 +66,26 @@ lib: {
defaults = defaults =
graph: defaults: graph: defaults:
let let
result = builtins.mapAttrs ( result = builtins.mapAttrs
name: entry: (
if defaults ? ${name} then name: entry:
if builtins.isString entry then if defaults ? ${name} then
{ if builtins.isString entry then
value = entry; {
before = defaults.${name}.before or [ ]; value = entry;
after = defaults.${name}.after or [ ]; before = defaults.${name}.before or [ ];
} after = defaults.${name}.after or [ ];
else }
entry else
// { entry
before = (entry.before or [ ]) ++ (defaults.${name}.before or [ ]); // {
after = (entry.after or [ ]) ++ (defaults.${name}.after or [ ]); before = (entry.before or [ ]) ++ (defaults.${name}.before or [ ]);
} after = (entry.after or [ ]) ++ (defaults.${name}.after or [ ]);
else }
entry else
) graph; entry
)
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
(vectorize value) (value: [
value (vectorize value)
]) list; value
])
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);
@ -123,8 +125,8 @@ lib: {
## @type List a -> a ## @type List a -> a
last = last =
list: list:
assert lib.errors.trace (list != [ ]) "List cannot be empty"; assert lib.errors.trace (list != [ ]) "List cannot be empty";
builtins.elemAt list (builtins.length list - 1); builtins.elemAt list (builtins.length list - 1);
## Slice part of a list to create a new list. ## Slice part of a list to create a new list.
## ##
@ -163,8 +165,8 @@ lib: {
## @type List -> List ## @type List -> List
init = init =
list: list:
assert lib.errors.trace (builtins.length list != 0) "lib.lists.init: list must not be empty."; assert lib.errors.trace (builtins.length list != 0) "lib.lists.init: list must not be empty.";
lib.lists.take (builtins.length list - 1) list; lib.lists.take (builtins.length list - 1) list;
## Reverse a list. ## Reverse a list.
## ##
@ -189,10 +191,12 @@ lib: {
list list
else else
builtins.tail ( builtins.tail (
builtins.concatMap (part: [ builtins.concatMap
separator (part: [
part separator
]) list part
])
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 = {
system: cross = lib.attrs.generate doubles (
lib.options.create { system:
description = "The cross-compiled package set for the ${system} target."; lib.options.create {
type = lib.types.packages; description = "The cross-compiled package set for the ${system} target.";
default = { }; type = lib.types.packages;
} 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,65 +52,37 @@ in
}; };
}; };
config.packages.cross = lib.attrs.generate doubles ( config = {
system: packages.cross = lib.attrs.generate doubles (
builtins.mapAttrs ( system:
namespace: builtins.mapAttrs
builtins.mapAttrs ( (
name: alias: namespace:
let builtins.mapAttrs (
setHost = name: alias:
package: let
package setHost =
// { package:
__modules__ = package.__modules__ ++ [ package
{ // {
config.platform = { __modules__ = package.__modules__ ++ [
host = lib.modules.override 5 system; {
target = lib.modules.override 5 system; config.platform = {
host = lib.modules.override 5 system;
target = lib.modules.override 5 system;
};
}
];
}; };
}
];
};
# if package != {}
# then
# (package.extend (
# {config}: {
# config = {
# platform = {
# host = lib.modules.overrides.force system;
# target = lib.modules.overrides.default system;
# };
# deps = { updated = alias // {
# build = { versions = builtins.mapAttrs (version: package: setHost package) alias.versions;
# only = setHost package.deps.build.only; };
# build = setHost package.deps.build.build; in
# host = setHost package.deps.build.host; updated
# target = setHost package.deps.build.target; )
# }; )
# host = { packages
# 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 // {
versions = builtins.mapAttrs (version: package: setHost package) alias.versions;
};
in
updated
)
) packages
);
} }