From e4fb9a38493a041861fe5c75bc8ddd129a2e5262 Mon Sep 17 00:00:00 2001 From: Ben Burdette Date: Tue, 21 Apr 2020 17:07:07 -0600 Subject: remove 'format' from Error constructor calls --- src/libmain/shared.cc | 10 +++++----- src/libmain/shared.hh | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src/libmain') diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index d41e772e9..f7ab56271 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -75,7 +75,7 @@ string getArg(const string & opt, Strings::iterator & i, const Strings::iterator & end) { ++i; - if (i == end) throw UsageError(format("'%1%' requires an argument") % opt); + if (i == end) throw UsageError("'%1%' requires an argument", opt); return *i; } @@ -229,7 +229,7 @@ bool LegacyArgs::processArgs(const Strings & args, bool finish) Strings ss(args); auto pos = ss.begin(); if (!parseArg(pos, ss.end())) - throw UsageError(format("unexpected argument '%1%'") % args.front()); + throw UsageError("unexpected argument '%1%'", args.front()); return true; } @@ -273,7 +273,7 @@ void showManPage(const string & name) restoreSignals(); setenv("MANPATH", settings.nixManDir.c_str(), 1); execlp("man", "man", name.c_str(), nullptr); - throw SysError(format("command 'man %1%' failed") % name.c_str()); + throw SysError("command 'man %1%' failed", name.c_str()); } @@ -301,7 +301,7 @@ int handleExceptions(const string & programName, std::function fun) % e.what() % programName); return 1; } catch (BaseError & e) { - printError(format(error + "%1%%2%") % (settings.showTrace ? e.prefix() : "") % e.msg()); + printError(error + "%1%%2%", (settings.showTrace ? e.prefix() : ""), e.msg()); if (e.prefix() != "" && !settings.showTrace) printError("(use '--show-trace' to show detailed location information)"); return e.status; @@ -338,7 +338,7 @@ RunPager::RunPager() execlp("pager", "pager", nullptr); execlp("less", "less", nullptr); execlp("more", "more", nullptr); - throw SysError(format("executing '%1%'") % pager); + throw SysError("executing '%1%'", pager); }); pid.setKillSignal(SIGINT); diff --git a/src/libmain/shared.hh b/src/libmain/shared.hh index b49574652..f558247c0 100644 --- a/src/libmain/shared.hh +++ b/src/libmain/shared.hh @@ -56,7 +56,7 @@ template N getIntArg(const string & opt, Strings::iterator & i, const Strings::iterator & end, bool allowUnit) { ++i; - if (i == end) throw UsageError(format("'%1%' requires an argument") % opt); + if (i == end) throw UsageError("'%1%' requires an argument", opt); string s = *i; N multiplier = 1; if (allowUnit && !s.empty()) { @@ -66,13 +66,13 @@ template N getIntArg(const string & opt, else if (u == 'M') multiplier = 1ULL << 20; else if (u == 'G') multiplier = 1ULL << 30; else if (u == 'T') multiplier = 1ULL << 40; - else throw UsageError(format("invalid unit specifier '%1%'") % u); + else throw UsageError("invalid unit specifier '%1%'", u); s.resize(s.size() - 1); } } N n; if (!string2Int(s, n)) - throw UsageError(format("'%1%' requires an integer argument") % opt); + throw UsageError("'%1%' requires an integer argument", opt); return n * multiplier; } -- cgit v1.2.3 From d8d4844b883dcea67cccb9bd75eecb14e60ae496 Mon Sep 17 00:00:00 2001 From: Ben Burdette Date: Fri, 24 Apr 2020 14:57:51 -0600 Subject: all things error to error.hh --- src/libmain/shared.cc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src/libmain') diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index f7ab56271..8551eb048 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -281,6 +281,8 @@ int handleExceptions(const string & programName, std::function fun) { ReceiveInterrupts receiveInterrupts; // FIXME: need better place for this + ErrorInfo::programName = programName; + string error = ANSI_RED "error:" ANSI_NORMAL " "; try { try { @@ -296,12 +298,15 @@ int handleExceptions(const string & programName, std::function fun) } catch (Exit & e) { return e.status; } catch (UsageError & e) { + // TODO: switch to logError + // logError(e.info()); printError( - format(error + "%1%\nTry '%2% --help' for more information.") + format("%1%\nTry '%2% --help' for more information.") % e.what() % programName); return 1; } catch (BaseError & e) { - printError(error + "%1%%2%", (settings.showTrace ? e.prefix() : ""), e.msg()); + // logError(e.info()); + printError("%1%%2%", (settings.showTrace ? e.prefix() : ""), e.msg()); if (e.prefix() != "" && !settings.showTrace) printError("(use '--show-trace' to show detailed location information)"); return e.status; -- cgit v1.2.3 From ab6f0b9641ad6e9cef72f73d23e31138a97a225b Mon Sep 17 00:00:00 2001 From: Ben Burdette Date: Sun, 3 May 2020 08:01:25 -0600 Subject: convert some printError calls to logError --- src/libmain/shared.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/libmain') diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index 8551eb048..5773d90cc 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -300,21 +300,21 @@ int handleExceptions(const string & programName, std::function fun) } catch (UsageError & e) { // TODO: switch to logError // logError(e.info()); - printError( + _printError( format("%1%\nTry '%2% --help' for more information.") % e.what() % programName); return 1; } catch (BaseError & e) { // logError(e.info()); - printError("%1%%2%", (settings.showTrace ? e.prefix() : ""), e.msg()); + _printError("%1%%2%", (settings.showTrace ? e.prefix() : ""), e.msg()); if (e.prefix() != "" && !settings.showTrace) - printError("(use '--show-trace' to show detailed location information)"); + _printError("(use '--show-trace' to show detailed location information)"); return e.status; } catch (std::bad_alloc & e) { - printError(error + "out of memory"); + _printError(error + "out of memory"); return 1; } catch (std::exception & e) { - printError(error + e.what()); + _printError(error + e.what()); return 1; } -- cgit v1.2.3 From 9c5ece44a7f32784dd5e2ea0faf110054f9233a8 Mon Sep 17 00:00:00 2001 From: Ben Burdette Date: Mon, 4 May 2020 13:46:15 -0600 Subject: separate msgs instead of appending to what() --- src/libmain/shared.cc | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'src/libmain') diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index 5773d90cc..db1e0ba1d 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -298,23 +298,21 @@ int handleExceptions(const string & programName, std::function fun) } catch (Exit & e) { return e.status; } catch (UsageError & e) { - // TODO: switch to logError - // logError(e.info()); - _printError( - format("%1%\nTry '%2% --help' for more information.") - % e.what() % programName); + logError(e.info()); + printError("Try '%1% --help' for more information.", programName); return 1; } catch (BaseError & e) { - // logError(e.info()); - _printError("%1%%2%", (settings.showTrace ? e.prefix() : ""), e.msg()); + if (settings.showTrace && e.prefix() != "") + printError(e.prefix()); + logError(e.info()); if (e.prefix() != "" && !settings.showTrace) - _printError("(use '--show-trace' to show detailed location information)"); + printError("(use '--show-trace' to show detailed location information)"); return e.status; } catch (std::bad_alloc & e) { - _printError(error + "out of memory"); + printError(error + "out of memory"); return 1; } catch (std::exception & e) { - _printError(error + e.what()); + printError(error + e.what()); return 1; } -- cgit v1.2.3 From b93c1bf3d67716231d2ff7ee4154b8c80a251b10 Mon Sep 17 00:00:00 2001 From: Ben Burdette Date: Mon, 11 May 2020 15:52:15 -0600 Subject: fixes to merged code --- src/libmain/stack.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libmain') diff --git a/src/libmain/stack.cc b/src/libmain/stack.cc index e6224de7d..b0a4a4c5d 100644 --- a/src/libmain/stack.cc +++ b/src/libmain/stack.cc @@ -1,4 +1,4 @@ -#include "types.hh" +#include "error.hh" #include #include -- cgit v1.2.3 From 170e86dff5724264e0d3d25b9af1bd42df6aec74 Mon Sep 17 00:00:00 2001 From: regnat Date: Fri, 5 Jun 2020 17:01:02 +0200 Subject: Make the logger customisable Add a new `--log-format` cli argument to change the format of the logs. The possible values are - raw (the default one for old-style commands) - bar (the default one for new-style commands) - bar-with-logs (equivalent to `--print-build-logs`) - internal-json (the internal machine-readable json format) --- src/libmain/common-args.cc | 9 +++++++++ src/libmain/loggers.cc | 47 ++++++++++++++++++++++++++++++++++++++++++++++ src/libmain/loggers.hh | 19 +++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 src/libmain/loggers.cc create mode 100644 src/libmain/loggers.hh (limited to 'src/libmain') diff --git a/src/libmain/common-args.cc b/src/libmain/common-args.cc index 51e199ea5..a0cccceb4 100644 --- a/src/libmain/common-args.cc +++ b/src/libmain/common-args.cc @@ -1,5 +1,6 @@ #include "common-args.hh" #include "globals.hh" +#include "loggers.hh" namespace nix { @@ -38,6 +39,14 @@ MixCommonArgs::MixCommonArgs(const string & programName) }}, }); + addFlag({ + .longName = "log-format", + .description = "Format of the logs. One of \"raw\", \"internal-json\", \"bar\" " + "or \"bar-with-logs\".", + .labels = {"format"}, + .handler = {[](std::string format) { setLogFormat(format); }}, + }); + addFlag({ .longName = "max-jobs", .shortName = 'j', diff --git a/src/libmain/loggers.cc b/src/libmain/loggers.cc new file mode 100644 index 000000000..d3d5b104b --- /dev/null +++ b/src/libmain/loggers.cc @@ -0,0 +1,47 @@ +#include "loggers.hh" +#include "../nix/progress-bar.hh" + +namespace nix { + +LogFormat defaultLogFormat = LogFormat::raw; + +LogFormat parseLogFormat(const string &logFormatStr) { + if (logFormatStr == "raw") + return LogFormat::raw; + else if (logFormatStr == "internal-json") + return LogFormat::internalJson; + else if (logFormatStr == "bar") + return LogFormat::bar; + else if (logFormatStr == "bar-with-logs") + return LogFormat::barWithLogs; + throw Error(format("option 'log-format' has an invalid value '%s'") % + logFormatStr); +} + +Logger *makeDefaultLogger() { + switch (defaultLogFormat) { + case LogFormat::raw: + return makeSimpleLogger(); + case LogFormat::internalJson: + return makeJSONLogger(*makeSimpleLogger()); + case LogFormat::bar: + return makeProgressBar(); + case LogFormat::barWithLogs: + return makeProgressBar(true); + } +} + +void setLogFormat(const string &logFormatStr) { + setLogFormat(parseLogFormat(logFormatStr)); +} + +void setLogFormat(const LogFormat &logFormat) { + defaultLogFormat = logFormat; + createDefaultLogger(); +} + +void createDefaultLogger() { + logger = makeDefaultLogger(); +} + +} diff --git a/src/libmain/loggers.hh b/src/libmain/loggers.hh new file mode 100644 index 000000000..f50cbb682 --- /dev/null +++ b/src/libmain/loggers.hh @@ -0,0 +1,19 @@ +#pragma once + +#include "types.hh" + +namespace nix { + +enum class LogFormat { + raw, + internalJson, + bar, + barWithLogs, +}; + +void setLogFormat(const string &logFormatStr); +void setLogFormat(const LogFormat &logFormat); + +void createDefaultLogger(); + +} -- cgit v1.2.3 From 4983401440e1c46d6c576bc36ac86169bd296f9f Mon Sep 17 00:00:00 2001 From: regnat Date: Fri, 5 Jun 2020 18:20:11 +0200 Subject: Unify the printing of the logs between bar-with-logs and raw Make the printing of the build logs systematically go through the logger, and replicate the behavior of `no-build-output` by having two different loggers (one that prints the build logs and one that doesn't) --- src/libmain/loggers.cc | 6 +++++- src/libmain/loggers.hh | 1 + src/libmain/shared.cc | 3 ++- 3 files changed, 8 insertions(+), 2 deletions(-) (limited to 'src/libmain') diff --git a/src/libmain/loggers.cc b/src/libmain/loggers.cc index d3d5b104b..b81096931 100644 --- a/src/libmain/loggers.cc +++ b/src/libmain/loggers.cc @@ -8,6 +8,8 @@ LogFormat defaultLogFormat = LogFormat::raw; LogFormat parseLogFormat(const string &logFormatStr) { if (logFormatStr == "raw") return LogFormat::raw; + else if (logFormatStr == "raw-with-logs") + return LogFormat::rawWithLogs; else if (logFormatStr == "internal-json") return LogFormat::internalJson; else if (logFormatStr == "bar") @@ -21,7 +23,9 @@ LogFormat parseLogFormat(const string &logFormatStr) { Logger *makeDefaultLogger() { switch (defaultLogFormat) { case LogFormat::raw: - return makeSimpleLogger(); + return makeSimpleLogger(false); + case LogFormat::rawWithLogs: + return makeSimpleLogger(true); case LogFormat::internalJson: return makeJSONLogger(*makeSimpleLogger()); case LogFormat::bar: diff --git a/src/libmain/loggers.hh b/src/libmain/loggers.hh index f50cbb682..f9ba5ee5e 100644 --- a/src/libmain/loggers.hh +++ b/src/libmain/loggers.hh @@ -6,6 +6,7 @@ namespace nix { enum class LogFormat { raw, + rawWithLogs, internalJson, bar, barWithLogs, diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index 70d1f0186..3bbb5cf93 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -2,6 +2,7 @@ #include "shared.hh" #include "store-api.hh" #include "util.hh" +#include "loggers.hh" #include #include @@ -169,7 +170,7 @@ LegacyArgs::LegacyArgs(const std::string & programName, .longName = "no-build-output", .shortName = 'Q', .description = "do not show build output", - .handler = {&settings.verboseBuild, false}, + .handler = {[&]() {setLogFormat(LogFormat::raw); }}, }); addFlag({ -- cgit v1.2.3 From c27f92698bbbb99df3c4403db6590276318940a0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 8 Jun 2020 13:24:01 +0200 Subject: Style fixes --- src/libmain/common-args.cc | 4 ++-- src/libmain/loggers.cc | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'src/libmain') diff --git a/src/libmain/common-args.cc b/src/libmain/common-args.cc index a0cccceb4..051668e53 100644 --- a/src/libmain/common-args.cc +++ b/src/libmain/common-args.cc @@ -41,8 +41,8 @@ MixCommonArgs::MixCommonArgs(const string & programName) addFlag({ .longName = "log-format", - .description = "Format of the logs. One of \"raw\", \"internal-json\", \"bar\" " - "or \"bar-with-logs\".", + .description = "format of log output; \"raw\", \"internal-json\", \"bar\" " + "or \"bar-with-logs\"", .labels = {"format"}, .handler = {[](std::string format) { setLogFormat(format); }}, }); diff --git a/src/libmain/loggers.cc b/src/libmain/loggers.cc index b81096931..ee3836382 100644 --- a/src/libmain/loggers.cc +++ b/src/libmain/loggers.cc @@ -16,8 +16,7 @@ LogFormat parseLogFormat(const string &logFormatStr) { return LogFormat::bar; else if (logFormatStr == "bar-with-logs") return LogFormat::barWithLogs; - throw Error(format("option 'log-format' has an invalid value '%s'") % - logFormatStr); + throw Error("option 'log-format' has an invalid value '%s'", logFormatStr); } Logger *makeDefaultLogger() { -- cgit v1.2.3 From 801112de1af8e519e29e1e1c678f97de45a8c49d Mon Sep 17 00:00:00 2001 From: regnat Date: Mon, 8 Jun 2020 17:16:52 +0200 Subject: Move progress-bar.cc to libmain Needed so that we can include it as a logger in loggers.cc without adding a dependency on nix This also requires moving names.hh to libutil to prevent a circular dependency between libmain and libexpr --- src/libmain/loggers.cc | 2 +- src/libmain/progress-bar.cc | 484 ++++++++++++++++++++++++++++++++++++++++++++ src/libmain/progress-bar.hh | 13 ++ 3 files changed, 498 insertions(+), 1 deletion(-) create mode 100644 src/libmain/progress-bar.cc create mode 100644 src/libmain/progress-bar.hh (limited to 'src/libmain') diff --git a/src/libmain/loggers.cc b/src/libmain/loggers.cc index ee3836382..41f0b35fb 100644 --- a/src/libmain/loggers.cc +++ b/src/libmain/loggers.cc @@ -1,5 +1,5 @@ #include "loggers.hh" -#include "../nix/progress-bar.hh" +#include "progress-bar.hh" namespace nix { diff --git a/src/libmain/progress-bar.cc b/src/libmain/progress-bar.cc new file mode 100644 index 000000000..c9ba89714 --- /dev/null +++ b/src/libmain/progress-bar.cc @@ -0,0 +1,484 @@ +#include "progress-bar.hh" +#include "util.hh" +#include "sync.hh" +#include "store-api.hh" +#include "names.hh" + +#include +#include +#include +#include + +namespace nix { + +static std::string getS(const std::vector & fields, size_t n) +{ + assert(n < fields.size()); + assert(fields[n].type == Logger::Field::tString); + return fields[n].s; +} + +static uint64_t getI(const std::vector & fields, size_t n) +{ + assert(n < fields.size()); + assert(fields[n].type == Logger::Field::tInt); + return fields[n].i; +} + +static std::string_view storePathToName(std::string_view path) +{ + auto base = baseNameOf(path); + auto i = base.find('-'); + return i == std::string::npos ? base.substr(0, 0) : base.substr(i + 1); +} + +class ProgressBar : public Logger +{ +private: + + struct ActInfo + { + std::string s, lastLine, phase; + ActivityType type = actUnknown; + uint64_t done = 0; + uint64_t expected = 0; + uint64_t running = 0; + uint64_t failed = 0; + std::map expectedByType; + bool visible = true; + ActivityId parent; + std::optional name; + }; + + struct ActivitiesByType + { + std::map::iterator> its; + uint64_t done = 0; + uint64_t expected = 0; + uint64_t failed = 0; + }; + + struct State + { + std::list activities; + std::map::iterator> its; + + std::map activitiesByType; + + uint64_t filesLinked = 0, bytesLinked = 0; + + uint64_t corruptedPaths = 0, untrustedPaths = 0; + + bool active = true; + bool haveUpdate = true; + }; + + Sync state_; + + std::thread updateThread; + + std::condition_variable quitCV, updateCV; + + bool printBuildLogs; + bool isTTY; + +public: + + ProgressBar(bool printBuildLogs, bool isTTY) + : printBuildLogs(printBuildLogs) + , isTTY(isTTY) + { + state_.lock()->active = isTTY; + updateThread = std::thread([&]() { + auto state(state_.lock()); + while (state->active) { + if (!state->haveUpdate) + state.wait(updateCV); + draw(*state); + state.wait_for(quitCV, std::chrono::milliseconds(50)); + } + }); + } + + ~ProgressBar() + { + stop(); + updateThread.join(); + } + + void stop() override + { + auto state(state_.lock()); + if (!state->active) return; + state->active = false; + std::string status = getStatus(*state); + writeToStderr("\r\e[K"); + if (status != "") + writeToStderr("[" + status + "]\n"); + updateCV.notify_one(); + quitCV.notify_one(); + } + + bool isVerbose() override { + return printBuildLogs; + } + + void log(Verbosity lvl, const FormatOrString & fs) override + { + auto state(state_.lock()); + log(*state, lvl, fs.s); + } + + void log(State & state, Verbosity lvl, const std::string & s) + { + if (state.active) { + writeToStderr("\r\e[K" + filterANSIEscapes(s, !isTTY) + ANSI_NORMAL "\n"); + draw(state); + } else { + auto s2 = s + ANSI_NORMAL "\n"; + if (!isTTY) s2 = filterANSIEscapes(s2, true); + writeToStderr(s2); + } + } + + void startActivity(ActivityId act, Verbosity lvl, ActivityType type, + const std::string & s, const Fields & fields, ActivityId parent) override + { + auto state(state_.lock()); + + if (lvl <= verbosity && !s.empty()) + log(*state, lvl, s + "..."); + + state->activities.emplace_back(ActInfo()); + auto i = std::prev(state->activities.end()); + i->s = s; + i->type = type; + i->parent = parent; + state->its.emplace(act, i); + state->activitiesByType[type].its.emplace(act, i); + + if (type == actBuild) { + std::string name(storePathToName(getS(fields, 0))); + if (hasSuffix(name, ".drv")) + name = name.substr(0, name.size() - 4); + i->s = fmt("building " ANSI_BOLD "%s" ANSI_NORMAL, name); + auto machineName = getS(fields, 1); + if (machineName != "") + i->s += fmt(" on " ANSI_BOLD "%s" ANSI_NORMAL, machineName); + auto curRound = getI(fields, 2); + auto nrRounds = getI(fields, 3); + if (nrRounds != 1) + i->s += fmt(" (round %d/%d)", curRound, nrRounds); + i->name = DrvName(name).name; + } + + if (type == actSubstitute) { + auto name = storePathToName(getS(fields, 0)); + auto sub = getS(fields, 1); + i->s = fmt( + hasPrefix(sub, "local") + ? "copying " ANSI_BOLD "%s" ANSI_NORMAL " from %s" + : "fetching " ANSI_BOLD "%s" ANSI_NORMAL " from %s", + name, sub); + } + + if (type == actPostBuildHook) { + auto name = storePathToName(getS(fields, 0)); + if (hasSuffix(name, ".drv")) + name = name.substr(0, name.size() - 4); + i->s = fmt("post-build " ANSI_BOLD "%s" ANSI_NORMAL, name); + i->name = DrvName(name).name; + } + + if (type == actQueryPathInfo) { + auto name = storePathToName(getS(fields, 0)); + i->s = fmt("querying " ANSI_BOLD "%s" ANSI_NORMAL " on %s", name, getS(fields, 1)); + } + + if ((type == actFileTransfer && hasAncestor(*state, actCopyPath, parent)) + || (type == actFileTransfer && hasAncestor(*state, actQueryPathInfo, parent)) + || (type == actCopyPath && hasAncestor(*state, actSubstitute, parent))) + i->visible = false; + + update(*state); + } + + /* Check whether an activity has an ancestore with the specified + type. */ + bool hasAncestor(State & state, ActivityType type, ActivityId act) + { + while (act != 0) { + auto i = state.its.find(act); + if (i == state.its.end()) break; + if (i->second->type == type) return true; + act = i->second->parent; + } + return false; + } + + void stopActivity(ActivityId act) override + { + auto state(state_.lock()); + + auto i = state->its.find(act); + if (i != state->its.end()) { + + auto & actByType = state->activitiesByType[i->second->type]; + actByType.done += i->second->done; + actByType.failed += i->second->failed; + + for (auto & j : i->second->expectedByType) + state->activitiesByType[j.first].expected -= j.second; + + actByType.its.erase(act); + state->activities.erase(i->second); + state->its.erase(i); + } + + update(*state); + } + + void result(ActivityId act, ResultType type, const std::vector & fields) override + { + auto state(state_.lock()); + + if (type == resFileLinked) { + state->filesLinked++; + state->bytesLinked += getI(fields, 0); + update(*state); + } + + else if (type == resBuildLogLine || type == resPostBuildLogLine) { + auto lastLine = trim(getS(fields, 0)); + if (!lastLine.empty()) { + auto i = state->its.find(act); + assert(i != state->its.end()); + ActInfo info = *i->second; + if (printBuildLogs) { + auto suffix = "> "; + if (type == resPostBuildLogLine) { + suffix = " (post)> "; + } + log(*state, lvlInfo, ANSI_FAINT + info.name.value_or("unnamed") + suffix + ANSI_NORMAL + lastLine); + } else { + state->activities.erase(i->second); + info.lastLine = lastLine; + state->activities.emplace_back(info); + i->second = std::prev(state->activities.end()); + update(*state); + } + } + } + + else if (type == resUntrustedPath) { + state->untrustedPaths++; + update(*state); + } + + else if (type == resCorruptedPath) { + state->corruptedPaths++; + update(*state); + } + + else if (type == resSetPhase) { + auto i = state->its.find(act); + assert(i != state->its.end()); + i->second->phase = getS(fields, 0); + update(*state); + } + + else if (type == resProgress) { + auto i = state->its.find(act); + assert(i != state->its.end()); + ActInfo & actInfo = *i->second; + actInfo.done = getI(fields, 0); + actInfo.expected = getI(fields, 1); + actInfo.running = getI(fields, 2); + actInfo.failed = getI(fields, 3); + update(*state); + } + + else if (type == resSetExpected) { + auto i = state->its.find(act); + assert(i != state->its.end()); + ActInfo & actInfo = *i->second; + auto type = (ActivityType) getI(fields, 0); + auto & j = actInfo.expectedByType[type]; + state->activitiesByType[type].expected -= j; + j = getI(fields, 1); + state->activitiesByType[type].expected += j; + update(*state); + } + } + + void update(State & state) + { + state.haveUpdate = true; + updateCV.notify_one(); + } + + void draw(State & state) + { + state.haveUpdate = false; + if (!state.active) return; + + std::string line; + + std::string status = getStatus(state); + if (!status.empty()) { + line += '['; + line += status; + line += "]"; + } + + if (!state.activities.empty()) { + if (!status.empty()) line += " "; + auto i = state.activities.rbegin(); + + while (i != state.activities.rend() && (!i->visible || (i->s.empty() && i->lastLine.empty()))) + ++i; + + if (i != state.activities.rend()) { + line += i->s; + if (!i->phase.empty()) { + line += " ("; + line += i->phase; + line += ")"; + } + if (!i->lastLine.empty()) { + if (!i->s.empty()) line += ": "; + line += i->lastLine; + } + } + } + + auto width = getWindowSize().second; + if (width <= 0) width = std::numeric_limits::max(); + + writeToStderr("\r" + filterANSIEscapes(line, false, width) + "\e[K"); + } + + std::string getStatus(State & state) + { + auto MiB = 1024.0 * 1024.0; + + std::string res; + + auto renderActivity = [&](ActivityType type, const std::string & itemFmt, const std::string & numberFmt = "%d", double unit = 1) { + auto & act = state.activitiesByType[type]; + uint64_t done = act.done, expected = act.done, running = 0, failed = act.failed; + for (auto & j : act.its) { + done += j.second->done; + expected += j.second->expected; + running += j.second->running; + failed += j.second->failed; + } + + expected = std::max(expected, act.expected); + + std::string s; + + if (running || done || expected || failed) { + if (running) + if (expected != 0) + s = fmt(ANSI_BLUE + numberFmt + ANSI_NORMAL "/" ANSI_GREEN + numberFmt + ANSI_NORMAL "/" + numberFmt, + running / unit, done / unit, expected / unit); + else + s = fmt(ANSI_BLUE + numberFmt + ANSI_NORMAL "/" ANSI_GREEN + numberFmt + ANSI_NORMAL, + running / unit, done / unit); + else if (expected != done) + if (expected != 0) + s = fmt(ANSI_GREEN + numberFmt + ANSI_NORMAL "/" + numberFmt, + done / unit, expected / unit); + else + s = fmt(ANSI_GREEN + numberFmt + ANSI_NORMAL, done / unit); + else + s = fmt(done ? ANSI_GREEN + numberFmt + ANSI_NORMAL : numberFmt, done / unit); + s = fmt(itemFmt, s); + + if (failed) + s += fmt(" (" ANSI_RED "%d failed" ANSI_NORMAL ")", failed / unit); + } + + return s; + }; + + auto showActivity = [&](ActivityType type, const std::string & itemFmt, const std::string & numberFmt = "%d", double unit = 1) { + auto s = renderActivity(type, itemFmt, numberFmt, unit); + if (s.empty()) return; + if (!res.empty()) res += ", "; + res += s; + }; + + showActivity(actBuilds, "%s built"); + + auto s1 = renderActivity(actCopyPaths, "%s copied"); + auto s2 = renderActivity(actCopyPath, "%s MiB", "%.1f", MiB); + + if (!s1.empty() || !s2.empty()) { + if (!res.empty()) res += ", "; + if (s1.empty()) res += "0 copied"; else res += s1; + if (!s2.empty()) { res += " ("; res += s2; res += ')'; } + } + + showActivity(actFileTransfer, "%s MiB DL", "%.1f", MiB); + + { + auto s = renderActivity(actOptimiseStore, "%s paths optimised"); + if (s != "") { + s += fmt(", %.1f MiB / %d inodes freed", state.bytesLinked / MiB, state.filesLinked); + if (!res.empty()) res += ", "; + res += s; + } + } + + // FIXME: don't show "done" paths in green. + showActivity(actVerifyPaths, "%s paths verified"); + + if (state.corruptedPaths) { + if (!res.empty()) res += ", "; + res += fmt(ANSI_RED "%d corrupted" ANSI_NORMAL, state.corruptedPaths); + } + + if (state.untrustedPaths) { + if (!res.empty()) res += ", "; + res += fmt(ANSI_RED "%d untrusted" ANSI_NORMAL, state.untrustedPaths); + } + + return res; + } + + void writeToStdout(std::string_view s) override + { + auto state(state_.lock()); + if (state->active) { + std::cerr << "\r\e[K"; + Logger::writeToStdout(s); + draw(*state); + } else { + Logger::writeToStdout(s); + } + } +}; + +Logger *makeProgressBar(bool printBuildLogs) +{ + return new ProgressBar( + printBuildLogs, + isatty(STDERR_FILENO) && getEnv("TERM").value_or("dumb") != "dumb" + ); +} + +void startProgressBar(bool printBuildLogs) +{ + logger = makeProgressBar(printBuildLogs); +} + +void stopProgressBar() +{ + auto progressBar = dynamic_cast(logger); + if (progressBar) progressBar->stop(); + +} + +} diff --git a/src/libmain/progress-bar.hh b/src/libmain/progress-bar.hh new file mode 100644 index 000000000..60d0a2076 --- /dev/null +++ b/src/libmain/progress-bar.hh @@ -0,0 +1,13 @@ +#pragma once + +#include "logging.hh" + +namespace nix { + +Logger* makeProgressBar(bool printBuildLogs = false); + +void startProgressBar(bool printBuildLogs = false); + +void stopProgressBar(); + +} -- cgit v1.2.3 From 8bd892117a324c22803366b872bfe7ed783b34eb Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 11 Jun 2020 15:39:08 +0200 Subject: Style fixes --- src/libmain/loggers.cc | 8 ++++---- src/libmain/loggers.hh | 4 ++-- src/libmain/progress-bar.cc | 2 +- src/libmain/progress-bar.hh | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src/libmain') diff --git a/src/libmain/loggers.cc b/src/libmain/loggers.cc index 41f0b35fb..350f90266 100644 --- a/src/libmain/loggers.cc +++ b/src/libmain/loggers.cc @@ -5,7 +5,7 @@ namespace nix { LogFormat defaultLogFormat = LogFormat::raw; -LogFormat parseLogFormat(const string &logFormatStr) { +LogFormat parseLogFormat(const std::string & logFormatStr) { if (logFormatStr == "raw") return LogFormat::raw; else if (logFormatStr == "raw-with-logs") @@ -19,7 +19,7 @@ LogFormat parseLogFormat(const string &logFormatStr) { throw Error("option 'log-format' has an invalid value '%s'", logFormatStr); } -Logger *makeDefaultLogger() { +Logger * makeDefaultLogger() { switch (defaultLogFormat) { case LogFormat::raw: return makeSimpleLogger(false); @@ -34,11 +34,11 @@ Logger *makeDefaultLogger() { } } -void setLogFormat(const string &logFormatStr) { +void setLogFormat(const std::string & logFormatStr) { setLogFormat(parseLogFormat(logFormatStr)); } -void setLogFormat(const LogFormat &logFormat) { +void setLogFormat(const LogFormat & logFormat) { defaultLogFormat = logFormat; createDefaultLogger(); } diff --git a/src/libmain/loggers.hh b/src/libmain/loggers.hh index f9ba5ee5e..cada03110 100644 --- a/src/libmain/loggers.hh +++ b/src/libmain/loggers.hh @@ -12,8 +12,8 @@ enum class LogFormat { barWithLogs, }; -void setLogFormat(const string &logFormatStr); -void setLogFormat(const LogFormat &logFormat); +void setLogFormat(const std::string & logFormatStr); +void setLogFormat(const LogFormat & logFormat); void createDefaultLogger(); diff --git a/src/libmain/progress-bar.cc b/src/libmain/progress-bar.cc index c9ba89714..b287de8a3 100644 --- a/src/libmain/progress-bar.cc +++ b/src/libmain/progress-bar.cc @@ -461,7 +461,7 @@ public: } }; -Logger *makeProgressBar(bool printBuildLogs) +Logger * makeProgressBar(bool printBuildLogs) { return new ProgressBar( printBuildLogs, diff --git a/src/libmain/progress-bar.hh b/src/libmain/progress-bar.hh index 60d0a2076..7f0dafecf 100644 --- a/src/libmain/progress-bar.hh +++ b/src/libmain/progress-bar.hh @@ -4,7 +4,7 @@ namespace nix { -Logger* makeProgressBar(bool printBuildLogs = false); +Logger * makeProgressBar(bool printBuildLogs = false); void startProgressBar(bool printBuildLogs = false); -- cgit v1.2.3 From 95eb0640620f0704b4ded6ed6c7509024c00273e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 11 Jun 2020 15:39:30 +0200 Subject: Shut up warning --- src/libmain/loggers.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/libmain') diff --git a/src/libmain/loggers.cc b/src/libmain/loggers.cc index 350f90266..c44bb6408 100644 --- a/src/libmain/loggers.cc +++ b/src/libmain/loggers.cc @@ -31,6 +31,8 @@ Logger * makeDefaultLogger() { return makeProgressBar(); case LogFormat::barWithLogs: return makeProgressBar(true); + default: + abort(); } } -- cgit v1.2.3 From e14e62fddde3b00ee82a8da29725f571ad8ecee1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 15 Jun 2020 14:12:39 +0200 Subject: Remove trailing whitespace --- src/libmain/progress-bar.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libmain') diff --git a/src/libmain/progress-bar.cc b/src/libmain/progress-bar.cc index 20d9915a0..8d0421c2f 100644 --- a/src/libmain/progress-bar.cc +++ b/src/libmain/progress-bar.cc @@ -133,7 +133,7 @@ public: { auto state(state_.lock()); - std::stringstream oss; + std::stringstream oss; oss << ei; log(*state, ei.level, oss.str()); -- cgit v1.2.3 From 5ed5d7acbd00298df7b4e3a638d731143084da2e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 15 Jun 2020 16:03:29 +0200 Subject: Improve "waiting for locks" messages These are now shown in the progress bar. Closes #3577. --- src/libmain/progress-bar.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libmain') diff --git a/src/libmain/progress-bar.cc b/src/libmain/progress-bar.cc index 8d0421c2f..6dfc3a4d3 100644 --- a/src/libmain/progress-bar.cc +++ b/src/libmain/progress-bar.cc @@ -156,7 +156,7 @@ public: { auto state(state_.lock()); - if (lvl <= verbosity && !s.empty()) + if (lvl <= verbosity && !s.empty() && type != actBuildWaiting) log(*state, lvl, s + "..."); state->activities.emplace_back(ActInfo()); -- cgit v1.2.3 From 4e995bc8a66e5486be83c935bd939726ac39cab3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 15 Jun 2020 18:01:05 +0200 Subject: Always hide the progress bar on exit --- src/libmain/progress-bar.cc | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/libmain') diff --git a/src/libmain/progress-bar.cc b/src/libmain/progress-bar.cc index 6dfc3a4d3..95a9187de 100644 --- a/src/libmain/progress-bar.cc +++ b/src/libmain/progress-bar.cc @@ -111,10 +111,7 @@ public: auto state(state_.lock()); if (!state->active) return; state->active = false; - std::string status = getStatus(*state); writeToStderr("\r\e[K"); - if (status != "") - writeToStderr("[" + status + "]\n"); updateCV.notify_one(); quitCV.notify_one(); } -- cgit v1.2.3 From 24a3208247c248956c5815554804da1bf9763ece Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 15 Jun 2020 18:20:05 +0200 Subject: Include only the base name of the program in error messages --- src/libmain/shared.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libmain') diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index 0f2c189a6..dc6d5e413 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -290,7 +290,7 @@ int handleExceptions(const string & programName, std::function fun) { ReceiveInterrupts receiveInterrupts; // FIXME: need better place for this - ErrorInfo::programName = programName; + ErrorInfo::programName = baseNameOf(programName); string error = ANSI_RED "error:" ANSI_NORMAL " "; try { -- cgit v1.2.3