2020-03-30 17:14:17 +00:00
|
|
|
#include "eval.hh"
|
|
|
|
#include "command.hh"
|
|
|
|
#include "common-args.hh"
|
|
|
|
#include "shared.hh"
|
|
|
|
#include "store-api.hh"
|
2021-03-02 03:50:41 +00:00
|
|
|
#include "path-with-outputs.hh"
|
2020-03-30 17:14:17 +00:00
|
|
|
#include "derivations.hh"
|
|
|
|
#include "affinity.hh"
|
|
|
|
#include "progress-bar.hh"
|
|
|
|
|
|
|
|
#include <regex>
|
|
|
|
|
|
|
|
using namespace nix;
|
|
|
|
|
2020-10-26 13:24:25 +00:00
|
|
|
struct DevelopSettings : Config
|
|
|
|
{
|
|
|
|
Setting<std::string> bashPrompt{this, "", "bash-prompt",
|
|
|
|
"The bash prompt (`PS1`) in `nix develop` shells."};
|
|
|
|
|
|
|
|
Setting<std::string> bashPromptSuffix{this, "", "bash-prompt-suffix",
|
|
|
|
"Suffix appended to the `PS1` environment variable in `nix develop` shells."};
|
|
|
|
};
|
|
|
|
|
|
|
|
static DevelopSettings developSettings;
|
|
|
|
|
|
|
|
static GlobalConfig::Register rDevelopSettings(&developSettings);
|
|
|
|
|
2020-03-30 17:14:17 +00:00
|
|
|
struct Var
|
|
|
|
{
|
2020-04-30 12:39:26 +00:00
|
|
|
bool exported = true;
|
|
|
|
bool associative = false;
|
2020-08-28 16:16:03 +00:00
|
|
|
std::string quoted; // quoted string or array
|
2020-03-30 17:14:17 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct BuildEnvironment
|
|
|
|
{
|
|
|
|
std::map<std::string, Var> env;
|
|
|
|
std::string bashFunctions;
|
|
|
|
};
|
|
|
|
|
|
|
|
BuildEnvironment readEnvironment(const Path & path)
|
|
|
|
{
|
|
|
|
BuildEnvironment res;
|
|
|
|
|
|
|
|
std::set<std::string> exported;
|
|
|
|
|
|
|
|
debug("reading environment file '%s'", path);
|
|
|
|
|
|
|
|
auto file = readFile(path);
|
|
|
|
|
|
|
|
auto pos = file.cbegin();
|
|
|
|
|
|
|
|
static std::string varNameRegex =
|
|
|
|
R"re((?:[a-zA-Z_][a-zA-Z0-9_]*))re";
|
|
|
|
|
|
|
|
static std::string simpleStringRegex =
|
|
|
|
R"re((?:[a-zA-Z0-9_/:\.\-\+=]*))re";
|
|
|
|
|
2020-10-21 15:54:21 +00:00
|
|
|
static std::string dquotedStringRegex =
|
|
|
|
R"re((?:\$?"(?:[^"\\]|\\[$`"\\\n])*"))re";
|
|
|
|
|
|
|
|
static std::string squotedStringRegex =
|
2021-02-17 15:42:03 +00:00
|
|
|
R"re((?:\$?(?:'(?:[^'\\]|\\[abeEfnrtv\\'"?])*'|\\')+))re";
|
2020-03-30 17:14:17 +00:00
|
|
|
|
2020-04-30 12:39:26 +00:00
|
|
|
static std::string indexedArrayRegex =
|
2020-07-03 12:58:58 +00:00
|
|
|
R"re((?:\(( *\[[0-9]+\]="(?:[^"\\]|\\.)*")*\)))re";
|
2020-03-30 17:14:17 +00:00
|
|
|
|
2020-10-21 15:54:21 +00:00
|
|
|
static std::regex declareRegex(
|
|
|
|
"^declare -a?x (" + varNameRegex + ")(=(" +
|
|
|
|
dquotedStringRegex + "|" + indexedArrayRegex + "))?\n");
|
|
|
|
|
2020-03-30 17:14:17 +00:00
|
|
|
static std::regex varRegex(
|
2020-10-21 15:54:21 +00:00
|
|
|
"^(" + varNameRegex + ")=(" + simpleStringRegex + "|" + squotedStringRegex + "|" + indexedArrayRegex + ")\n");
|
2020-04-30 12:39:26 +00:00
|
|
|
|
|
|
|
/* 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");
|
2020-03-30 17:14:17 +00:00
|
|
|
|
|
|
|
static std::regex functionRegex(
|
|
|
|
"^" + varNameRegex + " \\(\\) *\n");
|
|
|
|
|
|
|
|
while (pos != file.end()) {
|
|
|
|
|
|
|
|
std::smatch match;
|
|
|
|
|
2020-07-29 05:57:20 +00:00
|
|
|
if (std::regex_search(pos, file.cend(), match, declareRegex, std::regex_constants::match_continuous)) {
|
2020-03-30 17:14:17 +00:00
|
|
|
pos = match[0].second;
|
|
|
|
exported.insert(match[1]);
|
|
|
|
}
|
|
|
|
|
2020-07-29 05:57:20 +00:00
|
|
|
else if (std::regex_search(pos, file.cend(), match, varRegex, std::regex_constants::match_continuous)) {
|
2020-03-30 17:14:17 +00:00
|
|
|
pos = match[0].second;
|
2020-08-28 16:16:03 +00:00
|
|
|
res.env.insert({match[1], Var { .exported = exported.count(match[1]) > 0, .quoted = match[2] }});
|
2020-04-30 12:39:26 +00:00
|
|
|
}
|
|
|
|
|
2020-07-29 05:57:20 +00:00
|
|
|
else if (std::regex_search(pos, file.cend(), match, assocArrayRegex, std::regex_constants::match_continuous)) {
|
2020-04-30 12:39:26 +00:00
|
|
|
pos = match[0].second;
|
2020-08-28 16:16:03 +00:00
|
|
|
res.env.insert({match[1], Var { .associative = true, .quoted = match[2] }});
|
2020-03-30 17:14:17 +00:00
|
|
|
}
|
|
|
|
|
2020-07-29 05:57:20 +00:00
|
|
|
else if (std::regex_search(pos, file.cend(), match, functionRegex, std::regex_constants::match_continuous)) {
|
2020-03-30 17:14:17 +00:00
|
|
|
res.bashFunctions = std::string(pos, file.cend());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
else throw Error("shell environment '%s' has unexpected line '%s'",
|
|
|
|
path, file.substr(pos - file.cbegin(), 60));
|
|
|
|
}
|
|
|
|
|
2020-08-28 16:16:03 +00:00
|
|
|
res.env.erase("__output");
|
|
|
|
|
2020-03-30 17:14:17 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2020-04-30 11:05:29 +00:00
|
|
|
const static std::string getEnvSh =
|
|
|
|
#include "get-env.sh.gen.hh"
|
|
|
|
;
|
|
|
|
|
2020-03-30 17:14:17 +00:00
|
|
|
/* Given an existing derivation, return the shell environment as
|
|
|
|
initialised by stdenv's setup script. We do this by building a
|
|
|
|
modified derivation with the same dependencies and nearly the same
|
|
|
|
initial environment variables, that just writes the resulting
|
|
|
|
environment to a file and exits. */
|
2020-04-30 12:39:26 +00:00
|
|
|
StorePath getDerivationEnvironment(ref<Store> store, const StorePath & drvPath)
|
2020-03-30 17:14:17 +00:00
|
|
|
{
|
2020-04-30 12:39:26 +00:00
|
|
|
auto drv = store->derivationFromPath(drvPath);
|
|
|
|
|
2020-03-30 17:14:17 +00:00
|
|
|
auto builder = baseNameOf(drv.builder);
|
|
|
|
if (builder != "bash")
|
2020-06-04 08:57:40 +00:00
|
|
|
throw Error("'nix develop' only works on derivations that use 'bash' as their builder");
|
2020-03-30 17:14:17 +00:00
|
|
|
|
2020-04-30 11:05:29 +00:00
|
|
|
auto getEnvShPath = store->addTextToStore("get-env.sh", getEnvSh, {});
|
|
|
|
|
|
|
|
drv.args = {store->printStorePath(getEnvShPath)};
|
2020-03-30 17:14:17 +00:00
|
|
|
|
|
|
|
/* Remove derivation checks. */
|
|
|
|
drv.env.erase("allowedReferences");
|
|
|
|
drv.env.erase("allowedRequisites");
|
|
|
|
drv.env.erase("disallowedReferences");
|
|
|
|
drv.env.erase("disallowedRequisites");
|
|
|
|
|
|
|
|
/* Rehash and write the derivation. FIXME: would be nice to use
|
|
|
|
'buildDerivation', but that's privileged. */
|
2020-08-09 20:32:35 +00:00
|
|
|
drv.name += "-env";
|
2020-08-28 16:16:03 +00:00
|
|
|
for (auto & output : drv.outputs) {
|
|
|
|
output.second = { .output = DerivationOutputInputAddressed { .path = StorePath::dummy } };
|
|
|
|
drv.env[output.first] = "";
|
|
|
|
}
|
2020-04-30 11:05:29 +00:00
|
|
|
drv.inputSrcs.insert(std::move(getEnvShPath));
|
2020-06-17 03:51:02 +00:00
|
|
|
Hash h = std::get<0>(hashDerivationModulo(*store, drv, true));
|
2020-03-30 17:14:17 +00:00
|
|
|
|
2020-08-28 16:16:03 +00:00
|
|
|
for (auto & output : drv.outputs) {
|
|
|
|
auto outPath = store->makeOutputPath(output.first, h, drv.name);
|
|
|
|
output.second = { .output = DerivationOutputInputAddressed { .path = outPath } };
|
|
|
|
drv.env[output.first] = store->printStorePath(outPath);
|
|
|
|
}
|
2020-03-30 17:14:17 +00:00
|
|
|
|
2020-08-28 16:16:03 +00:00
|
|
|
auto shellDrvPath = writeDerivation(*store, drv);
|
2020-03-30 17:14:17 +00:00
|
|
|
|
|
|
|
/* Build the derivation. */
|
2021-04-05 13:48:18 +00:00
|
|
|
store->buildPaths({DerivedPath::Built{shellDrvPath}});
|
2020-03-30 17:14:17 +00:00
|
|
|
|
2020-08-28 19:59:14 +00:00
|
|
|
for (auto & [_0, outputAndOptPath] : drv.outputsAndOptPaths(*store)) {
|
|
|
|
auto & [_1, optPath] = outputAndOptPath;
|
|
|
|
assert(optPath);
|
|
|
|
auto & outPath = *optPath;
|
2020-08-28 16:16:03 +00:00
|
|
|
assert(store->isValidPath(outPath));
|
|
|
|
auto outPathS = store->toRealPath(outPath);
|
|
|
|
if (lstat(outPathS).st_size)
|
|
|
|
return outPath;
|
|
|
|
}
|
2020-03-30 17:14:17 +00:00
|
|
|
|
2020-08-28 16:16:03 +00:00
|
|
|
throw Error("get-env.sh failed to produce an environment");
|
2020-03-30 17:14:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct Common : InstallableCommand, MixProfile
|
|
|
|
{
|
|
|
|
std::set<string> ignoreVars{
|
|
|
|
"BASHOPTS",
|
|
|
|
"EUID",
|
|
|
|
"HOME", // FIXME: don't ignore in pure mode?
|
2020-10-09 20:03:18 +00:00
|
|
|
"HOSTNAME",
|
2020-03-30 17:14:17 +00:00
|
|
|
"NIX_BUILD_TOP",
|
|
|
|
"NIX_ENFORCE_PURITY",
|
|
|
|
"NIX_LOG_FD",
|
|
|
|
"PPID",
|
|
|
|
"PWD",
|
|
|
|
"SHELLOPTS",
|
|
|
|
"SHLVL",
|
|
|
|
"SSL_CERT_FILE", // FIXME: only want to ignore /no-cert-file.crt
|
|
|
|
"TEMP",
|
|
|
|
"TEMPDIR",
|
|
|
|
"TERM",
|
|
|
|
"TMP",
|
|
|
|
"TMPDIR",
|
|
|
|
"TZ",
|
|
|
|
"UID",
|
|
|
|
};
|
|
|
|
|
2020-10-18 19:06:36 +00:00
|
|
|
std::vector<std::pair<std::string, std::string>> redirects;
|
|
|
|
|
|
|
|
Common()
|
|
|
|
{
|
|
|
|
addFlag({
|
|
|
|
.longName = "redirect",
|
2021-01-13 13:18:04 +00:00
|
|
|
.description = "Redirect a store path to a mutable location.",
|
2020-10-18 19:06:36 +00:00
|
|
|
.labels = {"installable", "outputs-dir"},
|
|
|
|
.handler = {[&](std::string installable, std::string outputsDir) {
|
|
|
|
redirects.push_back({installable, outputsDir});
|
|
|
|
}}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-08-28 16:16:03 +00:00
|
|
|
std::string makeRcScript(
|
2020-10-18 19:06:36 +00:00
|
|
|
ref<Store> store,
|
2020-08-28 16:16:03 +00:00
|
|
|
const BuildEnvironment & buildEnvironment,
|
|
|
|
const Path & outputsDir = absPath(".") + "/outputs")
|
2020-03-30 17:14:17 +00:00
|
|
|
{
|
2020-08-28 16:16:03 +00:00
|
|
|
std::ostringstream out;
|
|
|
|
|
2020-04-30 12:46:51 +00:00
|
|
|
out << "unset shellHook\n";
|
|
|
|
|
2020-03-30 17:14:17 +00:00
|
|
|
out << "nix_saved_PATH=\"$PATH\"\n";
|
|
|
|
|
|
|
|
for (auto & i : buildEnvironment.env) {
|
|
|
|
if (!ignoreVars.count(i.first) && !hasPrefix(i.first, "BASH_")) {
|
2020-04-30 12:39:26 +00:00
|
|
|
if (i.second.associative)
|
2020-08-28 16:16:03 +00:00
|
|
|
out << fmt("declare -A %s=(%s)\n", i.first, i.second.quoted);
|
2020-04-30 12:39:26 +00:00
|
|
|
else {
|
2020-08-28 16:16:03 +00:00
|
|
|
out << fmt("%s=%s\n", i.first, i.second.quoted);
|
2020-04-30 12:39:26 +00:00
|
|
|
if (i.second.exported)
|
|
|
|
out << fmt("export %s\n", i.first);
|
|
|
|
}
|
2020-03-30 17:14:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out << "PATH=\"$PATH:$nix_saved_PATH\"\n";
|
|
|
|
|
|
|
|
out << buildEnvironment.bashFunctions << "\n";
|
|
|
|
|
2021-01-29 03:55:18 +00:00
|
|
|
out << "export NIX_BUILD_TOP=\"$(mktemp -d -t nix-shell.XXXXXX)\"\n";
|
2020-03-30 17:14:17 +00:00
|
|
|
for (auto & i : {"TMP", "TMPDIR", "TEMP", "TEMPDIR"})
|
|
|
|
out << fmt("export %s=\"$NIX_BUILD_TOP\"\n", i);
|
|
|
|
|
|
|
|
out << "eval \"$shellHook\"\n";
|
2020-08-28 16:16:03 +00:00
|
|
|
|
2020-10-18 19:06:36 +00:00
|
|
|
auto script = out.str();
|
|
|
|
|
2020-08-28 16:16:03 +00:00
|
|
|
/* Substitute occurrences of output paths. */
|
|
|
|
auto outputs = buildEnvironment.env.find("outputs");
|
|
|
|
assert(outputs != buildEnvironment.env.end());
|
|
|
|
|
|
|
|
// FIXME: properly unquote 'outputs'.
|
|
|
|
StringMap rewrites;
|
|
|
|
for (auto & outputName : tokenizeString<std::vector<std::string>>(replaceStrings(outputs->second.quoted, "'", ""))) {
|
|
|
|
auto from = buildEnvironment.env.find(outputName);
|
|
|
|
assert(from != buildEnvironment.env.end());
|
|
|
|
// FIXME: unquote
|
|
|
|
rewrites.insert({from->second.quoted, outputsDir + "/" + outputName});
|
|
|
|
}
|
|
|
|
|
2020-10-18 19:06:36 +00:00
|
|
|
/* Substitute redirects. */
|
2020-10-19 10:03:15 +00:00
|
|
|
for (auto & [installable_, dir_] : redirects) {
|
|
|
|
auto dir = absPath(dir_);
|
|
|
|
auto installable = parseInstallable(store, installable_);
|
2021-05-17 06:45:08 +00:00
|
|
|
auto builtPaths = toStorePaths(
|
|
|
|
store, Realise::Nothing, OperateOn::Output, {installable});
|
|
|
|
for (auto & path: builtPaths) {
|
2020-10-18 19:06:36 +00:00
|
|
|
auto from = store->printStorePath(path);
|
|
|
|
if (script.find(from) == std::string::npos)
|
|
|
|
warn("'%s' (path '%s') is not used by this build environment", installable->what(), from);
|
|
|
|
else {
|
|
|
|
printInfo("redirecting '%s' to '%s'", from, dir);
|
|
|
|
rewrites.insert({from, dir});
|
|
|
|
}
|
2021-05-17 06:45:08 +00:00
|
|
|
}
|
2020-10-18 19:06:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return rewriteStrings(script, rewrites);
|
2020-03-30 17:14:17 +00:00
|
|
|
}
|
|
|
|
|
2019-05-02 19:10:13 +00:00
|
|
|
Strings getDefaultFlakeAttrPaths() override
|
|
|
|
{
|
Support non-x86_64-linux system types in flakes
A command like
$ nix run nixpkgs#hello
will now build the attribute 'packages.${system}.hello' rather than
'packages.hello'. Note that this does mean that the flake needs to
export an attribute for every system type it supports, and you can't
build on unsupported systems. So 'packages' typically looks like this:
packages = nixpkgs.lib.genAttrs ["x86_64-linux" "i686-linux"] (system: {
hello = ...;
});
The 'checks', 'defaultPackage', 'devShell', 'apps' and 'defaultApp'
outputs similarly are now attrsets that map system types to
derivations/apps. 'nix flake check' checks that the derivations for
all platforms evaluate correctly, but only builds the derivations in
'checks.${system}'.
Fixes #2861. (That issue also talks about access to ~/.config/nixpkgs
and --arg, but I think it's reasonable to say that flakes shouldn't
support those.)
The alternative to attribute selection is to pass the system type as
an argument to the flake's 'outputs' function, e.g. 'outputs = { self,
nixpkgs, system }: ...'. However, that approach would be at odds with
hermetic evaluation and make it impossible to enumerate the packages
provided by a flake.
2019-10-15 15:52:10 +00:00
|
|
|
return {"devShell." + settings.thisSystem.get(), "defaultPackage." + settings.thisSystem.get()};
|
2019-05-02 19:10:13 +00:00
|
|
|
}
|
2019-07-12 14:16:27 +00:00
|
|
|
|
2020-03-30 17:14:17 +00:00
|
|
|
StorePath getShellOutPath(ref<Store> store)
|
|
|
|
{
|
|
|
|
auto path = installable->getStorePath();
|
|
|
|
if (path && hasSuffix(path->to_string(), "-env"))
|
2020-06-16 20:20:18 +00:00
|
|
|
return *path;
|
2020-03-30 17:14:17 +00:00
|
|
|
else {
|
|
|
|
auto drvs = toDerivations(store, {installable});
|
|
|
|
|
|
|
|
if (drvs.size() != 1)
|
|
|
|
throw Error("'%s' needs to evaluate to a single derivation, but it evaluated to %d derivations",
|
|
|
|
installable->what(), drvs.size());
|
|
|
|
|
|
|
|
auto & drvPath = *drvs.begin();
|
|
|
|
|
2020-04-30 12:39:26 +00:00
|
|
|
return getDerivationEnvironment(store, drvPath);
|
2020-03-30 17:14:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-27 17:12:54 +00:00
|
|
|
std::pair<BuildEnvironment, std::string> getBuildEnvironment(ref<Store> store)
|
2020-03-30 17:14:17 +00:00
|
|
|
{
|
|
|
|
auto shellOutPath = getShellOutPath(store);
|
|
|
|
|
2020-04-27 17:12:54 +00:00
|
|
|
auto strPath = store->printStorePath(shellOutPath);
|
|
|
|
|
2020-03-30 17:14:17 +00:00
|
|
|
updateProfile(shellOutPath);
|
|
|
|
|
2020-04-27 17:12:54 +00:00
|
|
|
return {readEnvironment(strPath), strPath};
|
2020-03-30 17:14:17 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-06-04 08:57:40 +00:00
|
|
|
struct CmdDevelop : Common, MixEnvironment
|
2020-03-30 17:14:17 +00:00
|
|
|
{
|
|
|
|
std::vector<std::string> command;
|
2020-08-28 17:24:29 +00:00
|
|
|
std::optional<std::string> phase;
|
2020-03-30 17:14:17 +00:00
|
|
|
|
2020-06-04 08:57:40 +00:00
|
|
|
CmdDevelop()
|
2020-03-30 17:14:17 +00:00
|
|
|
{
|
2020-05-04 20:40:19 +00:00
|
|
|
addFlag({
|
|
|
|
.longName = "command",
|
|
|
|
.shortName = 'c',
|
2021-01-13 13:18:04 +00:00
|
|
|
.description = "Instead of starting an interactive shell, start the specified command and arguments.",
|
2020-05-04 20:40:19 +00:00
|
|
|
.labels = {"command", "args"},
|
|
|
|
.handler = {[&](std::vector<std::string> ss) {
|
2020-03-30 17:14:17 +00:00
|
|
|
if (ss.empty()) throw UsageError("--command requires at least one argument");
|
|
|
|
command = ss;
|
2020-05-04 20:40:19 +00:00
|
|
|
}}
|
|
|
|
});
|
2020-08-28 17:24:29 +00:00
|
|
|
|
|
|
|
addFlag({
|
|
|
|
.longName = "phase",
|
2021-01-13 13:18:04 +00:00
|
|
|
.description = "The stdenv phase to run (e.g. `build` or `configure`).",
|
2020-08-28 17:24:29 +00:00
|
|
|
.labels = {"phase-name"},
|
|
|
|
.handler = {&phase},
|
|
|
|
});
|
|
|
|
|
|
|
|
addFlag({
|
|
|
|
.longName = "configure",
|
2021-01-13 13:18:04 +00:00
|
|
|
.description = "Run the `configure` phase.",
|
2020-08-28 17:24:29 +00:00
|
|
|
.handler = {&phase, {"configure"}},
|
|
|
|
});
|
|
|
|
|
|
|
|
addFlag({
|
|
|
|
.longName = "build",
|
2021-01-13 13:18:04 +00:00
|
|
|
.description = "Run the `build` phase.",
|
2020-08-28 17:24:29 +00:00
|
|
|
.handler = {&phase, {"build"}},
|
|
|
|
});
|
|
|
|
|
|
|
|
addFlag({
|
|
|
|
.longName = "check",
|
2021-01-13 13:18:04 +00:00
|
|
|
.description = "Run the `check` phase.",
|
2020-08-28 17:24:29 +00:00
|
|
|
.handler = {&phase, {"check"}},
|
|
|
|
});
|
|
|
|
|
|
|
|
addFlag({
|
|
|
|
.longName = "install",
|
2021-01-13 13:18:04 +00:00
|
|
|
.description = "Run the `install` phase.",
|
2020-08-28 17:24:29 +00:00
|
|
|
.handler = {&phase, {"install"}},
|
|
|
|
});
|
|
|
|
|
|
|
|
addFlag({
|
|
|
|
.longName = "installcheck",
|
2021-01-13 13:18:04 +00:00
|
|
|
.description = "Run the `installcheck` phase.",
|
2020-08-28 17:24:29 +00:00
|
|
|
.handler = {&phase, {"installCheck"}},
|
|
|
|
});
|
2020-03-30 17:14:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string description() override
|
|
|
|
{
|
|
|
|
return "run a bash shell that provides the build environment of a derivation";
|
|
|
|
}
|
|
|
|
|
2020-12-08 20:52:22 +00:00
|
|
|
std::string doc() override
|
2020-03-30 17:14:17 +00:00
|
|
|
{
|
2020-12-08 20:52:22 +00:00
|
|
|
return
|
|
|
|
#include "develop.md"
|
|
|
|
;
|
2020-03-30 17:14:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void run(ref<Store> store) override
|
|
|
|
{
|
2020-04-27 17:12:54 +00:00
|
|
|
auto [buildEnvironment, gcroot] = getBuildEnvironment(store);
|
2020-03-30 17:14:17 +00:00
|
|
|
|
|
|
|
auto [rcFileFd, rcFilePath] = createTempFile("nix-shell");
|
|
|
|
|
2020-10-18 19:06:36 +00:00
|
|
|
auto script = makeRcScript(store, buildEnvironment);
|
2020-03-30 17:14:17 +00:00
|
|
|
|
2020-08-28 17:24:29 +00:00
|
|
|
if (verbosity >= lvlDebug)
|
|
|
|
script += "set -x\n";
|
2020-03-30 17:14:17 +00:00
|
|
|
|
2020-08-28 17:24:29 +00:00
|
|
|
script += fmt("rm -f '%s'\n", rcFilePath);
|
|
|
|
|
|
|
|
if (phase) {
|
|
|
|
if (!command.empty())
|
|
|
|
throw UsageError("you cannot use both '--command' and '--phase'");
|
|
|
|
// FIXME: foundMakefile is set by buildPhase, need to get
|
|
|
|
// rid of that.
|
|
|
|
script += fmt("foundMakefile=1\n");
|
|
|
|
script += fmt("runHook %1%Phase\n", *phase);
|
|
|
|
}
|
2020-03-30 17:14:17 +00:00
|
|
|
|
2020-08-28 17:24:29 +00:00
|
|
|
else if (!command.empty()) {
|
2020-03-30 17:14:17 +00:00
|
|
|
std::vector<std::string> args;
|
|
|
|
for (auto s : command)
|
|
|
|
args.push_back(shellEscape(s));
|
2020-08-28 16:16:03 +00:00
|
|
|
script += fmt("exec %s\n", concatStringsSep(" ", args));
|
2020-03-30 17:14:17 +00:00
|
|
|
}
|
|
|
|
|
2020-10-09 20:02:00 +00:00
|
|
|
else {
|
|
|
|
script += "[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source ~/.bashrc;\n";
|
2020-10-26 13:24:25 +00:00
|
|
|
if (developSettings.bashPrompt != "")
|
|
|
|
script += fmt("[ -n \"$PS1\" ] && PS1=%s;\n", shellEscape(developSettings.bashPrompt));
|
|
|
|
if (developSettings.bashPromptSuffix != "")
|
|
|
|
script += fmt("[ -n \"$PS1\" ] && PS1+=%s;\n", shellEscape(developSettings.bashPromptSuffix));
|
2020-10-09 20:02:00 +00:00
|
|
|
}
|
|
|
|
|
2020-08-28 16:16:03 +00:00
|
|
|
writeFull(rcFileFd.get(), script);
|
2020-03-30 17:14:17 +00:00
|
|
|
|
|
|
|
stopProgressBar();
|
|
|
|
|
|
|
|
setEnviron();
|
2020-04-27 19:18:26 +00:00
|
|
|
// prevent garbage collection until shell exits
|
2020-04-28 17:18:54 +00:00
|
|
|
setenv("NIX_GCROOT", gcroot.data(), 1);
|
2020-03-30 17:14:17 +00:00
|
|
|
|
2020-07-06 15:08:54 +00:00
|
|
|
Path shell = "bash";
|
|
|
|
|
|
|
|
try {
|
|
|
|
auto state = getEvalState();
|
|
|
|
|
|
|
|
auto bashInstallable = std::make_shared<InstallableFlake>(
|
2021-02-17 16:32:10 +00:00
|
|
|
this,
|
2020-07-06 15:08:54 +00:00
|
|
|
state,
|
2020-09-08 12:50:23 +00:00
|
|
|
installable->nixpkgsFlakeRef(),
|
2020-07-06 15:08:54 +00:00
|
|
|
Strings{"bashInteractive"},
|
|
|
|
Strings{"legacyPackages." + settings.thisSystem.get() + "."},
|
|
|
|
lockFlags);
|
|
|
|
|
2020-07-15 18:22:52 +00:00
|
|
|
shell = state->store->printStorePath(
|
|
|
|
toStorePath(state->store, Realise::Outputs, OperateOn::Output, bashInstallable)) + "/bin/bash";
|
2020-07-06 15:08:54 +00:00
|
|
|
} catch (Error &) {
|
|
|
|
ignoreException();
|
|
|
|
}
|
|
|
|
|
2020-11-07 14:00:22 +00:00
|
|
|
// If running a phase or single command, don't want an interactive shell running after
|
2020-10-20 16:48:07 +00:00
|
|
|
// Ctrl-C, so don't pass --rcfile
|
2020-11-07 14:00:22 +00:00
|
|
|
auto args = phase || !command.empty() ? Strings{std::string(baseNameOf(shell)), rcFilePath}
|
2020-10-20 16:48:07 +00:00
|
|
|
: Strings{std::string(baseNameOf(shell)), "--rcfile", rcFilePath};
|
2020-03-30 17:14:17 +00:00
|
|
|
|
2021-04-07 11:10:02 +00:00
|
|
|
restoreProcessContext();
|
2020-03-30 17:14:17 +00:00
|
|
|
|
|
|
|
execvp(shell.c_str(), stringsToCharPtrs(args).data());
|
|
|
|
|
|
|
|
throw SysError("executing shell '%s'", shell);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct CmdPrintDevEnv : Common
|
|
|
|
{
|
|
|
|
std::string description() override
|
|
|
|
{
|
|
|
|
return "print shell code that can be sourced by bash to reproduce the build environment of a derivation";
|
|
|
|
}
|
|
|
|
|
2020-12-08 20:52:22 +00:00
|
|
|
std::string doc() override
|
2020-03-30 17:14:17 +00:00
|
|
|
{
|
2020-12-08 20:52:22 +00:00
|
|
|
return
|
|
|
|
#include "print-dev-env.md"
|
|
|
|
;
|
2020-03-30 17:14:17 +00:00
|
|
|
}
|
|
|
|
|
2020-05-05 13:18:23 +00:00
|
|
|
Category category() override { return catUtility; }
|
|
|
|
|
2020-03-30 17:14:17 +00:00
|
|
|
void run(ref<Store> store) override
|
|
|
|
{
|
2020-04-27 17:12:54 +00:00
|
|
|
auto buildEnvironment = getBuildEnvironment(store).first;
|
2020-03-30 17:14:17 +00:00
|
|
|
|
|
|
|
stopProgressBar();
|
|
|
|
|
2020-10-18 19:06:36 +00:00
|
|
|
std::cout << makeRcScript(store, buildEnvironment);
|
2020-03-30 17:14:17 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-10-06 11:36:55 +00:00
|
|
|
static auto rCmdPrintDevEnv = registerCommand<CmdPrintDevEnv>("print-dev-env");
|
|
|
|
static auto rCmdDevelop = registerCommand<CmdDevelop>("develop");
|