Skip to content

Commit

Permalink
Run builds in a pseudo-terminal
Browse files Browse the repository at this point in the history
This allows many programs (e.g. gcc, clang, cmake) to print colorized
log output (assuming $TERM is set to a value like "xterm").

There are other ways to get colors, in particular setting
CLICOLOR_FORCE, but they're less widely supported and can break
programs that parse tool output.
  • Loading branch information
edolstra committed Jun 16, 2019
1 parent b693029 commit e84c265
Showing 1 changed file with 46 additions and 8 deletions.
54 changes: 46 additions & 8 deletions src/libstore/build.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include <unistd.h>
#include <errno.h>
#include <cstring>
#include <termios.h>

#include <pwd.h>
#include <grp.h>
Expand Down Expand Up @@ -1558,8 +1559,8 @@ void DerivationGoal::buildDone()
if (hook) {
hook->builderOut.readSide = -1;
hook->fromHook.readSide = -1;
}
else builderOut.readSide = -1;
} else
builderOut.readSide = -1;

/* Close the log file. */
closeLogFile();
Expand Down Expand Up @@ -2181,7 +2182,43 @@ void DerivationGoal::startBuilder()
Path logFile = openLogFile();

/* Create a pipe to get the output of the builder. */
builderOut.create();
//builderOut.create();

builderOut.readSide = posix_openpt(O_RDWR | O_NOCTTY);
if (!builderOut.readSide)
throw SysError("opening pseudoterminal master");

std::string slaveName(ptsname(builderOut.readSide.get()));

if (chmod(slaveName.c_str(), 0600))
throw SysError("changing mode of pseudoterminal slave");

if (buildUser && chown(slaveName.c_str(), buildUser->getUID(), 0))
throw SysError("changing owner of pseudoterminal slave");

#if 0
// Mount the pt in the sandbox so that the "tty" command works.
// FIXME: this doesn't work with the new devpts in the sandbox.
if (useChroot)
dirsInChroot[slaveName] = {slaveName, false};
#endif

if (unlockpt(builderOut.readSide.get()))
throw SysError("unlocking pseudoterminal");

builderOut.writeSide = open(slaveName.c_str(), O_RDWR | O_NOCTTY);
if (!builderOut.writeSide)
throw SysError("opening pseudoterminal slave");

// Put the pt into raw mode to prevent \n -> \r\n translation.
struct termios term;
if (tcgetattr(builderOut.writeSide.get(), &term))
throw SysError("getting pseudoterminal attributes");

cfmakeraw(&term);

if (tcsetattr(builderOut.writeSide.get(), TCSANOW, &term))
throw SysError("putting pseudoterminal into raw mode");

result.startTime = time(0);

Expand Down Expand Up @@ -4361,14 +4398,15 @@ void Worker::waitForInput()
for (auto & k : fds2) {
if (FD_ISSET(k, &fds)) {
ssize_t rd = read(k, buffer.data(), buffer.size());
if (rd == -1) {
if (errno != EINTR)
throw SysError(format("reading from %1%")
% goal->getName());
} else if (rd == 0) {
// FIXME: is there a cleaner way to handle pt close
// than EIO? Is this even standard?
if (rd == 0 || (rd == -1 && errno == EIO)) {
debug(format("%1%: got EOF") % goal->getName());
goal->handleEOF(k);
j->fds.erase(k);
} else if (rd == -1) {
if (errno != EINTR)
throw SysError("%s: read failed", goal->getName());
} else {
printMsg(lvlVomit, format("%1%: read %2% bytes")
% goal->getName() % rd);
Expand Down

0 comments on commit e84c265

Please sign in to comment.