nix dev-shell: Support structured attrs

Tested against https://github.com/NixOS/nixpkgs/pull/72074.

Fixes #3540.
This commit is contained in:
Eelco Dolstra 2020-04-30 14:39:26 +02:00
parent 2fcfc6c2c6
commit efe6c186ea
No known key found for this signature in database
GPG key ID: 8170B4726D7198DE
2 changed files with 33 additions and 13 deletions

View file

@ -1,4 +1,5 @@
set -e set -e
if [ -e .attrs.sh ]; then source .attrs.sh; fi
export IN_NIX_SHELL=impure export IN_NIX_SHELL=impure
export dontAddDisableDepTrack=1 export dontAddDisableDepTrack=1
if [[ -n $stdenv ]]; then if [[ -n $stdenv ]]; then

View file

@ -13,7 +13,8 @@ using namespace nix;
struct Var struct Var
{ {
bool exported; bool exported = true;
bool associative = false;
std::string value; // quoted string or array std::string value; // quoted string or array
}; };
@ -48,11 +49,17 @@ BuildEnvironment readEnvironment(const Path & path)
static std::string quotedStringRegex = static std::string quotedStringRegex =
R"re((?:\$?'(?:[^'\\]|\\[abeEfnrtv\\'"?])*'))re"; R"re((?:\$?'(?:[^'\\]|\\[abeEfnrtv\\'"?])*'))re";
static std::string arrayRegex = static std::string indexedArrayRegex =
R"re((?:\(( *\[[^\]]+\]="(?:[^"\\]|\\.)*")*\)))re"; R"re((?:\(( *\[[0-9]+]="(?:[^"\\]|\\.)*")**\)))re";
static std::regex varRegex( static std::regex varRegex(
"^(" + varNameRegex + ")=(" + simpleStringRegex + "|" + quotedStringRegex + "|" + arrayRegex + ")\n"); "^(" + varNameRegex + ")=(" + simpleStringRegex + "|" + quotedStringRegex + "|" + indexedArrayRegex + ")\n");
/* Note: we distinguish between an indexed and associative array
using the space before the closing parenthesis. Will
undoubtedly regret this some day. */
static std::regex assocArrayRegex(
"^(" + varNameRegex + ")=" + R"re((?:\(( *\[[^\]]+\]="(?:[^"\\]|\\.)*")* *\)))re" + "\n");
static std::regex functionRegex( static std::regex functionRegex(
"^" + varNameRegex + " \\(\\) *\n"); "^" + varNameRegex + " \\(\\) *\n");
@ -68,7 +75,12 @@ BuildEnvironment readEnvironment(const Path & path)
else if (std::regex_search(pos, file.cend(), match, varRegex)) { else if (std::regex_search(pos, file.cend(), match, varRegex)) {
pos = match[0].second; pos = match[0].second;
res.env.insert({match[1], Var { (bool) exported.count(match[1]), match[2] }}); res.env.insert({match[1], Var { .exported = exported.count(match[1]) > 0, .value = match[2] }});
}
else if (std::regex_search(pos, file.cend(), match, assocArrayRegex)) {
pos = match[0].second;
res.env.insert({match[1], Var { .associative = true, .value = match[2] }});
} }
else if (std::regex_search(pos, file.cend(), match, functionRegex)) { else if (std::regex_search(pos, file.cend(), match, functionRegex)) {
@ -92,8 +104,10 @@ const static std::string getEnvSh =
modified derivation with the same dependencies and nearly the same modified derivation with the same dependencies and nearly the same
initial environment variables, that just writes the resulting initial environment variables, that just writes the resulting
environment to a file and exits. */ environment to a file and exits. */
StorePath getDerivationEnvironment(ref<Store> store, Derivation drv) StorePath getDerivationEnvironment(ref<Store> store, const StorePath & drvPath)
{ {
auto drv = store->derivationFromPath(drvPath);
auto builder = baseNameOf(drv.builder); auto builder = baseNameOf(drv.builder);
if (builder != "bash") if (builder != "bash")
throw Error("'nix dev-shell' only works on derivations that use 'bash' as their builder"); throw Error("'nix dev-shell' only works on derivations that use 'bash' as their builder");
@ -108,11 +122,12 @@ StorePath getDerivationEnvironment(ref<Store> store, Derivation drv)
drv.env.erase("disallowedReferences"); drv.env.erase("disallowedReferences");
drv.env.erase("disallowedRequisites"); drv.env.erase("disallowedRequisites");
// FIXME: handle structured attrs
/* Rehash and write the derivation. FIXME: would be nice to use /* Rehash and write the derivation. FIXME: would be nice to use
'buildDerivation', but that's privileged. */ 'buildDerivation', but that's privileged. */
auto drvName = drv.env["name"] + "-env"; auto drvName = std::string(drvPath.name());
assert(hasSuffix(drvName, ".drv"));
drvName.resize(drvName.size() - 4);
drvName += "-env";
for (auto & output : drv.outputs) for (auto & output : drv.outputs)
drv.env.erase(output.first); drv.env.erase(output.first);
drv.env["out"] = ""; drv.env["out"] = "";
@ -161,11 +176,15 @@ struct Common : InstallableCommand, MixProfile
for (auto & i : buildEnvironment.env) { for (auto & i : buildEnvironment.env) {
if (!ignoreVars.count(i.first) && !hasPrefix(i.first, "BASH_")) { if (!ignoreVars.count(i.first) && !hasPrefix(i.first, "BASH_")) {
if (i.second.associative)
out << fmt("declare -A %s=(%s)\n", i.first, i.second.value);
else {
out << fmt("%s=%s\n", i.first, i.second.value); out << fmt("%s=%s\n", i.first, i.second.value);
if (i.second.exported) if (i.second.exported)
out << fmt("export %s\n", i.first); out << fmt("export %s\n", i.first);
} }
} }
}
out << "PATH=\"$PATH:$nix_saved_PATH\"\n"; out << "PATH=\"$PATH:$nix_saved_PATH\"\n";
@ -194,7 +213,7 @@ struct Common : InstallableCommand, MixProfile
auto & drvPath = *drvs.begin(); auto & drvPath = *drvs.begin();
return getDerivationEnvironment(store, store->derivationFromPath(drvPath)); return getDerivationEnvironment(store, drvPath);
} }
} }