Skip to content

Commit 8b68410

Browse files
committed
Expose flake directory to nix fmt as FLAKE_DIR env var
This was discussed in NixOS#8034. I personally like `NIX_FLAKE_DIR`, 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.
1 parent 76a4d4c commit 8b68410

File tree

6 files changed

+57
-12
lines changed

6 files changed

+57
-12
lines changed

src/nix/formatter-run.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ Flags can be forwarded to the formatter by using `--` followed by the flags.
88

99
Any arguments will be forwarded to the formatter. Typically these are the files to format.
1010

11+
The environment variable `PRJ_ROOT` will be set to the absolute path to the
12+
directory containing the flake's `flake.nix`.
13+
1114

1215
# Example
1316

src/nix/formatter.cc

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
#include "nix/cmd/command.hh"
2+
#include "nix/cmd/installable-flake.hh"
23
#include "nix/cmd/installable-value.hh"
34
#include "nix/expr/eval.hh"
45
#include "nix/store/local-fs-store.hh"
56
#include "nix/cmd/installable-derived-path.hh"
7+
#include "nix/util/environment-variables.hh"
68
#include "run.hh"
79

810
using namespace nix;
@@ -72,22 +74,37 @@ struct CmdFormatterRun : MixFormatter, MixJSON
7274
auto evalState = getEvalState();
7375
auto evalStore = getEvalStore();
7476

75-
auto installable_ = parseInstallable(store, ".");
77+
auto installable_ = parseInstallable(store, ".").cast<InstallableFlake>();
7678
auto & installable = InstallableValue::require(*installable_);
7779
auto app = installable.toApp(*evalState).resolve(evalStore, store);
7880

81+
auto maybeFlakeDir = installable_->flakeRef.input.getSourcePath();
82+
assert(maybeFlakeDir.has_value());
83+
auto flakeDir = maybeFlakeDir.value();
84+
7985
Strings programArgs{app.program};
8086

8187
// Propagate arguments from the CLI
8288
for (auto & i : args) {
8389
programArgs.push_back(i);
8490
}
8591

92+
// Add the path to the flake as an environment variable. This enables formatters to format the entire flake even
93+
// if run from a subdirectory.
94+
StringMap env = getEnv();
95+
env["PRJ_ROOT"] = flakeDir;
96+
8697
// Release our references to eval caches to ensure they are persisted to disk, because
8798
// we are about to exec out of this process without running C++ destructors.
8899
evalState->evalCaches.clear();
89100

90-
execProgramInStore(store, UseLookupPath::DontUse, app.program, programArgs);
101+
execProgramInStore(
102+
store,
103+
UseLookupPath::DontUse,
104+
app.program,
105+
programArgs,
106+
std::nullopt, // Use default system
107+
env);
91108
};
92109
};
93110

src/nix/run.cc

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "nix/util/finally.hh"
1111
#include "nix/util/source-accessor.hh"
1212
#include "nix/expr/eval.hh"
13+
#include "nix/util/util.hh"
1314
#include <filesystem>
1415

1516
#ifdef __linux__
@@ -27,14 +28,37 @@ std::string chrootHelperName = "__run_in_chroot";
2728

2829
namespace nix {
2930

31+
/* Convert `env` to a list of strings suitable for `execve`'s `envp` argument. */
32+
Strings toEnvp(StringMap env)
33+
{
34+
Strings envStrs;
35+
for (auto & i : env) {
36+
envStrs.push_back(i.first + "=" + i.second);
37+
}
38+
39+
return envStrs;
40+
}
41+
3042
void execProgramInStore(ref<Store> store,
3143
UseLookupPath useLookupPath,
3244
const std::string & program,
3345
const Strings & args,
34-
std::optional<std::string_view> system)
46+
std::optional<std::string_view> system,
47+
std::optional<StringMap> env)
3548
{
3649
logger->stop();
3750

51+
char **envp;
52+
Strings envStrs;
53+
std::vector<char *> envCharPtrs;
54+
if (env.has_value()) {
55+
envStrs = toEnvp(env.value());
56+
envCharPtrs = stringsToCharPtrs(envStrs);
57+
envp = envCharPtrs.data();
58+
} else {
59+
envp = environ;
60+
}
61+
3862
restoreProcessContext();
3963

4064
/* If this is a diverted store (i.e. its "logical" location
@@ -54,7 +78,7 @@ void execProgramInStore(ref<Store> store,
5478
Strings helperArgs = { chrootHelperName, store->storeDir, store2->getRealStoreDir(), std::string(system.value_or("")), program };
5579
for (auto & arg : args) helperArgs.push_back(arg);
5680

57-
execv(getSelfExe().value_or("nix").c_str(), stringsToCharPtrs(helperArgs).data());
81+
execve(getSelfExe().value_or("nix").c_str(), stringsToCharPtrs(helperArgs).data(), envp);
5882

5983
throw SysError("could not execute chroot helper");
6084
}
@@ -65,9 +89,9 @@ void execProgramInStore(ref<Store> store,
6589
#endif
6690

6791
if (useLookupPath == UseLookupPath::Use)
68-
execvp(program.c_str(), stringsToCharPtrs(args).data());
92+
execvpe(program.c_str(), stringsToCharPtrs(args).data(), envp);
6993
else
70-
execv(program.c_str(), stringsToCharPtrs(args).data());
94+
execve(program.c_str(), stringsToCharPtrs(args).data(), envp);
7195

7296
throw SysError("unable to execute '%s'", program);
7397
}

src/nix/run.hh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ void execProgramInStore(ref<Store> store,
1414
UseLookupPath useLookupPath,
1515
const std::string & program,
1616
const Strings & args,
17-
std::optional<std::string_view> system = std::nullopt);
17+
std::optional<std::string_view> system = std::nullopt,
18+
std::optional<StringMap> env = std::nullopt);
1819

1920
}

tests/functional/formatter.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,12 @@ cat << EOF > flake.nix
3535
EOF
3636

3737
# No arguments check
38-
[[ "$(nix fmt)" = "Formatting(0):" ]]
39-
[[ "$(nix formatter run)" = "Formatting(0):" ]]
38+
[[ "$(nix fmt)" = "PRJ_ROOT=$TEST_HOME Formatting(0):" ]]
39+
[[ "$(nix formatter run)" = "PRJ_ROOT=$TEST_HOME Formatting(0):" ]]
4040

4141
# Argument forwarding check
42-
nix fmt ./file ./folder | grep 'Formatting(2): ./file ./folder'
43-
nix formatter run ./file ./folder | grep 'Formatting(2): ./file ./folder'
42+
nix fmt ./file ./folder | grep "PRJ_ROOT=$TEST_HOME Formatting(2): ./file ./folder"
43+
nix formatter run ./file ./folder | grep "PRJ_ROOT=$TEST_HOME Formatting(2): ./file ./folder"
4444

4545
# Build checks
4646
## Defaults to a ./result.

tests/functional/formatter.simple.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
#!/usr/bin/env bash
2-
echo "Formatting(${#}):" "${@}"
2+
echo "PRJ_ROOT=$PRJ_ROOT Formatting(${#}):" "${@}"

0 commit comments

Comments
 (0)