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

Expose flake directory to nix fmt as PRJ_ROOT env var

This was discussed in https://github.com/NixOS/nix/issues/8034. I
personally like `PRJ_ROOT`, which hopefully avoids some ambiguity
around with subflakes.

I only implemented this for `nix fmt` because it doesn't let you point
at a flake not on your filesystem.

macOS compilation fixes
This commit is contained in:
Jeremy Fleischman 2025-05-07 11:24:50 -07:00 committed by Jörg Thalheim
parent 76a4d4c291
commit 17eb2e8400
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. 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 # Example

View file

@ -1,8 +1,10 @@
#include "nix/cmd/command.hh" #include "nix/cmd/command.hh"
#include "nix/cmd/installable-flake.hh"
#include "nix/cmd/installable-value.hh" #include "nix/cmd/installable-value.hh"
#include "nix/expr/eval.hh" #include "nix/expr/eval.hh"
#include "nix/store/local-fs-store.hh" #include "nix/store/local-fs-store.hh"
#include "nix/cmd/installable-derived-path.hh" #include "nix/cmd/installable-derived-path.hh"
#include "nix/util/environment-variables.hh"
#include "run.hh" #include "run.hh"
using namespace nix; using namespace nix;
@ -72,10 +74,14 @@ struct CmdFormatterRun : MixFormatter, MixJSON
auto evalState = getEvalState(); auto evalState = getEvalState();
auto evalStore = getEvalStore(); auto evalStore = getEvalStore();
auto installable_ = parseInstallable(store, "."); auto installable_ = parseInstallable(store, ".").cast<InstallableFlake>();
auto & installable = InstallableValue::require(*installable_); auto & installable = InstallableValue::require(*installable_);
auto app = installable.toApp(*evalState).resolve(evalStore, store); 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}; Strings programArgs{app.program};
// Propagate arguments from the CLI // Propagate arguments from the CLI
@ -83,11 +89,22 @@ struct CmdFormatterRun : MixFormatter, MixJSON
programArgs.push_back(i); 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 // 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. // we are about to exec out of this process without running C++ destructors.
evalState->evalCaches.clear(); 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/finally.hh"
#include "nix/util/source-accessor.hh" #include "nix/util/source-accessor.hh"
#include "nix/expr/eval.hh" #include "nix/expr/eval.hh"
#include "nix/util/util.hh"
#include <filesystem> #include <filesystem>
#ifdef __linux__ #ifdef __linux__
@ -19,6 +20,8 @@
#include <queue> #include <queue>
extern char ** environ __attribute__((weak));
namespace nix::fs { using namespace std::filesystem; } namespace nix::fs { using namespace std::filesystem; }
using namespace nix; using namespace nix;
@ -27,14 +30,37 @@ std::string chrootHelperName = "__run_in_chroot";
namespace nix { 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, void execProgramInStore(ref<Store> store,
UseLookupPath useLookupPath, UseLookupPath useLookupPath,
const std::string & program, const std::string & program,
const Strings & args, const Strings & args,
std::optional<std::string_view> system) std::optional<std::string_view> system,
std::optional<StringMap> env)
{ {
logger->stop(); 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(); restoreProcessContext();
/* If this is a diverted store (i.e. its "logical" location /* 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 }; Strings helperArgs = { chrootHelperName, store->storeDir, store2->getRealStoreDir(), std::string(system.value_or("")), program };
for (auto & arg : args) helperArgs.push_back(arg); 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"); throw SysError("could not execute chroot helper");
} }
@ -64,10 +90,12 @@ void execProgramInStore(ref<Store> store,
linux::setPersonality(*system); linux::setPersonality(*system);
#endif #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()); execvp(program.c_str(), stringsToCharPtrs(args).data());
else } else
execv(program.c_str(), stringsToCharPtrs(args).data()); execve(program.c_str(), stringsToCharPtrs(args).data(), envp);
throw SysError("unable to execute '%s'", program); throw SysError("unable to execute '%s'", program);
} }

View file

@ -14,6 +14,7 @@ void execProgramInStore(ref<Store> store,
UseLookupPath useLookupPath, UseLookupPath useLookupPath,
const std::string & program, const std::string & program,
const Strings & args, 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 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 # No arguments check
[[ "$(nix fmt)" = "Formatting(0):" ]] [[ "$(nix fmt)" = "PRJ_ROOT=$TEST_HOME Formatting(0):" ]]
[[ "$(nix formatter run)" = "Formatting(0):" ]] [[ "$(nix formatter run)" = "PRJ_ROOT=$TEST_HOME Formatting(0):" ]]
# Argument forwarding check # Argument forwarding check
nix fmt ./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 '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 # Build checks
## Defaults to a ./result. ## Defaults to a ./result.

View file

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