Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: NixOS/nix
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 2fb70786dc09^
Choose a base ref
...
head repository: NixOS/nix
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 53ec68a9b973
Choose a head ref
  • 3 commits
  • 3 files changed
  • 1 contributor

Commits on Nov 9, 2020

  1. Macro hygiene

    edolstra committed Nov 9, 2020
    Copy the full SHA
    2fb7078 View commit details
  2. Copy the full SHA
    d043f14 View commit details

Commits on Nov 10, 2020

  1. Interactive progress bar

    During a build you can hit 'L' to enable/disable printing of build
    logs, 'v' or '+' to increase verbosity, and '-' to decrease verbosity.
    edolstra committed Nov 10, 2020
    Copy the full SHA
    53ec68a View commit details
Showing with 128 additions and 25 deletions.
  1. +1 −1 src/build-remote/build-remote.cc
  2. +125 −22 src/libmain/progress-bar.cc
  3. +2 −2 src/libutil/logging.hh
2 changes: 1 addition & 1 deletion src/build-remote/build-remote.cc
Original file line number Diff line number Diff line change
@@ -201,7 +201,7 @@ static int main_build_remote(int argc, char * * argv)
% concatStringsSep<StringSet>(", ", m.mandatoryFeatures);
}

logErrorInfo(lvlInfo, {
logErrorInfo(canBuildLocally ? lvlChatty : lvlWarn, {
.name = "Remote build",
.description = "Failed to find a machine for remote build!",
.hint = hint
147 changes: 125 additions & 22 deletions src/libmain/progress-bar.cc
Original file line number Diff line number Diff line change
@@ -9,6 +9,9 @@
#include <thread>
#include <iostream>

#include <termios.h>
#include <poll.h>

namespace nix {

static std::string getS(const std::vector<Logger::Field> & fields, size_t n)
@@ -71,24 +74,31 @@ class ProgressBar : public Logger

bool active = true;
bool haveUpdate = true;

bool printBuildLogs;
};

bool isTTY;

Sync<State> state_;

std::thread updateThread;
std::thread inputThread;

std::condition_variable quitCV, updateCV;

bool printBuildLogs;
bool isTTY;
std::optional<struct termios> savedTermAttrs;

Pipe inputPipe;

public:

ProgressBar(bool printBuildLogs, bool isTTY)
: printBuildLogs(printBuildLogs)
, isTTY(isTTY)
: isTTY(isTTY)
, state_({ .active = isTTY, .printBuildLogs = printBuildLogs })
{
state_.lock()->active = isTTY;

updateThread = std::thread([&]() {
auto state(state_.lock());
while (state->active) {
@@ -97,27 +107,120 @@ class ProgressBar : public Logger
draw(*state);
state.wait_for(quitCV, std::chrono::milliseconds(50));
}

if (savedTermAttrs) {
tcsetattr(STDIN_FILENO, TCSANOW, &*savedTermAttrs);
savedTermAttrs.reset();
}
});

if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO)) {

struct termios term;
if (tcgetattr(STDIN_FILENO, &term))
throw SysError("getting terminal attributes");

savedTermAttrs = term;

cfmakeraw(&term);

if (tcsetattr(STDIN_FILENO, TCSANOW, &term))
throw SysError("putting terminal into raw mode");

inputPipe.create();

inputThread = std::thread([this]() {
// FIXME: exceptions

struct pollfd fds[2];
fds[0] = { .fd = STDIN_FILENO, .events = POLLIN, .revents = 0 };
fds[1] = { .fd = inputPipe.readSide.get(), .events = POLLIN, .revents = 0 };

while (true) {
if (poll(fds, 2, -1) != 1) {
if (errno == EINTR) continue;
assert(false);
}

if (fds[1].revents & POLLIN) break;

assert(fds[0].revents & POLLIN);

char c;
auto n = read(STDIN_FILENO, &c, 1);
if (n == 0) break;
if (n == -1) {
if (errno == EINTR) continue;
break;
}
c = std::tolower(c);

if (c == 3 || c == 'q') {
triggerInterrupt();
break;
}
if (c == 'l') {
auto state(state_.lock());
log(*state, lvlError,
state->printBuildLogs
? ANSI_BOLD "Disabling build logs."
: ANSI_BOLD "Enabling build logs.");
state->printBuildLogs = !state->printBuildLogs;
draw(*state);
}
if (c == '+' || c == '=' || c == 'v') {
auto state(state_.lock());
verbosity = (Verbosity) (verbosity + 1);;
log(*state, lvlError, ANSI_BOLD "Increasing verbosity...");
}
if (c == '-') {
auto state(state_.lock());
verbosity = verbosity > lvlError ? (Verbosity) (verbosity - 1) : lvlError;
log(*state, lvlError, ANSI_BOLD "Decreasing verbosity...");
}
if (c == 'h' || c == '?') {
printError(
ANSI_BOLD "The following keys are available:\n"
" 'v' to increase verbosity.\n"
" '-' to decrease verbosity.\n"
" 'l' to show build log output.\n"
" 'q' to quit." ANSI_NORMAL);
}
}
});

log(lvlError, "Type 'h' for help.");
}
}

~ProgressBar()
{
stop();
updateThread.join();
}

void stop() override
{
auto state(state_.lock());
if (!state->active) return;
state->active = false;
writeToStderr("\r\e[K");
updateCV.notify_one();
quitCV.notify_one();
if (inputThread.joinable()) {
assert(inputPipe.writeSide);
writeFull(inputPipe.writeSide.get(), "x", false);
inputThread.join();
}

{
auto state(state_.lock());
if (!state->active) return;
state->active = false;
writeToStderr("\r\e[K");
updateCV.notify_one();
quitCV.notify_one();
}

updateThread.join();
}

bool isVerbose() override {
return printBuildLogs;
bool isVerbose() override
{
return state_.lock()->printBuildLogs;
}

void log(Verbosity lvl, const FormatOrString & fs) override
@@ -139,7 +242,7 @@ class ProgressBar : public Logger
void log(State & state, Verbosity lvl, const std::string & s)
{
if (state.active) {
writeToStderr("\r\e[K" + filterANSIEscapes(s, !isTTY) + ANSI_NORMAL "\n");
writeToStderr("\r\e[K" + replaceStrings(filterANSIEscapes(s, !isTTY) + ANSI_NORMAL "\n", "\n", "\r\n"));
draw(state);
} else {
auto s2 = s + ANSI_NORMAL "\n";
@@ -261,19 +364,19 @@ class ProgressBar : public Logger
auto i = state->its.find(act);
assert(i != state->its.end());
ActInfo info = *i->second;
if (printBuildLogs) {
if (state->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);
}
state->activities.erase(i->second);
info.lastLine = lastLine;
state->activities.emplace_back(info);
i->second = std::prev(state->activities.end());
if (state->printBuildLogs)
update(*state);
}
}

@@ -352,7 +455,7 @@ class ProgressBar : public Logger
line += i->phase;
line += ")";
}
if (!i->lastLine.empty()) {
if (!state.printBuildLogs && !i->lastLine.empty()) {
if (!i->s.empty()) line += ": ";
line += i->lastLine;
}
4 changes: 2 additions & 2 deletions src/libutil/logging.hh
Original file line number Diff line number Diff line change
@@ -175,8 +175,8 @@ extern Verbosity verbosity; /* suppress msgs > this */
lightweight status messages. */
#define logErrorInfo(level, errorInfo...) \
do { \
if (level <= nix::verbosity) { \
logger->logEI(level, errorInfo); \
if ((level) <= nix::verbosity) { \
logger->logEI((level), errorInfo); \
} \
} while (0)