Skip to content

Commit

Permalink
nix dev-shell: Support structured attrs
Browse files Browse the repository at this point in the history
Tested against NixOS/nixpkgs#72074.

Fixes #3540.
  • Loading branch information
edolstra committed Apr 30, 2020
1 parent 2fcfc6c commit efe6c18
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 13 deletions.
1 change: 1 addition & 0 deletions src/nix/get-env.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
set -e
if [ -e .attrs.sh ]; then source .attrs.sh; fi
export IN_NIX_SHELL=impure
export dontAddDisableDepTrack=1
if [[ -n $stdenv ]]; then
Expand Down
45 changes: 32 additions & 13 deletions src/nix/shell.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ using namespace nix;

struct Var
{
bool exported;
bool exported = true;
bool associative = false;
std::string value; // quoted string or array
};

Expand Down Expand Up @@ -48,11 +49,17 @@ BuildEnvironment readEnvironment(const Path & path)
static std::string quotedStringRegex =
R"re((?:\$?'(?:[^'\\]|\\[abeEfnrtv\\'"?])*'))re";

static std::string arrayRegex =
R"re((?:\(( *\[[^\]]+\]="(?:[^"\\]|\\.)*")*\)))re";
static std::string indexedArrayRegex =
R"re((?:\(( *\[[0-9]+]="(?:[^"\\]|\\.)*")**\)))re";

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(
"^" + varNameRegex + " \\(\\) *\n");
Expand All @@ -68,7 +75,12 @@ BuildEnvironment readEnvironment(const Path & path)

else if (std::regex_search(pos, file.cend(), match, varRegex)) {
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)) {
Expand All @@ -92,8 +104,10 @@ const static std::string getEnvSh =
modified derivation with the same dependencies and nearly the same
initial environment variables, that just writes the resulting
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);
if (builder != "bash")
throw Error("'nix dev-shell' only works on derivations that use 'bash' as their builder");
Expand All @@ -108,11 +122,12 @@ StorePath getDerivationEnvironment(ref<Store> store, Derivation drv)
drv.env.erase("disallowedReferences");
drv.env.erase("disallowedRequisites");

// FIXME: handle structured attrs

/* Rehash and write the derivation. FIXME: would be nice to use
'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)
drv.env.erase(output.first);
drv.env["out"] = "";
Expand Down Expand Up @@ -161,9 +176,13 @@ struct Common : InstallableCommand, MixProfile

for (auto & i : buildEnvironment.env) {
if (!ignoreVars.count(i.first) && !hasPrefix(i.first, "BASH_")) {
out << fmt("%s=%s\n", i.first, i.second.value);
if (i.second.exported)
out << fmt("export %s\n", i.first);
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);
if (i.second.exported)
out << fmt("export %s\n", i.first);
}
}
}

Expand Down Expand Up @@ -194,7 +213,7 @@ struct Common : InstallableCommand, MixProfile

auto & drvPath = *drvs.begin();

return getDerivationEnvironment(store, store->derivationFromPath(drvPath));
return getDerivationEnvironment(store, drvPath);
}
}

Expand Down

0 comments on commit efe6c18

Please sign in to comment.