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:
parent
304715d5f3
commit
82bbb3a66e
1 changed files with 117 additions and 37 deletions
|
|
@ -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);
|
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
|
class ProgressBar : public Logger
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
@ -61,6 +71,37 @@ private:
|
||||||
uint64_t failed = 0;
|
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;
|
typedef unsigned int LineId;
|
||||||
|
|
||||||
struct State
|
struct State
|
||||||
|
|
@ -83,6 +124,8 @@ private:
|
||||||
|
|
||||||
/* How many lines need to be erased when redrawing. */
|
/* How many lines need to be erased when redrawing. */
|
||||||
size_t prevStatusLines = 0;
|
size_t prevStatusLines = 0;
|
||||||
|
|
||||||
|
bool helpShown = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool isTTY;
|
bool isTTY;
|
||||||
|
|
@ -100,6 +143,15 @@ private:
|
||||||
|
|
||||||
public:
|
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)
|
ProgressBar(bool printBuildLogs, bool isTTY)
|
||||||
: isTTY(isTTY)
|
: isTTY(isTTY)
|
||||||
, state_({ .active = isTTY, .printBuildLogs = printBuildLogs })
|
, state_({ .active = isTTY, .printBuildLogs = printBuildLogs })
|
||||||
|
|
@ -124,8 +176,6 @@ public:
|
||||||
|
|
||||||
if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO)) {
|
if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO)) {
|
||||||
|
|
||||||
static auto helpLine = ANSI_BOLD "Type 'h' for help.";
|
|
||||||
|
|
||||||
struct termios term;
|
struct termios term;
|
||||||
if (tcgetattr(STDIN_FILENO, &term))
|
if (tcgetattr(STDIN_FILENO, &term))
|
||||||
throw SysError("getting terminal attributes");
|
throw SysError("getting terminal attributes");
|
||||||
|
|
@ -190,23 +240,25 @@ public:
|
||||||
}
|
}
|
||||||
if (c == 'h' || c == '?') {
|
if (c == 'h' || c == '?') {
|
||||||
auto state(state_.lock());
|
auto state(state_.lock());
|
||||||
if (state->statusLines.count(1)) {
|
if (state->helpShown) {
|
||||||
for (LineId i = 0; i <= 4; i++)
|
state->helpShown = false;
|
||||||
state->statusLines.erase(i);
|
resetHelp(*state);
|
||||||
state->statusLines.insert_or_assign(0, helpLine);
|
|
||||||
} else {
|
} else {
|
||||||
state->statusLines.insert_or_assign(0, ANSI_BOLD "The following keys are available:");
|
state->helpShown = true;
|
||||||
state->statusLines.insert_or_assign(1, ANSI_BOLD " 'v' to increase verbosity.");
|
state->statusLines.insert_or_assign(0, "");
|
||||||
state->statusLines.insert_or_assign(2, ANSI_BOLD " '-' to decrease verbosity.");
|
state->statusLines.insert_or_assign(1, ANSI_BOLD "The following keys are available:");
|
||||||
state->statusLines.insert_or_assign(3, ANSI_BOLD " 'l' to show build log output.");
|
state->statusLines.insert_or_assign(2, ANSI_BOLD " 'v' to increase verbosity.");
|
||||||
state->statusLines.insert_or_assign(4, ANSI_BOLD " 'q' to quit.");
|
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);
|
draw(*state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
state_.lock()->statusLines.insert_or_assign(0, helpLine);
|
resetHelp(*state_.lock());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -480,6 +532,43 @@ public:
|
||||||
state.statusLines.erase(100);
|
state.statusLines.erase(100);
|
||||||
else
|
else
|
||||||
state.statusLines.insert_or_assign(100, line);
|
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 = {})
|
void draw(State & state, std::optional<std::string_view> msg = {})
|
||||||
|
|
@ -495,7 +584,7 @@ public:
|
||||||
for (size_t i = 1; i < state.prevStatusLines; ++i)
|
for (size_t i = 1; i < state.prevStatusLines; ++i)
|
||||||
s += "\r\e[K\e[A";
|
s += "\r\e[K\e[A";
|
||||||
|
|
||||||
s += "\r";
|
s += "\r\e[K";
|
||||||
|
|
||||||
if (msg) {
|
if (msg) {
|
||||||
s += replaceStrings(*msg, "\n", "\r\n");
|
s += replaceStrings(*msg, "\n", "\r\n");
|
||||||
|
|
@ -514,44 +603,33 @@ public:
|
||||||
|
|
||||||
std::string getStatus(State & state)
|
std::string getStatus(State & state)
|
||||||
{
|
{
|
||||||
auto MiB = 1024.0 * 1024.0;
|
|
||||||
|
|
||||||
std::string res;
|
std::string res;
|
||||||
|
|
||||||
auto renderActivity = [&](ActivityType type, const std::string & itemFmt, const std::string & numberFmt = "%d", double unit = 1) {
|
auto renderActivity = [&](ActivityType type, const std::string & itemFmt, const std::string & numberFmt = "%d", double unit = 1) {
|
||||||
auto & act = state.activitiesByType[type];
|
auto stats = getActivityStats(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;
|
std::string s;
|
||||||
|
|
||||||
if (running || done || expected || failed) {
|
if (stats.running || stats.done || stats.expected || stats.failed) {
|
||||||
if (running)
|
if (stats.running)
|
||||||
if (expected != 0)
|
if (stats.expected != 0)
|
||||||
s = fmt(ANSI_BLUE + numberFmt + ANSI_NORMAL "/" ANSI_GREEN + numberFmt + ANSI_NORMAL "/" + numberFmt,
|
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
|
else
|
||||||
s = fmt(ANSI_BLUE + numberFmt + ANSI_NORMAL "/" ANSI_GREEN + numberFmt + ANSI_NORMAL,
|
s = fmt(ANSI_BLUE + numberFmt + ANSI_NORMAL "/" ANSI_GREEN + numberFmt + ANSI_NORMAL,
|
||||||
running / unit, done / unit);
|
stats.running / unit, stats.done / unit);
|
||||||
else if (expected != done)
|
else if (stats.expected != stats.done)
|
||||||
if (expected != 0)
|
if (stats.expected != 0)
|
||||||
s = fmt(ANSI_GREEN + numberFmt + ANSI_NORMAL "/" + numberFmt,
|
s = fmt(ANSI_GREEN + numberFmt + ANSI_NORMAL "/" + numberFmt,
|
||||||
done / unit, expected / unit);
|
stats.done / unit, stats.expected / unit);
|
||||||
else
|
else
|
||||||
s = fmt(ANSI_GREEN + numberFmt + ANSI_NORMAL, done / unit);
|
s = fmt(ANSI_GREEN + numberFmt + ANSI_NORMAL, stats.done / unit);
|
||||||
else
|
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);
|
s = fmt(itemFmt, s);
|
||||||
|
|
||||||
if (failed)
|
if (stats.failed)
|
||||||
s += fmt(" (" ANSI_RED "%d failed" ANSI_NORMAL ")", failed / unit);
|
s += fmt(" (" ANSI_RED "%d failed" ANSI_NORMAL ")", stats.failed / unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
|
|
@ -564,6 +642,7 @@ public:
|
||||||
res += s;
|
res += s;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if 0
|
||||||
showActivity(actBuilds, "%s built");
|
showActivity(actBuilds, "%s built");
|
||||||
|
|
||||||
auto s1 = renderActivity(actCopyPaths, "%s copied");
|
auto s1 = renderActivity(actCopyPaths, "%s copied");
|
||||||
|
|
@ -576,6 +655,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
showActivity(actFileTransfer, "%s MiB DL", "%.1f", MiB);
|
showActivity(actFileTransfer, "%s MiB DL", "%.1f", MiB);
|
||||||
|
#endif
|
||||||
|
|
||||||
{
|
{
|
||||||
auto s = renderActivity(actOptimiseStore, "%s paths optimised");
|
auto s = renderActivity(actOptimiseStore, "%s paths optimised");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue