aboutsummaryrefslogtreecommitdiff
path: root/src/libutil
diff options
context:
space:
mode:
authorJohn Ericson <John.Ericson@Obsidian.Systems>2020-06-18 21:58:27 +0000
committerJohn Ericson <John.Ericson@Obsidian.Systems>2020-06-18 22:11:19 +0000
commitbbbf3602a323538b8da38f1a2c7ce136a20f74c6 (patch)
treee783d838d8a75d854e0b72eea03784c5909040bc /src/libutil
parent406dbb7fce32f7d80b02f560d91c956698b58d6e (diff)
parent40526fbea56a8006eb7f1758d461a5acbe9a1694 (diff)
Merge branch 'enum-class' into no-hash-type-unknown
Diffstat (limited to 'src/libutil')
-rw-r--r--src/libutil/affinity.cc19
-rw-r--r--src/libutil/ansicolor.hh2
-rw-r--r--src/libutil/archive.cc19
-rw-r--r--src/libutil/args.cc15
-rw-r--r--src/libutil/args.hh2
-rw-r--r--src/libutil/compression.cc2
-rw-r--r--src/libutil/config.hh1
-rw-r--r--src/libutil/error.cc257
-rw-r--r--src/libutil/error.hh225
-rw-r--r--src/libutil/fmt.hh139
-rw-r--r--src/libutil/hash.cc20
-rw-r--r--src/libutil/hash.hh12
-rw-r--r--src/libutil/json.cc2
-rw-r--r--src/libutil/local.mk2
-rw-r--r--src/libutil/logging.cc64
-rw-r--r--src/libutil/logging.hh52
-rw-r--r--src/libutil/lru-cache.hh1
-rw-r--r--src/libutil/rust-ffi.cc2
-rw-r--r--src/libutil/rust-ffi.hh2
-rw-r--r--src/libutil/serialise.cc5
-rw-r--r--src/libutil/tests/hash.cc16
-rw-r--r--src/libutil/tests/local.mk2
-rw-r--r--src/libutil/tests/logging.cc255
-rw-r--r--src/libutil/tests/pool.cc127
-rw-r--r--src/libutil/types.hh144
-rw-r--r--src/libutil/url.hh2
-rw-r--r--src/libutil/util.cc104
-rw-r--r--src/libutil/util.hh12
28 files changed, 1057 insertions, 448 deletions
diff --git a/src/libutil/affinity.cc b/src/libutil/affinity.cc
index 98f8287ad..ac2295e4a 100644
--- a/src/libutil/affinity.cc
+++ b/src/libutil/affinity.cc
@@ -12,6 +12,17 @@ namespace nix {
#if __linux__
static bool didSaveAffinity = false;
static cpu_set_t savedAffinity;
+
+std::ostream& operator<<(std::ostream &os, const cpu_set_t &cset)
+{
+ auto count = CPU_COUNT(&cset);
+ for (int i=0; i < count; ++i)
+ {
+ os << (CPU_ISSET(i,&cset) ? "1" : "0");
+ }
+
+ return os;
+}
#endif
@@ -25,7 +36,7 @@ void setAffinityTo(int cpu)
CPU_ZERO(&newAffinity);
CPU_SET(cpu, &newAffinity);
if (sched_setaffinity(0, sizeof(cpu_set_t), &newAffinity) == -1)
- printError(format("failed to lock thread to CPU %1%") % cpu);
+ printError("failed to lock thread to CPU %1%", cpu);
#endif
}
@@ -47,7 +58,11 @@ void restoreAffinity()
#if __linux__
if (!didSaveAffinity) return;
if (sched_setaffinity(0, sizeof(cpu_set_t), &savedAffinity) == -1)
- printError("failed to restore affinity %1%");
+ {
+ std::ostringstream oss;
+ oss << savedAffinity;
+ printError("failed to restore CPU affinity %1%", oss.str());
+ }
#endif
}
diff --git a/src/libutil/ansicolor.hh b/src/libutil/ansicolor.hh
index 8ae07b092..a38c2d798 100644
--- a/src/libutil/ansicolor.hh
+++ b/src/libutil/ansicolor.hh
@@ -11,5 +11,7 @@ namespace nix {
#define ANSI_GREEN "\e[32;1m"
#define ANSI_YELLOW "\e[33;1m"
#define ANSI_BLUE "\e[34;1m"
+#define ANSI_MAGENTA "\e[35m;1m"
+#define ANSI_CYAN "\e[36m;1m"
}
diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc
index db544a212..6a8484705 100644
--- a/src/libutil/archive.cc
+++ b/src/libutil/archive.cc
@@ -46,7 +46,7 @@ static void dumpContents(const Path & path, size_t size,
sink << "contents" << size;
AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
- if (!fd) throw SysError(format("opening file '%1%'") % path);
+ if (!fd) throw SysError("opening file '%1%'", path);
std::vector<unsigned char> buf(65536);
size_t left = size;
@@ -68,7 +68,7 @@ static void dump(const Path & path, Sink & sink, PathFilter & filter)
struct stat st;
if (lstat(path.c_str(), &st))
- throw SysError(format("getting attributes of path '%1%'") % path);
+ throw SysError("getting attributes of path '%1%'", path);
sink << "(";
@@ -94,8 +94,9 @@ static void dump(const Path & path, Sink & sink, PathFilter & filter)
name.erase(pos);
}
if (unhacked.find(name) != unhacked.end())
- throw Error(format("file name collision in between '%1%' and '%2%'")
- % (path + "/" + unhacked[name]) % (path + "/" + i.name));
+ throw Error("file name collision in between '%1%' and '%2%'",
+ (path + "/" + unhacked[name]),
+ (path + "/" + i.name));
unhacked[name] = i.name;
} else
unhacked[i.name] = i.name;
@@ -111,7 +112,7 @@ static void dump(const Path & path, Sink & sink, PathFilter & filter)
else if (S_ISLNK(st.st_mode))
sink << "type" << "symlink" << "target" << readLink(path);
- else throw Error(format("file '%1%' has an unsupported type") % path);
+ else throw Error("file '%1%' has an unsupported type", path);
sink << ")";
}
@@ -247,7 +248,7 @@ static void parse(ParseSink & sink, Source & source, const Path & path)
} else if (s == "name") {
name = readString(source);
if (name.empty() || name == "." || name == ".." || name.find('/') != string::npos || name.find((char) 0) != string::npos)
- throw Error(format("NAR contains invalid file name '%1%'") % name);
+ throw Error("NAR contains invalid file name '%1%'", name);
if (name <= prevName)
throw Error("NAR directory is not sorted");
prevName = name;
@@ -303,14 +304,14 @@ struct RestoreSink : ParseSink
{
Path p = dstPath + path;
if (mkdir(p.c_str(), 0777) == -1)
- throw SysError(format("creating directory '%1%'") % p);
+ throw SysError("creating directory '%1%'", p);
};
void createRegularFile(const Path & path)
{
Path p = dstPath + path;
fd = open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666);
- if (!fd) throw SysError(format("creating file '%1%'") % p);
+ if (!fd) throw SysError("creating file '%1%'", p);
}
void isExecutable()
@@ -332,7 +333,7 @@ struct RestoreSink : ParseSink
OpenSolaris). Since preallocation is just an
optimisation, ignore it. */
if (errno && errno != EINVAL && errno != EOPNOTSUPP && errno != ENOSYS)
- throw SysError(format("preallocating file of %1% bytes") % len);
+ throw SysError("preallocating file of %1% bytes", len);
}
#endif
}
diff --git a/src/libutil/args.cc b/src/libutil/args.cc
index 4fe9539e4..1f0e4f803 100644
--- a/src/libutil/args.cc
+++ b/src/libutil/args.cc
@@ -45,7 +45,7 @@ void Args::parseCmdline(const Strings & _cmdline)
}
else if (!dashDash && std::string(arg, 0, 1) == "-") {
if (!processFlag(pos, cmdline.end()))
- throw UsageError(format("unrecognised flag '%1%'") % arg);
+ throw UsageError("unrecognised flag '%1%'", arg);
}
else {
pendingArgs.push_back(*pos++);
@@ -130,7 +130,7 @@ bool Args::processArgs(const Strings & args, bool finish)
{
if (expectedArgs.empty()) {
if (!args.empty())
- throw UsageError(format("unexpected argument '%1%'") % args.front());
+ throw UsageError("unexpected argument '%1%'", args.front());
return true;
}
@@ -227,10 +227,15 @@ MultiCommand::MultiCommand(const Commands & commands)
{
expectedArgs.push_back(ExpectedArg{"command", 1, true, [=](std::vector<std::string> ss) {
assert(!command);
- auto i = commands.find(ss[0]);
+ auto cmd = ss[0];
+ if (auto alias = get(deprecatedAliases, cmd)) {
+ warn("'%s' is a deprecated alias for '%s'", cmd, *alias);
+ cmd = *alias;
+ }
+ auto i = commands.find(cmd);
if (i == commands.end())
- throw UsageError("'%s' is not a recognised command", ss[0]);
- command = {ss[0], i->second()};
+ throw UsageError("'%s' is not a recognised command", cmd);
+ command = {cmd, i->second()};
}});
categories[Command::catDefault] = "Available commands";
diff --git a/src/libutil/args.hh b/src/libutil/args.hh
index f2315f67a..73991881d 100644
--- a/src/libutil/args.hh
+++ b/src/libutil/args.hh
@@ -235,6 +235,8 @@ public:
std::map<Command::Category, std::string> categories;
+ std::map<std::string, std::string> deprecatedAliases;
+
// Selected command, if any.
std::optional<std::pair<std::string, ref<Command>>> command;
diff --git a/src/libutil/compression.cc b/src/libutil/compression.cc
index 75e889f41..9f270c8f3 100644
--- a/src/libutil/compression.cc
+++ b/src/libutil/compression.cc
@@ -481,7 +481,7 @@ ref<CompressionSink> makeCompressionSink(const std::string & method, Sink & next
else if (method == "br")
return make_ref<BrotliCompressionSink>(nextSink);
else
- throw UnknownCompressionMethod(format("unknown compression method '%s'") % method);
+ throw UnknownCompressionMethod("unknown compression method '%s'", method);
}
ref<std::string> compress(const std::string & method, const std::string & in, const bool parallel)
diff --git a/src/libutil/config.hh b/src/libutil/config.hh
index 5c7a70a2e..66073546e 100644
--- a/src/libutil/config.hh
+++ b/src/libutil/config.hh
@@ -1,3 +1,4 @@
+#include <cassert>
#include <map>
#include <set>
diff --git a/src/libutil/error.cc b/src/libutil/error.cc
index a5571d4ec..5c2f9ed3f 100644
--- a/src/libutil/error.cc
+++ b/src/libutil/error.cc
@@ -2,9 +2,38 @@
#include <iostream>
#include <optional>
+#include "serialise.hh"
+#include <sstream>
-namespace nix
+namespace nix {
+
+
+const std::string nativeSystem = SYSTEM;
+
+// addPrefix is used for show-trace. Strings added with addPrefix
+// will print ahead of the error itself.
+BaseError & BaseError::addPrefix(const FormatOrString & fs)
+{
+ prefix_ = fs.s + prefix_;
+ return *this;
+}
+
+// c++ std::exception descendants must have a 'const char* what()' function.
+// This stringifies the error and caches it for use by what(), or similarly by msg().
+const string& BaseError::calcWhat() const
{
+ if (what_.has_value())
+ return *what_;
+ else {
+ err.name = sname();
+
+ std::ostringstream oss;
+ oss << err;
+ what_ = oss.str();
+
+ return *what_;
+ }
+}
std::optional<string> ErrorInfo::programName = std::nullopt;
@@ -15,132 +44,178 @@ std::ostream& operator<<(std::ostream &os, const hintformat &hf)
string showErrPos(const ErrPos &errPos)
{
- if (errPos.column > 0) {
- return fmt("(%1%:%2%)", errPos.lineNumber, errPos.column);
- } else {
- return fmt("(%1%)", errPos.lineNumber);
- };
+ if (errPos.line > 0) {
+ if (errPos.column > 0) {
+ return fmt("(%1%:%2%)", errPos.line, errPos.column);
+ } else {
+ return fmt("(%1%)", errPos.line);
+ }
+ }
+ else {
+ return "";
+ }
}
-void printCodeLines(const string &prefix, const NixCode &nixCode)
+// if nixCode contains lines of code, print them to the ostream, indicating the error column.
+void printCodeLines(std::ostream &out, const string &prefix, const NixCode &nixCode)
{
// previous line of code.
if (nixCode.prevLineOfCode.has_value()) {
- std::cout << fmt("%1% %|2$5d|| %3%",
- prefix,
- (nixCode.errPos.lineNumber - 1),
- *nixCode.prevLineOfCode)
- << std::endl;
+ out << std::endl
+ << fmt("%1% %|2$5d|| %3%",
+ prefix,
+ (nixCode.errPos.line - 1),
+ *nixCode.prevLineOfCode);
}
- // line of code containing the error.%2$+5d%
- std::cout << fmt("%1% %|2$5d|| %3%",
- prefix,
- (nixCode.errPos.lineNumber),
- nixCode.errLineOfCode)
- << std::endl;
-
- // error arrows for the column range.
- if (nixCode.errPos.column > 0) {
- int start = nixCode.errPos.column;
- std::string spaces;
- for (int i = 0; i < start; ++i) {
- spaces.append(" ");
+ if (nixCode.errLineOfCode.has_value()) {
+ // line of code containing the error.
+ out << std::endl
+ << fmt("%1% %|2$5d|| %3%",
+ prefix,
+ (nixCode.errPos.line),
+ *nixCode.errLineOfCode);
+ // error arrows for the column range.
+ if (nixCode.errPos.column > 0) {
+ int start = nixCode.errPos.column;
+ std::string spaces;
+ for (int i = 0; i < start; ++i) {
+ spaces.append(" ");
+ }
+
+ std::string arrows("^");
+
+ out << std::endl
+ << fmt("%1% |%2%" ANSI_RED "%3%" ANSI_NORMAL,
+ prefix,
+ spaces,
+ arrows);
}
-
- std::string arrows("^");
-
- std::cout << fmt("%1% |%2%" ANSI_RED "%3%" ANSI_NORMAL,
- prefix,
- spaces,
- arrows) << std::endl;
}
// next line of code.
if (nixCode.nextLineOfCode.has_value()) {
- std::cout << fmt("%1% %|2$5d|| %3%",
- prefix,
- (nixCode.errPos.lineNumber + 1),
- *nixCode.nextLineOfCode)
- << std::endl;
+ out << std::endl
+ << fmt("%1% %|2$5d|| %3%",
+ prefix,
+ (nixCode.errPos.line + 1),
+ *nixCode.nextLineOfCode);
}
}
-void printErrorInfo(const ErrorInfo &einfo)
+std::ostream& operator<<(std::ostream &out, const ErrorInfo &einfo)
{
- int errwidth = 80;
- string prefix = " ";
+ auto errwidth = std::max<size_t>(getWindowSize().second, 20);
+ string prefix = "";
string levelString;
switch (einfo.level) {
- case ErrLevel::elError: {
- levelString = ANSI_RED;
- levelString += "error:";
- levelString += ANSI_NORMAL;
- break;
- }
- case ErrLevel::elWarning: {
- levelString = ANSI_YELLOW;
- levelString += "warning:";
- levelString += ANSI_NORMAL;
- break;
- }
- default: {
- levelString = fmt("invalid error level: %1%", einfo.level);
- break;
- }
+ case Verbosity::Error: {
+ levelString = ANSI_RED;
+ levelString += "error:";
+ levelString += ANSI_NORMAL;
+ break;
+ }
+ case Verbosity::Warn: {
+ levelString = ANSI_YELLOW;
+ levelString += "warning:";
+ levelString += ANSI_NORMAL;
+ break;
+ }
+ case Verbosity::Info: {
+ levelString = ANSI_GREEN;
+ levelString += "info:";
+ levelString += ANSI_NORMAL;
+ break;
+ }
+ case Verbosity::Talkative: {
+ levelString = ANSI_GREEN;
+ levelString += "talk:";
+ levelString += ANSI_NORMAL;
+ break;
+ }
+ case Verbosity::Chatty: {
+ levelString = ANSI_GREEN;
+ levelString += "chat:";
+ levelString += ANSI_NORMAL;
+ break;
+ }
+ case Verbosity::Vomit: {
+ levelString = ANSI_GREEN;
+ levelString += "vomit:";
+ levelString += ANSI_NORMAL;
+ break;
+ }
+ case Verbosity::Debug: {
+ levelString = ANSI_YELLOW;
+ levelString += "debug:";
+ levelString += ANSI_NORMAL;
+ break;
+ }
+ default: {
+ levelString = fmt("invalid error level: %d", (uint8_t)einfo.level);
+ break;
+ }
}
- int ndl = prefix.length() + levelString.length() + 3 + einfo.name.length() + einfo.programName.value_or("").length();
- int dashwidth = ndl > (errwidth - 3) ? 3 : errwidth - ndl;
+ auto ndl = prefix.length() + levelString.length() + 3 + einfo.name.length() + einfo.programName.value_or("").length();
+ auto dashwidth = ndl > (errwidth - 3) ? 3 : errwidth - ndl;
- string dashes;
- for (int i = 0; i < dashwidth; ++i)
- dashes.append("-");
+ std::string dashes(dashwidth, '-');
// divider.
- std::cout << fmt("%1%%2%" ANSI_BLUE " %3% %4% %5% %6%" ANSI_NORMAL,
- prefix,
- levelString,
- "---",
- einfo.name,
- dashes,
- einfo.programName.value_or(""))
- << std::endl;
-
- // filename.
+ if (einfo.name != "")
+ out << fmt("%1%%2%" ANSI_BLUE " --- %3% %4% %5%" ANSI_NORMAL,
+ prefix,
+ levelString,
+ einfo.name,
+ dashes,
+ einfo.programName.value_or(""));
+ else
+ out << fmt("%1%%2%" ANSI_BLUE " -----%3% %4%" ANSI_NORMAL,
+ prefix,
+ levelString,
+ dashes,
+ einfo.programName.value_or(""));
+
+ bool nl = false; // intersperse newline between sections.
if (einfo.nixCode.has_value()) {
- if (einfo.nixCode->errPos.nixFile != "") {
- string eline = einfo.nixCode->errLineOfCode != ""
- ? string(" ") + showErrPos(einfo.nixCode->errPos)
- : "";
-
- std::cout << fmt("%1%in file: " ANSI_BLUE "%2%%3%" ANSI_NORMAL,
- prefix,
- einfo.nixCode->errPos.nixFile,
- eline) << std::endl;
- std::cout << prefix << std::endl;
+ if (einfo.nixCode->errPos.file != "") {
+ // filename, line, column.
+ out << std::endl << fmt("%1%in file: " ANSI_BLUE "%2% %3%" ANSI_NORMAL,
+ prefix,
+ einfo.nixCode->errPos.file,
+ showErrPos(einfo.nixCode->errPos));
} else {
- std::cout << fmt("%1%from command line argument", prefix) << std::endl;
- std::cout << prefix << std::endl;
+ out << std::endl << fmt("%1%from command line argument", prefix);
}
+ nl = true;
}
// description
- std::cout << prefix << einfo.description << std::endl;
- std::cout << prefix << std::endl;
+ if (einfo.description != "") {
+ if (nl)
+ out << std::endl << prefix;
+ out << std::endl << prefix << einfo.description;
+ nl = true;
+ }
// lines of code.
- if (einfo.nixCode->errLineOfCode != "") {
- printCodeLines(prefix, *einfo.nixCode);
- std::cout << prefix << std::endl;
+ if (einfo.nixCode.has_value() && einfo.nixCode->errLineOfCode.has_value()) {
+ if (nl)
+ out << std::endl << prefix;
+ printCodeLines(out, prefix, *einfo.nixCode);
+ nl = true;
}
// hint
if (einfo.hint.has_value()) {
- std::cout << prefix << *einfo.hint << std::endl;
- std::cout << prefix << std::endl;
+ if (nl)
+ out << std::endl << prefix;
+ out << std::endl << prefix << *einfo.hint;
+ nl = true;
}
-}
+ return out;
+}
}
diff --git a/src/libutil/error.hh b/src/libutil/error.hh
index f402b692e..4f9df826f 100644
--- a/src/libutil/error.hh
+++ b/src/libutil/error.hh
@@ -1,121 +1,186 @@
-#ifndef error_hh
-#define error_hh
+#pragma once
-#include "ansicolor.hh"
-#include <string>
-#include <optional>
-#include <iostream>
+
+#include "ref.hh"
#include "types.hh"
-namespace nix
-{
+#include <cstring>
+#include <list>
+#include <memory>
+#include <map>
+#include <optional>
-typedef enum {
- elWarning,
- elError
-} ErrLevel;
+#include "fmt.hh"
-struct ErrPos
-{
- int lineNumber;
- int column;
- string nixFile;
+/* Before 4.7, gcc's std::exception uses empty throw() specifiers for
+ * its (virtual) destructor and what() in c++11 mode, in violation of spec
+ */
+#ifdef __GNUC__
+#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7)
+#define EXCEPTION_NEEDS_THROW_SPEC
+#endif
+#endif
+
+namespace nix {
+
+/*
+
+This file defines two main structs/classes used in nix error handling.
+
+ErrorInfo provides a standard payload of error information, with conversion to string
+happening in the logger rather than at the call site.
+
+BaseError is the ancestor of nix specific exceptions (and Interrupted), and contains
+an ErrorInfo.
+
+ErrorInfo structs are sent to the logger as part of an exception, or directly with the
+logError or logWarning macros.
+
+See the error-demo.cc program for usage examples.
+*/
+
+enum struct Verbosity {
+ Error = 0,
+ Warn,
+ Info,
+ Talkative,
+ Chatty,
+ Debug,
+ Vomit,
+};
+
+// ErrPos indicates the location of an error in a nix file.
+struct ErrPos {
+ int line = 0;
+ int column = 0;
+ string file;
+
+ operator bool() const
+ {
+ return line != 0;
+ }
+
+ // convert from the Pos struct, found in libexpr.
template <class P>
ErrPos& operator=(const P &pos)
{
- lineNumber = pos.line;
+ line = pos.line;
column = pos.column;
- nixFile = pos.file;
+ file = pos.file;
return *this;
}
template <class P>
ErrPos(const P &p)
{
- *this = p;
+ *this = p;
}
};
-struct NixCode
-{
+struct NixCode {
ErrPos errPos;
std::optional<string> prevLineOfCode;
- string errLineOfCode;
+ std::optional<string> errLineOfCode;
std::optional<string> nextLineOfCode;
};
-// ----------------------------------------------------------------
-// format function for hints. same as fmt, except templated values
-// are always in yellow.
+struct ErrorInfo {
+ Verbosity level;
+ string name;
+ string description;
+ std::optional<hintformat> hint;
+ std::optional<NixCode> nixCode;
-template <class T>
-struct yellowify
-{
- yellowify(T &s) : value(s) {}
- T &value;
+ static std::optional<string> programName;
};
-template <class T>
-std::ostream& operator<<(std::ostream &out, const yellowify<T> &y)
-{
- return out << ANSI_YELLOW << y.value << ANSI_NORMAL;
-}
+std::ostream& operator<<(std::ostream &out, const ErrorInfo &einfo);
-class hintformat
+/* BaseError should generally not be caught, as it has Interrupted as
+ a subclass. Catch Error instead. */
+class BaseError : public std::exception
{
+protected:
+ string prefix_; // used for location traces etc.
+ mutable ErrorInfo err;
+
+ mutable std::optional<string> what_;
+ const string& calcWhat() const;
+
public:
- hintformat(string format) :fmt(format)
- {
- fmt.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit);
- }
- template<class T>
- hintformat& operator%(const T &value)
- {
- fmt % yellowify(value);
- return *this;
- }
+ unsigned int status = 1; // exit status
+
+ template<typename... Args>
+ BaseError(unsigned int status, const Args & ... args)
+ : err { .level = Verbosity::Error,
+ .hint = hintfmt(args...)
+ }
+ , status(status)
+ { }
+
+ template<typename... Args>
+ BaseError(const std::string & fs, const Args & ... args)
+ : err { .level = Verbosity::Error,
+ .hint = hintfmt(fs, args...)
+ }
+ { }
+
+ BaseError(hintformat hint)
+ : err { .level = Verbosity::Error,
+ .hint = hint
+ }
+ { }
+
+ BaseError(ErrorInfo && e)
+ : err(std::move(e))
+ { }
+
+ BaseError(const ErrorInfo & e)
+ : err(e)
+ { }
+
+ virtual const char* sname() const { return "BaseError"; }
+
+#ifdef EXCEPTION_NEEDS_THROW_SPEC
+ ~BaseError() throw () { };
+ const char * what() const throw () { return calcWhat().c_str(); }
+#else
+ const char * what() const noexcept override { return calcWhat().c_str(); }
+#endif
- std::string str() const
- {
- return fmt.str();
- }
+ const string & msg() const { return calcWhat(); }
+ const string & prefix() const { return prefix_; }
+ BaseError & addPrefix(const FormatOrString & fs);
- template <typename U>
- friend class AddHint;
-private:
- format fmt;
+ const ErrorInfo & info() { calcWhat(); return err; }
};
-std::ostream& operator<<(std::ostream &os, const hintformat &hf);
+#define MakeError(newClass, superClass) \
+ class newClass : public superClass \
+ { \
+ public: \
+ using superClass::superClass; \
+ virtual const char* sname() const override { return #newClass; } \
+ }
-template<typename... Args>
-inline hintformat hintfmt(const std::string & fs, const Args & ... args)
-{
- hintformat f(fs);
- formatHelper(f, args...);
- return f;
-}
+MakeError(Error, BaseError);
-// -------------------------------------------------
-// ErrorInfo.
-struct ErrorInfo
+class SysError : public Error
{
- ErrLevel level;
- string name;
- string description;
- std::optional<hintformat> hint;
- std::optional<NixCode> nixCode;
-
- static std::optional<string> programName;
-};
+public:
+ int errNo;
-// --------------------------------------------------------
-// error printing
+ template<typename... Args>
+ SysError(const Args & ... args)
+ :Error("")
+ {
+ errNo = errno;
+ auto hf = hintfmt(args...);
+ err.hint = hintfmt("%1%: %2%", normaltxt(hf.str()), strerror(errNo));
+ }
-// just to cout for now.
-void printErrorInfo(const ErrorInfo &einfo);
+ virtual const char* sname() const override { return "SysError"; }
+};
}
-
-#endif
diff --git a/src/libutil/fmt.hh b/src/libutil/fmt.hh
new file mode 100644
index 000000000..12ab9c407
--- /dev/null
+++ b/src/libutil/fmt.hh
@@ -0,0 +1,139 @@
+#pragma once
+
+#include <boost/format.hpp>
+#include <string>
+#include "ansicolor.hh"
+
+
+namespace nix {
+
+
+/* Inherit some names from other namespaces for convenience. */
+using std::string;
+using boost::format;
+
+
+/* A variadic template that does nothing. Useful to call a function
+ for all variadic arguments but ignoring the result. */
+struct nop { template<typename... T> nop(T...) {} };
+
+
+struct FormatOrString
+{
+ string s;
+ FormatOrString(const string & s) : s(s) { };
+ template<class F>
+ FormatOrString(const F & f) : s(f.str()) { };
+ FormatOrString(const char * s) : s(s) { };
+};
+
+
+/* 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). */
+
+template<class F>
+inline void formatHelper(F & f)
+{
+}
+
+template<class F, typename T, typename... Args>
+inline void formatHelper(F & f, const T & x, const 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<typename... Args>
+inline std::string fmt(const std::string & fs, const Args & ... args)
+{
+ boost::format f(fs);
+ f.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit);
+ formatHelper(f, args...);
+ return f.str();
+}
+
+// -----------------------------------------------------------------------------
+// format function for hints in errors. same as fmt, except templated values
+// are always in yellow.
+
+template <class T>
+struct yellowtxt
+{
+ yellowtxt(const T &s) : value(s) {}
+ const T &value;
+};
+
+template <class T>
+std::ostream& operator<<(std::ostream &out, const yellowtxt<T> &y)
+{
+ return out << ANSI_YELLOW << y.value << ANSI_NORMAL;
+}
+
+template <class T>
+struct normaltxt
+{
+ normaltxt(const T &s) : value(s) {}
+ const T &value;
+};
+
+template <class T>
+std::ostream& operator<<(std::ostream &out, const normaltxt<T> &y)
+{
+ return out << ANSI_NORMAL << y.value;
+}
+
+class hintformat
+{
+public:
+ hintformat(const string &format) :fmt(format)
+ {
+ fmt.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit);
+ }
+
+ hintformat(const hintformat &hf)
+ : fmt(hf.fmt)
+ {}
+
+ template<class T>
+ hintformat& operator%(const T &value)
+ {
+ fmt % yellowtxt(value);
+ return *this;
+ }
+
+ std::string str() const
+ {
+ return fmt.str();
+ }
+
+private:
+ format fmt;
+};
+
+std::ostream& operator<<(std::ostream &os, const hintformat &hf);
+
+template<typename... Args>
+inline hintformat hintfmt(const std::string & fs, const Args & ... args)
+{
+ hintformat f(fs);
+ formatHelper(f, args...);
+ return f;
+}
+
+}
diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc
index 6b9effdd2..106b47ae3 100644
--- a/src/libutil/hash.cc
+++ b/src/libutil/hash.cc
@@ -134,10 +134,10 @@ std::string Hash::to_string(Base base, bool includeType) const
return s;
}
-Hash::Hash(const std::string & s, HashType type) : Hash(s, std::optional { type }) { }
-Hash::Hash(const std::string & s) : Hash(s, std::optional<HashType>{}) { }
+Hash::Hash(std::string_view s, HashType type) : Hash(s, std::optional { type }) { }
+Hash::Hash(std::string_view s) : Hash(s, std::optional<HashType>{}) { }
-Hash::Hash(const std::string & s, std::optional<HashType> type)
+Hash::Hash(std::string_view s, std::optional<HashType> type)
: type(type)
{
size_t pos = 0;
@@ -206,7 +206,7 @@ Hash::Hash(const std::string & s, std::optional<HashType> type)
}
else if (isSRI || size == base64Len()) {
- auto d = base64Decode(std::string(s, pos));
+ auto d = base64Decode(s.substr(pos));
if (d.size() != hashSize)
throw BadHash("invalid %s hash '%s'", isSRI ? "SRI" : "base-64", s);
assert(hashSize);
@@ -217,6 +217,18 @@ Hash::Hash(const std::string & s, std::optional<HashType> type)
throw BadHash("hash '%s' has wrong length for hash type '%s'", s, printHashType(*type));
}
+Hash newHashAllowEmpty(std::string hashStr, std::optional<HashType> ht)
+{
+ if (hashStr.empty()) {
+ if (!ht)
+ throw BadHash("empty hash requires explicit hash type");
+ Hash h(*ht);
+ warn("found empty hash, assuming '%s'", h.to_string(Base::SRI, true));
+ return h;
+ } else
+ return Hash(hashStr, ht);
+}
+
union Ctx
{
diff --git a/src/libutil/hash.hh b/src/libutil/hash.hh
index 41322be67..8f9364440 100644
--- a/src/libutil/hash.hh
+++ b/src/libutil/hash.hh
@@ -25,7 +25,7 @@ const int sha512HashSize = 64;
extern const string base32Chars;
-enum struct Base : int {
+enum struct Base {
Base64,
Base32,
Base16,
@@ -52,11 +52,11 @@ struct Hash
Subresource Integrity hash expression). If the 'type' argument
is not present, then the hash type must be specified in the
string. */
- Hash(const std::string & s, std::optional<HashType> type);
+ Hash(std::string_view s, std::optional<HashType> type);
// type must be provided
- Hash(const std::string & s, HashType type);
+ Hash(std::string_view s, HashType type);
// hash type must be part of string
- Hash(const std::string & s);
+ Hash(std::string_view s);
void init();
@@ -93,7 +93,7 @@ struct Hash
/* Return a string representation of the hash, in base-16, base-32
or base-64. By default, this is prefixed by the hash type
(e.g. "sha256:"). */
- std::string to_string(Base base = Base::Base32, bool includeType = true) const;
+ std::string to_string(Base base, bool includeType) const;
std::string gitRev() const
{
@@ -108,6 +108,8 @@ struct Hash
}
};
+/* Helper that defaults empty hashes to the 0 hash. */
+Hash newHashAllowEmpty(std::string hashStr, std::optional<HashType> ht);
/* Print a hash in base-16 if it's MD5, or base-32 otherwise. */
string printHash16or32(const Hash & hash);
diff --git a/src/libutil/json.cc b/src/libutil/json.cc
index 74e37b4c4..01331947e 100644
--- a/src/libutil/json.cc
+++ b/src/libutil/json.cc
@@ -173,7 +173,7 @@ JSONObject JSONPlaceholder::object()
JSONPlaceholder::~JSONPlaceholder()
{
- assert(!first || std::uncaught_exception());
+ assert(!first || std::uncaught_exceptions());
}
}
diff --git a/src/libutil/local.mk b/src/libutil/local.mk
index 16c1fa03f..ae7eb67ad 100644
--- a/src/libutil/local.mk
+++ b/src/libutil/local.mk
@@ -7,5 +7,3 @@ libutil_DIR := $(d)
libutil_SOURCES := $(wildcard $(d)/*.cc)
libutil_LDFLAGS = $(LIBLZMA_LIBS) -lbz2 -pthread $(OPENSSL_LIBS) $(LIBBROTLI_LIBS) $(LIBARCHIVE_LIBS) $(BOOST_LDFLAGS) -lboost_context
-
-libutil_LIBS = libnixrust
diff --git a/src/libutil/logging.cc b/src/libutil/logging.cc
index 5cd2524c6..04156151f 100644
--- a/src/libutil/logging.cc
+++ b/src/libutil/logging.cc
@@ -18,7 +18,7 @@ void setCurActivity(const ActivityId activityId)
curActivity = activityId;
}
-Logger * logger = makeDefaultLogger();
+Logger * logger = makeSimpleLogger(true);
void Logger::warn(const std::string & msg)
{
@@ -35,13 +35,19 @@ class SimpleLogger : public Logger
public:
bool systemd, tty;
+ bool printBuildLogs;
- SimpleLogger()
+ SimpleLogger(bool printBuildLogs)
+ : printBuildLogs(printBuildLogs)
{
systemd = getEnv("IN_SYSTEMD") == "1";
tty = isatty(STDERR_FILENO);
}
+ bool isVerbose() override {
+ return printBuildLogs;
+ }
+
void log(Verbosity lvl, const FormatOrString & fs) override
{
if (lvl > verbosity) return;
@@ -63,13 +69,33 @@ public:
writeToStderr(prefix + filterANSIEscapes(fs.s, !tty) + "\n");
}
+ void logEI(const ErrorInfo & ei) override
+ {
+ std::stringstream oss;
+ oss << ei;
+
+ log(ei.level, oss.str());
+ }
+
void startActivity(ActivityId act, Verbosity lvl, ActivityType type,
const std::string & s, const Fields & fields, ActivityId parent)
- override
+ override
{
if (lvl <= verbosity && !s.empty())
log(lvl, s + "...");
}
+
+ void result(ActivityId act, ResultType type, const Fields & fields) override
+ {
+ if (type == ResultType::BuildLogLine && printBuildLogs) {
+ auto lastLine = fields[0].s;
+ printError(lastLine);
+ }
+ else if (type == ResultType::PostBuildLogLine && printBuildLogs) {
+ auto lastLine = fields[0].s;
+ printError("post-build-hook: " + lastLine);
+ }
+ }
};
Verbosity verbosity = Verbosity::Info;
@@ -94,9 +120,9 @@ void writeToStderr(const string & s)
}
}
-Logger * makeDefaultLogger()
+Logger * makeSimpleLogger(bool printBuildLogs)
{
- return new SimpleLogger();
+ return new SimpleLogger(printBuildLogs);
}
std::atomic<uint64_t> nextId{(uint64_t) getpid() << 32};
@@ -108,12 +134,15 @@ Activity::Activity(Logger & logger, Verbosity lvl, ActivityType type,
logger.startActivity(id, lvl, type, s, fields, parent);
}
-struct JSONLogger : Logger
-{
+struct JSONLogger : Logger {
Logger & prevLogger;
JSONLogger(Logger & prevLogger) : prevLogger(prevLogger) { }
+ bool isVerbose() override {
+ return true;
+ }
+
void addFields(nlohmann::json & json, const Fields & fields)
{
if (fields.empty()) return;
@@ -141,6 +170,19 @@ struct JSONLogger : Logger
write(json);
}
+ void logEI(const ErrorInfo & ei) override
+ {
+ std::ostringstream oss;
+ oss << ei;
+
+ nlohmann::json json;
+ json["action"] = "msg";
+ json["level"] = ei.level;
+ json["msg"] = oss.str();
+
+ write(json);
+ }
+
void startActivity(ActivityId act, Verbosity lvl, ActivityType type,
const std::string & s, const Fields & fields, ActivityId parent) override
{
@@ -231,13 +273,17 @@ bool handleJSONLogMessage(const std::string & msg,
}
} catch (std::exception & e) {
- printError("bad log message from builder: %s", e.what());
+ logError({
+ .name = "Json log message",
+ .hint = hintfmt("bad log message from builder: %s", e.what())
+ });
}
return true;
}
-Activity::~Activity() {
+Activity::~Activity()
+{
try {
logger.stopActivity(id);
} catch (...) {
diff --git a/src/libutil/logging.hh b/src/libutil/logging.hh
index 3449f5080..3b54101f0 100644
--- a/src/libutil/logging.hh
+++ b/src/libutil/logging.hh
@@ -1,20 +1,11 @@
#pragma once
#include "types.hh"
+#include "error.hh"
namespace nix {
-enum struct Verbosity : uint64_t {
- Error = 0,
- Warn,
- Info,
- Talkative,
- Chatty,
- Debug,
- Vomit,
-};
-
-enum struct ActivityType : uint64_t {
+enum struct ActivityType {
Unknown = 0,
CopyPath = 100,
Download = 101,
@@ -27,9 +18,10 @@ enum struct ActivityType : uint64_t {
Substitute = 108,
QueryPathInfo = 109,
PostBuildHook = 110,
+ BuildWaiting = 111,
};
-enum struct ResultType : uint64_t {
+enum struct ResultType {
FileLinked = 100,
BuildLogLine = 101,
UntrustedPath = 102,
@@ -63,6 +55,11 @@ public:
virtual ~Logger() { }
+ virtual void stop() { };
+
+ // Whether the logger prints the whole build log
+ virtual bool isVerbose() { return false; }
+
virtual void log(Verbosity lvl, const FormatOrString & fs) = 0;
void log(const FormatOrString & fs)
@@ -70,6 +67,14 @@ public:
log(Verbosity::Info, fs);
}
+ virtual void logEI(const ErrorInfo &ei) = 0;
+
+ void logEI(Verbosity lvl, ErrorInfo ei)
+ {
+ ei.level = lvl;
+ logEI(ei);
+ }
+
virtual void warn(const std::string & msg);
virtual void startActivity(ActivityId act, Verbosity lvl, ActivityType type,
@@ -141,7 +146,7 @@ struct PushActivity
extern Logger * logger;
-Logger * makeDefaultLogger();
+Logger * makeSimpleLogger(bool printBuildLogs = true);
Logger * makeJSONLogger(Logger & prevLogger);
@@ -151,9 +156,23 @@ bool handleJSONLogMessage(const std::string & msg,
extern Verbosity verbosity; /* suppress msgs > this */
-/* 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. */
+/* Print a message with the standard ErrorInfo format.
+ In general, use these 'log' macros for reporting problems that may require user
+ intervention or that need more explanation. Use the 'print' macros for more
+ lightweight status messages. */
+#define logErrorInfo(level, errorInfo...) \
+ do { \
+ if (level <= nix::verbosity) { \
+ logger->logEI(level, errorInfo); \
+ } \
+ } while (0)
+
+#define logError(errorInfo...) logErrorInfo(Verbosity::Error, errorInfo)
+#define logWarning(errorInfo...) logErrorInfo(Verbosity::Warn, errorInfo)
+
+/* Print a string 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) { \
@@ -167,6 +186,7 @@ extern Verbosity verbosity; /* suppress msgs > this */
#define debug(args...) printMsg(Verbosity::Debug, args)
#define vomit(args...) printMsg(Verbosity::Vomit, args)
+/* if verbosity >= Verbosity::Warn, print a message with a yellow 'warning:' prefix. */
template<typename... Args>
inline void warn(const std::string & fs, const Args & ... args)
{
diff --git a/src/libutil/lru-cache.hh b/src/libutil/lru-cache.hh
index 8b83f842c..6ef4a3e06 100644
--- a/src/libutil/lru-cache.hh
+++ b/src/libutil/lru-cache.hh
@@ -1,5 +1,6 @@
#pragma once
+#include <cassert>
#include <map>
#include <list>
#include <optional>
diff --git a/src/libutil/rust-ffi.cc b/src/libutil/rust-ffi.cc
index 6f36b3192..67924568f 100644
--- a/src/libutil/rust-ffi.cc
+++ b/src/libutil/rust-ffi.cc
@@ -1,3 +1,4 @@
+#if 0
#include "logging.hh"
#include "rust-ffi.hh"
@@ -20,3 +21,4 @@ std::ostream & operator << (std::ostream & str, const String & s)
}
}
+#endif
diff --git a/src/libutil/rust-ffi.hh b/src/libutil/rust-ffi.hh
index 228e2eead..cfbaf9dec 100644
--- a/src/libutil/rust-ffi.hh
+++ b/src/libutil/rust-ffi.hh
@@ -1,4 +1,5 @@
#pragma once
+#if 0
#include "serialise.hh"
@@ -185,3 +186,4 @@ struct Result
};
}
+#endif
diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc
index 8201549fd..c8b71188f 100644
--- a/src/libutil/serialise.cc
+++ b/src/libutil/serialise.cc
@@ -52,7 +52,10 @@ size_t threshold = 256 * 1024 * 1024;
static void warnLargeDump()
{
- printError("warning: dumping very large path (> 256 MiB); this may run out of memory");
+ logWarning({
+ .name = "Large path",
+ .description = "dumping very large path (> 256 MiB); this may run out of memory"
+ });
}
diff --git a/src/libutil/tests/hash.cc b/src/libutil/tests/hash.cc
index ecc0d4a03..84a50d61b 100644
--- a/src/libutil/tests/hash.cc
+++ b/src/libutil/tests/hash.cc
@@ -11,28 +11,28 @@ namespace nix {
// values taken from: https://tools.ietf.org/html/rfc1321
auto s1 = "";
auto hash = hashString(HashType::MD5, s1);
- ASSERT_EQ(hash.to_string(Base::Base16), "md5:d41d8cd98f00b204e9800998ecf8427e");
+ ASSERT_EQ(hash.to_string(Base::Base16, true), "md5:d41d8cd98f00b204e9800998ecf8427e");
}
TEST(hashString, testKnownMD5Hashes2) {
// values taken from: https://tools.ietf.org/html/rfc1321
auto s2 = "abc";
auto hash = hashString(HashType::MD5, s2);
- ASSERT_EQ(hash.to_string(Base::Base16), "md5:900150983cd24fb0d6963f7d28e17f72");
+ ASSERT_EQ(hash.to_string(Base::Base16, true), "md5:900150983cd24fb0d6963f7d28e17f72");
}
TEST(hashString, testKnownSHA1Hashes1) {
// values taken from: https://tools.ietf.org/html/rfc3174
auto s = "abc";
auto hash = hashString(HashType::SHA1, s);
- ASSERT_EQ(hash.to_string(Base::Base16),"sha1:a9993e364706816aba3e25717850c26c9cd0d89d");
+ ASSERT_EQ(hash.to_string(Base::Base16, true),"sha1:a9993e364706816aba3e25717850c26c9cd0d89d");
}
TEST(hashString, testKnownSHA1Hashes2) {
// values taken from: https://tools.ietf.org/html/rfc3174
auto s = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
auto hash = hashString(HashType::SHA1, s);
- ASSERT_EQ(hash.to_string(Base::Base16),"sha1:84983e441c3bd26ebaae4aa1f95129e5e54670f1");
+ ASSERT_EQ(hash.to_string(Base::Base16, true),"sha1:84983e441c3bd26ebaae4aa1f95129e5e54670f1");
}
TEST(hashString, testKnownSHA256Hashes1) {
@@ -40,7 +40,7 @@ namespace nix {
auto s = "abc";
auto hash = hashString(HashType::SHA256, s);
- ASSERT_EQ(hash.to_string(Base::Base16),
+ ASSERT_EQ(hash.to_string(Base::Base16, true),
"sha256:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
}
@@ -48,7 +48,7 @@ namespace nix {
// values taken from: https://tools.ietf.org/html/rfc4634
auto s = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
auto hash = hashString(HashType::SHA256, s);
- ASSERT_EQ(hash.to_string(Base::Base16),
+ ASSERT_EQ(hash.to_string(Base::Base16, true),
"sha256:248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1");
}
@@ -56,7 +56,7 @@ namespace nix {
// values taken from: https://tools.ietf.org/html/rfc4634
auto s = "abc";
auto hash = hashString(HashType::SHA512, s);
- ASSERT_EQ(hash.to_string(Base::Base16),
+ ASSERT_EQ(hash.to_string(Base::Base16, true),
"sha512:ddaf35a193617abacc417349ae20413112e6fa4e89a9"
"7ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd"
"454d4423643ce80e2a9ac94fa54ca49f");
@@ -67,7 +67,7 @@ namespace nix {
auto s = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu";
auto hash = hashString(HashType::SHA512, s);
- ASSERT_EQ(hash.to_string(Base::Base16),
+ ASSERT_EQ(hash.to_string(Base::Base16, true),
"sha512:8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa1"
"7299aeadb6889018501d289e4900f7e4331b99dec4b5433a"
"c7d329eeb6dd26545e96e55b874be909");
diff --git a/src/libutil/tests/local.mk b/src/libutil/tests/local.mk
index a297edb64..815e18560 100644
--- a/src/libutil/tests/local.mk
+++ b/src/libutil/tests/local.mk
@@ -8,7 +8,7 @@ libutil-tests_INSTALL_DIR :=
libutil-tests_SOURCES := $(wildcard $(d)/*.cc)
-libutil-tests_CXXFLAGS += -I src/libutil
+libutil-tests_CXXFLAGS += -I src/libutil -I src/libexpr
libutil-tests_LIBS = libutil
diff --git a/src/libutil/tests/logging.cc b/src/libutil/tests/logging.cc
new file mode 100644
index 000000000..2ffcbc41d
--- /dev/null
+++ b/src/libutil/tests/logging.cc
@@ -0,0 +1,255 @@
+#include "logging.hh"
+#include "nixexpr.hh"
+#include "util.hh"
+
+#include <gtest/gtest.h>
+
+namespace nix {
+
+ /* ----------------------------------------------------------------------------
+ * logEI
+ * --------------------------------------------------------------------------*/
+
+ TEST(logEI, catpuresBasicProperties) {
+
+ MakeError(TestError, Error);
+ ErrorInfo::programName = std::optional("error-unit-test");
+
+ try {
+ throw TestError("an error for testing purposes");
+ } catch (Error &e) {
+ testing::internal::CaptureStderr();
+ logger->logEI(e.info());
+ auto str = testing::internal::GetCapturedStderr();
+
+ ASSERT_STREQ(str.c_str(),"\x1B[31;1merror:\x1B[0m\x1B[34;1m --- TestError --- error-unit-test\x1B[0m\nan error for testing purposes\n");
+ }
+ }
+
+ TEST(logEI, appendingHintsToPreviousError) {
+
+ MakeError(TestError, Error);
+ ErrorInfo::programName = std::optional("error-unit-test");
+
+ try {
+ auto e = Error("initial error");
+ throw TestError(e.info());
+ } catch (Error &e) {
+ ErrorInfo ei = e.info();
+ ei.hint = hintfmt("%s; subsequent error message.", normaltxt(e.info().hint ? e.info().hint->str() : ""));
+
+ testing::internal::CaptureStderr();
+ logger->logEI(ei);
+ auto str = testing::internal::GetCapturedStderr();
+
+ ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- TestError --- error-unit-test\x1B[0m\n\x1B[33;1m\x1B[0minitial error\x1B[0m; subsequent error message.\n");
+ }
+
+ }
+
+ TEST(logEI, picksUpSysErrorExitCode) {
+
+ MakeError(TestError, Error);
+ ErrorInfo::programName = std::optional("error-unit-test");
+
+ try {
+ auto x = readFile(-1);
+ }
+ catch (SysError &e) {
+ testing::internal::CaptureStderr();
+ logError(e.info());
+ auto str = testing::internal::GetCapturedStderr();
+
+ ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- SysError --- error-unit-test\x1B[0m\n\x1B[33;1m\x1B[0mstatting file\x1B[0m: \x1B[33;1mBad file descriptor\x1B[0m\n");
+
+ }
+ }
+
+ TEST(logEI, loggingErrorOnInfoLevel) {
+ testing::internal::CaptureStderr();
+
+ logger->logEI({ .level = Verbosity::Info,
+ .name = "Info name",
+ .description = "Info description",
+ });
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "\x1B[32;1minfo:\x1B[0m\x1B[34;1m --- Info name --- error-unit-test\x1B[0m\nInfo description\n");
+ }
+
+ TEST(logEI, loggingErrorOnTalkativeLevel) {
+ verbosity = Verbosity::Talkative;
+
+ testing::internal::CaptureStderr();
+
+ logger->logEI({ .level = Verbosity::Talkative,
+ .name = "Talkative name",
+ .description = "Talkative description",
+ });
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "\x1B[32;1mtalk:\x1B[0m\x1B[34;1m --- Talkative name --- error-unit-test\x1B[0m\nTalkative description\n");
+ }
+
+ TEST(logEI, loggingErrorOnChattyLevel) {
+ verbosity = Verbosity::Chatty;
+
+ testing::internal::CaptureStderr();
+
+ logger->logEI({ .level = Verbosity::Chatty,
+ .name = "Chatty name",
+ .description = "Talkative description",
+ });
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "\x1B[32;1mchat:\x1B[0m\x1B[34;1m --- Chatty name --- error-unit-test\x1B[0m\nTalkative description\n");
+ }
+
+ TEST(logEI, loggingErrorOnDebugLevel) {
+ verbosity = Verbosity::Debug;
+
+ testing::internal::CaptureStderr();
+
+ logger->logEI({ .level = Verbosity::Debug,
+ .name = "Debug name",
+ .description = "Debug description",
+ });
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "\x1B[33;1mdebug:\x1B[0m\x1B[34;1m --- Debug name --- error-unit-test\x1B[0m\nDebug description\n");
+ }
+
+ TEST(logEI, loggingErrorOnVomitLevel) {
+ verbosity = Verbosity::Vomit;
+
+ testing::internal::CaptureStderr();
+
+ logger->logEI({ .level = Verbosity::Vomit,
+ .name = "Vomit name",
+ .description = "Vomit description",
+ });
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "\x1B[32;1mvomit:\x1B[0m\x1B[34;1m --- Vomit name --- error-unit-test\x1B[0m\nVomit description\n");
+ }
+
+ /* ----------------------------------------------------------------------------
+ * logError
+ * --------------------------------------------------------------------------*/
+
+
+ TEST(logError, logErrorWithoutHintOrCode) {
+ testing::internal::CaptureStderr();
+
+ logError({
+ .name = "name",
+ .description = "error description",
+ });
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- name --- error-unit-test\x1B[0m\nerror description\n");
+ }
+
+ TEST(logError, logErrorWithPreviousAndNextLinesOfCode) {
+ SymbolTable testTable;
+ auto problem_file = testTable.create("myfile.nix");
+
+ testing::internal::CaptureStderr();
+
+ logError({
+ .name = "error name",
+ .description = "error with code lines",
+ .hint = hintfmt("this hint has %1% templated %2%!!",
+ "yellow",
+ "values"),
+ .nixCode = NixCode {
+ .errPos = Pos(problem_file, 40, 13),
+ .prevLineOfCode = "previous line of code",
+ .errLineOfCode = "this is the problem line of code",
+ .nextLineOfCode = "next line of code",
+ }});
+
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- error name --- error-unit-test\x1B[0m\nin file: \x1B[34;1mmyfile.nix (40:13)\x1B[0m\n\nerror with code lines\n\n 39| previous line of code\n 40| this is the problem line of code\n | \x1B[31;1m^\x1B[0m\n 41| next line of code\n\nthis hint has \x1B[33;1myellow\x1B[0m templated \x1B[33;1mvalues\x1B[0m!!\n");
+ }
+
+ TEST(logError, logErrorWithoutLinesOfCode) {
+ SymbolTable testTable;
+ auto problem_file = testTable.create("myfile.nix");
+ testing::internal::CaptureStderr();
+
+ logError({
+ .name = "error name",
+ .description = "error without any code lines.",
+ .hint = hintfmt("this hint has %1% templated %2%!!",
+ "yellow",
+ "values"),
+ .nixCode = NixCode {
+ .errPos = Pos(problem_file, 40, 13)
+ }});
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- error name --- error-unit-test\x1B[0m\nin file: \x1B[34;1mmyfile.nix (40:13)\x1B[0m\n\nerror without any code lines.\n\nthis hint has \x1B[33;1myellow\x1B[0m templated \x1B[33;1mvalues\x1B[0m!!\n");
+ }
+
+ TEST(logError, logErrorWithOnlyHintAndName) {
+ SymbolTable testTable;
+ auto problem_file = testTable.create("myfile.nix");
+ testing::internal::CaptureStderr();
+
+ logError({
+ .name = "error name",
+ .hint = hintfmt("hint %1%", "only"),
+ .nixCode = NixCode {
+ .errPos = Pos(problem_file, 40, 13)
+ }});
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- error name --- error-unit-test\x1B[0m\nin file: \x1B[34;1mmyfile.nix (40:13)\x1B[0m\n\nhint \x1B[33;1monly\x1B[0m\n");
+
+ }
+
+ /* ----------------------------------------------------------------------------
+ * logWarning
+ * --------------------------------------------------------------------------*/
+
+ TEST(logWarning, logWarningWithNameDescriptionAndHint) {
+ testing::internal::CaptureStderr();
+
+ logWarning({
+ .name = "name",
+ .description = "error description",
+ .hint = hintfmt("there was a %1%", "warning"),
+ });
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "\x1B[33;1mwarning:\x1B[0m\x1B[34;1m --- name --- error-unit-test\x1B[0m\nerror description\n\nthere was a \x1B[33;1mwarning\x1B[0m\n");
+ }
+
+ TEST(logWarning, logWarningWithFileLineNumAndCode) {
+
+ SymbolTable testTable;
+ auto problem_file = testTable.create("myfile.nix");
+
+ testing::internal::CaptureStderr();
+
+ logWarning({
+ .name = "warning name",
+ .description = "warning description",
+ .hint = hintfmt("this hint has %1% templated %2%!!",
+ "yellow",
+ "values"),
+ .nixCode = NixCode {
+ .errPos = Pos(problem_file, 40, 13),
+ .prevLineOfCode = std::nullopt,
+ .errLineOfCode = "this is the problem line of code",
+ .nextLineOfCode = std::nullopt
+ }});
+
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "\x1B[33;1mwarning:\x1B[0m\x1B[34;1m --- warning name --- error-unit-test\x1B[0m\nin file: \x1B[34;1mmyfile.nix (40:13)\x1B[0m\n\nwarning description\n\n 40| this is the problem line of code\n | \x1B[31;1m^\x1B[0m\n\nthis hint has \x1B[33;1myellow\x1B[0m templated \x1B[33;1mvalues\x1B[0m!!\n");
+ }
+
+}
diff --git a/src/libutil/tests/pool.cc b/src/libutil/tests/pool.cc
new file mode 100644
index 000000000..127e42dda
--- /dev/null
+++ b/src/libutil/tests/pool.cc
@@ -0,0 +1,127 @@
+#include "pool.hh"
+#include <gtest/gtest.h>
+
+namespace nix {
+
+ struct TestResource
+ {
+
+ TestResource() {
+ static int counter = 0;
+ num = counter++;
+ }
+
+ int dummyValue = 1;
+ bool good = true;
+ int num;
+ };
+
+ /* ----------------------------------------------------------------------------
+ * Pool
+ * --------------------------------------------------------------------------*/
+
+ TEST(Pool, freshPoolHasZeroCountAndSpecifiedCapacity) {
+ auto isGood = [](const ref<TestResource> & r) { return r->good; };
+ auto createResource = []() { return make_ref<TestResource>(); };
+
+ Pool<TestResource> pool = Pool<TestResource>((size_t)1, createResource, isGood);
+
+ ASSERT_EQ(pool.count(), 0);
+ ASSERT_EQ(pool.capacity(), 1);
+ }
+
+ TEST(Pool, freshPoolCanGetAResource) {
+ auto isGood = [](const ref<TestResource> & r) { return r->good; };
+ auto createResource = []() { return make_ref<TestResource>(); };
+
+ Pool<TestResource> pool = Pool<TestResource>((size_t)1, createResource, isGood);
+ ASSERT_EQ(pool.count(), 0);
+
+ TestResource r = *(pool.get());
+
+ ASSERT_EQ(pool.count(), 1);
+ ASSERT_EQ(pool.capacity(), 1);
+ ASSERT_EQ(r.dummyValue, 1);
+ ASSERT_EQ(r.good, true);
+ }
+
+ TEST(Pool, capacityCanBeIncremented) {
+ auto isGood = [](const ref<TestResource> & r) { return r->good; };
+ auto createResource = []() { return make_ref<TestResource>(); };
+
+ Pool<TestResource> pool = Pool<TestResource>((size_t)1, createResource, isGood);
+ ASSERT_EQ(pool.capacity(), 1);
+ pool.incCapacity();
+ ASSERT_EQ(pool.capacity(), 2);
+ }
+
+ TEST(Pool, capacityCanBeDecremented) {
+ auto isGood = [](const ref<TestResource> & r) { return r->good; };
+ auto createResource = []() { return make_ref<TestResource>(); };
+
+ Pool<TestResource> pool = Pool<TestResource>((size_t)1, createResource, isGood);
+ ASSERT_EQ(pool.capacity(), 1);
+ pool.decCapacity();
+ ASSERT_EQ(pool.capacity(), 0);
+ }
+
+ TEST(Pool, flushBadDropsOutOfScopeResources) {
+ auto isGood = [](const ref<TestResource> & r) { return false; };
+ auto createResource = []() { return make_ref<TestResource>(); };
+
+ Pool<TestResource> pool = Pool<TestResource>((size_t)1, createResource, isGood);
+
+ {
+ auto _r = pool.get();
+ ASSERT_EQ(pool.count(), 1);
+ }
+
+ pool.flushBad();
+ ASSERT_EQ(pool.count(), 0);
+ }
+
+ // Test that the resources we allocate are being reused when they are still good.
+ TEST(Pool, reuseResource) {
+ auto isGood = [](const ref<TestResource> & r) { return true; };
+ auto createResource = []() { return make_ref<TestResource>(); };
+
+ Pool<TestResource> pool = Pool<TestResource>((size_t)1, createResource, isGood);
+
+ // Compare the instance counter between the two handles. We expect them to be equal
+ // as the pool should hand out the same (still) good one again.
+ int counter = -1;
+ {
+ Pool<TestResource>::Handle h = pool.get();
+ counter = h->num;
+ } // the first handle goes out of scope
+
+ { // the second handle should contain the same resource (with the same counter value)
+ Pool<TestResource>::Handle h = pool.get();
+ ASSERT_EQ(h->num, counter);
+ }
+ }
+
+ // Test that the resources we allocate are being thrown away when they are no longer good.
+ TEST(Pool, badResourceIsNotReused) {
+ auto isGood = [](const ref<TestResource> & r) { return false; };
+ auto createResource = []() { return make_ref<TestResource>(); };
+
+ Pool<TestResource> pool = Pool<TestResource>((size_t)1, createResource, isGood);
+
+ // Compare the instance counter between the two handles. We expect them
+ // to *not* be equal as the pool should hand out a new instance after
+ // the first one was returned.
+ int counter = -1;
+ {
+ Pool<TestResource>::Handle h = pool.get();
+ counter = h->num;
+ } // the first handle goes out of scope
+
+ {
+ // the second handle should contain a different resource (with a
+ //different counter value)
+ Pool<TestResource>::Handle h = pool.get();
+ ASSERT_NE(h->num, counter);
+ }
+ }
+}
diff --git a/src/libutil/types.hh b/src/libutil/types.hh
index 250c9581d..3af485fa0 100644
--- a/src/libutil/types.hh
+++ b/src/libutil/types.hh
@@ -1,164 +1,29 @@
#pragma once
-
#include "ref.hh"
-#include <string>
#include <list>
#include <set>
-#include <memory>
#include <map>
-
-#include <boost/format.hpp>
-
-/* Before 4.7, gcc's std::exception uses empty throw() specifiers for
- * its (virtual) destructor and what() in c++11 mode, in violation of spec
- */
-#ifdef __GNUC__
-#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7)
-#define EXCEPTION_NEEDS_THROW_SPEC
-#endif
-#endif
-
+#include <vector>
namespace nix {
-
-/* Inherit some names from other namespaces for convenience. */
-using std::string;
using std::list;
using std::set;
using std::vector;
-using boost::format;
-
-
-/* A variadic template that does nothing. Useful to call a function
- for all variadic arguments but ignoring the result. */
-struct nop { template<typename... T> nop(T...) {} };
-
-
-struct FormatOrString
-{
- string s;
- FormatOrString(const string & s) : s(s) { };
- template<class F>
- FormatOrString(const F & f) : s(f.str()) { };
- FormatOrString(const char * s) : s(s) { };
-};
-
-
-/* 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). */
-
-template<class F>
-inline void formatHelper(F & f)
-{
-}
-
-template<class F, typename T, typename... Args>
-inline void formatHelper(F & f, const T & x, const 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<typename... Args>
-inline std::string fmt(const std::string & fs, const Args & ... args)
-{
- boost::format f(fs);
- f.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit);
- 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
-{
-protected:
- string prefix_; // used for location traces etc.
- string err;
-public:
- unsigned int status = 1; // exit status
-
- template<typename... Args>
- BaseError(unsigned int status, const Args & ... args)
- : err(fmt(args...))
- , status(status)
- {
- }
-
- template<typename... Args>
- BaseError(const 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);
-};
-
-#define MakeError(newClass, superClass) \
- class newClass : public superClass \
- { \
- public: \
- using superClass::superClass; \
- }
-
-MakeError(Error, BaseError);
-
-class SysError : public Error
-{
-public:
- int errNo;
-
- template<typename... Args>
- SysError(const Args & ... args)
- : Error(addErrno(fmt(args...)))
- { }
-
-private:
-
- std::string addErrno(const std::string & s);
-};
-
+using std::string;
typedef list<string> Strings;
typedef set<string> StringSet;
-typedef std::map<std::string, std::string> StringMap;
-
+typedef std::map<string, string> StringMap;
/* Paths are just strings. */
+
typedef string Path;
typedef list<Path> Paths;
typedef set<Path> PathSet;
-
/* Helper class to run code at startup. */
template<typename T>
struct OnStartup
@@ -166,5 +31,4 @@ struct OnStartup
OnStartup(T && t) { t(); }
};
-
}
diff --git a/src/libutil/url.hh b/src/libutil/url.hh
index 4a0d4071b..2ef88ef2a 100644
--- a/src/libutil/url.hh
+++ b/src/libutil/url.hh
@@ -1,6 +1,6 @@
#pragma once
-#include "types.hh"
+#include "error.hh"
#include <regex>
diff --git a/src/libutil/util.cc b/src/libutil/util.cc
index d17e952ae..e912d4450 100644
--- a/src/libutil/util.cc
+++ b/src/libutil/util.cc
@@ -35,29 +35,11 @@
#endif
-extern char * * environ;
+extern char * * environ __attribute__((weak));
namespace nix {
-
-const std::string nativeSystem = SYSTEM;
-
-
-BaseError & BaseError::addPrefix(const FormatOrString & fs)
-{
- prefix_ = fs.s + prefix_;
- return *this;
-}
-
-
-std::string SysError::addErrno(const std::string & s)
-{
- errNo = errno;
- return s + ": " + strerror(errNo);
-}
-
-
std::optional<std::string> getEnv(const std::string & key)
{
char * value = getenv(key.c_str());
@@ -129,7 +111,7 @@ Path canonPath(const Path & path, bool resolveSymlinks)
string s;
if (path[0] != '/')
- throw Error(format("not an absolute path: '%1%'") % path);
+ throw Error("not an absolute path: '%1%'", path);
string::const_iterator i = path.begin(), end = path.end();
string temp;
@@ -165,7 +147,7 @@ Path canonPath(const Path & path, bool resolveSymlinks)
the symlink target might contain new symlinks). */
if (resolveSymlinks && isLink(s)) {
if (++followCount >= maxFollow)
- throw Error(format("infinite symlink recursion in path '%1%'") % path);
+ throw Error("infinite symlink recursion in path '%1%'", path);
temp = absPath(readLink(s), dirOf(s))
+ string(i, end);
i = temp.begin(); /* restart */
@@ -226,7 +208,7 @@ struct stat lstat(const Path & path)
{
struct stat st;
if (lstat(path.c_str(), &st))
- throw SysError(format("getting status of '%1%'") % path);
+ throw SysError("getting status of '%1%'", path);
return st;
}
@@ -238,7 +220,7 @@ bool pathExists(const Path & path)
res = lstat(path.c_str(), &st);
if (!res) return true;
if (errno != ENOENT && errno != ENOTDIR)
- throw SysError(format("getting status of %1%") % path);
+ throw SysError("getting status of %1%", path);
return false;
}
@@ -286,7 +268,7 @@ DirEntries readDirectory(DIR *dir, const Path & path)
#endif
);
}
- if (errno) throw SysError(format("reading directory '%1%'") % path);
+ if (errno) throw SysError("reading directory '%1%'", path);
return entries;
}
@@ -294,7 +276,7 @@ DirEntries readDirectory(DIR *dir, const Path & path)
DirEntries readDirectory(const Path & path)
{
AutoCloseDir dir(opendir(path.c_str()));
- if (!dir) throw SysError(format("opening directory '%1%'") % path);
+ if (!dir) throw SysError("opening directory '%1%'", path);
return readDirectory(dir.get(), path);
}
@@ -324,7 +306,7 @@ string readFile(const Path & path)
{
AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
if (!fd)
- throw SysError(format("opening file '%1%'") % path);
+ throw SysError("opening file '%1%'", path);
return readFile(fd.get());
}
@@ -332,7 +314,8 @@ string readFile(const Path & path)
void readFile(const Path & path, Sink & sink)
{
AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
- if (!fd) throw SysError("opening file '%s'", path);
+ if (!fd)
+ throw SysError("opening file '%s'", path);
drainFD(fd.get(), sink);
}
@@ -341,7 +324,7 @@ void writeFile(const Path & path, const string & s, mode_t mode)
{
AutoCloseFD fd = open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode);
if (!fd)
- throw SysError(format("opening file '%1%'") % path);
+ throw SysError("opening file '%1%'", path);
writeFull(fd.get(), s);
}
@@ -350,7 +333,7 @@ void writeFile(const Path & path, Source & source, mode_t mode)
{
AutoCloseFD fd = open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode);
if (!fd)
- throw SysError(format("opening file '%1%'") % path);
+ throw SysError("opening file '%1%'", path);
std::vector<unsigned char> buf(64 * 1024);
@@ -400,7 +383,7 @@ static void _deletePath(int parentfd, const Path & path, unsigned long long & by
struct stat st;
if (fstatat(parentfd, name.c_str(), &st, AT_SYMLINK_NOFOLLOW) == -1) {
if (errno == ENOENT) return;
- throw SysError(format("getting status of '%1%'") % path);
+ throw SysError("getting status of '%1%'", path);
}
if (!S_ISDIR(st.st_mode) && st.st_nlink == 1)
@@ -411,15 +394,15 @@ static void _deletePath(int parentfd, const Path & path, unsigned long long & by
const auto PERM_MASK = S_IRUSR | S_IWUSR | S_IXUSR;
if ((st.st_mode & PERM_MASK) != PERM_MASK) {
if (fchmodat(parentfd, name.c_str(), st.st_mode | PERM_MASK, 0) == -1)
- throw SysError(format("chmod '%1%'") % path);
+ throw SysError("chmod '%1%'", path);
}
int fd = openat(parentfd, path.c_str(), O_RDONLY);
if (!fd)
- throw SysError(format("opening directory '%1%'") % path);
+ throw SysError("opening directory '%1%'", path);
AutoCloseDir dir(fdopendir(fd));
if (!dir)
- throw SysError(format("opening directory '%1%'") % path);
+ throw SysError("opening directory '%1%'", path);
for (auto & i : readDirectory(dir.get(), path))
_deletePath(dirfd(dir.get()), path + "/" + i.name, bytesFreed);
}
@@ -427,7 +410,7 @@ static void _deletePath(int parentfd, const Path & path, unsigned long long & by
int flags = S_ISDIR(st.st_mode) ? AT_REMOVEDIR : 0;
if (unlinkat(parentfd, name.c_str(), flags) == -1) {
if (errno == ENOENT) return;
- throw SysError(format("cannot unlink '%1%'") % path);
+ throw SysError("cannot unlink '%1%'", path);
}
}
@@ -443,7 +426,7 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed)
// for backwards compatibility.
if (errno == ENOENT) return;
- throw SysError(format("opening directory '%1%'") % path);
+ throw SysError("opening directory '%1%'", path);
}
_deletePath(dirfd.get(), path, bytesFreed);
@@ -497,12 +480,12 @@ Path createTempDir(const Path & tmpRoot, const Path & prefix,
"wheel", then "tar" will fail to unpack archives that
have the setgid bit set on directories. */
if (chown(tmpDir.c_str(), (uid_t) -1, getegid()) != 0)
- throw SysError(format("setting group of directory '%1%'") % tmpDir);
+ throw SysError("setting group of directory '%1%'", tmpDir);
#endif
return tmpDir;
}
if (errno != EEXIST)
- throw SysError(format("creating directory '%1%'") % tmpDir);
+ throw SysError("creating directory '%1%'", tmpDir);
}
}
@@ -584,15 +567,15 @@ Paths createDirs(const Path & path)
if (lstat(path.c_str(), &st) == -1) {
created = createDirs(dirOf(path));
if (mkdir(path.c_str(), 0777) == -1 && errno != EEXIST)
- throw SysError(format("creating directory '%1%'") % path);
+ throw SysError("creating directory '%1%'", path);
st = lstat(path);
created.push_back(path);
}
if (S_ISLNK(st.st_mode) && stat(path.c_str(), &st) == -1)
- throw SysError(format("statting symlink '%1%'") % path);
+ throw SysError("statting symlink '%1%'", path);
- if (!S_ISDIR(st.st_mode)) throw Error(format("'%1%' is not a directory") % path);
+ if (!S_ISDIR(st.st_mode)) throw Error("'%1%' is not a directory", path);
return created;
}
@@ -601,7 +584,7 @@ Paths createDirs(const Path & path)
void createSymlink(const Path & target, const Path & link)
{
if (symlink(target.c_str(), link.c_str()))
- throw SysError(format("creating symlink from '%1%' to '%2%'") % link % target);
+ throw SysError("creating symlink from '%1%' to '%2%'", link, target);
}
@@ -618,7 +601,7 @@ void replaceSymlink(const Path & target, const Path & link)
}
if (rename(tmp.c_str(), link.c_str()) != 0)
- throw SysError(format("renaming '%1%' to '%2%'") % tmp % link);
+ throw SysError("renaming '%1%' to '%2%'", tmp, link);
break;
}
@@ -723,7 +706,7 @@ AutoDelete::~AutoDelete()
deletePath(path);
else {
if (remove(path.c_str()) == -1)
- throw SysError(format("cannot unlink '%1%'") % path);
+ throw SysError("cannot unlink '%1%'", path);
}
}
} catch (...) {
@@ -789,7 +772,7 @@ void AutoCloseFD::close()
if (fd != -1) {
if (::close(fd) == -1)
/* This should never happen. */
- throw SysError(format("closing file descriptor %1%") % fd);
+ throw SysError("closing file descriptor %1%", fd);
}
}
@@ -862,7 +845,7 @@ int Pid::kill()
{
assert(pid != -1);
- debug(format("killing process %1%") % pid);
+ debug("killing process %1%", pid);
/* Send the requested signal to the child. If it has its own
process group, send the signal to every process in the child
@@ -874,7 +857,7 @@ int Pid::kill()
#if __FreeBSD__ || __APPLE__
if (errno != EPERM || ::kill(pid, 0) != 0)
#endif
- printError((SysError("killing process %d", pid).msg()));
+ logError(SysError("killing process %d", pid).info());
}
return wait();
@@ -920,7 +903,7 @@ pid_t Pid::release()
void killUser(uid_t uid)
{
- debug(format("killing all processes running under uid '%1%'") % uid);
+ debug("killing all processes running under uid '%1%'", uid);
assert(uid != 0); /* just to be safe... */
@@ -949,7 +932,7 @@ void killUser(uid_t uid)
#endif
if (errno == ESRCH) break; /* no more processes */
if (errno != EINTR)
- throw SysError(format("cannot kill processes for uid '%1%'") % uid);
+ throw SysError("cannot kill processes for uid '%1%'", uid);
}
_exit(0);
@@ -957,7 +940,7 @@ void killUser(uid_t uid)
int status = pid.wait();
if (status != 0)
- throw Error(format("cannot kill processes for uid '%1%': %2%") % uid % statusToString(status));
+ throw Error("cannot kill processes for uid '%1%': %2%", uid, statusToString(status));
/* !!! We should really do some check to make sure that there are
no processes left running under `uid', but there is no portable
@@ -989,7 +972,7 @@ pid_t startProcess(std::function<void()> fun, const ProcessOptions & options)
{
auto wrapper = [&]() {
if (!options.allowVfork)
- logger = makeDefaultLogger();
+ logger = makeSimpleLogger();
try {
#if __linux__
if (options.dieWithParent && prctl(PR_SET_PDEATHSIG, SIGKILL) == -1)
@@ -1216,7 +1199,7 @@ void _interrupted()
/* Block user interrupts while an exception is being handled.
Throwing an exception while another exception is being handled
kills the program! */
- if (!interruptThrown && !std::uncaught_exception()) {
+ if (!interruptThrown && !std::uncaught_exceptions()) {
interruptThrown = true;
throw Interrupted("interrupted by the user");
}
@@ -1314,7 +1297,7 @@ bool statusOk(int status)
}
-bool hasPrefix(const string & s, const string & prefix)
+bool hasPrefix(std::string_view s, std::string_view prefix)
{
return s.compare(0, prefix.size(), prefix) == 0;
}
@@ -1351,7 +1334,7 @@ void ignoreException()
try {
throw;
} catch (std::exception & e) {
- printError(format("error (ignored): %1%") % e.what());
+ printError("error (ignored): %1%", e.what());
}
}
@@ -1408,7 +1391,7 @@ std::string filterANSIEscapes(const std::string & s, bool filterAll, unsigned in
static char base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-string base64Encode(const string & s)
+string base64Encode(std::string_view s)
{
string res;
int data = 0, nbits = 0;
@@ -1429,7 +1412,7 @@ string base64Encode(const string & s)
}
-string base64Decode(const string & s)
+string base64Decode(std::string_view s)
{
bool init = false;
char decode[256];
@@ -1464,17 +1447,6 @@ string base64Decode(const string & s)
}
-void callFailure(const std::function<void(std::exception_ptr exc)> & failure, std::exception_ptr exc)
-{
- try {
- failure(exc);
- } catch (std::exception & e) {
- printError(format("uncaught exception: %s") % e.what());
- abort();
- }
-}
-
-
static Sync<std::pair<unsigned short, unsigned short>> windowSize{{0, 0}};
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index 750957d54..3641daaec 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -1,6 +1,7 @@
#pragma once
#include "types.hh"
+#include "error.hh"
#include "logging.hh"
#include "ansicolor.hh"
@@ -416,7 +417,7 @@ template<class N> bool string2Float(const string & s, N & n)
/* Return true iff `s' starts with `prefix'. */
-bool hasPrefix(const string & s, const string & prefix);
+bool hasPrefix(std::string_view s, std::string_view prefix);
/* Return true iff `s' ends in `suffix'. */
@@ -454,13 +455,12 @@ std::string filterANSIEscapes(const std::string & s,
unsigned int width = std::numeric_limits<unsigned int>::max());
-/* Base::Base64 encoding/decoding. */
-string base64Encode(const string & s);
-string base64Decode(const string & s);
+/* Base64 encoding/decoding. */
+string base64Encode(std::string_view s);
+string base64Decode(std::string_view s);
-/* Get a value for the specified key from an associate container, or a
- default value if the key doesn't exist. */
+/* Get a value for the specified key from an associate container. */
template <class T>
std::optional<typename T::mapped_type> get(const T & map, const typename T::key_type & key)
{