Skip to content
12 changes: 12 additions & 0 deletions doc/manual/src/command-ref/nix-shell.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
[`--exclude` *regexp*]
[`--pure`]
[`--keep` *name*]
[`--no-out-link`]
[{`--out-link` | `-o`} *outlink*]
{{`--packages` | `-p`} {*packages* | *expressions*} … | [*path*]}

# Disambiguation
Expand Down Expand Up @@ -101,6 +103,16 @@ All options not listed here are passed to `nix-store
When a `--pure` shell is started, keep the listed environment
variables.

- [`--no-out-link`]{#opt-no-out-link}\
Do not create a symlink to the output path. This is the default.
Note that as a result the output does not become a root of the
garbage collector, and so might be deleted by `nix-store --gc`.

- [`--out-link`]{#opt-out-link} / `-o` *outlink*\
Create a symlink to the output path of the shell environment. The
output will then become a root of the garbage collector and will
not be deleted by `nix-store --gc`.

The following common options are supported:

# Environment variables
Expand Down
1 change: 1 addition & 0 deletions doc/manual/src/release-notes/rl-next.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
arguments will be ignored and the resulting derivation will have
`__impure` set to `true`, making it an impure derivation.

* `nix-shell` now accepts `--out-link` and `--no-out-link` flags.
35 changes: 26 additions & 9 deletions src/nix-build/nix-build.cc
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ static void main_nix_build(int argc, char * * argv)

AutoDelete tmpDir(createTempDir("", myName));

std::string outLink = "./result";
std::optional<std::string> outLink = runEnv ? std::nullopt : std::optional{"./result"};

// List of environment variables kept for --pure
std::set<std::string> keepVars{
Expand Down Expand Up @@ -155,7 +155,7 @@ static void main_nix_build(int argc, char * * argv)
; // obsolete

else if (*arg == "--no-out-link" || *arg == "--no-link")
outLink = (Path) tmpDir + "/result";
outLink = std::nullopt;

else if (*arg == "--attr" || *arg == "-A")
attrPaths.push_back(getArg(*arg, arg, end));
Expand Down Expand Up @@ -266,7 +266,8 @@ static void main_nix_build(int argc, char * * argv)
joined << "{...}@args: with import <nixpkgs> args; (pkgs.runCommandCC or pkgs.runCommand) \"shell\" { buildInputs = [ ";
for (const auto & i : left)
joined << '(' << i << ") ";
joined << "]; } \"\"";
joined << "]; } ";
joined << (outLink ? "\"touch $out\"" : "\"\"");
fromArgs = true;
left = {joined.str()};
} else if (!fromArgs) {
Expand Down Expand Up @@ -366,6 +367,14 @@ static void main_nix_build(int argc, char * * argv)
store->buildPaths(paths, buildMode, evalStore);
};

auto writeOutLink = [&](std::string_view prefix, const StorePath & outputPath, std::string_view outputName) {
if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>()) {
std::string symlink(prefix);
if (outputName != "out") symlink += "-" + outputName;
store2->addPermRoot(outputPath, absPath(symlink));
}
};

if (runEnv) {
if (drvs.size() != 1)
throw UsageError("nix-shell requires a single derivation");
Expand Down Expand Up @@ -431,6 +440,13 @@ static void main_nix_build(int argc, char * * argv)
pathsToCopy.insert(src);
}

if (outLink) {
pathsToBuild.push_back(DerivedPath::Built {
.drvPath = drvInfo.requireDrvPath(),
.outputs = drv.outputNames()
});
}

buildPaths(pathsToBuild);

if (dryRun) return;
Expand All @@ -446,6 +462,11 @@ static void main_nix_build(int argc, char * * argv)
drv = *resolvedDrv;
}

if (outLink) {
for (auto & [outputName, output] : drv.outputsAndOptPaths(*store))
if (output.second) writeOutLink(*outLink, *output.second, outputName);
}

// Set the environment.
auto env = getEnv();

Expand Down Expand Up @@ -610,7 +631,7 @@ static void main_nix_build(int argc, char * * argv)

for (auto & [drvPath, outputName] : pathsToBuildOrdered) {
auto & [counter, _wantedOutputs] = drvMap.at({drvPath});
std::string drvPrefix = outLink;
std::string drvPrefix = outLink.value_or((Path) tmpDir + "/result");
if (counter)
drvPrefix += fmt("-%d", counter + 1);

Expand All @@ -620,11 +641,7 @@ static void main_nix_build(int argc, char * * argv)
assert(maybeOutputPath);
auto outputPath = *maybeOutputPath;

if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>()) {
std::string symlink = drvPrefix;
if (outputName != "out") symlink += "-" + outputName;
store2->addPermRoot(outputPath, absPath(symlink));
}
writeOutLink(drvPrefix, outputPath, outputName);

outPaths.push_back(outputPath);
}
Expand Down
8 changes: 8 additions & 0 deletions tests/nix-shell.sh
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ chmod a+rx $TEST_ROOT/spaced\ \\\'\"shell.shebang.rb
output=$($TEST_ROOT/spaced\ \\\'\"shell.shebang.rb abc ruby)
[ "$output" = '-e load(ARGV.shift) -- '"$TEST_ROOT"'/spaced \'\''"shell.shebang.rb abc ruby' ]

# Test nix-shell --out-link -p creates gcroot
nix-shell --pure -p foo --out-link foo-root
[ -L foo-root ] && rm foo-root

# Test nix-shell --out-link -A creates gcroot
nix-shell --pure -A foo --out-link foo-root
[ -L foo-root ] && rm foo-root

# Test 'nix develop'.
nix develop -f "$shellDotNix" shellDrv -c bash -c '[[ -n $stdenv ]]'

Expand Down