Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nix-shell: Support --out-link #7248

Closed
wants to merge 10 commits into from
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