diff options
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | default.nix | 8 | ||||
-rw-r--r-- | nix-repl.cc | 137 |
3 files changed, 105 insertions, 44 deletions
diff --git a/README.md b/README.md index acdcd367fc9e..ac5ad98cf53c 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Here is a typical `nix-repl` session: nix-repl> map (x: x * x) xs [ 1 4 9 16 25 ] - nix-repl> :l <nixos> + nix-repl> :l <nixpkgs/nixos> Added 7 variables. nix-repl> config.services.dhcpd @@ -86,7 +86,7 @@ Here is a typical `nix-repl` session: Tab completion works on variables in scope and on attribute sets. For example: - $ nix-repl '<nixpkgs>' '<nixos>' + $ nix-repl '<nixpkgs>' '<nixpkgs/nixos>' Welcome to Nix version 1.6pre3215_2c1ecf8. Type :? for help. nix-repl> thunder<TAB> => thunderbird diff --git a/default.nix b/default.nix index 8690325e5289..919082981e1e 100644 --- a/default.nix +++ b/default.nix @@ -4,13 +4,13 @@ with import nixpkgs { inherit system; }; let nix = nixUnstable; in -runCommand "nix-repl" - { buildInputs = [ readline nix boehmgc ]; } +runCommandCC "nix-repl" + { buildInputs = [ pkgconfig readline nix boehmgc ]; } '' mkdir -p $out/bin - g++ -O3 -Wall -std=c++0x \ + g++ -O3 -Wall -std=c++14 \ -o $out/bin/nix-repl ${./nix-repl.cc} \ - -I${nix}/include/nix \ + $(pkg-config --cflags nix-main) \ -lnixformat -lnixutil -lnixstore -lnixexpr -lnixmain -lreadline -lgc \ -DNIX_VERSION=\"${(builtins.parseDrvName nix.name).version}\" '' diff --git a/nix-repl.cc b/nix-repl.cc index 1c878cd0e230..71790eb481a7 100644 --- a/nix-repl.cc +++ b/nix-repl.cc @@ -1,3 +1,5 @@ +#include <nix/config.h> + #include <iostream> #include <cstdlib> @@ -14,15 +16,22 @@ #include "get-drvs.hh" #include "derivations.hh" #include "affinity.hh" +#include "globals.hh" using namespace std; using namespace nix; +#define ESC_RED "\033[31m" +#define ESC_GRE "\033[32m" +#define ESC_YEL "\033[33m" +#define ESC_BLU "\033[34;1m" +#define ESC_MAG "\033[35m" +#define ESC_CYA "\033[36m" +#define ESC_END "\033[0m" string programId = "nix-repl"; const string historyFile = string(getenv("HOME")) + "/.nix-repl-history"; - struct NixRepl { string curDir; @@ -39,10 +48,11 @@ struct NixRepl StringSet completions; StringSet::iterator curCompletion; - NixRepl(const Strings & searchPath); + NixRepl(const Strings & searchPath, nix::ref<Store> store); void mainLoop(const Strings & files); void completePrefix(string prefix); bool getLine(string & input, const char * prompt); + Path getDerivationPath(Value & v); bool processLine(string line); void loadFile(const Path & path); void initEnv(); @@ -60,8 +70,35 @@ struct NixRepl void printHelp() { - std::cout << "Usage: nix-repl [--help|--version]"; - std::cout << std::endl; + cout << "Usage: nix-repl [--help] [--version] [-I path] paths...\n" + << "\n" + << "nix-repl is a simple read-eval-print loop (REPL) for the Nix package manager.\n" + << "\n" + << "Options:\n" + << " --help\n" + << " Prints out a summary of the command syntax and exits.\n" + << "\n" + << " --version\n" + << " Prints out the Nix version number on standard output and exits.\n" + << "\n" + << " -I path\n" + << " Add a path to the Nix expression search path. This option may be given\n" + << " multiple times. See the NIX_PATH environment variable for information on\n" + << " the semantics of the Nix search path. Paths added through -I take\n" + << " precedence over NIX_PATH.\n" + << "\n" + << " paths...\n" + << " A list of paths to files containing Nix expressions which nix-repl will\n" + << " load and add to its scope.\n" + << "\n" + << " A path surrounded in < and > will be looked up in the Nix expression search\n" + << " path, as in the Nix language itself.\n" + << "\n" + << " If an element of paths starts with http:// or https://, it is interpreted\n" + << " as the URL of a tarball that will be downloaded and unpacked to a temporary\n" + << " location. The tarball must include a single top-level directory containing\n" + << " at least a file named default.nix.\n" + << flush; } @@ -74,8 +111,8 @@ string removeWhitespace(string s) } -NixRepl::NixRepl(const Strings & searchPath) - : state(searchPath) +NixRepl::NixRepl(const Strings & searchPath, nix::ref<Store> store) + : state(searchPath, store) , staticEnv(false, &state.staticBaseEnv) { curDir = absPath("."); @@ -84,6 +121,7 @@ NixRepl::NixRepl(const Strings & searchPath) void NixRepl::mainLoop(const Strings & files) { + string error = ANSI_RED "error:" ANSI_NORMAL " "; std::cout << "Welcome to Nix version " << NIX_VERSION << ". Type :? for help." << std::endl << std::endl; for (auto & i : files) @@ -109,19 +147,19 @@ void NixRepl::mainLoop(const Strings & files) } try { - if (!processLine(input)) return; + if (!removeWhitespace(input).empty() && !processLine(input)) return; } catch (ParseError & e) { if (e.msg().find("unexpected $end") != std::string::npos) { // For parse errors on incomplete input, we continue waiting for the next line of // input without clearing the input so far. continue; } else { - printMsg(lvlError, "error: " + e.msg()); + printMsg(lvlError, format(error + "%1%%2%") % (settings.showTrace ? e.prefix() : "") % e.msg()); } } catch (Error & e) { - printMsg(lvlError, "error: " + e.msg()); + printMsg(lvlError, format(error + "%1%%2%") % (settings.showTrace ? e.prefix() : "") % e.msg()); } catch (Interrupted & e) { - printMsg(lvlError, "error: " + e.msg()); + printMsg(lvlError, format(error + "%1%%2%") % (settings.showTrace ? e.prefix() : "") % e.msg()); } // We handled the current input fully, so we should clear it and read brand new input. @@ -181,14 +219,13 @@ bool NixRepl::getLine(string & input, const char * prompt) char * s = readline(prompt); if (!s) return false; - string line = chomp(string(s)); - input.append(removeWhitespace(line)); + input.append(s); input.push_back('\n'); - free(s); - if (line != "") { - add_history(line.c_str()); + if (!removeWhitespace(s).empty()) { + add_history(s); append_history(1, 0); } + free(s); } _isInterrupted = 0; @@ -261,7 +298,7 @@ static int runProgram(const string & program, const Strings & args) _exit(1); } - return pid.wait(true); + return pid.wait(); } @@ -280,6 +317,17 @@ bool isVarName(const string & s) } +Path NixRepl::getDerivationPath(Value & v) { + DrvInfo drvInfo(state); + if (!getDerivation(state, v, drvInfo, false)) + throw Error("expression does not evaluate to a derivation, so I can't build it"); + Path drvPath = drvInfo.queryDrvPath(); + if (drvPath == "" || !state.store->isValidPath(drvPath)) + throw Error("expression did not evaluate to a valid derivation"); + return drvPath; +} + + bool NixRepl::processLine(string line) { if (line == "") return true; @@ -301,12 +349,14 @@ bool NixRepl::processLine(string line) << " <x> = <expr> Bind expression to variable\n" << " :a <expr> Add attributes from resulting set to scope\n" << " :b <expr> Build derivation\n" + << " :i <expr> Build derivation, then install result into current profile\n" << " :l <path> Load Nix expression and add it to scope\n" << " :p <expr> Evaluate and print expression recursively\n" << " :q Exit nix-repl\n" << " :r Reload all files\n" << " :s <expr> Build dependencies of derivation, then start nix-shell\n" - << " :t <expr> Describe result of evaluation\n"; + << " :t <expr> Describe result of evaluation\n" + << " :u <expr> Build derivation, then start nix-shell\n"; } else if (command == ":a" || command == ":add") { @@ -329,17 +379,21 @@ bool NixRepl::processLine(string line) Value v; evalString(arg, v); std::cout << showType(v) << std::endl; + + } else if (command == ":u") { + Value v, f, result; + evalString(arg, v); + evalString("drv: (import <nixpkgs> {}).runCommand \"shell\" { buildInputs = [ drv ]; } \"\"", f); + state.callFunction(f, v, result, Pos()); + + Path drvPath = getDerivationPath(result); + runProgram("nix-shell", Strings{drvPath}); } - else if (command == ":b" || command == ":s") { + else if (command == ":b" || command == ":i" || command == ":s") { Value v; evalString(arg, v); - DrvInfo drvInfo(state); - if (!getDerivation(state, v, drvInfo, false)) - throw Error("expression does not evaluation to a derivation, so I can't build it"); - Path drvPath = drvInfo.queryDrvPath(); - if (drvPath == "" || !store->isValidPath(drvPath)) - throw Error("expression did not evaluate to a valid derivation"); + Path drvPath = getDerivationPath(v); if (command == ":b") { /* We could do the build in this process using buildPaths(), @@ -351,8 +405,11 @@ bool NixRepl::processLine(string line) for (auto & i : drv.outputs) std::cout << format(" %1% -> %2%") % i.first % i.second.path << std::endl; } - } else + } else if (command == ":i") { + runProgram("nix-env", Strings{"-i", drvPath}); + } else { runProgram("nix-shell", Strings{drvPath}); + } } else if (command == ":p" || command == ":print") { @@ -499,23 +556,25 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m switch (v.type) { case tInt: - str << v.integer; + str << ESC_CYA << v.integer << ESC_END; break; case tBool: - str << (v.boolean ? "true" : "false"); + str << ESC_CYA << (v.boolean ? "true" : "false") << ESC_END; break; case tString: + str << ESC_YEL; printStringValue(str, v.string.s); + str << ESC_END; break; case tPath: - str << v.path; // !!! escaping? + str << ESC_GRE << v.path << ESC_END; // !!! escaping? break; case tNull: - str << "null"; + str << ESC_CYA "null" ESC_END; break; case tAttrs: { @@ -568,7 +627,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m try { printValue(str, *i.second, maxDepth - 1, seen); } catch (AssertionError & e) { - str << "«error: " << e.msg() << "»"; + str << ESC_RED "«error: " << e.msg() << "»" ESC_END; } str << "; "; } @@ -594,7 +653,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m try { printValue(str, *v.listElems()[n], maxDepth - 1, seen); } catch (AssertionError & e) { - str << "«error: " << e.msg() << "»"; + str << ESC_RED "«error: " << e.msg() << "»" ESC_END; } str << " "; } @@ -603,20 +662,23 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m str << "]"; break; - case tLambda: - str << "«lambda»"; + case tLambda: { + std::ostringstream s; + s << v.lambda.fun->pos; + str << ESC_BLU "«lambda @ " << filterANSIEscapes(s.str()) << "»" ESC_END; break; + } case tPrimOp: - str << "«primop»"; + str << ESC_MAG "«primop»" ESC_END; break; case tPrimOpApp: - str << "«primop-app»"; + str << ESC_BLU "«primop-app»" ESC_END; break; default: - str << "«unknown»"; + str << ESC_RED "«unknown»" ESC_END; break; } @@ -649,8 +711,7 @@ int main(int argc, char * * argv) return true; }); - store = openStore(); - NixRepl repl(searchPath); + NixRepl repl(searchPath, openStore()); repl.mainLoop(files); write_history(historyFile.c_str()); |