2013-09-02 13:18:15 +00:00
|
|
|
#include <iostream>
|
|
|
|
#include <cstdlib>
|
2018-10-29 13:44:58 +00:00
|
|
|
#include <cstring>
|
|
|
|
#include <climits>
|
2013-09-02 13:18:15 +00:00
|
|
|
|
2024-03-10 06:05:50 +00:00
|
|
|
#include "box_ptr.hh"
|
|
|
|
#include "repl-interacter.hh"
|
2023-02-04 03:42:36 +00:00
|
|
|
#include "repl.hh"
|
|
|
|
|
2020-06-17 15:13:00 +00:00
|
|
|
#include "ansicolor.hh"
|
2013-09-02 13:18:15 +00:00
|
|
|
#include "shared.hh"
|
|
|
|
#include "eval.hh"
|
2022-02-18 23:33:03 +00:00
|
|
|
#include "eval-cache.hh"
|
2013-09-02 15:53:58 +00:00
|
|
|
#include "eval-inline.hh"
|
2023-07-31 13:19:19 +00:00
|
|
|
#include "eval-settings.hh"
|
2019-10-23 13:29:16 +00:00
|
|
|
#include "attr-path.hh"
|
2024-03-10 06:05:50 +00:00
|
|
|
#include "signals.hh"
|
2013-09-02 15:53:58 +00:00
|
|
|
#include "store-api.hh"
|
2022-03-08 18:20:39 +00:00
|
|
|
#include "log-store.hh"
|
2017-10-24 10:45:11 +00:00
|
|
|
#include "common-eval-args.hh"
|
2013-09-06 12:58:53 +00:00
|
|
|
#include "get-drvs.hh"
|
|
|
|
#include "derivations.hh"
|
2016-03-19 13:52:39 +00:00
|
|
|
#include "globals.hh"
|
2023-02-04 03:42:36 +00:00
|
|
|
#include "flake/flake.hh"
|
|
|
|
#include "flake/lockfile.hh"
|
|
|
|
#include "editor-for.hh"
|
2017-05-10 16:34:18 +00:00
|
|
|
#include "finally.hh"
|
2020-08-24 16:10:33 +00:00
|
|
|
#include "markdown.hh"
|
2022-04-18 17:21:47 +00:00
|
|
|
#include "local-fs-store.hh"
|
2024-03-10 06:36:47 +00:00
|
|
|
#include "signals.hh"
|
2023-04-16 11:10:45 +00:00
|
|
|
#include "print.hh"
|
2024-03-24 01:04:29 +00:00
|
|
|
#include "progress-bar.hh"
|
2017-05-10 16:34:18 +00:00
|
|
|
|
2020-08-06 09:40:41 +00:00
|
|
|
#if HAVE_BOEHMGC
|
2020-03-19 12:50:01 +00:00
|
|
|
#define GC_INCLUDE_NEW
|
|
|
|
#include <gc/gc_cpp.h>
|
2020-08-06 09:40:41 +00:00
|
|
|
#endif
|
2020-03-19 12:50:01 +00:00
|
|
|
|
2024-03-18 00:01:05 +00:00
|
|
|
// XXX: These are for nix-doc features and will be removed in a future rewrite where this functionality is integrated more natively.
|
|
|
|
extern "C" {
|
|
|
|
char const *nd_get_function_docs(char const *filename, size_t line, size_t col);
|
|
|
|
void nd_free_string(char const *str);
|
|
|
|
}
|
|
|
|
|
2017-04-25 16:48:40 +00:00
|
|
|
namespace nix {
|
2013-09-02 13:18:15 +00:00
|
|
|
|
2024-03-18 00:01:05 +00:00
|
|
|
|
|
|
|
/** Wrapper around std::unique_ptr with a custom deleter for strings from nix-doc **/
|
|
|
|
using NdString = std::unique_ptr<const char, decltype(&nd_free_string)>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fetch a string representing the doc comment using nix-doc and wrap it in an RAII wrapper.
|
|
|
|
*/
|
|
|
|
NdString lambdaDocsForPos(SourcePath const path, nix::Pos const &pos) {
|
|
|
|
std::string const file = path.to_string();
|
|
|
|
return NdString{nd_get_function_docs(file.c_str(), pos.line, pos.column), &nd_free_string};
|
|
|
|
}
|
|
|
|
|
2024-03-08 08:19:15 +00:00
|
|
|
/**
|
|
|
|
* Returned by `NixRepl::processLine`.
|
|
|
|
*/
|
|
|
|
enum class ProcessLineResult {
|
|
|
|
/**
|
|
|
|
* The user exited with `:quit`. The REPL should exit. The surrounding
|
|
|
|
* program or evaluation (e.g., if the REPL was acting as the debugger)
|
|
|
|
* should also exit.
|
|
|
|
*/
|
2024-03-08 08:19:27 +00:00
|
|
|
Quit,
|
2024-03-08 08:19:15 +00:00
|
|
|
/**
|
|
|
|
* The user exited with `:continue`. The REPL should exit, but the program
|
|
|
|
* should continue running.
|
|
|
|
*/
|
2024-03-08 08:19:27 +00:00
|
|
|
Continue,
|
2024-03-08 08:19:15 +00:00
|
|
|
/**
|
|
|
|
* The user did not exit. The REPL should request another line of input.
|
|
|
|
*/
|
2024-03-08 08:19:27 +00:00
|
|
|
PromptAgain,
|
2024-03-08 08:19:15 +00:00
|
|
|
};
|
|
|
|
|
2020-08-06 09:40:41 +00:00
|
|
|
struct NixRepl
|
2023-02-21 14:38:46 +00:00
|
|
|
: AbstractNixRepl
|
2024-03-10 06:05:50 +00:00
|
|
|
, detail::ReplCompleterMixin
|
2020-08-06 09:40:41 +00:00
|
|
|
#if HAVE_BOEHMGC
|
2023-02-21 14:38:46 +00:00
|
|
|
, gc
|
2020-08-06 09:40:41 +00:00
|
|
|
#endif
|
2013-09-02 15:53:58 +00:00
|
|
|
{
|
2022-05-05 10:29:14 +00:00
|
|
|
size_t debugTraceIndex;
|
2021-12-20 19:32:21 +00:00
|
|
|
|
2013-09-09 14:02:46 +00:00
|
|
|
Strings loadedFiles;
|
2022-02-18 23:33:03 +00:00
|
|
|
std::function<AnnotatedValues()> getValues;
|
2013-09-09 14:02:46 +00:00
|
|
|
|
2013-09-09 15:06:14 +00:00
|
|
|
const static int envSize = 32768;
|
2021-09-14 16:49:22 +00:00
|
|
|
std::shared_ptr<StaticEnv> staticEnv;
|
2013-09-02 15:53:58 +00:00
|
|
|
Env * env;
|
|
|
|
int displ;
|
2013-09-06 17:51:59 +00:00
|
|
|
StringSet varNames;
|
|
|
|
|
2024-03-10 06:05:50 +00:00
|
|
|
box_ptr<ReplInteracter> interacter;
|
2017-04-25 16:56:29 +00:00
|
|
|
|
2023-06-23 17:51:25 +00:00
|
|
|
NixRepl(const SearchPath & searchPath, nix::ref<Store> store,ref<EvalState> state,
|
2022-02-18 23:33:03 +00:00
|
|
|
std::function<AnnotatedValues()> getValues);
|
2024-03-10 06:05:50 +00:00
|
|
|
virtual ~NixRepl() = default;
|
2023-02-04 03:42:36 +00:00
|
|
|
|
2024-03-08 08:19:15 +00:00
|
|
|
ReplExitStatus mainLoop() override;
|
2023-02-04 03:42:36 +00:00
|
|
|
void initEnv() override;
|
|
|
|
|
2024-03-10 06:05:50 +00:00
|
|
|
virtual StringSet completePrefix(const std::string & prefix) override;
|
2020-06-17 03:56:48 +00:00
|
|
|
StorePath getDerivationPath(Value & v);
|
2024-03-08 08:19:15 +00:00
|
|
|
ProcessLineResult processLine(std::string line);
|
2022-02-18 23:33:03 +00:00
|
|
|
|
2013-09-06 13:20:06 +00:00
|
|
|
void loadFile(const Path & path);
|
2021-07-19 15:47:03 +00:00
|
|
|
void loadFlake(const std::string & flakeRef);
|
2021-12-20 19:32:21 +00:00
|
|
|
void loadFiles();
|
2013-09-09 14:02:46 +00:00
|
|
|
void reloadFiles();
|
2013-09-02 16:18:27 +00:00
|
|
|
void addAttrsToScope(Value & attrs);
|
2022-04-22 19:45:39 +00:00
|
|
|
void addVarToScope(const Symbol name, Value & v);
|
2022-02-25 15:00:00 +00:00
|
|
|
Expr * parseString(std::string s);
|
|
|
|
void evalString(std::string s, Value & v);
|
2022-05-05 10:29:14 +00:00
|
|
|
void loadDebugTraceEnv(DebugTrace & dt);
|
2013-09-09 09:14:43 +00:00
|
|
|
|
Unify and refactor value printing
Previously, there were two mostly-identical value printers -- one in
`libexpr/eval.cc` (which didn't force values) and one in
`libcmd/repl.cc` (which did force values and also printed ANSI color
codes).
This PR unifies both of these printers into `print.cc` and provides a
`PrintOptions` struct for controlling the output, which allows for
toggling whether values are forced, whether repeated values are tracked,
and whether ANSI color codes are displayed.
Additionally, `PrintOptions` allows tuning the maximum number of
attributes, list items, and bytes in a string that will be displayed;
this makes it ideal for contexts where printing too much output (e.g.
all of Nixpkgs) is distracting. (As requested by @roberth in
https://github.com/NixOS/nix/pull/9554#issuecomment-1845095735)
Please read the tests for example output.
Future work:
- It would be nice to provide this function as a builtin, perhaps
`builtins.toStringDebug` -- a printing function that never fails would
be useful when debugging Nix code.
- It would be nice to support customizing `PrintOptions` members on the
command line, e.g. `--option to-string-max-attrs 1000`.
(cherry picked from commit 0fa08b451682fb3311fe58112ff05c4fe5bee3a4, )
===
Restore ambiguous value printer for `nix-instantiate`
The Nix team has requested that this output format remain unchanged.
I've added a warning to the man page explaining that `nix-instantiate
--eval` output will not parse correctly in many situations.
(cherry picked from commit df84dd4d8dd3fd6381ac2ca3064432ab31a16b79)
Change-Id: I7cca6b4b53cd0642f2d49af657d5676a8554c9f8
2024-03-08 02:05:47 +00:00
|
|
|
void printValue(std::ostream & str,
|
|
|
|
Value & v,
|
|
|
|
unsigned int maxDepth = std::numeric_limits<unsigned int>::max())
|
|
|
|
{
|
|
|
|
::nix::printValue(*state, str, v, PrintOptions {
|
|
|
|
.ansiColors = true,
|
|
|
|
.force = true,
|
|
|
|
.derivationPaths = true,
|
2024-03-08 07:25:42 +00:00
|
|
|
.maxDepth = maxDepth,
|
|
|
|
.prettyIndent = 2
|
Unify and refactor value printing
Previously, there were two mostly-identical value printers -- one in
`libexpr/eval.cc` (which didn't force values) and one in
`libcmd/repl.cc` (which did force values and also printed ANSI color
codes).
This PR unifies both of these printers into `print.cc` and provides a
`PrintOptions` struct for controlling the output, which allows for
toggling whether values are forced, whether repeated values are tracked,
and whether ANSI color codes are displayed.
Additionally, `PrintOptions` allows tuning the maximum number of
attributes, list items, and bytes in a string that will be displayed;
this makes it ideal for contexts where printing too much output (e.g.
all of Nixpkgs) is distracting. (As requested by @roberth in
https://github.com/NixOS/nix/pull/9554#issuecomment-1845095735)
Please read the tests for example output.
Future work:
- It would be nice to provide this function as a builtin, perhaps
`builtins.toStringDebug` -- a printing function that never fails would
be useful when debugging Nix code.
- It would be nice to support customizing `PrintOptions` members on the
command line, e.g. `--option to-string-max-attrs 1000`.
(cherry picked from commit 0fa08b451682fb3311fe58112ff05c4fe5bee3a4, )
===
Restore ambiguous value printer for `nix-instantiate`
The Nix team has requested that this output format remain unchanged.
I've added a warning to the man page explaining that `nix-instantiate
--eval` output will not parse correctly in many situations.
(cherry picked from commit df84dd4d8dd3fd6381ac2ca3064432ab31a16b79)
Change-Id: I7cca6b4b53cd0642f2d49af657d5676a8554c9f8
2024-03-08 02:05:47 +00:00
|
|
|
});
|
|
|
|
}
|
2013-09-02 15:53:58 +00:00
|
|
|
};
|
|
|
|
|
2022-02-25 15:00:00 +00:00
|
|
|
std::string removeWhitespace(std::string s)
|
2013-09-02 16:18:27 +00:00
|
|
|
{
|
|
|
|
s = chomp(s);
|
|
|
|
size_t n = s.find_first_not_of(" \n\r\t");
|
2022-02-25 15:00:00 +00:00
|
|
|
if (n != std::string::npos) s = std::string(s, n);
|
2013-09-02 16:18:27 +00:00
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2024-03-10 07:59:50 +00:00
|
|
|
static box_ptr<ReplInteracter> makeInteracter() {
|
|
|
|
if (experimentalFeatureSettings.isEnabled(Xp::ReplAutomation))
|
|
|
|
return make_box_ptr<AutomationInteracter>();
|
|
|
|
else
|
|
|
|
return make_box_ptr<ReadlineLikeInteracter>(getDataDir() + "/nix/repl-history");
|
|
|
|
}
|
2013-09-02 16:18:27 +00:00
|
|
|
|
2023-06-23 17:51:25 +00:00
|
|
|
NixRepl::NixRepl(const SearchPath & searchPath, nix::ref<Store> store, ref<EvalState> state,
|
2022-02-18 23:33:03 +00:00
|
|
|
std::function<NixRepl::AnnotatedValues()> getValues)
|
2023-02-04 03:42:36 +00:00
|
|
|
: AbstractNixRepl(state)
|
2022-03-28 18:09:21 +00:00
|
|
|
, debugTraceIndex(0)
|
2022-02-18 23:33:03 +00:00
|
|
|
, getValues(getValues)
|
2024-03-04 06:37:45 +00:00
|
|
|
, staticEnv(new StaticEnv(nullptr, state->staticBaseEnv.get()))
|
2024-03-10 07:59:50 +00:00
|
|
|
, interacter(makeInteracter())
|
2017-04-25 16:48:40 +00:00
|
|
|
{
|
2017-05-10 16:34:18 +00:00
|
|
|
}
|
|
|
|
|
2022-06-15 16:49:12 +00:00
|
|
|
void runNix(Path program, const Strings & args,
|
2021-06-29 12:52:46 +00:00
|
|
|
const std::optional<std::string> & input = {})
|
|
|
|
{
|
|
|
|
auto subprocessEnv = getEnv();
|
2021-07-15 16:17:18 +00:00
|
|
|
subprocessEnv["NIX_CONFIG"] = globalConfig.toKeyValue();
|
2021-06-29 12:52:46 +00:00
|
|
|
|
2022-06-15 16:49:12 +00:00
|
|
|
runProgram2(RunOptions {
|
2021-09-13 21:22:09 +00:00
|
|
|
.program = settings.nixBinDir+ "/" + program,
|
|
|
|
.args = args,
|
|
|
|
.environment = subprocessEnv,
|
|
|
|
.input = input,
|
|
|
|
});
|
2021-06-29 12:52:46 +00:00
|
|
|
|
2022-06-15 16:49:12 +00:00
|
|
|
return;
|
2021-06-29 12:52:46 +00:00
|
|
|
}
|
|
|
|
|
2022-05-05 10:29:14 +00:00
|
|
|
static std::ostream & showDebugTrace(std::ostream & out, const PosTable & positions, const DebugTrace & dt)
|
2022-04-07 18:09:47 +00:00
|
|
|
{
|
2022-05-05 10:29:14 +00:00
|
|
|
if (dt.isError)
|
2022-04-07 18:09:47 +00:00
|
|
|
out << ANSI_RED "error: " << ANSI_NORMAL;
|
|
|
|
out << dt.hint.str() << "\n";
|
|
|
|
|
|
|
|
// prefer direct pos, but if noPos then try the expr.
|
2022-12-12 23:48:04 +00:00
|
|
|
auto pos = dt.pos
|
|
|
|
? dt.pos
|
2024-03-06 04:24:35 +00:00
|
|
|
: positions[dt.expr.getPos() ? dt.expr.getPos() : noPos];
|
2022-04-07 18:09:47 +00:00
|
|
|
|
|
|
|
if (pos) {
|
2024-03-04 07:51:02 +00:00
|
|
|
out << *pos;
|
2022-12-12 23:48:04 +00:00
|
|
|
if (auto loc = pos->getCodeLines()) {
|
2022-04-07 18:09:47 +00:00
|
|
|
out << "\n";
|
2022-12-12 23:48:04 +00:00
|
|
|
printCodeLines(out, "", *pos, *loc);
|
2022-04-07 18:09:47 +00:00
|
|
|
out << "\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2024-03-04 07:52:57 +00:00
|
|
|
static bool isFirstRepl = true;
|
|
|
|
|
2024-03-08 08:19:15 +00:00
|
|
|
ReplExitStatus NixRepl::mainLoop()
|
2013-09-02 15:53:58 +00:00
|
|
|
{
|
2024-03-04 07:52:57 +00:00
|
|
|
if (isFirstRepl) {
|
|
|
|
std::string_view debuggerNotice = "";
|
|
|
|
if (state->debugRepl) {
|
|
|
|
debuggerNotice = " debugger";
|
|
|
|
}
|
2024-03-18 23:20:00 +00:00
|
|
|
notice("Lix %1%%2%\nType :? for help.", nixVersion, debuggerNotice);
|
2024-03-04 07:52:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
isFirstRepl = false;
|
2013-09-06 13:20:06 +00:00
|
|
|
|
2021-12-20 19:32:21 +00:00
|
|
|
loadFiles();
|
2013-09-02 13:18:15 +00:00
|
|
|
|
2024-03-10 06:05:50 +00:00
|
|
|
auto _guard = interacter->init(static_cast<detail::ReplCompleterMixin *>(this));
|
2017-05-10 16:34:18 +00:00
|
|
|
|
2024-03-24 01:04:29 +00:00
|
|
|
/* Stop the progress bar because it interferes with the display of
|
|
|
|
the repl. */
|
|
|
|
stopProgressBar();
|
|
|
|
|
2017-05-10 16:34:18 +00:00
|
|
|
std::string input;
|
2016-02-18 12:27:39 +00:00
|
|
|
|
2013-09-02 13:18:15 +00:00
|
|
|
while (true) {
|
2016-02-18 13:04:55 +00:00
|
|
|
// When continuing input from previous lines, don't print a prompt, just align to the same
|
2016-02-18 12:27:39 +00:00
|
|
|
// number of chars as the prompt.
|
2024-03-10 06:05:50 +00:00
|
|
|
if (!interacter->getLine(input, input.empty() ? ReplPromptType::ReplPrompt : ReplPromptType::ContinuationPrompt)) {
|
2024-03-08 08:19:15 +00:00
|
|
|
// Ctrl-D should exit the debugger.
|
2022-05-25 16:21:20 +00:00
|
|
|
state->debugStop = false;
|
2022-11-28 13:59:06 +00:00
|
|
|
logger->cout("");
|
2024-03-08 08:19:15 +00:00
|
|
|
// TODO: Should Ctrl-D exit just the current debugger session or
|
|
|
|
// the entire program?
|
|
|
|
return ReplExitStatus::QuitAll;
|
2022-02-15 16:49:25 +00:00
|
|
|
}
|
2013-09-02 13:18:15 +00:00
|
|
|
try {
|
2024-03-08 08:19:15 +00:00
|
|
|
switch (processLine(input)) {
|
2024-03-08 08:19:27 +00:00
|
|
|
case ProcessLineResult::Quit:
|
2024-03-08 08:19:15 +00:00
|
|
|
return ReplExitStatus::QuitAll;
|
|
|
|
case ProcessLineResult::Continue:
|
2024-03-08 08:19:27 +00:00
|
|
|
return ReplExitStatus::Continue;
|
|
|
|
case ProcessLineResult::PromptAgain:
|
2024-03-08 08:19:15 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
abort();
|
|
|
|
}
|
2016-02-18 12:27:39 +00:00
|
|
|
} catch (ParseError & e) {
|
2020-11-02 18:07:37 +00:00
|
|
|
if (e.msg().find("unexpected end of file") != std::string::npos) {
|
2016-02-18 12:27:39 +00:00
|
|
|
// For parse errors on incomplete input, we continue waiting for the next line of
|
|
|
|
// input without clearing the input so far.
|
|
|
|
continue;
|
|
|
|
} else {
|
2020-06-24 19:14:49 +00:00
|
|
|
printMsg(lvlError, e.msg());
|
2016-02-18 12:27:39 +00:00
|
|
|
}
|
2022-04-07 18:09:47 +00:00
|
|
|
} catch (EvalError & e) {
|
2024-03-05 05:58:29 +00:00
|
|
|
printMsg(lvlError, e.msg());
|
2013-09-02 13:18:15 +00:00
|
|
|
} catch (Error & e) {
|
2020-11-02 18:07:37 +00:00
|
|
|
printMsg(lvlError, e.msg());
|
2013-09-06 11:20:35 +00:00
|
|
|
} catch (Interrupted & e) {
|
2020-11-02 18:07:37 +00:00
|
|
|
printMsg(lvlError, e.msg());
|
2013-09-02 13:18:15 +00:00
|
|
|
}
|
|
|
|
|
2017-05-10 16:34:18 +00:00
|
|
|
// We handled the current input fully, so we should clear it
|
|
|
|
// and read brand new input.
|
2016-02-18 12:27:39 +00:00
|
|
|
input.clear();
|
2013-09-02 13:18:15 +00:00
|
|
|
std::cout << std::endl;
|
|
|
|
}
|
|
|
|
}
|
2013-09-02 15:53:58 +00:00
|
|
|
|
2022-02-25 15:00:00 +00:00
|
|
|
StringSet NixRepl::completePrefix(const std::string & prefix)
|
2013-09-06 17:51:59 +00:00
|
|
|
{
|
2017-05-10 16:34:18 +00:00
|
|
|
StringSet completions;
|
2017-04-25 17:19:15 +00:00
|
|
|
|
2017-05-10 16:34:18 +00:00
|
|
|
size_t start = prefix.find_last_of(" \n\r\t(){}[]");
|
|
|
|
std::string prev, cur;
|
|
|
|
if (start == std::string::npos) {
|
|
|
|
prev = "";
|
|
|
|
cur = prefix;
|
2016-02-18 12:50:52 +00:00
|
|
|
} else {
|
2017-05-10 16:34:18 +00:00
|
|
|
prev = std::string(prefix, 0, start + 1);
|
|
|
|
cur = std::string(prefix, start + 1);
|
2013-09-06 17:51:59 +00:00
|
|
|
}
|
|
|
|
|
2017-05-10 16:34:18 +00:00
|
|
|
size_t slash, dot;
|
2013-09-09 10:00:33 +00:00
|
|
|
|
2022-02-25 15:00:00 +00:00
|
|
|
if ((slash = cur.rfind('/')) != std::string::npos) {
|
2017-05-10 16:34:18 +00:00
|
|
|
try {
|
|
|
|
auto dir = std::string(cur, 0, slash);
|
|
|
|
auto prefix2 = std::string(cur, slash + 1);
|
|
|
|
for (auto & entry : readDirectory(dir == "" ? "/" : dir)) {
|
2024-03-18 02:14:18 +00:00
|
|
|
if (entry.name[0] != '.' && entry.name.starts_with(prefix2))
|
2017-05-10 16:34:18 +00:00
|
|
|
completions.insert(prev + dir + "/" + entry.name);
|
|
|
|
}
|
|
|
|
} catch (Error &) {
|
|
|
|
}
|
2022-02-25 15:00:00 +00:00
|
|
|
} else if ((dot = cur.rfind('.')) == std::string::npos) {
|
2013-09-09 10:00:33 +00:00
|
|
|
/* This is a variable name; look it up in the current scope. */
|
2017-05-10 16:34:18 +00:00
|
|
|
StringSet::iterator i = varNames.lower_bound(cur);
|
2013-09-09 10:00:33 +00:00
|
|
|
while (i != varNames.end()) {
|
2022-02-25 15:00:00 +00:00
|
|
|
if (i->substr(0, cur.size()) != cur) break;
|
2017-05-10 16:34:18 +00:00
|
|
|
completions.insert(prev + *i);
|
2013-09-09 10:00:33 +00:00
|
|
|
i++;
|
|
|
|
}
|
|
|
|
} else {
|
2022-10-05 10:09:57 +00:00
|
|
|
/* Temporarily disable the debugger, to avoid re-entering readline. */
|
|
|
|
auto debug_repl = state->debugRepl;
|
|
|
|
state->debugRepl = nullptr;
|
|
|
|
Finally restoreDebug([&]() { state->debugRepl = debug_repl; });
|
2013-09-09 10:00:33 +00:00
|
|
|
try {
|
|
|
|
/* This is an expression that should evaluate to an
|
|
|
|
attribute set. Evaluate it to get the names of the
|
|
|
|
attributes. */
|
2022-02-25 15:00:00 +00:00
|
|
|
auto expr = cur.substr(0, dot);
|
|
|
|
auto cur2 = cur.substr(dot + 1);
|
2013-09-09 10:00:33 +00:00
|
|
|
|
|
|
|
Expr * e = parseString(expr);
|
|
|
|
Value v;
|
2020-03-19 12:52:28 +00:00
|
|
|
e->eval(*state, *env, v);
|
2023-01-20 12:01:03 +00:00
|
|
|
state->forceAttrs(v, noPos, "while evaluating an attrset for the purpose of completion (this error should not be displayed; file an issue?)");
|
2013-09-09 10:00:33 +00:00
|
|
|
|
2015-09-07 11:05:58 +00:00
|
|
|
for (auto & i : *v.attrs) {
|
2022-03-05 13:40:24 +00:00
|
|
|
std::string_view name = state->symbols[i.name];
|
2022-02-25 15:00:00 +00:00
|
|
|
if (name.substr(0, cur2.size()) != cur2) continue;
|
2022-03-05 13:40:24 +00:00
|
|
|
completions.insert(concatStrings(prev, expr, ".", name));
|
2013-09-09 10:00:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} catch (ParseError & e) {
|
|
|
|
// Quietly ignore parse errors.
|
2014-04-11 10:51:15 +00:00
|
|
|
} catch (EvalError & e) {
|
2013-09-09 10:00:33 +00:00
|
|
|
// Quietly ignore evaluation errors.
|
2021-11-26 10:03:48 +00:00
|
|
|
} catch (BadURL & e) {
|
|
|
|
// Quietly ignore BadURL flake-related errors.
|
2013-09-09 10:00:33 +00:00
|
|
|
}
|
2013-09-06 17:51:59 +00:00
|
|
|
}
|
2017-05-10 16:34:18 +00:00
|
|
|
|
|
|
|
return completions;
|
2013-09-06 17:51:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-16 12:07:35 +00:00
|
|
|
// FIXME: DRY and match or use the parser
|
2022-02-25 15:00:00 +00:00
|
|
|
static bool isVarName(std::string_view s)
|
2013-09-09 11:56:53 +00:00
|
|
|
{
|
2016-02-14 07:50:47 +00:00
|
|
|
if (s.size() == 0) return false;
|
|
|
|
char c = s[0];
|
|
|
|
if ((c >= '0' && c <= '9') || c == '-' || c == '\'') return false;
|
2015-09-07 11:05:58 +00:00
|
|
|
for (auto & i : s)
|
|
|
|
if (!((i >= 'a' && i <= 'z') ||
|
|
|
|
(i >= 'A' && i <= 'Z') ||
|
|
|
|
(i >= '0' && i <= '9') ||
|
2016-02-14 07:16:30 +00:00
|
|
|
i == '_' || i == '-' || i == '\''))
|
2013-09-09 11:56:53 +00:00
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-06-17 03:56:48 +00:00
|
|
|
StorePath NixRepl::getDerivationPath(Value & v) {
|
2020-03-19 12:52:28 +00:00
|
|
|
auto drvInfo = getDerivation(*state, v, false);
|
2017-07-20 11:32:01 +00:00
|
|
|
if (!drvInfo)
|
2016-02-18 02:31:30 +00:00
|
|
|
throw Error("expression does not evaluate to a derivation, so I can't build it");
|
2022-03-02 09:57:19 +00:00
|
|
|
auto drvPath = drvInfo->queryDrvPath();
|
|
|
|
if (!drvPath)
|
|
|
|
throw Error("expression did not evaluate to a valid derivation (no 'drvPath' attribute)");
|
|
|
|
if (!state->store->isValidPath(*drvPath))
|
|
|
|
throw Error("expression evaluated to invalid derivation '%s'", state->store->printStorePath(*drvPath));
|
|
|
|
return *drvPath;
|
2016-02-18 02:31:30 +00:00
|
|
|
}
|
|
|
|
|
2022-05-05 10:29:14 +00:00
|
|
|
void NixRepl::loadDebugTraceEnv(DebugTrace & dt)
|
2022-03-28 21:28:59 +00:00
|
|
|
{
|
2022-05-05 10:29:14 +00:00
|
|
|
initEnv();
|
2022-03-28 21:28:59 +00:00
|
|
|
|
2022-05-25 16:21:20 +00:00
|
|
|
auto se = state->getStaticEnv(dt.expr);
|
2022-05-19 16:48:10 +00:00
|
|
|
if (se) {
|
2022-05-25 16:21:20 +00:00
|
|
|
auto vm = mapStaticEnvBindings(state->symbols, *se.get(), dt.env);
|
2022-03-28 21:28:59 +00:00
|
|
|
|
|
|
|
// add staticenv vars.
|
2022-05-05 10:29:14 +00:00
|
|
|
for (auto & [name, value] : *(vm.get()))
|
2022-05-25 16:21:20 +00:00
|
|
|
addVarToScope(state->symbols.create(name), *value);
|
2022-03-28 21:28:59 +00:00
|
|
|
}
|
|
|
|
}
|
2016-02-18 02:31:30 +00:00
|
|
|
|
2024-03-08 08:19:15 +00:00
|
|
|
ProcessLineResult NixRepl::processLine(std::string line)
|
2013-09-02 15:53:58 +00:00
|
|
|
{
|
2022-03-20 15:39:36 +00:00
|
|
|
line = trim(line);
|
2024-03-08 08:19:15 +00:00
|
|
|
if (line.empty())
|
2024-03-08 08:19:27 +00:00
|
|
|
return ProcessLineResult::PromptAgain;
|
2013-09-09 13:02:56 +00:00
|
|
|
|
2021-10-07 21:58:02 +00:00
|
|
|
_isInterrupted = false;
|
|
|
|
|
2022-02-25 15:00:00 +00:00
|
|
|
std::string command, arg;
|
2013-09-06 11:01:02 +00:00
|
|
|
|
2013-09-09 13:02:56 +00:00
|
|
|
if (line[0] == ':') {
|
2016-02-18 12:59:51 +00:00
|
|
|
size_t p = line.find_first_of(" \n\r\t");
|
2022-02-25 15:00:00 +00:00
|
|
|
command = line.substr(0, p);
|
|
|
|
if (p != std::string::npos) arg = removeWhitespace(line.substr(p));
|
2013-09-09 13:02:56 +00:00
|
|
|
} else {
|
|
|
|
arg = line;
|
|
|
|
}
|
2013-09-06 13:05:18 +00:00
|
|
|
|
2013-09-09 14:02:46 +00:00
|
|
|
if (command == ":?" || command == ":help") {
|
2020-12-09 12:07:01 +00:00
|
|
|
// FIXME: convert to Markdown, include in the 'nix repl' manpage.
|
2017-04-25 16:58:02 +00:00
|
|
|
std::cout
|
2020-06-24 19:14:49 +00:00
|
|
|
<< "The following commands are available:\n"
|
|
|
|
<< "\n"
|
2023-01-23 10:50:44 +00:00
|
|
|
<< " <expr> Evaluate and print expression\n"
|
|
|
|
<< " <x> = <expr> Bind expression to variable\n"
|
|
|
|
<< " :a, :add <expr> Add attributes from resulting set to scope\n"
|
|
|
|
<< " :b <expr> Build a derivation\n"
|
|
|
|
<< " :bl <expr> Build a derivation, creating GC roots in the\n"
|
|
|
|
<< " working directory\n"
|
|
|
|
<< " :e, :edit <expr> Open package or function in $EDITOR\n"
|
|
|
|
<< " :i <expr> Build derivation, then install result into\n"
|
|
|
|
<< " current profile\n"
|
|
|
|
<< " :l, :load <path> Load Nix expression and add it to scope\n"
|
|
|
|
<< " :lf, :load-flake <ref> Load Nix flake and add it to scope\n"
|
|
|
|
<< " :p, :print <expr> Evaluate and print expression recursively\n"
|
2024-03-11 09:02:09 +00:00
|
|
|
<< " Strings are printed directly, without escaping.\n"
|
2023-01-23 10:50:44 +00:00
|
|
|
<< " :q, :quit Exit nix-repl\n"
|
|
|
|
<< " :r, :reload Reload all files\n"
|
|
|
|
<< " :sh <expr> Build dependencies of derivation, then start\n"
|
|
|
|
<< " nix-shell\n"
|
|
|
|
<< " :t <expr> Describe result of evaluation\n"
|
|
|
|
<< " :u <expr> Build derivation, then start nix-shell\n"
|
2024-03-18 00:01:05 +00:00
|
|
|
<< " :doc <expr> Show documentation for the provided function (experimental lambda support)\n"
|
2023-01-23 10:50:44 +00:00
|
|
|
<< " :log <expr> Show logs for a derivation\n"
|
|
|
|
<< " :te, :trace-enable [bool] Enable, disable or toggle showing traces for\n"
|
|
|
|
<< " errors\n"
|
|
|
|
<< " :?, :help Brings up this help menu\n"
|
2022-02-03 20:15:21 +00:00
|
|
|
;
|
2022-05-25 16:21:20 +00:00
|
|
|
if (state->debugRepl) {
|
2022-05-05 21:24:57 +00:00
|
|
|
std::cout
|
|
|
|
<< "\n"
|
|
|
|
<< " Debug mode commands\n"
|
2023-01-23 10:50:44 +00:00
|
|
|
<< " :env Show env stack\n"
|
|
|
|
<< " :bt, :backtrace Show trace stack\n"
|
|
|
|
<< " :st Show current trace\n"
|
|
|
|
<< " :st <idx> Change to another trace in the stack\n"
|
|
|
|
<< " :c, :continue Go until end of program, exception, or builtins.break\n"
|
|
|
|
<< " :s, :step Go one step\n"
|
2022-05-05 21:24:57 +00:00
|
|
|
;
|
|
|
|
}
|
2022-02-03 20:15:21 +00:00
|
|
|
|
2021-12-20 19:32:21 +00:00
|
|
|
}
|
|
|
|
|
2022-05-25 16:21:20 +00:00
|
|
|
else if (state->debugRepl && (command == ":bt" || command == ":backtrace")) {
|
|
|
|
for (const auto & [idx, i] : enumerate(state->debugTraces)) {
|
2022-05-06 03:23:03 +00:00
|
|
|
std::cout << "\n" << ANSI_BLUE << idx << ANSI_NORMAL << ": ";
|
2022-05-25 16:21:20 +00:00
|
|
|
showDebugTrace(std::cout, state->positions, i);
|
2022-05-05 21:24:57 +00:00
|
|
|
}
|
2022-05-06 03:23:03 +00:00
|
|
|
}
|
2022-05-05 21:24:57 +00:00
|
|
|
|
2022-05-25 16:21:20 +00:00
|
|
|
else if (state->debugRepl && (command == ":env")) {
|
|
|
|
for (const auto & [idx, i] : enumerate(state->debugTraces)) {
|
2022-05-06 03:23:03 +00:00
|
|
|
if (idx == debugTraceIndex) {
|
2022-05-25 16:21:20 +00:00
|
|
|
printEnvBindings(*state, i.expr, i.env);
|
2022-05-06 03:23:03 +00:00
|
|
|
break;
|
2022-03-28 18:09:21 +00:00
|
|
|
}
|
|
|
|
}
|
2022-05-06 03:23:03 +00:00
|
|
|
}
|
2022-05-05 21:24:57 +00:00
|
|
|
|
2022-05-25 16:21:20 +00:00
|
|
|
else if (state->debugRepl && (command == ":st")) {
|
2022-05-06 03:23:03 +00:00
|
|
|
try {
|
|
|
|
// change the DebugTrace index.
|
|
|
|
debugTraceIndex = stoi(arg);
|
|
|
|
} catch (...) { }
|
|
|
|
|
2022-05-25 16:21:20 +00:00
|
|
|
for (const auto & [idx, i] : enumerate(state->debugTraces)) {
|
2022-05-06 03:23:03 +00:00
|
|
|
if (idx == debugTraceIndex) {
|
|
|
|
std::cout << "\n" << ANSI_BLUE << idx << ANSI_NORMAL << ": ";
|
2022-05-25 16:21:20 +00:00
|
|
|
showDebugTrace(std::cout, state->positions, i);
|
2022-05-06 03:23:03 +00:00
|
|
|
std::cout << std::endl;
|
2022-05-25 16:21:20 +00:00
|
|
|
printEnvBindings(*state, i.expr, i.env);
|
2022-05-06 03:23:03 +00:00
|
|
|
loadDebugTraceEnv(i);
|
|
|
|
break;
|
|
|
|
}
|
2021-12-20 19:32:21 +00:00
|
|
|
}
|
2022-05-06 03:23:03 +00:00
|
|
|
}
|
2022-05-05 21:24:57 +00:00
|
|
|
|
2022-05-25 16:21:20 +00:00
|
|
|
else if (state->debugRepl && (command == ":s" || command == ":step")) {
|
2022-05-06 03:23:03 +00:00
|
|
|
// set flag to stop at next DebugTrace; exit repl.
|
2022-05-25 16:21:20 +00:00
|
|
|
state->debugStop = true;
|
2024-03-08 08:19:27 +00:00
|
|
|
return ProcessLineResult::Continue;
|
2022-05-06 03:23:03 +00:00
|
|
|
}
|
2022-05-05 21:24:57 +00:00
|
|
|
|
2022-05-25 16:21:20 +00:00
|
|
|
else if (state->debugRepl && (command == ":c" || command == ":continue")) {
|
2022-05-06 03:23:03 +00:00
|
|
|
// set flag to run to next breakpoint or end of program; exit repl.
|
2022-05-25 16:21:20 +00:00
|
|
|
state->debugStop = false;
|
2024-03-08 08:19:27 +00:00
|
|
|
return ProcessLineResult::Continue;
|
2013-09-09 11:22:33 +00:00
|
|
|
}
|
|
|
|
|
2013-09-09 14:02:46 +00:00
|
|
|
else if (command == ":a" || command == ":add") {
|
2013-09-02 15:53:58 +00:00
|
|
|
Value v;
|
2013-09-09 13:02:56 +00:00
|
|
|
evalString(arg, v);
|
2013-09-02 16:18:27 +00:00
|
|
|
addAttrsToScope(v);
|
|
|
|
}
|
|
|
|
|
2013-09-09 14:02:46 +00:00
|
|
|
else if (command == ":l" || command == ":load") {
|
2020-03-19 12:52:28 +00:00
|
|
|
state->resetFileCache();
|
2013-09-09 13:02:56 +00:00
|
|
|
loadFile(arg);
|
2013-09-02 15:53:58 +00:00
|
|
|
}
|
|
|
|
|
2021-07-19 15:47:03 +00:00
|
|
|
else if (command == ":lf" || command == ":load-flake") {
|
|
|
|
loadFlake(arg);
|
|
|
|
}
|
|
|
|
|
2013-09-09 14:02:46 +00:00
|
|
|
else if (command == ":r" || command == ":reload") {
|
2020-03-19 12:52:28 +00:00
|
|
|
state->resetFileCache();
|
2013-09-09 14:02:46 +00:00
|
|
|
reloadFiles();
|
|
|
|
}
|
|
|
|
|
2019-10-23 13:29:16 +00:00
|
|
|
else if (command == ":e" || command == ":edit") {
|
|
|
|
Value v;
|
|
|
|
evalString(arg, v);
|
|
|
|
|
2023-04-06 11:15:50 +00:00
|
|
|
const auto [path, line] = [&] () -> std::pair<SourcePath, uint32_t> {
|
2022-03-04 19:54:50 +00:00
|
|
|
if (v.type() == nPath || v.type() == nString) {
|
Use `std::set<StringContextElem>` not `PathSet` for string contexts
Motivation
`PathSet` is not correct because string contexts have other forms
(`Built` and `DrvDeep`) that are not rendered as plain store paths.
Instead of wrongly using `PathSet`, or "stringly typed" using
`StringSet`, use `std::std<StringContextElem>`.
-----
In support of this change, `NixStringContext` is now defined as
`std::std<StringContextElem>` not `std:vector<StringContextElem>`. The
old definition was just used by a `getContext` method which was only
used by the eval cache. It can be deleted altogether since the types are
now unified and the preexisting `copyContext` function already suffices.
Summarizing the previous paragraph:
Old:
- `value/context.hh`: `NixStringContext = std::vector<StringContextElem>`
- `value.hh`: `NixStringContext Value::getContext(...)`
- `value.hh`: `copyContext(...)`
New:
- `value/context.hh`: `NixStringContext = std::set<StringContextElem>`
- `value.hh`: `copyContext(...)`
----
The string representation of string context elements no longer contains
the store dir. The diff of `src/libexpr/tests/value/context.cc` should
make clear what the new representation is, so we recommend reviewing
that file first. This was done for two reasons:
Less API churn:
`Value::mkString` and friends did not take a `Store` before. But if
`NixStringContextElem::{parse, to_string}` *do* take a store (as they
did before), then we cannot have the `Value` functions use them (in
order to work with the fully-structured `NixStringContext`) without
adding that argument.
That would have been a lot of churn of threading the store, and this
diff is already large enough, so the easier and less invasive thing to
do was simply make the element `parse` and `to_string` functions not
take the `Store` reference, and the easiest way to do that was to simply
drop the store dir.
Space usage:
Dropping the `/nix/store/` (or similar) from the internal representation
will safe space in the heap of the Nix programming being interpreted. If
the heap contains many strings with non-trivial contexts, the saving
could add up to something significant.
----
The eval cache version is bumped.
The eval cache serialization uses `NixStringContextElem::{parse,
to_string}`, and since those functions are changed per the above, that
means the on-disk representation is also changed.
This is simply done by changing the name of the used for the eval cache
from `eval-cache-v4` to eval-cache-v5`.
----
To avoid some duplication `EvalCache::mkPathString` is added to abstract
over the simple case of turning a store path to a string with just that
string in the context.
Context
This PR picks up where #7543 left off. That one introduced the fully
structured `NixStringContextElem` data type, but kept `PathSet context`
as an awkward middle ground between internal `char[][]` interpreter heap
string contexts and `NixStringContext` fully parsed string contexts.
The infelicity of `PathSet context` was specifically called out during
Nix team group review, but it was agreeing that fixing it could be left
as future work. This is that future work.
A possible follow-up step would be to get rid of the `char[][]`
evaluator heap representation, too, but it is not yet clear how to do
that. To use `NixStringContextElem` there we would need to get the STL
containers to GC pointers in the GC build, and I am not sure how to do
that.
----
PR #7543 effectively is writing the inverse of a `mkPathString`,
`mkOutputString`, and one more such function for the `DrvDeep` case. I
would like that PR to have property tests ensuring it is actually the
inverse as expected.
This PR sets things up nicely so that reworking that PR to be in that
more elegant and better tested way is possible.
Co-authored-by: Théophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com>
2023-01-29 01:31:10 +00:00
|
|
|
NixStringContext context;
|
2023-01-19 12:23:04 +00:00
|
|
|
auto path = state->coerceToPath(noPos, v, context, "while evaluating the filename to edit");
|
2022-12-12 23:48:04 +00:00
|
|
|
return {path, 0};
|
2022-03-04 19:54:50 +00:00
|
|
|
} else if (v.isLambda()) {
|
2022-03-04 18:31:59 +00:00
|
|
|
auto pos = state->positions[v.lambda.fun->pos];
|
2023-04-06 13:25:06 +00:00
|
|
|
if (auto path = std::get_if<SourcePath>(&pos.origin))
|
|
|
|
return {*path, pos.line};
|
2022-12-12 23:48:04 +00:00
|
|
|
else
|
|
|
|
throw Error("'%s' cannot be shown in an editor", pos);
|
2022-03-04 19:54:50 +00:00
|
|
|
} else {
|
|
|
|
// assume it's a derivation
|
|
|
|
return findPackageFilename(*state, v, arg);
|
|
|
|
}
|
|
|
|
}();
|
2019-10-23 13:29:16 +00:00
|
|
|
|
|
|
|
// Open in EDITOR
|
2022-12-12 23:48:04 +00:00
|
|
|
auto args = editorFor(path, line);
|
2019-10-23 14:48:28 +00:00
|
|
|
auto editor = args.front();
|
2019-10-23 13:29:16 +00:00
|
|
|
args.pop_front();
|
2021-11-17 15:38:03 +00:00
|
|
|
|
|
|
|
// runProgram redirects stdout to a StringSink,
|
|
|
|
// using runProgram2 to allow editors to display their UI
|
|
|
|
runProgram2(RunOptions { .program = editor, .searchPath = true, .args = args });
|
2019-10-23 13:29:16 +00:00
|
|
|
|
|
|
|
// Reload right after exiting the editor
|
2020-03-19 12:52:28 +00:00
|
|
|
state->resetFileCache();
|
2019-10-23 13:29:16 +00:00
|
|
|
reloadFiles();
|
|
|
|
}
|
|
|
|
|
2013-09-06 13:05:18 +00:00
|
|
|
else if (command == ":t") {
|
2013-09-02 16:00:48 +00:00
|
|
|
Value v;
|
2013-09-09 13:02:56 +00:00
|
|
|
evalString(arg, v);
|
2021-10-12 13:36:45 +00:00
|
|
|
logger->cout(showType(v));
|
|
|
|
}
|
2016-02-18 02:31:30 +00:00
|
|
|
|
2021-10-12 13:36:45 +00:00
|
|
|
else if (command == ":u") {
|
2016-02-18 02:31:30 +00:00
|
|
|
Value v, f, result;
|
|
|
|
evalString(arg, v);
|
|
|
|
evalString("drv: (import <nixpkgs> {}).runCommand \"shell\" { buildInputs = [ drv ]; } \"\"", f);
|
2022-03-04 18:31:59 +00:00
|
|
|
state->callFunction(f, v, result, PosIdx());
|
2016-02-18 02:31:30 +00:00
|
|
|
|
2020-06-17 03:56:48 +00:00
|
|
|
StorePath drvPath = getDerivationPath(result);
|
2021-06-29 12:52:46 +00:00
|
|
|
runNix("nix-shell", {state->store->printStorePath(drvPath)});
|
2013-09-02 16:00:48 +00:00
|
|
|
}
|
|
|
|
|
2022-05-05 21:24:57 +00:00
|
|
|
else if (command == ":b" || command == ":bl" || command == ":i" || command == ":sh" || command == ":log") {
|
2013-09-06 12:58:53 +00:00
|
|
|
Value v;
|
2013-09-09 13:02:56 +00:00
|
|
|
evalString(arg, v);
|
2020-06-17 03:56:48 +00:00
|
|
|
StorePath drvPath = getDerivationPath(v);
|
|
|
|
Path drvPathRaw = state->store->printStorePath(drvPath);
|
2013-09-06 13:05:18 +00:00
|
|
|
|
2022-04-18 17:21:47 +00:00
|
|
|
if (command == ":b" || command == ":bl") {
|
2023-01-11 21:32:30 +00:00
|
|
|
state->store->buildPaths({
|
|
|
|
DerivedPath::Built {
|
Make the Derived Path family of types inductive for dynamic derivations
We want to be able to write down `foo.drv^bar.drv^baz`:
`foo.drv^bar.drv` is the dynamic derivation (since it is itself a
derivation output, `bar.drv` from `foo.drv`).
To that end, we create `Single{Derivation,BuiltPath}` types, that are
very similar except instead of having multiple outputs (in a set or
map), they have a single one. This is for everything to the left of the
rightmost `^`.
`NixStringContextElem` has an analogous change, and now can reuse
`SingleDerivedPath` at the top level. In fact, if we ever get rid of
`DrvDeep`, `NixStringContextElem` could be replaced with
`SingleDerivedPath` entirely!
Important note: some JSON formats have changed.
We already can *produce* dynamic derivations, but we can't refer to them
directly. Today, we can merely express building or example at the top
imperatively over time by building `foo.drv^bar.drv`, and then with a
second nix invocation doing `<result-from-first>^baz`, but this is not
declarative. The ethos of Nix of being able to write down the full plan
everything you want to do, and then execute than plan with a single
command, and for that we need the new inductive form of these types.
Co-authored-by: Robert Hensing <roberth@users.noreply.github.com>
Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
2023-01-15 22:39:04 +00:00
|
|
|
.drvPath = makeConstantStorePathRef(drvPath),
|
2023-01-11 21:32:30 +00:00
|
|
|
.outputs = OutputsSpec::All { },
|
|
|
|
},
|
|
|
|
});
|
2021-10-12 13:27:02 +00:00
|
|
|
auto drv = state->store->readDerivation(drvPath);
|
|
|
|
logger->cout("\nThis derivation produced the following outputs:");
|
2022-04-18 17:21:47 +00:00
|
|
|
for (auto & [outputName, outputPath] : state->store->queryDerivationOutputMap(drvPath)) {
|
|
|
|
auto localStore = state->store.dynamic_pointer_cast<LocalFSStore>();
|
|
|
|
if (localStore && command == ":bl") {
|
|
|
|
std::string symlink = "repl-result-" + outputName;
|
|
|
|
localStore->addPermRoot(outputPath, absPath(symlink));
|
|
|
|
logger->cout(" ./%s -> %s", symlink, state->store->printStorePath(outputPath));
|
|
|
|
} else {
|
|
|
|
logger->cout(" %s -> %s", outputName, state->store->printStorePath(outputPath));
|
|
|
|
}
|
|
|
|
}
|
2016-02-16 06:24:50 +00:00
|
|
|
} else if (command == ":i") {
|
2021-06-29 12:52:46 +00:00
|
|
|
runNix("nix-env", {"-i", drvPathRaw});
|
2021-11-26 15:03:07 +00:00
|
|
|
} else if (command == ":log") {
|
|
|
|
settings.readOnlyMode = true;
|
|
|
|
Finally roModeReset([&]() {
|
|
|
|
settings.readOnlyMode = false;
|
|
|
|
});
|
|
|
|
auto subs = getDefaultSubstituters();
|
|
|
|
|
|
|
|
subs.push_front(state->store);
|
|
|
|
|
|
|
|
bool foundLog = false;
|
|
|
|
RunPager pager;
|
|
|
|
for (auto & sub : subs) {
|
2022-03-08 18:20:39 +00:00
|
|
|
auto * logSubP = dynamic_cast<LogStore *>(&*sub);
|
|
|
|
if (!logSubP) {
|
|
|
|
printInfo("Skipped '%s' which does not support retrieving build logs", sub->getUri());
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
auto & logSub = *logSubP;
|
|
|
|
|
|
|
|
auto log = logSub.getBuildLog(drvPath);
|
2021-11-26 15:03:07 +00:00
|
|
|
if (log) {
|
2022-03-08 18:20:39 +00:00
|
|
|
printInfo("got build log for '%s' from '%s'", drvPathRaw, logSub.getUri());
|
2021-11-26 15:03:07 +00:00
|
|
|
logger->writeToStdout(*log);
|
|
|
|
foundLog = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!foundLog) throw Error("build log of '%s' is not available", drvPathRaw);
|
2016-02-16 06:24:50 +00:00
|
|
|
} else {
|
2021-06-29 12:52:46 +00:00
|
|
|
runNix("nix-shell", {drvPathRaw});
|
2016-02-16 06:24:50 +00:00
|
|
|
}
|
2013-09-02 15:53:58 +00:00
|
|
|
}
|
|
|
|
|
2013-09-09 14:02:46 +00:00
|
|
|
else if (command == ":p" || command == ":print") {
|
2013-09-06 22:35:54 +00:00
|
|
|
Value v;
|
2013-09-09 13:02:56 +00:00
|
|
|
evalString(arg, v);
|
2024-03-11 09:02:09 +00:00
|
|
|
if (v.type() == nString) {
|
|
|
|
std::cout << v.string.s;
|
|
|
|
} else {
|
|
|
|
printValue(std::cout, v);
|
|
|
|
}
|
Unify and refactor value printing
Previously, there were two mostly-identical value printers -- one in
`libexpr/eval.cc` (which didn't force values) and one in
`libcmd/repl.cc` (which did force values and also printed ANSI color
codes).
This PR unifies both of these printers into `print.cc` and provides a
`PrintOptions` struct for controlling the output, which allows for
toggling whether values are forced, whether repeated values are tracked,
and whether ANSI color codes are displayed.
Additionally, `PrintOptions` allows tuning the maximum number of
attributes, list items, and bytes in a string that will be displayed;
this makes it ideal for contexts where printing too much output (e.g.
all of Nixpkgs) is distracting. (As requested by @roberth in
https://github.com/NixOS/nix/pull/9554#issuecomment-1845095735)
Please read the tests for example output.
Future work:
- It would be nice to provide this function as a builtin, perhaps
`builtins.toStringDebug` -- a printing function that never fails would
be useful when debugging Nix code.
- It would be nice to support customizing `PrintOptions` members on the
command line, e.g. `--option to-string-max-attrs 1000`.
(cherry picked from commit 0fa08b451682fb3311fe58112ff05c4fe5bee3a4, )
===
Restore ambiguous value printer for `nix-instantiate`
The Nix team has requested that this output format remain unchanged.
I've added a warning to the man page explaining that `nix-instantiate
--eval` output will not parse correctly in many situations.
(cherry picked from commit df84dd4d8dd3fd6381ac2ca3064432ab31a16b79)
Change-Id: I7cca6b4b53cd0642f2d49af657d5676a8554c9f8
2024-03-08 02:05:47 +00:00
|
|
|
std::cout << std::endl;
|
2013-09-06 22:35:54 +00:00
|
|
|
}
|
|
|
|
|
2022-02-15 16:49:25 +00:00
|
|
|
else if (command == ":q" || command == ":quit") {
|
2022-05-25 16:21:20 +00:00
|
|
|
state->debugStop = false;
|
2024-03-08 08:19:27 +00:00
|
|
|
return ProcessLineResult::Quit;
|
2022-02-15 16:49:25 +00:00
|
|
|
}
|
2013-09-09 13:02:56 +00:00
|
|
|
|
2020-08-24 11:11:56 +00:00
|
|
|
else if (command == ":doc") {
|
|
|
|
Value v;
|
|
|
|
evalString(arg, v);
|
2020-08-25 11:31:11 +00:00
|
|
|
if (auto doc = state->getDoc(v)) {
|
|
|
|
std::string markdown;
|
|
|
|
|
|
|
|
if (!doc->args.empty() && doc->name) {
|
|
|
|
auto args = doc->args;
|
2020-08-24 16:10:33 +00:00
|
|
|
for (auto & arg : args)
|
|
|
|
arg = "*" + arg + "*";
|
|
|
|
|
2020-08-25 11:31:11 +00:00
|
|
|
markdown +=
|
|
|
|
"**Synopsis:** `builtins." + (std::string) (*doc->name) + "` "
|
|
|
|
+ concatStringsSep(" ", args) + "\n\n";
|
|
|
|
}
|
|
|
|
|
2021-10-12 13:36:45 +00:00
|
|
|
markdown += stripIndentation(doc->doc);
|
2020-08-24 16:10:33 +00:00
|
|
|
|
2021-10-12 13:36:45 +00:00
|
|
|
logger->cout(trim(renderMarkdownToTerminal(markdown)));
|
2024-03-18 00:01:05 +00:00
|
|
|
} else if (v.isLambda()) {
|
|
|
|
auto pos = state->positions[v.lambda.fun->pos];
|
|
|
|
if (auto path = std::get_if<SourcePath>(&pos.origin)) {
|
|
|
|
// Path and position have now been obtained, feed to nix-doc library to get data.
|
|
|
|
auto docComment = lambdaDocsForPos(*path, pos);
|
|
|
|
if (!docComment) {
|
|
|
|
throw Error("lambda '%s' has no documentation comment", pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build and print Markdown representation of documentation comment.
|
|
|
|
std::string markdown = stripIndentation(docComment.get());
|
|
|
|
logger->cout(trim(renderMarkdownToTerminal(markdown)));
|
|
|
|
} else {
|
|
|
|
throw Error("lambda '%s' doesn't have a determinable source file", pos);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
throw Error("value '%s' does not have documentation", arg);
|
|
|
|
}
|
2020-08-24 11:11:56 +00:00
|
|
|
}
|
|
|
|
|
2022-05-05 21:24:57 +00:00
|
|
|
else if (command == ":te" || command == ":trace-enable") {
|
2021-12-28 13:13:07 +00:00
|
|
|
if (arg == "false" || (arg == "" && loggerSettings.showTrace)) {
|
|
|
|
std::cout << "not showing error traces\n";
|
|
|
|
loggerSettings.showTrace = false;
|
|
|
|
} else if (arg == "true" || (arg == "" && !loggerSettings.showTrace)) {
|
|
|
|
std::cout << "showing error traces\n";
|
|
|
|
loggerSettings.showTrace = true;
|
|
|
|
} else {
|
|
|
|
throw Error("unexpected argument '%s' to %s", arg, command);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2013-09-09 13:02:56 +00:00
|
|
|
else if (command != "")
|
2020-04-21 23:07:07 +00:00
|
|
|
throw Error("unknown command '%1%'", command);
|
2013-09-06 12:58:53 +00:00
|
|
|
|
2013-09-02 15:53:58 +00:00
|
|
|
else {
|
2013-09-09 11:56:53 +00:00
|
|
|
size_t p = line.find('=');
|
2022-02-25 15:00:00 +00:00
|
|
|
std::string name;
|
|
|
|
if (p != std::string::npos &&
|
2014-06-16 14:05:09 +00:00
|
|
|
p < line.size() &&
|
|
|
|
line[p + 1] != '=' &&
|
2022-02-25 15:00:00 +00:00
|
|
|
isVarName(name = removeWhitespace(line.substr(0, p))))
|
2013-09-09 11:56:53 +00:00
|
|
|
{
|
2022-02-25 15:00:00 +00:00
|
|
|
Expr * e = parseString(line.substr(p + 1));
|
2020-03-19 12:52:28 +00:00
|
|
|
Value & v(*state->allocValue());
|
2020-12-18 13:38:49 +00:00
|
|
|
v.mkThunk(env, e);
|
2020-03-19 12:52:28 +00:00
|
|
|
addVarToScope(state->symbols.create(name), v);
|
2013-09-09 11:56:53 +00:00
|
|
|
} else {
|
|
|
|
Value v;
|
|
|
|
evalString(line, v);
|
Unify and refactor value printing
Previously, there were two mostly-identical value printers -- one in
`libexpr/eval.cc` (which didn't force values) and one in
`libcmd/repl.cc` (which did force values and also printed ANSI color
codes).
This PR unifies both of these printers into `print.cc` and provides a
`PrintOptions` struct for controlling the output, which allows for
toggling whether values are forced, whether repeated values are tracked,
and whether ANSI color codes are displayed.
Additionally, `PrintOptions` allows tuning the maximum number of
attributes, list items, and bytes in a string that will be displayed;
this makes it ideal for contexts where printing too much output (e.g.
all of Nixpkgs) is distracting. (As requested by @roberth in
https://github.com/NixOS/nix/pull/9554#issuecomment-1845095735)
Please read the tests for example output.
Future work:
- It would be nice to provide this function as a builtin, perhaps
`builtins.toStringDebug` -- a printing function that never fails would
be useful when debugging Nix code.
- It would be nice to support customizing `PrintOptions` members on the
command line, e.g. `--option to-string-max-attrs 1000`.
(cherry picked from commit 0fa08b451682fb3311fe58112ff05c4fe5bee3a4, )
===
Restore ambiguous value printer for `nix-instantiate`
The Nix team has requested that this output format remain unchanged.
I've added a warning to the man page explaining that `nix-instantiate
--eval` output will not parse correctly in many situations.
(cherry picked from commit df84dd4d8dd3fd6381ac2ca3064432ab31a16b79)
Change-Id: I7cca6b4b53cd0642f2d49af657d5676a8554c9f8
2024-03-08 02:05:47 +00:00
|
|
|
printValue(std::cout, v, 1);
|
|
|
|
std::cout << std::endl;
|
2013-09-09 11:56:53 +00:00
|
|
|
}
|
2013-09-02 15:53:58 +00:00
|
|
|
}
|
2013-09-09 13:02:56 +00:00
|
|
|
|
2024-03-08 08:19:27 +00:00
|
|
|
return ProcessLineResult::PromptAgain;
|
2013-09-02 15:53:58 +00:00
|
|
|
}
|
|
|
|
|
2013-09-06 13:20:06 +00:00
|
|
|
void NixRepl::loadFile(const Path & path)
|
|
|
|
{
|
2013-09-09 14:02:46 +00:00
|
|
|
loadedFiles.remove(path);
|
|
|
|
loadedFiles.push_back(path);
|
2013-09-06 13:20:06 +00:00
|
|
|
Value v, v2;
|
2020-03-19 12:52:28 +00:00
|
|
|
state->evalFile(lookupFileArg(*state, path), v);
|
|
|
|
state->autoCallFunction(*autoArgs, v, v2);
|
2013-09-06 13:20:06 +00:00
|
|
|
addAttrsToScope(v2);
|
|
|
|
}
|
|
|
|
|
2021-07-19 15:47:03 +00:00
|
|
|
void NixRepl::loadFlake(const std::string & flakeRefS)
|
|
|
|
{
|
2022-02-06 23:28:21 +00:00
|
|
|
if (flakeRefS.empty())
|
2022-02-07 15:35:50 +00:00
|
|
|
throw Error("cannot use ':load-flake' without a path specified. (Use '.' for the current working directory.)");
|
2022-02-06 23:28:21 +00:00
|
|
|
|
2021-07-19 15:47:03 +00:00
|
|
|
auto flakeRef = parseFlakeRef(flakeRefS, absPath("."), true);
|
2022-02-24 17:09:00 +00:00
|
|
|
if (evalSettings.pureEval && !flakeRef.input.isLocked())
|
|
|
|
throw Error("cannot use ':load-flake' on locked flake reference '%s' (use --impure to override)", flakeRefS);
|
2021-07-19 15:47:03 +00:00
|
|
|
|
|
|
|
Value v;
|
|
|
|
|
|
|
|
flake::callFlake(*state,
|
|
|
|
flake::lockFlake(*state, flakeRef,
|
|
|
|
flake::LockFlags {
|
|
|
|
.updateLockFile = false,
|
|
|
|
.useRegistries = !evalSettings.pureEval,
|
2022-12-07 11:58:58 +00:00
|
|
|
.allowUnlocked = !evalSettings.pureEval,
|
2021-07-19 15:47:03 +00:00
|
|
|
}),
|
|
|
|
v);
|
|
|
|
addAttrsToScope(v);
|
|
|
|
}
|
|
|
|
|
2013-09-06 13:20:06 +00:00
|
|
|
|
2013-09-09 15:06:14 +00:00
|
|
|
void NixRepl::initEnv()
|
|
|
|
{
|
2020-03-19 12:52:28 +00:00
|
|
|
env = &state->allocEnv(envSize);
|
|
|
|
env->up = &state->baseEnv;
|
2013-09-09 15:06:14 +00:00
|
|
|
displ = 0;
|
2021-09-14 16:49:22 +00:00
|
|
|
staticEnv->vars.clear();
|
2013-09-09 15:22:42 +00:00
|
|
|
|
|
|
|
varNames.clear();
|
2022-05-25 16:21:20 +00:00
|
|
|
for (auto & i : state->staticBaseEnv->vars)
|
2022-03-05 13:40:24 +00:00
|
|
|
varNames.emplace(state->symbols[i.first]);
|
2013-09-09 15:06:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-09 14:02:46 +00:00
|
|
|
void NixRepl::reloadFiles()
|
|
|
|
{
|
2013-09-09 15:06:14 +00:00
|
|
|
initEnv();
|
|
|
|
|
2021-12-20 19:32:21 +00:00
|
|
|
loadFiles();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void NixRepl::loadFiles()
|
|
|
|
{
|
2013-09-09 14:02:46 +00:00
|
|
|
Strings old = loadedFiles;
|
|
|
|
loadedFiles.clear();
|
|
|
|
|
2015-09-07 11:05:58 +00:00
|
|
|
for (auto & i : old) {
|
2021-10-12 13:36:45 +00:00
|
|
|
notice("Loading '%1%'...", i);
|
2015-09-07 11:05:58 +00:00
|
|
|
loadFile(i);
|
2013-09-09 14:02:46 +00:00
|
|
|
}
|
2021-12-22 22:36:08 +00:00
|
|
|
|
2022-05-20 05:28:20 +00:00
|
|
|
for (auto & [i, what] : getValues()) {
|
|
|
|
notice("Loading installable '%1%'...", what);
|
2022-02-18 23:33:03 +00:00
|
|
|
addAttrsToScope(*i);
|
2013-09-09 14:02:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-02 16:18:27 +00:00
|
|
|
void NixRepl::addAttrsToScope(Value & attrs)
|
|
|
|
{
|
2023-01-19 12:23:04 +00:00
|
|
|
state->forceAttrs(attrs, [&]() { return attrs.determinePos(noPos); }, "while evaluating an attribute set to be merged in the global scope");
|
2021-12-27 12:18:55 +00:00
|
|
|
if (displ + attrs.attrs->size() >= envSize)
|
|
|
|
throw Error("environment full; cannot add more variables");
|
|
|
|
|
|
|
|
for (auto & i : *attrs.attrs) {
|
2022-01-04 01:13:16 +00:00
|
|
|
staticEnv->vars.emplace_back(i.name, displ);
|
2021-12-27 12:18:55 +00:00
|
|
|
env->values[displ++] = i.value;
|
2022-03-05 13:40:24 +00:00
|
|
|
varNames.emplace(state->symbols[i.name]);
|
2021-12-27 12:18:55 +00:00
|
|
|
}
|
2022-01-04 01:13:16 +00:00
|
|
|
staticEnv->sort();
|
|
|
|
staticEnv->deduplicate();
|
2021-10-12 13:36:45 +00:00
|
|
|
notice("Added %1% variables.", attrs.attrs->size());
|
2013-09-02 16:18:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-04-22 19:45:39 +00:00
|
|
|
void NixRepl::addVarToScope(const Symbol name, Value & v)
|
2013-09-02 15:53:58 +00:00
|
|
|
{
|
2013-09-09 15:06:14 +00:00
|
|
|
if (displ >= envSize)
|
|
|
|
throw Error("environment full; cannot add more variables");
|
2021-12-20 19:32:21 +00:00
|
|
|
if (auto oldVar = staticEnv->find(name); oldVar != staticEnv->vars.end())
|
|
|
|
staticEnv->vars.erase(oldVar);
|
2021-11-25 15:53:59 +00:00
|
|
|
staticEnv->vars.emplace_back(name, displ);
|
|
|
|
staticEnv->sort();
|
2013-09-09 11:56:53 +00:00
|
|
|
env->values[displ++] = &v;
|
2022-03-05 13:40:24 +00:00
|
|
|
varNames.emplace(state->symbols[name]);
|
2013-09-02 15:53:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-02-25 15:00:00 +00:00
|
|
|
Expr * NixRepl::parseString(std::string s)
|
2013-09-02 15:53:58 +00:00
|
|
|
{
|
2023-04-06 11:15:50 +00:00
|
|
|
return state->parseExprFromString(std::move(s), state->rootPath(CanonPath::fromCwd()), staticEnv);
|
2013-09-02 15:53:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-02-25 15:00:00 +00:00
|
|
|
void NixRepl::evalString(std::string s, Value & v)
|
2013-09-02 16:00:48 +00:00
|
|
|
{
|
|
|
|
Expr * e = parseString(s);
|
2020-03-19 12:52:28 +00:00
|
|
|
e->eval(*state, *env, v);
|
2024-03-04 06:32:31 +00:00
|
|
|
state->forceValue(v, v.determinePos(noPos));
|
2013-09-02 16:00:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-02-04 03:42:36 +00:00
|
|
|
std::unique_ptr<AbstractNixRepl> AbstractNixRepl::create(
|
2023-06-23 17:51:25 +00:00
|
|
|
const SearchPath & searchPath, nix::ref<Store> store, ref<EvalState> state,
|
2023-02-04 03:42:36 +00:00
|
|
|
std::function<AnnotatedValues()> getValues)
|
|
|
|
{
|
|
|
|
return std::make_unique<NixRepl>(
|
|
|
|
searchPath,
|
|
|
|
openStore(),
|
|
|
|
state,
|
|
|
|
getValues
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-03-08 08:19:15 +00:00
|
|
|
ReplExitStatus AbstractNixRepl::runSimple(
|
2023-02-04 03:42:36 +00:00
|
|
|
ref<EvalState> evalState,
|
2022-05-06 02:26:10 +00:00
|
|
|
const ValMap & extraEnv)
|
2013-09-02 15:53:58 +00:00
|
|
|
{
|
2022-06-02 20:34:27 +00:00
|
|
|
auto getValues = [&]()->NixRepl::AnnotatedValues{
|
|
|
|
NixRepl::AnnotatedValues values;
|
|
|
|
return values;
|
|
|
|
};
|
2023-06-23 17:51:25 +00:00
|
|
|
SearchPath searchPath = {};
|
2022-06-02 20:34:27 +00:00
|
|
|
auto repl = std::make_unique<NixRepl>(
|
|
|
|
searchPath,
|
|
|
|
openStore(),
|
|
|
|
evalState,
|
|
|
|
getValues
|
|
|
|
);
|
2020-08-05 19:26:17 +00:00
|
|
|
|
|
|
|
repl->initEnv();
|
|
|
|
|
2021-12-27 21:06:04 +00:00
|
|
|
// add 'extra' vars.
|
2022-05-05 10:29:14 +00:00
|
|
|
for (auto & [name, value] : extraEnv)
|
2022-05-25 16:21:20 +00:00
|
|
|
repl->addVarToScope(repl->state->symbols.create(name), *value);
|
2020-08-05 19:26:17 +00:00
|
|
|
|
2024-03-08 08:19:15 +00:00
|
|
|
return repl->mainLoop();
|
2020-08-05 19:26:17 +00:00
|
|
|
}
|
|
|
|
|
2013-09-02 15:53:58 +00:00
|
|
|
}
|