nix dev-shell: Improve environment handling

Only variables that were marked as exported are exported in the dev
shell. Also, we no longer try to parse the function section of the env
file, fixing

  $ nix dev-shell
  error: shell environment '/nix/store/h7ama3kahb8lypf4nvjx34z06g9ncw4h-nixops-1.7pre20190926.4c7acbb-env' has unexpected line '/^[a-z]?"""/ {'
This commit is contained in:
Eelco Dolstra 2019-09-27 17:01:25 +02:00
parent 15b888c9a5
commit 9b9de3a5e3
No known key found for this signature in database
GPG key ID: 8170B4726D7198DE

View file

@ -7,55 +7,76 @@
#include "affinity.hh" #include "affinity.hh"
#include "progress-bar.hh" #include "progress-bar.hh"
#include <regex>
using namespace nix; using namespace nix;
struct Var
{
bool exported;
std::string value; // quoted string or array
};
struct BuildEnvironment struct BuildEnvironment
{ {
// FIXME: figure out which vars should be exported. std::map<std::string, Var> env;
std::map<std::string, std::string> env; std::string bashFunctions;
std::map<std::string, std::string> functions;
}; };
BuildEnvironment readEnvironment(const Path & path) BuildEnvironment readEnvironment(const Path & path)
{ {
BuildEnvironment res; BuildEnvironment res;
auto lines = tokenizeString<Strings>(readFile(path), "\n"); std::set<std::string> exported;
auto getLine = auto file = readFile(path);
[&]() { //auto file = readFile("/tmp/x");
if (lines.empty())
throw Error("shell environment '%s' ends unexpectedly", path);
auto line = lines.front();
lines.pop_front();
return line;
};
while (!lines.empty()) { auto pos = file.cbegin();
auto line = getLine();
auto eq = line.find('='); static std::string varNameRegex =
if (eq != std::string::npos) { R"re((?:[a-zA-Z_][a-zA-Z0-9_]*))re";
std::string name(line, 0, eq);
std::string value(line, eq + 1); static std::regex declareRegex(
// FIXME: parse arrays "^declare -x (" + varNameRegex + ")" +
res.env.insert({name, value}); R"re((?:="((?:[^"\\]|\\.)*)")?\n)re");
static std::string simpleStringRegex =
R"re((?:[a-zA-Z0-9_/:\.\-1\+]*))re";
static std::string quotedStringRegex =
R"re((?:\$?'[^']*'))re";
static std::string arrayRegex =
R"re((?:\(( *\[[^\]]+\]="(?:[^"\\]|\\.)*")*\)))re";
static std::regex varRegex(
"^(" + varNameRegex + ")=(" + simpleStringRegex + "|" + quotedStringRegex + "|" + arrayRegex + ")\n");
static std::regex functionRegex(
"^" + varNameRegex + " \\(\\) *\n");
while (pos != file.end()) {
std::smatch match;
if (std::regex_search(pos, file.cend(), match, declareRegex)) {
pos = match[0].second;
exported.insert(match[1]);
} }
else if (hasSuffix(line, " () ")) { else if (std::regex_search(pos, file.cend(), match, varRegex)) {
std::string name(line, 0, line.size() - 4); pos = match[0].second;
// FIXME: validate name res.env.insert({match[1], Var { (bool) exported.count(match[1]), match[2] }});
auto l = getLine();
if (l != "{ ") throw Error("shell environment '%s' has unexpected line '%s'", path, l);
std::string body;
while ((l = getLine()) != "}") {
body += l;
body += '\n';
}
res.functions.insert({name, body});
} }
else throw Error("shell environment '%s' has unexpected line '%s'", path, line); else if (std::regex_search(pos, file.cend(), match, functionRegex)) {
res.bashFunctions = std::string(pos, file.cend());
break;
}
else throw Error("shell environment '%s' has unexpected line '%s'",
path, file.substr(pos - file.cbegin(), 60));
} }
return res; return res;
@ -72,7 +93,16 @@ Path getDerivationEnvironment(ref<Store> store, Derivation drv)
if (builder != "bash") if (builder != "bash")
throw Error("'nix shell' only works on derivations that use 'bash' as their builder"); throw Error("'nix shell' only works on derivations that use 'bash' as their builder");
drv.args = {"-c", "set -e; export IN_NIX_SHELL=impure; export dontAddDisableDepTrack=1; if [[ -n $stdenv ]]; then source $stdenv/setup; fi; set > $out"}; drv.args = {
"-c",
"set -e; "
"export IN_NIX_SHELL=impure; "
"export dontAddDisableDepTrack=1; "
"if [[ -n $stdenv ]]; then "
" source $stdenv/setup; "
"fi; "
"export > $out; "
"set >> $out "};
/* Remove derivation checks. */ /* Remove derivation checks. */
drv.env.erase("allowedReferences"); drv.env.erase("allowedReferences");
@ -146,18 +176,16 @@ struct Common : InstallableCommand, MixProfile
out << "nix_saved_PATH=\"$PATH\"\n"; out << "nix_saved_PATH=\"$PATH\"\n";
for (auto & i : buildEnvironment.env) { for (auto & i : buildEnvironment.env) {
// FIXME: shellEscape if (!ignoreVars.count(i.first) && !hasPrefix(i.first, "BASH_")) {
// FIXME: figure out what to export out << fmt("%s=%s\n", i.first, i.second.value);
// FIXME: handle arrays if (i.second.exported)
if (!ignoreVars.count(i.first) && !hasPrefix(i.first, "BASH_")) out << fmt("export %s\n", i.first);
out << fmt("export %s=%s\n", i.first, i.second); }
} }
out << "PATH=\"$PATH:$nix_saved_PATH\"\n"; out << "PATH=\"$PATH:$nix_saved_PATH\"\n";
for (auto & i : buildEnvironment.functions) { out << buildEnvironment.bashFunctions << "\n";
out << fmt("%s () {\n%s\n}\n", i.first, i.second);
}
// FIXME: set outputs // FIXME: set outputs