1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-21 09:49:36 +01:00

Add separate progress bars for substituting and building

This commit is contained in:
Eelco Dolstra 2020-11-10 22:13:35 +01:00
parent 304715d5f3
commit 82bbb3a66e

View file

@ -35,6 +35,16 @@ static std::string_view storePathToName(std::string_view path)
return i == std::string::npos ? base.substr(0, 0) : base.substr(i + 1);
}
std::string repeat(std::string_view s, size_t n)
{
std::string res;
for (size_t i = 0; i < n; ++i)
res += s;
return res;
}
auto MiB = 1024.0 * 1024.0;
class ProgressBar : public Logger
{
private:
@ -61,6 +71,37 @@ private:
uint64_t failed = 0;
};
struct ActivityStats
{
uint64_t done = 0;
uint64_t expected = 0;
uint64_t running = 0;
uint64_t failed = 0;
uint64_t left = 0;
};
ActivityStats getActivityStats(ActivitiesByType & act)
{
ActivityStats stats {
.done = act.done,
.expected = act.done,
.running = 0,
.failed = act.failed
};
for (auto & j : act.its) {
stats.done += j.second->done;
stats.expected += j.second->expected;
stats.running += j.second->running;
stats.failed += j.second->failed;
stats.left += j.second->expected - j.second->done;
}
stats.expected = std::max(stats.expected, act.expected);
return stats;
}
typedef unsigned int LineId;
struct State
@ -83,6 +124,8 @@ private:
/* How many lines need to be erased when redrawing. */
size_t prevStatusLines = 0;
bool helpShown = false;
};
bool isTTY;
@ -100,6 +143,15 @@ private:
public:
void resetHelp(State & state)
{
for (LineId i = 0; i <= 6; i++)
state.statusLines.erase(i);
state.statusLines.insert_or_assign(0, "");
state.statusLines.insert_or_assign(1, ANSI_BOLD "Type 'h' for help.");
state.statusLines.insert_or_assign(2, "");
}
ProgressBar(bool printBuildLogs, bool isTTY)
: isTTY(isTTY)
, state_({ .active = isTTY, .printBuildLogs = printBuildLogs })
@ -124,8 +176,6 @@ public:
if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO)) {
static auto helpLine = ANSI_BOLD "Type 'h' for help.";
struct termios term;
if (tcgetattr(STDIN_FILENO, &term))
throw SysError("getting terminal attributes");
@ -190,23 +240,25 @@ public:
}
if (c == 'h' || c == '?') {
auto state(state_.lock());
if (state->statusLines.count(1)) {
for (LineId i = 0; i <= 4; i++)
state->statusLines.erase(i);
state->statusLines.insert_or_assign(0, helpLine);
if (state->helpShown) {
state->helpShown = false;
resetHelp(*state);
} else {
state->statusLines.insert_or_assign(0, ANSI_BOLD "The following keys are available:");
state->statusLines.insert_or_assign(1, ANSI_BOLD " 'v' to increase verbosity.");
state->statusLines.insert_or_assign(2, ANSI_BOLD " '-' to decrease verbosity.");
state->statusLines.insert_or_assign(3, ANSI_BOLD " 'l' to show build log output.");
state->statusLines.insert_or_assign(4, ANSI_BOLD " 'q' to quit.");
state->helpShown = true;
state->statusLines.insert_or_assign(0, "");
state->statusLines.insert_or_assign(1, ANSI_BOLD "The following keys are available:");
state->statusLines.insert_or_assign(2, ANSI_BOLD " 'v' to increase verbosity.");
state->statusLines.insert_or_assign(3, ANSI_BOLD " '-' to decrease verbosity.");
state->statusLines.insert_or_assign(4, ANSI_BOLD " 'l' to show build log output.");
state->statusLines.insert_or_assign(5, ANSI_BOLD " 'q' to quit.");
state->statusLines.insert_or_assign(6, "");
}
draw(*state);
}
}
});
state_.lock()->statusLines.insert_or_assign(0, helpLine);
resetHelp(*state_.lock());
}
}
@ -480,6 +532,43 @@ public:
state.statusLines.erase(100);
else
state.statusLines.insert_or_assign(100, line);
auto renderBar = [](uint64_t done, uint64_t running, uint64_t expected)
{
expected = std::max(expected, (uint64_t) 1);
auto pct1 = std::min((double) done / expected, 1.0);
auto pct2 = std::min((double) (done + running) / expected, 1.0);
auto barLength = 60;
size_t chars1 = barLength * pct1;
size_t chars2 = barLength * pct2;
return
ANSI_GREEN + repeat("", chars1) +
ANSI_YELLOW + repeat("", chars2 - chars1) +
ANSI_NORMAL + repeat("", barLength - chars2);
};
auto copyPath = getActivityStats(state.activitiesByType[actCopyPath]);
auto copyPaths = getActivityStats(state.activitiesByType[actCopyPaths]);
if (copyPath.done || copyPath.expected) {
state.statusLines.insert_or_assign(50,
fmt(ANSI_BOLD "" ANSI_NORMAL " %s " ANSI_BOLD "Fetched" ANSI_NORMAL " %d / %d paths, %.1f / %.1f MiB %d",
renderBar(copyPath.done, copyPath.left, copyPath.expected),
copyPaths.done, copyPaths.expected,
copyPath.done / MiB, copyPath.expected / MiB, copyPath.left));
state.statusLines.insert_or_assign(51, "");
}
auto builds = getActivityStats(state.activitiesByType[actBuilds]);
if (builds.done || builds.expected) {
state.statusLines.insert_or_assign(50,
fmt(ANSI_BOLD "" ANSI_NORMAL " %s " ANSI_BOLD "Built" ANSI_NORMAL " %d / %d derivations",
renderBar(builds.done, builds.running, builds.expected),
builds.done, builds.expected));
state.statusLines.insert_or_assign(51, "");
}
}
void draw(State & state, std::optional<std::string_view> msg = {})
@ -495,7 +584,7 @@ public:
for (size_t i = 1; i < state.prevStatusLines; ++i)
s += "\r\e[K\e[A";
s += "\r";
s += "\r\e[K";
if (msg) {
s += replaceStrings(*msg, "\n", "\r\n");
@ -514,44 +603,33 @@ public:
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);
auto stats = getActivityStats(state.activitiesByType[type]);
std::string s;
if (running || done || expected || failed) {
if (running)
if (expected != 0)
if (stats.running || stats.done || stats.expected || stats.failed) {
if (stats.running)
if (stats.expected != 0)
s = fmt(ANSI_BLUE + numberFmt + ANSI_NORMAL "/" ANSI_GREEN + numberFmt + ANSI_NORMAL "/" + numberFmt,
running / unit, done / unit, expected / unit);
stats.running / unit, stats.done / unit, stats.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)
stats.running / unit, stats.done / unit);
else if (stats.expected != stats.done)
if (stats.expected != 0)
s = fmt(ANSI_GREEN + numberFmt + ANSI_NORMAL "/" + numberFmt,
done / unit, expected / unit);
stats.done / unit, stats.expected / unit);
else
s = fmt(ANSI_GREEN + numberFmt + ANSI_NORMAL, done / unit);
s = fmt(ANSI_GREEN + numberFmt + ANSI_NORMAL, stats.done / unit);
else
s = fmt(done ? ANSI_GREEN + numberFmt + ANSI_NORMAL : numberFmt, done / unit);
s = fmt(stats.done ? ANSI_GREEN + numberFmt + ANSI_NORMAL : numberFmt, stats.done / unit);
s = fmt(itemFmt, s);
if (failed)
s += fmt(" (" ANSI_RED "%d failed" ANSI_NORMAL ")", failed / unit);
if (stats.failed)
s += fmt(" (" ANSI_RED "%d failed" ANSI_NORMAL ")", stats.failed / unit);
}
return s;
@ -564,6 +642,7 @@ public:
res += s;
};
#if 0
showActivity(actBuilds, "%s built");
auto s1 = renderActivity(actCopyPaths, "%s copied");
@ -576,6 +655,7 @@ public:
}
showActivity(actFileTransfer, "%s MiB DL", "%.1f", MiB);
#endif
{
auto s = renderActivity(actOptimiseStore, "%s paths optimised");