diff --git a/src/libstore/build.cc b/src/libstore/build.cc index 7b881941e..ac8572246 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -3768,7 +3768,7 @@ void LocalStore::buildPaths(const PathSet & drvPaths, BuildMode buildMode) } if (!failed.empty()) - throw Error(format("build of %1% failed") % showPaths(failed), worker.exitStatus()); + throw Error(worker.exitStatus(), "build of %s failed",showPaths(failed)); } @@ -3804,7 +3804,7 @@ void LocalStore::ensurePath(const Path & path) worker.run(goals); if (goal->getExitCode() != Goal::ecSuccess) - throw Error(format("path ‘%1%’ does not exist and cannot be created") % path, worker.exitStatus()); + throw Error(worker.exitStatus(), "path ‘%s’ does not exist and cannot be created", path); } @@ -3825,7 +3825,7 @@ void LocalStore::repairPath(const Path & path) goals.insert(worker.makeDerivationGoal(deriver, StringSet(), bmRepair)); worker.run(goals); } else - throw Error(format("cannot repair path ‘%1%’") % path, worker.exitStatus()); + throw Error(worker.exitStatus(), "cannot repair path ‘%s’", path); } } diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 7b73557a5..5c9262dec 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -564,7 +564,7 @@ void RemoteStore::Connection::processStderr(Sink * sink, Source * source) if (msg == STDERR_ERROR) { string error = readString(from); unsigned int status = readInt(from); - throw Error(format("%1%") % error, status); + throw Error(status, error); } else if (msg != STDERR_LAST) throw Error("protocol error processing standard error"); diff --git a/src/libutil/args.hh b/src/libutil/args.hh index 6aa08aaca..ac12f8be6 100644 --- a/src/libutil/args.hh +++ b/src/libutil/args.hh @@ -8,7 +8,7 @@ namespace nix { -MakeError(UsageError, nix::Error); +MakeError(UsageError, Error); enum HashType : char; diff --git a/src/libutil/logging.hh b/src/libutil/logging.hh index 277dff280..ba99a81c3 100644 --- a/src/libutil/logging.hh +++ b/src/libutil/logging.hh @@ -66,14 +66,19 @@ Logger * makeDefaultLogger(); extern Verbosity verbosity; /* suppress msgs > this */ -#define printMsg(level, f) \ +/* Print a message if the current log level is at least the specified + level. Note that this has to be implemented as a macro to ensure + that the arguments are evaluated lazily. */ +#define printMsg(level, args...) \ do { \ if (level <= nix::verbosity) { \ - logger->log(level, (f)); \ + logger->log(level, fmt(args)); \ } \ } while (0) -#define debug(f) printMsg(lvlDebug, f) +#define printError(args...) printMsg(lvlError, args) +#define printInfo(args...) printMsg(lvlInfo, args) +#define debug(args...) printMsg(lvlDebug, args) void warnOnce(bool & haveWarned, const FormatOrString & fs); diff --git a/src/libutil/types.hh b/src/libutil/types.hh index bd192b850..b9a93d27d 100644 --- a/src/libutil/types.hh +++ b/src/libutil/types.hh @@ -41,6 +41,45 @@ struct FormatOrString }; +/* A helper for formatting strings. ‘fmt(format, a_0, ..., a_n)’ is + equivalent to ‘boost::format(format) % a_0 % ... % + ... a_n’. However, ‘fmt(s)’ is equivalent to ‘s’ (so no %-expansion + takes place). */ + +inline void formatHelper(boost::format & f) +{ +} + +template +inline void formatHelper(boost::format & f, T x, Args... args) +{ + formatHelper(f % x, args...); +} + +inline std::string fmt(const std::string & s) +{ + return s; +} + +inline std::string fmt(const char * s) +{ + return s; +} + +inline std::string fmt(const FormatOrString & fs) +{ + return fs.s; +} + +template +inline std::string fmt(const std::string & fs, Args... args) +{ + boost::format f(fs); + formatHelper(f, args...); + return f.str(); +} + + /* BaseError should generally not be caught, as it has Interrupted as a subclass. Catch Error instead. */ class BaseError : public std::exception @@ -49,14 +88,28 @@ protected: string prefix_; // used for location traces etc. string err; public: - unsigned int status; // exit status - BaseError(const FormatOrString & fs, unsigned int status = 1); + unsigned int status = 1; // exit status + + template + BaseError(unsigned int status, Args... args) + : err(fmt(args...)) + , status(status) + { + } + + template + BaseError(Args... args) + : err(fmt(args...)) + { + } + #ifdef EXCEPTION_NEEDS_THROW_SPEC ~BaseError() throw () { }; const char * what() const throw () { return err.c_str(); } #else const char * what() const noexcept { return err.c_str(); } #endif + const string & msg() const { return err; } const string & prefix() const { return prefix_; } BaseError & addPrefix(const FormatOrString & fs); @@ -66,7 +119,7 @@ public: class newClass : public superClass \ { \ public: \ - newClass(const FormatOrString & fs, unsigned int status = 1) : superClass(fs, status) { }; \ + using superClass::superClass; \ }; MakeError(Error, BaseError) @@ -75,7 +128,15 @@ class SysError : public Error { public: int errNo; - SysError(const FormatOrString & fs); + + template + SysError(Args... args) + : Error(addErrno(fmt(args...))) + { } + +private: + + std::string addErrno(const std::string & s); }; diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 68311a3df..ce54350d7 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -31,13 +31,6 @@ extern char * * environ; namespace nix { -BaseError::BaseError(const FormatOrString & fs, unsigned int status) - : status(status) -{ - err = fs.s; -} - - BaseError & BaseError::addPrefix(const FormatOrString & fs) { prefix_ = fs.s + prefix_; @@ -45,10 +38,10 @@ BaseError & BaseError::addPrefix(const FormatOrString & fs) } -SysError::SysError(const FormatOrString & fs) - : Error(format("%1%: %2%") % fs.s % strerror(errno)) - , errNo(errno) +std::string SysError::addErrno(const std::string & s) { + errNo = errno; + return s + ": " + strerror(errNo); }