1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-06-24 22:11:15 +02:00

Merge pull request #13143 from jfly/issue-8034-nix-fmt

Expose flake directory to `nix fmt` as `PRJ_ROOT` env var
This commit is contained in:
Jörg Thalheim 2025-05-23 10:24:26 +02:00 committed by GitHub
commit 76358748da
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 88 additions and 13 deletions

View file

@ -8,6 +8,10 @@ Flags can be forwarded to the formatter by using `--` followed by the flags.
Any arguments will be forwarded to the formatter. Typically these are the files to format.
The environment variable `PRJ_ROOT` (according to [prj-spec](https://github.com/numtide/prj-spec))
will be set to the absolute path to the directory containing the closest parent `flake.nix`
relative to the current directory.
# Example

View file

@ -1,8 +1,10 @@
#include "nix/cmd/command.hh"
#include "nix/cmd/installable-flake.hh"
#include "nix/cmd/installable-value.hh"
#include "nix/expr/eval.hh"
#include "nix/store/local-fs-store.hh"
#include "nix/cmd/installable-derived-path.hh"
#include "nix/util/environment-variables.hh"
#include "run.hh"
using namespace nix;
@ -72,10 +74,14 @@ struct CmdFormatterRun : MixFormatter, MixJSON
auto evalState = getEvalState();
auto evalStore = getEvalStore();
auto installable_ = parseInstallable(store, ".");
auto installable_ = parseInstallable(store, ".").cast<InstallableFlake>();
auto & installable = InstallableValue::require(*installable_);
auto app = installable.toApp(*evalState).resolve(evalStore, store);
auto maybeFlakeDir = installable_->flakeRef.input.getSourcePath();
assert(maybeFlakeDir.has_value());
auto flakeDir = maybeFlakeDir.value();
Strings programArgs{app.program};
// Propagate arguments from the CLI
@ -83,11 +89,22 @@ struct CmdFormatterRun : MixFormatter, MixJSON
programArgs.push_back(i);
}
// Add the path to the flake as an environment variable. This enables formatters to format the entire flake even
// if run from a subdirectory.
StringMap env = getEnv();
env["PRJ_ROOT"] = flakeDir;
// Release our references to eval caches to ensure they are persisted to disk, because
// we are about to exec out of this process without running C++ destructors.
evalState->evalCaches.clear();
execProgramInStore(store, UseLookupPath::DontUse, app.program, programArgs);
execProgramInStore(
store,
UseLookupPath::DontUse,
app.program,
programArgs,
std::nullopt, // Use default system
env);
};
};

View file

@ -10,6 +10,7 @@
#include "nix/util/finally.hh"
#include "nix/util/source-accessor.hh"
#include "nix/expr/eval.hh"
#include "nix/util/util.hh"
#include <filesystem>
#ifdef __linux__
@ -19,6 +20,8 @@
#include <queue>
extern char ** environ __attribute__((weak));
namespace nix::fs { using namespace std::filesystem; }
using namespace nix;
@ -27,14 +30,37 @@ std::string chrootHelperName = "__run_in_chroot";
namespace nix {
/* Convert `env` to a list of strings suitable for `execve`'s `envp` argument. */
Strings toEnvp(StringMap env)
{
Strings envStrs;
for (auto & i : env) {
envStrs.push_back(i.first + "=" + i.second);
}
return envStrs;
}
void execProgramInStore(ref<Store> store,
UseLookupPath useLookupPath,
const std::string & program,
const Strings & args,
std::optional<std::string_view> system)
std::optional<std::string_view> system,
std::optional<StringMap> env)
{
logger->stop();
char **envp;
Strings envStrs;
std::vector<char *> envCharPtrs;
if (env.has_value()) {
envStrs = toEnvp(env.value());
envCharPtrs = stringsToCharPtrs(envStrs);
envp = envCharPtrs.data();
} else {
envp = environ;
}
restoreProcessContext();
/* If this is a diverted store (i.e. its "logical" location
@ -54,7 +80,7 @@ void execProgramInStore(ref<Store> store,
Strings helperArgs = { chrootHelperName, store->storeDir, store2->getRealStoreDir(), std::string(system.value_or("")), program };
for (auto & arg : args) helperArgs.push_back(arg);
execv(getSelfExe().value_or("nix").c_str(), stringsToCharPtrs(helperArgs).data());
execve(getSelfExe().value_or("nix").c_str(), stringsToCharPtrs(helperArgs).data(), envp);
throw SysError("could not execute chroot helper");
}
@ -64,10 +90,12 @@ void execProgramInStore(ref<Store> store,
linux::setPersonality(*system);
#endif
if (useLookupPath == UseLookupPath::Use)
if (useLookupPath == UseLookupPath::Use) {
// We have to set `environ` by hand because there is no `execvpe` on macOS.
environ = envp;
execvp(program.c_str(), stringsToCharPtrs(args).data());
else
execv(program.c_str(), stringsToCharPtrs(args).data());
} else
execve(program.c_str(), stringsToCharPtrs(args).data(), envp);
throw SysError("unable to execute '%s'", program);
}

View file

@ -14,6 +14,7 @@ void execProgramInStore(ref<Store> store,
UseLookupPath useLookupPath,
const std::string & program,
const Strings & args,
std::optional<std::string_view> system = std::nullopt);
std::optional<std::string_view> system = std::nullopt,
std::optional<StringMap> env = std::nullopt);
}

View file

@ -34,13 +34,38 @@ cat << EOF > flake.nix
}
EOF
mkdir subflake
cp ./simple.nix ./simple.builder.sh ./formatter.simple.sh "${config_nix}" "$TEST_HOME/subflake"
cat << EOF > subflake/flake.nix
{
outputs = _: {
formatter.$system =
with import ./config.nix;
mkDerivation {
name = "formatter";
buildCommand = ''
mkdir -p \$out/bin
echo "#! ${shell}" > \$out/bin/formatter
cat \${./formatter.simple.sh} >> \$out/bin/formatter
chmod +x \$out/bin/formatter
'';
};
};
}
EOF
# No arguments check
[[ "$(nix fmt)" = "Formatting(0):" ]]
[[ "$(nix formatter run)" = "Formatting(0):" ]]
[[ "$(nix fmt)" = "PRJ_ROOT=$TEST_HOME Formatting(0):" ]]
[[ "$(nix formatter run)" = "PRJ_ROOT=$TEST_HOME Formatting(0):" ]]
# Argument forwarding check
nix fmt ./file ./folder | grep 'Formatting(2): ./file ./folder'
nix formatter run ./file ./folder | grep 'Formatting(2): ./file ./folder'
nix fmt ./file ./folder | grep "PRJ_ROOT=$TEST_HOME Formatting(2): ./file ./folder"
nix formatter run ./file ./folder | grep "PRJ_ROOT=$TEST_HOME Formatting(2): ./file ./folder"
# test subflake
cd subflake
nix fmt ./file | grep "PRJ_ROOT=$TEST_HOME/subflake Formatting(1): ./file"
# Build checks
## Defaults to a ./result.

View file

@ -1,2 +1,2 @@
#!/usr/bin/env bash
echo "Formatting(${#}):" "${@}"
echo "PRJ_ROOT=$PRJ_ROOT Formatting(${#}):" "${@}"