aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/build-remote/build-remote.cc9
-rw-r--r--src/error-demo/error-demo.cc66
-rw-r--r--src/error-demo/local.mk12
-rw-r--r--src/libexpr/attr-path.cc16
-rw-r--r--src/libexpr/attr-set.hh7
-rw-r--r--src/libexpr/eval-inline.hh24
-rw-r--r--src/libexpr/eval.cc114
-rw-r--r--src/libexpr/lexer.l4
-rw-r--r--src/libexpr/nixexpr.cc8
-rw-r--r--src/libexpr/nixexpr.hh8
-rw-r--r--src/libexpr/parser.y74
-rw-r--r--src/libexpr/primops.cc338
-rw-r--r--src/libexpr/primops/context.cc18
-rw-r--r--src/libexpr/primops/fetchGit.cc12
-rw-r--r--src/libexpr/primops/fetchMercurial.cc12
-rw-r--r--src/libexpr/primops/fetchTree.cc23
-rw-r--r--src/libexpr/primops/fromTOML.cc6
-rw-r--r--src/libexpr/value-to-json.cc4
-rw-r--r--src/libmain/progress-bar.cc10
-rw-r--r--src/libmain/shared.cc19
-rw-r--r--src/libmain/shared.hh6
-rw-r--r--src/libmain/stack.cc2
-rw-r--r--src/libstore/binary-cache-store.cc10
-rw-r--r--src/libstore/build.cc209
-rw-r--r--src/libstore/builtins/buildenv.cc22
-rw-r--r--src/libstore/builtins/fetchurl.cc4
-rw-r--r--src/libstore/daemon.cc14
-rw-r--r--src/libstore/derivations.cc6
-rw-r--r--src/libstore/filetransfer.cc12
-rw-r--r--src/libstore/filetransfer.hh5
-rw-r--r--src/libstore/gc.cc63
-rw-r--r--src/libstore/local-binary-cache-store.cc2
-rw-r--r--src/libstore/local-fs-store.cc6
-rw-r--r--src/libstore/local-store.cc106
-rw-r--r--src/libstore/nar-accessor.cc6
-rw-r--r--src/libstore/nar-info.cc2
-rw-r--r--src/libstore/optimise-store.cc33
-rw-r--r--src/libstore/pathlocks.cc11
-rw-r--r--src/libstore/profiles.cc8
-rw-r--r--src/libstore/references.cc2
-rw-r--r--src/libstore/remote-fs-accessor.cc2
-rw-r--r--src/libstore/remote-store.cc6
-rw-r--r--src/libstore/s3-binary-cache-store.cc12
-rw-r--r--src/libstore/sqlite.cc7
-rw-r--r--src/libstore/sqlite.hh2
-rw-r--r--src/libstore/store-api.cc10
-rw-r--r--src/libutil/affinity.cc19
-rw-r--r--src/libutil/archive.cc19
-rw-r--r--src/libutil/args.cc4
-rw-r--r--src/libutil/compression.cc2
-rw-r--r--src/libutil/error.cc247
-rw-r--r--src/libutil/error.hh220
-rw-r--r--src/libutil/fmt.hh139
-rw-r--r--src/libutil/logging.cc34
-rw-r--r--src/libutil/logging.hh40
-rw-r--r--src/libutil/serialise.cc5
-rw-r--r--src/libutil/tests/local.mk2
-rw-r--r--src/libutil/tests/logging.cc255
-rw-r--r--src/libutil/types.hh141
-rw-r--r--src/libutil/url.hh2
-rw-r--r--src/libutil/util.cc92
-rw-r--r--src/libutil/util.hh1
-rwxr-xr-xsrc/nix-build/nix-build.cc7
-rwxr-xr-xsrc/nix-channel/nix-channel.cc10
-rw-r--r--src/nix-daemon/nix-daemon.cc48
-rw-r--r--src/nix-env/nix-env.cc75
-rw-r--r--src/nix-env/user-env.cc2
-rw-r--r--src/nix-instantiate/nix-instantiate.cc4
-rw-r--r--src/nix-prefetch-url/nix-prefetch-url.cc10
-rw-r--r--src/nix-store/nix-store.cc52
-rw-r--r--src/nix/cat.cc4
-rw-r--r--src/nix/hash.cc2
-rw-r--r--src/nix/ls.cc2
-rw-r--r--src/nix/make-content-addressable.cc2
-rw-r--r--src/nix/repl.cc8
-rw-r--r--src/nix/run.cc4
-rw-r--r--src/nix/upgrade-nix.cc8
-rw-r--r--src/nix/verify.cc23
-rw-r--r--src/resolve-system-dependencies/resolve-system-dependencies.cc20
79 files changed, 1850 insertions, 1005 deletions
diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc
index d2ea6c956..49b35a4e9 100644
--- a/src/build-remote/build-remote.cc
+++ b/src/build-remote/build-remote.cc
@@ -200,9 +200,12 @@ static int _main(int argc, char * * argv)
} catch (std::exception & e) {
auto msg = chomp(drainFD(5, false));
- printError("cannot build on '%s': %s%s",
- bestMachine->storeUri, e.what(),
- (msg.empty() ? "" : ": " + msg));
+ logError({
+ .name = "Remote build",
+ .hint = hintfmt("cannot build on '%s': %s%s",
+ bestMachine->storeUri, e.what(),
+ (msg.empty() ? "" : ": " + msg))
+ });
bestMachine->enabled = false;
continue;
}
diff --git a/src/error-demo/error-demo.cc b/src/error-demo/error-demo.cc
deleted file mode 100644
index a9ff6057c..000000000
--- a/src/error-demo/error-demo.cc
+++ /dev/null
@@ -1,66 +0,0 @@
-#include "error.hh"
-#include "nixexpr.hh"
-
-#include <iostream>
-#include <optional>
-
-int main()
-{
- using namespace nix;
-
- // In each program where errors occur, this has to be set.
- ErrorInfo::programName = std::optional("error-demo");
-
- // Error in a program; no hint and no nix code.
- printErrorInfo(
- ErrorInfo { .level = elError,
- .name = "name",
- .description = "error description",
- });
-
- // Warning with name, description, and hint.
- // The hintfmt function makes all the substituted text yellow.
- printErrorInfo(
- ErrorInfo { .level = elWarning,
- .name = "name",
- .description = "error description",
- .hint = std::optional(
- hintfmt("there was a %1%", "warning")),
- });
-
-
- // Warning with nix file, line number, column, and the lines of
- // code where a warning occurred.
- SymbolTable testTable;
- auto problem_file = testTable.create("myfile.nix");
-
- printErrorInfo(
- ErrorInfo{
- .level = elWarning,
- .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
- }});
-
- // Error with previous and next lines of code.
- printErrorInfo(
- ErrorInfo{
- .level = elError,
- .name = "error name",
- .description = "error description",
- .hint = hintfmt("this hint has %1% templated %2%!!", "yellow", "values"),
- .nixCode = NixCode {
- .errPos = Pos(problem_file, 40, 13),
- .prevLineOfCode = std::optional("previous line of code"),
- .errLineOfCode = "this is the problem line of code",
- .nextLineOfCode = std::optional("next line of code"),
- }});
-
-
- return 0;
-}
diff --git a/src/error-demo/local.mk b/src/error-demo/local.mk
deleted file mode 100644
index 2c528490a..000000000
--- a/src/error-demo/local.mk
+++ /dev/null
@@ -1,12 +0,0 @@
-programs += error-demo
-
-error-demo_DIR := $(d)
-
-error-demo_SOURCES := \
- $(wildcard $(d)/*.cc) \
-
-error-demo_CXXFLAGS += -I src/libutil -I src/libexpr
-
-error-demo_LIBS = libutil libexpr
-
-error-demo_LDFLAGS = -pthread $(SODIUM_LIBS) $(EDITLINE_LIBS) $(BOOST_LDFLAGS) -lboost_context -lboost_thread -lboost_system
diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc
index 76d101b98..9a9531a3f 100644
--- a/src/libexpr/attr-path.cc
+++ b/src/libexpr/attr-path.cc
@@ -19,7 +19,7 @@ static Strings parseAttrPath(const string & s)
++i;
while (1) {
if (i == s.end())
- throw Error(format("missing closing quote in selection path '%1%'") % s);
+ throw Error("missing closing quote in selection path '%1%'", s);
if (*i == '"') break;
cur.push_back(*i++);
}
@@ -60,11 +60,11 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attr
if (v->type != tAttrs)
throw TypeError(
- format("the expression selected by the selection path '%1%' should be a set but is %2%")
- % attrPath % showType(*v));
-
+ "the expression selected by the selection path '%1%' should be a set but is %2%",
+ attrPath,
+ showType(*v));
if (attr.empty())
- throw Error(format("empty attribute name in selection path '%1%'") % attrPath);
+ throw Error("empty attribute name in selection path '%1%'", attrPath);
Bindings::iterator a = v->attrs->find(state.symbols.create(attr));
if (a == v->attrs->end())
@@ -77,9 +77,9 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attr
if (!v->isList())
throw TypeError(
- format("the expression selected by the selection path '%1%' should be a list but is %2%")
- % attrPath % showType(*v));
-
+ "the expression selected by the selection path '%1%' should be a list but is %2%",
+ attrPath,
+ showType(*v));
if (attrIndex >= v->listSize())
throw AttrPathNotFound("list index %1% in selection path '%2%' is out of range", attrIndex, attrPath);
diff --git a/src/libexpr/attr-set.hh b/src/libexpr/attr-set.hh
index 118c7bd5d..f5651891f 100644
--- a/src/libexpr/attr-set.hh
+++ b/src/libexpr/attr-set.hh
@@ -76,7 +76,12 @@ public:
{
auto a = get(name);
if (!a)
- throw Error("attribute '%s' missing, at %s", name, pos);
+ throw Error(
+ ErrorInfo {
+ .hint = hintfmt("attribute '%s' missing", name),
+ .nixCode = NixCode { .errPos = pos }
+ });
+
return *a;
}
diff --git a/src/libexpr/eval-inline.hh b/src/libexpr/eval-inline.hh
index 942cda1ea..eee49e02e 100644
--- a/src/libexpr/eval-inline.hh
+++ b/src/libexpr/eval-inline.hh
@@ -7,20 +7,28 @@
namespace nix {
-LocalNoInlineNoReturn(void throwEvalError(const char * s, const Pos & pos))
+LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s))
{
- throw EvalError(format(s) % pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt(s),
+ .nixCode = NixCode { .errPos = pos }
+ });
}
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v))
{
- throw TypeError(format(s) % showType(v));
+ throw TypeError(s, showType(v));
}
-LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v, const Pos & pos))
+LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const Value & v))
{
- throw TypeError(format(s) % showType(v) % pos);
+ throw TypeError(
+ ErrorInfo {
+ .hint = hintfmt(s, showType(v)),
+ .nixCode = NixCode { .errPos = pos }
+ });
}
@@ -43,7 +51,7 @@ void EvalState::forceValue(Value & v, const Pos & pos)
else if (v.type == tApp)
callFunction(*v.app.left, *v.app.right, v, noPos);
else if (v.type == tBlackhole)
- throwEvalError("infinite recursion encountered, at %1%", pos);
+ throwEvalError(pos, "infinite recursion encountered");
}
@@ -59,7 +67,7 @@ inline void EvalState::forceAttrs(Value & v, const Pos & pos)
{
forceValue(v, pos);
if (v.type != tAttrs)
- throwTypeError("value is %1% while a set was expected, at %2%", v, pos);
+ throwTypeError(pos, "value is %1% while a set was expected", v);
}
@@ -75,7 +83,7 @@ inline void EvalState::forceList(Value & v, const Pos & pos)
{
forceValue(v, pos);
if (!v.isList())
- throwTypeError("value is %1% while a list was expected, at %2%", v, pos);
+ throwTypeError(pos, "value is %1% while a list was expected", v);
}
/* Note: Various places expect the allocated memory to be zeroed. */
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 82eb1582e..7bf25ea17 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -501,52 +501,81 @@ Value & EvalState::getBuiltin(const string & name)
LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2))
{
- throw EvalError(format(s) % s2);
+ throw EvalError(s, s2);
}
-LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, const Pos & pos))
+LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2))
{
- throw EvalError(format(s) % s2 % pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt(s, s2),
+ .nixCode = NixCode { .errPos = pos }
+ });
}
LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, const string & s3))
{
- throw EvalError(format(s) % s2 % s3);
+ throw EvalError(s, s2, s3);
}
-LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, const string & s3, const Pos & pos))
+LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2, const string & s3))
{
- throw EvalError(format(s) % s2 % s3 % pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt(s, s2, s3),
+ .nixCode = NixCode { .errPos = pos }
+ });
}
-LocalNoInlineNoReturn(void throwEvalError(const char * s, const Symbol & sym, const Pos & p1, const Pos & p2))
+LocalNoInlineNoReturn(void throwEvalError(const Pos & p1, const char * s, const Symbol & sym, const Pos & p2))
{
- throw EvalError(format(s) % sym % p1 % p2);
+ // p1 is where the error occurred; p2 is a position mentioned in the message.
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt(s, sym, p2),
+ .nixCode = NixCode { .errPos = p1 }
+ });
}
-LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos))
+LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s))
{
- throw TypeError(format(s) % pos);
+ throw TypeError(
+ ErrorInfo {
+ .hint = hintfmt(s),
+ .nixCode = NixCode { .errPos = pos }
+ });
}
LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s1))
{
- throw TypeError(format(s) % s1);
+ throw TypeError(s, s1);
}
-LocalNoInlineNoReturn(void throwTypeError(const char * s, const ExprLambda & fun, const Symbol & s2, const Pos & pos))
+LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const ExprLambda & fun, const Symbol & s2))
{
- throw TypeError(format(s) % fun.showNamePos() % s2 % pos);
+ throw TypeError(
+ ErrorInfo {
+ .hint = hintfmt(s, fun.showNamePos(), s2),
+ .nixCode = NixCode { .errPos = pos }
+ });
}
-LocalNoInlineNoReturn(void throwAssertionError(const char * s, const string & s1, const Pos & pos))
+LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const string & s1))
{
- throw AssertionError(format(s) % s1 % pos);
+ throw AssertionError(
+ ErrorInfo {
+ .hint = hintfmt(s, s1),
+ .nixCode = NixCode { .errPos = pos }
+ });
}
-LocalNoInlineNoReturn(void throwUndefinedVarError(const char * s, const string & s1, const Pos & pos))
+LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char * s, const string & s1))
{
- throw UndefinedVarError(format(s) % s1 % pos);
+ throw UndefinedVarError(
+ ErrorInfo {
+ .hint = hintfmt(s, s1),
+ .nixCode = NixCode { .errPos = pos }
+ });
}
LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2))
@@ -614,7 +643,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
return j->value;
}
if (!env->prevWith)
- throwUndefinedVarError("undefined variable '%1%' at %2%", var.name, var.pos);
+ throwUndefinedVarError(var.pos, "undefined variable '%1%'", var.name);
for (size_t l = env->prevWith; l; --l, env = env->up) ;
}
}
@@ -812,7 +841,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos)
Value v;
e->eval(*this, env, v);
if (v.type != tBool)
- throwTypeError("value is %1% while a Boolean was expected, at %2%", v, pos);
+ throwTypeError(pos, "value is %1% while a Boolean was expected", v);
return v.boolean;
}
@@ -926,7 +955,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
Symbol nameSym = state.symbols.create(nameVal.string.s);
Bindings::iterator j = v.attrs->find(nameSym);
if (j != v.attrs->end())
- throwEvalError("dynamic attribute '%1%' at %2% already defined at %3%", nameSym, i.pos, *j->pos);
+ throwEvalError(i.pos, "dynamic attribute '%1%' already defined at %2%", nameSym, *j->pos);
i.valueExpr->setName(nameSym);
/* Keep sorted order so find can catch duplicates */
@@ -1014,7 +1043,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
} else {
state.forceAttrs(*vAttrs, pos);
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
- throwEvalError("attribute '%1%' missing, at %2%", name, pos);
+ throwEvalError(pos, "attribute '%1%' missing", name);
}
vAttrs = j->value;
pos2 = j->pos;
@@ -1140,7 +1169,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
}
if (fun.type != tLambda)
- throwTypeError("attempt to call something which is not a function but %1%, at %2%", fun, pos);
+ throwTypeError(pos, "attempt to call something which is not a function but %1%", fun);
ExprLambda & lambda(*fun.lambda.fun);
@@ -1168,8 +1197,8 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
for (auto & i : lambda.formals->formals) {
Bindings::iterator j = arg.attrs->find(i.name);
if (j == arg.attrs->end()) {
- if (!i.def) throwTypeError("%1% called without required argument '%2%', at %3%",
- lambda, i.name, pos);
+ if (!i.def) throwTypeError(pos, "%1% called without required argument '%2%'",
+ lambda, i.name);
env2.values[displ++] = i.def->maybeThunk(*this, env2);
} else {
attrsUsed++;
@@ -1184,7 +1213,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
user. */
for (auto & i : *arg.attrs)
if (lambda.formals->argNames.find(i.name) == lambda.formals->argNames.end())
- throwTypeError("%1% called with unexpected argument '%2%', at %3%", lambda, i.name, pos);
+ throwTypeError(pos, "%1% called with unexpected argument '%2%'", lambda, i.name);
abort(); // can't happen
}
}
@@ -1273,7 +1302,7 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v)
if (!state.evalBool(env, cond, pos)) {
std::ostringstream out;
cond->show(out);
- throwAssertionError("assertion '%1%' failed at %2%", out.str(), pos);
+ throwAssertionError(pos, "assertion '%1%' failed at %2%", out.str());
}
body->eval(state, env, v);
}
@@ -1425,14 +1454,14 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
nf = n;
nf += vTmp.fpoint;
} else
- throwEvalError("cannot add %1% to an integer, at %2%", showType(vTmp), pos);
+ throwEvalError(pos, "cannot add %1% to an integer", showType(vTmp));
} else if (firstType == tFloat) {
if (vTmp.type == tInt) {
nf += vTmp.integer;
} else if (vTmp.type == tFloat) {
nf += vTmp.fpoint;
} else
- throwEvalError("cannot add %1% to a float, at %2%", showType(vTmp), pos);
+ throwEvalError(pos, "cannot add %1% to a float", showType(vTmp));
} else
s << state.coerceToString(pos, vTmp, context, false, firstType == tString);
}
@@ -1443,7 +1472,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
mkFloat(v, nf);
else if (firstType == tPath) {
if (!context.empty())
- throwEvalError("a string that refers to a store path cannot be appended to a path, at %1%", pos);
+ throwEvalError(pos, "a string that refers to a store path cannot be appended to a path");
auto path = canonPath(s.str());
mkPath(v, path.c_str());
} else
@@ -1492,7 +1521,7 @@ NixInt EvalState::forceInt(Value & v, const Pos & pos)
{
forceValue(v, pos);
if (v.type != tInt)
- throwTypeError("value is %1% while an integer was expected, at %2%", v, pos);
+ throwTypeError(pos, "value is %1% while an integer was expected", v);
return v.integer;
}
@@ -1503,7 +1532,7 @@ NixFloat EvalState::forceFloat(Value & v, const Pos & pos)
if (v.type == tInt)
return v.integer;
else if (v.type != tFloat)
- throwTypeError("value is %1% while a float was expected, at %2%", v, pos);
+ throwTypeError(pos, "value is %1% while a float was expected", v);
return v.fpoint;
}
@@ -1512,7 +1541,7 @@ bool EvalState::forceBool(Value & v, const Pos & pos)
{
forceValue(v, pos);
if (v.type != tBool)
- throwTypeError("value is %1% while a Boolean was expected, at %2%", v, pos);
+ throwTypeError(pos, "value is %1% while a Boolean was expected", v);
return v.boolean;
}
@@ -1527,7 +1556,7 @@ void EvalState::forceFunction(Value & v, const Pos & pos)
{
forceValue(v, pos);
if (v.type != tLambda && v.type != tPrimOp && v.type != tPrimOpApp && !isFunctor(v))
- throwTypeError("value is %1% while a function was expected, at %2%", v, pos);
+ throwTypeError(pos, "value is %1% while a function was expected", v);
}
@@ -1536,7 +1565,7 @@ string EvalState::forceString(Value & v, const Pos & pos)
forceValue(v, pos);
if (v.type != tString) {
if (pos)
- throwTypeError("value is %1% while a string was expected, at %2%", v, pos);
+ throwTypeError(pos, "value is %1% while a string was expected", v);
else
throwTypeError("value is %1% while a string was expected", v);
}
@@ -1565,8 +1594,8 @@ string EvalState::forceStringNoCtx(Value & v, const Pos & pos)
string s = forceString(v, pos);
if (v.string.context) {
if (pos)
- throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%'), at %3%",
- v.string.s, v.string.context[0], pos);
+ throwEvalError(pos, "the string '%1%' is not allowed to refer to a store path (such as '%2%')",
+ v.string.s, v.string.context[0]);
else
throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%')",
v.string.s, v.string.context[0]);
@@ -1622,7 +1651,7 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
return *maybeString;
}
auto i = v.attrs->find(sOutPath);
- if (i == v.attrs->end()) throwTypeError("cannot coerce a set to a string, at %1%", pos);
+ if (i == v.attrs->end()) throwTypeError(pos, "cannot coerce a set to a string");
return coerceToString(pos, *i->value, context, coerceMore, copyToStore);
}
@@ -1653,7 +1682,7 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
}
}
- throwTypeError("cannot coerce %1% to a string, at %2%", v, pos);
+ throwTypeError(pos, "cannot coerce %1% to a string", v);
}
@@ -1684,7 +1713,7 @@ Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
{
string path = coerceToString(pos, v, context, false, false);
if (path == "" || path[0] != '/')
- throwEvalError("string '%1%' doesn't represent an absolute path, at %2%", path, pos);
+ throwEvalError(pos, "string '%1%' doesn't represent an absolute path", path);
return path;
}
@@ -1891,8 +1920,11 @@ void EvalState::printStats()
string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const
{
- throw TypeError(format("cannot coerce %1% to a string, at %2%") %
- showType() % pos);
+ throw TypeError(
+ ErrorInfo {
+ .hint = hintfmt("cannot coerce %1% to a string", showType()),
+ .nixCode = NixCode { .errPos = pos }
+ });
}
diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l
index c34e5c383..85376a08f 100644
--- a/src/libexpr/lexer.l
+++ b/src/libexpr/lexer.l
@@ -127,14 +127,14 @@ or { return OR_KW; }
try {
yylval->n = boost::lexical_cast<int64_t>(yytext);
} catch (const boost::bad_lexical_cast &) {
- throw ParseError(format("invalid integer '%1%'") % yytext);
+ throw ParseError("invalid integer '%1%'", yytext);
}
return INT;
}
{FLOAT} { errno = 0;
yylval->nf = strtod(yytext, 0);
if (errno != 0)
- throw ParseError(format("invalid float '%1%'") % yytext);
+ throw ParseError("invalid float '%1%'", yytext);
return FLOAT;
}
diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc
index 63cbef1dd..91a508305 100644
--- a/src/libexpr/nixexpr.cc
+++ b/src/libexpr/nixexpr.cc
@@ -267,8 +267,12 @@ void ExprVar::bindVars(const StaticEnv & env)
/* Otherwise, the variable must be obtained from the nearest
enclosing `with'. If there is no `with', then we can issue an
"undefined variable" error now. */
- if (withLevel == -1) throw UndefinedVarError(format("undefined variable '%1%' at %2%") % name % pos);
-
+ if (withLevel == -1)
+ throw UndefinedVarError(
+ ErrorInfo {
+ .hint = hintfmt("undefined variable '%1%'", name),
+ .nixCode = NixCode { .errPos = pos }
+ });
fromWith = true;
this->level = withLevel;
}
diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh
index 25798cac6..47d0e85ec 100644
--- a/src/libexpr/nixexpr.hh
+++ b/src/libexpr/nixexpr.hh
@@ -2,6 +2,7 @@
#include "value.hh"
#include "symbol-table.hh"
+#include "error.hh"
#include <map>
@@ -235,8 +236,11 @@ struct ExprLambda : Expr
: pos(pos), arg(arg), matchAttrs(matchAttrs), formals(formals), body(body)
{
if (!arg.empty() && formals && formals->argNames.find(arg) != formals->argNames.end())
- throw ParseError(format("duplicate formal function argument '%1%' at %2%")
- % arg % pos);
+ throw ParseError(
+ ErrorInfo {
+ .hint = hintfmt("duplicate formal function argument '%1%'", arg),
+ .nixCode = NixCode { .errPos = pos }
+ });
};
void setName(Symbol & name);
string showNamePos() const;
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index 1993fa6c1..0417a3c21 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -31,7 +31,7 @@ namespace nix {
Expr * result;
Path basePath;
Symbol path;
- string error;
+ ErrorInfo error;
Symbol sLetBody;
ParseData(EvalState & state)
: state(state)
@@ -64,15 +64,23 @@ namespace nix {
static void dupAttr(const AttrPath & attrPath, const Pos & pos, const Pos & prevPos)
{
- throw ParseError(format("attribute '%1%' at %2% already defined at %3%")
- % showAttrPath(attrPath) % pos % prevPos);
+ throw ParseError(
+ ErrorInfo {
+ .hint = hintfmt("attribute '%1%' already defined at %2%",
+ showAttrPath(attrPath), prevPos),
+ .nixCode = NixCode { .errPos = pos },
+ });
}
static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos)
{
- throw ParseError(format("attribute '%1%' at %2% already defined at %3%")
- % attr % pos % prevPos);
+ throw ParseError(
+ ErrorInfo {
+ .hint = hintfmt("attribute '%1%' already defined at %2%",
+ attr, prevPos),
+ .nixCode = NixCode { .errPos = pos },
+ });
}
@@ -140,8 +148,12 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
{
if (!formals->argNames.insert(formal.name).second)
- throw ParseError(format("duplicate formal function argument '%1%' at %2%")
- % formal.name % pos);
+ throw ParseError(
+ ErrorInfo {
+ .hint = hintfmt("duplicate formal function argument '%1%'",
+ formal.name),
+ .nixCode = NixCode { .errPos = pos },
+ });
formals->formals.push_front(formal);
}
@@ -249,8 +261,10 @@ static inline Pos makeCurPos(const YYLTYPE & loc, ParseData * data)
void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * error)
{
- data->error = (format("%1%, at %2%")
- % error % makeCurPos(*loc, data)).str();
+ data->error = ErrorInfo {
+ .hint = hintfmt(error),
+ .nixCode = NixCode { .errPos = makeCurPos(*loc, data) }
+ };
}
@@ -327,8 +341,11 @@ expr_function
{ $$ = new ExprWith(CUR_POS, $2, $4); }
| LET binds IN expr_function
{ if (!$2->dynamicAttrs.empty())
- throw ParseError(format("dynamic attributes not allowed in let at %1%")
- % CUR_POS);
+ throw ParseError(
+ ErrorInfo {
+ .hint = hintfmt("dynamic attributes not allowed in let"),
+ .nixCode = NixCode { .errPos = CUR_POS },
+ });
$$ = new ExprLet($2, $4);
}
| expr_if
@@ -405,7 +422,11 @@ expr_simple
| URI {
static bool noURLLiterals = settings.isExperimentalFeatureEnabled("no-url-literals");
if (noURLLiterals)
- throw ParseError("URL literals are disabled, at %s", CUR_POS);
+ throw ParseError(
+ ErrorInfo {
+ .hint = hintfmt("URL literals are disabled"),
+ .nixCode = NixCode { .errPos = CUR_POS }
+ });
$$ = new ExprString(data->symbols.create($1));
}
| '(' expr ')' { $$ = $2; }
@@ -475,8 +496,11 @@ attrs
$$->push_back(AttrName(str->s));
delete str;
} else
- throw ParseError(format("dynamic attributes not allowed in inherit at %1%")
- % makeCurPos(@2, data));
+ throw ParseError(
+ ErrorInfo {
+ .hint = hintfmt("dynamic attributes not allowed in inherit"),
+ .nixCode = NixCode { .errPos = makeCurPos(@2, data) },
+ });
}
| { $$ = new AttrPath; }
;
@@ -671,11 +695,11 @@ Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos
Path res = r.second + suffix;
if (pathExists(res)) return canonPath(res);
}
- format f = format(
- "file '%1%' was not found in the Nix search path (add it using $NIX_PATH or -I)"
- + string(pos ? ", at %2%" : ""));
- f.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit);
- throw ThrownError(f % path % pos);
+ throw ThrownError(
+ ErrorInfo {
+ .hint = hintfmt("file '%1%' was not found in the Nix search path (add it using $NIX_PATH or -I)", path),
+ .nixCode = NixCode { .errPos = pos }
+ });
}
@@ -691,7 +715,11 @@ std::pair<bool, std::string> EvalState::resolveSearchPathElem(const SearchPathEl
res = { true, store->toRealPath(fetchers::downloadTarball(
store, resolveUri(elem.second), "source", false).storePath) };
} catch (FileTransferError & e) {
- printError(format("warning: Nix search path entry '%1%' cannot be downloaded, ignoring") % elem.second);
+ logWarning(
+ ErrorInfo {
+ .name = "Entry download",
+ .hint = hintfmt("Nix search path entry '%1%' cannot be downloaded, ignoring", elem.second)
+ });
res = { false, "" };
}
} else {
@@ -699,7 +727,11 @@ std::pair<bool, std::string> EvalState::resolveSearchPathElem(const SearchPathEl
if (pathExists(path))
res = { true, path };
else {
- printError(format("warning: Nix search path entry '%1%' does not exist, ignoring") % elem.second);
+ logWarning(
+ ErrorInfo {
+ .name = "Entry not found",
+ .hint = hintfmt("warning: Nix search path entry '%1%' does not exist, ignoring", elem.second)
+ });
res = { false, "" };
}
}
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index eb3116d81..23ab7dec6 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -93,8 +93,12 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
try {
state.realiseContext(context);
} catch (InvalidPathError & e) {
- throw EvalError(format("cannot import '%1%', since path '%2%' is not valid, at %3%")
- % path % e.path % pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("cannot import '%1%', since path '%2%' is not valid",
+ path, e.path),
+ .nixCode = NixCode { .errPos = pos }
+ });
}
Path realPath = state.checkSourcePath(state.toRealPath(path, context));
@@ -170,8 +174,13 @@ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value
try {
state.realiseContext(context);
} catch (InvalidPathError & e) {
- throw EvalError(format("cannot import '%1%', since path '%2%' is not valid, at %3%")
- % path % e.path % pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt(
+ "cannot import '%1%', since path '%2%' is not valid",
+ path, e.path),
+ .nixCode = NixCode { .errPos = pos }
+ });
}
path = state.checkSourcePath(path);
@@ -180,17 +189,17 @@ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value
void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
if (!handle)
- throw EvalError(format("could not open '%1%': %2%") % path % dlerror());
+ throw EvalError("could not open '%1%': %2%", path, dlerror());
dlerror();
ValueInitializer func = (ValueInitializer) dlsym(handle, sym.c_str());
if(!func) {
char *message = dlerror();
if (message)
- throw EvalError(format("could not load symbol '%1%' from '%2%': %3%") % sym % path % message);
+ throw EvalError("could not load symbol '%1%' from '%2%': %3%", sym, path, message);
else
- throw EvalError(format("symbol '%1%' from '%2%' resolved to NULL when a function pointer was expected")
- % sym % path);
+ throw EvalError("symbol '%1%' from '%2%' resolved to NULL when a function pointer was expected",
+ sym, path);
}
(func)(state, v);
@@ -206,7 +215,11 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
auto elems = args[0]->listElems();
auto count = args[0]->listSize();
if (count == 0) {
- throw EvalError(format("at least one argument to 'exec' required, at %1%") % pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("at least one argument to 'exec' required"),
+ .nixCode = NixCode { .errPos = pos }
+ });
}
PathSet context;
auto program = state.coerceToString(pos, *elems[0], context, false, false);
@@ -217,22 +230,25 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
try {
state.realiseContext(context);
} catch (InvalidPathError & e) {
- throw EvalError(format("cannot execute '%1%', since path '%2%' is not valid, at %3%")
- % program % e.path % pos);
- }
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("cannot execute '%1%', since path '%2%' is not valid",
+ program, e.path),
+ .nixCode = NixCode { .errPos = pos }
+ });}
auto output = runProgram(program, true, commandArgs);
Expr * parsed;
try {
parsed = state.parseExprFromString(output, pos.file);
} catch (Error & e) {
- e.addPrefix(format("While parsing the output from '%1%', at %2%\n") % program % pos);
+ e.addPrefix(fmt("While parsing the output from '%1%', at %2%\n", program, pos));
throw;
}
try {
state.eval(parsed, v);
} catch (Error & e) {
- e.addPrefix(format("While evaluating the output from '%1%', at %2%\n") % program % pos);
+ e.addPrefix(fmt("While evaluating the output from '%1%', at %2%\n", program, pos));
throw;
}
}
@@ -338,7 +354,7 @@ struct CompareValues
if (v1->type == tInt && v2->type == tFloat)
return v1->integer < v2->fpoint;
if (v1->type != v2->type)
- throw EvalError(format("cannot compare %1% with %2%") % showType(*v1) % showType(*v2));
+ throw EvalError("cannot compare %1% with %2%", showType(*v1), showType(*v2));
switch (v1->type) {
case tInt:
return v1->integer < v2->integer;
@@ -349,7 +365,7 @@ struct CompareValues
case tPath:
return strcmp(v1->path, v2->path) < 0;
default:
- throw EvalError(format("cannot compare %1% with %2%") % showType(*v1) % showType(*v2));
+ throw EvalError("cannot compare %1% with %2%", showType(*v1), showType(*v2));
}
}
};
@@ -370,7 +386,11 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
Bindings::iterator startSet =
args[0]->attrs->find(state.symbols.create("startSet"));
if (startSet == args[0]->attrs->end())
- throw EvalError(format("attribute 'startSet' required, at %1%") % pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("attribute 'startSet' required"),
+ .nixCode = NixCode { .errPos = pos }
+ });
state.forceList(*startSet->value, pos);
ValueList workSet;
@@ -381,7 +401,11 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
Bindings::iterator op =
args[0]->attrs->find(state.symbols.create("operator"));
if (op == args[0]->attrs->end())
- throw EvalError(format("attribute 'operator' required, at %1%") % pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("attribute 'operator' required"),
+ .nixCode = NixCode { .errPos = pos }
+ });
state.forceValue(*op->value, pos);
/* Construct the closure by applying the operator to element of
@@ -400,7 +424,11 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
Bindings::iterator key =
e->attrs->find(state.symbols.create("key"));
if (key == e->attrs->end())
- throw EvalError(format("attribute 'key' required, at %1%") % pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("attribute 'key' required"),
+ .nixCode = NixCode { .errPos = pos }
+ });
state.forceValue(*key->value, pos);
if (!doneKeys.insert(key->value).second) continue;
@@ -430,7 +458,7 @@ static void prim_abort(EvalState & state, const Pos & pos, Value * * args, Value
{
PathSet context;
string s = state.coerceToString(pos, *args[0], context);
- throw Abort(format("evaluation aborted with the following error message: '%1%'") % s);
+ throw Abort("evaluation aborted with the following error message: '%1%'", s);
}
@@ -505,9 +533,9 @@ static void prim_trace(EvalState & state, const Pos & pos, Value * * args, Value
{
state.forceValue(*args[0], pos);
if (args[0]->type == tString)
- printError(format("trace: %1%") % args[0]->string.s);
+ printError("trace: %1%", args[0]->string.s);
else
- printError(format("trace: %1%") % *args[0]);
+ printError("trace: %1%", *args[0]);
state.forceValue(*args[1], pos);
v = *args[1];
}
@@ -532,13 +560,17 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
/* Figure out the name first (for stack backtraces). */
Bindings::iterator attr = args[0]->attrs->find(state.sName);
if (attr == args[0]->attrs->end())
- throw EvalError(format("required attribute 'name' missing, at %1%") % pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("required attribute 'name' missing"),
+ .nixCode = NixCode { .errPos = pos }
+ });
string drvName;
Pos & posDrvName(*attr->pos);
try {
drvName = state.forceStringNoCtx(*attr->value, pos);
} catch (Error & e) {
- e.addPrefix(format("while evaluating the derivation attribute 'name' at %1%:\n") % posDrvName);
+ e.addPrefix(fmt("while evaluating the derivation attribute 'name' at %1%:\n", posDrvName));
throw;
}
@@ -575,25 +607,42 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
auto handleHashMode = [&](const std::string & s) {
if (s == "recursive") ingestionMethod = FileIngestionMethod::Recursive;
else if (s == "flat") ingestionMethod = FileIngestionMethod::Flat;
- else throw EvalError("invalid value '%s' for 'outputHashMode' attribute, at %s", s, posDrvName);
+ else
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("invalid value '%s' for 'outputHashMode' attribute", s),
+ .nixCode = NixCode { .errPos = posDrvName }
+ });
};
auto handleOutputs = [&](const Strings & ss) {
outputs.clear();
for (auto & j : ss) {
if (outputs.find(j) != outputs.end())
- throw EvalError(format("duplicate derivation output '%1%', at %2%") % j % posDrvName);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("duplicate derivation output '%1%'", j),
+ .nixCode = NixCode { .errPos = posDrvName }
+ });
/* !!! Check whether j is a valid attribute
name. */
/* Derivations cannot be named ‘drv’, because
then we'd have an attribute ‘drvPath’ in
the resulting set. */
if (j == "drv")
- throw EvalError(format("invalid derivation output name 'drv', at %1%") % posDrvName);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("invalid derivation output name 'drv'" ),
+ .nixCode = NixCode { .errPos = posDrvName }
+ });
outputs.insert(j);
}
if (outputs.empty())
- throw EvalError(format("derivation cannot have an empty set of outputs, at %1%") % posDrvName);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("derivation cannot have an empty set of outputs"),
+ .nixCode = NixCode { .errPos = posDrvName }
+ });
};
try {
@@ -705,18 +754,35 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
/* Do we have all required attributes? */
if (drv.builder == "")
- throw EvalError(format("required attribute 'builder' missing, at %1%") % posDrvName);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("required attribute 'builder' missing"),
+ .nixCode = NixCode { .errPos = posDrvName }
+ });
+
if (drv.platform == "")
- throw EvalError(format("required attribute 'system' missing, at %1%") % posDrvName);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("required attribute 'system' missing"),
+ .nixCode = NixCode { .errPos = posDrvName }
+ });
/* Check whether the derivation name is valid. */
if (isDerivation(drvName))
- throw EvalError("derivation names are not allowed to end in '%s', at %s", drvExtension, posDrvName);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("derivation names are not allowed to end in '%s'", drvExtension),
+ .nixCode = NixCode { .errPos = posDrvName }
+ });
if (outputHash) {
/* Handle fixed-output derivations. */
if (outputs.size() != 1 || *(outputs.begin()) != "out")
- throw Error(format("multiple outputs are not supported in fixed-output derivations, at %1%") % posDrvName);
+ throw Error(
+ ErrorInfo {
+ .hint = hintfmt("multiple outputs are not supported in fixed-output derivations"),
+ .nixCode = NixCode { .errPos = posDrvName }
+ });
HashType ht = outputHashAlgo.empty() ? htUnknown : parseHashType(outputHashAlgo);
@@ -821,7 +887,11 @@ static void prim_storePath(EvalState & state, const Pos & pos, Value * * args, V
e.g. nix-push does the right thing. */
if (!state.store->isStorePath(path)) path = canonPath(path, true);
if (!state.store->isInStore(path))
- throw EvalError(format("path '%1%' is not in the Nix store, at %2%") % path % pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("path '%1%' is not in the Nix store", path),
+ .nixCode = NixCode { .errPos = pos }
+ });
Path path2 = state.store->toStorePath(path);
if (!settings.readOnlyMode)
state.store->ensurePath(state.store->parseStorePath(path2));
@@ -837,9 +907,13 @@ static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args,
try {
state.realiseContext(context);
} catch (InvalidPathError & e) {
- throw EvalError(format(
- "cannot check the existence of '%1%', since path '%2%' is not valid, at %3%")
- % path % e.path % pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt(
+ "cannot check the existence of '%1%', since path '%2%' is not valid",
+ path, e.path),
+ .nixCode = NixCode { .errPos = pos }
+ });
}
try {
@@ -882,12 +956,16 @@ static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Va
try {
state.realiseContext(context);
} catch (InvalidPathError & e) {
- throw EvalError(format("cannot read '%1%', since path '%2%' is not valid, at %3%")
- % path % e.path % pos);
- }
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("cannot read '%1%', since path '%2%' is not valid"
+ , path, e.path),
+ .nixCode = NixCode { .errPos = pos }
+ });
+ }
string s = readFile(state.checkSourcePath(state.toRealPath(path, context)));
if (s.find((char) 0) != string::npos)
- throw Error(format("the contents of the file '%1%' cannot be represented as a Nix string") % path);
+ throw Error("the contents of the file '%1%' cannot be represented as a Nix string", path);
mkString(v, s.c_str());
}
@@ -911,7 +989,11 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
i = v2.attrs->find(state.symbols.create("path"));
if (i == v2.attrs->end())
- throw EvalError(format("attribute 'path' missing, at %1%") % pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("attribute 'path' missing"),
+ .nixCode = NixCode { .errPos = pos }
+ });
PathSet context;
string path = state.coerceToString(pos, *i->value, context, false, false);
@@ -919,8 +1001,12 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
try {
state.realiseContext(context);
} catch (InvalidPathError & e) {
- throw EvalError(format("cannot find '%1%', since path '%2%' is not valid, at %3%")
- % path % e.path % pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("cannot find '%1%', since path '%2%' is not valid",
+ path, e.path),
+ .nixCode = NixCode { .errPos = pos }
+ });
}
searchPath.emplace_back(prefix, path);
@@ -937,7 +1023,11 @@ static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Va
string type = state.forceStringNoCtx(*args[0], pos);
HashType ht = parseHashType(type);
if (ht == htUnknown)
- throw Error(format("unknown hash type '%1%', at %2%") % type % pos);
+ throw Error(
+ ErrorInfo {
+ .hint = hintfmt("unknown hash type '%1%'", type),
+ .nixCode = NixCode { .errPos = pos }
+ });
PathSet context; // discarded
Path p = state.coerceToPath(pos, *args[1], context);
@@ -953,8 +1043,12 @@ static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Val
try {
state.realiseContext(ctx);
} catch (InvalidPathError & e) {
- throw EvalError(format("cannot read '%1%', since path '%2%' is not valid, at %3%")
- % path % e.path % pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("cannot read '%1%', since path '%2%' is not valid",
+ path, e.path),
+ .nixCode = NixCode { .errPos = pos }
+ });
}
DirEntries entries = readDirectory(state.checkSourcePath(path));
@@ -1024,9 +1118,15 @@ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Valu
for (auto path : context) {
if (path.at(0) != '/')
- throw EvalError(format(
- "in 'toFile': the file named '%1%' must not contain a reference "
- "to a derivation but contains (%2%), at %3%") % name % path % pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt(
+ "in 'toFile': the file named '%1%' must not contain a reference "
+ "to a derivation but contains (%2%)",
+ name,
+ path),
+ .nixCode = NixCode { .errPos = pos }
+ });
refs.insert(state.store->parseStorePath(path));
}
@@ -1094,11 +1194,21 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
PathSet context;
Path path = state.coerceToPath(pos, *args[1], context);
if (!context.empty())
- throw EvalError(format("string '%1%' cannot refer to other paths, at %2%") % path % pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("string '%1%' cannot refer to other paths", path),
+ .nixCode = NixCode { .errPos = pos }
+ });
state.forceValue(*args[0], pos);
if (args[0]->type != tLambda)
- throw TypeError(format("first argument in call to 'filterSource' is not a function but %1%, at %2%") % showType(*args[0]) % pos);
+ throw TypeError(
+ ErrorInfo {
+ .hint = hintfmt(
+ "first argument in call to 'filterSource' is not a function but %1%",
+ showType(*args[0])),
+ .nixCode = NixCode { .errPos = pos }
+ });
addPath(state, pos, std::string(baseNameOf(path)), path, args[0], FileIngestionMethod::Recursive, Hash(), v);
}
@@ -1118,7 +1228,12 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value
PathSet context;
path = state.coerceToPath(*attr.pos, *attr.value, context);
if (!context.empty())
- throw EvalError(format("string '%1%' cannot refer to other paths, at %2%") % path % *attr.pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("string '%1%' cannot refer to other paths",
+ path),
+ .nixCode = NixCode { .errPos = *attr.pos }
+ });
} else if (attr.name == state.sName)
name = state.forceStringNoCtx(*attr.value, *attr.pos);
else if (n == "filter") {
@@ -1129,10 +1244,19 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value
else if (n == "sha256")
expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256);
else
- throw EvalError(format("unsupported argument '%1%' to 'addPath', at %2%") % attr.name % *attr.pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("unsupported argument '%1%' to 'addPath'",
+ attr.name),
+ .nixCode = NixCode { .errPos = *attr.pos }
+ });
}
if (path.empty())
- throw EvalError(format("'path' required, at %1%") % pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("'path' required"),
+ .nixCode = NixCode { .errPos = pos }
+ });
if (name.empty())
name = baseNameOf(path);
@@ -1190,7 +1314,11 @@ void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
// !!! Should we create a symbol here or just do a lookup?
Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr));
if (i == args[1]->attrs->end())
- throw EvalError(format("attribute '%1%' missing, at %2%") % attr % pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("attribute '%1%' missing", attr),
+ .nixCode = NixCode { .errPos = pos }
+ });
// !!! add to stack trace?
if (state.countCalls && i->pos) state.attrSelects[*i->pos]++;
state.forceValue(*i->value, pos);
@@ -1270,15 +1398,22 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args,
Bindings::iterator j = v2.attrs->find(state.sName);
if (j == v2.attrs->end())
- throw TypeError(format("'name' attribute missing in a call to 'listToAttrs', at %1%") % pos);
+ throw TypeError(
+ ErrorInfo {
+ .hint = hintfmt("'name' attribute missing in a call to 'listToAttrs'"),
+ .nixCode = NixCode { .errPos = pos }
+ });
string name = state.forceStringNoCtx(*j->value, pos);
Symbol sym = state.symbols.create(name);
if (seen.insert(sym).second) {
Bindings::iterator j2 = v2.attrs->find(state.symbols.create(state.sValue));
if (j2 == v2.attrs->end())
- throw TypeError(format("'value' attribute missing in a call to 'listToAttrs', at %1%") % pos);
-
+ throw TypeError(
+ ErrorInfo {
+ .hint = hintfmt("'value' attribute missing in a call to 'listToAttrs'"),
+ .nixCode = NixCode { .errPos = pos }
+ });
v.attrs->push_back(Attr(sym, j2->value, j2->pos));
}
}
@@ -1351,7 +1486,11 @@ static void prim_functionArgs(EvalState & state, const Pos & pos, Value * * args
{
state.forceValue(*args[0], pos);
if (args[0]->type != tLambda)
- throw TypeError(format("'functionArgs' requires a function, at %1%") % pos);
+ throw TypeError(
+ ErrorInfo {
+ .hint = hintfmt("'functionArgs' requires a function"),
+ .nixCode = NixCode { .errPos = pos }
+ });
if (!args[0]->lambda.fun->matchAttrs) {
state.mkAttrs(v, 0);
@@ -1404,7 +1543,11 @@ static void elemAt(EvalState & state, const Pos & pos, Value & list, int n, Valu
{
state.forceList(list, pos);
if (n < 0 || (unsigned int) n >= list.listSize())
- throw Error(format("list index %1% is out of bounds, at %2%") % n % pos);
+ throw Error(
+ ErrorInfo {
+ .hint = hintfmt("list index %1% is out of bounds", n),
+ .nixCode = NixCode { .errPos = pos }
+ });
state.forceValue(*list.listElems()[n], pos);
v = *list.listElems()[n];
}
@@ -1431,7 +1574,12 @@ static void prim_tail(EvalState & state, const Pos & pos, Value * * args, Value
{
state.forceList(*args[0], pos);
if (args[0]->listSize() == 0)
- throw Error(format("'tail' called on an empty list, at %1%") % pos);
+ throw Error(
+ ErrorInfo {
+ .hint = hintfmt("'tail' called on an empty list"),
+ .nixCode = NixCode { .errPos = pos }
+ });
+
state.mkList(v, args[0]->listSize() - 1);
for (unsigned int n = 0; n < v.listSize(); ++n)
v.listElems()[n] = args[0]->listElems()[n + 1];
@@ -1572,7 +1720,12 @@ static void prim_genList(EvalState & state, const Pos & pos, Value * * args, Val
auto len = state.forceInt(*args[1], pos);
if (len < 0)
- throw EvalError(format("cannot create list of size %1%, at %2%") % len % pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("cannot create list of size %1%", len),
+ .nixCode = NixCode { .errPos = pos }
+ });
+
state.mkList(v, len);
@@ -1730,7 +1883,12 @@ static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value &
state.forceValue(*args[1], pos);
NixFloat f2 = state.forceFloat(*args[1], pos);
- if (f2 == 0) throw EvalError(format("division by zero, at %1%") % pos);
+ if (f2 == 0)
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("division by zero"),
+ .nixCode = NixCode { .errPos = pos }
+ });
if (args[0]->type == tFloat || args[1]->type == tFloat) {
mkFloat(v, state.forceFloat(*args[0], pos) / state.forceFloat(*args[1], pos));
@@ -1739,7 +1897,12 @@ static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value &
NixInt i2 = state.forceInt(*args[1], pos);
/* Avoid division overflow as it might raise SIGFPE. */
if (i1 == std::numeric_limits<NixInt>::min() && i2 == -1)
- throw EvalError(format("overflow in integer division, at %1%") % pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("overflow in integer division"),
+ .nixCode = NixCode { .errPos = pos }
+ });
+
mkInt(v, i1 / i2);
}
}
@@ -1795,7 +1958,12 @@ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, V
PathSet context;
string s = state.coerceToString(pos, *args[2], context);
- if (start < 0) throw EvalError(format("negative start position in 'substring', at %1%") % pos);
+ if (start < 0)
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("negative start position in 'substring'"),
+ .nixCode = NixCode { .errPos = pos }
+ });
mkString(v, (unsigned int) start >= s.size() ? "" : string(s, start, len), context);
}
@@ -1815,7 +1983,11 @@ static void prim_hashString(EvalState & state, const Pos & pos, Value * * args,
string type = state.forceStringNoCtx(*args[0], pos);
HashType ht = parseHashType(type);
if (ht == htUnknown)
- throw Error(format("unknown hash type '%1%', at %2%") % type % pos);
+ throw Error(
+ ErrorInfo {
+ .hint = hintfmt("unknown hash type '%1%'", type),
+ .nixCode = NixCode { .errPos = pos }
+ });
PathSet context; // discarded
string s = state.forceString(*args[1], context, pos);
@@ -1857,10 +2029,18 @@ void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v)
} catch (std::regex_error &e) {
if (e.code() == std::regex_constants::error_space) {
- // limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
- throw EvalError("memory limit exceeded by regular expression '%s', at %s", re, pos);
+ // limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("memory limit exceeded by regular expression '%s'", re),
+ .nixCode = NixCode { .errPos = pos }
+ });
} else {
- throw EvalError("invalid regular expression '%s', at %s", re, pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("invalid regular expression '%s'", re),
+ .nixCode = NixCode { .errPos = pos }
+ });
}
}
}
@@ -1924,10 +2104,18 @@ static void prim_split(EvalState & state, const Pos & pos, Value * * args, Value
} catch (std::regex_error &e) {
if (e.code() == std::regex_constants::error_space) {
- // limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
- throw EvalError("memory limit exceeded by regular expression '%s', at %s", re, pos);
+ // limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("memory limit exceeded by regular expression '%s'", re),
+ .nixCode = NixCode { .errPos = pos }
+ });
} else {
- throw EvalError("invalid regular expression '%s', at %s", re, pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("invalid regular expression '%s'", re),
+ .nixCode = NixCode { .errPos = pos }
+ });
}
}
}
@@ -1958,7 +2146,11 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar
state.forceList(*args[0], pos);
state.forceList(*args[1], pos);
if (args[0]->listSize() != args[1]->listSize())
- throw EvalError(format("'from' and 'to' arguments to 'replaceStrings' have different lengths, at %1%") % pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("'from' and 'to' arguments to 'replaceStrings' have different lengths"),
+ .nixCode = NixCode { .errPos = pos }
+ });
vector<string> from;
from.reserve(args[0]->listSize());
diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc
index 94fa0158c..7f895fc01 100644
--- a/src/libexpr/primops/context.cc
+++ b/src/libexpr/primops/context.cc
@@ -146,7 +146,11 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
auto sAllOutputs = state.symbols.create("allOutputs");
for (auto & i : *args[1]->attrs) {
if (!state.store->isStorePath(i.name))
- throw EvalError("Context key '%s' is not a store path, at %s", i.name, i.pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("Context key '%s' is not a store path", i.name),
+ .nixCode = NixCode { .errPos = *i.pos }
+ });
if (!settings.readOnlyMode)
state.store->ensurePath(state.store->parseStorePath(i.name));
state.forceAttrs(*i.value, *i.pos);
@@ -160,7 +164,11 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
if (iter != i.value->attrs->end()) {
if (state.forceBool(*iter->value, *iter->pos)) {
if (!isDerivation(i.name)) {
- throw EvalError("Tried to add all-outputs context of %s, which is not a derivation, to a string, at %s", i.name, i.pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("Tried to add all-outputs context of %s, which is not a derivation, to a string", i.name),
+ .nixCode = NixCode { .errPos = *i.pos }
+ });
}
context.insert("=" + string(i.name));
}
@@ -170,7 +178,11 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
if (iter != i.value->attrs->end()) {
state.forceList(*iter->value, *iter->pos);
if (iter->value->listSize() && !isDerivation(i.name)) {
- throw EvalError("Tried to add derivation output context of %s, which is not a derivation, to a string, at %s", i.name, i.pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("Tried to add derivation output context of %s, which is not a derivation, to a string", i.name),
+ .nixCode = NixCode { .errPos = *i.pos }
+ });
}
for (unsigned int n = 0; n < iter->value->listSize(); ++n) {
auto name = state.forceStringNoCtx(*iter->value->listElems()[n], *iter->pos);
diff --git a/src/libexpr/primops/fetchGit.cc b/src/libexpr/primops/fetchGit.cc
index 1a8798fcc..52826b56c 100644
--- a/src/libexpr/primops/fetchGit.cc
+++ b/src/libexpr/primops/fetchGit.cc
@@ -35,11 +35,19 @@ static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Va
else if (n == "submodules")
fetchSubmodules = state.forceBool(*attr.value, *attr.pos);
else
- throw EvalError("unsupported argument '%s' to 'fetchGit', at %s", attr.name, *attr.pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("unsupported argument '%s' to 'fetchGit'", attr.name),
+ .nixCode = NixCode { .errPos = *attr.pos }
+ });
}
if (url.empty())
- throw EvalError(format("'url' argument required, at %1%") % pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("'url' argument required"),
+ .nixCode = NixCode { .errPos = pos }
+ });
} else
url = state.coerceToString(pos, *args[0], context, false, false);
diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc
index 0a1ba49d5..bb008ba6b 100644
--- a/src/libexpr/primops/fetchMercurial.cc
+++ b/src/libexpr/primops/fetchMercurial.cc
@@ -38,11 +38,19 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
else if (n == "name")
name = state.forceStringNoCtx(*attr.value, *attr.pos);
else
- throw EvalError("unsupported argument '%s' to 'fetchMercurial', at %s", attr.name, *attr.pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("unsupported argument '%s' to 'fetchMercurial'", attr.name),
+ .nixCode = NixCode { .errPos = *attr.pos }
+ });
}
if (url.empty())
- throw EvalError(format("'url' argument required, at %1%") % pos);
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("'url' argument required"),
+ .nixCode = NixCode { .errPos = pos }
+ });
} else
url = state.coerceToString(pos, *args[0], context, false, false);
diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc
index 97254aa04..62bc4f433 100644
--- a/src/libexpr/primops/fetchTree.cc
+++ b/src/libexpr/primops/fetchTree.cc
@@ -66,7 +66,11 @@ static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, V
}
if (!attrs.count("type"))
- throw Error("attribute 'type' is missing in call to 'fetchTree', at %s", pos);
+ throw Error(
+ ErrorInfo {
+ .hint = hintfmt("attribute 'type' is missing in call to 'fetchTree'"),
+ .nixCode = NixCode { .errPos = pos }
+ });
input = fetchers::inputFromAttrs(attrs);
} else
@@ -107,13 +111,20 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
else if (n == "name")
name = state.forceStringNoCtx(*attr.value, *attr.pos);
else
- throw EvalError("unsupported argument '%s' to '%s', at %s",
- attr.name, who, *attr.pos);
- }
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("unsupported argument '%s' to '%s'",
+ attr.name, who),
+ .nixCode = NixCode { .errPos = *attr.pos }
+ });
+ }
if (!url)
- throw EvalError("'url' argument required, at %s", pos);
-
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("'url' argument required"),
+ .nixCode = NixCode { .errPos = pos }
+ });
} else
url = state.forceStringNoCtx(*args[0], pos);
diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc
index c43324dbb..948069401 100644
--- a/src/libexpr/primops/fromTOML.cc
+++ b/src/libexpr/primops/fromTOML.cc
@@ -81,7 +81,11 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va
try {
visit(v, parser(tomlStream).parse());
} catch (std::runtime_error & e) {
- throw EvalError("while parsing a TOML string at %s: %s", pos, e.what());
+ throw EvalError(
+ ErrorInfo {
+ .hint = hintfmt("while parsing a TOML string: %s", e.what()),
+ .nixCode = NixCode { .errPos = pos }
+ });
}
}
diff --git a/src/libexpr/value-to-json.cc b/src/libexpr/value-to-json.cc
index 5fe8570ad..6ec8315ba 100644
--- a/src/libexpr/value-to-json.cc
+++ b/src/libexpr/value-to-json.cc
@@ -79,7 +79,7 @@ void printValueAsJSON(EvalState & state, bool strict,
break;
default:
- throw TypeError(format("cannot convert %1% to JSON") % showType(v));
+ throw TypeError("cannot convert %1% to JSON", showType(v));
}
}
@@ -93,7 +93,7 @@ void printValueAsJSON(EvalState & state, bool strict,
void ExternalValueBase::printValueAsJSON(EvalState & state, bool strict,
JSONPlaceholder & out, PathSet & context) const
{
- throw TypeError(format("cannot convert %1% to JSON") % showType());
+ throw TypeError("cannot convert %1% to JSON", showType());
}
diff --git a/src/libmain/progress-bar.cc b/src/libmain/progress-bar.cc
index b287de8a3..20d9915a0 100644
--- a/src/libmain/progress-bar.cc
+++ b/src/libmain/progress-bar.cc
@@ -129,6 +129,16 @@ public:
log(*state, lvl, fs.s);
}
+ void logEI(const ErrorInfo &ei) override
+ {
+ auto state(state_.lock());
+
+ std::stringstream oss;
+ oss << ei;
+
+ log(*state, ei.level, oss.str());
+ }
+
void log(State & state, Verbosity lvl, const std::string & s)
{
if (state.active) {
diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc
index 3bbb5cf93..0f2c189a6 100644
--- a/src/libmain/shared.cc
+++ b/src/libmain/shared.cc
@@ -76,7 +76,7 @@ string getArg(const string & opt,
Strings::iterator & i, const Strings::iterator & end)
{
++i;
- if (i == end) throw UsageError(format("'%1%' requires an argument") % opt);
+ if (i == end) throw UsageError("'%1%' requires an argument", opt);
return *i;
}
@@ -235,7 +235,7 @@ bool LegacyArgs::processArgs(const Strings & args, bool finish)
Strings ss(args);
auto pos = ss.begin();
if (!parseArg(pos, ss.end()))
- throw UsageError(format("unexpected argument '%1%'") % args.front());
+ throw UsageError("unexpected argument '%1%'", args.front());
return true;
}
@@ -282,7 +282,7 @@ void showManPage(const string & name)
restoreSignals();
setenv("MANPATH", settings.nixManDir.c_str(), 1);
execlp("man", "man", name.c_str(), nullptr);
- throw SysError(format("command 'man %1%' failed") % name.c_str());
+ throw SysError("command 'man %1%' failed", name.c_str());
}
@@ -290,6 +290,8 @@ int handleExceptions(const string & programName, std::function<void()> fun)
{
ReceiveInterrupts receiveInterrupts; // FIXME: need better place for this
+ ErrorInfo::programName = programName;
+
string error = ANSI_RED "error:" ANSI_NORMAL " ";
try {
try {
@@ -305,12 +307,13 @@ int handleExceptions(const string & programName, std::function<void()> fun)
} catch (Exit & e) {
return e.status;
} catch (UsageError & e) {
- printError(
- format(error + "%1%\nTry '%2% --help' for more information.")
- % e.what() % programName);
+ logError(e.info());
+ printError("Try '%1% --help' for more information.", programName);
return 1;
} catch (BaseError & e) {
- printError(format(error + "%1%%2%") % (settings.showTrace ? e.prefix() : "") % e.msg());
+ if (settings.showTrace && e.prefix() != "")
+ printError(e.prefix());
+ logError(e.info());
if (e.prefix() != "" && !settings.showTrace)
printError("(use '--show-trace' to show detailed location information)");
return e.status;
@@ -347,7 +350,7 @@ RunPager::RunPager()
execlp("pager", "pager", nullptr);
execlp("less", "less", nullptr);
execlp("more", "more", nullptr);
- throw SysError(format("executing '%1%'") % pager);
+ throw SysError("executing '%1%'", pager);
});
pid.setKillSignal(SIGINT);
diff --git a/src/libmain/shared.hh b/src/libmain/shared.hh
index b49574652..f558247c0 100644
--- a/src/libmain/shared.hh
+++ b/src/libmain/shared.hh
@@ -56,7 +56,7 @@ template<class N> N getIntArg(const string & opt,
Strings::iterator & i, const Strings::iterator & end, bool allowUnit)
{
++i;
- if (i == end) throw UsageError(format("'%1%' requires an argument") % opt);
+ if (i == end) throw UsageError("'%1%' requires an argument", opt);
string s = *i;
N multiplier = 1;
if (allowUnit && !s.empty()) {
@@ -66,13 +66,13 @@ template<class N> N getIntArg(const string & opt,
else if (u == 'M') multiplier = 1ULL << 20;
else if (u == 'G') multiplier = 1ULL << 30;
else if (u == 'T') multiplier = 1ULL << 40;
- else throw UsageError(format("invalid unit specifier '%1%'") % u);
+ else throw UsageError("invalid unit specifier '%1%'", u);
s.resize(s.size() - 1);
}
}
N n;
if (!string2Int(s, n))
- throw UsageError(format("'%1%' requires an integer argument") % opt);
+ throw UsageError("'%1%' requires an integer argument", opt);
return n * multiplier;
}
diff --git a/src/libmain/stack.cc b/src/libmain/stack.cc
index e6224de7d..b0a4a4c5d 100644
--- a/src/libmain/stack.cc
+++ b/src/libmain/stack.cc
@@ -1,4 +1,4 @@
-#include "types.hh"
+#include "error.hh"
#include <cstring>
#include <cstddef>
diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc
index 357962007..649331495 100644
--- a/src/libstore/binary-cache-store.cc
+++ b/src/libstore/binary-cache-store.cc
@@ -40,14 +40,14 @@ void BinaryCacheStore::init()
upsertFile(cacheInfoFile, "StoreDir: " + storeDir + "\n", "text/x-nix-cache-info");
} else {
for (auto & line : tokenizeString<Strings>(*cacheInfo, "\n")) {
- size_t colon = line.find(':');
- if (colon == std::string::npos) continue;
+ size_t colon= line.find(':');
+ if (colon ==std::string::npos) continue;
auto name = line.substr(0, colon);
auto value = trim(line.substr(colon + 1, std::string::npos));
if (name == "StoreDir") {
if (value != storeDir)
- throw Error(format("binary cache '%s' is for Nix stores with prefix '%s', not '%s'")
- % getUri() % value % storeDir);
+ throw Error("binary cache '%s' is for Nix stores with prefix '%s', not '%s'",
+ getUri(), value, storeDir);
} else if (name == "WantMassQuery") {
wantMassQuery.setDefault(value == "1" ? "true" : "false");
} else if (name == "Priority") {
@@ -287,7 +287,7 @@ void BinaryCacheStore::narFromPath(const StorePath & storePath, Sink & sink)
try {
getFile(info->url, *decompressor);
} catch (NoSuchBinaryCacheFile & e) {
- throw SubstituteGone(e.what());
+ throw SubstituteGone(e.info());
}
decompressor->finish();
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index 3ab6220e3..de393e837 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -453,7 +453,7 @@ static void commonChildInit(Pipe & logPipe)
that e.g. ssh cannot open /dev/tty) and it doesn't receive
terminal signals. */
if (setsid() == -1)
- throw SysError(format("creating a new session"));
+ throw SysError("creating a new session");
/* Dup the write side of the logger pipe into stderr. */
if (dup2(logPipe.writeSide.get(), STDERR_FILENO) == -1)
@@ -466,7 +466,7 @@ static void commonChildInit(Pipe & logPipe)
/* Reroute stdin to /dev/null. */
int fdDevNull = open(pathNullDevice.c_str(), O_RDWR);
if (fdDevNull == -1)
- throw SysError(format("cannot open '%1%'") % pathNullDevice);
+ throw SysError("cannot open '%1%'", pathNullDevice);
if (dup2(fdDevNull, STDIN_FILENO) == -1)
throw SysError("cannot dup null device into stdin");
close(fdDevNull);
@@ -488,12 +488,18 @@ void handleDiffHook(
auto diffRes = runProgram(diffHookOptions);
if (!statusOk(diffRes.first))
- throw ExecError(diffRes.first, fmt("diff-hook program '%1%' %2%", diffHook, statusToString(diffRes.first)));
+ throw ExecError(diffRes.first,
+ "diff-hook program '%1%' %2%",
+ diffHook,
+ statusToString(diffRes.first));
if (diffRes.second != "")
printError(chomp(diffRes.second));
} catch (Error & error) {
- printError("diff hook execution failed: %s", error.what());
+ ErrorInfo ei = error.info();
+ ei.hint = hintfmt("diff hook execution failed: %s",
+ (error.info().hint.has_value() ? error.info().hint->str() : ""));
+ logError(ei);
}
}
}
@@ -542,37 +548,37 @@ bool UserLock::findFreeUser() {
/* Get the members of the build-users-group. */
struct group * gr = getgrnam(settings.buildUsersGroup.get().c_str());
if (!gr)
- throw Error(format("the group '%1%' specified in 'build-users-group' does not exist")
- % settings.buildUsersGroup);
+ throw Error("the group '%1%' specified in 'build-users-group' does not exist",
+ settings.buildUsersGroup);
gid = gr->gr_gid;
/* Copy the result of getgrnam. */
Strings users;
for (char * * p = gr->gr_mem; *p; ++p) {
- debug(format("found build user '%1%'") % *p);
+ debug("found build user '%1%'", *p);
users.push_back(*p);
}
if (users.empty())
- throw Error(format("the build users group '%1%' has no members")
- % settings.buildUsersGroup);
+ throw Error("the build users group '%1%' has no members",
+ settings.buildUsersGroup);
/* Find a user account that isn't currently in use for another
build. */
for (auto & i : users) {
- debug(format("trying user '%1%'") % i);
+ debug("trying user '%1%'", i);
struct passwd * pw = getpwnam(i.c_str());
if (!pw)
- throw Error(format("the user '%1%' in the group '%2%' does not exist")
- % i % settings.buildUsersGroup);
+ throw Error("the user '%1%' in the group '%2%' does not exist",
+ i, settings.buildUsersGroup);
fnUserLock = (format("%1%/userpool/%2%") % settings.nixStateDir % pw->pw_uid).str();
AutoCloseFD fd = open(fnUserLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600);
if (!fd)
- throw SysError(format("opening user lock '%1%'") % fnUserLock);
+ throw SysError("opening user lock '%1%'", fnUserLock);
if (lockFile(fd.get(), ltWrite, false)) {
fdUserLock = std::move(fd);
@@ -581,8 +587,8 @@ bool UserLock::findFreeUser() {
/* Sanity check... */
if (uid == getuid() || uid == geteuid())
- throw Error(format("the Nix user should not be a member of '%1%'")
- % settings.buildUsersGroup);
+ throw Error("the Nix user should not be a member of '%1%'",
+ settings.buildUsersGroup);
#if __linux__
/* Get the list of supplementary groups of this build user. This
@@ -592,7 +598,7 @@ bool UserLock::findFreeUser() {
int err = getgrouplist(pw->pw_name, pw->pw_gid,
supplementaryGIDs.data(), &ngroups);
if (err == -1)
- throw Error(format("failed to get list of supplementary groups for '%1%'") % pw->pw_name);
+ throw Error("failed to get list of supplementary groups for '%1%'", pw->pw_name);
supplementaryGIDs.resize(ngroups);
#endif
@@ -601,6 +607,7 @@ bool UserLock::findFreeUser() {
return true;
}
}
+
return false;
}
@@ -1151,7 +1158,10 @@ void DerivationGoal::loadDerivation()
trace("loading derivation");
if (nrFailed != 0) {
- printError("cannot build missing derivation '%s'", worker.store.printStorePath(drvPath));
+ logError({
+ .name = "missing derivation during build",
+ .hint = hintfmt("cannot build missing derivation '%s'", worker.store.printStorePath(drvPath))
+ });
done(BuildResult::MiscFailure);
return;
}
@@ -1302,8 +1312,12 @@ void DerivationGoal::repairClosure()
/* Check each path (slow!). */
for (auto & i : outputClosure) {
if (worker.pathContentsGood(i)) continue;
- printError("found corrupted or missing path '%s' in the output closure of '%s'",
- worker.store.printStorePath(i), worker.store.printStorePath(drvPath));
+ logError({
+ .name = "Corrupt path in closure",
+ .hint = hintfmt(
+ "found corrupted or missing path '%s' in the output closure of '%s'",
+ worker.store.printStorePath(i), worker.store.printStorePath(drvPath))
+ });
auto drvPath2 = outputsToDrv.find(i);
if (drvPath2 == outputsToDrv.end())
addWaitee(worker.makeSubstitutionGoal(i, Repair));
@@ -1337,8 +1351,12 @@ void DerivationGoal::inputsRealised()
if (nrFailed != 0) {
if (!useDerivation)
throw Error("some dependencies of '%s' are missing", worker.store.printStorePath(drvPath));
- printError("cannot build derivation '%s': %s dependencies couldn't be built",
- worker.store.printStorePath(drvPath), nrFailed);
+ logError({
+ .name = "Dependencies could not be built",
+ .hint = hintfmt(
+ "cannot build derivation '%s': %s dependencies couldn't be built",
+ worker.store.printStorePath(drvPath), nrFailed)
+ });
done(BuildResult::DependencyFailed);
return;
}
@@ -1523,7 +1541,7 @@ void DerivationGoal::tryLocalBuild() {
startBuilder();
} catch (BuildError & e) {
- printError(e.msg());
+ logError(e.info());
outputLocks.unlock();
buildUser.reset();
worker.permanentFailure = true;
@@ -1740,7 +1758,7 @@ void DerivationGoal::buildDone()
outputLocks.unlock();
} catch (BuildError & e) {
- printError(e.msg());
+ logError(e.info());
outputLocks.unlock();
@@ -1803,7 +1821,7 @@ HookReply DerivationGoal::tryBuildHook()
}
}
- debug(format("hook reply is '%1%'") % reply);
+ debug("hook reply is '%1%'", reply);
if (reply == "decline")
return rpDecline;
@@ -1819,8 +1837,12 @@ HookReply DerivationGoal::tryBuildHook()
} catch (SysError & e) {
if (e.errNo == EPIPE) {
- printError("build hook died unexpectedly: %s",
- chomp(drainFD(worker.hook->fromHook.readSide.get())));
+ logError({
+ .name = "Build hook died",
+ .hint = hintfmt(
+ "build hook died unexpectedly: %s",
+ chomp(drainFD(worker.hook->fromHook.readSide.get())))
+ });
worker.hook = 0;
return rpDecline;
} else
@@ -2000,7 +2022,7 @@ void DerivationGoal::startBuilder()
string s = get(drv->env, "exportReferencesGraph").value_or("");
Strings ss = tokenizeString<Strings>(s);
if (ss.size() % 2 != 0)
- throw BuildError(format("odd number of tokens in 'exportReferencesGraph': '%1%'") % s);
+ throw BuildError("odd number of tokens in 'exportReferencesGraph': '%1%'", s);
for (Strings::iterator i = ss.begin(); i != ss.end(); ) {
string fileName = *i++;
static std::regex regex("[A-Za-z_][A-Za-z0-9_.-]*");
@@ -2049,7 +2071,7 @@ void DerivationGoal::startBuilder()
worker.store.computeFSClosure(worker.store.parseStorePath(worker.store.toStorePath(i.second.source)), closure);
} catch (InvalidPath & e) {
} catch (Error & e) {
- throw Error(format("while processing 'sandbox-paths': %s") % e.what());
+ throw Error("while processing 'sandbox-paths': %s", e.what());
}
for (auto & i : closure) {
auto p = worker.store.printStorePath(i);
@@ -2096,10 +2118,10 @@ void DerivationGoal::startBuilder()
printMsg(lvlChatty, format("setting up chroot environment in '%1%'") % chrootRootDir);
if (mkdir(chrootRootDir.c_str(), 0750) == -1)
- throw SysError(format("cannot create '%1%'") % chrootRootDir);
+ throw SysError("cannot create '%1%'", chrootRootDir);
if (buildUser && chown(chrootRootDir.c_str(), 0, buildUser->getGID()) == -1)
- throw SysError(format("cannot change ownership of '%1%'") % chrootRootDir);
+ throw SysError("cannot change ownership of '%1%'", chrootRootDir);
/* Create a writable /tmp in the chroot. Many builders need
this. (Of course they should really respect $TMPDIR
@@ -2143,7 +2165,7 @@ void DerivationGoal::startBuilder()
chmod_(chrootStoreDir, 01775);
if (buildUser && chown(chrootStoreDir.c_str(), 0, buildUser->getGID()) == -1)
- throw SysError(format("cannot change ownership of '%1%'") % chrootStoreDir);
+ throw SysError("cannot change ownership of '%1%'", chrootStoreDir);
for (auto & i : inputPaths) {
auto p = worker.store.printStorePath(i);
@@ -2176,7 +2198,7 @@ void DerivationGoal::startBuilder()
if (needsHashRewrite()) {
if (pathExists(homeDir))
- throw Error(format("home directory '%1%' exists; please remove it to assure purity of builds without sandboxing") % homeDir);
+ throw Error("home directory '%1%' exists; please remove it to assure purity of builds without sandboxing", homeDir);
/* We're not doing a chroot build, but we have some valid
output paths. Since we can't just overwrite or delete
@@ -2221,8 +2243,7 @@ void DerivationGoal::startBuilder()
if (line == "extra-sandbox-paths" || line == "extra-chroot-dirs") {
state = stExtraChrootDirs;
} else {
- throw Error(format("unknown pre-build hook command '%1%'")
- % line);
+ throw Error("unknown pre-build hook command '%1%'", line);
}
} else if (state == stExtraChrootDirs) {
if (line == "") {
@@ -2244,7 +2265,7 @@ void DerivationGoal::startBuilder()
startDaemon();
/* Run the builder. */
- printMsg(lvlChatty, format("executing builder '%1%'") % drv->builder);
+ printMsg(lvlChatty, "executing builder '%1%'", drv->builder);
/* Create the log file. */
Path logFile = openLogFile();
@@ -2982,7 +3003,7 @@ void DerivationGoal::chownToBuilder(const Path & path)
{
if (!buildUser) return;
if (chown(path.c_str(), buildUser->getUID(), buildUser->getGID()) == -1)
- throw SysError(format("cannot change ownership of '%1%'") % path);
+ throw SysError("cannot change ownership of '%1%'", path);
}
@@ -3119,7 +3140,7 @@ void DerivationGoal::runChild()
/* Bind-mount chroot directory to itself, to treat it as a
different filesystem from /, as needed for pivot_root. */
if (mount(chrootRootDir.c_str(), chrootRootDir.c_str(), 0, MS_BIND, 0) == -1)
- throw SysError(format("unable to bind mount '%1%'") % chrootRootDir);
+ throw SysError("unable to bind mount '%1%'", chrootRootDir);
/* Bind-mount the sandbox's Nix store onto itself so that
we can mark it as a "shared" subtree, allowing bind
@@ -3181,7 +3202,7 @@ void DerivationGoal::runChild()
filesystem that we want in the chroot
environment. */
auto doBind = [&](const Path & source, const Path & target, bool optional = false) {
- debug(format("bind mounting '%1%' to '%2%'") % source % target);
+ debug("bind mounting '%1%' to '%2%'", source, target);
struct stat st;
if (stat(source.c_str(), &st) == -1) {
if (optional && errno == ENOENT)
@@ -3253,16 +3274,16 @@ void DerivationGoal::runChild()
/* Do the chroot(). */
if (chdir(chrootRootDir.c_str()) == -1)
- throw SysError(format("cannot change directory to '%1%'") % chrootRootDir);
+ throw SysError("cannot change directory to '%1%'", chrootRootDir);
if (mkdir("real-root", 0) == -1)
throw SysError("cannot create real-root directory");
if (pivot_root(".", "real-root") == -1)
- throw SysError(format("cannot pivot old root directory onto '%1%'") % (chrootRootDir + "/real-root"));
+ throw SysError("cannot pivot old root directory onto '%1%'", (chrootRootDir + "/real-root"));
if (chroot(".") == -1)
- throw SysError(format("cannot change root directory to '%1%'") % chrootRootDir);
+ throw SysError("cannot change root directory to '%1%'", chrootRootDir);
if (umount2("real-root", MNT_DETACH) == -1)
throw SysError("cannot unmount real root filesystem");
@@ -3283,7 +3304,7 @@ void DerivationGoal::runChild()
#endif
if (chdir(tmpDirInSandbox.c_str()) == -1)
- throw SysError(format("changing into '%1%'") % tmpDir);
+ throw SysError("changing into '%1%'", tmpDir);
/* Close all other file descriptors. */
closeMostFDs({STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO});
@@ -3422,9 +3443,9 @@ void DerivationGoal::runChild()
sandboxProfile += "(allow file-read* file-write* process-exec\n";
for (auto & i : dirsInChroot) {
if (i.first != i.second.source)
- throw Error(format(
- "can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin")
- % i.first % i.second.source);
+ throw Error(
+ "can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin",
+ i.first, i.second.source);
string path = i.first;
struct stat st;
@@ -3515,7 +3536,7 @@ void DerivationGoal::runChild()
else if (drv->builder == "builtin:unpack-channel")
builtinUnpackChannel(drv2);
else
- throw Error(format("unsupported builtin function '%1%'") % string(drv->builder, 8));
+ throw Error("unsupported builtin function '%1%'", string(drv->builder, 8));
_exit(0);
} catch (std::exception & e) {
writeFull(STDERR_FILENO, "error: " + string(e.what()) + "\n");
@@ -3525,7 +3546,7 @@ void DerivationGoal::runChild()
execve(builder, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
- throw SysError(format("executing '%1%'") % drv->builder);
+ throw SysError("executing '%1%'", drv->builder);
} catch (std::exception & e) {
writeFull(STDERR_FILENO, "\1while setting up the build environment: " + string(e.what()) + "\n");
@@ -3558,7 +3579,7 @@ static void moveCheckToStore(const Path & src, const Path & dst)
directory's parent link ".."). */
struct stat st;
if (lstat(src.c_str(), &st) == -1) {
- throw SysError(format("getting attributes of path '%1%'") % src);
+ throw SysError("getting attributes of path '%1%'", src);
}
bool changePerm = (geteuid() && S_ISDIR(st.st_mode) && !(st.st_mode & S_IWUSR));
@@ -3567,7 +3588,7 @@ static void moveCheckToStore(const Path & src, const Path & dst)
chmod_(src, st.st_mode | S_IWUSR);
if (rename(src.c_str(), dst.c_str()))
- throw SysError(format("renaming '%1%' to '%2%'") % src % dst);
+ throw SysError("renaming '%1%' to '%2%'", src, dst);
if (changePerm)
chmod_(dst, st.st_mode);
@@ -3633,7 +3654,7 @@ void DerivationGoal::registerOutputs()
replaceValidPath(path, actualPath);
else
if (buildMode != bmCheck && rename(actualPath.c_str(), worker.store.toRealPath(path).c_str()) == -1)
- throw SysError(format("moving build output '%1%' from the sandbox to the Nix store") % path);
+ throw SysError("moving build output '%1%' from the sandbox to the Nix store", path);
}
if (buildMode != bmCheck) actualPath = worker.store.toRealPath(path);
}
@@ -3654,13 +3675,16 @@ void DerivationGoal::registerOutputs()
user. */
if ((!S_ISLNK(st.st_mode) && (st.st_mode & (S_IWGRP | S_IWOTH))) ||
(buildUser && st.st_uid != buildUser->getUID()))
- throw BuildError(format("suspicious ownership or permission on '%1%'; rejecting this build output") % path);
+ throw BuildError("suspicious ownership or permission on '%1%'; rejecting this build output", path);
#endif
/* Apply hash rewriting if necessary. */
bool rewritten = false;
if (!outputRewrites.empty()) {
- printError(format("warning: rewriting hashes in '%1%'; cross fingers") % path);
+ logWarning({
+ .name = "Rewriting hashes",
+ .hint = hintfmt("rewriting hashes in '%1%'; cross fingers", path)
+ });
/* Canonicalise first. This ensures that the path we're
rewriting doesn't contain a hard link to /etc/shadow or
@@ -3692,8 +3716,9 @@ void DerivationGoal::registerOutputs()
/* The output path should be a regular file without execute permission. */
if (!S_ISREG(st.st_mode) || (st.st_mode & S_IXUSR) != 0)
throw BuildError(
- format("output path '%1%' should be a non-executable regular file "
- "since recursive hashing is not enabled (outputHashMode=flat)") % path);
+ "output path '%1%' should be a non-executable regular file "
+ "since recursive hashing is not enabled (outputHashMode=flat)",
+ path);
}
/* Check the hash. In hash mode, move the path produced by
@@ -3823,10 +3848,10 @@ void DerivationGoal::registerOutputs()
result.isNonDeterministic = true;
Path prev = worker.store.printStorePath(i->second.path) + checkSuffix;
bool prevExists = keepPreviousRound && pathExists(prev);
- auto msg = prevExists
- ? fmt("output '%s' of '%s' differs from '%s' from previous round",
+ hintformat hint = prevExists
+ ? hintfmt("output '%s' of '%s' differs from '%s' from previous round",
worker.store.printStorePath(i->second.path), worker.store.printStorePath(drvPath), prev)
- : fmt("output '%s' of '%s' differs from previous round",
+ : hintfmt("output '%s' of '%s' differs from previous round",
worker.store.printStorePath(i->second.path), worker.store.printStorePath(drvPath));
handleDiffHook(
@@ -3836,9 +3861,14 @@ void DerivationGoal::registerOutputs()
worker.store.printStorePath(drvPath), tmpDir);
if (settings.enforceDeterminism)
- throw NotDeterministic(msg);
+ throw NotDeterministic(hint);
+
+ logError({
+ .name = "Output determinism error",
+ .hint = hint
+ });
+
- printError(msg);
curRound = nrRounds; // we know enough, bail out early
}
}
@@ -4056,7 +4086,7 @@ Path DerivationGoal::openLogFile()
settings.compressLog ? ".bz2" : "");
fdLogFile = open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0666);
- if (!fdLogFile) throw SysError(format("creating log file '%1%'") % logFileName);
+ if (!fdLogFile) throw SysError("creating log file '%1%'", logFileName);
logFileSink = std::make_shared<FdSink>(fdLogFile.get());
@@ -4102,9 +4132,12 @@ void DerivationGoal::handleChildOutput(int fd, const string & data)
{
logSize += data.size();
if (settings.maxLogSize && logSize > settings.maxLogSize) {
- printError(
- format("%1% killed after writing more than %2% bytes of log output")
- % getName() % settings.maxLogSize);
+ logError({
+ .name = "Max log size exceeded",
+ .hint = hintfmt(
+ "%1% killed after writing more than %2% bytes of log output",
+ getName(), settings.maxLogSize)
+ });
killChild();
done(BuildResult::LogLimitExceeded);
return;
@@ -4389,7 +4422,7 @@ void SubstitutionGoal::tryNext()
throw;
} catch (Error & e) {
if (settings.tryFallback) {
- printError(e.what());
+ logError(e.info());
tryNext();
return;
}
@@ -4415,8 +4448,11 @@ void SubstitutionGoal::tryNext()
&& !sub->isTrusted
&& !info->checkSignatures(worker.store, worker.store.getPublicKeys()))
{
- printError("warning: substituter '%s' does not have a valid signature for path '%s'",
- sub->getUri(), worker.store.printStorePath(storePath));
+ logWarning({
+ .name = "Invalid path signature",
+ .hint = hintfmt("substituter '%s' does not have a valid signature for path '%s'",
+ sub->getUri(), worker.store.printStorePath(storePath))
+ });
tryNext();
return;
}
@@ -4559,7 +4595,6 @@ void SubstitutionGoal::handleEOF(int fd)
if (fd == outPipe.readSide.get()) worker.wakeUp(shared_from_this());
}
-
//////////////////////////////////////////////////////////////////////
@@ -4776,9 +4811,9 @@ void Worker::run(const Goals & _topGoals)
if (!children.empty() || !waitingForAWhile.empty())
waitForInput();
else {
- if (awake.empty() && 0 == settings.maxBuildJobs) throw Error(
- "unable to start any build; either increase '--max-jobs' "
- "or enable remote builds");
+ if (awake.empty() && 0 == settings.maxBuildJobs)
+ throw Error("unable to start any build; either increase '--max-jobs' "
+ "or enable remote builds");
assert(!awake.empty());
}
}
@@ -4791,7 +4826,6 @@ void Worker::run(const Goals & _topGoals)
assert(!settings.keepGoing || children.empty());
}
-
void Worker::waitForInput()
{
printMsg(lvlVomit, "waiting for children");
@@ -4830,7 +4864,7 @@ void Worker::waitForInput()
if (!waitingForAWhile.empty()) {
useTimeout = true;
if (lastWokenUp == steady_time_point::min())
- printError("waiting for locks, build slots or build users...");
+ printInfo("waiting for locks, build slots or build users...");
if (lastWokenUp == steady_time_point::min() || lastWokenUp > before) lastWokenUp = before;
timeout = std::max(1L,
(long) std::chrono::duration_cast<std::chrono::seconds>(
@@ -4879,15 +4913,15 @@ void Worker::waitForInput()
// FIXME: is there a cleaner way to handle pt close
// than EIO? Is this even standard?
if (rd == 0 || (rd == -1 && errno == EIO)) {
- debug(format("%1%: got EOF") % goal->getName());
+ debug("%1%: got EOF", goal->getName());
goal->handleEOF(k);
j->fds.erase(k);
} else if (rd == -1) {
if (errno != EINTR)
throw SysError("%s: read failed", goal->getName());
} else {
- printMsg(lvlVomit, format("%1%: read %2% bytes")
- % goal->getName() % rd);
+ printMsg(lvlVomit, "%1%: read %2% bytes",
+ goal->getName(), rd);
string data((char *) buffer.data(), rd);
j->lastOutput = after;
goal->handleChildOutput(k, data);
@@ -4900,9 +4934,12 @@ void Worker::waitForInput()
j->respectTimeouts &&
after - j->lastOutput >= std::chrono::seconds(settings.maxSilentTime))
{
- printError(
- format("%1% timed out after %2% seconds of silence")
- % goal->getName() % settings.maxSilentTime);
+ logError({
+ .name = "Silent build timeout",
+ .hint = hintfmt(
+ "%1% timed out after %2% seconds of silence",
+ goal->getName(), settings.maxSilentTime)
+ });
goal->timedOut();
}
@@ -4911,9 +4948,12 @@ void Worker::waitForInput()
j->respectTimeouts &&
after - j->timeStarted >= std::chrono::seconds(settings.buildTimeout))
{
- printError(
- format("%1% timed out after %2% seconds")
- % goal->getName() % settings.buildTimeout);
+ logError({
+ .name = "Build timeout",
+ .hint = hintfmt(
+ "%1% timed out after %2% seconds",
+ goal->getName(), settings.buildTimeout)
+ });
goal->timedOut();
}
}
@@ -4972,7 +5012,11 @@ bool Worker::pathContentsGood(const StorePath & path)
res = info->narHash == nullHash || info->narHash == current.first;
}
pathContentsGoodCache.insert_or_assign(path.clone(), res);
- if (!res) printError("path '%s' is corrupted or missing!", store.printStorePath(path));
+ if (!res)
+ logError({
+ .name = "Corrupted path",
+ .hint = hintfmt("path '%s' is corrupted or missing!", store.printStorePath(path))
+ });
return res;
}
@@ -5028,7 +5072,6 @@ void LocalStore::buildPaths(const std::vector<StorePathWithOutputs> & drvPaths,
throw Error(worker.exitStatus(), "build of %s failed", showPaths(failed));
}
-
BuildResult LocalStore::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
BuildMode buildMode)
{
diff --git a/src/libstore/builtins/buildenv.cc b/src/libstore/builtins/buildenv.cc
index 1b802d908..6c493ed77 100644
--- a/src/libstore/builtins/buildenv.cc
+++ b/src/libstore/builtins/buildenv.cc
@@ -22,7 +22,11 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
srcFiles = readDirectory(srcDir);
} catch (SysError & e) {
if (e.errNo == ENOTDIR) {
- printError("warning: not including '%s' in the user environment because it's not a directory", srcDir);
+ logWarning(
+ ErrorInfo {
+ .name = "Create links - directory",
+ .hint = hintfmt("not including '%s' in the user environment because it's not a directory", srcDir)
+ });
return;
}
throw;
@@ -41,7 +45,11 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
throw SysError("getting status of '%1%'", srcFile);
} catch (SysError & e) {
if (e.errNo == ENOENT || e.errNo == ENOTDIR) {
- printError("warning: skipping dangling symlink '%s'", dstFile);
+ logWarning(
+ ErrorInfo {
+ .name = "Create links - skipping symlink",
+ .hint = hintfmt("skipping dangling symlink '%s'", dstFile)
+ });
continue;
}
throw;
@@ -72,15 +80,15 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
if (!S_ISDIR(lstat(target).st_mode))
throw Error("collision between '%1%' and non-directory '%2%'", srcFile, target);
if (unlink(dstFile.c_str()) == -1)
- throw SysError(format("unlinking '%1%'") % dstFile);
+ throw SysError("unlinking '%1%'", dstFile);
if (mkdir(dstFile.c_str(), 0755) == -1)
- throw SysError(format("creating directory '%1%'"));
+ throw SysError("creating directory '%1%'", dstFile);
createLinks(state, target, dstFile, state.priorities[dstFile]);
createLinks(state, srcFile, dstFile, priority);
continue;
}
} else if (errno != ENOENT)
- throw SysError(format("getting status of '%1%'") % dstFile);
+ throw SysError("getting status of '%1%'", dstFile);
}
else {
@@ -99,11 +107,11 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
if (prevPriority < priority)
continue;
if (unlink(dstFile.c_str()) == -1)
- throw SysError(format("unlinking '%1%'") % dstFile);
+ throw SysError("unlinking '%1%'", dstFile);
} else if (S_ISDIR(dstSt.st_mode))
throw Error("collision between non-directory '%1%' and directory '%2%'", srcFile, dstFile);
} else if (errno != ENOENT)
- throw SysError(format("getting status of '%1%'") % dstFile);
+ throw SysError("getting status of '%1%'", dstFile);
}
createSymlink(srcFile, dstFile);
diff --git a/src/libstore/builtins/fetchurl.cc b/src/libstore/builtins/fetchurl.cc
index 486babf14..2048f8f87 100644
--- a/src/libstore/builtins/fetchurl.cc
+++ b/src/libstore/builtins/fetchurl.cc
@@ -18,7 +18,7 @@ void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData)
auto getAttr = [&](const string & name) {
auto i = drv.env.find(name);
- if (i == drv.env.end()) throw Error(format("attribute '%s' missing") % name);
+ if (i == drv.env.end()) throw Error("attribute '%s' missing", name);
return i->second;
};
@@ -54,7 +54,7 @@ void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData)
auto executable = drv.env.find("executable");
if (executable != drv.env.end() && executable->second == "1") {
if (chmod(storePath.c_str(), 0755) == -1)
- throw SysError(format("making '%1%' executable") % storePath);
+ throw SysError("making '%1%' executable", storePath);
}
};
diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc
index 877c70a03..172cfe3cb 100644
--- a/src/libstore/daemon.cc
+++ b/src/libstore/daemon.cc
@@ -73,6 +73,18 @@ struct TunnelLogger : public Logger
enqueueMsg(*buf.s);
}
+ void logEI(const ErrorInfo & ei) override
+ {
+ if (ei.level > verbosity) return;
+
+ std::stringstream oss;
+ oss << ei;
+
+ StringSink buf;
+ buf << STDERR_NEXT << oss.str() << "\n"; // (fs.s + "\n");
+ enqueueMsg(*buf.s);
+ }
+
/* startWork() means that we're starting an operation for which we
want to send out stderr to the client. */
void startWork()
@@ -744,7 +756,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
}
default:
- throw Error(format("invalid operation %1%") % op);
+ throw Error("invalid operation %1%", op);
}
}
diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc
index e268c65ff..915e02eed 100644
--- a/src/libstore/derivations.cc
+++ b/src/libstore/derivations.cc
@@ -87,7 +87,7 @@ static void expect(std::istream & str, const string & s)
char s2[s.size()];
str.read(s2, s.size());
if (string(s2, s.size()) != s)
- throw FormatError(format("expected string '%1%'") % s);
+ throw FormatError("expected string '%1%'", s);
}
@@ -114,7 +114,7 @@ static Path parsePath(std::istream & str)
{
string s = parseString(str);
if (s.size() == 0 || s[0] != '/')
- throw FormatError(format("bad path '%1%' in derivation") % s);
+ throw FormatError("bad path '%1%' in derivation", s);
return s;
}
@@ -196,7 +196,7 @@ Derivation readDerivation(const Store & store, const Path & drvPath)
try {
return parseDerivation(store, readFile(drvPath));
} catch (FormatError & e) {
- throw Error(format("error parsing derivation '%1%': %2%") % drvPath % e.msg());
+ throw Error("error parsing derivation '%1%': %2%", drvPath, e.msg());
}
}
diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc
index e9684b3d4..6ca3393ab 100644
--- a/src/libstore/filetransfer.cc
+++ b/src/libstore/filetransfer.cc
@@ -112,7 +112,7 @@ struct curlFileTransfer : public FileTransfer
if (requestHeaders) curl_slist_free_all(requestHeaders);
try {
if (!done)
- fail(FileTransferError(Interrupted, format("download of '%s' was interrupted") % request.uri));
+ fail(FileTransferError(Interrupted, "download of '%s' was interrupted", request.uri));
} catch (...) {
ignoreException();
}
@@ -517,7 +517,7 @@ struct curlFileTransfer : public FileTransfer
int running;
CURLMcode mc = curl_multi_perform(curlm, &running);
if (mc != CURLM_OK)
- throw nix::Error(format("unexpected error from curl_multi_perform(): %s") % curl_multi_strerror(mc));
+ throw nix::Error("unexpected error from curl_multi_perform(): %s", curl_multi_strerror(mc));
/* Set the promises of any finished requests. */
CURLMsg * msg;
@@ -547,7 +547,7 @@ struct curlFileTransfer : public FileTransfer
vomit("download thread waiting for %d ms", sleepTimeMs);
mc = curl_multi_wait(curlm, extraFDs, 1, sleepTimeMs, &numfds);
if (mc != CURLM_OK)
- throw nix::Error(format("unexpected error from curl_multi_wait(): %s") % curl_multi_strerror(mc));
+ throw nix::Error("unexpected error from curl_multi_wait(): %s", curl_multi_strerror(mc));
nextWakeup = std::chrono::steady_clock::time_point();
@@ -599,7 +599,11 @@ struct curlFileTransfer : public FileTransfer
workerThreadMain();
} catch (nix::Interrupted & e) {
} catch (std::exception & e) {
- printError("unexpected error in download thread: %s", e.what());
+ logError({
+ .name = "File transfer",
+ .hint = hintfmt("unexpected error in download thread: %s",
+ e.what())
+ });
}
{
diff --git a/src/libstore/filetransfer.hh b/src/libstore/filetransfer.hh
index 2347f363d..11dca2fe0 100644
--- a/src/libstore/filetransfer.hh
+++ b/src/libstore/filetransfer.hh
@@ -103,8 +103,9 @@ class FileTransferError : public Error
{
public:
FileTransfer::Error error;
- FileTransferError(FileTransfer::Error error, const FormatOrString & fs)
- : Error(fs), error(error)
+ template<typename... Args>
+ FileTransferError(FileTransfer::Error error, const Args & ... args)
+ : Error(args...), error(error)
{ }
};
diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc
index 95a4bc934..92f9328c3 100644
--- a/src/libstore/gc.cc
+++ b/src/libstore/gc.cc
@@ -38,10 +38,10 @@ AutoCloseFD LocalStore::openGCLock(LockType lockType)
AutoCloseFD fdGCLock = open(fnGCLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600);
if (!fdGCLock)
- throw SysError(format("opening global GC lock '%1%'") % fnGCLock);
+ throw SysError("opening global GC lock '%1%'", fnGCLock);
if (!lockFile(fdGCLock.get(), lockType, false)) {
- printError(format("waiting for the big garbage collector lock..."));
+ printInfo("waiting for the big garbage collector lock...");
lockFile(fdGCLock.get(), lockType, true);
}
@@ -65,8 +65,8 @@ static void makeSymlink(const Path & link, const Path & target)
/* Atomically replace the old one. */
if (rename(tempLink.c_str(), link.c_str()) == -1)
- throw SysError(format("cannot rename '%1%' to '%2%'")
- % tempLink % link);
+ throw SysError("cannot rename '%1%' to '%2%'",
+ tempLink , link);
}
@@ -91,15 +91,15 @@ Path LocalFSStore::addPermRoot(const StorePath & storePath,
Path gcRoot(canonPath(_gcRoot));
if (isInStore(gcRoot))
- throw Error(format(
+ throw Error(
"creating a garbage collector root (%1%) in the Nix store is forbidden "
- "(are you running nix-build inside the store?)") % gcRoot);
+ "(are you running nix-build inside the store?)", gcRoot);
if (indirect) {
/* Don't clobber the link if it already exists and doesn't
point to the Nix store. */
if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot))))
- throw Error(format("cannot create symlink '%1%'; already exists") % gcRoot);
+ throw Error("cannot create symlink '%1%'; already exists", gcRoot);
makeSymlink(gcRoot, printStorePath(storePath));
addIndirectRoot(gcRoot);
}
@@ -109,10 +109,10 @@ Path LocalFSStore::addPermRoot(const StorePath & storePath,
Path rootsDir = canonPath((format("%1%/%2%") % stateDir % gcRootsDir).str());
if (string(gcRoot, 0, rootsDir.size() + 1) != rootsDir + "/")
- throw Error(format(
+ throw Error(
"path '%1%' is not a valid garbage collector root; "
- "it's not in the directory '%2%'")
- % gcRoot % rootsDir);
+ "it's not in the directory '%2%'",
+ gcRoot, rootsDir);
}
if (baseNameOf(gcRoot) == std::string(storePath.to_string()))
@@ -129,10 +129,13 @@ Path LocalFSStore::addPermRoot(const StorePath & storePath,
if (settings.checkRootReachability) {
auto roots = findRoots(false);
if (roots[storePath.clone()].count(gcRoot) == 0)
- printError(
- "warning: '%1%' is not in a directory where the garbage collector looks for roots; "
+ logWarning(
+ ErrorInfo {
+ .name = "GC root",
+ .hint = hintfmt("warning: '%1%' is not in a directory where the garbage collector looks for roots; "
"therefore, '%2%' might be removed by the garbage collector",
- gcRoot, printStorePath(storePath));
+ gcRoot, printStorePath(storePath))
+ });
}
/* Grab the global GC root, causing us to block while a GC is in
@@ -170,7 +173,7 @@ void LocalStore::addTempRoot(const StorePath & path)
way. */
struct stat st;
if (fstat(state->fdTempRoots.get(), &st) == -1)
- throw SysError(format("statting '%1%'") % fnTempRoots);
+ throw SysError("statting '%1%'", fnTempRoots);
if (st.st_size == 0) break;
/* The garbage collector deleted this file before we could
@@ -216,7 +219,7 @@ void LocalStore::findTempRoots(FDs & fds, Roots & tempRoots, bool censor)
if (!*fd) {
/* It's okay if the file has disappeared. */
if (errno == ENOENT) continue;
- throw SysError(format("opening temporary roots file '%1%'") % path);
+ throw SysError("opening temporary roots file '%1%'", path);
}
/* This should work, but doesn't, for some reason. */
@@ -227,7 +230,7 @@ void LocalStore::findTempRoots(FDs & fds, Roots & tempRoots, bool censor)
only succeed if the owning process has died. In that case
we don't care about its temporary roots. */
if (lockFile(fd->get(), ltWrite, false)) {
- printError(format("removing stale temporary roots file '%1%'") % path);
+ printInfo("removing stale temporary roots file '%1%'", path);
unlink(path.c_str());
writeFull(fd->get(), "d");
continue;
@@ -403,7 +406,7 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor)
if (!fdDir) {
if (errno == ENOENT || errno == EACCES)
continue;
- throw SysError(format("opening %1%") % fdStr);
+ throw SysError("opening %1%", fdStr);
}
struct dirent * fd_ent;
while (errno = 0, fd_ent = readdir(fdDir.get())) {
@@ -413,7 +416,7 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor)
if (errno) {
if (errno == ESRCH)
continue;
- throw SysError(format("iterating /proc/%1%/fd") % ent->d_name);
+ throw SysError("iterating /proc/%1%/fd", ent->d_name);
}
fdDir.reset();
@@ -541,7 +544,7 @@ void LocalStore::deletePathRecursive(GCState & state, const Path & path)
struct stat st;
if (lstat(realPath.c_str(), &st)) {
if (errno == ENOENT) return;
- throw SysError(format("getting status of %1%") % realPath);
+ throw SysError("getting status of %1%", realPath);
}
printInfo(format("deleting '%1%'") % path);
@@ -559,10 +562,10 @@ void LocalStore::deletePathRecursive(GCState & state, const Path & path)
// size.
try {
if (chmod(realPath.c_str(), st.st_mode | S_IWUSR) == -1)
- throw SysError(format("making '%1%' writable") % realPath);
+ throw SysError("making '%1%' writable", realPath);
Path tmp = trashDir + "/" + std::string(baseNameOf(path));
if (rename(realPath.c_str(), tmp.c_str()))
- throw SysError(format("unable to rename '%1%' to '%2%'") % realPath % tmp);
+ throw SysError("unable to rename '%1%' to '%2%'", realPath, tmp);
state.bytesInvalidated += size;
} catch (SysError & e) {
if (e.errNo == ENOSPC) {
@@ -681,7 +684,7 @@ void LocalStore::tryToDelete(GCState & state, const Path & path)
void LocalStore::removeUnusedLinks(const GCState & state)
{
AutoCloseDir dir(opendir(linksDir.c_str()));
- if (!dir) throw SysError(format("opening directory '%1%'") % linksDir);
+ if (!dir) throw SysError("opening directory '%1%'", linksDir);
long long actualSize = 0, unsharedSize = 0;
@@ -694,7 +697,7 @@ void LocalStore::removeUnusedLinks(const GCState & state)
struct stat st;
if (lstat(path.c_str(), &st) == -1)
- throw SysError(format("statting '%1%'") % path);
+ throw SysError("statting '%1%'", path);
if (st.st_nlink != 1) {
actualSize += st.st_size;
@@ -705,14 +708,14 @@ void LocalStore::removeUnusedLinks(const GCState & state)
printMsg(lvlTalkative, format("deleting unused link '%1%'") % path);
if (unlink(path.c_str()) == -1)
- throw SysError(format("deleting '%1%'") % path);
+ throw SysError("deleting '%1%'", path);
state.results.bytesFreed += st.st_size;
}
struct stat st;
if (stat(linksDir.c_str(), &st) == -1)
- throw SysError(format("statting '%1%'") % linksDir);
+ throw SysError("statting '%1%'", linksDir);
long long overhead = st.st_blocks * 512ULL;
printInfo(format("note: currently hard linking saves %.2f MiB")
@@ -747,7 +750,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
/* Find the roots. Since we've grabbed the GC lock, the set of
permanent roots cannot increase now. */
- printError("finding garbage collector roots...");
+ printInfo("finding garbage collector roots...");
Roots rootMap;
if (!options.ignoreLiveness)
findRootsNoTemp(rootMap, true);
@@ -799,14 +802,14 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
} else if (options.maxFreed > 0) {
if (state.shouldDelete)
- printError("deleting garbage...");
+ printInfo("deleting garbage...");
else
- printError("determining live/dead paths...");
+ printInfo("determining live/dead paths...");
try {
AutoCloseDir dir(opendir(realStoreDir.c_str()));
- if (!dir) throw SysError(format("opening directory '%1%'") % realStoreDir);
+ if (!dir) throw SysError("opening directory '%1%'", realStoreDir);
/* Read the store and immediately delete all paths that
aren't valid. When using --max-freed etc., deleting
@@ -868,7 +871,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
/* Clean up the links directory. */
if (options.action == GCOptions::gcDeleteDead || options.action == GCOptions::gcDeleteSpecific) {
- printError("deleting unused links...");
+ printInfo("deleting unused links...");
removeUnusedLinks(state);
}
diff --git a/src/libstore/local-binary-cache-store.cc b/src/libstore/local-binary-cache-store.cc
index 363be1443..48aca478c 100644
--- a/src/libstore/local-binary-cache-store.cc
+++ b/src/libstore/local-binary-cache-store.cc
@@ -74,7 +74,7 @@ static void atomicWrite(const Path & path, const std::string & s)
AutoDelete del(tmp, false);
writeFile(tmp, s);
if (rename(tmp.c_str(), path.c_str()))
- throw SysError(format("renaming '%1%' to '%2%'") % tmp % path);
+ throw SysError("renaming '%1%' to '%2%'", tmp, path);
del.cancel();
}
diff --git a/src/libstore/local-fs-store.cc b/src/libstore/local-fs-store.cc
index aa5abd835..2d564a0d7 100644
--- a/src/libstore/local-fs-store.cc
+++ b/src/libstore/local-fs-store.cc
@@ -22,7 +22,7 @@ struct LocalStoreAccessor : public FSAccessor
{
Path storePath = store->toStorePath(path);
if (!store->isValidPath(store->parseStorePath(storePath)))
- throw InvalidPath(format("path '%1%' is not a valid store path") % storePath);
+ throw InvalidPath("path '%1%' is not a valid store path", storePath);
return store->getRealStoreDir() + std::string(path, store->storeDir.size());
}
@@ -33,11 +33,11 @@ struct LocalStoreAccessor : public FSAccessor
struct stat st;
if (lstat(realPath.c_str(), &st)) {
if (errno == ENOENT || errno == ENOTDIR) return {Type::tMissing, 0, false};
- throw SysError(format("getting status of '%1%'") % path);
+ throw SysError("getting status of '%1%'", path);
}
if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode))
- throw Error(format("file '%1%' has unsupported type") % path);
+ throw Error("file '%1%' has unsupported type", path);
return {
S_ISREG(st.st_mode) ? Type::tRegular :
diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc
index c2c45b4c2..5521b8633 100644
--- a/src/libstore/local-store.cc
+++ b/src/libstore/local-store.cc
@@ -87,18 +87,22 @@ LocalStore::LocalStore(const Params & params)
struct group * gr = getgrnam(settings.buildUsersGroup.get().c_str());
if (!gr)
- printError(format("warning: the group '%1%' specified in 'build-users-group' does not exist")
- % settings.buildUsersGroup);
+ logError({
+ .name = "'build-users-group' not found",
+ .hint = hintfmt(
+ "warning: the group '%1%' specified in 'build-users-group' does not exist",
+ settings.buildUsersGroup)
+ });
else {
struct stat st;
if (stat(realStoreDir.c_str(), &st))
- throw SysError(format("getting attributes of path '%1%'") % realStoreDir);
+ throw SysError("getting attributes of path '%1%'", realStoreDir);
if (st.st_uid != 0 || st.st_gid != gr->gr_gid || (st.st_mode & ~S_IFMT) != perm) {
if (chown(realStoreDir.c_str(), 0, gr->gr_gid) == -1)
- throw SysError(format("changing ownership of path '%1%'") % realStoreDir);
+ throw SysError("changing ownership of path '%1%'", realStoreDir);
if (chmod(realStoreDir.c_str(), perm) == -1)
- throw SysError(format("changing permissions on path '%1%'") % realStoreDir);
+ throw SysError("changing permissions on path '%1%'", realStoreDir);
}
}
}
@@ -109,12 +113,12 @@ LocalStore::LocalStore(const Params & params)
struct stat st;
while (path != "/") {
if (lstat(path.c_str(), &st))
- throw SysError(format("getting status of '%1%'") % path);
+ throw SysError("getting status of '%1%'", path);
if (S_ISLNK(st.st_mode))
- throw Error(format(
+ throw Error(
"the path '%1%' is a symlink; "
- "this is not allowed for the Nix store and its parent directories")
- % path);
+ "this is not allowed for the Nix store and its parent directories",
+ path);
path = dirOf(path);
}
}
@@ -147,7 +151,7 @@ LocalStore::LocalStore(const Params & params)
globalLock = openLockFile(globalLockPath.c_str(), true);
if (!lockFile(globalLock.get(), ltRead, false)) {
- printError("waiting for the big Nix store lock...");
+ printInfo("waiting for the big Nix store lock...");
lockFile(globalLock.get(), ltRead, true);
}
@@ -155,8 +159,8 @@ LocalStore::LocalStore(const Params & params)
upgrade. */
int curSchema = getSchema();
if (curSchema > nixSchemaVersion)
- throw Error(format("current Nix store schema is version %1%, but I only support %2%")
- % curSchema % nixSchemaVersion);
+ throw Error("current Nix store schema is version %1%, but I only support %2%",
+ curSchema, nixSchemaVersion);
else if (curSchema == 0) { /* new store */
curSchema = nixSchemaVersion;
@@ -178,7 +182,7 @@ LocalStore::LocalStore(const Params & params)
"please upgrade Nix to version 1.11 first.");
if (!lockFile(globalLock.get(), ltWrite, false)) {
- printError("waiting for exclusive access to the Nix store...");
+ printInfo("waiting for exclusive access to the Nix store...");
lockFile(globalLock.get(), ltWrite, true);
}
@@ -256,7 +260,7 @@ LocalStore::~LocalStore()
}
if (future.valid()) {
- printError("waiting for auto-GC to finish on exit...");
+ printInfo("waiting for auto-GC to finish on exit...");
future.get();
}
@@ -284,7 +288,7 @@ int LocalStore::getSchema()
if (pathExists(schemaPath)) {
string s = readFile(schemaPath);
if (!string2Int(s, curSchema))
- throw Error(format("'%1%' is corrupt") % schemaPath);
+ throw Error("'%1%' is corrupt", schemaPath);
}
return curSchema;
}
@@ -293,7 +297,7 @@ int LocalStore::getSchema()
void LocalStore::openDB(State & state, bool create)
{
if (access(dbDir.c_str(), R_OK | W_OK))
- throw SysError(format("Nix database directory '%1%' is not writable") % dbDir);
+ throw SysError("Nix database directory '%1%' is not writable", dbDir);
/* Open the Nix database. */
string dbPath = dbDir + "/db.sqlite";
@@ -367,7 +371,7 @@ void LocalStore::makeStoreWritable()
throw SysError("setting up a private mount namespace");
if (mount(0, realStoreDir.c_str(), "none", MS_REMOUNT | MS_BIND, 0) == -1)
- throw SysError(format("remounting %1% writable") % realStoreDir);
+ throw SysError("remounting %1% writable", realStoreDir);
}
#endif
}
@@ -388,7 +392,7 @@ static void canonicaliseTimestampAndPermissions(const Path & path, const struct
| 0444
| (st.st_mode & S_IXUSR ? 0111 : 0);
if (chmod(path.c_str(), mode) == -1)
- throw SysError(format("changing mode of '%1%' to %2$o") % path % mode);
+ throw SysError("changing mode of '%1%' to %2$o", path, mode);
}
}
@@ -406,7 +410,7 @@ static void canonicaliseTimestampAndPermissions(const Path & path, const struct
#else
if (!S_ISLNK(st.st_mode) && utimes(path.c_str(), times) == -1)
#endif
- throw SysError(format("changing modification time of '%1%'") % path);
+ throw SysError("changing modification time of '%1%'", path);
}
}
@@ -415,7 +419,7 @@ void canonicaliseTimestampAndPermissions(const Path & path)
{
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);
canonicaliseTimestampAndPermissions(path, st);
}
@@ -430,17 +434,17 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe
setattrlist() to remove other attributes as well. */
if (lchflags(path.c_str(), 0)) {
if (errno != ENOTSUP)
- throw SysError(format("clearing flags of path '%1%'") % path);
+ throw SysError("clearing flags of path '%1%'", path);
}
#endif
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);
/* Really make sure that the path is of a supported type. */
if (!(S_ISREG(st.st_mode) || S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode)))
- throw Error(format("file '%1%' has an unsupported type") % path);
+ throw Error("file '%1%' has an unsupported type", path);
#if __linux__
/* Remove extended attributes / ACLs. */
@@ -474,7 +478,7 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe
if (fromUid != (uid_t) -1 && st.st_uid != fromUid) {
assert(!S_ISDIR(st.st_mode));
if (inodesSeen.find(Inode(st.st_dev, st.st_ino)) == inodesSeen.end())
- throw BuildError(format("invalid ownership on file '%1%'") % path);
+ throw BuildError("invalid ownership on file '%1%'", path);
mode_t mode = st.st_mode & ~S_IFMT;
assert(S_ISLNK(st.st_mode) || (st.st_uid == geteuid() && (mode == 0444 || mode == 0555) && st.st_mtime == mtimeStore));
return;
@@ -498,8 +502,8 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe
if (!S_ISLNK(st.st_mode) &&
chown(path.c_str(), geteuid(), getegid()) == -1)
#endif
- throw SysError(format("changing owner of '%1%' to %2%")
- % path % geteuid());
+ throw SysError("changing owner of '%1%' to %2%",
+ path, geteuid());
}
if (S_ISDIR(st.st_mode)) {
@@ -518,11 +522,11 @@ void canonicalisePathMetaData(const Path & path, uid_t fromUid, InodesSeen & ino
be a symlink, since we can't change its ownership. */
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);
if (st.st_uid != geteuid()) {
assert(S_ISLNK(st.st_mode));
- throw Error(format("wrong ownership of top-level store path '%1%'") % path);
+ throw Error("wrong ownership of top-level store path '%1%'", path);
}
}
@@ -859,7 +863,7 @@ void LocalStore::querySubstitutablePathInfos(const StorePathSet & paths,
} catch (SubstituterDisabled &) {
} catch (Error & e) {
if (settings.tryFallback)
- printError(e.what());
+ logError(e.info());
else
throw;
}
@@ -1187,7 +1191,7 @@ void LocalStore::invalidatePathChecked(const StorePath & path)
bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
{
- printError(format("reading the Nix store..."));
+ printInfo(format("reading the Nix store..."));
bool errors = false;
@@ -1219,12 +1223,15 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
Path linkPath = linksDir + "/" + link.name;
string hash = hashPath(htSHA256, linkPath).first.to_string(Base32, false);
if (hash != link.name) {
- printError(
- "link '%s' was modified! expected hash '%s', got '%s'",
- linkPath, link.name, hash);
+ logError({
+ .name = "Invalid hash",
+ .hint = hintfmt(
+ "link '%s' was modified! expected hash '%s', got '%s'",
+ linkPath, link.name, hash)
+ });
if (repair) {
if (unlink(linkPath.c_str()) == 0)
- printError("removed link '%s'", linkPath);
+ printInfo("removed link '%s'", linkPath);
else
throw SysError("removing corrupt link '%s'", linkPath);
} else {
@@ -1254,8 +1261,11 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
auto current = hashSink->finish();
if (info->narHash != nullHash && info->narHash != current.first) {
- printError("path '%s' was modified! expected hash '%s', got '%s'",
- printStorePath(i), info->narHash.to_string(Base32, true), current.first.to_string(Base32, true));
+ logError({
+ .name = "Invalid hash - path modified",
+ .hint = hintfmt("path '%s' was modified! expected hash '%s', got '%s'",
+ printStorePath(i), info->narHash.to_string(Base32, true), current.first.to_string(Base32, true))
+ });
if (repair) repairPath(i); else errors = true;
} else {
@@ -1263,14 +1273,14 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
/* Fill in missing hashes. */
if (info->narHash == nullHash) {
- printError("fixing missing hash on '%s'", printStorePath(i));
+ printInfo("fixing missing hash on '%s'", printStorePath(i));
info->narHash = current.first;
update = true;
}
/* Fill in missing narSize fields (from old stores). */
if (info->narSize == 0) {
- printError("updating size field on '%s' to %s", printStorePath(i), current.second);
+ printInfo("updating size field on '%s' to %s", printStorePath(i), current.second);
info->narSize = current.second;
update = true;
}
@@ -1286,7 +1296,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
/* It's possible that the path got GC'ed, so ignore
errors on invalid paths. */
if (isValidPath(i))
- printError("error: %s", e.msg());
+ logError(e.info());
else
warn(e.msg());
errors = true;
@@ -1306,7 +1316,10 @@ void LocalStore::verifyPath(const Path & pathS, const StringSet & store,
if (!done.insert(pathS).second) return;
if (!isStorePath(pathS)) {
- printError("path '%s' is not in the Nix store", pathS);
+ logError({
+ .name = "Nix path not found",
+ .hint = hintfmt("path '%s' is not in the Nix store", pathS)
+ });
return;
}
@@ -1325,16 +1338,19 @@ void LocalStore::verifyPath(const Path & pathS, const StringSet & store,
}
if (canInvalidate) {
- printError("path '%s' disappeared, removing from database...", pathS);
+ printInfo("path '%s' disappeared, removing from database...", pathS);
auto state(_state.lock());
invalidatePath(*state, path);
} else {
- printError("path '%s' disappeared, but it still has valid referrers!", pathS);
+ logError({
+ .name = "Missing path with referrers",
+ .hint = hintfmt("path '%s' disappeared, but it still has valid referrers!", pathS)
+ });
if (repair)
try {
repairPath(path);
} catch (Error & e) {
- warn(e.msg());
+ logWarning(e.info());
errors = true;
}
else errors = true;
@@ -1374,7 +1390,7 @@ static void makeMutable(const Path & path)
AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
if (fd == -1) {
if (errno == ELOOP) return; // it's a symlink
- throw SysError(format("opening file '%1%'") % path);
+ throw SysError("opening file '%1%'", path);
}
unsigned int flags = 0, old;
@@ -1392,7 +1408,7 @@ static void makeMutable(const Path & path)
void LocalStore::upgradeStore7()
{
if (getuid() != 0) return;
- printError("removing immutable bits from the Nix store (this may take a while)...");
+ printInfo("removing immutable bits from the Nix store (this may take a while)...");
makeMutable(realStoreDir);
}
diff --git a/src/libstore/nar-accessor.cc b/src/libstore/nar-accessor.cc
index b74480684..ca663d837 100644
--- a/src/libstore/nar-accessor.cc
+++ b/src/libstore/nar-accessor.cc
@@ -184,7 +184,7 @@ struct NarAccessor : public FSAccessor
auto i = get(path);
if (i.type != FSAccessor::Type::tDirectory)
- throw Error(format("path '%1%' inside NAR file is not a directory") % path);
+ throw Error("path '%1%' inside NAR file is not a directory", path);
StringSet res;
for (auto & child : i.children)
@@ -197,7 +197,7 @@ struct NarAccessor : public FSAccessor
{
auto i = get(path);
if (i.type != FSAccessor::Type::tRegular)
- throw Error(format("path '%1%' inside NAR file is not a regular file") % path);
+ throw Error("path '%1%' inside NAR file is not a regular file", path);
if (getNarBytes) return getNarBytes(i.start, i.size);
@@ -209,7 +209,7 @@ struct NarAccessor : public FSAccessor
{
auto i = get(path);
if (i.type != FSAccessor::Type::tSymlink)
- throw Error(format("path '%1%' inside NAR file is not a symlink") % path);
+ throw Error("path '%1%' inside NAR file is not a symlink", path);
return i.target;
}
};
diff --git a/src/libstore/nar-info.cc b/src/libstore/nar-info.cc
index 6b16be08a..232284723 100644
--- a/src/libstore/nar-info.cc
+++ b/src/libstore/nar-info.cc
@@ -7,7 +7,7 @@ NarInfo::NarInfo(const Store & store, const std::string & s, const std::string &
: ValidPathInfo(StorePath::dummy.clone()) // FIXME: hack
{
auto corrupt = [&]() {
- throw Error(format("NAR info file '%1%' is corrupt") % whence);
+ throw Error("NAR info file '%1%' is corrupt", whence);
};
auto parseHashField = [&](const string & s) {
diff --git a/src/libstore/optimise-store.cc b/src/libstore/optimise-store.cc
index 3f4b72b9c..d760d110c 100644
--- a/src/libstore/optimise-store.cc
+++ b/src/libstore/optimise-store.cc
@@ -19,9 +19,9 @@ static void makeWritable(const Path & path)
{
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);
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
- throw SysError(format("changing writability of '%1%'") % path);
+ throw SysError("changing writability of '%1%'", path);
}
@@ -47,7 +47,7 @@ LocalStore::InodeHash LocalStore::loadInodeHash()
InodeHash inodeHash;
AutoCloseDir dir(opendir(linksDir.c_str()));
- if (!dir) throw SysError(format("opening directory '%1%'") % linksDir);
+ if (!dir) throw SysError("opening directory '%1%'", linksDir);
struct dirent * dirent;
while (errno = 0, dirent = readdir(dir.get())) { /* sic */
@@ -55,7 +55,7 @@ LocalStore::InodeHash LocalStore::loadInodeHash()
// We don't care if we hit non-hash files, anything goes
inodeHash.insert(dirent->d_ino);
}
- if (errno) throw SysError(format("reading directory '%1%'") % linksDir);
+ if (errno) throw SysError("reading directory '%1%'", linksDir);
printMsg(lvlTalkative, format("loaded %1% hash inodes") % inodeHash.size());
@@ -68,7 +68,7 @@ Strings LocalStore::readDirectoryIgnoringInodes(const Path & path, const InodeHa
Strings names;
AutoCloseDir dir(opendir(path.c_str()));
- if (!dir) throw SysError(format("opening directory '%1%'") % path);
+ if (!dir) throw SysError("opening directory '%1%'", path);
struct dirent * dirent;
while (errno = 0, dirent = readdir(dir.get())) { /* sic */
@@ -83,7 +83,7 @@ Strings LocalStore::readDirectoryIgnoringInodes(const Path & path, const InodeHa
if (name == "." || name == "..") continue;
names.push_back(name);
}
- if (errno) throw SysError(format("reading directory '%1%'") % path);
+ if (errno) throw SysError("reading directory '%1%'", path);
return names;
}
@@ -96,7 +96,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
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);
#if __APPLE__
/* HFS/macOS has some undocumented security feature disabling hardlinking for
@@ -130,7 +130,10 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
NixOS (example: $fontconfig/var/cache being modified). Skip
those files. FIXME: check the modification time. */
if (S_ISREG(st.st_mode) && (st.st_mode & S_IWUSR)) {
- printError(format("skipping suspicious writable file '%1%'") % path);
+ logWarning({
+ .name = "Suspicious file",
+ .hint = hintfmt("skipping suspicious writable file '%1%'", path)
+ });
return;
}
@@ -186,7 +189,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
current file with a hard link to that file. */
struct stat stLink;
if (lstat(linkPath.c_str(), &stLink))
- throw SysError(format("getting attributes of path '%1%'") % linkPath);
+ throw SysError("getting attributes of path '%1%'", linkPath);
if (st.st_ino == stLink.st_ino) {
debug(format("'%1%' is already linked to '%2%'") % path % linkPath);
@@ -194,7 +197,10 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
}
if (st.st_size != stLink.st_size) {
- printError(format("removing corrupted link '%1%'") % linkPath);
+ logWarning({
+ .name = "Corrupted link",
+ .hint = hintfmt("removing corrupted link '%1%'", linkPath)
+ });
unlink(linkPath.c_str());
goto retry;
}
@@ -229,7 +235,10 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
/* Atomically replace the old file with the new hard link. */
if (rename(tempLink.c_str(), path.c_str()) == -1) {
if (unlink(tempLink.c_str()) == -1)
- printError(format("unable to unlink '%1%'") % tempLink);
+ logError({
+ .name = "Unlink error",
+ .hint = hintfmt("unable to unlink '%1%'", tempLink)
+ });
if (errno == EMLINK) {
/* Some filesystems generate too many links on the rename,
rather than on the original link. (Probably it
@@ -238,7 +247,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
debug("'%s' has reached maximum number of links", linkPath);
return;
}
- throw SysError(format("cannot rename '%1%' to '%2%'") % tempLink % path);
+ throw SysError("cannot rename '%1%' to '%2%'", tempLink, path);
}
stats.filesLinked++;
diff --git a/src/libstore/pathlocks.cc b/src/libstore/pathlocks.cc
index 2635e3940..926f4ea1e 100644
--- a/src/libstore/pathlocks.cc
+++ b/src/libstore/pathlocks.cc
@@ -20,7 +20,7 @@ AutoCloseFD openLockFile(const Path & path, bool create)
fd = open(path.c_str(), O_CLOEXEC | O_RDWR | (create ? O_CREAT : 0), 0600);
if (!fd && (create || errno != ENOENT))
- throw SysError(format("opening lock file '%1%'") % path);
+ throw SysError("opening lock file '%1%'", path);
return fd;
}
@@ -51,7 +51,7 @@ bool lockFile(int fd, LockType lockType, bool wait)
while (flock(fd, type) != 0) {
checkInterrupt();
if (errno != EINTR)
- throw SysError(format("acquiring/releasing lock"));
+ throw SysError("acquiring/releasing lock");
else
return false;
}
@@ -60,7 +60,7 @@ bool lockFile(int fd, LockType lockType, bool wait)
checkInterrupt();
if (errno == EWOULDBLOCK) return false;
if (errno != EINTR)
- throw SysError(format("acquiring/releasing lock"));
+ throw SysError("acquiring/releasing lock");
}
}
@@ -124,7 +124,7 @@ bool PathLocks::lockPaths(const PathSet & paths,
hasn't been unlinked). */
struct stat st;
if (fstat(fd.get(), &st) == -1)
- throw SysError(format("statting lock file '%1%'") % lockPath);
+ throw SysError("statting lock file '%1%'", lockPath);
if (st.st_size != 0)
/* This lock file has been unlinked, so we're holding
a lock on a deleted file. This means that other
@@ -160,7 +160,8 @@ void PathLocks::unlock()
if (close(i.first) == -1)
printError(
- format("error (ignored): cannot close lock file on '%1%'") % i.second);
+ "error (ignored): cannot close lock file on '%1%'",
+ i.second);
debug(format("lock released on '%1%'") % i.second);
}
diff --git a/src/libstore/profiles.cc b/src/libstore/profiles.cc
index 2bef51878..6cfe393a4 100644
--- a/src/libstore/profiles.cc
+++ b/src/libstore/profiles.cc
@@ -50,7 +50,7 @@ Generations findGenerations(Path profile, int & curGen)
gen.number = n;
struct stat st;
if (lstat(gen.path.c_str(), &st) != 0)
- throw SysError(format("statting '%1%'") % gen.path);
+ throw SysError("statting '%1%'", gen.path);
gen.creationTime = st.st_mtime;
gens.push_back(gen);
}
@@ -117,7 +117,7 @@ Path createGeneration(ref<LocalFSStore> store, Path profile, Path outPath)
static void removeFile(const Path & path)
{
if (remove(path.c_str()) == -1)
- throw SysError(format("cannot unlink '%1%'") % path);
+ throw SysError("cannot unlink '%1%'", path);
}
@@ -149,7 +149,7 @@ void deleteGenerations(const Path & profile, const std::set<unsigned int> & gens
Generations gens = findGenerations(profile, curGen);
if (gensToDelete.find(curGen) != gensToDelete.end())
- throw Error(format("cannot delete current generation of profile %1%'") % profile);
+ throw Error("cannot delete current generation of profile %1%'", profile);
for (auto & i : gens) {
if (gensToDelete.find(i.number) == gensToDelete.end()) continue;
@@ -226,7 +226,7 @@ void deleteGenerationsOlderThan(const Path & profile, const string & timeSpec, b
int days;
if (!string2Int(strDays, days) || days < 1)
- throw Error(format("invalid number of days specifier '%1%'") % timeSpec);
+ throw Error("invalid number of days specifier '%1%'", timeSpec);
time_t oldTime = curTime - days * 24 * 3600;
diff --git a/src/libstore/references.cc b/src/libstore/references.cc
index 102e15921..a10d536a3 100644
--- a/src/libstore/references.cc
+++ b/src/libstore/references.cc
@@ -92,7 +92,7 @@ PathSet scanForReferences(const string & path,
auto baseName = std::string(baseNameOf(i));
string::size_type pos = baseName.find('-');
if (pos == string::npos)
- throw Error(format("bad reference '%1%'") % i);
+ throw Error("bad reference '%1%'", i);
string s = string(baseName, 0, pos);
assert(s.size() == refLength);
assert(backMap.find(s) == backMap.end());
diff --git a/src/libstore/remote-fs-accessor.cc b/src/libstore/remote-fs-accessor.cc
index 5a2d103b9..9277a8e6b 100644
--- a/src/libstore/remote-fs-accessor.cc
+++ b/src/libstore/remote-fs-accessor.cc
@@ -51,7 +51,7 @@ std::pair<ref<FSAccessor>, Path> RemoteFSAccessor::fetch(const Path & path_)
std::string restPath = std::string(path, storePath.size());
if (!store->isValidPath(store->parseStorePath(storePath)))
- throw InvalidPath(format("path '%1%' is not a valid store path") % storePath);
+ throw InvalidPath("path '%1%' is not a valid store path", storePath);
auto i = nars.find(storePath);
if (i != nars.end()) return {i->second, restPath};
diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc
index 735f59a91..99fee8150 100644
--- a/src/libstore/remote-store.cc
+++ b/src/libstore/remote-store.cc
@@ -116,11 +116,11 @@ ref<RemoteStore::Connection> UDSRemoteStore::openConnection()
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
if (socketPath.size() + 1 >= sizeof(addr.sun_path))
- throw Error(format("socket path '%1%' is too long") % socketPath);
+ throw Error("socket path '%1%' is too long", socketPath);
strcpy(addr.sun_path, socketPath.c_str());
if (::connect(conn->fd.get(), (struct sockaddr *) &addr, sizeof(addr)) == -1)
- throw SysError(format("cannot connect to daemon at '%1%'") % socketPath);
+ throw SysError("cannot connect to daemon at '%1%'", socketPath);
conn->from.fd = conn->fd.get();
conn->to.fd = conn->fd.get();
@@ -365,7 +365,7 @@ void RemoteStore::queryPathInfoUncached(const StorePath & path,
} catch (Error & e) {
// Ugly backwards compatibility hack.
if (e.msg().find("is not valid") != std::string::npos)
- throw InvalidPath(e.what());
+ throw InvalidPath(e.info());
throw;
}
if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 17) {
diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc
index b24e7b7d6..427dd48ce 100644
--- a/src/libstore/s3-binary-cache-store.cc
+++ b/src/libstore/s3-binary-cache-store.cc
@@ -32,8 +32,10 @@ namespace nix {
struct S3Error : public Error
{
Aws::S3::S3Errors err;
- S3Error(Aws::S3::S3Errors err, const FormatOrString & fs)
- : Error(fs), err(err) { };
+
+ template<typename... Args>
+ S3Error(Aws::S3::S3Errors err, const Args & ... args)
+ : Error(args...), err(err) { };
};
/* Helper: given an Outcome<R, E>, return R in case of success, or
@@ -109,7 +111,9 @@ class RetryStrategy : public Aws::Client::DefaultRetryStrategy
auto retry = Aws::Client::DefaultRetryStrategy::ShouldRetry(error, attemptedRetries);
if (retry)
printError("AWS error '%s' (%s), will retry in %d ms",
- error.GetExceptionName(), error.GetMessage(), CalculateDelayBeforeNextRetry(error, attemptedRetries));
+ error.GetExceptionName(),
+ error.GetMessage(),
+ CalculateDelayBeforeNextRetry(error, attemptedRetries));
return retry;
}
};
@@ -249,7 +253,7 @@ struct S3BinaryCacheStoreImpl : public S3BinaryCacheStore
// If bucket listing is disabled, 404s turn into 403s
|| error.GetErrorType() == Aws::S3::S3Errors::ACCESS_DENIED)
return false;
- throw Error(format("AWS error fetching '%s': %s") % path % error.GetMessage());
+ throw Error("AWS error fetching '%s': %s", path, error.GetMessage());
}
return true;
diff --git a/src/libstore/sqlite.cc b/src/libstore/sqlite.cc
index eb1daafc5..16cdb6619 100644
--- a/src/libstore/sqlite.cc
+++ b/src/libstore/sqlite.cc
@@ -29,7 +29,7 @@ SQLite::SQLite(const Path & path, bool create)
{
if (sqlite3_open_v2(path.c_str(), &db,
SQLITE_OPEN_READWRITE | (create ? SQLITE_OPEN_CREATE : 0), 0) != SQLITE_OK)
- throw Error(format("cannot open SQLite database '%s'") % path);
+ throw Error("cannot open SQLite database '%s'", path);
if (sqlite3_busy_timeout(db, 60 * 60 * 1000) != SQLITE_OK)
throwSQLiteError(db, "setting timeout");
@@ -204,7 +204,10 @@ void handleSQLiteBusy(const SQLiteBusy & e)
if (now > lastWarned + 10) {
lastWarned = now;
- printError("warning: %s", e.what());
+ logWarning(
+ ErrorInfo { .name = "Sqlite busy",
+ .hint = hintfmt(e.what())
+ });
}
/* Sleep for a while since retrying the transaction right away
diff --git a/src/libstore/sqlite.hh b/src/libstore/sqlite.hh
index fd04c9b07..dd81ab051 100644
--- a/src/libstore/sqlite.hh
+++ b/src/libstore/sqlite.hh
@@ -3,7 +3,7 @@
#include <functional>
#include <string>
-#include "types.hh"
+#include "error.hh"
struct sqlite3;
struct sqlite3_stmt;
diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc
index f6901bf42..464261010 100644
--- a/src/libstore/store-api.cc
+++ b/src/libstore/store-api.cc
@@ -23,7 +23,7 @@ bool Store::isInStore(const Path & path) const
Path Store::toStorePath(const Path & path) const
{
if (!isInStore(path))
- throw Error(format("path '%1%' is not in the Nix store") % path);
+ throw Error("path '%1%' is not in the Nix store", path);
Path::size_type slash = path.find('/', storeDir.size() + 1);
if (slash == Path::npos)
return path;
@@ -775,7 +775,11 @@ void ValidPathInfo::sign(const Store & store, const SecretKey & secretKey)
bool ValidPathInfo::isContentAddressed(const Store & store) const
{
auto warn = [&]() {
- printError("warning: path '%s' claims to be content-addressed but isn't", store.printStorePath(path));
+ logWarning(
+ ErrorInfo{
+ .name = "Path not content-addressed",
+ .hint = hintfmt("path '%s' claims to be content-addressed but isn't", store.printStorePath(path))
+ });
};
if (hasPrefix(ca, "text:")) {
@@ -934,7 +938,7 @@ std::list<ref<Store>> getDefaultSubstituters()
try {
stores.push_back(openStore(uri));
} catch (Error & e) {
- printError("warning: %s", e.what());
+ logWarning(e.info());
}
};
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/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 afeaf4cea..ce6580119 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;
}
diff --git a/src/libutil/compression.cc b/src/libutil/compression.cc
index 860b04adb..a117ddc72 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/error.cc b/src/libutil/error.cc
index a5571d4ec..1fcb8111c 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,80 +44,118 @@ 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 = " ";
+ 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::lvlError: {
+ levelString = ANSI_RED;
+ levelString += "error:";
+ levelString += ANSI_NORMAL;
+ break;
+ }
+ case Verbosity::lvlWarn: {
+ levelString = ANSI_YELLOW;
+ levelString += "warning:";
+ levelString += ANSI_NORMAL;
+ break;
+ }
+ case Verbosity::lvlInfo: {
+ levelString = ANSI_GREEN;
+ levelString += "info:";
+ levelString += ANSI_NORMAL;
+ break;
+ }
+ case Verbosity::lvlTalkative: {
+ levelString = ANSI_GREEN;
+ levelString += "talk:";
+ levelString += ANSI_NORMAL;
+ break;
+ }
+ case Verbosity::lvlChatty: {
+ levelString = ANSI_GREEN;
+ levelString += "chat:";
+ levelString += ANSI_NORMAL;
+ break;
+ }
+ case Verbosity::lvlVomit: {
+ levelString = ANSI_GREEN;
+ levelString += "vomit:";
+ levelString += ANSI_NORMAL;
+ break;
+ }
+ case Verbosity::lvlDebug: {
+ levelString = ANSI_YELLOW;
+ levelString += "debug:";
+ levelString += ANSI_NORMAL;
+ break;
+ }
+ default: {
+ levelString = fmt("invalid error level: %1%", einfo.level);
+ break;
+ }
}
int ndl = prefix.length() + levelString.length() + 3 + einfo.name.length() + einfo.programName.value_or("").length();
@@ -99,48 +166,58 @@ void printErrorInfo(const ErrorInfo &einfo)
dashes.append("-");
// 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..8a48fa105 100644
--- a/src/libutil/error.hh
+++ b/src/libutil/error.hh
@@ -1,121 +1,181 @@
-#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 <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.
+
+*/
+
+typedef enum {
+ lvlError = 0,
+ lvlWarn,
+ lvlInfo,
+ lvlTalkative,
+ lvlChatty,
+ lvlDebug,
+ lvlVomit
+} Verbosity;
+
+// 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 = lvlError,
+ .hint = hintfmt(args...)
+ }
+ , status(status)
+ { }
+
+ template<typename... Args>
+ BaseError(const Args & ... args)
+ : err { .level = lvlError,
+ .hint = hintfmt(args...)
+ }
+ { }
+
+ BaseError(hintformat hint)
+ : err { .level = lvlError,
+ .hint = hint
+ }
+ { }
+
+ BaseError(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/logging.cc b/src/libutil/logging.cc
index 15cbc1589..108dc3bd1 100644
--- a/src/libutil/logging.cc
+++ b/src/libutil/logging.cc
@@ -69,9 +69,17 @@ 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 + "...");
@@ -126,8 +134,7 @@ 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) { }
@@ -163,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
{
@@ -253,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 e3d91e01f..b99b246c3 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 {
typedef enum {
- lvlError = 0,
- lvlWarn,
- lvlInfo,
- lvlTalkative,
- lvlChatty,
- lvlDebug,
- lvlVomit
-} Verbosity;
-
-typedef enum {
actUnknown = 0,
actCopyPath = 100,
actFileTransfer = 101,
@@ -75,6 +66,14 @@ public:
log(lvlInfo, 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,
@@ -156,9 +155,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(lvlError, errorInfo)
+#define logWarning(errorInfo...) logErrorInfo(lvlWarn, 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) { \
@@ -172,6 +185,7 @@ extern Verbosity verbosity; /* suppress msgs > this */
#define debug(args...) printMsg(lvlDebug, args)
#define vomit(args...) printMsg(lvlVomit, args)
+/* if verbosity >= lvlWarn, 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/serialise.cc b/src/libutil/serialise.cc
index 8201549fd..35f7ee917 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(ErrorInfo {
+ .name = "Large path",
+ .description = "dumping very large path (> 256 MiB); this may run out of memory"
+ });
}
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..fbdc91253
--- /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 = lvlInfo,
+ .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 = lvlTalkative;
+
+ testing::internal::CaptureStderr();
+
+ logger->logEI({ .level = lvlTalkative,
+ .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 = lvlChatty;
+
+ testing::internal::CaptureStderr();
+
+ logger->logEI({ .level = lvlChatty,
+ .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 = lvlDebug;
+
+ testing::internal::CaptureStderr();
+
+ logger->logEI({ .level = lvlDebug,
+ .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 = lvlVomit;
+
+ testing::internal::CaptureStderr();
+
+ logger->logEI({ .level = lvlVomit,
+ .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/types.hh b/src/libutil/types.hh
index 250c9581d..89ae108f9 100644
--- a/src/libutil/types.hh
+++ b/src/libutil/types.hh
@@ -3,157 +3,24 @@
#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;
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 b66447e08..a2281237b 100644
--- a/src/libutil/util.cc
+++ b/src/libutil/util.cc
@@ -40,24 +40,6 @@ extern char * * environ;
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
@@ -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());
}
}
@@ -1464,17 +1447,6 @@ string base64Decode(std::string_view 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 c95232317..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"
diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc
index 8649de5e9..591fff999 100755
--- a/src/nix-build/nix-build.cc
+++ b/src/nix-build/nix-build.cc
@@ -368,7 +368,12 @@ static void _main(int argc, char * * argv)
shell = drv->queryOutPath() + "/bin/bash";
} catch (Error & e) {
- printError("warning: %s; will use bash from your environment", e.what());
+ logWarning(
+ ErrorInfo {
+ .name = "bashInteractive",
+ .hint = hintfmt("%s; will use bash from your environment",
+ (e.info().hint ? e.info().hint->str() : ""))
+ });
shell = "bash";
}
}
diff --git a/src/nix-channel/nix-channel.cc b/src/nix-channel/nix-channel.cc
index abd390414..3ccf620c9 100755
--- a/src/nix-channel/nix-channel.cc
+++ b/src/nix-channel/nix-channel.cc
@@ -38,7 +38,7 @@ static void writeChannels()
{
auto channelsFD = AutoCloseFD{open(channelsList.c_str(), O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC, 0644)};
if (!channelsFD)
- throw SysError(format("opening '%1%' for writing") % channelsList);
+ throw SysError("opening '%1%' for writing", channelsList);
for (const auto & channel : channels)
writeFull(channelsFD.get(), channel.second + " " + channel.first + "\n");
}
@@ -47,9 +47,9 @@ static void writeChannels()
static void addChannel(const string & url, const string & name)
{
if (!regex_search(url, std::regex("^(file|http|https)://")))
- throw Error(format("invalid channel URL '%1%'") % url);
+ throw Error("invalid channel URL '%1%'", url);
if (!regex_search(name, std::regex("^[a-zA-Z0-9_][a-zA-Z0-9_\\.-]*$")))
- throw Error(format("invalid channel identifier '%1%'") % name);
+ throw Error("invalid channel identifier '%1%'", name);
readChannels();
channels[name] = url;
writeChannels();
@@ -137,9 +137,9 @@ static void update(const StringSet & channelNames)
if (S_ISLNK(st.st_mode))
// old-skool ~/.nix-defexpr
if (unlink(nixDefExpr.c_str()) == -1)
- throw SysError(format("unlinking %1%") % nixDefExpr);
+ throw SysError("unlinking %1%", nixDefExpr);
} else if (errno != ENOENT) {
- throw SysError(format("getting status of %1%") % nixDefExpr);
+ throw SysError("getting status of %1%", nixDefExpr);
}
createDirs(nixDefExpr);
auto channelLink = nixDefExpr + "/channels";
diff --git a/src/nix-daemon/nix-daemon.cc b/src/nix-daemon/nix-daemon.cc
index e68d1b1be..582c78d14 100644
--- a/src/nix-daemon/nix-daemon.cc
+++ b/src/nix-daemon/nix-daemon.cc
@@ -36,7 +36,7 @@ using namespace nix::daemon;
#define SPLICE_F_MOVE 0
static ssize_t splice(int fd_in, void *off_in, int fd_out, void *off_out, size_t len, unsigned int flags)
{
- /* We ignore most parameters, we just have them for conformance with the linux syscall */
+ // We ignore most parameters, we just have them for conformance with the linux syscall
std::vector<char> buf(8192);
auto read_count = read(fd_in, buf.data(), buf.size());
if (read_count == -1)
@@ -57,7 +57,7 @@ static void sigChldHandler(int sigNo)
{
// Ensure we don't modify errno of whatever we've interrupted
auto saved_errno = errno;
- /* Reap all dead children. */
+ // Reap all dead children.
while (waitpid(-1, 0, WNOHANG) > 0) ;
errno = saved_errno;
}
@@ -106,7 +106,7 @@ struct PeerInfo
};
-/* Get the identity of the caller, if possible. */
+// Get the identity of the caller, if possible.
static PeerInfo getPeerInfo(int remote)
{
PeerInfo peer = { false, 0, false, 0, false, 0 };
@@ -154,13 +154,12 @@ static void daemonLoop(char * * argv)
if (chdir("/") == -1)
throw SysError("cannot change current directory");
- /* Get rid of children automatically; don't let them become
- zombies. */
+ // Get rid of children automatically; don't let them become zombies.
setSigChldAction(true);
AutoCloseFD fdSocket;
- /* Handle socket-based activation by systemd. */
+ // Handle socket-based activation by systemd.
auto listenFds = getEnv("LISTEN_FDS");
if (listenFds) {
if (getEnv("LISTEN_PID") != std::to_string(getpid()) || listenFds != "1")
@@ -169,17 +168,17 @@ static void daemonLoop(char * * argv)
closeOnExec(fdSocket.get());
}
- /* Otherwise, create and bind to a Unix domain socket. */
+ // Otherwise, create and bind to a Unix domain socket.
else {
createDirs(dirOf(settings.nixDaemonSocketFile));
fdSocket = createUnixDomainSocket(settings.nixDaemonSocketFile, 0666);
}
- /* Loop accepting connections. */
+ // Loop accepting connections.
while (1) {
try {
- /* Accept a connection. */
+ // Accept a connection.
struct sockaddr_un remoteAddr;
socklen_t remoteAddrLen = sizeof(remoteAddr);
@@ -209,13 +208,13 @@ static void daemonLoop(char * * argv)
trusted = Trusted;
if ((!trusted && !matchUser(user, group, allowedUsers)) || group == settings.buildUsersGroup)
- throw Error(format("user '%1%' is not allowed to connect to the Nix daemon") % user);
+ throw Error("user '%1%' is not allowed to connect to the Nix daemon", user);
printInfo(format((string) "accepted connection from pid %1%, user %2%" + (trusted ? " (trusted)" : ""))
% (peer.pidKnown ? std::to_string(peer.pid) : "<unknown>")
% (peer.uidKnown ? user : "<unknown>"));
- /* Fork a child to handle the connection. */
+ // Fork a child to handle the connection.
ProcessOptions options;
options.errorPrefix = "unexpected Nix daemon error: ";
options.dieWithParent = false;
@@ -224,20 +223,20 @@ static void daemonLoop(char * * argv)
startProcess([&]() {
fdSocket = -1;
- /* Background the daemon. */
+ // Background the daemon.
if (setsid() == -1)
- throw SysError(format("creating a new session"));
+ throw SysError("creating a new session");
- /* Restore normal handling of SIGCHLD. */
+ // Restore normal handling of SIGCHLD.
setSigChldAction(false);
- /* For debugging, stuff the pid into argv[1]. */
+ // For debugging, stuff the pid into argv[1].
if (peer.pidKnown && argv[1]) {
string processName = std::to_string(peer.pid);
strncpy(argv[1], processName.c_str(), strlen(argv[1]));
}
- /* Handle the connection. */
+ // Handle the connection.
FdSource from(remote.get());
FdSink to(remote.get());
processConnection(openUncachedStore(), from, to, trusted, NotRecursive, user, peer.uid);
@@ -247,8 +246,11 @@ static void daemonLoop(char * * argv)
} catch (Interrupted & e) {
return;
- } catch (Error & e) {
- printError(format("error processing connection: %1%") % e.msg());
+ } catch (Error & error) {
+ ErrorInfo ei = error.info();
+ ei.hint = std::optional(hintfmt("error processing connection: %1%",
+ (error.info().hint.has_value() ? error.info().hint->str() : "")));
+ logError(ei);
}
}
}
@@ -261,7 +263,7 @@ static int _main(int argc, char * * argv)
parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) {
if (*arg == "--daemon")
- ; /* ignored for backwards compatibility */
+ ; // ignored for backwards compatibility
else if (*arg == "--help")
showManPage("nix-daemon");
else if (*arg == "--version")
@@ -276,7 +278,7 @@ static int _main(int argc, char * * argv)
if (stdio) {
if (getStoreType() == tDaemon) {
- /* Forward on this connection to the real daemon */
+ // Forward on this connection to the real daemon
auto socketPath = settings.nixDaemonSocketFile;
auto s = socket(PF_UNIX, SOCK_STREAM, 0);
if (s == -1)
@@ -284,17 +286,17 @@ static int _main(int argc, char * * argv)
auto socketDir = dirOf(socketPath);
if (chdir(socketDir.c_str()) == -1)
- throw SysError(format("changing to socket directory '%1%'") % socketDir);
+ throw SysError("changing to socket directory '%1%'", socketDir);
auto socketName = std::string(baseNameOf(socketPath));
auto addr = sockaddr_un{};
addr.sun_family = AF_UNIX;
if (socketName.size() + 1 >= sizeof(addr.sun_path))
- throw Error(format("socket name %1% is too long") % socketName);
+ throw Error("socket name %1% is too long", socketName);
strcpy(addr.sun_path, socketName.c_str());
if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) == -1)
- throw SysError(format("cannot connect to daemon at %1%") % socketPath);
+ throw SysError("cannot connect to daemon at %1%", socketPath);
auto nfds = (s > STDIN_FILENO ? s : STDIN_FILENO) + 1;
while (true) {
diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc
index f7b04eb2b..7ab5ba500 100644
--- a/src/nix-env/nix-env.cc
+++ b/src/nix-env/nix-env.cc
@@ -25,7 +25,6 @@
#include <sys/stat.h>
#include <unistd.h>
-
using namespace nix;
using std::cout;
@@ -70,8 +69,7 @@ typedef void (* Operation) (Globals & globals,
static string needArg(Strings::iterator & i,
Strings & args, const string & arg)
{
- if (i == args.end()) throw UsageError(
- format("'%1%' requires an argument") % arg);
+ if (i == args.end()) throw UsageError("'%1%' requires an argument", arg);
return *i++;
}
@@ -125,7 +123,10 @@ static void getAllExprs(EvalState & state,
if (hasSuffix(attrName, ".nix"))
attrName = string(attrName, 0, attrName.size() - 4);
if (!attrs.insert(attrName).second) {
- printError(format("warning: name collision in input Nix expressions, skipping '%1%'") % path2);
+ logError({
+ .name = "Name collision",
+ .hint = hintfmt("warning: name collision in input Nix expressions, skipping '%1%'", path2)
+ });
continue;
}
/* Load the expression on demand. */
@@ -133,7 +134,7 @@ static void getAllExprs(EvalState & state,
Value & vArg(*state.allocValue());
mkString(vArg, path2);
if (v.attrs->size() == v.attrs->capacity())
- throw Error(format("too many Nix expressions in directory '%1%'") % path);
+ throw Error("too many Nix expressions in directory '%1%'", path);
mkApp(*state.allocAttr(v, state.symbols.create(attrName)), vFun, vArg);
}
else if (S_ISDIR(st.st_mode))
@@ -144,11 +145,12 @@ static void getAllExprs(EvalState & state,
}
+
static void loadSourceExpr(EvalState & state, const Path & path, Value & v)
{
struct stat st;
if (stat(path.c_str(), &st) == -1)
- throw SysError(format("getting information about '%1%'") % path);
+ throw SysError("getting information about '%1%'", path);
if (isNixExpr(path, st))
state.evalFile(path, v);
@@ -221,7 +223,7 @@ static void checkSelectorUse(DrvNames & selectors)
/* Check that all selectors have been used. */
for (auto & i : selectors)
if (i.hits == 0 && i.fullName != "*")
- throw Error(format("selector '%1%' matches no derivations") % i.fullName);
+ throw Error("selector '%1%' matches no derivations", i.fullName);
}
@@ -507,7 +509,7 @@ static void opInstall(Globals & globals, Strings opFlags, Strings opArgs)
globals.preserveInstalled = true;
else if (arg == "--remove-all" || arg == "-r")
globals.removeAll = true;
- else throw UsageError(format("unknown flag '%1%'") % arg);
+ else throw UsageError("unknown flag '%1%'", arg);
}
installDerivations(globals, opArgs, globals.profile);
@@ -618,7 +620,7 @@ static void opUpgrade(Globals & globals, Strings opFlags, Strings opArgs)
else if (arg == "--leq") upgradeType = utLeq;
else if (arg == "--eq") upgradeType = utEq;
else if (arg == "--always") upgradeType = utAlways;
- else throw UsageError(format("unknown flag '%1%'") % arg);
+ else throw UsageError("unknown flag '%1%'", arg);
}
upgradeDerivations(globals, opArgs, upgradeType);
@@ -637,7 +639,7 @@ static void setMetaFlag(EvalState & state, DrvInfo & drv,
static void opSetFlag(Globals & globals, Strings opFlags, Strings opArgs)
{
if (opFlags.size() > 0)
- throw UsageError(format("unknown flag '%1%'") % opFlags.front());
+ throw UsageError("unknown flag '%1%'", opFlags.front());
if (opArgs.size() < 2)
throw UsageError("not enough arguments to '--set-flag'");
@@ -680,7 +682,7 @@ static void opSet(Globals & globals, Strings opFlags, Strings opArgs)
for (Strings::iterator i = opFlags.begin(); i != opFlags.end(); ) {
string arg = *i++;
if (parseInstallSourceOptions(globals, i, opFlags, arg)) ;
- else throw UsageError(format("unknown flag '%1%'") % arg);
+ else throw UsageError("unknown flag '%1%'", arg);
}
DrvInfos elems;
@@ -759,7 +761,7 @@ static void uninstallDerivations(Globals & globals, Strings & selectors,
static void opUninstall(Globals & globals, Strings opFlags, Strings opArgs)
{
if (opFlags.size() > 0)
- throw UsageError(format("unknown flag '%1%'") % opFlags.front());
+ throw UsageError("unknown flag '%1%'", opFlags.front());
uninstallDerivations(globals, opArgs, globals.profile);
}
@@ -872,7 +874,11 @@ static void queryJSON(Globals & globals, vector<DrvInfo> & elems)
auto placeholder = metaObj.placeholder(j);
Value * v = i.queryMeta(j);
if (!v) {
- printError("derivation '%s' has invalid meta attribute '%s'", i.queryName(), j);
+ logError({
+ .name = "Invalid meta attribute",
+ .hint = hintfmt("derivation '%s' has invalid meta attribute '%s'",
+ i.queryName(), j)
+ });
placeholder.write(nullptr);
} else {
PathSet context;
@@ -922,7 +928,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
else if (arg == "--attr" || arg == "-A")
attrPath = needArg(i, opFlags, arg);
else
- throw UsageError(format("unknown flag '%1%'") % arg);
+ throw UsageError("unknown flag '%1%'", arg);
}
if (printAttrPath && source != sAvailable)
@@ -1122,8 +1128,13 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
XMLAttrs attrs2;
attrs2["name"] = j;
Value * v = i.queryMeta(j);
- if (!v)
- printError("derivation '%s' has invalid meta attribute '%s'", i.queryName(), j);
+ if (!v)
+ logError({
+ .name = "Invalid meta attribute",
+ .hint = hintfmt(
+ "derivation '%s' has invalid meta attribute '%s'",
+ i.queryName(), j)
+ });
else {
if (v->type == tString) {
attrs2["type"] = "string";
@@ -1188,9 +1199,9 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
static void opSwitchProfile(Globals & globals, Strings opFlags, Strings opArgs)
{
if (opFlags.size() > 0)
- throw UsageError(format("unknown flag '%1%'") % opFlags.front());
+ throw UsageError("unknown flag '%1%'", opFlags.front());
if (opArgs.size() != 1)
- throw UsageError(format("exactly one argument expected"));
+ throw UsageError("exactly one argument expected");
Path profile = absPath(opArgs.front());
Path profileLink = getHome() + "/.nix-profile";
@@ -1218,10 +1229,10 @@ static void switchGeneration(Globals & globals, int dstGen)
if (!dst) {
if (dstGen == prevGen)
- throw Error(format("no generation older than the current (%1%) exists")
- % curGen);
+ throw Error("no generation older than the current (%1%) exists",
+ curGen);
else
- throw Error(format("generation %1% does not exist") % dstGen);
+ throw Error("generation %1% does not exist", dstGen);
}
printInfo(format("switching from generation %1% to %2%")
@@ -1236,13 +1247,13 @@ static void switchGeneration(Globals & globals, int dstGen)
static void opSwitchGeneration(Globals & globals, Strings opFlags, Strings opArgs)
{
if (opFlags.size() > 0)
- throw UsageError(format("unknown flag '%1%'") % opFlags.front());
+ throw UsageError("unknown flag '%1%'", opFlags.front());
if (opArgs.size() != 1)
- throw UsageError(format("exactly one argument expected"));
+ throw UsageError("exactly one argument expected");
int dstGen;
if (!string2Int(opArgs.front(), dstGen))
- throw UsageError(format("expected a generation number"));
+ throw UsageError("expected a generation number");
switchGeneration(globals, dstGen);
}
@@ -1251,9 +1262,9 @@ static void opSwitchGeneration(Globals & globals, Strings opFlags, Strings opArg
static void opRollback(Globals & globals, Strings opFlags, Strings opArgs)
{
if (opFlags.size() > 0)
- throw UsageError(format("unknown flag '%1%'") % opFlags.front());
+ throw UsageError("unknown flag '%1%'", opFlags.front());
if (opArgs.size() != 0)
- throw UsageError(format("no arguments expected"));
+ throw UsageError("no arguments expected");
switchGeneration(globals, prevGen);
}
@@ -1262,9 +1273,9 @@ static void opRollback(Globals & globals, Strings opFlags, Strings opArgs)
static void opListGenerations(Globals & globals, Strings opFlags, Strings opArgs)
{
if (opFlags.size() > 0)
- throw UsageError(format("unknown flag '%1%'") % opFlags.front());
+ throw UsageError("unknown flag '%1%'", opFlags.front());
if (opArgs.size() != 0)
- throw UsageError(format("no arguments expected"));
+ throw UsageError("no arguments expected");
PathLocks lock;
lockProfile(lock, globals.profile);
@@ -1289,7 +1300,7 @@ static void opListGenerations(Globals & globals, Strings opFlags, Strings opArgs
static void opDeleteGenerations(Globals & globals, Strings opFlags, Strings opArgs)
{
if (opFlags.size() > 0)
- throw UsageError(format("unknown flag '%1%'") % opFlags.front());
+ throw UsageError("unknown flag '%1%'", opFlags.front());
if (opArgs.size() == 1 && opArgs.front() == "old") {
deleteOldGenerations(globals.profile, globals.dryRun);
@@ -1297,18 +1308,18 @@ static void opDeleteGenerations(Globals & globals, Strings opFlags, Strings opAr
deleteGenerationsOlderThan(globals.profile, opArgs.front(), globals.dryRun);
} else if (opArgs.size() == 1 && opArgs.front().find('+') != string::npos) {
if(opArgs.front().size() < 2)
- throw Error(format("invalid number of generations ‘%1%’") % opArgs.front());
+ throw Error("invalid number of generations ‘%1%’", opArgs.front());
string str_max = string(opArgs.front(), 1, opArgs.front().size());
int max;
if (!string2Int(str_max, max) || max == 0)
- throw Error(format("invalid number of generations to keep ‘%1%’") % opArgs.front());
+ throw Error("invalid number of generations to keep ‘%1%’", opArgs.front());
deleteGenerationsGreaterThan(globals.profile, max, globals.dryRun);
} else {
std::set<unsigned int> gens;
for (auto & i : opArgs) {
unsigned int n;
if (!string2Int(i, n))
- throw UsageError(format("invalid generation number '%1%'") % i);
+ throw UsageError("invalid generation number '%1%'", i);
gens.insert(n);
}
deleteGenerations(globals.profile, gens, globals.dryRun);
diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc
index f852916d8..f804b77a0 100644
--- a/src/nix-env/user-env.cc
+++ b/src/nix-env/user-env.cc
@@ -146,7 +146,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
Path lockTokenCur = optimisticLockProfile(profile);
if (lockToken != lockTokenCur) {
- printError(format("profile '%1%' changed while we were busy; restarting") % profile);
+ printInfo("profile '%1%' changed while we were busy; restarting", profile);
return false;
}
diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc
index 6c99d1181..bf353677a 100644
--- a/src/nix-instantiate/nix-instantiate.cc
+++ b/src/nix-instantiate/nix-instantiate.cc
@@ -66,7 +66,7 @@ void processExpr(EvalState & state, const Strings & attrPaths,
/* What output do we want? */
string outputName = i.queryOutputName();
if (outputName == "")
- throw Error(format("derivation '%1%' lacks an 'outputName' attribute ") % drvPath);
+ throw Error("derivation '%1%' lacks an 'outputName' attribute ", drvPath);
if (gcRoot == "")
printGCWarning();
@@ -166,7 +166,7 @@ static int _main(int argc, char * * argv)
if (findFile) {
for (auto & i : files) {
Path p = state->findFile(i);
- if (p == "") throw Error(format("unable to find '%1%'") % i);
+ if (p == "") throw Error("unable to find '%1%'", i);
std::cout << p << std::endl;
}
return 0;
diff --git a/src/nix-prefetch-url/nix-prefetch-url.cc b/src/nix-prefetch-url/nix-prefetch-url.cc
index b645bdc1b..55b72bda6 100644
--- a/src/nix-prefetch-url/nix-prefetch-url.cc
+++ b/src/nix-prefetch-url/nix-prefetch-url.cc
@@ -37,11 +37,11 @@ string resolveMirrorUri(EvalState & state, string uri)
auto mirrorList = vMirrors.attrs->find(state.symbols.create(mirrorName));
if (mirrorList == vMirrors.attrs->end())
- throw Error(format("unknown mirror name '%1%'") % mirrorName);
+ throw Error("unknown mirror name '%1%'", mirrorName);
state.forceList(*mirrorList->value);
if (mirrorList->value->listSize() < 1)
- throw Error(format("mirror URI '%1%' did not expand to anything") % uri);
+ throw Error("mirror URI '%1%' did not expand to anything", uri);
string mirror = state.forceString(*mirrorList->value->listElems()[0]);
return mirror + (hasSuffix(mirror, "/") ? "" : "/") + string(s, p + 1);
@@ -73,7 +73,7 @@ static int _main(int argc, char * * argv)
string s = getArg(*arg, arg, end);
ht = parseHashType(s);
if (ht == htUnknown)
- throw UsageError(format("unknown hash type '%1%'") % s);
+ throw UsageError("unknown hash type '%1%'", s);
}
else if (*arg == "--print-path")
printPath = true;
@@ -151,7 +151,7 @@ static int _main(int argc, char * * argv)
if (name.empty())
name = baseNameOf(uri);
if (name.empty())
- throw Error(format("cannot figure out file name for '%1%'") % uri);
+ throw Error("cannot figure out file name for '%1%'", uri);
/* If an expected hash is given, the file may already exist in
the store. */
@@ -207,7 +207,7 @@ static int _main(int argc, char * * argv)
hash = unpack ? hashPath(ht, tmpFile).first : hashFile(ht, tmpFile);
if (expectedHash != Hash(ht) && expectedHash != hash)
- throw Error(format("hash mismatch for '%1%'") % uri);
+ throw Error("hash mismatch for '%1%'", uri);
const auto recursive = unpack ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat;
diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc
index ef8fb0951..dea53b52f 100644
--- a/src/nix-store/nix-store.cc
+++ b/src/nix-store/nix-store.cc
@@ -124,7 +124,7 @@ static void opRealise(Strings opFlags, Strings opArgs)
else if (i == "--repair") buildMode = bmRepair;
else if (i == "--check") buildMode = bmCheck;
else if (i == "--ignore-unknown") ignoreUnknown = true;
- else throw UsageError(format("unknown flag '%1%'") % i);
+ else throw UsageError("unknown flag '%1%'", i);
std::vector<StorePathWithOutputs> paths;
for (auto & i : opArgs)
@@ -178,7 +178,7 @@ static void opAddFixed(Strings opFlags, Strings opArgs)
for (auto & i : opFlags)
if (i == "--recursive") recursive = FileIngestionMethod::Recursive;
- else throw UsageError(format("unknown flag '%1%'") % i);
+ else throw UsageError("unknown flag '%1%'", i);
if (opArgs.empty())
throw UsageError("first argument must be hash algorithm");
@@ -198,10 +198,10 @@ static void opPrintFixedPath(Strings opFlags, Strings opArgs)
for (auto i : opFlags)
if (i == "--recursive") recursive = FileIngestionMethod::Recursive;
- else throw UsageError(format("unknown flag '%1%'") % i);
+ else throw UsageError("unknown flag '%1%'", i);
if (opArgs.size() != 3)
- throw UsageError(format("'--print-fixed-path' requires three arguments"));
+ throw UsageError("'--print-fixed-path' requires three arguments");
Strings::iterator i = opArgs.begin();
HashType hashAlgo = parseHashType(*i++);
@@ -296,9 +296,9 @@ static void opQuery(Strings opFlags, Strings opArgs)
else if (i == "--use-output" || i == "-u") useOutput = true;
else if (i == "--force-realise" || i == "--force-realize" || i == "-f") forceRealise = true;
else if (i == "--include-outputs") includeOutputs = true;
- else throw UsageError(format("unknown flag '%1%'") % i);
+ else throw UsageError("unknown flag '%1%'", i);
if (prev != qDefault && prev != query)
- throw UsageError(format("query type '%1%' conflicts with earlier flag") % i);
+ throw UsageError("query type '%1%' conflicts with earlier flag", i);
}
if (query == qDefault) query = qOutputs;
@@ -444,7 +444,7 @@ static void opPrintEnv(Strings opFlags, Strings opArgs)
Derivation drv = store->derivationFromPath(store->parseStorePath(drvPath));
/* Print each environment variable in the derivation in a format
- that can be sourced by the shell. */
+ * that can be sourced by the shell. */
for (auto & i : drv.env)
cout << format("export %1%; %1%=%2%\n") % i.first % shellEscape(i.second);
@@ -531,7 +531,7 @@ static void opRegisterValidity(Strings opFlags, Strings opArgs)
for (auto & i : opFlags)
if (i == "--reregister") reregister = true;
else if (i == "--hash-given") hashGiven = true;
- else throw UsageError(format("unknown flag '%1%'") % i);
+ else throw UsageError("unknown flag '%1%'", i);
if (!opArgs.empty()) throw UsageError("no arguments expected");
@@ -545,7 +545,7 @@ static void opCheckValidity(Strings opFlags, Strings opArgs)
for (auto & i : opFlags)
if (i == "--print-invalid") printInvalid = true;
- else throw UsageError(format("unknown flag '%1%'") % i);
+ else throw UsageError("unknown flag '%1%'", i);
for (auto & i : opArgs) {
auto path = store->followLinksToStorePath(i);
@@ -576,7 +576,7 @@ static void opGC(Strings opFlags, Strings opArgs)
long long maxFreed = getIntArg<long long>(*i, i, opFlags.end(), true);
options.maxFreed = maxFreed >= 0 ? maxFreed : 0;
}
- else throw UsageError(format("bad sub-operation '%1%' in GC") % *i);
+ else throw UsageError("bad sub-operation '%1%' in GC", *i);
if (!opArgs.empty()) throw UsageError("no arguments expected");
@@ -612,7 +612,7 @@ static void opDelete(Strings opFlags, Strings opArgs)
for (auto & i : opFlags)
if (i == "--ignore-liveness") options.ignoreLiveness = true;
- else throw UsageError(format("unknown flag '%1%'") % i);
+ else throw UsageError("unknown flag '%1%'", i);
for (auto & i : opArgs)
options.pathsToDelete.insert(store->followLinksToStorePath(i));
@@ -650,7 +650,7 @@ static void opRestore(Strings opFlags, Strings opArgs)
static void opExport(Strings opFlags, Strings opArgs)
{
for (auto & i : opFlags)
- throw UsageError(format("unknown flag '%1%'") % i);
+ throw UsageError("unknown flag '%1%'", i);
StorePathSet paths;
@@ -666,7 +666,7 @@ static void opExport(Strings opFlags, Strings opArgs)
static void opImport(Strings opFlags, Strings opArgs)
{
for (auto & i : opFlags)
- throw UsageError(format("unknown flag '%1%'") % i);
+ throw UsageError("unknown flag '%1%'", i);
if (!opArgs.empty()) throw UsageError("no arguments expected");
@@ -701,10 +701,13 @@ static void opVerify(Strings opFlags, Strings opArgs)
for (auto & i : opFlags)
if (i == "--check-contents") checkContents = true;
else if (i == "--repair") repair = Repair;
- else throw UsageError(format("unknown flag '%1%'") % i);
+ else throw UsageError("unknown flag '%1%'", i);
if (store->verifyStore(checkContents, repair)) {
- printError("warning: not all errors were fixed");
+ logWarning({
+ .name = "Store consistency",
+ .description = "not all errors were fixed"
+ });
throw Exit(1);
}
}
@@ -726,9 +729,14 @@ static void opVerifyPath(Strings opFlags, Strings opArgs)
store->narFromPath(path, sink);
auto current = sink.finish();
if (current.first != info->narHash) {
- printError(
- "path '%s' was modified! expected hash '%s', got '%s'",
- store->printStorePath(path), info->narHash.to_string(Base32, true), current.first.to_string(Base32, true));
+ logError({
+ .name = "Hash mismatch",
+ .hint = hintfmt(
+ "path '%s' was modified! expected hash '%s', got '%s'",
+ store->printStorePath(path),
+ info->narHash.to_string(Base32, true),
+ current.first.to_string(Base32, true))
+ });
status = 1;
}
}
@@ -764,7 +772,7 @@ static void opServe(Strings opFlags, Strings opArgs)
bool writeAllowed = false;
for (auto & i : opFlags)
if (i == "--write") writeAllowed = true;
- else throw UsageError(format("unknown flag '%1%'") % i);
+ else throw UsageError("unknown flag '%1%'", i);
if (!opArgs.empty()) throw UsageError("no arguments expected");
@@ -835,7 +843,7 @@ static void opServe(Strings opFlags, Strings opArgs)
for (auto & p : willSubstitute) subs.emplace_back(p.clone());
store->buildPaths(subs);
} catch (Error & e) {
- printError(format("warning: %1%") % e.msg());
+ logWarning(e.info());
}
}
@@ -962,7 +970,7 @@ static void opServe(Strings opFlags, Strings opArgs)
}
default:
- throw Error(format("unknown serve command %1%") % cmd);
+ throw Error("unknown serve command %1%", cmd);
}
out.flush();
@@ -973,7 +981,7 @@ static void opServe(Strings opFlags, Strings opArgs)
static void opGenerateBinaryCacheKey(Strings opFlags, Strings opArgs)
{
for (auto & i : opFlags)
- throw UsageError(format("unknown flag '%1%'") % i);
+ throw UsageError("unknown flag '%1%'", i);
if (opArgs.size() != 3) throw UsageError("three arguments expected");
auto i = opArgs.begin();
diff --git a/src/nix/cat.cc b/src/nix/cat.cc
index fd91f2036..c82819af8 100644
--- a/src/nix/cat.cc
+++ b/src/nix/cat.cc
@@ -13,9 +13,9 @@ struct MixCat : virtual Args
{
auto st = accessor->stat(path);
if (st.type == FSAccessor::Type::tMissing)
- throw Error(format("path '%1%' does not exist") % path);
+ throw Error("path '%1%' does not exist", path);
if (st.type != FSAccessor::Type::tRegular)
- throw Error(format("path '%1%' is not a regular file") % path);
+ throw Error("path '%1%' is not a regular file", path);
std::cout << accessor->readFile(path);
}
diff --git a/src/nix/hash.cc b/src/nix/hash.cc
index 0a7c343d0..0dd4998d9 100644
--- a/src/nix/hash.cc
+++ b/src/nix/hash.cc
@@ -133,7 +133,7 @@ static int compatNixHash(int argc, char * * argv)
string s = getArg(*arg, arg, end);
ht = parseHashType(s);
if (ht == htUnknown)
- throw UsageError(format("unknown hash type '%1%'") % s);
+ throw UsageError("unknown hash type '%1%'", s);
}
else if (*arg == "--to-base16") op = opTo16;
else if (*arg == "--to-base32") op = opTo32;
diff --git a/src/nix/ls.cc b/src/nix/ls.cc
index b9716a6a1..d2157f2d4 100644
--- a/src/nix/ls.cc
+++ b/src/nix/ls.cc
@@ -63,7 +63,7 @@ struct MixLs : virtual Args, MixJSON
auto st = accessor->stat(path);
if (st.type == FSAccessor::Type::tMissing)
- throw Error(format("path '%1%' does not exist") % path);
+ throw Error("path '%1%' does not exist", path);
doPath(st, path,
st.type == FSAccessor::Type::tDirectory ? "." : std::string(baseNameOf(path)),
showDirectory);
diff --git a/src/nix/make-content-addressable.cc b/src/nix/make-content-addressable.cc
index 3e7ff544d..719ea4fd1 100644
--- a/src/nix/make-content-addressable.cc
+++ b/src/nix/make-content-addressable.cc
@@ -85,7 +85,7 @@ struct CmdMakeContentAddressable : StorePathsCommand, MixJSON
info.ca = makeFixedOutputCA(FileIngestionMethod::Recursive, info.narHash);
if (!json)
- printError("rewrote '%s' to '%s'", pathS, store->printStorePath(info.path));
+ printInfo("rewrote '%s' to '%s'", pathS, store->printStorePath(info.path));
auto source = sinkToSource([&](Sink & nextSink) {
RewritingSink rsink2(oldHashPart, storePathToHash(store->printStorePath(info.path)), nextSink);
diff --git a/src/nix/repl.cc b/src/nix/repl.cc
index ea8ff1553..4bcaaeebf 100644
--- a/src/nix/repl.cc
+++ b/src/nix/repl.cc
@@ -218,12 +218,12 @@ void NixRepl::mainLoop(const std::vector<std::string> & files)
// input without clearing the input so far.
continue;
} else {
- printMsg(lvlError, format(error + "%1%%2%") % (settings.showTrace ? e.prefix() : "") % e.msg());
+ printMsg(lvlError, error + "%1%%2%", (settings.showTrace ? e.prefix() : ""), e.msg());
}
} catch (Error & e) {
- printMsg(lvlError, format(error + "%1%%2%") % (settings.showTrace ? e.prefix() : "") % e.msg());
+ printMsg(lvlError, error + "%1%%2%", (settings.showTrace ? e.prefix() : ""), e.msg());
} catch (Interrupted & e) {
- printMsg(lvlError, format(error + "%1%%2%") % (settings.showTrace ? e.prefix() : "") % e.msg());
+ printMsg(lvlError, error + "%1%%2%", (settings.showTrace ? e.prefix() : ""), e.msg());
}
// We handled the current input fully, so we should clear it
@@ -512,7 +512,7 @@ bool NixRepl::processLine(string line)
return false;
else if (command != "")
- throw Error(format("unknown command '%1%'") % command);
+ throw Error("unknown command '%1%'", command);
else {
size_t p = line.find('=');
diff --git a/src/nix/run.cc b/src/nix/run.cc
index b888281a5..c9b69aec7 100644
--- a/src/nix/run.cc
+++ b/src/nix/run.cc
@@ -197,10 +197,10 @@ void chrootHelper(int argc, char * * argv)
Finally freeCwd([&]() { free(cwd); });
if (chroot(tmpDir.c_str()) == -1)
- throw SysError(format("chrooting into '%s'") % tmpDir);
+ throw SysError("chrooting into '%s'", tmpDir);
if (chdir(cwd) == -1)
- throw SysError(format("chdir to '%s' in chroot") % cwd);
+ throw SysError("chdir to '%s' in chroot", cwd);
} else
if (mount(realStoreDir.c_str(), storeDir.c_str(), "", MS_BIND, 0) == -1)
throw SysError("mounting '%s' on '%s'", realStoreDir, storeDir);
diff --git a/src/nix/upgrade-nix.cc b/src/nix/upgrade-nix.cc
index 678780f33..fdf94e5a3 100644
--- a/src/nix/upgrade-nix.cc
+++ b/src/nix/upgrade-nix.cc
@@ -68,7 +68,11 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand
if (dryRun) {
stopProgressBar();
- printError("would upgrade to version %s", version);
+ logWarning(
+ ErrorInfo {
+ .name = "Version update",
+ .hint = hintfmt("would upgrade to version %s", version)
+ });
return;
}
@@ -94,7 +98,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand
{"--profile", profileDir, "-i", store->printStorePath(storePath), "--no-sandbox"});
}
- printError(ANSI_GREEN "upgrade to version %s done" ANSI_NORMAL, version);
+ printInfo(ANSI_GREEN "upgrade to version %s done" ANSI_NORMAL, version);
}
/* Return the profile in which Nix is installed. */
diff --git a/src/nix/verify.cc b/src/nix/verify.cc
index 287dad101..246b4b480 100644
--- a/src/nix/verify.cc
+++ b/src/nix/verify.cc
@@ -99,11 +99,15 @@ struct CmdVerify : StorePathsCommand
if (hash.first != info->narHash) {
corrupted++;
act2.result(resCorruptedPath, store->printStorePath(info->path));
- printError(
- "path '%s' was modified! expected hash '%s', got '%s'",
- store->printStorePath(info->path), info->narHash.to_string(Base32, true), hash.first.to_string(Base32, true));
+ logError({
+ .name = "Hash error - path modified",
+ .hint = hintfmt(
+ "path '%s' was modified! expected hash '%s', got '%s'",
+ store->printStorePath(info->path),
+ info->narHash.to_string(Base32, true),
+ hash.first.to_string(Base32, true))
+ });
}
-
}
if (!noTrust) {
@@ -139,7 +143,7 @@ struct CmdVerify : StorePathsCommand
doSigs(info2->sigs);
} catch (InvalidPath &) {
} catch (Error & e) {
- printError(format(ANSI_RED "error:" ANSI_NORMAL " %s") % e.what());
+ logError(e.info());
}
}
@@ -150,7 +154,12 @@ struct CmdVerify : StorePathsCommand
if (!good) {
untrusted++;
act2.result(resUntrustedPath, store->printStorePath(info->path));
- printError("path '%s' is untrusted", store->printStorePath(info->path));
+ logError({
+ .name = "Untrusted path",
+ .hint = hintfmt("path '%s' is untrusted",
+ store->printStorePath(info->path))
+ });
+
}
}
@@ -158,7 +167,7 @@ struct CmdVerify : StorePathsCommand
done++;
} catch (Error & e) {
- printError(format(ANSI_RED "error:" ANSI_NORMAL " %s") % e.what());
+ logError(e.info());
failed++;
}
diff --git a/src/resolve-system-dependencies/resolve-system-dependencies.cc b/src/resolve-system-dependencies/resolve-system-dependencies.cc
index 8f0c99c84..82feacb3d 100644
--- a/src/resolve-system-dependencies/resolve-system-dependencies.cc
+++ b/src/resolve-system-dependencies/resolve-system-dependencies.cc
@@ -39,12 +39,18 @@ std::set<std::string> runResolver(const Path & filename)
throw SysError("statting '%s'", filename);
if (!S_ISREG(st.st_mode)) {
- printError("file '%s' is not a regular file", filename);
+ logError({
+ .name = "Regular MACH file",
+ .hint = hintfmt("file '%s' is not a regular file", filename)
+ });
return {};
}
if (st.st_size < sizeof(mach_header_64)) {
- printError("file '%s' is too short for a MACH binary", filename);
+ logError({
+ .name = "File too short",
+ .hint = hintfmt("file '%s' is too short for a MACH binary", filename)
+ });
return {};
}
@@ -66,13 +72,19 @@ std::set<std::string> runResolver(const Path & filename)
}
}
if (mach64_offset == 0) {
- printError(format("Could not find any mach64 blobs in file '%1%', continuing...") % filename);
+ logError({
+ .name = "No mach64 blobs",
+ .hint = hintfmt("Could not find any mach64 blobs in file '%1%', continuing...", filename)
+ });
return {};
}
} else if (magic == MH_MAGIC_64 || magic == MH_CIGAM_64) {
mach64_offset = 0;
} else {
- printError(format("Object file has unknown magic number '%1%', skipping it...") % magic);
+ logError({
+ .name = "Magic number",
+ .hint = hintfmt("Object file has unknown magic number '%1%', skipping it...", magic)
+ });
return {};
}