diff options
Diffstat (limited to 'src/nix-build')
-rwxr-xr-x | src/nix-build/nix-build.cc | 194 |
1 files changed, 111 insertions, 83 deletions
diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 50fcf16abdf0..08c6793577a4 100755 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -1,30 +1,32 @@ #include <cstring> +#include <fstream> +#include <iostream> #include <regex> -#include "util.hh" -#include <unistd.h> -#include "shared.hh" #include <sstream> #include <vector> -#include <iostream> -#include <fstream> + +#include <unistd.h> + #include "store-api.hh" #include "globals.hh" #include "derivations.hh" +#include "affinity.hh" +#include "util.hh" +#include "shared.hh" using namespace nix; -using std::stringstream; -extern char ** environ; +extern char * * environ; /* Recreate the effect of the perl shellwords function, breaking up a * string into arguments like a shell word, including escapes */ std::vector<string> shellwords(const string & s) { - auto whitespace = std::regex("^(\\s+).*"); + std::regex whitespace("^(\\s+).*"); auto begin = s.cbegin(); - auto res = std::vector<string>{}; - auto cur = stringstream{}; + std::vector<string> res; + std::string cur; enum state { sBegin, sQuote @@ -33,36 +35,41 @@ std::vector<string> shellwords(const string & s) auto it = begin; for (; it != s.cend(); ++it) { if (st == sBegin) { - auto match = std::smatch{}; + std::smatch match; if (regex_search(it, s.cend(), match, whitespace)) { - cur << string(begin, it); - res.push_back(cur.str()); - cur = stringstream{}; + cur.append(begin, it); + res.push_back(cur); + cur.clear(); it = match[1].second; begin = it; } } switch (*it) { case '"': - cur << string(begin, it); + cur.append(begin, it); begin = it + 1; st = st == sBegin ? sQuote : sBegin; break; case '\\': /* perl shellwords mostly just treats the next char as part of the string with no special processing */ - cur << string(begin, it); + cur.append(begin, it); begin = ++it; break; } } - cur << string(begin, it); - auto last = cur.str(); - if (!last.empty()) { - res.push_back(std::move(last)); - } + cur.append(begin, it); + if (!cur.empty()) res.push_back(cur); return res; } +static void maybePrintExecError(ExecError & e) +{ + if (WIFEXITED(e.status)) + throw Exit(WEXITSTATUS(e.status)); + else + throw e; +} + int main(int argc, char ** argv) { return handleExceptions(argv[0], [&]() { @@ -76,26 +83,26 @@ int main(int argc, char ** argv) auto packages = false; auto interactive = true; - auto instArgs = Strings{}; - auto buildArgs = Strings{}; - auto exprs = Strings{}; + Strings instArgs; + Strings buildArgs; + Strings exprs; auto shell = getEnv("SHELL", "/bin/sh"); - auto envCommand = string{}; // interactive shell - auto envExclude = Strings{}; + std::string envCommand; // interactive shell + Strings envExclude; auto myName = runEnv ? "nix-shell" : "nix-build"; auto inShebang = false; - auto script = string{}; - auto savedArgs = std::vector<string>{}; + std::string script; + std::vector<string> savedArgs; - auto tmpDir = AutoDelete{createTempDir("", myName)}; + AutoDelete tmpDir(createTempDir("", myName)); - auto outLink = string("./result"); + std::string outLink = "./result"; auto drvLink = (Path) tmpDir + "/derivation"; - auto args = std::vector<string>{}; + std::vector<string> args; for (int i = 1; i < argc; ++i) args.push_back(argv[i]); // Heuristic to see if we're invoked as a shebang script, namely, if we @@ -110,7 +117,7 @@ int main(int argc, char ** argv) inShebang = true; for (int i = 2; i < argc - 1; ++i) savedArgs.push_back(argv[i]); - args = std::vector<string>{}; + std::vector<string> args; for (auto line : lines) { line = chomp(line); std::smatch match; @@ -284,7 +291,7 @@ int main(int argc, char ** argv) execArgs = "-a PERL"; } - auto joined = stringstream{}; + std::ostringstream joined; for (const auto & i : savedArgs) joined << shellEscape(i) << ' '; @@ -320,7 +327,7 @@ int main(int argc, char ** argv) if (packages) { instArgs.push_back("--expr"); - auto joined = stringstream{}; + std::ostringstream joined; joined << "with import <nixpkgs> { }; runCommand \"shell\" { buildInputs = [ "; for (const auto & i : exprs) joined << '(' << i << ") "; @@ -338,19 +345,23 @@ int main(int argc, char ** argv) for (auto & expr : exprs) { // Instantiate. - auto drvPaths = std::vector<string>{}; + std::vector<string> drvPaths; if (!std::regex_match(expr, std::regex("^/.*\\.drv$"))) { // If we're in a #! script, interpret filenames relative to the // script. if (inShebang && !packages) expr = absPath(expr, dirOf(script)); - auto instantiateArgs = Strings{"--add-root", drvLink, "--indirect"}; + Strings instantiateArgs{"--add-root", drvLink, "--indirect"}; for (const auto & arg : instArgs) instantiateArgs.push_back(arg); instantiateArgs.push_back(expr); - auto instOutput = runProgram(settings.nixBinDir + "/nix-instantiate", false, instantiateArgs); - drvPaths = tokenizeString<std::vector<string>>(instOutput); + try { + auto instOutput = runProgram(settings.nixBinDir + "/nix-instantiate", false, instantiateArgs); + drvPaths = tokenizeString<std::vector<string>>(instOutput); + } catch (ExecError & e) { + maybePrintExecError(e); + } } else { drvPaths.push_back(expr); } @@ -365,7 +376,7 @@ int main(int argc, char ** argv) auto drv = store->derivationFromPath(drvPath); // Build or fetch all dependencies of the derivation. - auto nixStoreArgs = Strings{"-r", "--no-output", "--no-gc-warning"}; + Strings nixStoreArgs{"-r", "--no-output", "--no-gc-warning"}; for (const auto & arg : buildArgs) nixStoreArgs.push_back(arg); for (const auto & input : drv.inputDrvs) @@ -373,35 +384,36 @@ int main(int argc, char ** argv) nixStoreArgs.push_back(input.first); for (const auto & src : drv.inputSrcs) nixStoreArgs.push_back(src); - runProgram(settings.nixBinDir + "/nix-store", false, nixStoreArgs); + + try { + runProgram(settings.nixBinDir + "/nix-store", false, nixStoreArgs); + } catch (ExecError & e) { + maybePrintExecError(e); + } // Set the environment. + auto env = getEnv(); + auto tmp = getEnv("TMPDIR", getEnv("XDG_RUNTIME_DIR", "/tmp")); + if (pure) { - auto skippedEnv = std::vector<string>{"HOME", "USER", "LOGNAME", "DISPLAY", "PATH", "TERM", "IN_NIX_SHELL", "TZ", "PAGER", "NIX_BUILD_SHELL"}; - auto removed = std::vector<string>{}; - for (auto i = size_t{0}; environ[i]; ++i) { - auto eq = strchr(environ[i], '='); - if (!eq) - // invalid env, just keep going - continue; - auto name = string(environ[i], eq); - if (find(skippedEnv.begin(), skippedEnv.end(), name) == skippedEnv.end()) - removed.emplace_back(std::move(name)); - } - for (const auto & name : removed) - unsetenv(name.c_str()); + std::set<string> keepVars{"HOME", "USER", "LOGNAME", "DISPLAY", "PATH", "TERM", "IN_NIX_SHELL", "TZ", "PAGER", "NIX_BUILD_SHELL"}; + decltype(env) newEnv; + for (auto & i : env) + if (keepVars.count(i.first)) + newEnv.emplace(i); + env = newEnv; // NixOS hack: prevent /etc/bashrc from sourcing /etc/profile. - setenv("__ETC_PROFILE_SOURCED", "1", 1); + env["__ETC_PROFILE_SOURCED"] = "1"; } - setenv("NIX_BUILD_TOP", tmp.c_str(), 1); - setenv("TMPDIR", tmp.c_str(), 1); - setenv("TEMPDIR", tmp.c_str(), 1); - setenv("TMP", tmp.c_str(), 1); - setenv("TEMP", tmp.c_str(), 1); - setenv("NIX_STORE", store->storeDir.c_str(), 1); - for (const auto & env : drv.env) - setenv(env.first.c_str(), env.second.c_str(), 1); + + env["NIX_BUILD_TOP"] = env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] = env["TEMP"] = tmp; + env["NIX_STORE"] = store->storeDir; + + for (auto & var : drv.env) + env.emplace(var); + + restoreAffinity(); // Run a shell using the derivation's environment. For // convenience, source $stdenv/setup to setup additional @@ -410,7 +422,6 @@ int main(int argc, char ** argv) auto rcfile = (Path) tmpDir + "/rc"; writeFile(rcfile, (format( "rm -rf '%1%'; " - "unset BASH_ENV; " "[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source ~/.bashrc; " "%2%" "dontAddDisableDepTrack=1; " @@ -424,12 +435,26 @@ int main(int argc, char ** argv) "shopt -u nullglob; " "unset TZ; %4%" "%5%" - ) % (Path) tmpDir % (pure ? "" : "p=$PATH") % (pure ? "" : "PATH=$PATH:$p; unset p; ") % (getenv("TZ") ? (string("export TZ='") + getenv("TZ") + "'; ") : "") % envCommand).str()); - setenv("BASH_ENV", rcfile.c_str(), 1); - if (interactive) - execlp(getEnv("NIX_BUILD_SHELL", "bash").c_str(), "bash", "--rcfile", rcfile.c_str(), NULL); - else - execlp(getEnv("NIX_BUILD_SHELL", "bash").c_str(), "bash", rcfile.c_str(), NULL); + ) + % (Path) tmpDir + % (pure ? "" : "p=$PATH; ") + % (pure ? "" : "PATH=$PATH:$p; unset p; ") + % (getenv("TZ") ? (string("export TZ='") + getenv("TZ") + "'; ") : "") + % envCommand).str()); + + Strings envStrs; + for (auto & i : env) + envStrs.push_back(i.first + "=" + i.second); + + auto args = interactive + ? Strings{"bash", "--rcfile", rcfile} + : Strings{"bash", rcfile}; + + environ = stringsToCharPtrs(envStrs).data(); + + execvp(getEnv("NIX_BUILD_SHELL", "bash").c_str(), + stringsToCharPtrs(args).data()); + throw SysError("executing shell"); } @@ -437,11 +462,11 @@ int main(int argc, char ** argv) // ./result, ./result-dev, and so on, rather than ./result, // ./result-2-dev, and so on. This combines multiple derivation // paths into one "/nix/store/drv-path!out1,out2,..." argument. - auto prevDrvPath = string{}; - auto drvPaths2 = Strings{}; + std::string prevDrvPath; + Strings drvPaths2; for (const auto & drvPath : drvPaths) { auto p = drvPath; - auto output = string{"out"}; + std::string output = "out"; std::smatch match; if (std::regex_match(drvPath, match, std::regex("(.*)!(.*)"))) { p = match[1].str(); @@ -460,25 +485,28 @@ int main(int argc, char ** argv) } } // Build. - auto outPaths = Strings{}; - auto nixStoreArgs = Strings{"--add-root", outLink, "--indirect", "-r"}; + Strings outPaths; + Strings nixStoreArgs{"--add-root", outLink, "--indirect", "-r"}; for (const auto & arg : buildArgs) nixStoreArgs.push_back(arg); for (const auto & path : drvPaths2) nixStoreArgs.push_back(path); - auto nixStoreRes = runProgram(settings.nixBinDir + "/nix-store", false, nixStoreArgs); - for (const auto & outpath : tokenizeString<std::vector<string>>(nixStoreRes)) { - outPaths.push_back(chomp(outpath)); + + std::string nixStoreRes; + try { + nixStoreRes = runProgram(settings.nixBinDir + "/nix-store", false, nixStoreArgs); + } catch (ExecError & e) { + maybePrintExecError(e); } + for (const auto & outpath : tokenizeString<std::vector<string>>(nixStoreRes)) + outPaths.push_back(chomp(outpath)); + if (dryRun) continue; - for (const auto & outPath : outPaths) { - auto target = readLink(outPath); - std::cout << target << '\n'; - } + + for (const auto & outPath : outPaths) + std::cout << readLink(outPath) << '\n'; } - return; }); } - |