example refactor snowflake module system
This commit is contained in:
parent
0314109bc0
commit
3e29615db9
26 changed files with 1306 additions and 165 deletions
61
nix/snow/flake/modules/apps.nix
Normal file
61
nix/snow/flake/modules/apps.nix
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
{ lib, flake-parts-lib, ... }:
|
||||
let
|
||||
inherit (lib)
|
||||
mkOption
|
||||
types
|
||||
;
|
||||
inherit (flake-parts-lib)
|
||||
mkTransposedPerSystemModule
|
||||
;
|
||||
|
||||
programType = lib.types.coercedTo derivationType lib.getExe lib.types.str;
|
||||
|
||||
derivationType = lib.types.package // {
|
||||
check = lib.isDerivation;
|
||||
};
|
||||
|
||||
appType = lib.types.submodule {
|
||||
options = {
|
||||
type = mkOption {
|
||||
type = lib.types.enum [ "app" ];
|
||||
default = "app";
|
||||
description = ''
|
||||
A type tag for `apps` consumers.
|
||||
'';
|
||||
};
|
||||
program = mkOption {
|
||||
type = programType;
|
||||
description = ''
|
||||
A path to an executable or a derivation with `meta.mainProgram`.
|
||||
'';
|
||||
};
|
||||
meta = mkOption {
|
||||
type = types.lazyAttrsOf lib.types.raw;
|
||||
default = { };
|
||||
# TODO refer to Nix manual 2.25
|
||||
description = ''
|
||||
Metadata information about the app.
|
||||
Standardized in Nix at <https://github.com/NixOS/nix/pull/11297>.
|
||||
|
||||
Note: `nix flake check` is only aware of the `description` attribute in `meta`.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
mkTransposedPerSystemModule {
|
||||
name = "apps";
|
||||
option = mkOption {
|
||||
type = types.lazyAttrsOf appType;
|
||||
default = { };
|
||||
description = ''
|
||||
Programs runnable with nix run `<name>`.
|
||||
'';
|
||||
example = lib.literalExpression ''
|
||||
{
|
||||
default.program = "''${config.packages.hello}/bin/hello";
|
||||
}
|
||||
'';
|
||||
};
|
||||
file = ./apps.nix;
|
||||
}
|
||||
21
nix/snow/flake/modules/checks.nix
Normal file
21
nix/snow/flake/modules/checks.nix
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{ lib, flake-parts-lib, ... }:
|
||||
let
|
||||
inherit (lib)
|
||||
mkOption
|
||||
types
|
||||
;
|
||||
inherit (flake-parts-lib)
|
||||
mkTransposedPerSystemModule
|
||||
;
|
||||
in
|
||||
mkTransposedPerSystemModule {
|
||||
name = "checks";
|
||||
option = mkOption {
|
||||
type = types.lazyAttrsOf types.package;
|
||||
default = { };
|
||||
description = ''
|
||||
Derivations to be built by [`nix flake check`](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake-check.html).
|
||||
'';
|
||||
};
|
||||
file = ./checks.nix;
|
||||
}
|
||||
78
nix/snow/flake/modules/debug.nix
Normal file
78
nix/snow/flake/modules/debug.nix
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
{ config, flake-parts-lib, lib, options, getSystem, extendModules, ... }:
|
||||
let
|
||||
inherit (lib)
|
||||
mapAttrs
|
||||
mkIf
|
||||
mkOption
|
||||
optionalAttrs
|
||||
types
|
||||
;
|
||||
inherit (flake-parts-lib)
|
||||
mkPerSystemOption
|
||||
;
|
||||
|
||||
mkDebugConfig = { config, options, extendModules }: config // {
|
||||
inherit config;
|
||||
inherit (config) _module;
|
||||
inherit options;
|
||||
inherit extendModules;
|
||||
};
|
||||
in
|
||||
{
|
||||
options = {
|
||||
debug = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to add the attributes `debug`, `allSystems` and `currentSystem`
|
||||
to the flake output. When `true`, this allows inspection of options via
|
||||
`nix repl`.
|
||||
|
||||
```
|
||||
$ nix repl
|
||||
nix-repl> :lf .
|
||||
nix-repl> currentSystem._module.args.pkgs.hello
|
||||
«derivation /nix/store/7vf0d0j7majv1ch1xymdylyql80cn5fp-hello-2.12.1.drv»
|
||||
```
|
||||
|
||||
Each of `debug`, `allSystems.<system>` and `currentSystem` is an
|
||||
attribute set consisting of the `config` attributes, plus the extra
|
||||
attributes `_module`, `config`, `options`, `extendModules`. So note that
|
||||
these are not part of the `config` parameter, but are merged in for
|
||||
debugging convenience.
|
||||
|
||||
- `debug`: The top-level options
|
||||
- `allSystems`: The `perSystem` submodule applied to the configured `systems`.
|
||||
- `currentSystem`: Shortcut into `allSystems`. Only available in impure mode.
|
||||
Works for arbitrary system values.
|
||||
|
||||
See [Expore and debug option values](../debug.html) for more examples.
|
||||
'';
|
||||
};
|
||||
perSystem = mkPerSystemOption
|
||||
({ options, config, extendModules, ... }: {
|
||||
_file = ./formatter.nix;
|
||||
options = {
|
||||
debug = mkOption {
|
||||
description = ''
|
||||
Values to return in e.g. `allSystems.<system>` when
|
||||
[`debug = true`](#opt-debug).
|
||||
'';
|
||||
type = types.lazyAttrsOf types.raw;
|
||||
};
|
||||
};
|
||||
config = {
|
||||
debug = mkDebugConfig { inherit config options extendModules; };
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
config = mkIf config.debug {
|
||||
flake = {
|
||||
debug = mkDebugConfig { inherit config options extendModules; };
|
||||
allSystems = mapAttrs (_s: c: c.debug) config.allSystems;
|
||||
} // optionalAttrs (builtins?currentSystem) {
|
||||
currentSystem = (getSystem builtins.currentSystem).debug;
|
||||
};
|
||||
};
|
||||
}
|
||||
30
nix/snow/flake/modules/devShells.nix
Normal file
30
nix/snow/flake/modules/devShells.nix
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
{ lib, flake-parts-lib, ... }:
|
||||
let
|
||||
inherit (lib)
|
||||
mkOption
|
||||
types
|
||||
literalExpression
|
||||
;
|
||||
inherit (flake-parts-lib)
|
||||
mkTransposedPerSystemModule
|
||||
;
|
||||
in
|
||||
mkTransposedPerSystemModule {
|
||||
name = "devShells";
|
||||
option = mkOption {
|
||||
type = types.lazyAttrsOf types.package;
|
||||
default = { };
|
||||
description = ''
|
||||
An attribute set of packages to be used as shells.
|
||||
[`nix develop .#<name>`](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-develop.html) will run `devShells.<name>`.
|
||||
'';
|
||||
example = literalExpression ''
|
||||
{
|
||||
default = pkgs.mkShell {
|
||||
nativeBuildInputs = with pkgs; [ wget bat cargo ];
|
||||
};
|
||||
}
|
||||
'';
|
||||
};
|
||||
file = ./devShells.nix;
|
||||
}
|
||||
47
nix/snow/flake/modules/flake.nix
Normal file
47
nix/snow/flake/modules/flake.nix
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}: let
|
||||
inherit
|
||||
(lib)
|
||||
mkOption
|
||||
types
|
||||
;
|
||||
|
||||
flake = mkOption {
|
||||
type = types.submoduleWith {
|
||||
modules = [
|
||||
{
|
||||
freeformType =
|
||||
types.lazyAttrsOf
|
||||
(types.unique
|
||||
{
|
||||
message = ''
|
||||
No option has been declared for this flake output attribute, so its definitions can't be merged automatically.
|
||||
Possible solutions:
|
||||
- Load a module that defines this flake output attribute
|
||||
Many modules are listed at https://flake.parts
|
||||
- Declare an option for this flake output attribute
|
||||
- Make sure the output attribute is spelled correctly
|
||||
- Define the value only once, with a single definition in a single module
|
||||
'';
|
||||
}
|
||||
types.raw);
|
||||
}
|
||||
];
|
||||
};
|
||||
description = ''
|
||||
Raw flake output attributes. Any attribute can be set here, but some
|
||||
attributes are represented by options, to provide appropriate
|
||||
configuration merging.
|
||||
'';
|
||||
};
|
||||
in {
|
||||
options = {
|
||||
inherit flake;
|
||||
output = {inherit flake;};
|
||||
};
|
||||
|
||||
config = {inherit (config) flake;};
|
||||
}
|
||||
52
nix/snow/flake/modules/formatter.nix
Normal file
52
nix/snow/flake/modules/formatter.nix
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
{ config, lib, flake-parts-lib, ... }:
|
||||
let
|
||||
inherit (lib)
|
||||
filterAttrs
|
||||
mapAttrs
|
||||
mkOption
|
||||
optionalAttrs
|
||||
types
|
||||
;
|
||||
inherit (flake-parts-lib)
|
||||
mkPerSystemOption
|
||||
;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
flake.formatter = mkOption {
|
||||
type = types.lazyAttrsOf types.package;
|
||||
default = { };
|
||||
description = ''
|
||||
An attribute set of per system a package used by [`nix fmt`](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-fmt.html).
|
||||
'';
|
||||
};
|
||||
|
||||
perSystem = mkPerSystemOption {
|
||||
_file = ./formatter.nix;
|
||||
options = {
|
||||
formatter = mkOption {
|
||||
type = types.nullOr types.package;
|
||||
default = null;
|
||||
description = ''
|
||||
A package used by [`nix fmt`](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-fmt.html).
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
config = {
|
||||
flake.formatter =
|
||||
mapAttrs
|
||||
(k: v: v.formatter)
|
||||
(filterAttrs
|
||||
(k: v: v.formatter != null)
|
||||
config.allSystems
|
||||
);
|
||||
|
||||
perInput = system: flake:
|
||||
optionalAttrs (flake?formatter.${system}) {
|
||||
formatter = flake.formatter.${system};
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
21
nix/snow/flake/modules/legacyPackages.nix
Normal file
21
nix/snow/flake/modules/legacyPackages.nix
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{ lib, flake-parts-lib, ... }:
|
||||
let
|
||||
inherit (lib)
|
||||
mkOption
|
||||
types
|
||||
;
|
||||
inherit (flake-parts-lib)
|
||||
mkTransposedPerSystemModule
|
||||
;
|
||||
in
|
||||
mkTransposedPerSystemModule {
|
||||
name = "legacyPackages";
|
||||
option = mkOption {
|
||||
type = types.lazyAttrsOf types.raw;
|
||||
default = { };
|
||||
description = ''
|
||||
An attribute set of unmergeable values. This is also used by [`nix build .#<attrpath>`](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-build.html).
|
||||
'';
|
||||
};
|
||||
file = ./legacyPackages.nix;
|
||||
}
|
||||
32
nix/snow/flake/modules/moduleWithSystem.nix
Normal file
32
nix/snow/flake/modules/moduleWithSystem.nix
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
{ withSystem, ... }:
|
||||
{
|
||||
config = {
|
||||
_module.args = {
|
||||
moduleWithSystem =
|
||||
module:
|
||||
|
||||
{ config, ... }:
|
||||
let
|
||||
system =
|
||||
config._module.args.system or
|
||||
config._module.args.pkgs.stdenv.hostPlatform.system or
|
||||
(throw "moduleWithSystem: Could not determine the configuration's system parameter for this module system application.");
|
||||
|
||||
allArgs = withSystem system (args: args);
|
||||
|
||||
lazyArgsPerParameter = f: builtins.mapAttrs
|
||||
(k: v: allArgs.${k} or (throw "moduleWithSystem: module argument `${k}` does not exist."))
|
||||
(builtins.functionArgs f);
|
||||
|
||||
# Use reflection to make the call lazy in the argument.
|
||||
# Restricts args to the ones declared.
|
||||
callLazily = f: a: f (lazyArgsPerParameter f);
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
(callLazily module allArgs)
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
36
nix/snow/flake/modules/nixosConfigurations.nix
Normal file
36
nix/snow/flake/modules/nixosConfigurations.nix
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib)
|
||||
mkOption
|
||||
types
|
||||
literalExpression
|
||||
;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
flake.nixosConfigurations = mkOption {
|
||||
type = types.lazyAttrsOf types.raw;
|
||||
default = { };
|
||||
description = ''
|
||||
Instantiated NixOS configurations. Used by `nixos-rebuild`.
|
||||
|
||||
`nixosConfigurations` is for specific machines. If you want to expose
|
||||
reusable configurations, add them to [`nixosModules`](#opt-flake.nixosModules)
|
||||
in the form of modules (no `lib.nixosSystem`), so that you can reference
|
||||
them in this or another flake's `nixosConfigurations`.
|
||||
'';
|
||||
example = literalExpression ''
|
||||
{
|
||||
my-machine = inputs.nixpkgs.lib.nixosSystem {
|
||||
# system is not needed with freshly generated hardware-configuration.nix
|
||||
# system = "x86_64-linux"; # or set nixpkgs.hostPlatform in a module.
|
||||
modules = [
|
||||
./my-machine/nixos-configuration.nix
|
||||
config.nixosModules.my-module
|
||||
];
|
||||
};
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
26
nix/snow/flake/modules/nixosModules.nix
Normal file
26
nix/snow/flake/modules/nixosModules.nix
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{ self, lib, moduleLocation, ... }:
|
||||
let
|
||||
inherit (lib)
|
||||
mapAttrs
|
||||
mkOption
|
||||
types
|
||||
;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
flake.nixosModules = mkOption {
|
||||
type = types.lazyAttrsOf types.deferredModule;
|
||||
default = { };
|
||||
apply = mapAttrs (k: v: {
|
||||
_class = "nixos";
|
||||
_file = "${toString moduleLocation}#nixosModules.${k}";
|
||||
imports = [ v ];
|
||||
});
|
||||
description = ''
|
||||
NixOS modules.
|
||||
|
||||
You may use this for reusable pieces of configuration, service modules, etc.
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
26
nix/snow/flake/modules/nixpkgs.nix
Normal file
26
nix/snow/flake/modules/nixpkgs.nix
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#
|
||||
# Nixpkgs module. The only exception to the rule.
|
||||
#
|
||||
# Provides a `pkgs` argument in `perSystem`.
|
||||
#
|
||||
# Arguably, this shouldn't be in flake-parts, but in nixpkgs.
|
||||
# Nixpkgs could define its own module that does this, which would be
|
||||
# a more consistent UX, but for now this will do.
|
||||
#
|
||||
# The existence of this module does not mean that other flakes' logic
|
||||
# will be accepted into flake-parts, because it's against the
|
||||
# spirit of Flakes.
|
||||
#
|
||||
{
|
||||
config = {
|
||||
perSystem = { inputs', lib, ... }: {
|
||||
config = {
|
||||
_module.args.pkgs = lib.mkOptionDefault (
|
||||
builtins.seq
|
||||
(inputs'.nixpkgs or (throw "flake-parts: The flake does not have a `nixpkgs` input. Please add it, or set `perSystem._module.args.pkgs` yourself."))
|
||||
inputs'.nixpkgs.legacyPackages
|
||||
);
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
32
nix/snow/flake/modules/overlays.nix
Normal file
32
nix/snow/flake/modules/overlays.nix
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib)
|
||||
mkOption
|
||||
types
|
||||
;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
flake.overlays = mkOption {
|
||||
# uniq -> ordered: https://github.com/NixOS/nixpkgs/issues/147052
|
||||
# also update description when done
|
||||
type = types.lazyAttrsOf (types.uniq (types.functionTo (types.functionTo (types.lazyAttrsOf types.unspecified))));
|
||||
# This eta expansion exists for the sole purpose of making nix flake check happy.
|
||||
apply = lib.mapAttrs (_k: f: final: prev: f final prev);
|
||||
default = { };
|
||||
example = lib.literalExpression ''
|
||||
{
|
||||
default = final: prev: {};
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
An attribute set of [overlays](https://nixos.org/manual/nixpkgs/stable/#chap-overlays).
|
||||
|
||||
Note that the overlays themselves are not mergeable. While overlays
|
||||
can be composed, the order of composition is significant, but the
|
||||
module system does not guarantee sufficiently deterministic
|
||||
definition ordering, across versions and when changing `imports`.
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
23
nix/snow/flake/modules/packages.nix
Normal file
23
nix/snow/flake/modules/packages.nix
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{ lib, flake-parts-lib, ... }:
|
||||
let
|
||||
inherit (lib)
|
||||
mkOption
|
||||
types
|
||||
;
|
||||
inherit (flake-parts-lib)
|
||||
mkTransposedPerSystemModule
|
||||
;
|
||||
in
|
||||
mkTransposedPerSystemModule {
|
||||
name = "packages";
|
||||
option = mkOption {
|
||||
type = types.lazyAttrsOf types.package;
|
||||
default = { };
|
||||
description = ''
|
||||
An attribute set of packages to be built by [`nix build`](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-build.html).
|
||||
|
||||
`nix build .#<name>` will build `packages.<name>`.
|
||||
'';
|
||||
};
|
||||
file = ./packages.nix;
|
||||
}
|
||||
159
nix/snow/flake/modules/perSystem.nix
Normal file
159
nix/snow/flake/modules/perSystem.nix
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
{ config, lib, flake-parts-lib, self, ... }:
|
||||
let
|
||||
inherit (lib)
|
||||
genAttrs
|
||||
mapAttrs
|
||||
mkOption
|
||||
types
|
||||
;
|
||||
inherit (lib.strings)
|
||||
escapeNixIdentifier
|
||||
;
|
||||
inherit (flake-parts-lib)
|
||||
mkPerSystemType
|
||||
;
|
||||
|
||||
rootConfig = config;
|
||||
|
||||
# Stubs for self and inputs. While it'd be possible to define aliases
|
||||
# inside perSystem, that is not a general solution, and it would make
|
||||
# top.config harder to discover, stretching the learning curve rather
|
||||
# than flattening it.
|
||||
|
||||
throwAliasError' = param:
|
||||
throw ''
|
||||
`${param}` (without `'`) is not a `perSystem` module argument, but a
|
||||
module argument of the top level config.
|
||||
|
||||
The following is an example usage of `${param}`. Note that its binding
|
||||
is in the `top` parameter list, which is declared by the top level module
|
||||
rather than the `perSystem` module.
|
||||
|
||||
top@{ config, lib, ${param}, ... }: {
|
||||
perSystem = { config, ${param}', ... }: {
|
||||
# in scope here:
|
||||
# - ${param}
|
||||
# - ${param}'
|
||||
# - config (of perSystem)
|
||||
# - top.config (note the `top@` pattern)
|
||||
};
|
||||
}
|
||||
'';
|
||||
|
||||
throwAliasError = param:
|
||||
throw ''
|
||||
`${param}` is not a `perSystem` module argument, but a module argument of
|
||||
the top level config.
|
||||
|
||||
The following is an example usage of `${param}`. Note that its binding
|
||||
is in the `top` parameter list, which is declared by the top level module
|
||||
rather than the `perSystem` module.
|
||||
|
||||
top@{ config, lib, ${param}, ... }: {
|
||||
perSystem = { config, ... }: {
|
||||
# in scope here:
|
||||
# - ${param}
|
||||
# - config (of perSystem)
|
||||
# - top.config (note the `top@` pattern)
|
||||
};
|
||||
}
|
||||
'';
|
||||
|
||||
/**
|
||||
We primarily use `systems` to help memoize the per system context, but that
|
||||
doesn't extend to arbitrary `system`s.
|
||||
For that, we use the slightly less efficient, but perfectly acceptable
|
||||
`memoizeStr` function.
|
||||
*/
|
||||
otherMemoizedSystems = flake-parts-lib.memoizeStr config.perSystem;
|
||||
|
||||
in
|
||||
{
|
||||
options = {
|
||||
systems = mkOption {
|
||||
description = ''
|
||||
All the system types to enumerate in the flake output subattributes.
|
||||
|
||||
In other words, all valid values for `system` in e.g. `packages.<system>.foo`.
|
||||
'';
|
||||
type = types.listOf types.str;
|
||||
};
|
||||
|
||||
perInput = mkOption {
|
||||
description = ''
|
||||
A function that pre-processes flake inputs.
|
||||
|
||||
It is called for users of `perSystem` such that `inputs'.''${name} = config.perInput system inputs.''${name}`.
|
||||
|
||||
This is used for [`inputs'`](../module-arguments.html#inputs) and [`self'`](../module-arguments.html#self).
|
||||
|
||||
The attributes returned by the `perInput` function definitions are merged into a single namespace (per input),
|
||||
so each module should return an attribute set with usually only one or two predictable attribute names. Otherwise,
|
||||
the `inputs'` namespace gets polluted.
|
||||
'';
|
||||
type = types.functionTo (types.functionTo (types.lazyAttrsOf types.unspecified));
|
||||
};
|
||||
|
||||
perSystem = mkOption {
|
||||
description = ''
|
||||
A function from system to flake-like attributes omitting the `<system>` attribute.
|
||||
|
||||
Modules defined here have access to the suboptions and [some convenient module arguments](../module-arguments.html).
|
||||
'';
|
||||
type = mkPerSystemType ({ config, system, ... }: {
|
||||
_file = ./perSystem.nix;
|
||||
config = {
|
||||
_module.args.inputs' =
|
||||
mapAttrs
|
||||
(inputName: input:
|
||||
builtins.addErrorContext "while retrieving system-dependent attributes for input ${escapeNixIdentifier inputName}" (
|
||||
if input._type or null == "flake"
|
||||
then rootConfig.perInput system input
|
||||
else
|
||||
throw "Trying to retrieve system-dependent attributes for input ${escapeNixIdentifier inputName}, but this input is not a flake. Perhaps flake = false was added to the input declarations by mistake, or you meant to use a different input, or you meant to use plain old inputs, not inputs'."
|
||||
)
|
||||
)
|
||||
self.inputs;
|
||||
_module.args.self' =
|
||||
builtins.addErrorContext "while retrieving system-dependent attributes for a flake's own outputs" (
|
||||
rootConfig.perInput system self
|
||||
);
|
||||
|
||||
# Custom error messages
|
||||
_module.args.self = throwAliasError' "self";
|
||||
_module.args.inputs = throwAliasError' "inputs";
|
||||
_module.args.getSystem = throwAliasError "getSystem";
|
||||
_module.args.withSystem = throwAliasError "withSystem";
|
||||
_module.args.moduleWithSystem = throwAliasError "moduleWithSystem";
|
||||
};
|
||||
});
|
||||
apply = modules: system:
|
||||
(lib.evalModules {
|
||||
inherit modules;
|
||||
prefix = [ "perSystem" system ];
|
||||
specialArgs = {
|
||||
inherit system;
|
||||
};
|
||||
class = "perSystem";
|
||||
}).config;
|
||||
};
|
||||
|
||||
allSystems = mkOption {
|
||||
type = types.lazyAttrsOf types.unspecified;
|
||||
description = "The system-specific config for each of systems.";
|
||||
internal = true;
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
allSystems = genAttrs config.systems config.perSystem;
|
||||
_module.args.getSystem = system: config.allSystems.${system} or (otherMemoizedSystems system);
|
||||
|
||||
# The warning is there for a reason. Only use this in situations where the
|
||||
# performance cost has already been incurred, such as in `flakeModules.easyOverlay`,
|
||||
# where we run in the context of an overlay, and the performance cost of the
|
||||
# extra `pkgs` makes the cost of running `perSystem` probably negligible.
|
||||
_module.args.getSystemIgnoreWarning = system: config.allSystems.${system} or (config.perSystem system);
|
||||
};
|
||||
|
||||
}
|
||||
132
nix/snow/flake/modules/transposition.nix
Normal file
132
nix/snow/flake/modules/transposition.nix
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
{ config, lib, flake-parts-lib, ... }:
|
||||
|
||||
let
|
||||
inherit (lib)
|
||||
filterAttrs
|
||||
mapAttrs
|
||||
mkOption
|
||||
types
|
||||
;
|
||||
inherit (lib.strings)
|
||||
escapeNixIdentifier
|
||||
;
|
||||
|
||||
transpositionModule = {
|
||||
options = {
|
||||
adHoc = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to provide a stub option declaration for {option}`perSystem.<name>`.
|
||||
|
||||
The stub option declaration does not support merging and lacks
|
||||
documentation, so you are recommended to declare the {option}`perSystem.<name>`
|
||||
option yourself and avoid {option}`adHoc`.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
perInputAttributeError = { flake, attrName, system, attrConfig }:
|
||||
# This uses flake.outPath for lack of a better identifier.
|
||||
# Consider adding a perInput variation that has a normally-redundant argument for the input name.
|
||||
# Tested manually with
|
||||
# perSystem = { inputs', ... }: {
|
||||
# packages.extra = inputs'.nixpkgs.extra;
|
||||
# packages.default = inputs'.nixpkgs.packages.default;
|
||||
# packages.veryWrong = (top.config.perInput "x86_64-linux" inputs'.nixpkgs.legacyPackages.hello).packages.default;
|
||||
# };
|
||||
# transposition.extra = {};
|
||||
let
|
||||
attrPath = "${escapeNixIdentifier attrName}.${escapeNixIdentifier system}";
|
||||
flakeIdentifier =
|
||||
if flake._type or null != "flake"
|
||||
then
|
||||
throw "An attempt was made to access attribute ${attrPath} on a value that's supposed to be a flake, but may not be a proper flake."
|
||||
else
|
||||
builtins.addErrorContext "while trying to find out how to describe what is supposedly a flake, whose attribute ${attrPath} was accessed but does not exist" (
|
||||
toString flake.outPath
|
||||
);
|
||||
# This ought to be generalized by extending attrConfig, but this is the only known and common mistake for now.
|
||||
alternateAttrNameHint =
|
||||
if attrName == "packages" && flake?legacyPackages
|
||||
then # Unfortunately we can't just switch them out, because that will put packages *sets* where single packages are expected in user code, resulting in potentially much worse and more confusing errors down the line.
|
||||
"\nIt does define legacyPackages; try that instead?"
|
||||
else "";
|
||||
in
|
||||
if flake?${attrName}
|
||||
then
|
||||
throw ''
|
||||
Attempt to access ${attrPath} of flake ${flakeIdentifier}, but it does not have it.
|
||||
It does have attribute ${escapeNixIdentifier attrName}, so it appears that it does not support system type ${escapeNixIdentifier system}.
|
||||
''
|
||||
else
|
||||
throw ''
|
||||
Attempt to access ${attrPath} of flake ${flakeIdentifier}, but it does not have attribute ${escapeNixIdentifier attrName}.${alternateAttrNameHint}
|
||||
'';
|
||||
|
||||
|
||||
in
|
||||
{
|
||||
options = {
|
||||
transposition = lib.mkOption {
|
||||
description = ''
|
||||
A helper that defines transposed attributes in the flake outputs.
|
||||
|
||||
When you define `transposition.foo = { };`, definitions are added to the effect of (pseudo-code):
|
||||
|
||||
```nix
|
||||
flake.foo.''${system} = (perSystem system).foo;
|
||||
perInput = system: inputFlake: inputFlake.foo.''${system};
|
||||
```
|
||||
|
||||
Transposition is the operation that swaps the indices of a data structure.
|
||||
Here it refers specifically to the transposition between
|
||||
|
||||
```plain
|
||||
perSystem: .''${system}.''${attribute}
|
||||
outputs: .''${attribute}.''${system}
|
||||
```
|
||||
|
||||
It also defines the reverse operation in [{option}`perInput`](#opt-perInput).
|
||||
'';
|
||||
type =
|
||||
types.lazyAttrsOf
|
||||
(types.submoduleWith { modules = [ transpositionModule ]; });
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
flake =
|
||||
lib.mapAttrs
|
||||
(attrName: attrConfig:
|
||||
mapAttrs
|
||||
(system: v: v.${attrName} or (
|
||||
abort ''
|
||||
Could not find option ${attrName} in the perSystem module. It is required to declare such an option whenever transposition.<name> is defined (and in this instance <name> is ${attrName}).
|
||||
''))
|
||||
config.allSystems
|
||||
)
|
||||
config.transposition;
|
||||
|
||||
perInput =
|
||||
system: flake:
|
||||
mapAttrs
|
||||
(attrName: attrConfig:
|
||||
flake.${attrName}.${system} or (
|
||||
throw (perInputAttributeError { inherit system flake attrName attrConfig; })
|
||||
)
|
||||
)
|
||||
config.transposition;
|
||||
|
||||
perSystem = {
|
||||
options =
|
||||
mapAttrs
|
||||
(k: v: lib.mkOption { })
|
||||
(filterAttrs
|
||||
(k: v: v.adHoc)
|
||||
config.transposition
|
||||
);
|
||||
};
|
||||
};
|
||||
}
|
||||
37
nix/snow/flake/modules/withSystem.nix
Normal file
37
nix/snow/flake/modules/withSystem.nix
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
{ lib, flake-parts-lib, getSystem, ... }:
|
||||
let
|
||||
inherit (lib)
|
||||
mkOption
|
||||
types
|
||||
;
|
||||
inherit (flake-parts-lib)
|
||||
mkPerSystemOption
|
||||
;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
perSystem = mkPerSystemOption ({ config, options, specialArgs, ... }: {
|
||||
_file = ./perSystem.nix;
|
||||
options = {
|
||||
allModuleArgs = mkOption {
|
||||
type = types.lazyAttrsOf (types.raw or types.unspecified);
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
description = "Internal option that exposes _module.args, for use by withSystem.";
|
||||
};
|
||||
};
|
||||
config = {
|
||||
allModuleArgs = config._module.args // specialArgs // { inherit config options; };
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
config = {
|
||||
_module.args = {
|
||||
withSystem =
|
||||
system: f:
|
||||
f
|
||||
(getSystem system).allModuleArgs;
|
||||
};
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue