feat: working transient deps

This commit is contained in:
Jake Hamilton 2024-07-03 00:35:21 -07:00
parent 2be3111b2c
commit aa0fd6128b
Signed by: jakehamilton
GPG key ID: 9762169A1B35EA68
12 changed files with 1300 additions and 1309 deletions

View file

@ -3,10 +3,10 @@
"lib": { "lib": {
"locked": { "locked": {
"dir": "lib", "dir": "lib",
"dirtyRev": "a707b0f06be6b36bcbfe88d0a9a5b9a803983a06-dirty", "dirtyRev": "2be3111b2c0911f40b47fe0a1fb22b5f5188cf59-dirty",
"dirtyShortRev": "a707b0f-dirty", "dirtyShortRev": "2be3111-dirty",
"lastModified": 1719079124, "lastModified": 1719251485,
"narHash": "sha256-4HwA3q5f7SUBmcXX9Vz9WsA9oHBQ/GiZTwE4iSVq9s8=", "narHash": "sha256-63NvfFVeTDITfNu60rmCUlaZtAeZUnvrIaOLSk9ScC8=",
"type": "git", "type": "git",
"url": "file:../?dir=lib" "url": "file:../?dir=lib"
}, },

View file

@ -4,9 +4,7 @@ lib: {
## Merge a list of option definitions into a single value. ## Merge a list of option definitions into a single value.
## ##
## @type Location -> List Definition -> Any ## @type Location -> List Definition -> Any
default = default = location: definitions: let
location: definitions:
let
values = lib.options.getDefinitionValues definitions; values = lib.options.getDefinitionValues definitions;
first = builtins.elemAt values 0; first = builtins.elemAt values 0;
mergedFunctions = x: lib.options.mergeDefault location (builtins.map (f: f x) values); mergedFunctions = x: lib.options.mergeDefault location (builtins.map (f: f x) values);
@ -15,35 +13,30 @@ lib: {
mergedBools = builtins.any lib.bools.or false values; mergedBools = builtins.any lib.bools.or false values;
mergedStrings = lib.strings.concat values; mergedStrings = lib.strings.concat values;
in in
if builtins.length values == 1 then if builtins.length values == 1
builtins.elemAt values 0 then builtins.elemAt values 0
else if builtins.all builtins.isFunction values then else if builtins.all builtins.isFunction values
mergedFunctions then mergedFunctions
else if builtins.all builtins.isList values then else if builtins.all builtins.isList values
mergedLists then mergedLists
else if builtins.all builtins.isAttrs values then else if builtins.all builtins.isAttrs values
mergedAttrs then mergedAttrs
else if builtins.all builtins.isBool values then else if builtins.all builtins.isBool values
mergedBools then mergedBools
else if builtins.all lib.strings.isString values then else if builtins.all lib.strings.isString values
mergedStrings then mergedStrings
else if builtins.all builtins.isInt values && builtins.all (x: x == first) values then else if builtins.all builtins.isInt values && builtins.all (x: x == first) values
first then first
# TODO: Improve this error message to show the location and definitions for the option. # TODO: Improve this error message to show the location and definitions for the option.
else else builtins.throw "Cannot merge definitions.";
builtins.throw "Cannot merge definitions.";
## Merge multiple option definitions together. ## Merge multiple option definitions together.
## ##
## @type Location -> Type -> List Definition ## @type Location -> Type -> List Definition
definitions = definitions = location: type: definitions: let
location: type: definitions:
let
identifier = lib.options.getIdentifier location; identifier = lib.options.getIdentifier location;
resolve = resolve = definition: let
definition: properties = builtins.addErrorContext "while evaluating definitions from `${definition.__file__ or "<unknown>"}`:" (
let
properties = builtins.addErrorContext "while evaluating definitions from `${definition.__file__}`:" (
lib.modules.apply.properties definition.value lib.modules.apply.properties definition.value
); );
normalize = value: { normalize = value: {
@ -57,27 +50,27 @@ lib: {
overridden = lib.modules.apply.overrides resolved; overridden = lib.modules.apply.overrides resolved;
values = values =
if builtins.any (definition: lib.types.is "order" definition.value) overridden.values then if builtins.any (definition: lib.types.is "order" definition.value) overridden.values
lib.modules.apply.order overridden.values then lib.modules.apply.order overridden.values
else else overridden.values;
overridden.values;
isDefined = values != []; isDefined = values != [];
invalid = builtins.filter (definition: !(type.check definition.value)) values; invalid = builtins.filter (definition: !(type.check definition.value)) values;
merged = merged =
if isDefined then if isDefined
if builtins.all (definition: type.check definition.value) values then then
type.merge location values if builtins.all (definition: type.check definition.value) values
else then type.merge location values
builtins.throw "A definition for `${identifier}` is not of type `${type.description}`. Definition values:${lib.options.getDefinitions invalid}" else builtins.throw "A definition for `${identifier}` is not of type `${type.description}`. Definition values:${lib.options.getDefinitions invalid}"
else else builtins.throw "The option `${identifier}` is used but not defined.";
builtins.throw "The option `${identifier}` is used but not defined.";
optional = if isDefined then { value = merged; } else { }; optional =
in if isDefined
{ then {value = merged;}
else {};
in {
inherit inherit
isDefined isDefined
values values
@ -94,12 +87,8 @@ lib: {
## Merge multiple option declarations together. ## Merge multiple option declarations together.
## ##
## @type Location -> List Option ## @type Location -> List Option
declarations = declarations = location: options: let
location: options: merge = result: option: let
let
merge =
result: option:
let
mergedType = result.type.mergeType option.options.type.functor; mergedType = result.type.mergeType option.options.type.functor;
isTypeMergeable = mergedType != null; isTypeMergeable = mergedType != null;
shared = name: option.options ? ${name} && result ? ${name}; shared = name: option.options ? ${name} && result ? ${name};
@ -108,14 +97,15 @@ lib: {
serializedFiles = builtins.concatStringsSep " and " files; serializedFiles = builtins.concatStringsSep " and " files;
getSubModules = option.options.type.getSubModules or null; getSubModules = option.options.type.getSubModules or null;
submodules = submodules =
if getSubModules != null then if getSubModules != null
then
builtins.map (module: { builtins.map (module: {
__file__ = option.__file__; __file__ = option.__file__;
includes = [module]; includes = [module];
}) getSubModules })
getSubModules
++ result.options ++ result.options
else else result.options;
result.options;
in in
if if
shared "default" shared "default"
@ -123,8 +113,7 @@ lib: {
|| shared "description" || shared "description"
|| shared "apply" || shared "apply"
|| (shared "type" && !isTypeMergeable) || (shared "type" && !isTypeMergeable)
then then builtins.throw "The option `${lib.options.getIdentifier location}` in `${option.__file__}` is already declared in ${serializedFiles}"
builtins.throw "The option `${lib.options.getIdentifier location}` in `${option.__file__}` is already declared in ${serializedFiles}"
else else
option.options option.options
// result // result
@ -138,50 +127,43 @@ lib: {
inherit location; inherit location;
declarations = []; declarations = [];
options = []; options = [];
} options; }
options;
## Merge an option, only supporting a single unique definition. ## Merge an option, only supporting a single unique definition.
## ##
## @type String -> Location -> List Definition -> Any ## @type String -> Location -> List Definition -> Any
unique = unique = message: location: definitions: let
message: location: definitions:
let
identifier = lib.options.getIdentifier location; identifier = lib.options.getIdentifier location;
total = builtins.length definitions; total = builtins.length definitions;
first = builtins.elemAt definitions 0; first = builtins.elemAt definitions 0;
in in
if total == 1 then if total == 1
first.value then first.value
else if total == 0 then else if total == 0
builtins.throw "Cannot merge unused option `${identifier}`.\n${message}" then builtins.throw "Cannot merge unused option `${identifier}`.\n${message}"
else else builtins.throw "The option `${identifier}` is defined multiple times, but must be unique.\n${message}\nDefinitions:${lib.options.getDefinitions definitions}";
builtins.throw "The option `${identifier}` is defined multiple times, but must be unique.\n${message}\nDefinitions:${lib.options.getDefinitions definitions}";
## Merge a single instance of an option. ## Merge a single instance of an option.
## ##
## @type Location -> List Definition -> Any ## @type Location -> List Definition -> Any
one = lib.options.merge.unique ""; one = lib.options.merge.unique "";
equal = equal = location: definitions: let
location: definitions:
let
identifier = lib.options.getIdentifier location; identifier = lib.options.getIdentifier location;
first = builtins.elemAt definitions 0; first = builtins.elemAt definitions 0;
rest = builtins.tail definitions; rest = builtins.tail definitions;
merge = merge = x: y:
x: y: if x != y
if x != y then then builtins.throw "The option `${identifier}` has conflicting definitions:${lib.options.getDefinitions definitions}"
builtins.throw "The option `${identifier}` has conflicting definitions:${lib.options.getDefinitions definitions}" else x;
else
x;
merged = builtins.foldl' merge first rest; merged = builtins.foldl' merge first rest;
in in
if builtins.length definitions == 0 then if builtins.length definitions == 0
builtins.throw "Cannot merge unused option `${identifier}`." then builtins.throw "Cannot merge unused option `${identifier}`."
else if builtins.length definitions == 1 then else if builtins.length definitions == 1
first.value then first.value
else else merged.value;
merged.value;
}; };
## Check whether a value is an option. ## Check whether a value is an option.
@ -192,8 +174,7 @@ lib: {
## Create an option. ## Create an option.
## ##
## @type { type? :: String | Null, apply? :: (a -> b) | Null, default? :: { value :: a, text :: String }, example? :: String | Null, visible? :: Bool | Null, internal? :: Bool | Null, writable? :: Bool | Null, description? :: String | Null } -> Option a ## @type { type? :: String | Null, apply? :: (a -> b) | Null, default? :: { value :: a, text :: String }, example? :: String | Null, visible? :: Bool | Null, internal? :: Bool | Null, writable? :: Bool | Null, description? :: String | Null } -> Option a
create = create = settings @ {
settings@{
type ? lib.types.unspecified, type ? lib.types.unspecified,
apply ? null, apply ? null,
default ? {}, default ? {},
@ -202,8 +183,7 @@ lib: {
internal ? null, internal ? null,
writable ? null, writable ? null,
description ? null, description ? null,
}: }: {
{
__type__ = "option"; __type__ = "option";
inherit inherit
type type
@ -220,9 +200,7 @@ lib: {
## Create a sink option. ## Create a sink option.
## ##
## @type @alias lib.options.create ## @type @alias lib.options.create
sink = sink = settings: let
settings:
let
defaults = { defaults = {
internal = true; internal = true;
visible = false; visible = false;
@ -246,9 +224,7 @@ lib: {
## Convert a list of option identifiers into a single identifier. ## Convert a list of option identifiers into a single identifier.
## ##
## @type List String -> String ## @type List String -> String
getIdentifier = getIdentifier = location: let
location:
let
special = [ special = [
# lib.types.attrs.of (lib.types.submodule {}) # lib.types.attrs.of (lib.types.submodule {})
"<name>" "<name>"
@ -257,23 +233,24 @@ lib: {
# lib.types.function # lib.types.function
"<function body>" "<function body>"
]; ];
escape = part: if builtins.elem part special then part else lib.strings.escape.nix.identifier part; escape = part:
if builtins.elem part special
then part
else lib.strings.escape.nix.identifier part;
in in
lib.strings.concatMapSep "." escape location; lib.strings.concatMapSep "." escape location;
## Get a string message of the definitions for an option. ## Get a string message of the definitions for an option.
## ##
## @type List Definition -> String ## @type List Definition -> String
getDefinitions = getDefinitions = definitions: let
definitions: serialize = definition: let
let valueWithRecursionLimit =
serialize = lib.generators.withRecursion {
definition:
let
valueWithRecursionLimit = lib.generators.withRecursion {
limit = 10; limit = 10;
throw = false; throw = false;
} definition.value; }
definition.value;
eval = builtins.tryEval (lib.generators.pretty {} valueWithRecursionLimit); eval = builtins.tryEval (lib.generators.pretty {} valueWithRecursionLimit);
@ -286,27 +263,24 @@ lib: {
value = builtins.concatStringsSep "\n " (firstFiveLines ++ ellipsis); value = builtins.concatStringsSep "\n " (firstFiveLines ++ ellipsis);
result = result =
if !eval.success then if !eval.success
"" then ""
else if linesLength > 1 then else if linesLength > 1
":\n " + value then ":\n " + value
else else ": " + value;
": " + value; in "\n- In `${definition.__file__}`${result}";
in
"\n- In `${definition.__file__}`${result}";
in in
lib.strings.concatMap serialize definitions; lib.strings.concatMap serialize definitions;
## Run a set of definitions, calculating the resolved value and associated information. ## Run a set of definitions, calculating the resolved value and associated information.
## ##
## @type Location -> Option -> List Definition -> String & { value :: Any, highestPriority :: Int, isDefined :: Bool, files :: List String, definitions :: List Any, definitionsWithLocations :: List Definition } ## @type Location -> Option -> List Definition -> String & { value :: Any, highestPriority :: Int, isDefined :: Bool, files :: List String, definitions :: List Any, definitionsWithLocations :: List Definition }
run = run = location: option: definitions: let
location: option: definitions:
let
identifier = lib.options.getIdentifier location; identifier = lib.options.getIdentifier location;
definitionsWithDefault = definitionsWithDefault =
if option ? default && option.default ? value then if option ? default && option.default ? value
then
[ [
{ {
__file__ = builtins.head option.declarations; __file__ = builtins.head option.declarations;
@ -314,25 +288,28 @@ lib: {
} }
] ]
++ definitions ++ definitions
else else definitions;
definitions;
merged = merged =
if option.writable or null == false && builtins.length definitionsWithDefault > 1 then if option.writable or null == false && builtins.length definitionsWithDefault > 1
let then let
separatedDefinitions = builtins.map ( separatedDefinitions =
builtins.map (
definition: definition:
definition definition
// { // {
value = (lib.options.merge.definitions location option.type [definition]).merged; value = (lib.options.merge.definitions location option.type [definition]).merged;
} }
) definitionsWithDefault; )
definitionsWithDefault;
in in
builtins.throw "The option `${identifier}` is not writable, but is set more than once:${lib.options.getDefinitions separatedDefinitions}" builtins.throw "The option `${identifier}` is not writable, but is set more than once:${lib.options.getDefinitions separatedDefinitions}"
else else lib.options.merge.definitions location option.type definitionsWithDefault;
lib.options.merge.definitions location option.type definitionsWithDefault;
value = if option.apply or null != null then option.apply merged.merged else merged.merged; value =
if option.apply or null != null
then option.apply merged.merged
else merged.merged;
in in
option option
// { // {

View file

@ -1048,7 +1048,7 @@ lib: {
merge = location: definitions: let merge = location: definitions: let
process = value: process = value:
if initial.check value if initial.check value
then (builtins.trace "transforming...") transform value then transform value
else value; else value;
normalize = definition: definition // {value = process definition.value;}; normalize = definition: definition // {value = process definition.value;};
normalized = builtins.map normalize definitions; normalized = builtins.map normalize definitions;
@ -1068,6 +1068,43 @@ lib: {
}; };
}; };
## Create a type that allows a value which is either the final type or is transformable
## to the final type.
##
## @type Attrs -> (Any -> Any) -> Attrs -> Attrs
coerceWithLocation = initial: transform: final: let
in
if initial.getSubModules != null
then builtins.throw "lib.types.coerceWithLocation's first argument may not have submodules, but got ${initial.description}"
else
lib.types.create {
name = "Coerce";
description = "${initial.description} that is transformed to ${final.description}";
fallback = final.fallback;
check = value: final.check value || (initial.check value && final.check (transform [] value));
merge = location: definitions: let
process = value:
if initial.check value
then transform location value
else value;
normalize = definition: definition // {value = process definition.value;};
normalized = builtins.map normalize definitions;
in
final.merge location normalized;
getSubOptions = final.getSubOptions;
getSubModules = final.getSubModules;
withSubModules = modules: lib.types.coerceWithLocation initial transform (final.withSubModules modules);
mergeType = x: y: null;
functor =
lib.types.functor "coerceWithLocation"
// {
wrapped = final;
};
children = {
inherit initial final;
};
};
dag = { dag = {
## Create a type that allows a DAG (Directed Acyclic Graph) of a given type. ## Create a type that allows a DAG (Directed Acyclic Graph) of a given type.
## ##

View file

@ -8,10 +8,10 @@
}, },
"locked": { "locked": {
"dir": "foundation", "dir": "foundation",
"dirtyRev": "a707b0f06be6b36bcbfe88d0a9a5b9a803983a06-dirty", "dirtyRev": "2be3111b2c0911f40b47fe0a1fb22b5f5188cf59-dirty",
"dirtyShortRev": "a707b0f-dirty", "dirtyShortRev": "2be3111-dirty",
"lastModified": 1719079124, "lastModified": 1719251485,
"narHash": "sha256-hz9vVcHSvlq/W01UOh/GqPFUoH9DzCFB16n23oj7fnQ=", "narHash": "sha256-9G1TPBdlQNXCZf6A66bCT9m2vhodkSF+rDtqOVuFteY=",
"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": "a707b0f06be6b36bcbfe88d0a9a5b9a803983a06-dirty", "dirtyRev": "2be3111b2c0911f40b47fe0a1fb22b5f5188cf59-dirty",
"dirtyShortRev": "a707b0f-dirty", "dirtyShortRev": "2be3111-dirty",
"lastModified": 1719079124, "lastModified": 1719251485,
"narHash": "sha256-hz9vVcHSvlq/W01UOh/GqPFUoH9DzCFB16n23oj7fnQ=", "narHash": "sha256-9G1TPBdlQNXCZf6A66bCT9m2vhodkSF+rDtqOVuFteY=",
"type": "git", "type": "git",
"url": "file:../?dir=lib" "url": "file:../?dir=lib"
}, },

View file

@ -83,7 +83,13 @@ in {
} }
); );
in in
built // {inherit (package) meta;}; built
// {
inherit (package) meta;
extras = {
inherit package;
};
};
}; };
}; };
} }

View file

@ -1,16 +1,13 @@
# This file handles creating all of the exports for this project and is not # This file handles creating all of the exports for this project and is not
# exported itself. # exported itself.
{ {config}: let
lib, inherit (config) lib;
config,
}: let
lib' = config.lib;
in { in {
freeform = lib.types.any; # freeform = lib.types.any;
config = { config = {
exports = { exports = {
lib = config.lib; inherit lib;
modules = import ./modules.nix; modules = import ./modules.nix;
packages = { packages = {
@ -25,7 +22,7 @@ in {
# }; # };
# })) # }))
# .config; # .config;
# foundation-gcc = config.packages.foundation.gcc; foundation-gcc = config.packages.foundation.gcc;
# foundation-binutils = config.packages.foundation.binutils; # foundation-binutils = config.packages.foundation.binutils;
# foundation-linux-headers = config.packages.foundation.linux-headers.versions.latest.extend { # foundation-linux-headers = config.packages.foundation.linux-headers.versions.latest.extend {
# platform.host = lib.modules.overrides.force "x86_64-linux"; # platform.host = lib.modules.overrides.force "x86_64-linux";

View file

@ -1,8 +1,9 @@
{ {
lib, lib,
lib',
config, config,
}: { }: let
lib' = config.lib;
in {
config = { config = {
lib.packages = { lib.packages = {
dependencies = { dependencies = {
@ -20,52 +21,85 @@
in in
builtins.head sorted; builtins.head sorted;
resolve = package: resolve = alias:
if package ? versions if alias ? versions
then then
package.versions.${config.preferences.packages.version} alias.versions.${config.preferences.packages.version}
or (package.versions.${lib'.packages.getLatest package}) or (alias.versions.${lib'.packages.getLatest alias})
else package; else alias;
build = package: build: host: target: let build = alias: build: host: target: let
resolved = lib'.packages.resolve package; package = lib'.packages.resolve alias;
buildDependencies = build': host': target': buildDependencies = 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');
result = resolved.extend ( result = lib.modules.run {
modules =
package.__modules__
++ [
lib'.types.package.children.submodule
(
{config}: { {config}: {
config = { config = {
__modules__ = package.__modules__;
platform = { platform = {
build = build; build = lib.modules.overrides.force (lib'.systems.withBuildInfo build);
host = host; host = lib.modules.overrides.force (lib'.systems.withBuildInfo host);
target = lib.modules.override 150 target; target = lib.modules.overrides.force (lib'.systems.withBuildInfo target);
}; };
# NOTE: This does not seem to work and instead the pre-existing deps are used.
# This causes an issue because the platforms will be wrong.
# deps = {
# build = {
# only = buildDependencies build build build package.deps.build.only;
# build = buildDependencies build build target package.deps.build.build;
# host = buildDependencies build host target package.deps.build.host;
# target = buildDependencies build target target package.deps.build.target;
# };
# host = {
# only = buildDependencies host host host package.deps.host.only;
# host = buildDependencies host host target package.deps.host.host;
# target = buildDependencies host target target package.deps.host.target;
# };
# target = {
# only = buildDependencies target target target package.deps.target.only;
# target = buildDependencies target target target package.deps.target.target;
# };
# };
};
}
)
];
};
resolved =
result.config
// {
deps = { deps = {
build = { build = {
only = buildDependencies build build build resolved.deps.build.only; only = buildDependencies build build build package.deps.build.only;
build = buildDependencies build build target resolved.deps.build.build; build = buildDependencies build build target package.deps.build.build;
host = buildDependencies build host target resolved.deps.build.host; host = buildDependencies build host target package.deps.build.host;
target = buildDependencies build target target resolved.deps.build.target; target = buildDependencies build target target package.deps.build.target;
}; };
host = { host = {
only = buildDependencies host host host resolved.deps.host.only; only = buildDependencies host host host package.deps.host.only;
host = buildDependencies host host target resolved.deps.host.host; host = buildDependencies host host target package.deps.host.host;
target = buildDependencies host target target resolved.deps.host.target; target = buildDependencies host target target package.deps.host.target;
}; };
target = { target = {
only = buildDependencies target target target resolved.deps.target.only; only = buildDependencies target target target package.deps.target.only;
target = buildDependencies target target target resolved.deps.target.target; target = buildDependencies target target target package.deps.target.target;
}; };
}; };
package = config.builder.build config;
}; };
}
);
in in
result; resolved
// {
package = resolved.builder.build resolved;
};
}; };
}; };
} }

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,11 @@
{ {
lib, lib,
lib',
config, config,
}: { }: let
inherit (config) preferences builders;
lib' = config.lib;
in {
config = { config = {
lib.types = { lib.types = {
license = let license = let
@ -52,6 +55,12 @@
in in
lib.types.either type (lib.types.list.of type); lib.types.either type (lib.types.list.of type);
platform =
lib.types.coerce
lib.types.string
lib'.systems.withBuildInfo
lib'.systems.types.platformWithBuildInfo;
builder = lib.types.submodule { builder = lib.types.submodule {
freeform = lib.types.any; freeform = lib.types.any;
@ -63,194 +72,89 @@
}; };
}; };
packages = lib.types.attrs.of (lib'.types.alias); packages = lib.types.attrs.of (lib.types.attrs.of lib'.types.alias);
alias = lib.types.attrs.of ( dependencies = let
lib.types.submodule {
options = {
versions = lib.options.create {
description = "All available package versions.";
type = lib.types.attrs.of lib'.types.package.base;
};
};
}
);
dependencies = lib.types.attrs.of (
lib.types.nullish (lib'.types.package.resolved)
);
package = {
resolved = let
initial = initial =
lib.types.raw (lib.types.attrs.lazy lib.types.any)
// { // {
check = value: check = value:
!(value ? __merged__) lib.types.attrs.any.check value
&& lib.types.raw.check && value ? versions;
value;
}; };
transform = value: let
package = lib'.packages.resolve value;
packageSubmodule = package.extend (args: {
options.__export__ = lib.options.create {
type = lib.types.raw;
default.value = {
inherit (args) options config;
};
};
config.__export__ = {
inherit (args) options config;
};
});
type = lib'.types.package.base.withSubModules (lib'.types.package.base.getSubModules
++ [
(args: {
options.__export__ = lib.options.create {
type = lib.types.raw;
default.value = {
inherit (args) options config;
};
};
config.__export__ = {
inherit (args) options config;
};
})
]);
typeSubmodule = lib.modules.run {
modules = type.getSubModules;
args = type.functor.payload.args;
};
getOptions = export: let
process = path: option:
if builtins.isAttrs option
then
if lib.types.is "option" option
then [
{
inherit path;
option = option;
}
]
else
builtins.concatLists (
lib.attrs.mapToList
(name: value: process (path ++ [name]) value)
option
)
else [];
in in
process [] export.options; lib.types.attrs.of (lib.types.coerce initial lib'.packages.resolve lib'.types.package);
packageOptions = getOptions packageSubmodule.__export__; alias = lib.types.submodule {
typeOptions = getOptions typeSubmodule.config.__export__;
customOptions =
builtins.filter
(
packageOption:
builtins.all (typeOption: packageOption.path != typeOption.path) typeOptions
)
packageOptions;
packageOptionsIds = builtins.map (option: builtins.concatStringsSep "." option.path) packageOptions;
typeOptionsIds = builtins.map (option: builtins.concatStringsSep "." option.path) typeOptions;
customOptionsIds = builtins.map (option: builtins.concatStringsSep "." option.path) customOptions;
resolvedOptions =
builtins.foldl' (
resolved: option: let
first = builtins.head option.path;
generated = lib.attrs.set option.path option.option;
in
if first == "__export__" || first == "__module__" || first == "__merged__"
then resolved
else lib.attrs.mergeRecursive resolved generated
) {}
customOptions;
in
# (builtins.trace (builtins.deepSeq packageOptionsIds packageOptionsIds))
# (builtins.trace (builtins.deepSeq typeOptionsIds typeOptionsIds))
# (builtins.trace (builtins.deepSeq customOptionsIds customOptionsIds))
# (builtins.trace packageSubmodule.__export__.config)
(args: {
__file__ = packageSubmodule.__export__.config.__file__ or "virtual:tidepool/src/lib/types.nix";
options =
resolvedOptions
// {
__merged__ = lib.options.create {
type = lib.types.bool;
default.value = true;
};
};
config =
builtins.removeAttrs package ["extend" "package" "platform"]
// {
platform = {
build = lib.modules.overrides.default package.platform.build.triple;
host = lib.modules.overrides.default package.platform.host.triple;
target = lib.modules.overrides.default package.platform.target.triple;
};
};
# // {
# builder =
# (builtins.trace "builder")
# (builtins.trace (args.config ? builder))
# lib.modules.alias
# packageSubmodule.__export__.options.builder;
# };
});
in
lib.types.coerce initial transform lib'.types.package.base;
base = lib.types.submodule (
{
config,
meta,
}: {
options = { options = {
extend = lib.options.create { stable = lib.options.create {
description = "Extend the package's submodules with additional configuration."; description = "The stable version of the package.";
type = lib.types.function lib.types.raw; type = lib.types.nullish lib'.types.package;
default.value = value: let };
result = meta.extend {
latest = lib.options.create {
description = "The latest version of the package.";
type = lib'.types.package;
};
versions = lib.options.create {
description = "Available versions of the package.";
type = lib.types.attrs.of lib'.types.package;
default.value = {};
};
};
};
package = let
normalize = value:
if builtins.isFunction value || builtins.isList value
then value
else if value ? __modules__
then value.__modules__
else {
config = value;
};
initial = lib.types.create {
name = "PackageConfig";
description = "configuration for a package";
check = value: builtins.isFunction value || builtins.isAttrs value || builtins.isList value;
merge = location: definitions: let
normalized =
builtins.map
(definition: lib.lists.from.any (normalize definition.value))
definitions;
in
builtins.concatLists
normalized;
};
transform = location: value: let
modules = modules =
if builtins.isAttrs value lib.lists.from.any (normalize value);
then [{config = value;}]
else lib.lists.from.any value; result = lib.modules.run {
prefix = location;
modules =
modules
++ [submodule {config.__modules__ = modules;}];
}; };
in in
result.config; result.config;
};
name = lib.options.create { final = lib.types.attrs.any;
description = "The name of the package.";
type = lib.types.string;
default = {
text = "\${config.pname}-\${config.version}";
value =
if config.pname != null && config.version != null
then "${config.pname}-${config.version}"
else "";
};
};
pname = lib.options.create { submodule = {config}: {
description = "The program name for the package"; options = {
type = lib.types.nullish lib.types.string; __modules__ = lib.options.create {
default.value = null; description = "User specified modules for the package definition.";
}; type = lib.types.list.of (initial
// {
version = lib.options.create { merge = lib.options.merge.one;
description = "The version for the package."; });
type = lib.types.nullish lib.types.version; # writable = false;
default.value = null; internal = true;
default.value = [];
}; };
meta = { meta = {
@ -306,57 +210,55 @@
platform = { platform = {
build = lib.options.create { build = lib.options.create {
description = "The build platform for the package."; description = "The build platform for the package.";
type = lib.types.string; type = lib'.types.platform;
default.value = "x86_64-linux"; default.value = lib'.systems.withBuildInfo "x86_64-linux";
apply = raw: let
system = lib'.systems.from.string raw;
x = lib'.systems.withBuildInfo raw;
in
x;
}; };
host = lib.options.create { host = lib.options.create {
description = "The host platform for the package."; description = "The host platform for the package.";
type = lib.types.string; type = lib'.types.platform;
default.value = "x86_64-linux"; default.value = lib'.systems.withBuildInfo "x86_64-linux";
# apply = raw: let
# system = lib'.systems.from.string raw;
# in {
# inherit raw system;
# double = lib'.systems.into.double system;
# triple = lib'.systems.into.triple system;
# };
apply = raw: let
system = lib'.systems.from.string raw;
x = lib'.systems.withBuildInfo raw;
in
x;
}; };
target = lib.options.create { target = lib.options.create {
description = "The target platform for the package."; description = "The target platform for the package.";
type = lib.types.string; type = lib'.types.platform;
default.value = "x86_64-linux"; default.value = lib'.systems.withBuildInfo "x86_64-linux";
# apply = raw: let
# system = lib'.systems.from.string raw;
# in {
# inherit raw system;
# double = lib'.systems.into.double system;
# triple = lib'.systems.into.triple system;
# };
apply = raw: let
system = lib'.systems.from.string raw;
x = lib'.systems.withBuildInfo raw;
in
x;
}; };
}; };
name = lib.options.create {
description = "The name of the package.";
type = lib.types.string;
default = {
text = "\${config.pname}-\${config.version}";
value =
if config.pname != null && config.version != null
then "${config.pname}-${config.version}"
else "";
};
};
pname = lib.options.create {
description = "The program name for the package";
type = lib.types.nullish lib.types.string;
default.value = null;
};
version = lib.options.create {
description = "The version for the package.";
type = lib.types.nullish lib.types.version;
default.value = null;
};
builder = lib.options.create {
description = "The builder for the package.";
type = lib'.types.builder;
};
phases = lib.options.create { phases = lib.options.create {
description = "The phases for the package."; description = "The phases for the package.";
type = lib.types.dag.of (lib.types.either lib.types.string (lib.types.function lib.types.string)); type = lib.types.dag.of lib.types.string;
default.value = {}; default.value = {};
}; };
@ -366,9 +268,10 @@
default.value = {}; default.value = {};
}; };
builder = lib.options.create { package = lib.options.create {
description = "The builder for the package."; description = "The built derivation.";
type = lib'.types.builder; type = lib.types.derivation;
default.value = config.builder.build config;
}; };
deps = { deps = {
@ -432,15 +335,23 @@
}; };
}; };
}; };
};
};
package = lib.options.create { type =
description = "The built derivation."; (lib.types.coerceWithLocation initial transform final)
type = lib.types.derivation; // {
default.value = config.builder.build config; name = "Package";
description = "a package definition";
}; };
in
type
// {
children =
type.children
// {
inherit submodule;
}; };
}
);
}; };
}; };
}; };

View file

@ -12,6 +12,8 @@ in {
platforms = ["i686-linux"]; platforms = ["i686-linux"];
}; };
name = "${config.pname}-${config.version}";
pname = "a"; pname = "a";
version = "1.0.0"; version = "1.0.0";

View file

@ -1,15 +1,12 @@
{ {config}: let
lib', inherit (config) lib builders packages;
config,
}: let
inherit (config) builders packages;
in { in {
config.packages.aux.b = { config.packages.aux.b = {
versions = { versions = {
"latest" = {config}: { "latest" = {config}: {
options = { options = {
custom = lib'.options.create { custom = lib.options.create {
type = lib'.types.bool; type = lib.types.bool;
}; };
}; };
@ -18,6 +15,8 @@ in {
platforms = ["i686-linux"]; platforms = ["i686-linux"];
}; };
name = "${config.pname}-${config.version}";
custom = true; custom = true;
pname = "b"; pname = "b";

View file

@ -1,9 +1,7 @@
{ {config}: let
lib, inherit (config) lib;
lib',
config, doubles = lib.systems.doubles.all;
}: let
doubles = lib'.systems.doubles.all;
packages = builtins.removeAttrs config.packages ["cross"]; packages = builtins.removeAttrs config.packages ["cross"];
in { in {
@ -17,13 +15,13 @@ in {
packages = lib.options.create { packages = lib.options.create {
description = "The package set."; description = "The package set.";
type = lib.types.submodule { type = lib.types.submodule {
freeform = lib.types.attrs.of (lib.types.submodule {freeform = lib'.types.alias;}); 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 = {};
} }
); );
@ -49,38 +47,39 @@ in {
builtins.mapAttrs ( builtins.mapAttrs (
name: alias: let name: alias: let
setHost = package: setHost = package:
if package != {} package;
then # if package != {}
(package.extend ( # then
{config}: { # (package.extend (
config = { # {config}: {
platform = { # config = {
host = lib.modules.overrides.force system; # platform = {
target = lib.modules.overrides.default system; # host = lib.modules.overrides.force system;
}; # target = lib.modules.overrides.default system;
# };
deps = { # deps = {
build = { # build = {
only = setHost package.deps.build.only; # only = setHost package.deps.build.only;
build = setHost package.deps.build.build; # build = setHost package.deps.build.build;
host = setHost package.deps.build.host; # host = setHost package.deps.build.host;
target = setHost package.deps.build.target; # target = setHost package.deps.build.target;
}; # };
host = { # host = {
only = setHost package.deps.host.only; # only = setHost package.deps.host.only;
host = setHost package.deps.host.host; # host = setHost package.deps.host.host;
target = setHost package.deps.host.target; # target = setHost package.deps.host.target;
}; # };
target = { # target = {
only = setHost package.deps.target.only; # only = setHost package.deps.target.only;
target = setHost package.deps.target.target; # target = setHost package.deps.target.target;
}; # };
}; # };
}; # };
} # }
)) # ))
.config # .config
else package; # else package;
updated = updated =
alias alias