Improved logging abstraction
This also gets rid of --log-type, since the nested log type isn't useful in a multi-threaded situation, and nobody cares about the "pretty" log type.
This commit is contained in:
		
							parent
							
								
									c879a20850
								
							
						
					
					
						commit
						41633f9f73
					
				
					 29 changed files with 394 additions and 456 deletions
				
			
		|  | @ -31,7 +31,6 @@ | |||
| <arg><option>-K</option></arg> | ||||
| <arg><option>--fallback</option></arg> | ||||
| <arg><option>--readonly-mode</option></arg> | ||||
| <arg><option>--log-type</option> <replaceable>type</replaceable></arg> | ||||
| <arg><option>--show-trace</option></arg> | ||||
| <arg> | ||||
|   <option>-I</option> | ||||
|  |  | |||
|  | @ -201,61 +201,6 @@ | |||
| </varlistentry> | ||||
| 
 | ||||
| 
 | ||||
| <varlistentry xml:id="opt-log-type"><term><option>--log-type</option> | ||||
| <replaceable>type</replaceable></term> | ||||
| 
 | ||||
|   <listitem> | ||||
| 
 | ||||
|   <para>This option determines how the output written to standard | ||||
|   error is formatted.  Nix’s diagnostic messages are typically | ||||
|   <emphasis>nested</emphasis>.  For instance, when tracing Nix | ||||
|   expression evaluation (<command>nix-env -vvvvv</command>, messages | ||||
|   from subexpressions are nested inside their parent expressions.  Nix | ||||
|   builder output is also often nested.  For instance, the Nix Packages | ||||
|   generic builder nests the various build tasks (unpack, configure, | ||||
|   compile, etc.), and the GNU Make in <literal>stdenv-linux</literal> | ||||
|   has been patched to provide nesting for recursive Make | ||||
|   invocations.</para> | ||||
| 
 | ||||
|   <para><replaceable>type</replaceable> can be one of the | ||||
|   following: | ||||
| 
 | ||||
|   <variablelist> | ||||
| 
 | ||||
|     <varlistentry><term><literal>pretty</literal></term> | ||||
| 
 | ||||
|       <listitem><para>Pretty-print the output, indicating different | ||||
|       nesting levels using spaces.  This is the | ||||
|       default.</para></listitem> | ||||
| 
 | ||||
|     </varlistentry> | ||||
| 
 | ||||
|     <varlistentry><term><literal>escapes</literal></term> | ||||
| 
 | ||||
|       <listitem><para>Indicate nesting using escape codes that can be | ||||
|       interpreted by the <command>nix-log2xml</command> tool in the | ||||
|       Nix source distribution.  The resulting XML file can be fed into | ||||
|       the <command>log2html.xsl</command> stylesheet to create an HTML | ||||
|       file that can be browsed interactively, using JavaScript to | ||||
|       expand and collapse parts of the output.</para></listitem> | ||||
| 
 | ||||
|     </varlistentry> | ||||
| 
 | ||||
|     <varlistentry><term><literal>flat</literal></term> | ||||
| 
 | ||||
|       <listitem><para>Remove all nesting.</para></listitem> | ||||
| 
 | ||||
|     </varlistentry> | ||||
| 
 | ||||
|   </variablelist> | ||||
| 
 | ||||
|   </para> | ||||
| 
 | ||||
|   </listitem> | ||||
| 
 | ||||
| </varlistentry> | ||||
| 
 | ||||
| 
 | ||||
| <varlistentry><term><option>--arg</option> <replaceable>name</replaceable> <replaceable>value</replaceable></term> | ||||
| 
 | ||||
|   <listitem><para>This option is accepted by | ||||
|  |  | |||
|  | @ -110,13 +110,6 @@ for (my $n = 0; $n < scalar @ARGV; $n++) { | |||
|         $n += 2; | ||||
|     } | ||||
| 
 | ||||
|     elsif ($arg eq "--log-type") { | ||||
|         $n++; | ||||
|         die "$0: ‘$arg’ requires an argument\n" unless $n < scalar @ARGV; | ||||
|         push @instArgs, ($arg, $ARGV[$n]); | ||||
|         push @buildArgs, ($arg, $ARGV[$n]); | ||||
|     } | ||||
| 
 | ||||
|     elsif ($arg eq "--option") { | ||||
|         die "$0: ‘$arg’ requires two arguments\n" unless $n + 2 < scalar @ARGV; | ||||
|         push @instArgs, ($arg, $ARGV[$n + 1], $ARGV[$n + 2]); | ||||
|  | @ -124,7 +117,7 @@ for (my $n = 0; $n < scalar @ARGV; $n++) { | |||
|         $n += 2; | ||||
|     } | ||||
| 
 | ||||
|     elsif ($arg eq "--max-jobs" || $arg eq "-j" || $arg eq "--max-silent-time" || $arg eq "--log-type" || $arg eq "--cores" || $arg eq "--timeout" || $arg eq '--add-root') { | ||||
|     elsif ($arg eq "--max-jobs" || $arg eq "-j" || $arg eq "--max-silent-time" || $arg eq "--cores" || $arg eq "--timeout" || $arg eq '--add-root') { | ||||
|         $n++; | ||||
|         die "$0: ‘$arg’ requires an argument\n" unless $n < scalar @ARGV; | ||||
|         push @buildArgs, ($arg, $ARGV[$n]); | ||||
|  |  | |||
|  | @ -641,7 +641,7 @@ void EvalState::evalFile(const Path & path, Value & v) | |||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     startNest(nest, lvlTalkative, format("evaluating file ‘%1%’") % path2); | ||||
|     Activity act(*logger, lvlTalkative, format("evaluating file ‘%1%’") % path2); | ||||
|     Expr * e = parseExprFromFile(checkSourcePath(path2)); | ||||
|     try { | ||||
|         eval(e, v); | ||||
|  |  | |||
|  | @ -290,7 +290,7 @@ static void getDerivations(EvalState & state, Value & vIn, | |||
|             attrs.insert(std::pair<string, Symbol>(i.name, i.name)); | ||||
| 
 | ||||
|         for (auto & i : attrs) { | ||||
|             startNest(nest, lvlDebug, format("evaluating attribute ‘%1%’") % i.first); | ||||
|             Activity act(*logger, lvlDebug, format("evaluating attribute ‘%1%’") % i.first); | ||||
|             string pathPrefix2 = addToPath(pathPrefix, i.first); | ||||
|             Value & v2(*v.attrs->find(i.second)->value); | ||||
|             if (combineChannels) | ||||
|  | @ -310,8 +310,7 @@ static void getDerivations(EvalState & state, Value & vIn, | |||
| 
 | ||||
|     else if (v.isList()) { | ||||
|         for (unsigned int n = 0; n < v.listSize(); ++n) { | ||||
|             startNest(nest, lvlDebug, | ||||
|                 format("evaluating list element")); | ||||
|             Activity act(*logger, lvlDebug, "evaluating list element"); | ||||
|             string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str()); | ||||
|             if (getDerivation(state, *v.listElems()[n], pathPrefix2, drvs, done, ignoreAssertionFailures)) | ||||
|                 getDerivations(state, *v.listElems()[n], pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); | ||||
|  |  | |||
|  | @ -124,7 +124,7 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args | |||
|                 env->values[displ++] = attr.value; | ||||
|             } | ||||
| 
 | ||||
|             startNest(nest, lvlTalkative, format("evaluating file ‘%1%’") % path); | ||||
|             Activity act(*logger, lvlTalkative, format("evaluating file ‘%1%’") % path); | ||||
|             Expr * e = state.parseExprFromFile(resolveExprPath(path), staticEnv); | ||||
| 
 | ||||
|             e->eval(state, *env, v); | ||||
|  | @ -284,7 +284,7 @@ typedef list<Value *> ValueList; | |||
| 
 | ||||
| static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * args, Value & v) | ||||
| { | ||||
|     startNest(nest, lvlDebug, "finding dependencies"); | ||||
|     Activity act(*logger, lvlDebug, "finding dependencies"); | ||||
| 
 | ||||
|     state.forceAttrs(*args[0], pos); | ||||
| 
 | ||||
|  | @ -457,7 +457,7 @@ void prim_valueSize(EvalState & state, const Pos & pos, Value * * args, Value & | |||
|    derivation. */ | ||||
| static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * args, Value & v) | ||||
| { | ||||
|     startNest(nest, lvlVomit, "evaluating derivation"); | ||||
|     Activity act(*logger, lvlVomit, "evaluating derivation"); | ||||
| 
 | ||||
|     state.forceAttrs(*args[0], pos); | ||||
| 
 | ||||
|  | @ -494,7 +494,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * | |||
|     for (auto & i : *args[0]->attrs) { | ||||
|         if (i.name == state.sIgnoreNulls) continue; | ||||
|         string key = i.name; | ||||
|         startNest(nest, lvlVomit, format("processing attribute ‘%1%’") % key); | ||||
|         Activity act(*logger, lvlVomit, format("processing attribute ‘%1%’") % key); | ||||
| 
 | ||||
|         try { | ||||
| 
 | ||||
|  |  | |||
|  | @ -18,15 +18,6 @@ MixCommonArgs::MixCommonArgs(const string & programName) | |||
|         verbosity = lvlDebug; | ||||
|     }); | ||||
| 
 | ||||
|     mkFlag1(0, "log-type", "type", "set logging format ('pretty', 'flat', 'systemd')", | ||||
|         [](std::string s) { | ||||
|             if (s == "pretty") logType = ltPretty; | ||||
|             else if (s == "escapes") logType = ltEscapes; | ||||
|             else if (s == "flat") logType = ltFlat; | ||||
|             else if (s == "systemd") logType = ltSystemd; | ||||
|             else throw UsageError("unknown log type"); | ||||
|         }); | ||||
| 
 | ||||
|     mkFlag(0, "option", {"name", "value"}, "set a Nix configuration option (overriding nix.conf)", 2, | ||||
|         [](Strings ss) { | ||||
|             auto name = ss.front(); ss.pop_front(); | ||||
|  |  | |||
|  | @ -111,8 +111,7 @@ void initNix() | |||
|     std::cerr.rdbuf()->pubsetbuf(buf, sizeof(buf)); | ||||
| #endif | ||||
| 
 | ||||
|     if (getEnv("IN_SYSTEMD") == "1") | ||||
|         logType = ltSystemd; | ||||
|     logger = makeDefaultLogger(); | ||||
| 
 | ||||
|     /* Initialise OpenSSL locking. */ | ||||
|     opensslLocks = std::vector<std::mutex>(CRYPTO_num_locks()); | ||||
|  |  | |||
|  | @ -1657,7 +1657,7 @@ void DerivationGoal::startBuilder() | |||
|         nrRounds > 1 ? "building path(s) %1% (round %2%/%3%)" : | ||||
|         "building path(s) %1%"); | ||||
|     f.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit); | ||||
|     startNest(nest, lvlInfo, f % showPaths(missingPaths) % curRound % nrRounds); | ||||
|     printMsg(lvlInfo, f % showPaths(missingPaths) % curRound % nrRounds); | ||||
| 
 | ||||
|     /* Right platform? */ | ||||
|     if (!drv->canBuildLocally()) { | ||||
|  | @ -2192,8 +2192,6 @@ void DerivationGoal::runChild() | |||
| 
 | ||||
|     try { /* child */ | ||||
| 
 | ||||
|         logType = ltFlat; | ||||
| 
 | ||||
|         commonChildInit(builderOut); | ||||
| 
 | ||||
| #if __linux__ | ||||
|  | @ -2535,7 +2533,6 @@ void DerivationGoal::runChild() | |||
|         /* Execute the program.  This should not return. */ | ||||
|         if (drv->isBuiltin()) { | ||||
|             try { | ||||
|                 logType = ltFlat; | ||||
|                 if (drv->builder == "builtin:fetchurl") | ||||
|                     builtinFetchurl(*drv); | ||||
|                 else | ||||
|  | @ -2667,8 +2664,7 @@ void DerivationGoal::registerOutputs() | |||
|             rewritten = true; | ||||
|         } | ||||
| 
 | ||||
|         startNest(nest, lvlTalkative, | ||||
|             format("scanning for references inside ‘%1%’") % path); | ||||
|         Activity act(*logger, lvlTalkative, format("scanning for references inside ‘%1%’") % path); | ||||
| 
 | ||||
|         /* Check that fixed-output derivations produced the right
 | ||||
|            outputs (i.e., the content hash should match the specified | ||||
|  | @ -2955,7 +2951,7 @@ void DerivationGoal::handleChildOutput(int fd, const string & data) | |||
|             return; | ||||
|         } | ||||
|         if (verbosity >= settings.buildVerbosity) | ||||
|             writeToStderr(filterANSIEscapes(data, true)); | ||||
|             printMsg(lvlError, filterANSIEscapes(data, true)); // FIXME
 | ||||
|         if (bzLogFile) { | ||||
|             int err; | ||||
|             BZ2_bzWrite(&err, bzLogFile, (unsigned char *) data.data(), data.size()); | ||||
|  | @ -2965,7 +2961,7 @@ void DerivationGoal::handleChildOutput(int fd, const string & data) | |||
|     } | ||||
| 
 | ||||
|     if (hook && fd == hook->fromHook.readSide) | ||||
|         writeToStderr(data); | ||||
|         printMsg(lvlError, data); // FIXME?
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -3388,7 +3384,8 @@ void SubstitutionGoal::finished() | |||
| void SubstitutionGoal::handleChildOutput(int fd, const string & data) | ||||
| { | ||||
|     assert(fd == logPipe.readSide); | ||||
|     if (verbosity >= settings.buildVerbosity) writeToStderr(data); | ||||
|     if (verbosity >= settings.buildVerbosity) | ||||
|         printMsg(lvlError, data); // FIXME
 | ||||
|     /* Don't write substitution output to a log file for now.  We
 | ||||
|        probably should, though. */ | ||||
| } | ||||
|  | @ -3586,7 +3583,7 @@ void Worker::run(const Goals & _topGoals) | |||
| { | ||||
|     for (auto & i : _topGoals) topGoals.insert(i); | ||||
| 
 | ||||
|     startNest(nest, lvlDebug, format("entered goal loop")); | ||||
|     Activity act(*logger, lvlDebug, "entered goal loop"); | ||||
| 
 | ||||
|     while (1) { | ||||
| 
 | ||||
|  | @ -3804,8 +3801,6 @@ void Worker::markContentsGood(const Path & path) | |||
| 
 | ||||
| void LocalStore::buildPaths(const PathSet & drvPaths, BuildMode buildMode) | ||||
| { | ||||
|     startNest(nest, lvlDebug, format("building %1%") % showPaths(drvPaths)); | ||||
| 
 | ||||
|     Worker worker(*this); | ||||
| 
 | ||||
|     Goals goals; | ||||
|  | @ -3835,8 +3830,6 @@ void LocalStore::buildPaths(const PathSet & drvPaths, BuildMode buildMode) | |||
| BuildResult LocalStore::buildDerivation(const Path & drvPath, const BasicDerivation & drv, | ||||
|     BuildMode buildMode) | ||||
| { | ||||
|     startNest(nest, lvlDebug, format("building %1%") % showPaths({drvPath})); | ||||
| 
 | ||||
|     Worker worker(*this); | ||||
|     auto goal = worker.makeBasicDerivationGoal(drvPath, drv, buildMode); | ||||
| 
 | ||||
|  |  | |||
|  | @ -514,7 +514,7 @@ void LocalStore::tryToDelete(GCState & state, const Path & path) | |||
| 
 | ||||
|     if (path == linksDir || path == state.trashDir) return; | ||||
| 
 | ||||
|     startNest(nest, lvlDebug, format("considering whether to delete ‘%1%’") % path); | ||||
|     Activity act(*logger, lvlDebug, format("considering whether to delete ‘%1%’") % path); | ||||
| 
 | ||||
|     if (!isValidPath(path)) { | ||||
|         /* A lock file belonging to a path that we're building right
 | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "types.hh" | ||||
| #include "logging.hh" | ||||
| 
 | ||||
| #include <map> | ||||
| #include <sys/types.h> | ||||
|  |  | |||
|  | @ -228,7 +228,7 @@ void LocalStore::optimiseStore(OptimiseStats & stats) | |||
|     for (auto & i : paths) { | ||||
|         addTempRoot(i); | ||||
|         if (!isValidPath(i)) continue; /* path was GC'ed, probably */ | ||||
|         startNest(nest, lvlChatty, format("hashing files in ‘%1%’") % i); | ||||
|         Activity act(*logger, lvlChatty, format("hashing files in ‘%1%’") % i); | ||||
|         optimisePath_(stats, i, inodeHash); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -121,7 +121,7 @@ void RemoteStore::setOptions(ref<Connection> conn) | |||
|         conn->to << settings.useBuildHook; | ||||
|     if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 4) | ||||
|         conn->to << settings.buildVerbosity | ||||
|            << logType | ||||
|                  << 0 // obsolete log type
 | ||||
|                  << settings.printBuildTrace; | ||||
|     if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 6) | ||||
|         conn->to << settings.buildCores; | ||||
|  | @ -561,10 +561,8 @@ void RemoteStore::Connection::processStderr(Sink * sink, Source * source) | |||
|             writeString(buf, source->read(buf, len), to); | ||||
|             to.flush(); | ||||
|         } | ||||
|         else { | ||||
|             string s = readString(from); | ||||
|             writeToStderr(s); | ||||
|         } | ||||
|         else | ||||
|             printMsg(lvlError, chomp(readString(from))); | ||||
|     } | ||||
|     if (msg == STDERR_ERROR) { | ||||
|         string error = readString(from); | ||||
|  |  | |||
							
								
								
									
										79
									
								
								src/libutil/logging.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/libutil/logging.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,79 @@ | |||
| #include "logging.hh" | ||||
| #include "util.hh" | ||||
| 
 | ||||
| namespace nix { | ||||
| 
 | ||||
| Logger * logger = 0; | ||||
| 
 | ||||
| class SimpleLogger : public Logger | ||||
| { | ||||
| public: | ||||
| 
 | ||||
|     bool systemd, tty; | ||||
| 
 | ||||
|     SimpleLogger() | ||||
|     { | ||||
|         systemd = getEnv("IN_SYSTEMD") == "1"; | ||||
|         tty = isatty(STDERR_FILENO); | ||||
|     } | ||||
| 
 | ||||
|     void log(Verbosity lvl, const FormatOrString & fs) override | ||||
|     { | ||||
|         if (lvl > verbosity) return; | ||||
| 
 | ||||
|         std::string prefix; | ||||
| 
 | ||||
|         if (systemd) { | ||||
|             char c; | ||||
|             switch (lvl) { | ||||
|             case lvlError: c = '3'; break; | ||||
|             case lvlInfo: c = '5'; break; | ||||
|             case lvlTalkative: case lvlChatty: c = '6'; break; | ||||
|             default: c = '7'; | ||||
|             } | ||||
|             prefix = std::string("<") + c + ">"; | ||||
|         } | ||||
| 
 | ||||
|         writeToStderr(prefix + (tty ? fs.s : filterANSIEscapes(fs.s)) + "\n"); | ||||
|     } | ||||
| 
 | ||||
|     void startActivity(Activity & activity, Verbosity lvl, const FormatOrString & fs) override | ||||
|     { | ||||
|         log(lvl, fs); | ||||
|     } | ||||
| 
 | ||||
|     void stopActivity(Activity & activity) override | ||||
|     { | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| Verbosity verbosity = lvlInfo; | ||||
| 
 | ||||
| void warnOnce(bool & haveWarned, const FormatOrString & fs) | ||||
| { | ||||
|     if (!haveWarned) { | ||||
|         printMsg(lvlError, format("warning: %1%") % fs.s); | ||||
|         haveWarned = true; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void writeToStderr(const string & s) | ||||
| { | ||||
|     try { | ||||
|         writeFull(STDERR_FILENO, s); | ||||
|     } catch (SysError & e) { | ||||
|         /* Ignore failing writes to stderr if we're in an exception
 | ||||
|            handler, otherwise throw an exception.  We need to ignore | ||||
|            write errors in exception handlers to ensure that cleanup | ||||
|            code runs to completion if the other side of stderr has | ||||
|            been closed unexpectedly. */ | ||||
|         if (!std::uncaught_exception()) throw; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| Logger * makeDefaultLogger() | ||||
| { | ||||
|     return new SimpleLogger(); | ||||
| } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										82
									
								
								src/libutil/logging.hh
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/libutil/logging.hh
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,82 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "types.hh" | ||||
| 
 | ||||
| namespace nix { | ||||
| 
 | ||||
| typedef enum { | ||||
|     lvlError = 0, | ||||
|     lvlInfo, | ||||
|     lvlTalkative, | ||||
|     lvlChatty, | ||||
|     lvlDebug, | ||||
|     lvlVomit | ||||
| } Verbosity; | ||||
| 
 | ||||
| class Activity; | ||||
| 
 | ||||
| class Logger | ||||
| { | ||||
|     friend class Activity; | ||||
| 
 | ||||
| public: | ||||
| 
 | ||||
|     virtual ~Logger() { } | ||||
| 
 | ||||
|     virtual void log(Verbosity lvl, const FormatOrString & fs) = 0; | ||||
| 
 | ||||
|     void log(const FormatOrString & fs) | ||||
|     { | ||||
|         log(lvlInfo, fs); | ||||
|     } | ||||
| 
 | ||||
|     virtual void setExpected(const std::string & label, uint64_t value = 1) { } | ||||
|     virtual void setProgress(const std::string & label, uint64_t value = 1) { } | ||||
|     virtual void incExpected(const std::string & label, uint64_t value = 1) { } | ||||
|     virtual void incProgress(const std::string & label, uint64_t value = 1) { } | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
|     virtual void startActivity(Activity & activity, Verbosity lvl, const FormatOrString & fs) = 0; | ||||
| 
 | ||||
|     virtual void stopActivity(Activity & activity) = 0; | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| class Activity | ||||
| { | ||||
| public: | ||||
|     Logger & logger; | ||||
| 
 | ||||
|     Activity(Logger & logger, Verbosity lvl, const FormatOrString & fs) | ||||
|         : logger(logger) | ||||
|     { | ||||
|         logger.startActivity(*this, lvl, fs); | ||||
|     } | ||||
| 
 | ||||
|     ~Activity() | ||||
|     { | ||||
|         logger.stopActivity(*this); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| extern Logger * logger; | ||||
| 
 | ||||
| Logger * makeDefaultLogger(); | ||||
| 
 | ||||
| extern Verbosity verbosity; /* suppress msgs > this */ | ||||
| 
 | ||||
| #define printMsg(level, f) \ | ||||
|     do { \ | ||||
|         if (level <= nix::verbosity) { \ | ||||
|             logger->log(level, (f)); \ | ||||
|         } \ | ||||
|     } while (0) | ||||
| 
 | ||||
| #define debug(f) printMsg(lvlDebug, f) | ||||
| 
 | ||||
| void warnOnce(bool & haveWarned, const FormatOrString & fs); | ||||
| 
 | ||||
| void writeToStderr(const string & s); | ||||
| 
 | ||||
| } | ||||
|  | @ -89,14 +89,4 @@ typedef list<Path> Paths; | |||
| typedef set<Path> PathSet; | ||||
| 
 | ||||
| 
 | ||||
| typedef enum { | ||||
|     lvlError = 0, | ||||
|     lvlInfo, | ||||
|     lvlTalkative, | ||||
|     lvlChatty, | ||||
|     lvlDebug, | ||||
|     lvlVomit | ||||
| } Verbosity; | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -356,8 +356,7 @@ void deletePath(const Path & path) | |||
| 
 | ||||
| void deletePath(const Path & path, unsigned long long & bytesFreed) | ||||
| { | ||||
|     startNest(nest, lvlDebug, | ||||
|         format("recursively deleting path ‘%1%’") % path); | ||||
|     Activity act(*logger, lvlDebug, format("recursively deleting path ‘%1%’") % path); | ||||
|     bytesFreed = 0; | ||||
|     _deletePath(path, bytesFreed); | ||||
| } | ||||
|  | @ -456,113 +455,6 @@ void replaceSymlink(const Path & target, const Path & link) | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| LogType logType = ltPretty; | ||||
| Verbosity verbosity = lvlInfo; | ||||
| 
 | ||||
| static int nestingLevel = 0; | ||||
| 
 | ||||
| 
 | ||||
| Nest::Nest() | ||||
| { | ||||
|     nest = false; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| Nest::~Nest() | ||||
| { | ||||
|     close(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static string escVerbosity(Verbosity level) | ||||
| { | ||||
|     return std::to_string((int) level); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void Nest::open(Verbosity level, const FormatOrString & fs) | ||||
| { | ||||
|     if (level <= verbosity) { | ||||
|         if (logType == ltEscapes) | ||||
|             std::cerr << "\033[" << escVerbosity(level) << "p" | ||||
|                       << fs.s << "\n"; | ||||
|         else | ||||
|             printMsg_(level, fs); | ||||
|         nest = true; | ||||
|         nestingLevel++; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void Nest::close() | ||||
| { | ||||
|     if (nest) { | ||||
|         nestingLevel--; | ||||
|         if (logType == ltEscapes) | ||||
|             std::cerr << "\033[q"; | ||||
|         nest = false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void printMsg_(Verbosity level, const FormatOrString & fs) | ||||
| { | ||||
|     checkInterrupt(); | ||||
|     if (level > verbosity) return; | ||||
| 
 | ||||
|     string prefix; | ||||
|     if (logType == ltPretty) | ||||
|         for (int i = 0; i < nestingLevel; i++) | ||||
|             prefix += "|   "; | ||||
|     else if (logType == ltEscapes && level != lvlInfo) | ||||
|         prefix = "\033[" + escVerbosity(level) + "s"; | ||||
|     else if (logType == ltSystemd) { | ||||
|         char c; | ||||
|         switch (level) { | ||||
|             case lvlError: c = '3'; break; | ||||
|             case lvlInfo: c = '5'; break; | ||||
|             case lvlTalkative: case lvlChatty: c = '6'; break; | ||||
|             default: c = '7'; | ||||
|         } | ||||
|         prefix = string("<") + c + ">"; | ||||
|     } | ||||
| 
 | ||||
|     string s = (format("%1%%2%\n") % prefix % fs.s).str(); | ||||
|     if (!isatty(STDERR_FILENO)) s = filterANSIEscapes(s); | ||||
|     writeToStderr(s); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void warnOnce(bool & haveWarned, const FormatOrString & fs) | ||||
| { | ||||
|     if (!haveWarned) { | ||||
|         printMsg(lvlError, format("warning: %1%") % fs.s); | ||||
|         haveWarned = true; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void writeToStderr(const string & s) | ||||
| { | ||||
|     try { | ||||
|         if (_writeToStderr) | ||||
|             _writeToStderr((const unsigned char *) s.data(), s.size()); | ||||
|         else | ||||
|             writeFull(STDERR_FILENO, s); | ||||
|     } catch (SysError & e) { | ||||
|         /* Ignore failing writes to stderr if we're in an exception
 | ||||
|            handler, otherwise throw an exception.  We need to ignore | ||||
|            write errors in exception handlers to ensure that cleanup | ||||
|            code runs to completion if the other side of stderr has | ||||
|            been closed unexpectedly. */ | ||||
|         if (!std::uncaught_exception()) throw; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| std::function<void(const unsigned char * buf, size_t count)> _writeToStderr; | ||||
| 
 | ||||
| 
 | ||||
| void readFull(int fd, unsigned char * buf, size_t count) | ||||
| { | ||||
|     while (count) { | ||||
|  | @ -953,7 +845,8 @@ static pid_t doFork(bool allowVfork, std::function<void()> fun) | |||
| pid_t startProcess(std::function<void()> fun, const ProcessOptions & options) | ||||
| { | ||||
|     auto wrapper = [&]() { | ||||
|         if (!options.allowVfork) _writeToStderr = 0; | ||||
|         if (!options.allowVfork) | ||||
|             logger = makeDefaultLogger(); | ||||
|         try { | ||||
| #if __linux__ | ||||
|             if (options.dieWithParent && prctl(PR_SET_PDEATHSIG, SIGKILL) == -1) | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "types.hh" | ||||
| #include "logging.hh" | ||||
| 
 | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
|  | @ -125,54 +126,6 @@ T singleton(const A & a) | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Messages. */ | ||||
| 
 | ||||
| 
 | ||||
| typedef enum { | ||||
|     ltPretty,   /* nice, nested output */ | ||||
|     ltEscapes,  /* nesting indicated using escape codes (for log2xml) */ | ||||
|     ltFlat,     /* no nesting */ | ||||
|     ltSystemd,  /* use systemd severity prefixes */ | ||||
| } LogType; | ||||
| 
 | ||||
| extern LogType logType; | ||||
| extern Verbosity verbosity; /* suppress msgs > this */ | ||||
| 
 | ||||
| class Nest | ||||
| { | ||||
| private: | ||||
|     bool nest; | ||||
| public: | ||||
|     Nest(); | ||||
|     ~Nest(); | ||||
|     void open(Verbosity level, const FormatOrString & fs); | ||||
|     void close(); | ||||
| }; | ||||
| 
 | ||||
| void printMsg_(Verbosity level, const FormatOrString & fs); | ||||
| 
 | ||||
| #define startNest(varName, level, f) \ | ||||
|     Nest varName; \ | ||||
|     if (level <= verbosity) { \ | ||||
|       varName.open(level, (f)); \ | ||||
|     } | ||||
| 
 | ||||
| #define printMsg(level, f) \ | ||||
|     do { \ | ||||
|         if (level <= nix::verbosity) { \ | ||||
|             nix::printMsg_(level, (f)); \ | ||||
|         } \ | ||||
|     } while (0) | ||||
| 
 | ||||
| #define debug(f) printMsg(lvlDebug, f) | ||||
| 
 | ||||
| void warnOnce(bool & haveWarned, const FormatOrString & fs); | ||||
| 
 | ||||
| void writeToStderr(const string & s); | ||||
| 
 | ||||
| extern std::function<void(const unsigned char * buf, size_t count)> _writeToStderr; | ||||
| 
 | ||||
| 
 | ||||
| /* Wrappers arount read()/write() that read/write exactly the
 | ||||
|    requested number of bytes. */ | ||||
| void readFull(int fd, unsigned char * buf, size_t count); | ||||
|  |  | |||
|  | @ -33,19 +33,23 @@ using namespace nix; | |||
| static FdSource from(STDIN_FILENO); | ||||
| static FdSink to(STDOUT_FILENO); | ||||
| 
 | ||||
| bool canSendStderr; | ||||
| static bool canSendStderr; | ||||
| 
 | ||||
| static Logger * defaultLogger; | ||||
| 
 | ||||
| 
 | ||||
| /* This function is called anytime we want to write something to
 | ||||
|    stderr.  If we're in a state where the protocol allows it (i.e., | ||||
|    when canSendStderr), send the message to the client over the | ||||
|    socket. */ | ||||
| static void tunnelStderr(const unsigned char * buf, size_t count) | ||||
| /* Logger that forwards log messages to the client, *if* we're in a
 | ||||
|    state where the protocol allows it (i.e., when canSendStderr is | ||||
|    true). */ | ||||
| class TunnelLogger : public Logger | ||||
| { | ||||
|     void log(Verbosity lvl, const FormatOrString & fs) override | ||||
|     { | ||||
|         if (lvl > verbosity) return; | ||||
| 
 | ||||
|         if (canSendStderr) { | ||||
|             try { | ||||
|             to << STDERR_NEXT; | ||||
|             writeString(buf, count, to); | ||||
|                 to << STDERR_NEXT << (fs.s + "\n"); | ||||
|                 to.flush(); | ||||
|             } catch (...) { | ||||
|                 /* Write failed; that means that the other side is
 | ||||
|  | @ -54,8 +58,18 @@ static void tunnelStderr(const unsigned char * buf, size_t count) | |||
|                 throw; | ||||
|             } | ||||
|         } else | ||||
|         writeFull(STDERR_FILENO, buf, count); | ||||
| } | ||||
|             defaultLogger->log(lvl, fs); | ||||
|     } | ||||
| 
 | ||||
|     void startActivity(Activity & activity, Verbosity lvl, const FormatOrString & fs) override | ||||
|     { | ||||
|         log(lvl, fs); | ||||
|     } | ||||
| 
 | ||||
|     void stopActivity(Activity & activity) override | ||||
|     { | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /* startWork() means that we're starting an operation for which we
 | ||||
|  | @ -430,7 +444,7 @@ static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVe | |||
|             settings.useBuildHook = readInt(from) != 0; | ||||
|         if (GET_PROTOCOL_MINOR(clientVersion) >= 4) { | ||||
|             settings.buildVerbosity = (Verbosity) readInt(from); | ||||
|             logType = (LogType) readInt(from); | ||||
|             readInt(from); // obsolete logType
 | ||||
|             settings.printBuildTrace = readInt(from) != 0; | ||||
|         } | ||||
|         if (GET_PROTOCOL_MINOR(clientVersion) >= 6) | ||||
|  | @ -557,7 +571,8 @@ static void processConnection(bool trusted) | |||
|     MonitorFdHup monitor(from.fd); | ||||
| 
 | ||||
|     canSendStderr = false; | ||||
|     _writeToStderr = tunnelStderr; | ||||
|     defaultLogger = logger; | ||||
|     logger = new TunnelLogger(); | ||||
| 
 | ||||
|     /* Exchange the greeting. */ | ||||
|     unsigned int magic = readInt(from); | ||||
|  |  | |||
|  | @ -996,7 +996,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) | |||
|         try { | ||||
|             if (i.hasFailed()) continue; | ||||
| 
 | ||||
|             startNest(nest, lvlDebug, format("outputting query result ‘%1%’") % i.attrPath); | ||||
|             Activity act(*logger, lvlDebug, format("outputting query result ‘%1%’") % i.attrPath); | ||||
| 
 | ||||
|             if (globals.prebuiltOnly && | ||||
|                 validPaths.find(i.queryOutPath()) == validPaths.end() && | ||||
|  |  | |||
|  | @ -19,7 +19,7 @@ using namespace nix; | |||
| 
 | ||||
| static Expr * parseStdin(EvalState & state) | ||||
| { | ||||
|     startNest(nest, lvlTalkative, format("parsing standard input")); | ||||
|     Activity act(*logger, lvlTalkative, format("parsing standard input")); | ||||
|     return state.parseExprFromString(drainFD(0), absPath(".")); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,4 @@ | |||
| #include "command.hh" | ||||
| #include "progress-bar.hh" | ||||
| #include "shared.hh" | ||||
| #include "store-api.hh" | ||||
| #include "sync.hh" | ||||
|  | @ -47,16 +46,9 @@ struct CmdCopy : StorePathsCommand | |||
|         ref<Store> srcStore = srcUri.empty() ? store : openStoreAt(srcUri); | ||||
|         ref<Store> dstStore = dstUri.empty() ? store : openStoreAt(dstUri); | ||||
| 
 | ||||
|         ProgressBar progressBar; | ||||
|         std::string copiedLabel = "copied"; | ||||
| 
 | ||||
|         std::atomic<size_t> done{0}; | ||||
|         std::atomic<size_t> total{storePaths.size()}; | ||||
| 
 | ||||
|         auto showProgress = [&]() { | ||||
|             return (format("[%d/%d copied]") % done % total).str(); | ||||
|         }; | ||||
| 
 | ||||
|         progressBar.updateStatus(showProgress()); | ||||
|         logger->setExpected(copiedLabel, storePaths.size()); | ||||
| 
 | ||||
|         ThreadPool pool; | ||||
| 
 | ||||
|  | @ -71,7 +63,7 @@ struct CmdCopy : StorePathsCommand | |||
|                 checkInterrupt(); | ||||
| 
 | ||||
|                 if (!dstStore->isValidPath(storePath)) { | ||||
|                     auto activity(progressBar.startActivity(format("copying ‘%s’...") % storePath)); | ||||
|                     Activity act(*logger, lvlInfo, format("copying ‘%s’...") % storePath); | ||||
| 
 | ||||
|                     StringSink sink; | ||||
|                     srcStore->exportPaths({storePath}, false, sink); | ||||
|  | @ -79,16 +71,12 @@ struct CmdCopy : StorePathsCommand | |||
|                     StringSource source(*sink.s); | ||||
|                     dstStore->importPaths(false, source, 0); | ||||
| 
 | ||||
|                     done++; | ||||
|                     logger->incProgress(copiedLabel); | ||||
|                 } else | ||||
|                     total--; | ||||
| 
 | ||||
|                 progressBar.updateStatus(showProgress()); | ||||
|                     logger->incExpected(copiedLabel, -1); | ||||
|             }); | ||||
| 
 | ||||
|         pool.process(); | ||||
| 
 | ||||
|         progressBar.done(); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ | |||
| #include "legacy.hh" | ||||
| #include "shared.hh" | ||||
| #include "store-api.hh" | ||||
| #include "progress-bar.hh" | ||||
| 
 | ||||
| namespace nix { | ||||
| 
 | ||||
|  | @ -42,6 +43,8 @@ void mainWrapped(int argc, char * * argv) | |||
| 
 | ||||
|     assert(args.command); | ||||
| 
 | ||||
|     StartProgressBar bar; | ||||
| 
 | ||||
|     args.command->prepare(); | ||||
|     args.command->run(); | ||||
| } | ||||
|  |  | |||
|  | @ -1,5 +1,4 @@ | |||
| #include "command.hh" | ||||
| #include "progress-bar.hh" | ||||
| #include "shared.hh" | ||||
| #include "store-api.hh" | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,72 +1,157 @@ | |||
| #include "progress-bar.hh" | ||||
| #include "util.hh" | ||||
| #include "sync.hh" | ||||
| 
 | ||||
| #include <iostream> | ||||
| #include <map> | ||||
| 
 | ||||
| namespace nix { | ||||
| 
 | ||||
| ProgressBar::ProgressBar() | ||||
| class ProgressBar : public Logger | ||||
| { | ||||
|     _writeToStderr = [&](const unsigned char * buf, size_t count) { | ||||
|         auto state_(state.lock()); | ||||
|         assert(!state_->done); | ||||
|         std::cerr << "\r\e[K" << std::string((const char *) buf, count); | ||||
|         render(*state_); | ||||
| private: | ||||
| 
 | ||||
|     struct ActInfo | ||||
|     { | ||||
|         Activity * activity; | ||||
|         Verbosity lvl; | ||||
|         std::string s; | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| ProgressBar::~ProgressBar() | ||||
| { | ||||
|     done(); | ||||
| } | ||||
|     struct Progress | ||||
|     { | ||||
|         uint64_t expected = 0, progress = 0; | ||||
|     }; | ||||
| 
 | ||||
| void ProgressBar::updateStatus(const std::string & s) | ||||
| { | ||||
|     auto state_(state.lock()); | ||||
|     assert(!state_->done); | ||||
|     state_->status = s; | ||||
|     render(*state_); | ||||
| } | ||||
|     struct State | ||||
|     { | ||||
|         std::list<ActInfo> activities; | ||||
|         std::map<Activity *, std::list<ActInfo>::iterator> its; | ||||
|         std::map<std::string, Progress> progress; | ||||
|     }; | ||||
| 
 | ||||
| void ProgressBar::done() | ||||
| { | ||||
|     _writeToStderr = decltype(_writeToStderr)(); | ||||
|     auto state_(state.lock()); | ||||
|     assert(state_->activities.empty()); | ||||
|     state_->done = true; | ||||
|     std::cerr << "\r\e[K"; | ||||
|     std::cerr.flush(); | ||||
| } | ||||
|     Sync<State> state_; | ||||
| 
 | ||||
| void ProgressBar::render(State & state_) | ||||
| { | ||||
|     std::cerr << '\r' << state_.status; | ||||
|     if (!state_.activities.empty()) { | ||||
|         if (!state_.status.empty()) std::cerr << ' '; | ||||
|         std::cerr << *state_.activities.rbegin(); | ||||
| public: | ||||
| 
 | ||||
|     ~ProgressBar() | ||||
|     { | ||||
|         auto state(state_.lock()); | ||||
|         assert(state->activities.empty()); | ||||
|         writeToStderr("\r\e[K"); | ||||
|     } | ||||
|     std::cerr << "\e[K"; | ||||
|     std::cerr.flush(); | ||||
| } | ||||
| 
 | ||||
|     void log(Verbosity lvl, const FormatOrString & fs) override | ||||
|     { | ||||
|         auto state(state_.lock()); | ||||
|         log(*state, lvl, fs.s); | ||||
|     } | ||||
| 
 | ||||
| ProgressBar::Activity ProgressBar::startActivity(const FormatOrString & fs) | ||||
|     void log(State & state, Verbosity lvl, const std::string & s) | ||||
|     { | ||||
|         writeToStderr("\r\e[K" + s + "\n"); | ||||
|         update(state); | ||||
|     } | ||||
| 
 | ||||
|     void startActivity(Activity & activity, Verbosity lvl, const FormatOrString & fs) override | ||||
|     { | ||||
|         if (lvl > verbosity) return; | ||||
|         auto state(state_.lock()); | ||||
|         state->activities.emplace_back(ActInfo{&activity, lvl, fs.s}); | ||||
|         state->its.emplace(&activity, std::prev(state->activities.end())); | ||||
|         update(*state); | ||||
|     } | ||||
| 
 | ||||
|     void stopActivity(Activity & activity) override | ||||
|     { | ||||
|         auto state(state_.lock()); | ||||
|         auto i = state->its.find(&activity); | ||||
|         if (i == state->its.end()) return; | ||||
|         state->activities.erase(i->second); | ||||
|         state->its.erase(i); | ||||
|         update(*state); | ||||
|     } | ||||
| 
 | ||||
|     void setExpected(const std::string & label, uint64_t value) override | ||||
|     { | ||||
|         auto state(state_.lock()); | ||||
|         state->progress[label].expected = value; | ||||
|     } | ||||
| 
 | ||||
|     void setProgress(const std::string & label, uint64_t value) override | ||||
|     { | ||||
|         auto state(state_.lock()); | ||||
|         state->progress[label].progress = value; | ||||
|     } | ||||
| 
 | ||||
|     void incExpected(const std::string & label, uint64_t value) override | ||||
|     { | ||||
|         auto state(state_.lock()); | ||||
|         state->progress[label].expected += value; | ||||
|     } | ||||
| 
 | ||||
|     void incProgress(const std::string & label, uint64_t value) | ||||
|     { | ||||
|         auto state(state_.lock()); | ||||
|         state->progress[label].progress += value; | ||||
|     } | ||||
| 
 | ||||
|     void update() | ||||
|     { | ||||
|         auto state(state_.lock()); | ||||
|     } | ||||
| 
 | ||||
|     void update(State & state) | ||||
|     { | ||||
|         std::string line = "\r"; | ||||
| 
 | ||||
|         std::string status = getStatus(state); | ||||
|         if (!status.empty()) { | ||||
|             line += '['; | ||||
|             line += status; | ||||
|             line += "]"; | ||||
|         } | ||||
| 
 | ||||
|         if (!state.activities.empty()) { | ||||
|             if (!status.empty()) line += " "; | ||||
|             line += state.activities.rbegin()->s; | ||||
|         } | ||||
| 
 | ||||
|         line += "\e[K"; | ||||
|         writeToStderr(line); | ||||
|     } | ||||
| 
 | ||||
|     std::string getStatus(State & state) | ||||
|     { | ||||
|         std::string res; | ||||
|         for (auto & p : state.progress) | ||||
|             if (p.second.expected || p.second.progress) { | ||||
|                 if (!res.empty()) res += ", "; | ||||
|                 res += std::to_string(p.second.progress); | ||||
|                 if (p.second.expected) { | ||||
|                     res += "/"; | ||||
|                     res += std::to_string(p.second.expected); | ||||
|                 } | ||||
|                 res += " "; res += p.first; | ||||
|             } | ||||
|         return res; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| StartProgressBar::StartProgressBar() | ||||
| { | ||||
|     return Activity(*this, fs); | ||||
|     if (isatty(STDERR_FILENO)) { | ||||
|         prev = logger; | ||||
|         logger = new ProgressBar(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| ProgressBar::Activity::Activity(ProgressBar & pb, const FormatOrString & fs) | ||||
|     : pb(pb) | ||||
| StartProgressBar::~StartProgressBar() | ||||
| { | ||||
|     auto state_(pb.state.lock()); | ||||
|     state_->activities.push_back(fs.s); | ||||
|     it = state_->activities.end(); --it; | ||||
|     pb.render(*state_); | ||||
| } | ||||
| 
 | ||||
| ProgressBar::Activity::~Activity() | ||||
| { | ||||
|     auto state_(pb.state.lock()); | ||||
|     state_->activities.erase(it); | ||||
|     if (prev) { | ||||
|         auto bar = logger; | ||||
|         logger = prev; | ||||
|         delete bar; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -1,49 +1,15 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "sync.hh" | ||||
| #include "util.hh" | ||||
| #include "logging.hh" | ||||
| 
 | ||||
| namespace nix { | ||||
| 
 | ||||
| class ProgressBar | ||||
| class StartProgressBar | ||||
| { | ||||
| private: | ||||
|     struct State | ||||
|     { | ||||
|         std::string status; | ||||
|         bool done = false; | ||||
|         std::list<std::string> activities; | ||||
|     }; | ||||
| 
 | ||||
|     Sync<State> state; | ||||
| 
 | ||||
|     Logger * prev = 0; | ||||
| public: | ||||
| 
 | ||||
|     ProgressBar(); | ||||
| 
 | ||||
|     ~ProgressBar(); | ||||
| 
 | ||||
|     void updateStatus(const std::string & s); | ||||
| 
 | ||||
|     void done(); | ||||
| 
 | ||||
|     class Activity | ||||
|     { | ||||
|         friend class ProgressBar; | ||||
|     private: | ||||
|         ProgressBar & pb; | ||||
|         std::list<std::string>::iterator it; | ||||
|         Activity(ProgressBar & pb, const FormatOrString & fs); | ||||
|     public: | ||||
|         ~Activity(); | ||||
|     }; | ||||
| 
 | ||||
|     Activity startActivity(const FormatOrString & fs); | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
|     void render(State & state_); | ||||
| 
 | ||||
|     StartProgressBar(); | ||||
|     ~StartProgressBar(); | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -1,5 +1,4 @@ | |||
| #include "command.hh" | ||||
| #include "progress-bar.hh" | ||||
| #include "shared.hh" | ||||
| #include "store-api.hh" | ||||
| #include "thread-pool.hh" | ||||
|  | @ -38,21 +37,15 @@ struct CmdCopySigs : StorePathsCommand | |||
|         for (auto & s : substituterUris) | ||||
|             substituters.push_back(openStoreAt(s)); | ||||
| 
 | ||||
|         ProgressBar progressBar; | ||||
| 
 | ||||
|         ThreadPool pool; | ||||
| 
 | ||||
|         std::atomic<size_t> done{0}; | ||||
|         std::string doneLabel = "done"; | ||||
|         std::atomic<size_t> added{0}; | ||||
| 
 | ||||
|         auto showProgress = [&]() { | ||||
|             return (format("[%d/%d done]") % done % storePaths.size()).str(); | ||||
|         }; | ||||
| 
 | ||||
|         progressBar.updateStatus(showProgress()); | ||||
|         logger->setExpected(doneLabel, storePaths.size()); | ||||
| 
 | ||||
|         auto doPath = [&](const Path & storePath) { | ||||
|             auto activity(progressBar.startActivity(format("getting signatures for ‘%s’") % storePath)); | ||||
|             Activity act(*logger, lvlInfo, format("getting signatures for ‘%s’") % storePath); | ||||
| 
 | ||||
|             checkInterrupt(); | ||||
| 
 | ||||
|  | @ -83,8 +76,7 @@ struct CmdCopySigs : StorePathsCommand | |||
|                 added += newSigs.size(); | ||||
|             } | ||||
| 
 | ||||
|             done++; | ||||
|             progressBar.updateStatus(showProgress()); | ||||
|             logger->incProgress(doneLabel); | ||||
|         }; | ||||
| 
 | ||||
|         for (auto & storePath : storePaths) | ||||
|  | @ -92,8 +84,6 @@ struct CmdCopySigs : StorePathsCommand | |||
| 
 | ||||
|         pool.process(); | ||||
| 
 | ||||
|         progressBar.done(); | ||||
| 
 | ||||
|         printMsg(lvlInfo, format("imported %d signatures") % added); | ||||
|     } | ||||
| }; | ||||
|  |  | |||
|  | @ -1,5 +1,4 @@ | |||
| #include "command.hh" | ||||
| #include "progress-bar.hh" | ||||
| #include "shared.hh" | ||||
| #include "store-api.hh" | ||||
| #include "sync.hh" | ||||
|  | @ -57,30 +56,16 @@ struct CmdVerify : StorePathsCommand | |||
| 
 | ||||
|         auto publicKeys = getDefaultPublicKeys(); | ||||
| 
 | ||||
|         std::atomic<size_t> done{0}; | ||||
|         std::atomic<size_t> untrusted{0}; | ||||
|         std::atomic<size_t> corrupted{0}; | ||||
|         std::atomic<size_t> done{0}; | ||||
|         std::atomic<size_t> failed{0}; | ||||
| 
 | ||||
|         ProgressBar progressBar; | ||||
| 
 | ||||
|         auto showProgress = [&](bool final) { | ||||
|             std::string s; | ||||
|             if (final) | ||||
|                 s = (format("checked %d paths") % storePaths.size()).str(); | ||||
|             else | ||||
|                 s = (format("[%d/%d checked") % done % storePaths.size()).str(); | ||||
|             if (corrupted > 0) | ||||
|                 s += (format(", %d corrupted") % corrupted).str(); | ||||
|             if (untrusted > 0) | ||||
|                 s += (format(", %d untrusted") % untrusted).str(); | ||||
|             if (failed > 0) | ||||
|                 s += (format(", %d failed") % failed).str(); | ||||
|             if (!final) s += "]"; | ||||
|             return s; | ||||
|         }; | ||||
| 
 | ||||
|         progressBar.updateStatus(showProgress(false)); | ||||
|         std::string doneLabel("paths checked"); | ||||
|         std::string untrustedLabel("untrusted"); | ||||
|         std::string corruptedLabel("corrupted"); | ||||
|         std::string failedLabel("failed"); | ||||
|         logger->setExpected(doneLabel, storePaths.size()); | ||||
| 
 | ||||
|         ThreadPool pool; | ||||
| 
 | ||||
|  | @ -88,7 +73,7 @@ struct CmdVerify : StorePathsCommand | |||
|             try { | ||||
|                 checkInterrupt(); | ||||
| 
 | ||||
|                 auto activity(progressBar.startActivity(format("checking ‘%s’") % storePath)); | ||||
|                 Activity act(*logger, lvlInfo, format("checking ‘%s’") % storePath); | ||||
| 
 | ||||
|                 auto info = store->queryPathInfo(storePath); | ||||
| 
 | ||||
|  | @ -100,6 +85,7 @@ struct CmdVerify : StorePathsCommand | |||
|                     auto hash = sink.finish(); | ||||
| 
 | ||||
|                     if (hash.first != info->narHash) { | ||||
|                         logger->incProgress(corruptedLabel); | ||||
|                         corrupted = 1; | ||||
|                         printMsg(lvlError, | ||||
|                             format("path ‘%s’ was modified! expected hash ‘%s’, got ‘%s’") | ||||
|  | @ -147,18 +133,19 @@ struct CmdVerify : StorePathsCommand | |||
|                     } | ||||
| 
 | ||||
|                     if (!good) { | ||||
|                         logger->incProgress(untrustedLabel); | ||||
|                         untrusted++; | ||||
|                         printMsg(lvlError, format("path ‘%s’ is untrusted") % info->path); | ||||
|                     } | ||||
| 
 | ||||
|                 } | ||||
| 
 | ||||
|                 logger->incProgress(doneLabel); | ||||
|                 done++; | ||||
| 
 | ||||
|                 progressBar.updateStatus(showProgress(false)); | ||||
| 
 | ||||
|             } catch (Error & e) { | ||||
|                 printMsg(lvlError, format(ANSI_RED "error:" ANSI_NORMAL " %s") % e.what()); | ||||
|                 logger->incProgress(failedLabel); | ||||
|                 failed++; | ||||
|             } | ||||
|         }; | ||||
|  | @ -168,9 +155,8 @@ struct CmdVerify : StorePathsCommand | |||
| 
 | ||||
|         pool.process(); | ||||
| 
 | ||||
|         progressBar.done(); | ||||
| 
 | ||||
|         printMsg(lvlInfo, showProgress(true)); | ||||
|         printMsg(lvlInfo, format("%d paths checked, %d untrusted, %d corrupted, %d failed") | ||||
|             % done % untrusted % corrupted % failed); | ||||
| 
 | ||||
|         throw Exit( | ||||
|             (corrupted ? 1 : 0) | | ||||
|  |  | |||
|  | @ -2,16 +2,7 @@ source common.sh | |||
| 
 | ||||
| clearStore | ||||
| 
 | ||||
| # Produce an escaped log file. | ||||
| path=$(nix-build --log-type escapes -vv dependencies.nix --no-out-link 2> $TEST_ROOT/log.esc) | ||||
| 
 | ||||
| # Convert it to an XML representation. | ||||
| nix-log2xml < $TEST_ROOT/log.esc > $TEST_ROOT/log.xml | ||||
| 
 | ||||
| # Is this well-formed XML? | ||||
| if test "$xmllint" != "false"; then | ||||
|     $xmllint --noout $TEST_ROOT/log.xml || fail "malformed XML" | ||||
| fi | ||||
| path=$(nix-build dependencies.nix --no-out-link) | ||||
| 
 | ||||
| # Test nix-store -l. | ||||
| [ "$(nix-store -l $path)" = FOO ] | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue