diff --git a/src/libmain/loggers.cc b/src/libmain/loggers.cc index 80080d616..6e8f78d6f 100644 --- a/src/libmain/loggers.cc +++ b/src/libmain/loggers.cc @@ -17,6 +17,12 @@ LogFormat parseLogFormat(const std::string & logFormatStr) { return LogFormat::bar; else if (logFormatStr == "bar-with-logs") return LogFormat::barWithLogs; +#if !defined(WIN32) || !defined(_WIN32) || !defined(__WIN32__) || !defined(__NT__) + else if (logFormatStr == "multiline") + return LogFormat::multiline; + else if (logFormatStr == "multiline-with-logs") + return LogFormat::multilineWithLogs; +#endif throw Error("option 'log-format' has an invalid value '%s'", logFormatStr); } @@ -35,6 +41,19 @@ Logger * makeDefaultLogger() { logger->setPrintBuildLogs(true); return logger; } +#if !defined(WIN32) || !defined(_WIN32) || !defined(__WIN32__) || !defined(__NT__) + 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; + } +#endif default: abort(); } diff --git a/src/libmain/loggers.hh b/src/libmain/loggers.hh index e5721420c..50f3778c2 100644 --- a/src/libmain/loggers.hh +++ b/src/libmain/loggers.hh @@ -11,6 +11,10 @@ enum class LogFormat { internalJSON, bar, barWithLogs, +#if !defined(WIN32) || !defined(_WIN32) || !defined(__WIN32__) || !defined(__NT__) + multiline, + multilineWithLogs, +#endif }; void setLogFormat(const std::string & logFormatStr); diff --git a/src/libmain/progress-bar.cc b/src/libmain/progress-bar.cc index d83b09cd4..5b9381bb9 100644 --- a/src/libmain/progress-bar.cc +++ b/src/libmain/progress-bar.cc @@ -73,6 +73,8 @@ private: std::map activitiesByType; + int lastLines = 0; + uint64_t filesLinked = 0, bytesLinked = 0; uint64_t corruptedPaths = 0, untrustedPaths = 0; @@ -89,6 +91,7 @@ private: std::condition_variable quitCV, updateCV; bool printBuildLogs = false; + bool printMultiline = false; bool isTTY; public: @@ -103,7 +106,7 @@ public: 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)); } }); @@ -165,8 +168,7 @@ public: void 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); @@ -354,60 +356,99 @@ public: updateCV.notify_one(); } - std::chrono::milliseconds draw(State & state) + std::chrono::milliseconds draw(State & state, const std::optional & 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::max(); + } + if (printMultiline && (state.lastLines >= 1)) { + 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 moreBuilds = 0; auto now = std::chrono::steady_clock::now(); 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(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; + } + + /* 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( + delay - (now - i->startTime) + ) + ); + } + + if (printMultiline) { + line = i->s; + } else { + line += " "; + line += i->s; } - ++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 += ": "; + if (!i->s.empty()) { + line += ": "; + } line += i->lastLine; } + if (printMultiline) { + if (state.lastLines < (height - 1)) { + writeToStderr(filterANSIEscapes(line, false, width) + "\n"); + state.lastLines++; + } else { + moreBuilds++; + } + } } } - auto width = getWindowSize().second; - if (width <= 0) width = std::numeric_limits::max(); + if (printMultiline && moreBuilds) { + writeToStderr(fmt("And %d more...", moreBuilds)); + } - writeToStderr("\r" + filterANSIEscapes(line, false, width) + ANSI_NORMAL + "\e[K"); + if (!printMultiline) { + writeToStderr("\r" + filterANSIEscapes(line, false, width) + ANSI_NORMAL + "\e[K"); + } return nextWakeup; } @@ -506,9 +547,8 @@ public: { auto state(state_.lock()); if (state->active) { - std::cerr << "\r\e[K"; Logger::writeToStdout(s); - draw(*state); + draw(*state, {}); } else { Logger::writeToStdout(s); } @@ -521,7 +561,7 @@ public: 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]; } @@ -529,6 +569,11 @@ public: { this->printBuildLogs = printBuildLogs; } + + void setPrintMultiline(bool printMultiline) override + { + this->printMultiline = printMultiline; + } }; 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) + { } }; /**