aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFinn Behrens <me@kloenk.dev>2024-06-01 16:06:26 +0200
committerFinn Behrens <me@kloenk.dev>2024-06-22 14:20:27 +0200
commitda4e46dd1fc04067b5ba4bc16dd68134fa7efad2 (patch)
treede928e67208328cac91b1afb8ce368918f0b8266 /src
parent21865ccce0a5bf6edcb98a09b57b3f076ee9ba9f (diff)
libmain: add progress bar with multiple status lines
Add the log-formats `multiline` and `multiline-with-logs` which offer multiple current active building status lines. Change-Id: Idd8afe62f8591b5d8b70e258c5cefa09be4cab03
Diffstat (limited to 'src')
-rw-r--r--src/libmain/common-args.cc2
-rw-r--r--src/libmain/loggers.cc15
-rw-r--r--src/libmain/loggers.hh2
-rw-r--r--src/libmain/progress-bar.cc112
-rw-r--r--src/libmain/progress-bar.hh9
-rw-r--r--src/libutil/logging.hh3
6 files changed, 104 insertions, 39 deletions
diff --git a/src/libmain/common-args.cc b/src/libmain/common-args.cc
index 8fcb10325..0b756301c 100644
--- a/src/libmain/common-args.cc
+++ b/src/libmain/common-args.cc
@@ -58,7 +58,7 @@ MixCommonArgs::MixCommonArgs(const std::string & programName)
addFlag({
.longName = "log-format",
- .description = "Set the format of log output; one of `raw`, `internal-json`, `bar` or `bar-with-logs`.",
+ .description = "Set the format of log output; one of `raw`, `internal-json`, `bar`, `bar-with-logs`, `multiline` or `multiline-with-logs`.",
.category = loggingCategory,
.labels = {"format"},
.handler = {[](std::string format) { setLogFormat(format); }},
diff --git a/src/libmain/loggers.cc b/src/libmain/loggers.cc
index 80080d616..8c3c4e355 100644
--- a/src/libmain/loggers.cc
+++ b/src/libmain/loggers.cc
@@ -17,6 +17,10 @@ LogFormat parseLogFormat(const std::string & logFormatStr) {
return LogFormat::bar;
else if (logFormatStr == "bar-with-logs")
return LogFormat::barWithLogs;
+ else if (logFormatStr == "multiline")
+ return LogFormat::multiline;
+ else if (logFormatStr == "multiline-with-logs")
+ return LogFormat::multilineWithLogs;
throw Error("option 'log-format' has an invalid value '%s'", logFormatStr);
}
@@ -35,6 +39,17 @@ Logger * makeDefaultLogger() {
logger->setPrintBuildLogs(true);
return logger;
}
+ case LogFormat::multiline: {
+ auto logger = makeProgressBar();
+ logger->setPrintMultiline(true);
+ return logger;
+ }
+ case LogFormat::multilineWithLogs: {
+ auto logger = makeProgressBar();
+ logger->setPrintMultiline(true);
+ logger->setPrintBuildLogs(true);
+ return logger;
+ }
default:
abort();
}
diff --git a/src/libmain/loggers.hh b/src/libmain/loggers.hh
index e5721420c..6064b6160 100644
--- a/src/libmain/loggers.hh
+++ b/src/libmain/loggers.hh
@@ -11,6 +11,8 @@ enum class LogFormat {
internalJSON,
bar,
barWithLogs,
+ multiline,
+ multilineWithLogs,
};
void setLogFormat(const std::string & logFormatStr);
diff --git a/src/libmain/progress-bar.cc b/src/libmain/progress-bar.cc
index b3466a860..b3b46fc21 100644
--- a/src/libmain/progress-bar.cc
+++ b/src/libmain/progress-bar.cc
@@ -95,8 +95,7 @@ void ProgressBar::logEI(const ErrorInfo & ei)
void ProgressBar::log(State & state, Verbosity lvl, std::string_view s)
{
if (state.active) {
- writeToStderr("\r\e[K" + filterANSIEscapes(s, !isTTY) + ANSI_NORMAL "\n");
- draw(state);
+ draw(state, s);
} else {
auto s2 = s + ANSI_NORMAL "\n";
if (!isTTY) s2 = filterANSIEscapes(s2, true);
@@ -234,10 +233,14 @@ void ProgressBar::result(ActivityId act, ResultType type, const std::vector<Fiel
}
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());
+ if (!printMultiline) {
+ state->activities.erase(i->second);
+ info.lastLine = lastLine;
+ state->activities.emplace_back(info);
+ i->second = std::prev(state->activities.end());
+ } else {
+ i->second->lastLine = lastLine;
+ }
update(*state);
}
}
@@ -290,60 +293,93 @@ void ProgressBar::update(State & state)
updateCV.notify_one();
}
-std::chrono::milliseconds ProgressBar::draw(State & state)
+std::chrono::milliseconds ProgressBar::draw(State & state, const std::optional<std::string_view> & s)
{
auto nextWakeup = A_LONG_TIME;
state.haveUpdate = false;
if (state.paused || !state.active) return nextWakeup;
- std::string line;
+ auto windowSize = getWindowSize();
+ auto width = windowSize.second;
+ if (width <= 0) {
+ width = std::numeric_limits<decltype(width)>::max();
+ }
+
+ if (printMultiline && (state.lastLines >= 1)) {
+ // FIXME: make sure this works on windows
+ writeToStderr(fmt("\e[G\e[%dF\e[J", state.lastLines));
+ }
+ state.lastLines = 0;
+
+ if (s != std::nullopt)
+ writeToStderr("\r\e[K" + filterANSIEscapes(s.value(), !isTTY) + ANSI_NORMAL "\n");
+
+ std::string line;
std::string status = getStatus(state);
if (!status.empty()) {
line += '[';
line += status;
line += "]";
}
+ if (printMultiline && !line.empty()) {
+ writeToStderr(filterANSIEscapes(line, false, width) + "\n");
+ state.lastLines++;
+ }
+ auto height = windowSize.first > 0 ? windowSize.first : 25;
+ auto moreActivities = 0;
auto now = std::chrono::steady_clock::now();
+ std::string activity_line;
if (!state.activities.empty()) {
- if (!status.empty()) line += " ";
- auto i = state.activities.rbegin();
-
- while (i != state.activities.rend()) {
- if (i->visible && (!i->s.empty() || !i->lastLine.empty())) {
- /* Don't show activities until some time has
- passed, to avoid displaying very short
- activities. */
- auto delay = std::chrono::milliseconds(10);
- if (i->startTime + delay < now)
- break;
- else
- nextWakeup = std::min(nextWakeup, std::chrono::duration_cast<std::chrono::milliseconds>(delay - (now - i->startTime)));
+ for (auto i = state.activities.begin(); i != state.activities.end(); ++i) {
+ if (!(i->visible && (!i->s.empty() || !i->lastLine.empty()))) {
+ continue;
}
- ++i;
- }
+ /* Don't show activities until some time has
+ passed, to avoid displaying very short
+ activities. */
+ auto delay = std::chrono::milliseconds(10);
+ if (i->startTime + delay >= now) {
+ nextWakeup = std::min(
+ nextWakeup,
+ std::chrono::duration_cast<std::chrono::milliseconds>(
+ delay - (now - i->startTime)
+ )
+ );
+ }
+
+ activity_line = i->s;
- if (i != state.activities.rend()) {
- line += i->s;
if (!i->phase.empty()) {
- line += " (";
- line += i->phase;
- line += ")";
+ activity_line += " (";
+ activity_line += i->phase;
+ activity_line += ")";
}
if (!i->lastLine.empty()) {
- if (!i->s.empty()) line += ": ";
- line += i->lastLine;
+ if (!i->s.empty())
+ activity_line += ": ";
+ activity_line += i->lastLine;
+ }
+
+ if (printMultiline) {
+ if (state.lastLines < (height -1)) {
+ writeToStderr(filterANSIEscapes(activity_line, false, width) + "\n");
+ state.lastLines++;
+ } else moreActivities++;
}
}
}
- auto width = getWindowSize().second;
- if (width <= 0) width = std::numeric_limits<decltype(width)>::max();
+ if (printMultiline && moreActivities)
+ writeToStderr(fmt("And %d more...", moreActivities));
- writeToStderr("\r" + filterANSIEscapes(line, false, width) + ANSI_NORMAL + "\e[K");
+ if (!printMultiline) {
+ line += " " + activity_line;
+ writeToStderr("\r" + filterANSIEscapes(line, false, width) + ANSI_NORMAL + "\e[K");
+ }
return nextWakeup;
}
@@ -442,9 +478,8 @@ void ProgressBar::writeToStdout(std::string_view s)
{
auto state(state_.lock());
if (state->active) {
- std::cerr << "\r\e[K";
Logger::writeToStdout(s);
- draw(*state);
+ draw(*state, {});
} else {
Logger::writeToStdout(s);
}
@@ -457,7 +492,7 @@ std::optional<char> ProgressBar::ask(std::string_view msg)
std::cerr << fmt("\r\e[K%s ", msg);
auto s = trim(readLine(STDIN_FILENO));
if (s.size() != 1) return {};
- draw(*state);
+ draw(*state, {});
return s[0];
}
@@ -466,6 +501,11 @@ void ProgressBar::setPrintBuildLogs(bool printBuildLogs)
this->printBuildLogs = printBuildLogs;
}
+void ProgressBar::setPrintMultiline(bool printMultiline)
+{
+ this->printMultiline = printMultiline;
+}
+
Logger * makeProgressBar()
{
return new ProgressBar(shouldANSI());
diff --git a/src/libmain/progress-bar.hh b/src/libmain/progress-bar.hh
index 76e2ed4ff..176e941e8 100644
--- a/src/libmain/progress-bar.hh
+++ b/src/libmain/progress-bar.hh
@@ -47,6 +47,8 @@ struct ProgressBar : public Logger
std::map<ActivityType, ActivitiesByType> activitiesByType;
+ int lastLines = 0;
+
uint64_t filesLinked = 0, bytesLinked = 0;
uint64_t corruptedPaths = 0, untrustedPaths = 0;
@@ -63,6 +65,7 @@ struct ProgressBar : public Logger
std::condition_variable quitCV, updateCV;
bool printBuildLogs = false;
+ bool printMultiline = false;
bool isTTY;
ProgressBar(bool isTTY)
@@ -75,7 +78,7 @@ struct ProgressBar : public Logger
while (state->active) {
if (!state->haveUpdate)
state.wait_for(updateCV, nextWakeup);
- nextWakeup = draw(*state);
+ nextWakeup = draw(*state, {});
state.wait_for(quitCV, std::chrono::milliseconds(50));
}
});
@@ -114,7 +117,7 @@ struct ProgressBar : public Logger
void update(State & state);
- std::chrono::milliseconds draw(State & state);
+ std::chrono::milliseconds draw(State & state, const std::optional<std::string_view> & s);
std::string getStatus(State & state);
@@ -123,6 +126,8 @@ struct ProgressBar : public Logger
std::optional<char> ask(std::string_view msg) override;
void setPrintBuildLogs(bool printBuildLogs) override;
+
+ void setPrintMultiline(bool printMultiline) override;
};
Logger * makeProgressBar();
diff --git a/src/libutil/logging.hh b/src/libutil/logging.hh
index 64be8bc62..115b979f8 100644
--- a/src/libutil/logging.hh
+++ b/src/libutil/logging.hh
@@ -114,6 +114,9 @@ public:
virtual void setPrintBuildLogs(bool printBuildLogs)
{ }
+
+ virtual void setPrintMultiline(bool printMultiline)
+ { }
};
/**