Skip to content

Commit b4a86f6

Browse files
committed
WIP: expose flake directory to nix fmt as FLAKE_DIR env var
This was discussed in NixOS#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 b4a86f6

File tree

9 files changed

+66
-17
lines changed

9 files changed

+66
-17
lines changed

src/libstore/unix/build/derivation-builder.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "nix/store/builtins.hh"
88
#include "nix/store/builtins/buildenv.hh"
99
#include "nix/store/path-references.hh"
10+
#include "nix/util/environment-variables.hh"
1011
#include "nix/util/finally.hh"
1112
#include "nix/util/util.hh"
1213
#include "nix/util/archive.hh"
@@ -187,7 +188,6 @@ class DerivationBuilderImpl : public DerivationBuilder, DerivationBuilderParams
187188
typedef std::map<Path, ChrootPath> PathsInChroot; // maps target path to source path
188189
PathsInChroot pathsInChroot;
189190

190-
typedef std::map<std::string, std::string> Environment;
191191
Environment env;
192192

193193
/**

src/libutil/environment-variables.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ std::optional<std::string> getEnvNonEmpty(const std::string & key)
2121
return value;
2222
}
2323

24-
std::map<std::string, std::string> getEnv()
24+
Environment getEnv()
2525
{
26-
std::map<std::string, std::string> env;
26+
Environment env;
2727
for (size_t i = 0; environ[i]; ++i) {
2828
auto s = environ[i];
2929
auto eq = strchr(s, '=');
@@ -41,7 +41,7 @@ void clearEnv()
4141
unsetenv(name.first.c_str());
4242
}
4343

44-
void replaceEnv(const std::map<std::string, std::string> & newEnv)
44+
void replaceEnv(const Environment & newEnv)
4545
{
4646
clearEnv();
4747
for (auto & newEnvVar : newEnv)

src/libutil/include/nix/util/environment-variables.hh

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ namespace nix {
1515

1616
static constexpr auto environmentVariablesCategory = "Options that change environment variables";
1717

18+
typedef std::map<std::string, std::string> Environment;
19+
1820
/**
1921
* @return an environment variable.
2022
*/
@@ -34,7 +36,7 @@ std::optional<std::string> getEnvNonEmpty(const std::string & key);
3436
/**
3537
* Get the entire environment.
3638
*/
37-
std::map<std::string, std::string> getEnv();
39+
Environment getEnv();
3840

3941
#ifdef _WIN32
4042
/**
@@ -64,6 +66,6 @@ void clearEnv();
6466
/**
6567
* Replace the entire environment with the given one.
6668
*/
67-
void replaceEnv(const std::map<std::string, std::string> & newEnv);
69+
void replaceEnv(const Environment & newEnv);
6870

6971
}

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 `NIX_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: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "nix/expr/eval.hh"
44
#include "nix/store/local-fs-store.hh"
55
#include "nix/cmd/installable-derived-path.hh"
6+
#include "nix/util/environment-variables.hh"
67
#include "run.hh"
78

89
using namespace nix;
@@ -72,6 +73,11 @@ struct CmdFormatterRun : MixFormatter, MixJSON
7273
auto evalState = getEvalState();
7374
auto evalStore = getEvalStore();
7475

76+
auto flakeRef = parseFlakeRef(fetchSettings, ".", std::filesystem::current_path().string());
77+
auto maybeFlakeDir = flakeRef.input.getSourcePath();
78+
assert(maybeFlakeDir.has_value());
79+
auto flakeDir = maybeFlakeDir.value();
80+
7581
auto installable_ = parseInstallable(store, ".");
7682
auto & installable = InstallableValue::require(*installable_);
7783
auto app = installable.toApp(*evalState).resolve(evalStore, store);
@@ -87,7 +93,18 @@ struct CmdFormatterRun : MixFormatter, MixJSON
8793
// we are about to exec out of this process without running C++ destructors.
8894
evalState->evalCaches.clear();
8995

90-
execProgramInStore(store, UseLookupPath::DontUse, app.program, programArgs);
96+
// Add the path to the flake as an environment variable. This enables formatters to format the entire flake even
97+
// if run from a subdirectory.
98+
Environment env = getEnv();
99+
env["NIX_FLAKE_DIR"] = flakeDir;
100+
101+
execProgramInStore(
102+
store,
103+
UseLookupPath::DontUse,
104+
app.program,
105+
programArgs,
106+
"", // <<< is there a cleaner way of passing the default value?
107+
env);
91108
};
92109
};
93110

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

0 commit comments

Comments
 (0)