about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2016-04-25T14·47+0200
committerEelco Dolstra <eelco.dolstra@logicblox.com>2016-04-25T17·18+0200
commit5761827d5bd90efda9ba2183ac30ad73d51de6bf (patch)
tree4f99fc2bf540e598cff211bcbb454378bdcdee85
parent6e1b09927930f47bfdf024dad4accd5583e2c5d2 (diff)
Show the log tail when a build fails
If --no-build-output is given (which will become the default for the
"nix" command at least), show the last 10 lines of the build output if
the build fails.
-rw-r--r--src/libmain/shared.cc2
-rw-r--r--src/libstore/build.cc54
-rw-r--r--src/libstore/globals.cc1
-rw-r--r--src/libstore/globals.hh8
-rw-r--r--src/libstore/remote-store.cc2
-rw-r--r--src/nix-daemon/nix-daemon.cc2
6 files changed, 54 insertions, 15 deletions
diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc
index 0d9f9af73e16..0b6311516ad4 100644
--- a/src/libmain/shared.cc
+++ b/src/libmain/shared.cc
@@ -171,7 +171,7 @@ struct LegacyArgs : public MixCommonArgs
         : MixCommonArgs(programName), parseArg(parseArg)
     {
         mkFlag('Q', "no-build-output", "do not show build output",
-            &settings.buildVerbosity, lvlVomit);
+            &settings.verboseBuild, false);
 
         mkFlag('K', "keep-failed", "keep temporary directories of failed builds",
             &settings.keepFailed);
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index f680b3737895..3830d7a671e8 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -747,6 +747,11 @@ private:
     /* Number of bytes received from the builder's stdout/stderr. */
     unsigned long logSize;
 
+    /* The most recent log lines. */
+    std::list<std::string> logTail;
+
+    std::string currentLogLine;
+
     /* Pipe for the builder's standard output/error. */
     Pipe builderOut;
 
@@ -872,6 +877,7 @@ private:
     /* Callback used by the worker to write to the log. */
     void handleChildOutput(int fd, const string & data) override;
     void handleEOF(int fd) override;
+    void flushLine();
 
     /* Return the set of (in)valid paths. */
     PathSet checkPathValidity(bool returnValid, bool checkHash);
@@ -1462,11 +1468,19 @@ void DerivationGoal::buildDone()
                     if (pathExists(chrootRootDir + i))
                         rename((chrootRootDir + i).c_str(), i.c_str());
 
+            std::string msg = (format("builder for ‘%1%’ %2%")
+                % drvPath % statusToString(status)).str();
+
+            if (!settings.verboseBuild && !logTail.empty()) {
+                msg += (format("; last %d log lines:") % logTail.size()).str();
+                for (auto & line : logTail)
+                    msg += "\n  " + line;
+            }
+
             if (diskFull)
-                printMsg(lvlError, "note: build failure may have been caused by lack of free disk space");
+                msg += "\nnote: build failure may have been caused by lack of free disk space";
 
-            throw BuildError(format("builder for ‘%1%’ %2%")
-                % drvPath % statusToString(status));
+            throw BuildError(msg);
         }
 
         /* Compute the FS closure of the outputs and register them as
@@ -2920,8 +2934,20 @@ void DerivationGoal::handleChildOutput(int fd, const string & data)
             done(BuildResult::LogLimitExceeded);
             return;
         }
-        if (verbosity >= settings.buildVerbosity)
-            printMsg(lvlError, filterANSIEscapes(data, true)); // FIXME
+
+        for (size_t pos = 0; true; ) {
+            auto newline = data.find('\n', pos);
+
+            if (newline == std::string::npos) {
+                currentLogLine.append(data, pos, std::string::npos);
+                break;
+            }
+
+            currentLogLine.append(data, pos, newline - pos);
+            flushLine();
+            pos = newline + 1;
+        }
+
         if (bzLogFile) {
             int err;
             BZ2_bzWrite(&err, bzLogFile, (unsigned char *) data.data(), data.size());
@@ -2937,10 +2963,23 @@ void DerivationGoal::handleChildOutput(int fd, const string & data)
 
 void DerivationGoal::handleEOF(int fd)
 {
+    flushLine();
     worker.wakeUp(shared_from_this());
 }
 
 
+void DerivationGoal::flushLine()
+{
+    if (settings.verboseBuild)
+        printMsg(lvlInfo, filterANSIEscapes(currentLogLine, true));
+    else {
+        logTail.push_back(currentLogLine);
+        if (logTail.size() > settings.logLines) logTail.pop_front();
+    }
+    currentLogLine = "";
+}
+
+
 PathSet DerivationGoal::checkPathValidity(bool returnValid, bool checkHash)
 {
     PathSet result;
@@ -3341,10 +3380,7 @@ void SubstitutionGoal::finished()
 void SubstitutionGoal::handleChildOutput(int fd, const string & data)
 {
     assert(fd == logPipe.readSide);
-    if (verbosity >= settings.buildVerbosity)
-        printMsg(lvlError, data); // FIXME
-    /* Don't write substitution output to a log file for now.  We
-       probably should, though. */
+    printMsg(lvlError, data); // FIXME
 }
 
 
diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc
index f4c41d715f35..90539ea85420 100644
--- a/src/libstore/globals.cc
+++ b/src/libstore/globals.cc
@@ -28,7 +28,6 @@ Settings::Settings()
     keepFailed = false;
     keepGoing = false;
     tryFallback = false;
-    buildVerbosity = lvlError;
     maxBuildJobs = 1;
     buildCores = 1;
 #ifdef _SC_NPROCESSORS_ONLN
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index 8fa49e2dc120..60251ef421bf 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -78,8 +78,12 @@ struct Settings {
        instead. */
     bool tryFallback;
 
-    /* Verbosity level for build output. */
-    Verbosity buildVerbosity;
+    /* Whether to show build log output in real time. */
+    bool verboseBuild = true;
+
+    /* If verboseBuild is false, the number of lines of the tail of
+       the log to show if a build fails. */
+    size_t logLines = 10;
 
     /* Maximum number of parallel build jobs.  0 means unlimited. */
     unsigned int maxBuildJobs;
diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc
index 831f4a83e029..4663291b9171 100644
--- a/src/libstore/remote-store.cc
+++ b/src/libstore/remote-store.cc
@@ -120,7 +120,7 @@ void RemoteStore::setOptions(ref<Connection> conn)
     if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 2)
         conn->to << settings.useBuildHook;
     if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 4)
-        conn->to << settings.buildVerbosity
+        conn->to << (settings.verboseBuild ? lvlError : lvlVomit)
                  << 0 // obsolete log type
                  << 0 /* obsolete print build trace */;
     if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 6)
diff --git a/src/nix-daemon/nix-daemon.cc b/src/nix-daemon/nix-daemon.cc
index fbac852e0bd8..3c2e0521028c 100644
--- a/src/nix-daemon/nix-daemon.cc
+++ b/src/nix-daemon/nix-daemon.cc
@@ -443,7 +443,7 @@ static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVe
         if (GET_PROTOCOL_MINOR(clientVersion) >= 2)
             settings.useBuildHook = readInt(from) != 0;
         if (GET_PROTOCOL_MINOR(clientVersion) >= 4) {
-            settings.buildVerbosity = (Verbosity) readInt(from);
+            settings.verboseBuild = lvlError == (Verbosity) readInt(from);
             readInt(from); // obsolete logType
             readInt(from); // obsolete printBuildTrace
         }