1
0
Fork 0
mirror of https://github.com/hercules-ci/flake-parts.git synced 2024-12-14 11:47:31 +00:00
flake-parts/lib.nix
Robert Hensing f427ecf1a0 Repurpose moduleLocation impl for new errorLocation
The difference is that moduleLocation is "guaranteed" reliable data,
whereas errorLocation is the best choice for error messages in the core.

moduleLocation is suitable for the module key attribute.
errorLocation is best for the *ROOT* module _file attribute.

Initially I applied errorLocation in too many places. It is only needed
when the flake output attribute names are strict in it.
To avoid confusion, I'm not exposing errorLocation to the modules, until
we have a concrete use case for it.
2023-10-14 00:03:28 +02:00

241 lines
7.2 KiB
Nix

{ lib
# Optionally a string with extra version info to be included in the error message
# in case is lib is out of date. Empty or starts with space.
, revInfo ? ""
}:
let
inherit (lib)
mkOption
mkOptionType
defaultFunctor
isAttrs
isFunction
showOption
throwIf
types
warnIf
getAttrFromPath
setAttrByPath
attrByPath
optionalAttrs
;
inherit (lib.modules)
mkAliasAndWrapDefsWithPriority;
inherit (lib.types)
path
submoduleWith
;
# Polyfill isFlake until Nix with https://github.com/NixOS/nix/pull/7207 is common
isFlake = maybeFlake:
if maybeFlake ? _type
then maybeFlake._type == "flake"
else maybeFlake ? inputs && maybeFlake ? outputs && maybeFlake ? sourceInfo;
# Polyfill https://github.com/NixOS/nixpkgs/pull/163617
deferredModuleWith = lib.deferredModuleWith or (
attrs@{ staticModules ? [ ] }: mkOptionType {
name = "deferredModule";
description = "module";
check = x: isAttrs x || isFunction x || path.check x;
merge = loc: defs: staticModules ++ map (def: lib.setDefaultModuleLocation "${def.file}, via option ${showOption loc}" def.value) defs;
inherit (submoduleWith { modules = staticModules; })
getSubOptions
getSubModules;
substSubModules = m: deferredModuleWith (attrs // {
staticModules = m;
});
functor = defaultFunctor "deferredModuleWith" // {
type = deferredModuleWith;
payload = {
inherit staticModules;
};
binOp = lhs: rhs: {
staticModules = lhs.staticModules ++ rhs.staticModules;
};
};
}
);
errorExample = ''
For example:
outputs = inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } { /* module */ };
To avoid an infinite recursion, *DO NOT* pass `self.inputs` and
*DO NOT* pass `inherit (self) inputs`, but pass the output function
arguments as `inputs` like above.
'';
flake-parts-lib = rec {
evalFlakeModule =
args@
{ inputs ? self.inputs
, specialArgs ? { }
# legacy
, self ? inputs.self or (throw ''
When invoking flake-parts, you must pass all the flake output arguments,
and not just `self.inputs`.
${errorExample}
'')
, moduleLocation ? "${self.outPath}/flake.nix"
}:
let
module = lib.setDefaultModuleLocation errorLocation module;
inputsPos = builtins.unsafeGetAttrPos "inputs" args;
errorLocation =
# Best case: user makes it explicit
args.moduleLocation or (
# Slightly worse: Nix does not technically commit to unsafeGetAttrPos semantics
if inputsPos != null
then inputsPos.file
# Slightly worse: self may not be valid when an error occurs
else if args?inputs.self.outPath
then args.inputs.self.outPath + "/flake.nix"
# Fallback
else "<mkFlake argument>"
);
in
throwIf
(!args?self && !args?inputs) ''
When invoking flake-parts, you must pass in the flake output arguments.
${errorExample}
''
warnIf
(!args?inputs) ''
When invoking flake-parts, it is recommended to pass all the flake output
arguments in the `inputs` parameter. If you only pass `self`, it's not
possible to use the `inputs` module argument in the module `imports`.
Please pass the output function arguments. ${errorExample}
''
(module:
lib.evalModules {
specialArgs = {
inherit self flake-parts-lib moduleLocation;
inputs = args.inputs or /* legacy, warned above */ self.inputs;
} // specialArgs;
modules = [ ./all-modules.nix module ];
}
);
# Function to extract the default flakeModule from
# what may be a flake, returning the argument unmodified
# if it's not a flake.
#
# Useful to map over an 'imports' list to make it less
# verbose in the common case.
defaultModule = maybeFlake:
if isFlake maybeFlake
then maybeFlake.flakeModules.default or maybeFlake
else maybeFlake;
mkFlake = args: module:
let
eval = flake-parts-lib.evalFlakeModule args module;
in
eval.config.flake;
# For extending options in an already declared submodule.
# Workaround for https://github.com/NixOS/nixpkgs/issues/146882
mkSubmoduleOptions =
options:
mkOption {
type = types.submoduleWith {
modules = [{ inherit options; }];
};
};
mkDeferredModuleType =
module:
deferredModuleWith {
staticModules = [ module ];
};
mkPerSystemType = mkDeferredModuleType;
mkDeferredModuleOption =
module:
mkOption {
type = flake-parts-lib.mkPerSystemType module;
};
mkPerSystemOption = mkDeferredModuleOption;
# Helper function for defining a per-system option that
# gets transposed by the usual flake system logic to a
# top-level flake attribute.
mkTransposedPerSystemModule = { name, option, file }: {
_file = file;
options = {
flake = flake-parts-lib.mkSubmoduleOptions {
${name} = mkOption {
type = types.lazyAttrsOf option.type;
default = { };
description = ''
See {option}`perSystem.${name}` for description and examples.
'';
};
};
perSystem = flake-parts-lib.mkPerSystemOption {
_file = file;
options.${name} = option;
};
};
config = {
transposition.${name} = { };
};
};
# Needed pending https://github.com/NixOS/nixpkgs/pull/198450
mkAliasOptionModule = from: to: { config, options, ... }:
let
fromOpt = getAttrFromPath from options;
toOf = attrByPath to
(abort "Renaming error: option `${showOption to}' does not exist.");
toType = let opt = attrByPath to { } options; in opt.type or (types.submodule { });
in
{
options = setAttrByPath from (mkOption
{
visible = true;
description = lib.mdDoc "Alias of {option}`${showOption to}`.";
apply = x: (toOf config);
} // optionalAttrs (toType != null) {
type = toType;
});
config = mkAliasAndWrapDefsWithPriority (setAttrByPath to) fromOpt;
};
# Helper function for importing while preserving module location. To be added
# in nixpkgs: https://github.com/NixOS/nixpkgs/pull/230588
# I expect these functions to remain identical. This one will stick around
# for a while to support older nixpkgs-lib.
importApply =
modulePath: staticArgs:
lib.setDefaultModuleLocation modulePath (import modulePath staticArgs);
};
# A best effort, lenient estimate. Please use a recent nixpkgs lib if you
# override it at all.
minVersion = "22.05";
in
if builtins.compareVersions lib.version minVersion < 0
then
abort ''
The nixpkgs-lib dependency of flake-parts was overridden but is too old.
The minimum supported version of nixpkgs-lib is ${minVersion},
but the actual version is ${lib.version}${revInfo}.
''
else
flake-parts-lib