Skip to content

Commit b5bd4cf

Browse files
committed
WIP: expose flake directory to nix fmt as FLAKE_DIR env var
This was discussed in #8034. I personally like `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. This is in a bit of a rough state. I left conflict markers (`<<<`) anywhere I did something in need of cleanup.
1 parent 1e822bd commit b5bd4cf

File tree

6 files changed

+78
-11
lines changed

6 files changed

+78
-11
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 `FLAKE_DIR` 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: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,28 @@
77

88
using namespace nix;
99

10+
//<<< where should this go?
11+
Environment getEnvironment()
12+
{
13+
Environment env;
14+
15+
for (char **envp = environ; *envp != NULL; ++envp) {
16+
std::string envStr = *envp;
17+
size_t equalsIndex = envStr.find("=");
18+
19+
if(equalsIndex == std::string::npos) {
20+
throw Error("invalid environment variable %s', must contain '='", envStr);
21+
}
22+
23+
std::string key = envStr.substr(0, equalsIndex);
24+
std::string value = envStr.substr(equalsIndex + 1);
25+
env[key] = value;
26+
}
27+
28+
return env;
29+
}
30+
//<<<
31+
1032
struct CmdFormatter : NixMultiCommand
1133
{
1234
CmdFormatter()
@@ -72,6 +94,12 @@ struct CmdFormatterRun : MixFormatter, MixJSON
7294
auto evalState = getEvalState();
7395
auto evalStore = getEvalStore();
7496

97+
auto flakeRef = parseFlakeRef(fetchSettings, ".", std::filesystem::current_path().string());
98+
auto flakeUrl = flakeRef.input.toURL();
99+
// TODO: <<< is there a more future-proof way to assert that this flake is on the file system? >>>
100+
assert(flakeUrl.scheme == "path" || flakeUrl.scheme == "git+file");
101+
auto flakeDir = flakeUrl.path;
102+
75103
auto installable_ = parseInstallable(store, ".");
76104
auto & installable = InstallableValue::require(*installable_);
77105
auto app = installable.toApp(*evalState).resolve(evalStore, store);
@@ -87,7 +115,12 @@ struct CmdFormatterRun : MixFormatter, MixJSON
87115
// we are about to exec out of this process without running C++ destructors.
88116
evalState->evalCaches.clear();
89117

90-
execProgramInStore(store, UseLookupPath::DontUse, app.program, programArgs);
118+
Environment env = getEnvironment();
119+
env["FLAKE_DIR"] = flakeDir;
120+
121+
execProgramInStore(store, UseLookupPath::DontUse, app.program, programArgs,
122+
"", // <<< is there a cleaner way of passing the default value?
123+
env);
91124
};
92125
};
93126

src/nix/run.cc

Lines changed: 30 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,39 @@ std::string chrootHelperName = "__run_in_chroot";
2728

2829
namespace nix {
2930

31+
//<<< code copied (and modified) from src/libstore/unix/build/derivation-builder.cc
32+
Strings toEnvp(Environment env)
33+
{
34+
/* Convert `env` to a list of strings suitable for `execve`'s `envp` argument. */
35+
Strings envStrs;
36+
for (auto & i : env) {
37+
envStrs.push_back(i.first + "=" + i.second);
38+
}
39+
40+
return envStrs;
41+
}
42+
//<<<
43+
3044
void execProgramInStore(ref<Store> store,
3145
UseLookupPath useLookupPath,
3246
const std::string & program,
3347
const Strings & args,
34-
std::optional<std::string_view> system)
48+
std::optional<std::string_view> system,
49+
std::optional<Environment> env)
3550
{
3651
logger->stop();
3752

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

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

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

5985
throw SysError("could not execute chroot helper");
6086
}
@@ -65,9 +91,9 @@ void execProgramInStore(ref<Store> store,
6591
#endif
6692

6793
if (useLookupPath == UseLookupPath::Use)
68-
execvp(program.c_str(), stringsToCharPtrs(args).data());
94+
execvpe(program.c_str(), stringsToCharPtrs(args).data(), envp);
6995
else
70-
execv(program.c_str(), stringsToCharPtrs(args).data());
96+
execve(program.c_str(), stringsToCharPtrs(args).data(), envp);
7197

7298
throw SysError("unable to execute '%s'", program);
7399
}

src/nix/run.hh

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,15 @@ enum struct UseLookupPath {
1010
DontUse
1111
};
1212

13+
//<<< code copied (and modified) from src/libstore/unix/build/derivation-builder.cc
14+
typedef std::map<std::string, std::string> Environment;
15+
//<<<
16+
1317
void execProgramInStore(ref<Store> store,
1418
UseLookupPath useLookupPath,
1519
const std::string & program,
1620
const Strings & args,
17-
std::optional<std::string_view> system = std::nullopt);
21+
std::optional<std::string_view> system = std::nullopt,
22+
std::optional<Environment> env = std::nullopt);
1823

1924
}

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)" = "FLAKE_DIR=$TEST_HOME Formatting(0):" ]]
39+
[[ "$(nix formatter run)" = "FLAKE_DIR=$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 "FLAKE_DIR=$TEST_HOME Formatting(2): ./file ./folder"
43+
nix formatter run ./file ./folder | grep "FLAKE_DIR=$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 "FLAKE_DIR=$FLAKE_DIR Formatting(${#}):" "${@}"

0 commit comments

Comments
 (0)