Files
nixpkgs/pkgs/development/misc/resholve/resholve-utils.nix
T
Silvan Mosberger 4f0dadbf38 treewide: format all inactive Nix files
After final improvements to the official formatter implementation,
this commit now performs the first treewide reformat of Nix files using it.
This is part of the implementation of RFC 166.

Only "inactive" files are reformatted, meaning only files that
aren't being touched by any PR with activity in the past 2 months.
This is to avoid conflicts for PRs that might soon be merged.
Later we can do a full treewide reformat to get the rest,
which should not cause as many conflicts.

A CI check has already been running for some time to ensure that new and
already-formatted files are formatted, so the files being reformatted here
should also stay formatted.

This commit was automatically created and can be verified using

    nix-build https://github.com/infinisil/treewide-nixpkgs-reformat-script/archive/a08b3a4d199c6124ac5b36a889d9099b4383463f.tar.gz \
      --argstr baseRev b32a094368
    result/bin/apply-formatting $NIXPKGS_PATH
2024-12-10 20:26:33 +01:00

289 lines
7.5 KiB
Nix

{
lib,
stdenv,
resholve,
binlore,
writeTextFile,
}:
rec {
/*
These functions break up the work of partially validating the
'solutions' attrset and massaging it into env/cli args.
Note: some of the left-most args do not *have* to be passed as
deep as they are, but I've done so to provide more error context
*/
# for brevity / line length
spaces = l: builtins.concatStringsSep " " l;
colons = l: builtins.concatStringsSep ":" l;
semicolons = l: builtins.concatStringsSep ";" l;
# Throw a fit with dotted attr path context
nope = path: msg: throw "${builtins.concatStringsSep "." path}: ${msg}";
# Special-case directive value representations by type
phraseDirective =
solution: env: name: val:
if builtins.isInt val then
builtins.toString val
else if builtins.isString val then
name
else if true == val then
name
else if false == val then
"" # omit!
else if null == val then
"" # omit!
else if builtins.isList val then
"${name}:${semicolons (map lib.escapeShellArg val)}"
else
nope [ solution env name ] "unexpected type: ${builtins.typeOf val}";
# Build fake/fix/keep directives from Nix types
phraseDirectives =
solution: env: val:
lib.mapAttrsToList (phraseDirective solution env) val;
# Custom ~search-path routine to handle relative path strings
relSafeBinPath =
input:
if lib.isDerivation input then
((lib.getOutput "bin" input) + "/bin")
else if builtins.isString input then
input
else
throw "unexpected type for input: ${builtins.typeOf input}";
# Special-case value representation by type/name
phraseEnvVal =
solution: env: val:
if env == "inputs" then
(colons (map relSafeBinPath val))
else if builtins.isString val then
val
else if builtins.isList val then
spaces val
else if builtins.isAttrs val then
spaces (phraseDirectives solution env val)
else
nope [ solution env ] "unexpected type: ${builtins.typeOf val}";
# Shell-format each env value
shellEnv =
solution: env: value:
lib.escapeShellArg (phraseEnvVal solution env value);
# Build a single ENV=val pair
phraseEnv =
solution: env: value:
"RESHOLVE_${lib.toUpper env}=${shellEnv solution env value}";
/*
Discard attrs:
- claimed by phraseArgs
- only needed for binlore.collect
*/
removeUnneededArgs =
value:
removeAttrs value [
"scripts"
"flags"
"unresholved"
];
# Verify required arguments are present
validateSolution =
{
scripts,
inputs,
interpreter,
...
}:
true;
# Pull out specific solution keys to build ENV=val pairs
phraseEnvs =
solution: value: spaces (lib.mapAttrsToList (phraseEnv solution) (removeUnneededArgs value));
# Pull out specific solution keys to build CLI argstring
phraseArgs =
{
flags ? [ ],
scripts,
...
}:
spaces (flags ++ scripts);
phraseBinloreArgs =
value:
let
hasUnresholved = builtins.hasAttr "unresholved" value;
in
{
drvs = value.inputs ++ lib.optionals hasUnresholved [ value.unresholved ];
strip = if hasUnresholved then [ value.unresholved ] else [ ];
};
# Build a single resholve invocation
phraseInvocation =
solution: value:
if validateSolution value then
# we pass resholve a directory
"RESHOLVE_LORE=${binlore.collect (phraseBinloreArgs value)} ${phraseEnvs solution value} ${resholve}/bin/resholve --overwrite ${phraseArgs value}"
else
throw "invalid solution"; # shouldn't trigger for now
injectUnresholved =
solutions: unresholved:
(builtins.mapAttrs (name: value: value // { inherit unresholved; }) solutions);
# Build resholve invocation for each solution.
phraseCommands =
solutions: unresholved:
builtins.concatStringsSep "\n" (
lib.mapAttrsToList phraseInvocation (injectUnresholved solutions unresholved)
);
/*
subshell/PS4/set -x and : command to output resholve envs
and invocation. Extra context makes it clearer what the
Nix API is doing, makes nix-shell debugging easier, etc.
*/
phraseContext =
{
invokable,
prep ? ''cd "$out"'',
}:
''
(
${prep}
PS4=$'\x1f'"\033[33m[resholve context]\033[0m "
set -x
: invoking resholve with PWD=$PWD
${invokable}
)
'';
phraseContextForPWD =
invokable:
phraseContext {
inherit invokable;
prep = "";
};
phraseContextForOut = invokable: phraseContext { inherit invokable; };
phraseSolution = name: solution: (phraseContextForOut (phraseInvocation name solution));
phraseSolutions =
solutions: unresholved: phraseContextForOut (phraseCommands solutions unresholved);
writeScript =
name: partialSolution: text:
writeTextFile {
inherit name text;
executable = true;
checkPhase =
''
${
(phraseContextForPWD (
phraseInvocation name (
partialSolution
// {
scripts = [ "${placeholder "out"}" ];
}
)
))
}
''
+ lib.optionalString (partialSolution.interpreter != "none") ''
${partialSolution.interpreter} -n $out
'';
};
writeScriptBin =
name: partialSolution: text:
writeTextFile rec {
inherit name text;
executable = true;
destination = "/bin/${name}";
checkPhase =
''
${phraseContextForOut (
phraseInvocation name (
partialSolution
// {
scripts = [ "bin/${name}" ];
}
)
)}
''
+ lib.optionalString (partialSolution.interpreter != "none") ''
${partialSolution.interpreter} -n $out/bin/${name}
'';
};
mkDerivation =
{
pname,
src,
version,
passthru ? { },
solutions,
...
}@attrs:
let
inherit stdenv;
/*
Knock out our special solutions arg, but otherwise
just build what the caller is giving us. We'll
actually resholve it separately below (after we
generate binlore for it).
*/
unresholved = (
stdenv.mkDerivation (
(removeAttrs attrs [ "solutions" ])
// {
inherit version src;
pname = "${pname}-unresholved";
}
)
);
in
/*
resholve in a separate derivation; some concerns:
- we aren't keeping many of the user's args, so they
can't readily set LOGLEVEL and such...
- not sure how this affects multiple outputs
*/
lib.extendDerivation true passthru (
stdenv.mkDerivation {
src = unresholved;
inherit version pname;
buildInputs = [ resholve ];
disallowedReferences = [ resholve ];
# retain a reference to the base
passthru = unresholved.passthru // {
unresholved = unresholved;
# fallback attr for update bot to query our src
originalSrc = unresholved.src;
};
# do these imply that we should use NoCC or something?
dontConfigure = true;
dontBuild = true;
installPhase = ''
cp -R $src $out
'';
# enable below for verbose debug info if needed
# supports default python.logging levels
# LOGLEVEL="INFO";
preFixup = phraseSolutions solutions unresholved;
# don't break the metadata...
meta = unresholved.meta;
}
);
}