nix-build/nix-shell: Eliminate call to nix-instantiate / nix-store
Note that this removes the need for a derivation symlink, so the --drv-path and --add-drv-link flags now do nothing.
This commit is contained in:
		
							parent
							
								
									c94f3d5575
								
							
						
					
					
						commit
						4c9ff89c26
					
				
					 6 changed files with 449 additions and 511 deletions
				
			
		|  | @ -118,6 +118,8 @@ public: | ||||||
|     Expr * parseExprFromString(const string & s, const Path & basePath, StaticEnv & staticEnv); |     Expr * parseExprFromString(const string & s, const Path & basePath, StaticEnv & staticEnv); | ||||||
|     Expr * parseExprFromString(const string & s, const Path & basePath); |     Expr * parseExprFromString(const string & s, const Path & basePath); | ||||||
| 
 | 
 | ||||||
|  |     Expr * parseStdin(); | ||||||
|  | 
 | ||||||
|     /* Evaluate an expression read from the given file to normal
 |     /* Evaluate an expression read from the given file to normal
 | ||||||
|        form. */ |        form. */ | ||||||
|     void evalFile(const Path & path, Value & v); |     void evalFile(const Path & path, Value & v); | ||||||
|  |  | ||||||
|  | @ -601,6 +601,13 @@ Expr * EvalState::parseExprFromString(const string & s, const Path & basePath) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | Expr * EvalState::parseStdin() | ||||||
|  | { | ||||||
|  |     //Activity act(*logger, lvlTalkative, format("parsing standard input")); | ||||||
|  |     return parseExprFromString(drainFD(0), absPath(".")); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| void EvalState::addToSearchPath(const string & s) | void EvalState::addToSearchPath(const string & s) | ||||||
| { | { | ||||||
|     size_t pos = s.find('='); |     size_t pos = s.find('='); | ||||||
|  |  | ||||||
|  | @ -222,7 +222,14 @@ struct LegacyArgs : public MixCommonArgs | ||||||
| void parseCmdLine(int argc, char * * argv, | void parseCmdLine(int argc, char * * argv, | ||||||
|     std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg) |     std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg) | ||||||
| { | { | ||||||
|     LegacyArgs(baseNameOf(argv[0]), parseArg).parseCmdline(argvToStrings(argc, argv)); |     parseCmdLine(baseNameOf(argv[0]), argvToStrings(argc, argv), parseArg); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void parseCmdLine(const string & programName, const Strings & args, | ||||||
|  |     std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg) | ||||||
|  | { | ||||||
|  |     LegacyArgs(programName, parseArg).parseCmdline(args); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -25,6 +25,9 @@ void initNix(); | ||||||
| void parseCmdLine(int argc, char * * argv, | void parseCmdLine(int argc, char * * argv, | ||||||
|     std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg); |     std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg); | ||||||
| 
 | 
 | ||||||
|  | void parseCmdLine(const string & programName, const Strings & args, | ||||||
|  |     std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg); | ||||||
|  | 
 | ||||||
| void printVersion(const string & programName); | void printVersion(const string & programName); | ||||||
| 
 | 
 | ||||||
| /* Ugh.  No better place to put this. */ | /* Ugh.  No better place to put this. */ | ||||||
|  |  | ||||||
|  | @ -5,8 +5,6 @@ | ||||||
| #include <sstream> | #include <sstream> | ||||||
| #include <vector> | #include <vector> | ||||||
| 
 | 
 | ||||||
| #include <unistd.h> |  | ||||||
| 
 |  | ||||||
| #include "store-api.hh" | #include "store-api.hh" | ||||||
| #include "globals.hh" | #include "globals.hh" | ||||||
| #include "derivations.hh" | #include "derivations.hh" | ||||||
|  | @ -14,7 +12,10 @@ | ||||||
| #include "util.hh" | #include "util.hh" | ||||||
| #include "shared.hh" | #include "shared.hh" | ||||||
| #include "eval.hh" | #include "eval.hh" | ||||||
|  | #include "eval-inline.hh" | ||||||
| #include "get-drvs.hh" | #include "get-drvs.hh" | ||||||
|  | #include "common-opts.hh" | ||||||
|  | #include "attr-path.hh" | ||||||
| 
 | 
 | ||||||
| using namespace nix; | using namespace nix; | ||||||
| using namespace std::string_literals; | using namespace std::string_literals; | ||||||
|  | @ -65,510 +66,435 @@ std::vector<string> shellwords(const string & s) | ||||||
|     return res; |     return res; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void maybePrintExecError(ExecError & e) | void mainWrapped(int argc, char * * argv) | ||||||
| { | { | ||||||
|     if (WIFEXITED(e.status)) |     initNix(); | ||||||
|         throw Exit(WEXITSTATUS(e.status)); |     initGC(); | ||||||
|  | 
 | ||||||
|  |     auto dryRun = false; | ||||||
|  |     auto runEnv = std::regex_search(argv[0], std::regex("nix-shell$")); | ||||||
|  |     auto pure = false; | ||||||
|  |     auto fromArgs = false; | ||||||
|  |     auto packages = false; | ||||||
|  |     // Same condition as bash uses for interactive shells
 | ||||||
|  |     auto interactive = isatty(STDIN_FILENO) && isatty(STDERR_FILENO); | ||||||
|  |     Strings attrPaths; | ||||||
|  |     Strings left; | ||||||
|  |     Strings searchPath; | ||||||
|  |     std::map<string, string> autoArgs_; | ||||||
|  |     RepairFlag repair = NoRepair; | ||||||
|  |     Path gcRoot; | ||||||
|  |     BuildMode buildMode = bmNormal; | ||||||
|  |     bool readStdin = false; | ||||||
|  | 
 | ||||||
|  |     auto shell = getEnv("SHELL", "/bin/sh"); | ||||||
|  |     std::string envCommand; // interactive shell
 | ||||||
|  |     Strings envExclude; | ||||||
|  | 
 | ||||||
|  |     auto myName = runEnv ? "nix-shell" : "nix-build"; | ||||||
|  | 
 | ||||||
|  |     auto inShebang = false; | ||||||
|  |     std::string script; | ||||||
|  |     std::vector<string> savedArgs; | ||||||
|  | 
 | ||||||
|  |     AutoDelete tmpDir(createTempDir("", myName)); | ||||||
|  | 
 | ||||||
|  |     std::string outLink = "./result"; | ||||||
|  | 
 | ||||||
|  |     Strings 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
 | ||||||
|  |     // have a single argument, it's the name of an executable file, and it
 | ||||||
|  |     // starts with "#!".
 | ||||||
|  |     if (runEnv && argc > 1 && !std::regex_search(argv[1], std::regex("nix-shell"))) { | ||||||
|  |         script = argv[1]; | ||||||
|  |         if (access(script.c_str(), F_OK) == 0 && access(script.c_str(), X_OK) == 0) { | ||||||
|  |             auto lines = tokenizeString<Strings>(readFile(script), "\n"); | ||||||
|  |             if (std::regex_search(lines.front(), std::regex("^#!"))) { | ||||||
|  |                 lines.pop_front(); | ||||||
|  |                 inShebang = true; | ||||||
|  |                 for (int i = 2; i < argc; ++i) | ||||||
|  |                     savedArgs.push_back(argv[i]); | ||||||
|  |                 args.clear(); | ||||||
|  |                 for (auto line : lines) { | ||||||
|  |                     line = chomp(line); | ||||||
|  |                     std::smatch match; | ||||||
|  |                     if (std::regex_match(line, match, std::regex("^#!\\s*nix-shell (.*)$"))) | ||||||
|  |                         for (const auto & word : shellwords(match[1].str())) | ||||||
|  |                             args.push_back(word); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     parseCmdLine(myName, args, [&](Strings::iterator & arg, const Strings::iterator & end) { | ||||||
|  |         if (*arg == "--help") { | ||||||
|  |             deletePath(tmpDir); | ||||||
|  |             showManPage(myName); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         else if (*arg == "--version") | ||||||
|  |             printVersion(myName); | ||||||
|  | 
 | ||||||
|  |         else if (*arg == "--add-drv-link") | ||||||
|  |             ; // obsolete
 | ||||||
|  | 
 | ||||||
|  |         else if (*arg == "--no-out-link" || *arg == "--no-link") | ||||||
|  |             outLink = (Path) tmpDir + "/result"; | ||||||
|  | 
 | ||||||
|  |         else if (*arg == "--attr" || *arg == "-A") | ||||||
|  |             attrPaths.push_back(getArg(*arg, arg, end)); | ||||||
|  | 
 | ||||||
|  |         else if (*arg == "--drv-link") | ||||||
|  |             getArg(*arg, arg, end); // obsolete
 | ||||||
|  | 
 | ||||||
|  |         else if (*arg == "--out-link" || *arg == "-o") | ||||||
|  |             outLink = getArg(*arg, arg, end); | ||||||
|  | 
 | ||||||
|  |         else if (parseAutoArgs(arg, end, autoArgs_)) | ||||||
|  |             ; | ||||||
|  | 
 | ||||||
|  |         else if (parseSearchPathArg(arg, end, searchPath)) | ||||||
|  |             ; | ||||||
|  | 
 | ||||||
|  |         else if (*arg == "--add-root") | ||||||
|  |             gcRoot = getArg(*arg, arg, end); | ||||||
|  | 
 | ||||||
|  |         else if (*arg == "--dry-run") | ||||||
|  |             dryRun = true; | ||||||
|  | 
 | ||||||
|  |         else if (*arg == "--repair") { | ||||||
|  |             repair = Repair; | ||||||
|  |             buildMode = bmRepair; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         else if (*arg == "--run-env") // obsolete
 | ||||||
|  |             runEnv = true; | ||||||
|  | 
 | ||||||
|  |         else if (*arg == "--command" || *arg == "--run") { | ||||||
|  |             if (*arg == "--run") | ||||||
|  |                 interactive = false; | ||||||
|  |             envCommand = getArg(*arg, arg, end) + "\nexit"; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         else if (*arg == "--check") | ||||||
|  |             buildMode = bmCheck; | ||||||
|  | 
 | ||||||
|  |         else if (*arg == "--exclude") | ||||||
|  |             envExclude.push_back(getArg(*arg, arg, end)); | ||||||
|  | 
 | ||||||
|  |         else if (*arg == "--expr" || *arg == "-E") | ||||||
|  |             fromArgs = true; | ||||||
|  | 
 | ||||||
|  |         else if (*arg == "--pure") pure = true; | ||||||
|  |         else if (*arg == "--impure") pure = false; | ||||||
|  | 
 | ||||||
|  |         else if (*arg == "--packages" || *arg == "-p") | ||||||
|  |             packages = true; | ||||||
|  | 
 | ||||||
|  |         else if (inShebang && *arg == "-i") { | ||||||
|  |             auto interpreter = getArg(*arg, arg, end); | ||||||
|  |             interactive = false; | ||||||
|  |             auto execArgs = ""; | ||||||
|  | 
 | ||||||
|  |             auto shellEscape = [](const string & s) { | ||||||
|  |                 return "'" + std::regex_replace(s, std::regex("'"), "'\\''") + "'"; | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             // Überhack to support Perl. Perl examines the shebang and
 | ||||||
|  |             // executes it unless it contains the string "perl" or "indir",
 | ||||||
|  |             // or (undocumented) argv[0] does not contain "perl". Exploit
 | ||||||
|  |             // the latter by doing "exec -a".
 | ||||||
|  |             if (std::regex_search(interpreter, std::regex("perl"))) | ||||||
|  |                 execArgs = "-a PERL"; | ||||||
|  | 
 | ||||||
|  |             std::ostringstream joined; | ||||||
|  |             for (const auto & i : savedArgs) | ||||||
|  |                 joined << shellEscape(i) << ' '; | ||||||
|  | 
 | ||||||
|  |             if (std::regex_search(interpreter, std::regex("ruby"))) { | ||||||
|  |                 // Hack for Ruby. Ruby also examines the shebang. It tries to
 | ||||||
|  |                 // read the shebang to understand which packages to read from. Since
 | ||||||
|  |                 // this is handled via nix-shell -p, we wrap our ruby script execution
 | ||||||
|  |                 // in ruby -e 'load' which ignores the shebangs.
 | ||||||
|  |                 envCommand = (format("exec %1% %2% -e 'load(\"%3%\") -- %4%") % execArgs % interpreter % script % joined.str()).str(); | ||||||
|  |             } else { | ||||||
|  |                 envCommand = (format("exec %1% %2% %3% %4%") % execArgs % interpreter % script % joined.str()).str(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         else if (*arg == "-") | ||||||
|  |             readStdin = true; | ||||||
|  | 
 | ||||||
|  |         else if (*arg != "" && arg->at(0) == '-') | ||||||
|  |             return false; | ||||||
|  | 
 | ||||||
|  |         else | ||||||
|  |             left.push_back(*arg); | ||||||
|  | 
 | ||||||
|  |         return true; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     if (packages && fromArgs) | ||||||
|  |         throw UsageError("‘-p’ and ‘-E’ are mutually exclusive"); | ||||||
|  | 
 | ||||||
|  |     auto store = openStore(); | ||||||
|  | 
 | ||||||
|  |     EvalState state(searchPath, store); | ||||||
|  |     state.repair = repair; | ||||||
|  | 
 | ||||||
|  |     Bindings & autoArgs(*evalAutoArgs(state, autoArgs_)); | ||||||
|  | 
 | ||||||
|  |     if (packages) { | ||||||
|  |         std::ostringstream joined; | ||||||
|  |         joined << "with import <nixpkgs> { }; (pkgs.runCommandCC or pkgs.runCommand) \"shell\" { buildInputs = [ "; | ||||||
|  |         for (const auto & i : left) | ||||||
|  |             joined << '(' << i << ") "; | ||||||
|  |         joined << "]; } \"\""; | ||||||
|  |         fromArgs = true; | ||||||
|  |         left = {joined.str()}; | ||||||
|  |     } else if (!fromArgs) { | ||||||
|  |         if (left.empty() && runEnv && pathExists("shell.nix")) | ||||||
|  |             left = {"shell.nix"}; | ||||||
|  |         if (left.empty()) | ||||||
|  |             left = {"default.nix"}; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (runEnv) | ||||||
|  |         setenv("IN_NIX_SHELL", pure ? "pure" : "impure", 1); | ||||||
|  | 
 | ||||||
|  |     /* Parse the expressions. */ | ||||||
|  |     std::vector<Expr *> exprs; | ||||||
|  | 
 | ||||||
|  |     if (readStdin) | ||||||
|  |         exprs = {state.parseStdin()}; | ||||||
|     else |     else | ||||||
|         throw e; |         for (auto i : left) { | ||||||
|  |             if (fromArgs) | ||||||
|  |                 exprs.push_back(state.parseExprFromString(i, absPath("."))); | ||||||
|  |             else | ||||||
|  |                 /* If we're in a #! script, interpret filenames
 | ||||||
|  |                    relative to the script. */ | ||||||
|  |                 exprs.push_back(state.parseExprFromFile(resolveExprPath(lookupFileArg(state, | ||||||
|  |                     inShebang && !packages ? absPath(i, dirOf(script)) : i)))); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     /* Evaluate them into derivations. */ | ||||||
|  |     DrvInfos drvs; | ||||||
|  | 
 | ||||||
|  |     if (attrPaths.empty()) attrPaths = {""}; | ||||||
|  | 
 | ||||||
|  |     for (auto e : exprs) { | ||||||
|  |         Value vRoot; | ||||||
|  |         state.eval(e, vRoot); | ||||||
|  | 
 | ||||||
|  |         for (auto & i : attrPaths) { | ||||||
|  |             Value & v(*findAlongAttrPath(state, i, autoArgs, vRoot)); | ||||||
|  |             state.forceValue(v); | ||||||
|  |             getDerivations(state, v, "", autoArgs, drvs, false); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     auto buildPaths = [&](const PathSet & paths) { | ||||||
|  |         /* Note: we do this even when !printMissing to efficiently
 | ||||||
|  |            fetch binary cache data. */ | ||||||
|  |         unsigned long long downloadSize, narSize; | ||||||
|  |         PathSet willBuild, willSubstitute, unknown; | ||||||
|  |         store->queryMissing(paths, | ||||||
|  |             willBuild, willSubstitute, unknown, downloadSize, narSize); | ||||||
|  | 
 | ||||||
|  |         if (settings.printMissing) | ||||||
|  |             printMissing(ref<Store>(store), willBuild, willSubstitute, unknown, downloadSize, narSize); | ||||||
|  | 
 | ||||||
|  |         if (!dryRun) | ||||||
|  |             store->buildPaths(paths, buildMode); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     if (runEnv) { | ||||||
|  |         if (drvs.size() != 1) | ||||||
|  |             throw UsageError("nix-shell requires a single derivation"); | ||||||
|  | 
 | ||||||
|  |         auto & drvInfo = drvs.front(); | ||||||
|  |         auto drv = store->derivationFromPath(drvInfo.queryDrvPath()); | ||||||
|  | 
 | ||||||
|  |         PathSet pathsToBuild; | ||||||
|  | 
 | ||||||
|  |         /* Figure out what bash shell to use. If $NIX_BUILD_SHELL
 | ||||||
|  |            is not set, then build bashInteractive from | ||||||
|  |            <nixpkgs>. */ | ||||||
|  |         auto shell = getEnv("NIX_BUILD_SHELL", ""); | ||||||
|  | 
 | ||||||
|  |         if (shell == "") { | ||||||
|  | 
 | ||||||
|  |             try { | ||||||
|  |                 auto expr = state.parseExprFromString("(import <nixpkgs> {}).bashInteractive", absPath(".")); | ||||||
|  | 
 | ||||||
|  |                 Value v; | ||||||
|  |                 state.eval(expr, v); | ||||||
|  | 
 | ||||||
|  |                 auto drv = getDerivation(state, v, false); | ||||||
|  |                 if (!drv) | ||||||
|  |                     throw Error("the ‘bashInteractive’ attribute in <nixpkgs> did not evaluate to a derivation"); | ||||||
|  | 
 | ||||||
|  |                 pathsToBuild.insert(drv->queryDrvPath()); | ||||||
|  | 
 | ||||||
|  |                 shell = drv->queryOutPath() + "/bin/bash"; | ||||||
|  | 
 | ||||||
|  |             } catch (Error & e) { | ||||||
|  |                 printError("warning: %s; will use bash from your environment", e.what()); | ||||||
|  |                 shell = "bash"; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Build or fetch all dependencies of the derivation.
 | ||||||
|  |         for (const auto & input : drv.inputDrvs) | ||||||
|  |             if (std::all_of(envExclude.cbegin(), envExclude.cend(), [&](const string & exclude) { return !std::regex_search(input.first, std::regex(exclude)); })) | ||||||
|  |                 pathsToBuild.insert(input.first); | ||||||
|  |         for (const auto & src : drv.inputSrcs) | ||||||
|  |             pathsToBuild.insert(src); | ||||||
|  | 
 | ||||||
|  |         buildPaths(pathsToBuild); | ||||||
|  | 
 | ||||||
|  |         if (dryRun) return; | ||||||
|  | 
 | ||||||
|  |         // Set the environment.
 | ||||||
|  |         auto env = getEnv(); | ||||||
|  | 
 | ||||||
|  |         auto tmp = getEnv("TMPDIR", getEnv("XDG_RUNTIME_DIR", "/tmp")); | ||||||
|  | 
 | ||||||
|  |         if (pure) { | ||||||
|  |             std::set<string> keepVars{"HOME", "USER", "LOGNAME", "DISPLAY", "PATH", "TERM", "IN_NIX_SHELL", "TZ", "PAGER", "NIX_BUILD_SHELL", "SHLVL"}; | ||||||
|  |             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.
 | ||||||
|  |             env["__ETC_PROFILE_SOURCED"] = "1"; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         env["NIX_BUILD_TOP"] = env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] = env["TEMP"] = tmp; | ||||||
|  |         env["NIX_STORE"] = store->storeDir; | ||||||
|  |         env["NIX_BUILD_CORES"] = std::to_string(settings.buildCores); | ||||||
|  | 
 | ||||||
|  |         auto passAsFile = tokenizeString<StringSet>(get(drv.env, "passAsFile", "")); | ||||||
|  | 
 | ||||||
|  |         bool keepTmp = false; | ||||||
|  |         int fileNr = 0; | ||||||
|  | 
 | ||||||
|  |         for (auto & var : drv.env) | ||||||
|  |             if (passAsFile.count(var.first)) { | ||||||
|  |                 keepTmp = true; | ||||||
|  |                 string fn = ".attr-" + std::to_string(fileNr++); | ||||||
|  |                 Path p = (Path) tmpDir + "/" + fn; | ||||||
|  |                 writeFile(p, var.second); | ||||||
|  |                 env[var.first + "Path"] = p; | ||||||
|  |             } else | ||||||
|  |                 env[var.first] = var.second; | ||||||
|  | 
 | ||||||
|  |         restoreAffinity(); | ||||||
|  | 
 | ||||||
|  |         /* Run a shell using the derivation's environment.  For
 | ||||||
|  |            convenience, source $stdenv/setup to setup additional | ||||||
|  |            environment variables and shell functions.  Also don't | ||||||
|  |            lose the current $PATH directories. */ | ||||||
|  |         auto rcfile = (Path) tmpDir + "/rc"; | ||||||
|  |         writeFile(rcfile, fmt( | ||||||
|  |                 (keepTmp ? "" : "rm -rf '%1%'; "s) + | ||||||
|  |                 "[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source ~/.bashrc; " | ||||||
|  |                 "%2%" | ||||||
|  |                 "dontAddDisableDepTrack=1; " | ||||||
|  |                 "[ -e $stdenv/setup ] && source $stdenv/setup; " | ||||||
|  |                 "%3%" | ||||||
|  |                 "set +e; " | ||||||
|  |                 R"s([ -n "$PS1" ] && PS1='\n\[\033[1;32m\][nix-shell:\w]\$\[\033[0m\] '; )s" | ||||||
|  |                 "if [ \"$(type -t runHook)\" = function ]; then runHook shellHook; fi; " | ||||||
|  |                 "unset NIX_ENFORCE_PURITY; " | ||||||
|  |                 "unset NIX_INDENT_MAKE; " | ||||||
|  |                 "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)); | ||||||
|  | 
 | ||||||
|  |         Strings envStrs; | ||||||
|  |         for (auto & i : env) | ||||||
|  |             envStrs.push_back(i.first + "=" + i.second); | ||||||
|  | 
 | ||||||
|  |         auto args = interactive | ||||||
|  |             ? Strings{"bash", "--rcfile", rcfile} | ||||||
|  |             : Strings{"bash", rcfile}; | ||||||
|  | 
 | ||||||
|  |         auto envPtrs = stringsToCharPtrs(envStrs); | ||||||
|  | 
 | ||||||
|  |         environ = envPtrs.data(); | ||||||
|  | 
 | ||||||
|  |         auto argPtrs = stringsToCharPtrs(args); | ||||||
|  | 
 | ||||||
|  |         restoreSignals(); | ||||||
|  | 
 | ||||||
|  |         execvp(shell.c_str(), argPtrs.data()); | ||||||
|  | 
 | ||||||
|  |         throw SysError("executing shell ‘%s’", shell); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     else { | ||||||
|  | 
 | ||||||
|  |         PathSet pathsToBuild; | ||||||
|  | 
 | ||||||
|  |         std::map<Path, Path> drvPrefixes; | ||||||
|  |         std::map<Path, Path> resultSymlinks; | ||||||
|  |         std::vector<Path> outPaths; | ||||||
|  | 
 | ||||||
|  |         for (auto & drvInfo : drvs) { | ||||||
|  |             auto drvPath = drvInfo.queryDrvPath(); | ||||||
|  |             pathsToBuild.insert(drvPath); | ||||||
|  | 
 | ||||||
|  |             auto outputName = drvInfo.queryOutputName(); | ||||||
|  |             if (outputName == "") | ||||||
|  |                 throw Error("derivation ‘%s’ lacks an ‘outputName’ attribute", drvPath); | ||||||
|  | 
 | ||||||
|  |             pathsToBuild.insert(drvPath + (outputName != "out" ? "!" + outputName : "")); | ||||||
|  | 
 | ||||||
|  |             std::string drvPrefix; | ||||||
|  |             auto i = drvPrefixes.find(drvPath); | ||||||
|  |             if (i != drvPrefixes.end()) | ||||||
|  |                 drvPrefix = i->second; | ||||||
|  |             else { | ||||||
|  |                 drvPrefix = outLink; | ||||||
|  |                 if (drvPrefixes.size()) | ||||||
|  |                     drvPrefix += fmt("-%d", drvPrefixes.size() + 1); | ||||||
|  |                 drvPrefixes[drvPath] = drvPrefix; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             std::string symlink = drvPrefix; | ||||||
|  |             if (outputName != "out") symlink += "-" + outputName; | ||||||
|  | 
 | ||||||
|  |             resultSymlinks[symlink] = drvInfo.queryOutPath(); | ||||||
|  |             outPaths.push_back(drvInfo.queryOutPath()); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         buildPaths(pathsToBuild); | ||||||
|  | 
 | ||||||
|  |         if (dryRun) return; | ||||||
|  | 
 | ||||||
|  |         for (auto & symlink : resultSymlinks) | ||||||
|  |             if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>()) | ||||||
|  |                 store2->addPermRoot(symlink.second, absPath(symlink.first), true); | ||||||
|  | 
 | ||||||
|  |         for (auto & path : outPaths) | ||||||
|  |             std::cout << path << '\n'; | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int main(int argc, char ** argv) | int main(int argc, char * * argv) | ||||||
| { | { | ||||||
|     return handleExceptions(argv[0], [&]() { |     return handleExceptions(argv[0], [&]() { | ||||||
|         initNix(); |         return mainWrapped(argc, argv); | ||||||
|         initGC(); |  | ||||||
| 
 |  | ||||||
|         auto store = openStore(); |  | ||||||
|         auto dryRun = false; |  | ||||||
|         auto verbose = false; |  | ||||||
|         auto runEnv = std::regex_search(argv[0], std::regex("nix-shell$")); |  | ||||||
|         auto pure = false; |  | ||||||
|         auto fromArgs = false; |  | ||||||
|         auto packages = false; |  | ||||||
|         // Same condition as bash uses for interactive shells
 |  | ||||||
|         auto interactive = isatty(STDIN_FILENO) && isatty(STDERR_FILENO); |  | ||||||
| 
 |  | ||||||
|         Strings instArgs; |  | ||||||
|         Strings buildArgs; |  | ||||||
|         Strings exprs; |  | ||||||
|         Strings searchPath; |  | ||||||
| 
 |  | ||||||
|         auto shell = getEnv("SHELL", "/bin/sh"); |  | ||||||
|         std::string envCommand; // interactive shell
 |  | ||||||
|         Strings envExclude; |  | ||||||
| 
 |  | ||||||
|         auto myName = runEnv ? "nix-shell" : "nix-build"; |  | ||||||
| 
 |  | ||||||
|         auto inShebang = false; |  | ||||||
|         std::string script; |  | ||||||
|         std::vector<string> savedArgs; |  | ||||||
| 
 |  | ||||||
|         AutoDelete tmpDir(createTempDir("", myName)); |  | ||||||
| 
 |  | ||||||
|         std::string outLink = "./result"; |  | ||||||
|         auto drvLink = (Path) tmpDir + "/derivation"; |  | ||||||
| 
 |  | ||||||
|         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
 |  | ||||||
|         // have a single argument, it's the name of an executable file, and it
 |  | ||||||
|         // starts with "#!".
 |  | ||||||
|         if (runEnv && argc > 1 && !std::regex_search(argv[1], std::regex("nix-shell"))) { |  | ||||||
|             script = argv[1]; |  | ||||||
|             if (access(script.c_str(), F_OK) == 0 && access(script.c_str(), X_OK) == 0) { |  | ||||||
|                 auto lines = tokenizeString<Strings>(readFile(script), "\n"); |  | ||||||
|                 if (std::regex_search(lines.front(), std::regex("^#!"))) { |  | ||||||
|                     lines.pop_front(); |  | ||||||
|                     inShebang = true; |  | ||||||
|                     for (int i = 2; i < argc; ++i) |  | ||||||
|                         savedArgs.push_back(argv[i]); |  | ||||||
|                     args.clear(); |  | ||||||
|                     for (auto line : lines) { |  | ||||||
|                         line = chomp(line); |  | ||||||
|                         std::smatch match; |  | ||||||
|                         if (std::regex_match(line, match, std::regex("^#!\\s*nix-shell (.*)$"))) |  | ||||||
|                             for (const auto & word : shellwords(match[1].str())) |  | ||||||
|                                 args.push_back(word); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         for (size_t n = 0; n < args.size(); ++n) { |  | ||||||
|             auto arg = args[n]; |  | ||||||
| 
 |  | ||||||
|             if (arg == "--help") { |  | ||||||
|                 deletePath(tmpDir); |  | ||||||
|                 showManPage(myName); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             else if (arg == "--version") |  | ||||||
|                 printVersion(myName); |  | ||||||
| 
 |  | ||||||
|             else if (arg == "--add-drv-link") { |  | ||||||
|                 drvLink = "./derivation"; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             else if (arg == "--no-out-link" || arg == "--no-link") { |  | ||||||
|                 outLink = (Path) tmpDir + "/result"; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             else if (arg == "--drv-link") { |  | ||||||
|                 n++; |  | ||||||
|                 if (n >= args.size()) { |  | ||||||
|                     throw UsageError("--drv-link requires an argument"); |  | ||||||
|                 } |  | ||||||
|                 drvLink = args[n]; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             else if (arg == "--out-link" || arg == "-o") { |  | ||||||
|                 n++; |  | ||||||
|                 if (n >= args.size()) { |  | ||||||
|                     throw UsageError(format("%1% requires an argument") % arg); |  | ||||||
|                 } |  | ||||||
|                 outLink = args[n]; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             else if (arg == "--attr" || arg == "-A" || arg == "-I") { |  | ||||||
|                 n++; |  | ||||||
|                 if (n >= args.size()) { |  | ||||||
|                     throw UsageError(format("%1% requires an argument") % arg); |  | ||||||
|                 } |  | ||||||
|                 instArgs.push_back(arg); |  | ||||||
|                 instArgs.push_back(args[n]); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             else if (arg == "--arg" || arg == "--argstr") { |  | ||||||
|                 if (n + 2 >= args.size()) { |  | ||||||
|                     throw UsageError(format("%1% requires two arguments") % arg); |  | ||||||
|                 } |  | ||||||
|                 instArgs.push_back(arg); |  | ||||||
|                 instArgs.push_back(args[n + 1]); |  | ||||||
|                 instArgs.push_back(args[n + 2]); |  | ||||||
|                 n += 2; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             else if (arg == "--option") { |  | ||||||
|                 if (n + 2 >= args.size()) { |  | ||||||
|                     throw UsageError(format("%1% requires two arguments") % arg); |  | ||||||
|                 } |  | ||||||
|                 instArgs.push_back(arg); |  | ||||||
|                 instArgs.push_back(args[n + 1]); |  | ||||||
|                 instArgs.push_back(args[n + 2]); |  | ||||||
|                 buildArgs.push_back(arg); |  | ||||||
|                 buildArgs.push_back(args[n + 1]); |  | ||||||
|                 buildArgs.push_back(args[n + 2]); |  | ||||||
|                 settings.set(args[n + 1], args[n + 2]); |  | ||||||
|                 n += 2; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             else if (arg == "--max-jobs" || arg == "-j" || arg == "--max-silent-time" || arg == "--cores" || arg == "--timeout" || arg == "--add-root") { |  | ||||||
|                 n++; |  | ||||||
|                 if (n >= args.size()) { |  | ||||||
|                     throw UsageError(format("%1% requires an argument") % arg); |  | ||||||
|                 } |  | ||||||
|                 buildArgs.push_back(arg); |  | ||||||
|                 buildArgs.push_back(args[n]); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             else if (arg == "--dry-run") { |  | ||||||
|                 buildArgs.push_back("--dry-run"); |  | ||||||
|                 dryRun = true; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             else if (arg == "--show-trace") { |  | ||||||
|                 instArgs.push_back(arg); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             else if (arg == "-") { |  | ||||||
|                 exprs = Strings{"-"}; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             else if (arg == "--verbose" || (arg.size() >= 2 && arg.substr(0, 2) == "-v")) { |  | ||||||
|                 buildArgs.push_back(arg); |  | ||||||
|                 instArgs.push_back(arg); |  | ||||||
|                 verbose = true; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             else if (arg == "--quiet" || arg == "--repair") { |  | ||||||
|                 buildArgs.push_back(arg); |  | ||||||
|                 instArgs.push_back(arg); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             else if (arg == "--check") { |  | ||||||
|                 buildArgs.push_back(arg); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             else if (arg == "--run-env") { // obsolete
 |  | ||||||
|                 runEnv = true; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             else if (arg == "--command" || arg == "--run") { |  | ||||||
|                 n++; |  | ||||||
|                 if (n >= args.size()) { |  | ||||||
|                     throw UsageError(format("%1% requires an argument") % arg); |  | ||||||
|                 } |  | ||||||
|                 envCommand = args[n] + "\nexit"; |  | ||||||
|                 if (arg == "--run") |  | ||||||
|                     interactive = false; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             else if (arg == "--exclude") { |  | ||||||
|                 n++; |  | ||||||
|                 if (n >= args.size()) { |  | ||||||
|                     throw UsageError(format("%1% requires an argument") % arg); |  | ||||||
|                 } |  | ||||||
|                 envExclude.push_back(args[n]); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             else if (arg == "--pure") { pure = true; } |  | ||||||
|             else if (arg == "--impure") { pure = false; } |  | ||||||
| 
 |  | ||||||
|             else if (arg == "--expr" || arg == "-E") { |  | ||||||
|                 fromArgs = true; |  | ||||||
|                 instArgs.push_back("--expr"); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             else if (arg == "--packages" || arg == "-p") { |  | ||||||
|                 packages = true; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             else if (inShebang && arg == "-i") { |  | ||||||
|                 n++; |  | ||||||
|                 if (n >= args.size()) { |  | ||||||
|                     throw UsageError(format("%1% requires an argument") % arg); |  | ||||||
|                 } |  | ||||||
|                 interactive = false; |  | ||||||
|                 auto interpreter = args[n]; |  | ||||||
|                 auto execArgs = ""; |  | ||||||
| 
 |  | ||||||
|                 auto shellEscape = [](const string & s) { |  | ||||||
|                     return "'" + std::regex_replace(s, std::regex("'"), "'\\''") + "'"; |  | ||||||
|                 }; |  | ||||||
| 
 |  | ||||||
|                 // Überhack to support Perl. Perl examines the shebang and
 |  | ||||||
|                 // executes it unless it contains the string "perl" or "indir",
 |  | ||||||
|                 // or (undocumented) argv[0] does not contain "perl". Exploit
 |  | ||||||
|                 // the latter by doing "exec -a".
 |  | ||||||
|                 if (std::regex_search(interpreter, std::regex("perl"))) |  | ||||||
|                     execArgs = "-a PERL"; |  | ||||||
| 
 |  | ||||||
|                 std::ostringstream joined; |  | ||||||
|                 for (const auto & i : savedArgs) |  | ||||||
|                     joined << shellEscape(i) << ' '; |  | ||||||
| 
 |  | ||||||
|                 if (std::regex_search(interpreter, std::regex("ruby"))) { |  | ||||||
|                     // Hack for Ruby. Ruby also examines the shebang. It tries to
 |  | ||||||
|                     // read the shebang to understand which packages to read from. Since
 |  | ||||||
|                     // this is handled via nix-shell -p, we wrap our ruby script execution
 |  | ||||||
|                     // in ruby -e 'load' which ignores the shebangs.
 |  | ||||||
|                     envCommand = (format("exec %1% %2% -e 'load(\"%3%\") -- %4%") % execArgs % interpreter % script % joined.str()).str(); |  | ||||||
|                 } else { |  | ||||||
|                     envCommand = (format("exec %1% %2% %3% %4%") % execArgs % interpreter % script % joined.str()).str(); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             else if (!arg.empty() && arg[0] == '-') { |  | ||||||
|                 buildArgs.push_back(arg); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             else if (arg == "-Q" || arg == "--no-build-output") { |  | ||||||
|                 buildArgs.push_back(arg); |  | ||||||
|                 instArgs.push_back(arg); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             else { |  | ||||||
|                 exprs.push_back(arg); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         EvalState state(searchPath, store); |  | ||||||
| 
 |  | ||||||
|         if (packages && fromArgs) { |  | ||||||
|             throw UsageError("‘-p’ and ‘-E’ are mutually exclusive"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (packages) { |  | ||||||
|             instArgs.push_back("--expr"); |  | ||||||
|             std::ostringstream joined; |  | ||||||
|             joined << "with import <nixpkgs> { }; (pkgs.runCommandCC or pkgs.runCommand) \"shell\" { buildInputs = [ "; |  | ||||||
|             for (const auto & i : exprs) |  | ||||||
|                 joined << '(' << i << ") "; |  | ||||||
|             joined << "]; } \"\""; |  | ||||||
|             exprs = Strings{joined.str()}; |  | ||||||
|         } else if (!fromArgs) { |  | ||||||
|             if (exprs.empty() && runEnv && access("shell.nix", F_OK) == 0) |  | ||||||
|                 exprs.push_back("shell.nix"); |  | ||||||
|             if (exprs.empty()) |  | ||||||
|                 exprs.push_back("default.nix"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (runEnv) |  | ||||||
|             setenv("IN_NIX_SHELL", pure ? "pure" : "impure", 1); |  | ||||||
| 
 |  | ||||||
|         for (auto & expr : exprs) { |  | ||||||
|             // Instantiate.
 |  | ||||||
|             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)); |  | ||||||
| 
 |  | ||||||
|                 Strings instantiateArgs{"--add-root", drvLink, "--indirect"}; |  | ||||||
|                 for (const auto & arg : instArgs) |  | ||||||
|                     instantiateArgs.push_back(arg); |  | ||||||
|                 instantiateArgs.push_back(expr); |  | ||||||
|                 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); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (runEnv) { |  | ||||||
|                 if (drvPaths.size() != 1) |  | ||||||
|                     throw UsageError("a single derivation is required"); |  | ||||||
|                 auto drvPath = drvPaths[0]; |  | ||||||
|                 drvPath = drvPath.substr(0, drvPath.find_first_of('!')); |  | ||||||
|                 if (isLink(drvPath)) |  | ||||||
|                     drvPath = readLink(drvPath); |  | ||||||
|                 auto drv = store->derivationFromPath(drvPath); |  | ||||||
| 
 |  | ||||||
|                 // Build or fetch all dependencies of the derivation.
 |  | ||||||
|                 Strings nixStoreArgs{"-r", "--no-output", "--no-gc-warning"}; |  | ||||||
|                 for (const auto & arg : buildArgs) |  | ||||||
|                     nixStoreArgs.push_back(arg); |  | ||||||
|                 for (const auto & input : drv.inputDrvs) |  | ||||||
|                     if (std::all_of(envExclude.cbegin(), envExclude.cend(), [&](const string & exclude) { return !std::regex_search(input.first, std::regex(exclude)); })) |  | ||||||
|                         nixStoreArgs.push_back(input.first); |  | ||||||
|                 for (const auto & src : drv.inputSrcs) |  | ||||||
|                     nixStoreArgs.push_back(src); |  | ||||||
| 
 |  | ||||||
|                 try { |  | ||||||
|                     runProgram(settings.nixBinDir + "/nix-store", false, nixStoreArgs); |  | ||||||
|                 } catch (ExecError & e) { |  | ||||||
|                     maybePrintExecError(e); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 if (dryRun) return; |  | ||||||
| 
 |  | ||||||
|                 // Set the environment.
 |  | ||||||
|                 auto env = getEnv(); |  | ||||||
| 
 |  | ||||||
|                 auto tmp = getEnv("TMPDIR", getEnv("XDG_RUNTIME_DIR", "/tmp")); |  | ||||||
| 
 |  | ||||||
|                 if (pure) { |  | ||||||
|                     std::set<string> keepVars{"HOME", "USER", "LOGNAME", "DISPLAY", "PATH", "TERM", "IN_NIX_SHELL", "TZ", "PAGER", "NIX_BUILD_SHELL", "SHLVL"}; |  | ||||||
|                     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.
 |  | ||||||
|                     env["__ETC_PROFILE_SOURCED"] = "1"; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 env["NIX_BUILD_TOP"] = env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] = env["TEMP"] = tmp; |  | ||||||
|                 env["NIX_STORE"] = store->storeDir; |  | ||||||
|                 env["NIX_BUILD_CORES"] = std::to_string(settings.buildCores); |  | ||||||
| 
 |  | ||||||
|                 auto passAsFile = tokenizeString<StringSet>(get(drv.env, "passAsFile", "")); |  | ||||||
| 
 |  | ||||||
|                 bool keepTmp = false; |  | ||||||
|                 int fileNr = 0; |  | ||||||
| 
 |  | ||||||
|                 for (auto & var : drv.env) |  | ||||||
|                     if (passAsFile.count(var.first)) { |  | ||||||
|                         keepTmp = true; |  | ||||||
|                         string fn = ".attr-" + std::to_string(fileNr++); |  | ||||||
|                         Path p = (Path) tmpDir + "/" + fn; |  | ||||||
|                         writeFile(p, var.second); |  | ||||||
|                         env[var.first + "Path"] = p; |  | ||||||
|                     } else |  | ||||||
|                         env[var.first] = var.second; |  | ||||||
| 
 |  | ||||||
|                 restoreAffinity(); |  | ||||||
| 
 |  | ||||||
|                 // Run a shell using the derivation's environment.  For
 |  | ||||||
|                 // convenience, source $stdenv/setup to setup additional
 |  | ||||||
|                 // environment variables and shell functions.  Also don't lose
 |  | ||||||
|                 // the current $PATH directories.
 |  | ||||||
|                 auto rcfile = (Path) tmpDir + "/rc"; |  | ||||||
|                 writeFile(rcfile, fmt( |  | ||||||
|                         (keepTmp ? "" : "rm -rf '%1%'; "s) + |  | ||||||
|                         "[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source ~/.bashrc; " |  | ||||||
|                         "%2%" |  | ||||||
|                         "dontAddDisableDepTrack=1; " |  | ||||||
|                         "[ -e $stdenv/setup ] && source $stdenv/setup; " |  | ||||||
|                         "%3%" |  | ||||||
|                         "set +e; " |  | ||||||
|                         R"s([ -n "$PS1" ] && PS1='\n\[\033[1;32m\][nix-shell:\w]\$\[\033[0m\] '; )s" |  | ||||||
|                         "if [ \"$(type -t runHook)\" = function ]; then runHook shellHook; fi; " |  | ||||||
|                         "unset NIX_ENFORCE_PURITY; " |  | ||||||
|                         "unset NIX_INDENT_MAKE; " |  | ||||||
|                         "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)); |  | ||||||
| 
 |  | ||||||
|                 Strings envStrs; |  | ||||||
|                 for (auto & i : env) |  | ||||||
|                     envStrs.push_back(i.first + "=" + i.second); |  | ||||||
| 
 |  | ||||||
|                 auto args = interactive |  | ||||||
|                     ? Strings{"bash", "--rcfile", rcfile} |  | ||||||
|                     : Strings{"bash", rcfile}; |  | ||||||
| 
 |  | ||||||
|                 auto envPtrs = stringsToCharPtrs(envStrs); |  | ||||||
| 
 |  | ||||||
|                 auto shell = getEnv("NIX_BUILD_SHELL", ""); |  | ||||||
| 
 |  | ||||||
|                 if (shell == "") { |  | ||||||
| 
 |  | ||||||
|                     try { |  | ||||||
| 
 |  | ||||||
|                         auto expr = state.parseExprFromString("(import <nixpkgs> {}).bashInteractive", absPath(".")); |  | ||||||
| 
 |  | ||||||
|                         Value v; |  | ||||||
|                         state.eval(expr, v); |  | ||||||
| 
 |  | ||||||
|                         auto drv = getDerivation(state, v, false); |  | ||||||
|                         if (!drv) |  | ||||||
|                             throw Error("the ‘bashInteractive’ attribute in <nixpkgs> did not evaluate to a derivation"); |  | ||||||
| 
 |  | ||||||
|                         auto drvPath = drv->queryDrvPath(); |  | ||||||
| 
 |  | ||||||
|                         unsigned long long downloadSize, narSize; |  | ||||||
|                         PathSet willBuild, willSubstitute, unknown; |  | ||||||
|                         store->queryMissing({drvPath}, |  | ||||||
|                             willBuild, willSubstitute, unknown, downloadSize, narSize); |  | ||||||
| 
 |  | ||||||
|                         if (settings.printMissing) |  | ||||||
|                             printMissing(ref<Store>(store), willBuild, willSubstitute, unknown, downloadSize, narSize); |  | ||||||
| 
 |  | ||||||
|                         store->buildPaths({drvPath}); |  | ||||||
| 
 |  | ||||||
|                         shell = drv->queryOutPath() + "/bin/bash"; |  | ||||||
|                         if (!pathExists(shell)) |  | ||||||
|                             throw Error("expected shell ‘%s’ to exist, but it doesn't", shell); |  | ||||||
| 
 |  | ||||||
|                     } catch (Error & e) { |  | ||||||
|                         printError("warning: %s; will use bash from your environment", e.what()); |  | ||||||
|                         shell = "bash"; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 environ = envPtrs.data(); |  | ||||||
| 
 |  | ||||||
|                 auto argPtrs = stringsToCharPtrs(args); |  | ||||||
| 
 |  | ||||||
|                 restoreSignals(); |  | ||||||
| 
 |  | ||||||
|                 execvp(shell.c_str(), argPtrs.data()); |  | ||||||
| 
 |  | ||||||
|                 throw SysError("executing shell ‘%s’", shell); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             // Ugly hackery to make "nix-build -A foo.all" produce symlinks
 |  | ||||||
|             // ./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.
 |  | ||||||
|             std::string prevDrvPath; |  | ||||||
|             Strings drvPaths2; |  | ||||||
|             for (const auto & drvPath : drvPaths) { |  | ||||||
|                 auto p = drvPath; |  | ||||||
|                 std::string output = "out"; |  | ||||||
|                 std::smatch match; |  | ||||||
|                 if (std::regex_match(drvPath, match, std::regex("(.*)!(.*)"))) { |  | ||||||
|                     p = match[1].str(); |  | ||||||
|                     output = match[2].str(); |  | ||||||
|                 } |  | ||||||
|                 auto target = readLink(p); |  | ||||||
|                 if (verbose) |  | ||||||
|                     std::cerr << "derivation is " << target << '\n'; |  | ||||||
|                 if (target == prevDrvPath) { |  | ||||||
|                     auto last = drvPaths2.back(); |  | ||||||
|                     drvPaths2.pop_back(); |  | ||||||
|                     drvPaths2.push_back(last + "," + output); |  | ||||||
|                 } else { |  | ||||||
|                     drvPaths2.push_back(target + "!" + output); |  | ||||||
|                     prevDrvPath = target; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             // Build.
 |  | ||||||
|             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); |  | ||||||
| 
 |  | ||||||
|             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) |  | ||||||
|                 std::cout << readLink(outPath) << '\n'; |  | ||||||
|         } |  | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -17,13 +17,6 @@ | ||||||
| using namespace nix; | using namespace nix; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| static Expr * parseStdin(EvalState & state) |  | ||||||
| { |  | ||||||
|     //Activity act(*logger, lvlTalkative, format("parsing standard input"));
 |  | ||||||
|     return state.parseExprFromString(drainFD(0), absPath(".")); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static Path gcRoot; | static Path gcRoot; | ||||||
| static int rootNr = 0; | static int rootNr = 0; | ||||||
| static bool indirectRoot = false; | static bool indirectRoot = false; | ||||||
|  | @ -166,7 +159,7 @@ int main(int argc, char * * argv) | ||||||
| 
 | 
 | ||||||
|         Bindings & autoArgs(*evalAutoArgs(state, autoArgs_)); |         Bindings & autoArgs(*evalAutoArgs(state, autoArgs_)); | ||||||
| 
 | 
 | ||||||
|         if (attrPaths.empty()) attrPaths.push_back(""); |         if (attrPaths.empty()) attrPaths = {""}; | ||||||
| 
 | 
 | ||||||
|         if (findFile) { |         if (findFile) { | ||||||
|             for (auto & i : files) { |             for (auto & i : files) { | ||||||
|  | @ -178,7 +171,7 @@ int main(int argc, char * * argv) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (readStdin) { |         if (readStdin) { | ||||||
|             Expr * e = parseStdin(state); |             Expr * e = state.parseStdin(); | ||||||
|             processExpr(state, attrPaths, parseOnly, strict, autoArgs, |             processExpr(state, attrPaths, parseOnly, strict, autoArgs, | ||||||
|                 evalOnly, outputKind, xmlOutputSourceLocation, e); |                 evalOnly, outputKind, xmlOutputSourceLocation, e); | ||||||
|         } else if (files.empty() && !fromArgs) |         } else if (files.empty() && !fromArgs) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue