2022-05-25 14:36:33 +00:00
{ config , lib , flake-parts-lib , self , . . . }:
2021-10-27 09:05:52 +00:00
let
inherit ( lib )
2021-11-22 21:01:38 +00:00
genAttrs
2021-10-27 09:05:52 +00:00
mapAttrs
mkOption
types
;
perSystem: Check that inputs'.<name> is a flake, for better error message
The alternative would have been to try and make this not an evaluation error,
but that would cause problems.
Idea: use filterAttrs to construct an attrset "inputFlakes". This would be
bad because it requires that we evaluate all inputs, just to see whether
they're flakes or not.
If Nix were to provide this metadata independently, which it could do
very efficiently, we still ought to wonder what's the purpose of
eliding those attributes. The only ways you'll encounter this
exception in `inputs'.<broken>` is by either
- making a mistake, in which case the error message is great
- or traversing all inputs, which nobody should do.
See https://flake.parts/best-practices-for-module-writing#do-not-traverse-inputs
Other idea: return *something empty*. This avoids the strictness
problem above, but still optimizes for the wrong use case.
Ultimately though, attribute transposition should not be needed at all,
but this project is largely dependent on the implicit schema imposed by Nix.
2024-01-11 11:19:58 +00:00
inherit ( lib . strings )
escapeNixIdentifier
;
2022-05-25 14:36:33 +00:00
inherit ( flake-parts-lib )
2022-05-13 08:14:10 +00:00
mkPerSystemType
;
2021-10-27 09:05:52 +00:00
rootConfig = config ;
2022-12-27 11:53:19 +00:00
# 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)
} ;
}
'' ;
2021-10-27 09:05:52 +00:00
in
{
options = {
systems = mkOption {
2022-11-11 06:39:25 +00:00
description = ''
2022-11-11 05:40:37 +00:00
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 ` .
'' ;
2021-10-27 09:05:52 +00:00
type = types . listOf types . str ;
} ;
perInput = mkOption {
2023-01-06 23:12:57 +00:00
description = ''
A function that pre-processes flake inputs .
It is called for users of ` perSystem ` such that ` inputs' . '' ${ name } = c o n f i g . p e r I n p u t s y s t e m i n p u t s . ''$ { n a m e } ` .
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 .
'' ;
2021-10-27 09:05:52 +00:00
type = types . functionTo ( types . functionTo ( types . lazyAttrsOf types . unspecified ) ) ;
} ;
perSystem = mkOption {
2023-01-06 23:12:57 +00:00
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 ) .
'' ;
2022-05-13 08:14:10 +00:00
type = mkPerSystemType ( { config , system , . . . }: {
_file = ./perSystem.nix ;
config = {
perSystem: Check that inputs'.<name> is a flake, for better error message
The alternative would have been to try and make this not an evaluation error,
but that would cause problems.
Idea: use filterAttrs to construct an attrset "inputFlakes". This would be
bad because it requires that we evaluate all inputs, just to see whether
they're flakes or not.
If Nix were to provide this metadata independently, which it could do
very efficiently, we still ought to wonder what's the purpose of
eliding those attributes. The only ways you'll encounter this
exception in `inputs'.<broken>` is by either
- making a mistake, in which case the error message is great
- or traversing all inputs, which nobody should do.
See https://flake.parts/best-practices-for-module-writing#do-not-traverse-inputs
Other idea: return *something empty*. This avoids the strictness
problem above, but still optimizes for the wrong use case.
Ultimately though, attribute transposition should not be needed at all,
but this project is largely dependent on the implicit schema imposed by Nix.
2024-01-11 11:19:58 +00:00
_module . args . inputs' =
mapAttrs
( inputName : input :
builtins . addErrorContext " w h i l e r e t r i e v i n g s y s t e m - d e p e n d e n t a t t r i b u t e s f o r i n p u t ${ escapeNixIdentifier inputName } " (
if input . _type or null == " f l a k e "
then rootConfig . perInput system input
else
throw " T r y i n g t o r e t r i e v e s y s t e m - d e p e n d e n t a t t r i b u t e s f o r i n p u t ${ escapeNixIdentifier inputName } , b u t t h i s i n p u t i s n o t a f l a k e . P e r h a p s f l a k e = f a l s e w a s a d d e d t o t h e i n p u t d e c l a r a t i o n s b y m i s t a k e , o r y o u m e a n t t o u s e a d i f f e r e n t i n p u t , o r y o u m e a n t t o u s e p l a i n o l d i n p u t s , n o t i n p u t s ' . "
)
)
self . inputs ;
_module . args . self' =
builtins . addErrorContext " w h i l e r e t r i e v i n g s y s t e m - d e p e n d e n t a t t r i b u t e s f o r a f l a k e ' s o w n o u t p u t s " (
rootConfig . perInput system self
) ;
2022-12-27 11:53:19 +00:00
# Custom error messages
_module . args . self = throwAliasError' " s e l f " ;
_module . args . inputs = throwAliasError' " i n p u t s " ;
_module . args . getSystem = throwAliasError " g e t S y s t e m " ;
_module . args . withSystem = throwAliasError " w i t h S y s t e m " ;
_module . args . moduleWithSystem = throwAliasError " m o d u l e W i t h S y s t e m " ;
2022-05-13 08:14:10 +00:00
} ;
2021-10-27 09:05:52 +00:00
} ) ;
2022-05-25 16:09:17 +00:00
apply = modules : system :
( lib . evalModules {
inherit modules ;
prefix = [ " p e r S y s t e m " system ] ;
specialArgs = {
inherit system ;
} ;
2023-05-08 20:49:12 +00:00
class = " p e r S y s t e m " ;
2022-05-25 16:09:17 +00:00
} ) . config ;
2021-10-27 09:05:52 +00:00
} ;
2021-11-22 21:01:38 +00:00
allSystems = mkOption {
type = types . lazyAttrsOf types . unspecified ;
2022-11-11 06:39:25 +00:00
description = " T h e s y s t e m - s p e c i f i c c o n f i g f o r e a c h o f s y s t e m s . " ;
2021-11-22 21:01:38 +00:00
internal = true ;
} ;
} ;
config = {
allSystems = genAttrs config . systems config . perSystem ;
# TODO: Sub-optimal error message. Get Nix to support a memoization primop, or get Nix Flakes to support systems properly or get Nix Flakes to add a name to flakes.
2022-04-06 16:01:26 +00:00
_module . args . getSystem = system : config . allSystems . ${ system } or ( builtins . trace " u s i n g n o n - m e m o i z e d s y s t e m ${ system } " config . perSystem system ) ;
2023-01-05 00:07:58 +00:00
# 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.
2023-01-05 00:15:50 +00:00
_module . args . getSystemIgnoreWarning = system : config . allSystems . ${ system } or ( config . perSystem system ) ;
2021-10-27 09:05:52 +00:00
} ;
}