labs/tidepool/src/lib/types.nix
Ross Smyth be0d6bc0e1 refactor: remove target (#31)
WIP. Posting just to make sure people know this is something I'd like to do.

Context: https://matrix.to/#/!PvnunzXUk4Vm1CmKS4:auxolotl.org/$bOm6iaBpWbo-KRJgo7FUZdeFIv9StzRHxklbx7OQ23g?via=auxolotl.org&via=matrix.org&via=catgirl.cloud

Reasons:

1. Why should GCC inflict complication upon all of tidepool just because of its own architectural deficiencies?
2. Most other compilers are multi-target so it doesn't matter
2. Target is useless for 99.9% of packages and only leads to user confusion, especially since many compilers also call what would be host here "target".
3. Canadian-cross is not very well supported. GCC barely supports it, and is broken in many cases. Nixpkgs does not support it (regardless of what its API may imply) and no one has ever complained.

Plan:

1. Remove most target related things. No reason to have it.
2. Add configuration parameter on GCC that selects its target

What does this mean:

This basically moves target to be a GCC-specific artifact rather than an official parameter every package must endure the cognitive load of. This is because it is.  Nixpkgs plans on dumping target in the future as well because of this. Or at least making it an alias.

If you want to create a compiler with a different target, then override GCC to do so. No need to pervade all of tidepool just for GCC.

Co-authored-by: Victor Fuentes <vlinkz@snowflakeos.org>
Reviewed-on: #31
Reviewed-by: vlinkz <vlinkz@snowflakeos.org>
Co-authored-by: Ross Smyth <snix@treefroog.com>
Co-committed-by: Ross Smyth <snix@treefroog.com>
2025-10-07 03:46:52 +00:00

834 lines
28 KiB
Nix

{ config }@global:
let
inherit (config) lib;
pretty = lib.generators.pretty { };
in
{
config = {
lib.types = {
license =
let
type = lib.types.submodule (
{ config }:
{
options = {
name = lib.options.create {
description = "The short name of the license.";
type = lib.types.string;
};
fullName = lib.options.create {
description = "The full name of the license.";
type = lib.types.string;
};
spdxId = lib.options.create {
description = "The SPDX identifier for the license.";
type = lib.types.nullish lib.types.string;
default.value = null;
};
url = lib.options.create {
description = "The URL for the license.";
type = lib.types.string;
default = {
text = "spdx.org entry, if `spdxId` is set, `null` otherwise";
value = if config.spdxId == null then null else "https://spdx.org/licenses/${config.spdxId}.html";
};
};
free = lib.options.create {
description = "Whether the license is free.";
type = lib.types.bool;
default.value = true;
};
redistributable = lib.options.create {
description = "Whether the license is allows redistribution.";
type = lib.types.bool;
default = {
text = "config.free";
value = config.free;
};
};
};
}
);
in
lib.types.either type (lib.types.list.of type);
context = lib.types.submodule {
options = global.config.packages.context.options;
};
settings = lib.types.submodule {
options = global.config.packages.settings.options;
};
platform =
lib.types.coerce lib.types.string lib.systems.withBuildInfo
lib.systems.types.platformWithBuildInfo;
builder =
let
normalize =
value:
if builtins.isNull value then
{ }
else if builtins.isPath value then
{ includes = [ value ]; }
else if builtins.isFunction value || builtins.isList value then
value
else if value ? __modules__ then
value.__modules__
else
{ config = value; };
initial = lib.types.create {
name = "BuilderConfig";
description = "configuration for a builder";
check =
value:
builtins.isFunction value
|| builtins.isAttrs value
|| builtins.isList value
|| builtins.isNull value
|| builtins.isPath 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 = lib.lists.from.any (normalize value);
result = lib.modules.run {
prefix = location;
args = {
global = global.config;
};
modules = modules ++ [
submodule
{ config.__modules__ = modules; }
];
};
in
result.config;
final = lib.types.raw // {
merge =
location: definitions:
let
modules = builtins.concatMap (
definition: lib.lists.from.any (normalize definition.value)
) definitions;
result = lib.modules.run {
prefix = location;
args = {
global = global.config;
};
modules = modules ++ [
submodule
{ config.__modules__ = modules; }
];
};
in
result.config;
};
submodule =
{
config,
meta,
name ? "builder",
}:
{
options = {
__modules__ = lib.options.create {
description = "User specified modules for the builder definition.";
type = lib.types.list.of (initial // { merge = lib.options.merge.one; });
internal = true;
default.value = [ ];
};
extend = lib.options.create {
description = "Extend the package definition.";
type = lib.types.function lib.types.raw;
default.value =
module:
let
normalized =
if builtins.isList module then
module
else if builtins.isFunction module || module ? config then
[ module ]
else
[
{
config = module;
}
];
modules = config.__modules__ ++ normalized;
result = lib.modules.run {
prefix = meta.prefix;
args = {
global = global.config;
};
modules = [
submodule
{ config.__modules__ = modules; }
]
++ modules;
};
in
result.config;
};
name = lib.options.create {
description = "The name of the builder.";
type = lib.types.string;
default = lib.attrs.when (name != "builder") {
value = name;
};
writable = false;
};
build = lib.options.create {
description = "The build function which takes a package definition and creates a derivation.";
type = lib.types.function lib.types.artifact;
};
settings = lib.options.create {
description = "The settings for the builder.";
type = lib.types.submodule ({ config }: { });
default.value = { };
};
};
};
type = (lib.types.coerceWithLocation initial transform final) // {
name = "Builder";
description = "a builder";
};
in
type
// {
children = type.children // {
inherit submodule;
};
};
aliases =
let
normalize =
value:
if builtins.isNull value then
{ }
else if builtins.isPath value then
{ includes = [ value ]; }
else 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
|| builtins.isNull value
|| builtins.isPath value;
};
transform =
value:
let
result = builtins.mapAttrs (
namespace: aliases:
builtins.mapAttrs (
id: alias:
alias
// {
versions = builtins.mapAttrs (version: package: {
__modules__ = (lib.lists.from.any (normalize package)) ++ [
{
config = {
id = lib.modules.overrides.default id;
version = lib.modules.overrides.default version;
namespace = lib.modules.overrides.default namespace;
};
}
];
}) alias.versions;
}
) aliases
) value;
in
result;
final = lib.types.attrs.of (lib.types.attrs.of lib.types.alias);
in
lib.types.coerce initial transform final;
platforms = {
build = lib.types.attrs.of lib.types.platforms.host;
host = lib.types.attrs.of lib.types.platforms.host;
alias = lib.types.submodule {
options = {
stable = lib.options.create {
description = "The stable version of the artifact.";
type = lib.types.artifact;
default.value = null;
};
latest = lib.options.create {
description = "The latest version of the artifact.";
type = lib.types.artifact;
default.value = null;
};
# NOTE: When adding a version alias here, update lib.packages.WELL_KNOWN_ALIASES
versions = lib.options.create {
description = "Available versions of the artifact.";
type = lib.types.attrs.of lib.types.artifact;
default.value = { };
};
};
};
spec = lib.types.submodule (
{ config }:
{
options = {
build = lib.options.create {
description = "The build platform for the package.";
type = lib.types.string;
default.value = "@all";
};
host = lib.options.create {
description = "The host platform for the package.";
type = lib.types.string;
default.value = "@build";
};
};
}
);
};
artifact =
let
base = lib.types.derivation // {
name = "Artifact";
description = "package artifact";
};
in
lib.types.nullish (
lib.types.withCheck base (
value:
value ? package
&& builtins.isAttrs value.package
&& value ? meta
&& builtins.isAttrs value.meta
&& value ? extend
&& builtins.isFunction value.extend
&& value ? extras
&& builtins.isAttrs value.extras
)
);
dependencies =
let
type = lib.types.withCheck lib.types.raw lib.types.package.children.final.check;
entries = type // {
name = "Dependency";
description = "a package dependency";
};
in
lib.types.attrs.of entries;
alias = lib.types.submodule (
{ config, name }:
{
options = {
extend = lib.options.create {
description = "Extend the default version with additional attributes.";
type = lib.types.function lib.types.raw;
writable = false;
default.value =
let
default = config.${global.config.preferences.packages.version};
in
default.extend
or (builtins.throw "Package \"${name}\" does not have a ${global.config.preferences.packages.version} release.");
};
stable = lib.options.create {
description = "The stable version of the package.";
type = lib.types.package;
default.value = config.latest;
};
latest = lib.options.create {
description = "The latest version of the package.";
type = lib.types.package;
default.value =
if config.versions == { } then null else config.versions.${lib.packages.getLatest config};
};
# NOTE: When adding a version alias here, update lib.packages.WELL_KNOWN_ALIASES
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.isNull value then
{ }
else if builtins.isPath value then
{ includes = [ value ]; }
else 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
|| builtins.isNull value
|| builtins.isPath 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 = lib.lists.from.any (normalize value);
result = lib.modules.run {
prefix = location;
args = {
global = global.config;
};
modules = modules ++ [
submodule
{ config.__modules__ = modules; }
];
};
in
result.config;
final = lib.types.raw // {
merge =
location: definitions:
let
modules = builtins.concatMap (
definition: lib.lists.from.any (normalize definition.value)
) definitions;
result = lib.modules.run {
prefix = location;
args = {
global = global.config;
};
modules = modules ++ [
submodule
{ config.__modules__ = modules; }
];
};
in
result.config;
};
deps =
build: host:
lib.types.submodule {
options = {
build = lib.options.create {
description = "Dependencies which are executed in the build environment.";
type = lib.types.dependencies;
default.value = { };
};
host = lib.options.create {
description = "Dependencies which are executed in the host environment.";
type = lib.types.dependencies;
default.value = { };
};
};
};
submodule =
{
config,
meta,
}:
let
build = config.platform.build;
host = config.platform.host;
in
{
options = {
__modules__ = lib.options.create {
description = "User specified modules for the package definition.";
type = lib.types.list.of (initial // { merge = lib.options.merge.one; });
internal = true;
default.value = [ ];
};
__toString = lib.options.create {
description = "The derivation of the package as a string.";
type = lib.types.function lib.types.string;
internal = true;
writable = false;
default.value = value: builtins.toString value.package;
};
extend = lib.options.create {
description = "Extend the package definition.";
type = lib.types.function lib.types.raw;
default.value =
module:
let
normalized =
if builtins.isList module then
module
else if builtins.isFunction module || module ? config then
[ module ]
else
[
{
config = module;
}
];
modules = config.__modules__ ++ normalized;
result = lib.modules.run {
prefix = meta.prefix;
args = {
global = global.config;
};
modules = [
submodule
{ config.__modules__ = modules; }
]
++ modules;
};
in
result.config;
};
extendTemporarily = lib.options.create {
description = "Extend the package definition.";
type = lib.types.function lib.types.raw;
internal = true;
default.value =
module:
let
normalized =
if builtins.isList module then
module
else if builtins.isFunction module || module ? config then
[ module ]
else
[
{
config = module;
}
];
modules = config.__modules__;
result = lib.modules.run {
prefix = meta.prefix;
args = {
global = global.config;
};
modules = [
submodule
{ config.__modules__ = modules; }
{
config = {
packages = lib.modules.override 0 config.packages;
extend = lib.modules.override 0 config.extend;
extendTemporarily = lib.modules.override 0 config.extendTemporarily;
};
}
]
++ modules
++ normalized;
};
in
result.config
// {
inherit (config)
extend
extendTemporarily
packages
;
};
};
meta = {
description = lib.options.create {
description = "The description for the package.";
type = lib.types.string;
default.value = "";
};
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 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;
};
};
extras = lib.options.create {
description = "Extra information for the package.";
type = lib.types.attrs.of lib.types.any;
default.value = { };
};
platforms = lib.options.create {
description = "The platforms that the package supports.";
type = lib.types.either lib.types.platforms.spec (lib.types.list.of lib.types.platforms.spec);
default.value = [ ];
apply = spec: lib.platforms.from.spec spec;
};
platform =
let
initial = builtins.head config.platforms;
in
{
build = lib.options.create {
description = "The build platform for the package.";
type = lib.types.string;
default.value = initial.build or "x86_64-linux";
};
host = lib.options.create {
description = "The host platform for the package.";
type = lib.types.string;
default.value = initial.host or "x86_64-linux";
};
};
name = lib.options.create {
description = "The name of the package.";
type = lib.types.string;
default = {
text = "\${config.id}-\${config.version}";
value = "${config.namespace}-${config.id}-${config.version}";
};
};
namespace = lib.options.create {
description = "The namespace that the package belongs to.";
type = lib.types.string;
default.value = "unknown";
};
id = lib.options.create {
description = "The program name for the package";
type = lib.types.string;
default.value = "unknown";
};
version = lib.options.create {
description = "The version for the package.";
type = lib.types.nullish lib.types.version;
default.value = "unknown";
};
builder = lib.options.create {
description = "The builder for the package.";
type = lib.types.builder;
};
src = lib.options.create {
description = "The source for the package.";
type =
let
type = lib.types.nullish (
lib.types.one [
lib.types.derivation
lib.types.path
]
);
in
lib.types.either type (lib.types.attrs.of type);
default.value = null;
};
outputs = lib.options.create {
description = "The outputs for the package.";
type =
let
initial = lib.types.attrs.of lib.types.derivation;
transform = builtins.attrNames;
final = lib.types.list.of lib.types.string;
in
lib.types.coerce initial transform final;
default.value = [ "out" ];
apply = outputs: lib.attrs.generate outputs (output: config.package.${output});
};
env = lib.options.create {
description = "The environment for the package.";
type = lib.types.attrs.of lib.types.string;
default.value = { };
};
package = lib.options.create {
description = "The built derivation.";
type = lib.types.derivation;
default.value = config.builder.build config;
};
packages = lib.options.create {
description = "The built derivations for all platforms.";
type = lib.types.raw;
writable = false;
default.value = lib.attrs.generate lib.systems.doubles.all (
build: lib.attrs.generate lib.systems.doubles.all (host: lib.packages.build config build host)
);
};
phases = lib.options.create {
description = "The phases for the package.";
type = lib.types.either (lib.types.dag.of lib.types.string) (
lib.types.function (lib.types.dag.of lib.types.string)
);
default.value = { };
};
context = lib.options.create {
description = "The context information that the package provides.";
type = lib.types.context;
default.value = { };
};
settings = lib.options.create {
description = "The settings for the package.";
type = lib.types.settings;
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 {
description = "The dependencies for the package.";
type = deps build host;
default.value = { };
apply = value: {
build = lib.packages.dependencies.build build build value.build;
host = lib.packages.dependencies.build build host value.host;
};
};
};
};
type = (lib.types.coerceWithLocation initial transform final) // {
name = "Package";
description = "a package definition";
};
in
type
// {
children = type.children // {
inherit submodule;
};
};
};
};
}