diff --git a/doc/manual/rl-next/nix-env-shell.md b/doc/manual/rl-next/nix-env-shell.md new file mode 100644 index 000000000..b2344417a --- /dev/null +++ b/doc/manual/rl-next/nix-env-shell.md @@ -0,0 +1,12 @@ +--- +synopsis: "`nix env shell` is the new `nix shell`, and `nix shell` remains an accepted alias" +issues: 10504 +prs: 10807 +--- + +This is part of an effort to bring more structure to the CLI subcommands. + +`nix env` will be about the process environment. +Future commands may include `nix env run` and `nix env print-env`. + +It is also somewhat analogous to the [planned](https://github.com/NixOS/nix/issues/10504) `nix dev shell` (currently `nix develop`), which is less about environment variables, and more about running a development shell, which is a more powerful command, but also requires more setup. diff --git a/src/nix/env.cc b/src/nix/env.cc new file mode 100644 index 000000000..47efdf308 --- /dev/null +++ b/src/nix/env.cc @@ -0,0 +1,98 @@ +#include "command.hh" +#include "run.hh" +#include + +using namespace nix; + +struct CmdEnv : NixMultiCommand +{ + CmdEnv() : NixMultiCommand("env", RegisterCommand::getCommandsFor({"env"})) + { } + + std::string description() override + { + return "manipulate the process environment"; + } + + Category category() override { return catUtility; } +}; + +static auto rCmdEnv = registerCommand("env"); + +struct CmdShell : InstallablesCommand, MixEnvironment +{ + + using InstallablesCommand::run; + + std::vector command = { getEnv("SHELL").value_or("bash") }; + + CmdShell() + { + addFlag({ + .longName = "command", + .shortName = 'c', + .description = "Command and arguments to be executed, defaulting to `$SHELL`", + .labels = {"command", "args"}, + .handler = {[&](std::vector ss) { + if (ss.empty()) throw UsageError("--command requires at least one argument"); + command = ss; + }} + }); + } + + std::string description() override + { + return "run a shell in which the specified packages are available"; + } + + std::string doc() override + { + return + #include "shell.md" + ; + } + + void run(ref store, Installables && installables) override + { + auto outPaths = Installable::toStorePaths(getEvalStore(), store, Realise::Outputs, OperateOn::Output, installables); + + auto accessor = store->getFSAccessor(); + + std::unordered_set done; + std::queue todo; + for (auto & path : outPaths) todo.push(path); + + setEnviron(); + + std::vector pathAdditions; + + while (!todo.empty()) { + auto path = todo.front(); + todo.pop(); + if (!done.insert(path).second) continue; + + if (true) + pathAdditions.push_back(store->printStorePath(path) + "/bin"); + + auto propPath = accessor->resolveSymlinks( + CanonPath(store->printStorePath(path)) / "nix-support" / "propagated-user-env-packages"); + if (auto st = accessor->maybeLstat(propPath); st && st->type == SourceAccessor::tRegular) { + for (auto & p : tokenizeString(accessor->readFile(propPath))) + todo.push(store->parseStorePath(p)); + } + } + + auto unixPath = tokenizeString(getEnv("PATH").value_or(""), ":"); + unixPath.insert(unixPath.begin(), pathAdditions.begin(), pathAdditions.end()); + auto unixPathString = concatStringsSep(":", unixPath); + setEnv("PATH", unixPathString.c_str()); + + Strings args; + for (auto & arg : command) args.push_back(arg); + + runProgramInStore(store, UseLookupPath::Use, *command.begin(), args); + } +}; + +static auto rCmdShell = registerCommand2({"env", "shell"}); + diff --git a/src/nix/main.cc b/src/nix/main.cc index 3602402ae..94fd05fba 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -163,6 +163,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs, virtual RootArgs {"optimise-store", { AliasStatus::Deprecated, {"store", "optimise"}}}, {"ping-store", { AliasStatus::Deprecated, {"store", "ping"}}}, {"sign-paths", { AliasStatus::Deprecated, {"store", "sign"}}}, + {"shell", { AliasStatus::AcceptedShorthand, {"env", "shell"}}}, {"show-derivation", { AliasStatus::Deprecated, {"derivation", "show"}}}, {"show-config", { AliasStatus::Deprecated, {"config", "show"}}}, {"to-base16", { AliasStatus::Deprecated, {"hash", "to-base16"}}}, diff --git a/src/nix/run.cc b/src/nix/run.cc index cc999ddf4..7d3122470 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -71,83 +71,6 @@ void runProgramInStore(ref store, } -struct CmdShell : InstallablesCommand, MixEnvironment -{ - - using InstallablesCommand::run; - - std::vector command = { getEnv("SHELL").value_or("bash") }; - - CmdShell() - { - addFlag({ - .longName = "command", - .shortName = 'c', - .description = "Command and arguments to be executed, defaulting to `$SHELL`", - .labels = {"command", "args"}, - .handler = {[&](std::vector ss) { - if (ss.empty()) throw UsageError("--command requires at least one argument"); - command = ss; - }} - }); - } - - std::string description() override - { - return "run a shell in which the specified packages are available"; - } - - std::string doc() override - { - return - #include "shell.md" - ; - } - - void run(ref store, Installables && installables) override - { - auto outPaths = Installable::toStorePaths(getEvalStore(), store, Realise::Outputs, OperateOn::Output, installables); - - auto accessor = store->getFSAccessor(); - - std::unordered_set done; - std::queue todo; - for (auto & path : outPaths) todo.push(path); - - setEnviron(); - - std::vector pathAdditions; - - while (!todo.empty()) { - auto path = todo.front(); - todo.pop(); - if (!done.insert(path).second) continue; - - if (true) - pathAdditions.push_back(store->printStorePath(path) + "/bin"); - - auto propPath = accessor->resolveSymlinks( - CanonPath(store->printStorePath(path)) / "nix-support" / "propagated-user-env-packages"); - if (auto st = accessor->maybeLstat(propPath); st && st->type == SourceAccessor::tRegular) { - for (auto & p : tokenizeString(accessor->readFile(propPath))) - todo.push(store->parseStorePath(p)); - } - } - - auto unixPath = tokenizeString(getEnv("PATH").value_or(""), ":"); - unixPath.insert(unixPath.begin(), pathAdditions.begin(), pathAdditions.end()); - auto unixPathString = concatStringsSep(":", unixPath); - setEnv("PATH", unixPathString.c_str()); - - Strings args; - for (auto & arg : command) args.push_back(arg); - - runProgramInStore(store, UseLookupPath::Use, *command.begin(), args); - } -}; - -static auto rCmdShell = registerCommand("shell"); - struct CmdRun : InstallableValueCommand { using InstallableCommand::run; diff --git a/tests/functional/shell.sh b/tests/functional/shell.sh index e6bb4b161..1760eefff 100755 --- a/tests/functional/shell.sh +++ b/tests/functional/shell.sh @@ -5,6 +5,9 @@ source common.sh clearStore clearCache +# nix shell is an alias for nix env shell. We'll use the shorter form in the rest of the test. +nix env shell -f shell-hello.nix hello -c hello | grep 'Hello World' + nix shell -f shell-hello.nix hello -c hello | grep 'Hello World' nix shell -f shell-hello.nix hello -c hello NixOS | grep 'Hello NixOS'