diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 000000000..5ace4600a
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,6 @@
+version: 2
+updates:
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "weekly"
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 7feefc855..7755466a0 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -10,5 +10,5 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- - uses: cachix/install-nix-action@v8
+ - uses: cachix/install-nix-action@v10
- run: nix-build release.nix --arg nix '{ outPath = ./.; revCount = 123; shortRev = "abcdefgh"; }' --arg systems '[ builtins.currentSystem ]' -A installerScript -A perlBindings
diff --git a/doc/manual/installation/installing-binary.xml b/doc/manual/installation/installing-binary.xml
index d25c46b85..64c7a37fb 100644
--- a/doc/manual/installation/installing-binary.xml
+++ b/doc/manual/installation/installing-binary.xml
@@ -97,7 +97,7 @@ $ rm -rf /nix
installation on your system:
- sh <(curl https://nixos.org/nix/install) --daemon
+ sh <(curl -L https://nixos.org/nix/install) --daemon
The multi-user installation of Nix will create build users between
@@ -178,7 +178,7 @@ sudo rm /Library/LaunchDaemons/org.nixos.nix-daemon.plist
is a bit of a misnomer). To use this approach, just install Nix with:
- $ sh <(curl https://nixos.org/nix/install) --darwin-use-unencrypted-nix-store-volume
+ $ sh <(curl -L https://nixos.org/nix/install) --darwin-use-unencrypted-nix-store-volume
If you don't like the sound of this, you'll want to weigh the
@@ -429,7 +429,7 @@ LABEL=Nix\040Store /nix apfs rw,nobrowse
NixOS.org installation script:
- sh <(curl https://nixos.org/nix/install)
+ sh <(curl -L https://nixos.org/nix/install)
diff --git a/mk/run_test.sh b/mk/run_test.sh
new file mode 100755
index 000000000..6af5b070a
--- /dev/null
+++ b/mk/run_test.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+set -u
+
+red=""
+green=""
+yellow=""
+normal=""
+
+post_run_msg="ran test $1..."
+if [ -t 1 ]; then
+ red="[31;1m"
+ green="[32;1m"
+ yellow="[33;1m"
+ normal="[m"
+fi
+(cd $(dirname $1) && env ${TESTS_ENVIRONMENT} init.sh 2>/dev/null > /dev/null)
+log="$(cd $(dirname $1) && env ${TESTS_ENVIRONMENT} $(basename $1) 2>&1)"
+status=$?
+if [ $status -eq 0 ]; then
+ echo "$post_run_msg [${green}PASS$normal]"
+elif [ $status -eq 99 ]; then
+ echo "$post_run_msg [${yellow}SKIP$normal]"
+else
+ echo "$post_run_msg [${red}FAIL$normal]"
+ echo "$log" | sed 's/^/ /'
+ exit "$status"
+fi
diff --git a/mk/tests.mk b/mk/tests.mk
index 70c30661b..2e39bb694 100644
--- a/mk/tests.mk
+++ b/mk/tests.mk
@@ -1,45 +1,15 @@
# Run program $1 as part of ‘make installcheck’.
+
+test-deps =
+
define run-install-test
- installcheck: $1
+ installcheck: $1.test
- _installcheck-list += $1
+ .PHONY: $1.test
+ $1.test: $1 $(test-deps)
+ @env TEST_NAME=$(notdir $(basename $1)) TESTS_ENVIRONMENT="$(tests-environment)" mk/run_test.sh $1
endef
-# Color code from https://unix.stackexchange.com/a/10065
-installcheck:
- @total=0; failed=0; \
- red=""; \
- green=""; \
- yellow=""; \
- normal=""; \
- if [ -t 1 ]; then \
- red="[31;1m"; \
- green="[32;1m"; \
- yellow="[33;1m"; \
- normal="[m"; \
- fi; \
- for i in $(_installcheck-list); do \
- total=$$((total + 1)); \
- printf "running test $$i..."; \
- log="$$(cd $$(dirname $$i) && $(tests-environment) $$(basename $$i) 2>&1)"; \
- status=$$?; \
- if [ $$status -eq 0 ]; then \
- echo " [$${green}PASS$$normal]"; \
- elif [ $$status -eq 99 ]; then \
- echo " [$${yellow}SKIP$$normal]"; \
- else \
- echo " [$${red}FAIL$$normal]"; \
- echo "$$log" | sed 's/^/ /'; \
- failed=$$((failed + 1)); \
- fi; \
- done; \
- if [ "$$failed" != 0 ]; then \
- echo "$${red}$$failed out of $$total tests failed $$normal"; \
- exit 1; \
- else \
- echo "$${green}All tests succeeded$$normal"; \
- fi
-
.PHONY: check installcheck
diff --git a/scripts/install-multi-user.sh b/scripts/install-multi-user.sh
index 157e8ddb4..00c9d540b 100644
--- a/scripts/install-multi-user.sh
+++ b/scripts/install-multi-user.sh
@@ -526,7 +526,7 @@ This script is going to call sudo a lot. Normally, it would show you
exactly what commands it is running and why. However, the script is
run in a headless fashion, like this:
- $ curl https://nixos.org/nix/install | sh
+ $ curl -L https://nixos.org/nix/install | sh
or maybe in a CI pipeline. Because of that, we're going to skip the
verbose output in the interest of brevity.
@@ -534,7 +534,7 @@ verbose output in the interest of brevity.
If you would like to
see the output, try like this:
- $ curl -o install-nix https://nixos.org/nix/install
+ $ curl -L -o install-nix https://nixos.org/nix/install
$ sh ./install-nix
EOF
diff --git a/scripts/install-nix-from-closure.sh b/scripts/install-nix-from-closure.sh
index 826ca8b8c..5824c2217 100644
--- a/scripts/install-nix-from-closure.sh
+++ b/scripts/install-nix-from-closure.sh
@@ -113,7 +113,7 @@ if [ "$(uname -s)" = "Darwin" ]; then
(
echo ""
echo "Installing on macOS >=10.15 requires relocating the store to an apfs volume."
- echo "Use sh <(curl https://nixos.org/nix/install) --darwin-use-unencrypted-nix-store-volume or run the preparation steps manually."
+ echo "Use sh <(curl -L https://nixos.org/nix/install) --darwin-use-unencrypted-nix-store-volume or run the preparation steps manually."
echo "See https://nixos.org/nix/manual/#sect-macos-installation"
echo ""
) >&2
diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc
index 2e2a17b14..83854df49 100644
--- a/src/libexpr/attr-path.cc
+++ b/src/libexpr/attr-path.cc
@@ -130,7 +130,7 @@ Pos findDerivationFilename(EvalState & state, Value & v, std::string what)
Symbol file = state.symbols.create(filename);
- return { file, lineno, 0 };
+ return { foFile, file, lineno, 0 };
}
diff --git a/src/libexpr/attr-set.hh b/src/libexpr/attr-set.hh
index c601d09c2..7eaa16c59 100644
--- a/src/libexpr/attr-set.hh
+++ b/src/libexpr/attr-set.hh
@@ -78,7 +78,7 @@ public:
if (!a)
throw Error({
.hint = hintfmt("attribute '%s' missing", name),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
return *a;
diff --git a/src/libexpr/eval-inline.hh b/src/libexpr/eval-inline.hh
index 3d544c903..30f6ec7db 100644
--- a/src/libexpr/eval-inline.hh
+++ b/src/libexpr/eval-inline.hh
@@ -11,7 +11,7 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s))
{
throw EvalError({
.hint = hintfmt(s),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
}
@@ -25,7 +25,7 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const
{
throw TypeError({
.hint = hintfmt(s, showType(v)),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
}
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index b90a64357..c1a9af9b2 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -529,7 +529,7 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const
{
throw EvalError({
.hint = hintfmt(s, s2),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
}
@@ -542,7 +542,7 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const
{
throw EvalError({
.hint = hintfmt(s, s2, s3),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
}
@@ -551,7 +551,7 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & p1, const char * s, const
// p1 is where the error occurred; p2 is a position mentioned in the message.
throw EvalError({
.hint = hintfmt(s, sym, p2),
- .nixCode = NixCode { .errPos = p1 }
+ .errPos = p1
});
}
@@ -559,7 +559,7 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s))
{
throw TypeError({
.hint = hintfmt(s),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
}
@@ -572,7 +572,7 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const
{
throw TypeError({
.hint = hintfmt(s, fun.showNamePos(), s2),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
}
@@ -580,7 +580,7 @@ LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s,
{
throw AssertionError({
.hint = hintfmt(s, s1),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
}
@@ -588,23 +588,18 @@ LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char *
{
throw UndefinedVarError({
.hint = hintfmt(s, s1),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
}
-LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2))
+LocalNoInline(void addErrorTrace(Error & e, const char * s, const string & s2))
{
- e.addPrefix(format(s) % s2);
+ e.addTrace(std::nullopt, s, s2);
}
-LocalNoInline(void addErrorPrefix(Error & e, const char * s, const ExprLambda & fun, const Pos & pos))
+LocalNoInline(void addErrorTrace(Error & e, const Pos & pos, const char * s, const string & s2))
{
- e.addPrefix(format(s) % fun.showNamePos() % pos);
-}
-
-LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2, const Pos & pos))
-{
- e.addPrefix(format(s) % s2 % pos);
+ e.addTrace(pos, s, s2);
}
@@ -818,7 +813,7 @@ void EvalState::evalFile(const Path & path_, Value & v)
try {
eval(e, v);
} catch (Error & e) {
- addErrorPrefix(e, "while evaluating the file '%1%':\n", path2);
+ addErrorTrace(e, "while evaluating the file '%1%':", path2);
throw;
}
@@ -1068,8 +1063,8 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
} catch (Error & e) {
if (pos2 && pos2->file != state.sDerivationNix)
- addErrorPrefix(e, "while evaluating the attribute '%1%' at %2%:\n",
- showAttrPath(state, env, attrPath), *pos2);
+ addErrorTrace(e, *pos2, "while evaluating the attribute '%1%'",
+ showAttrPath(state, env, attrPath));
throw;
}
@@ -1237,11 +1232,15 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
/* Evaluate the body. This is conditional on showTrace, because
catching exceptions makes this function not tail-recursive. */
- if (settings.showTrace)
+ if (loggerSettings.showTrace.get())
try {
lambda.body->eval(*this, env2, v);
} catch (Error & e) {
- addErrorPrefix(e, "while evaluating %1%, called from %2%:\n", lambda, pos);
+ addErrorTrace(e, lambda.pos, "while evaluating %s",
+ (lambda.name.set()
+ ? "'" + (string) lambda.name + "'"
+ : "anonymous lambdaction"));
+ addErrorTrace(e, pos, "from call site%s", "");
throw;
}
else
@@ -1516,7 +1515,7 @@ void EvalState::forceValueDeep(Value & v)
try {
recurse(*i.value);
} catch (Error & e) {
- addErrorPrefix(e, "while evaluating the attribute '%1%' at %2%:\n", i.name, *i.pos);
+ addErrorTrace(e, *i.pos, "while evaluating the attribute '%1%'", i.name);
throw;
}
}
@@ -1936,7 +1935,7 @@ string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, boo
{
throw TypeError({
.hint = hintfmt("cannot coerce %1% to a string", showType()),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
}
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 863365259..0d52a7f63 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -250,7 +250,7 @@ private:
friend struct ExprAttrs;
friend struct ExprLet;
- Expr * parse(const char * text, const Path & path,
+ Expr * parse(const char * text, FileOrigin origin, const Path & path,
const Path & basePath, StaticEnv & staticEnv);
public:
diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc
index a4937e722..9055f59a1 100644
--- a/src/libexpr/get-drvs.cc
+++ b/src/libexpr/get-drvs.cc
@@ -1,7 +1,7 @@
#include "get-drvs.hh"
#include "util.hh"
#include "eval-inline.hh"
-#include "derivations.hh"
+#include "store-api.hh"
#include
#include
diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc
index b4b65883d..d5698011f 100644
--- a/src/libexpr/nixexpr.cc
+++ b/src/libexpr/nixexpr.cc
@@ -197,7 +197,22 @@ std::ostream & operator << (std::ostream & str, const Pos & pos)
if (!pos)
str << "undefined position";
else
- str << (format(ANSI_BOLD "%1%" ANSI_NORMAL ":%2%:%3%") % (string) pos.file % pos.line % pos.column).str();
+ {
+ auto f = format(ANSI_BOLD "%1%" ANSI_NORMAL ":%2%:%3%");
+ switch (pos.origin) {
+ case foFile:
+ f % (string) pos.file;
+ break;
+ case foStdin:
+ case foString:
+ f % "(string)";
+ break;
+ default:
+ throw Error("unhandled Pos origin!");
+ }
+ str << (f % pos.line % pos.column).str();
+ }
+
return str;
}
@@ -270,7 +285,7 @@ void ExprVar::bindVars(const StaticEnv & env)
if (withLevel == -1)
throw UndefinedVarError({
.hint = hintfmt("undefined variable '%1%'", name),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
fromWith = true;
this->level = withLevel;
diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh
index ec6fd3190..e4cbc660f 100644
--- a/src/libexpr/nixexpr.hh
+++ b/src/libexpr/nixexpr.hh
@@ -24,11 +24,12 @@ MakeError(RestrictedPathError, Error);
struct Pos
{
+ FileOrigin origin;
Symbol file;
unsigned int line, column;
- Pos() : line(0), column(0) { };
- Pos(const Symbol & file, unsigned int line, unsigned int column)
- : file(file), line(line), column(column) { };
+ Pos() : origin(foString), line(0), column(0) { };
+ Pos(FileOrigin origin, const Symbol & file, unsigned int line, unsigned int column)
+ : origin(origin), file(file), line(line), column(column) { };
operator bool() const
{
return line != 0;
@@ -238,7 +239,7 @@ struct ExprLambda : Expr
if (!arg.empty() && formals && formals->argNames.find(arg) != formals->argNames.end())
throw ParseError({
.hint = hintfmt("duplicate formal function argument '%1%'", arg),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
};
void setName(Symbol & name);
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index a639be64e..878f06c96 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -30,7 +30,8 @@ namespace nix {
SymbolTable & symbols;
Expr * result;
Path basePath;
- Symbol path;
+ Symbol file;
+ FileOrigin origin;
ErrorInfo error;
Symbol sLetBody;
ParseData(EvalState & state)
@@ -65,18 +66,17 @@ namespace nix {
static void dupAttr(const AttrPath & attrPath, const Pos & pos, const Pos & prevPos)
{
throw ParseError({
- .hint = hintfmt("attribute '%1%' already defined at %2%",
+ .hint = hintfmt("attribute '%1%' already defined at %2%",
showAttrPath(attrPath), prevPos),
- .nixCode = NixCode { .errPos = pos },
+ .errPos = pos
});
}
-
static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos)
{
throw ParseError({
.hint = hintfmt("attribute '%1%' already defined at %2%", attr, prevPos),
- .nixCode = NixCode { .errPos = pos },
+ .errPos = pos
});
}
@@ -148,7 +148,7 @@ static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
throw ParseError({
.hint = hintfmt("duplicate formal function argument '%1%'",
formal.name),
- .nixCode = NixCode { .errPos = pos },
+ .errPos = pos
});
formals->formals.push_front(formal);
}
@@ -246,7 +246,7 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vectorpath, loc.first_line, loc.first_column);
+ return Pos(data->origin, data->file, loc.first_line, loc.first_column);
}
#define CUR_POS makeCurPos(*yylocp, data)
@@ -259,7 +259,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
{
data->error = {
.hint = hintfmt(error),
- .nixCode = NixCode { .errPos = makeCurPos(*loc, data) }
+ .errPos = makeCurPos(*loc, data)
};
}
@@ -339,7 +339,7 @@ expr_function
{ if (!$2->dynamicAttrs.empty())
throw ParseError({
.hint = hintfmt("dynamic attributes not allowed in let"),
- .nixCode = NixCode { .errPos = CUR_POS },
+ .errPos = CUR_POS
});
$$ = new ExprLet($2, $4);
}
@@ -419,7 +419,7 @@ expr_simple
if (noURLLiterals)
throw ParseError({
.hint = hintfmt("URL literals are disabled"),
- .nixCode = NixCode { .errPos = CUR_POS }
+ .errPos = CUR_POS
});
$$ = new ExprString(data->symbols.create($1));
}
@@ -492,7 +492,7 @@ attrs
} else
throw ParseError({
.hint = hintfmt("dynamic attributes not allowed in inherit"),
- .nixCode = NixCode { .errPos = makeCurPos(@2, data) },
+ .errPos = makeCurPos(@2, data)
});
}
| { $$ = new AttrPath; }
@@ -569,13 +569,24 @@ formal
namespace nix {
-Expr * EvalState::parse(const char * text,
+Expr * EvalState::parse(const char * text, FileOrigin origin,
const Path & path, const Path & basePath, StaticEnv & staticEnv)
{
yyscan_t scanner;
ParseData data(*this);
+ data.origin = origin;
+ switch (origin) {
+ case foFile:
+ data.file = data.symbols.create(path);
+ break;
+ case foStdin:
+ case foString:
+ data.file = data.symbols.create(text);
+ break;
+ default:
+ assert(false);
+ }
data.basePath = basePath;
- data.path = data.symbols.create(path);
yylex_init(&scanner);
yy_scan_string(text, scanner);
@@ -625,13 +636,13 @@ Expr * EvalState::parseExprFromFile(const Path & path)
Expr * EvalState::parseExprFromFile(const Path & path, StaticEnv & staticEnv)
{
- return parse(readFile(path).c_str(), path, dirOf(path), staticEnv);
+ return parse(readFile(path).c_str(), foFile, path, dirOf(path), staticEnv);
}
Expr * EvalState::parseExprFromString(std::string_view s, const Path & basePath, StaticEnv & staticEnv)
{
- return parse(s.data(), "(string)", basePath, staticEnv);
+ return parse(s.data(), foString, "", basePath, staticEnv);
}
@@ -644,7 +655,7 @@ Expr * EvalState::parseExprFromString(std::string_view s, const Path & basePath)
Expr * EvalState::parseStdin()
{
//Activity act(*logger, lvlTalkative, format("parsing standard input"));
- return parseExprFromString(drainFD(0), absPath("."));
+ return parse(drainFD(0).data(), foStdin, "", absPath("."), staticBaseEnv);
}
@@ -693,7 +704,7 @@ Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos
? "cannot look up '<%s>' in pure evaluation mode (use '--impure' to override)"
: "file '%s' was not found in the Nix search path (add it using $NIX_PATH or -I)",
path),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
}
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 2f1a41a64..dec917b38 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -96,7 +96,7 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
} catch (InvalidPathError & e) {
throw EvalError({
.hint = hintfmt("cannot import '%1%', since path '%2%' is not valid", path, e.path),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
}
@@ -177,7 +177,7 @@ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value
.hint = hintfmt(
"cannot import '%1%', since path '%2%' is not valid",
path, e.path),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
}
@@ -215,7 +215,7 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
if (count == 0) {
throw EvalError({
.hint = hintfmt("at least one argument to 'exec' required"),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
}
PathSet context;
@@ -230,7 +230,7 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
throw EvalError({
.hint = hintfmt("cannot execute '%1%', since path '%2%' is not valid",
program, e.path),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
}
@@ -239,13 +239,13 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
try {
parsed = state.parseExprFromString(output, pos.file);
} catch (Error & e) {
- e.addPrefix(fmt("While parsing the output from '%1%', at %2%\n", program, pos));
+ e.addTrace(pos, "While parsing the output from '%1%'", program);
throw;
}
try {
state.eval(parsed, v);
} catch (Error & e) {
- e.addPrefix(fmt("While evaluating the output from '%1%', at %2%\n", program, pos));
+ e.addTrace(pos, "While evaluating the output from '%1%'", program);
throw;
}
}
@@ -385,7 +385,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
if (startSet == args[0]->attrs->end())
throw EvalError({
.hint = hintfmt("attribute 'startSet' required"),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
state.forceList(*startSet->value, pos);
@@ -399,7 +399,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
if (op == args[0]->attrs->end())
throw EvalError({
.hint = hintfmt("attribute 'operator' required"),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
state.forceValue(*op->value, pos);
@@ -421,7 +421,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
if (key == e->attrs->end())
throw EvalError({
.hint = hintfmt("attribute 'key' required"),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
state.forceValue(*key->value, pos);
@@ -471,7 +471,7 @@ static void prim_addErrorContext(EvalState & state, const Pos & pos, Value * * a
v = *args[1];
} catch (Error & e) {
PathSet context;
- e.addPrefix(format("%1%\n") % state.coerceToString(pos, *args[0], context));
+ e.addTrace(std::nullopt, state.coerceToString(pos, *args[0], context));
throw;
}
}
@@ -556,14 +556,14 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
if (attr == args[0]->attrs->end())
throw EvalError({
.hint = hintfmt("required attribute 'name' missing"),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
string drvName;
Pos & posDrvName(*attr->pos);
try {
drvName = state.forceStringNoCtx(*attr->value, pos);
} catch (Error & e) {
- e.addPrefix(fmt("while evaluating the derivation attribute 'name' at %1%:\n", posDrvName));
+ e.addTrace(posDrvName, "while evaluating the derivation attribute 'name'");
throw;
}
@@ -603,7 +603,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
else
throw EvalError({
.hint = hintfmt("invalid value '%s' for 'outputHashMode' attribute", s),
- .nixCode = NixCode { .errPos = posDrvName }
+ .errPos = posDrvName
});
};
@@ -613,7 +613,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
if (outputs.find(j) != outputs.end())
throw EvalError({
.hint = hintfmt("duplicate derivation output '%1%'", j),
- .nixCode = NixCode { .errPos = posDrvName }
+ .errPos = posDrvName
});
/* !!! Check whether j is a valid attribute
name. */
@@ -623,14 +623,14 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
if (j == "drv")
throw EvalError({
.hint = hintfmt("invalid derivation output name 'drv'" ),
- .nixCode = NixCode { .errPos = posDrvName }
+ .errPos = posDrvName
});
outputs.insert(j);
}
if (outputs.empty())
throw EvalError({
.hint = hintfmt("derivation cannot have an empty set of outputs"),
- .nixCode = NixCode { .errPos = posDrvName }
+ .errPos = posDrvName
});
};
@@ -696,8 +696,9 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
}
} catch (Error & e) {
- e.addPrefix(format("while evaluating the attribute '%1%' of the derivation '%2%' at %3%:\n")
- % key % drvName % posDrvName);
+ e.addTrace(posDrvName,
+ "while evaluating the attribute '%1%' of the derivation '%2%'",
+ key, drvName);
throw;
}
}
@@ -745,20 +746,20 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
if (drv.builder == "")
throw EvalError({
.hint = hintfmt("required attribute 'builder' missing"),
- .nixCode = NixCode { .errPos = posDrvName }
+ .errPos = posDrvName
});
if (drv.platform == "")
throw EvalError({
.hint = hintfmt("required attribute 'system' missing"),
- .nixCode = NixCode { .errPos = posDrvName }
+ .errPos = posDrvName
});
/* Check whether the derivation name is valid. */
if (isDerivation(drvName))
throw EvalError({
.hint = hintfmt("derivation names are not allowed to end in '%s'", drvExtension),
- .nixCode = NixCode { .errPos = posDrvName }
+ .errPos = posDrvName
});
if (outputHash) {
@@ -766,20 +767,20 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
if (outputs.size() != 1 || *(outputs.begin()) != "out")
throw Error({
.hint = hintfmt("multiple outputs are not supported in fixed-output derivations"),
- .nixCode = NixCode { .errPos = posDrvName }
+ .errPos = posDrvName
});
- HashType ht = outputHashAlgo.empty() ? htUnknown : parseHashType(outputHashAlgo);
-
+ std::optional ht = parseHashTypeOpt(outputHashAlgo);
Hash h = newHashAllowEmpty(*outputHash, ht);
auto outPath = state.store->makeFixedOutputPath(ingestionMethod, h, drvName);
if (!jsonObject) drv.env["out"] = state.store->printStorePath(outPath);
drv.outputs.insert_or_assign("out", DerivationOutput {
- std::move(outPath),
- (ingestionMethod == FileIngestionMethod::Recursive ? "r:" : "")
- + printHashType(h.type),
- h.to_string(Base16, false),
+ .path = std::move(outPath),
+ .hash = FixedOutputHash {
+ .method = ingestionMethod,
+ .hash = std::move(h),
+ },
});
}
@@ -793,7 +794,10 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
for (auto & i : outputs) {
if (!jsonObject) drv.env[i] = "";
drv.outputs.insert_or_assign(i,
- DerivationOutput { StorePath::dummy, "", "" });
+ DerivationOutput {
+ .path = StorePath::dummy,
+ .hash = std::optional {},
+ });
}
Hash h = hashDerivationModulo(*state.store, Derivation(drv), true);
@@ -802,7 +806,10 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
auto outPath = state.store->makeOutputPath(i, h, drvName);
if (!jsonObject) drv.env[i] = state.store->printStorePath(outPath);
drv.outputs.insert_or_assign(i,
- DerivationOutput { std::move(outPath), "", "" });
+ DerivationOutput {
+ .path = std::move(outPath),
+ .hash = std::optional(),
+ });
}
}
@@ -874,7 +881,7 @@ static void prim_storePath(EvalState & state, const Pos & pos, Value * * args, V
if (!state.store->isInStore(path))
throw EvalError({
.hint = hintfmt("path '%1%' is not in the Nix store", path),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
Path path2 = state.store->toStorePath(path);
if (!settings.readOnlyMode)
@@ -895,7 +902,7 @@ static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args,
.hint = hintfmt(
"cannot check the existence of '%1%', since path '%2%' is not valid",
path, e.path),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
}
@@ -941,7 +948,7 @@ static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Va
} catch (InvalidPathError & e) {
throw EvalError({
.hint = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
}
string s = readFile(state.checkSourcePath(state.toRealPath(path, context)));
@@ -972,7 +979,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
if (i == v2.attrs->end())
throw EvalError({
.hint = hintfmt("attribute 'path' missing"),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
PathSet context;
@@ -983,7 +990,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
} catch (InvalidPathError & e) {
throw EvalError({
.hint = hintfmt("cannot find '%1%', since path '%2%' is not valid", path, e.path),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
}
@@ -999,17 +1006,17 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
string type = state.forceStringNoCtx(*args[0], pos);
- HashType ht = parseHashType(type);
- if (ht == htUnknown)
+ std::optional ht = parseHashType(type);
+ if (!ht)
throw Error({
.hint = hintfmt("unknown hash type '%1%'", type),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
PathSet context; // discarded
Path p = state.coerceToPath(pos, *args[1], context);
- mkString(v, hashFile(ht, state.checkSourcePath(p)).to_string(Base16, false), context);
+ mkString(v, hashFile(*ht, state.checkSourcePath(p)).to_string(Base16, false), context);
}
/* Read a directory (without . or ..) */
@@ -1022,7 +1029,7 @@ static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Val
} catch (InvalidPathError & e) {
throw EvalError({
.hint = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
}
@@ -1098,7 +1105,7 @@ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Valu
"in 'toFile': the file named '%1%' must not contain a reference "
"to a derivation but contains (%2%)",
name, path),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
refs.insert(state.store->parseStorePath(path));
}
@@ -1169,7 +1176,7 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
if (!context.empty())
throw EvalError({
.hint = hintfmt("string '%1%' cannot refer to other paths", path),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
state.forceValue(*args[0], pos);
@@ -1178,7 +1185,7 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
.hint = hintfmt(
"first argument in call to 'filterSource' is not a function but %1%",
showType(*args[0])),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
addPath(state, pos, std::string(baseNameOf(path)), path, args[0], FileIngestionMethod::Recursive, Hash(), v);
@@ -1201,7 +1208,7 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value
if (!context.empty())
throw EvalError({
.hint = hintfmt("string '%1%' cannot refer to other paths", path),
- .nixCode = NixCode { .errPos = *attr.pos }
+ .errPos = *attr.pos
});
} else if (attr.name == state.sName)
name = state.forceStringNoCtx(*attr.value, *attr.pos);
@@ -1215,13 +1222,13 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value
else
throw EvalError({
.hint = hintfmt("unsupported argument '%1%' to 'addPath'", attr.name),
- .nixCode = NixCode { .errPos = *attr.pos }
+ .errPos = *attr.pos
});
}
if (path.empty())
throw EvalError({
.hint = hintfmt("'path' required"),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
if (name.empty())
name = baseNameOf(path);
@@ -1282,7 +1289,7 @@ void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
if (i == args[1]->attrs->end())
throw EvalError({
.hint = hintfmt("attribute '%1%' missing", attr),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
// !!! add to stack trace?
if (state.countCalls && i->pos) state.attrSelects[*i->pos]++;
@@ -1365,7 +1372,7 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args,
if (j == v2.attrs->end())
throw TypeError({
.hint = hintfmt("'name' attribute missing in a call to 'listToAttrs'"),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
string name = state.forceStringNoCtx(*j->value, pos);
@@ -1375,7 +1382,7 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args,
if (j2 == v2.attrs->end())
throw TypeError({
.hint = hintfmt("'value' attribute missing in a call to 'listToAttrs'"),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
v.attrs->push_back(Attr(sym, j2->value, j2->pos));
}
@@ -1451,7 +1458,7 @@ static void prim_functionArgs(EvalState & state, const Pos & pos, Value * * args
if (args[0]->type != tLambda)
throw TypeError({
.hint = hintfmt("'functionArgs' requires a function"),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
if (!args[0]->lambda.fun->matchAttrs) {
@@ -1507,7 +1514,7 @@ static void elemAt(EvalState & state, const Pos & pos, Value & list, int n, Valu
if (n < 0 || (unsigned int) n >= list.listSize())
throw Error({
.hint = hintfmt("list index %1% is out of bounds", n),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
state.forceValue(*list.listElems()[n], pos);
v = *list.listElems()[n];
@@ -1537,7 +1544,7 @@ static void prim_tail(EvalState & state, const Pos & pos, Value * * args, Value
if (args[0]->listSize() == 0)
throw Error({
.hint = hintfmt("'tail' called on an empty list"),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
state.mkList(v, args[0]->listSize() - 1);
@@ -1682,7 +1689,7 @@ static void prim_genList(EvalState & state, const Pos & pos, Value * * args, Val
if (len < 0)
throw EvalError({
.hint = hintfmt("cannot create list of size %1%", len),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
state.mkList(v, len);
@@ -1844,7 +1851,7 @@ static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value &
if (f2 == 0)
throw EvalError({
.hint = hintfmt("division by zero"),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
if (args[0]->type == tFloat || args[1]->type == tFloat) {
@@ -1856,7 +1863,7 @@ static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value &
if (i1 == std::numeric_limits::min() && i2 == -1)
throw EvalError({
.hint = hintfmt("overflow in integer division"),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
mkInt(v, i1 / i2);
@@ -1917,7 +1924,7 @@ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, V
if (start < 0)
throw EvalError({
.hint = hintfmt("negative start position in 'substring'"),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
mkString(v, (unsigned int) start >= s.size() ? "" : string(s, start, len), context);
@@ -1936,17 +1943,17 @@ static void prim_stringLength(EvalState & state, const Pos & pos, Value * * args
static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
string type = state.forceStringNoCtx(*args[0], pos);
- HashType ht = parseHashType(type);
- if (ht == htUnknown)
+ std::optional ht = parseHashType(type);
+ if (!ht)
throw Error({
.hint = hintfmt("unknown hash type '%1%'", type),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
PathSet context; // discarded
string s = state.forceString(*args[1], context, pos);
- mkString(v, hashString(ht, s).to_string(Base16, false), context);
+ mkString(v, hashString(*ht, s).to_string(Base16, false), context);
}
@@ -1986,12 +1993,12 @@ void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v)
// limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
throw EvalError({
.hint = hintfmt("memory limit exceeded by regular expression '%s'", re),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
} else {
throw EvalError({
.hint = hintfmt("invalid regular expression '%s'", re),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
}
}
@@ -2059,12 +2066,12 @@ static void prim_split(EvalState & state, const Pos & pos, Value * * args, Value
// limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
throw EvalError({
.hint = hintfmt("memory limit exceeded by regular expression '%s'", re),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
} else {
throw EvalError({
.hint = hintfmt("invalid regular expression '%s'", re),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
}
}
@@ -2098,7 +2105,7 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar
if (args[0]->listSize() != args[1]->listSize())
throw EvalError({
.hint = hintfmt("'from' and 'to' arguments to 'replaceStrings' have different lengths"),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
vector from;
diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc
index efa2e9576..dbb93bae6 100644
--- a/src/libexpr/primops/context.cc
+++ b/src/libexpr/primops/context.cc
@@ -1,6 +1,6 @@
#include "primops.hh"
#include "eval-inline.hh"
-#include "derivations.hh"
+#include "store-api.hh"
namespace nix {
@@ -148,7 +148,7 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
if (!state.store->isStorePath(i.name))
throw EvalError({
.hint = hintfmt("Context key '%s' is not a store path", i.name),
- .nixCode = NixCode { .errPos = *i.pos }
+ .errPos = *i.pos
});
if (!settings.readOnlyMode)
state.store->ensurePath(state.store->parseStorePath(i.name));
@@ -165,7 +165,7 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
if (!isDerivation(i.name)) {
throw EvalError({
.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 }
+ .errPos = *i.pos
});
}
context.insert("=" + string(i.name));
@@ -178,7 +178,7 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
if (iter->value->listSize() && !isDerivation(i.name)) {
throw EvalError({
.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 }
+ .errPos = *i.pos
});
}
for (unsigned int n = 0; n < iter->value->listSize(); ++n) {
diff --git a/src/libexpr/primops/fetchGit.cc b/src/libexpr/primops/fetchGit.cc
index dd7229a3d..36b0db2bd 100644
--- a/src/libexpr/primops/fetchGit.cc
+++ b/src/libexpr/primops/fetchGit.cc
@@ -37,14 +37,14 @@ static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Va
else
throw EvalError({
.hint = hintfmt("unsupported argument '%s' to 'fetchGit'", attr.name),
- .nixCode = NixCode { .errPos = *attr.pos }
+ .errPos = *attr.pos
});
}
if (url.empty())
throw EvalError({
.hint = hintfmt("'url' argument required"),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
} else
diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc
index 9bace8f89..59166b777 100644
--- a/src/libexpr/primops/fetchMercurial.cc
+++ b/src/libexpr/primops/fetchMercurial.cc
@@ -40,14 +40,14 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
else
throw EvalError({
.hint = hintfmt("unsupported argument '%s' to 'fetchMercurial'", attr.name),
- .nixCode = NixCode { .errPos = *attr.pos }
+ .errPos = *attr.pos
});
}
if (url.empty())
throw EvalError({
.hint = hintfmt("'url' argument required"),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
} else
diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc
index 9be93710a..01d6ad8b0 100644
--- a/src/libexpr/primops/fetchTree.cc
+++ b/src/libexpr/primops/fetchTree.cc
@@ -68,7 +68,7 @@ static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, V
if (!attrs.count("type"))
throw Error({
.hint = hintfmt("attribute 'type' is missing in call to 'fetchTree'"),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
input = fetchers::inputFromAttrs(attrs);
@@ -112,14 +112,14 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
else
throw EvalError({
.hint = hintfmt("unsupported argument '%s' to '%s'", attr.name, who),
- .nixCode = NixCode { .errPos = *attr.pos }
+ .errPos = *attr.pos
});
}
if (!url)
throw EvalError({
.hint = hintfmt("'url' argument required"),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
} else
url = state.forceStringNoCtx(*args[0], pos);
diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc
index 7615d1379..b00827a4b 100644
--- a/src/libexpr/primops/fromTOML.cc
+++ b/src/libexpr/primops/fromTOML.cc
@@ -83,7 +83,7 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va
} catch (std::runtime_error & e) {
throw EvalError({
.hint = hintfmt("while parsing a TOML string: %s", e.what()),
- .nixCode = NixCode { .errPos = pos }
+ .errPos = pos
});
}
}
diff --git a/src/libfetchers/cache.hh b/src/libfetchers/cache.hh
index d76ab1233..3db4f081c 100644
--- a/src/libfetchers/cache.hh
+++ b/src/libfetchers/cache.hh
@@ -6,6 +6,8 @@ namespace nix::fetchers {
struct Cache
{
+ virtual ~Cache() { }
+
virtual void add(
ref store,
const Attrs & inAttrs,
diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc
index 11cac4c55..9174c3de4 100644
--- a/src/libfetchers/fetchers.cc
+++ b/src/libfetchers/fetchers.cc
@@ -36,7 +36,7 @@ std::unique_ptr inputFromAttrs(const Attrs & attrs)
if (res) {
if (auto narHash = maybeGetStrAttr(attrs, "narHash"))
// FIXME: require SRI hash.
- res->narHash = newHashAllowEmpty(*narHash, htUnknown);
+ res->narHash = newHashAllowEmpty(*narHash, {});
return res;
}
}
diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc
index 7966da314..f5356f0af 100644
--- a/src/libfetchers/tarball.cc
+++ b/src/libfetchers/tarball.cc
@@ -70,7 +70,10 @@ DownloadFileResult downloadFile(
ValidPathInfo info(store->makeFixedOutputPath(FileIngestionMethod::Flat, hash, name));
info.narHash = hashString(htSHA256, *sink.s);
info.narSize = sink.s->size();
- info.ca = makeFixedOutputCA(FileIngestionMethod::Flat, hash);
+ info.ca = FixedOutputHash {
+ .method = FileIngestionMethod::Flat,
+ .hash = hash,
+ };
auto source = StringSource { *sink.s };
store->addToStore(info, source, NoRepair, NoCheckSigs);
storePath = std::move(info.path);
@@ -264,7 +267,7 @@ struct TarballInputScheme : InputScheme
auto input = std::make_unique(parseURL(getStrAttr(attrs, "url")));
if (auto hash = maybeGetStrAttr(attrs, "hash"))
- input->hash = newHashAllowEmpty(*hash, htUnknown);
+ input->hash = newHashAllowEmpty(*hash, {});
return input;
}
diff --git a/src/libmain/loggers.cc b/src/libmain/loggers.cc
index c44bb6408..fa18f991d 100644
--- a/src/libmain/loggers.cc
+++ b/src/libmain/loggers.cc
@@ -26,7 +26,7 @@ Logger * makeDefaultLogger() {
case LogFormat::rawWithLogs:
return makeSimpleLogger(true);
case LogFormat::internalJson:
- return makeJSONLogger(*makeSimpleLogger());
+ return makeJSONLogger(*makeSimpleLogger(true));
case LogFormat::bar:
return makeProgressBar();
case LogFormat::barWithLogs:
diff --git a/src/libmain/progress-bar.cc b/src/libmain/progress-bar.cc
index 95a9187de..3f7d99a1d 100644
--- a/src/libmain/progress-bar.cc
+++ b/src/libmain/progress-bar.cc
@@ -131,7 +131,7 @@ public:
auto state(state_.lock());
std::stringstream oss;
- oss << ei;
+ showErrorInfo(oss, ei, loggerSettings.showTrace.get());
log(*state, ei.level, oss.str());
}
diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc
index 1cb422967..52718c231 100644
--- a/src/libmain/shared.cc
+++ b/src/libmain/shared.cc
@@ -323,10 +323,8 @@ int handleExceptions(const string & programName, std::function fun)
printError("Try '%1% --help' for more information.", programName);
return 1;
} catch (BaseError & e) {
- if (settings.showTrace && e.prefix() != "")
- printError(e.prefix());
logError(e.info());
- if (e.prefix() != "" && !settings.showTrace)
+ if (e.hasTrace() && !loggerSettings.showTrace.get())
printError("(use '--show-trace' to show detailed location information)");
return e.status;
} catch (std::bad_alloc & e) {
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index 3b0efa1a5..347fe1b99 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -1950,8 +1950,11 @@ void linkOrCopy(const Path & from, const Path & to)
/* Hard-linking fails if we exceed the maximum link count on a
file (e.g. 32000 of ext3), which is quite possible after a
'nix-store --optimise'. FIXME: actually, why don't we just
- bind-mount in this case? */
- if (errno != EMLINK)
+ bind-mount in this case?
+
+ It can also fail with EPERM in BeegFS v7 and earlier versions
+ which don't allow hard-links to other directories */
+ if (errno != EMLINK && errno != EPERM)
throw SysError("linking '%s' to '%s'", to, from);
copyPath(from, to);
}
@@ -2750,8 +2753,8 @@ struct RestrictedStore : public LocalFSStore
void queryReferrers(const StorePath & path, StorePathSet & referrers) override
{ }
- StorePathSet queryDerivationOutputs(const StorePath & path) override
- { throw Error("queryDerivationOutputs"); }
+ OutputPathMap queryDerivationOutputMap(const StorePath & path) override
+ { throw Error("queryDerivationOutputMap"); }
std::optional queryPathFromHashPart(const std::string & hashPart) override
{ throw Error("queryPathFromHashPart"); }
@@ -3714,14 +3717,11 @@ void DerivationGoal::registerOutputs()
/* Check that fixed-output derivations produced the right
outputs (i.e., the content hash should match the specified
hash). */
- std::string ca;
+ std::optional ca;
if (fixedOutput) {
- FileIngestionMethod outputHashMode; Hash h;
- i.second.parseHashInfo(outputHashMode, h);
-
- if (outputHashMode == FileIngestionMethod::Flat) {
+ if (i.second.hash->method == FileIngestionMethod::Flat) {
/* 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(
@@ -3732,20 +3732,22 @@ void DerivationGoal::registerOutputs()
/* Check the hash. In hash mode, move the path produced by
the derivation to its content-addressed location. */
- Hash h2 = outputHashMode == FileIngestionMethod::Recursive
- ? hashPath(h.type, actualPath).first
- : hashFile(h.type, actualPath);
+ Hash h2 = i.second.hash->method == FileIngestionMethod::Recursive
+ ? hashPath(*i.second.hash->hash.type, actualPath).first
+ : hashFile(*i.second.hash->hash.type, actualPath);
- auto dest = worker.store.makeFixedOutputPath(outputHashMode, h2, i.second.path.name());
+ auto dest = worker.store.makeFixedOutputPath(i.second.hash->method, h2, i.second.path.name());
- if (h != h2) {
+ if (i.second.hash->hash != h2) {
/* Throw an error after registering the path as
valid. */
worker.hashMismatch = true;
delayedException = std::make_exception_ptr(
BuildError("hash mismatch in fixed-output derivation '%s':\n wanted: %s\n got: %s",
- worker.store.printStorePath(dest), h.to_string(SRI, true), h2.to_string(SRI, true)));
+ worker.store.printStorePath(dest),
+ i.second.hash->hash.to_string(SRI, true),
+ h2.to_string(SRI, true)));
Path actualDest = worker.store.Store::toRealPath(dest);
@@ -3765,7 +3767,10 @@ void DerivationGoal::registerOutputs()
else
assert(worker.store.parseStorePath(path) == dest);
- ca = makeFixedOutputCA(outputHashMode, h2);
+ ca = FixedOutputHash {
+ .method = i.second.hash->method,
+ .hash = h2,
+ };
}
/* Get rid of all weird permissions. This also checks that
@@ -3838,7 +3843,10 @@ void DerivationGoal::registerOutputs()
info.ca = ca;
worker.store.signPathInfo(info);
- if (!info.references.empty()) info.ca.clear();
+ if (!info.references.empty()) {
+ // FIXME don't we have an experimental feature for fixed output with references?
+ info.ca = {};
+ }
infos.emplace(i.first, std::move(info));
}
@@ -4998,7 +5006,7 @@ bool Worker::pathContentsGood(const StorePath & path)
if (!pathExists(store.printStorePath(path)))
res = false;
else {
- HashResult current = hashPath(info->narHash.type, store.printStorePath(path));
+ HashResult current = hashPath(*info->narHash.type, store.printStorePath(path));
Hash nullHash(htSHA256);
res = info->narHash == nullHash || info->narHash == current.first;
}
diff --git a/src/libstore/builtins/fetchurl.cc b/src/libstore/builtins/fetchurl.cc
index 2048f8f87..e630cf6f1 100644
--- a/src/libstore/builtins/fetchurl.cc
+++ b/src/libstore/builtins/fetchurl.cc
@@ -58,14 +58,17 @@ void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData)
}
};
+ /* We always have one output, and if it's a fixed-output derivation (as
+ checked below) it must be the only output */
+ auto & output = drv.outputs.begin()->second;
+
/* Try the hashed mirrors first. */
- if (getAttr("outputHashMode") == "flat")
+ if (output.hash && output.hash->method == FileIngestionMethod::Flat)
for (auto hashedMirror : settings.hashedMirrors.get())
try {
if (!hasSuffix(hashedMirror, "/")) hashedMirror += '/';
- auto ht = parseHashType(getAttr("outputHashAlgo"));
- auto h = Hash(getAttr("outputHash"), ht);
- fetch(hashedMirror + printHashType(h.type) + "/" + h.to_string(Base16, false));
+ auto & h = output.hash->hash;
+ fetch(hashedMirror + printHashType(*h.type) + "/" + h.to_string(Base16, false));
return;
} catch (Error & e) {
debug(e.what());
diff --git a/src/libstore/content-address.cc b/src/libstore/content-address.cc
new file mode 100644
index 000000000..3d753836f
--- /dev/null
+++ b/src/libstore/content-address.cc
@@ -0,0 +1,85 @@
+#include "content-address.hh"
+
+namespace nix {
+
+std::string FixedOutputHash::printMethodAlgo() const {
+ return makeFileIngestionPrefix(method) + printHashType(*hash.type);
+}
+
+std::string makeFileIngestionPrefix(const FileIngestionMethod m) {
+ switch (m) {
+ case FileIngestionMethod::Flat:
+ return "";
+ case FileIngestionMethod::Recursive:
+ return "r:";
+ default:
+ throw Error("impossible, caught both cases");
+ }
+}
+
+std::string makeFixedOutputCA(FileIngestionMethod method, const Hash & hash)
+{
+ return "fixed:"
+ + makeFileIngestionPrefix(method)
+ + hash.to_string(Base32, true);
+}
+
+// FIXME Put this somewhere?
+template struct overloaded : Ts... { using Ts::operator()...; };
+template overloaded(Ts...) -> overloaded;
+
+std::string renderContentAddress(ContentAddress ca) {
+ return std::visit(overloaded {
+ [](TextHash th) {
+ return "text:" + th.hash.to_string(Base32, true);
+ },
+ [](FixedOutputHash fsh) {
+ return makeFixedOutputCA(fsh.method, fsh.hash);
+ }
+ }, ca);
+}
+
+ContentAddress parseContentAddress(std::string_view rawCa) {
+ auto prefixSeparator = rawCa.find(':');
+ if (prefixSeparator != string::npos) {
+ auto prefix = string(rawCa, 0, prefixSeparator);
+ if (prefix == "text") {
+ auto hashTypeAndHash = rawCa.substr(prefixSeparator+1, string::npos);
+ Hash hash = Hash(string(hashTypeAndHash));
+ if (*hash.type != htSHA256) {
+ throw Error("parseContentAddress: the text hash should have type SHA256");
+ }
+ return TextHash { hash };
+ } else if (prefix == "fixed") {
+ // This has to be an inverse of makeFixedOutputCA
+ auto methodAndHash = rawCa.substr(prefixSeparator+1, string::npos);
+ if (methodAndHash.substr(0,2) == "r:") {
+ std::string_view hashRaw = methodAndHash.substr(2,string::npos);
+ return FixedOutputHash {
+ .method = FileIngestionMethod::Recursive,
+ .hash = Hash(string(hashRaw)),
+ };
+ } else {
+ std::string_view hashRaw = methodAndHash;
+ return FixedOutputHash {
+ .method = FileIngestionMethod::Flat,
+ .hash = Hash(string(hashRaw)),
+ };
+ }
+ } else {
+ throw Error("parseContentAddress: format not recognized; has to be text or fixed");
+ }
+ } else {
+ throw Error("Not a content address because it lacks an appropriate prefix");
+ }
+};
+
+std::optional parseContentAddressOpt(std::string_view rawCaOpt) {
+ return rawCaOpt == "" ? std::optional {} : parseContentAddress(rawCaOpt);
+};
+
+std::string renderContentAddress(std::optional ca) {
+ return ca ? renderContentAddress(*ca) : "";
+}
+
+}
diff --git a/src/libstore/content-address.hh b/src/libstore/content-address.hh
new file mode 100644
index 000000000..ba4797f5b
--- /dev/null
+++ b/src/libstore/content-address.hh
@@ -0,0 +1,56 @@
+#pragma once
+
+#include
+#include "hash.hh"
+
+namespace nix {
+
+enum struct FileIngestionMethod : uint8_t {
+ Flat = false,
+ Recursive = true
+};
+
+struct TextHash {
+ Hash hash;
+};
+
+/// Pair of a hash, and how the file system was ingested
+struct FixedOutputHash {
+ FileIngestionMethod method;
+ Hash hash;
+ std::string printMethodAlgo() const;
+};
+
+/*
+ We've accumulated several types of content-addressed paths over the years;
+ fixed-output derivations support multiple hash algorithms and serialisation
+ methods (flat file vs NAR). Thus, ‘ca’ has one of the following forms:
+
+ * ‘text:sha256:’: For paths
+ computed by makeTextPath() / addTextToStore().
+
+ * ‘fixed:::’: For paths computed by
+ makeFixedOutputPath() / addToStore().
+*/
+typedef std::variant<
+ TextHash, // for paths computed by makeTextPath() / addTextToStore
+ FixedOutputHash // for path computed by makeFixedOutputPath
+> ContentAddress;
+
+/* Compute the prefix to the hash algorithm which indicates how the files were
+ ingested. */
+std::string makeFileIngestionPrefix(const FileIngestionMethod m);
+
+/* Compute the content-addressability assertion (ValidPathInfo::ca)
+ for paths created by makeFixedOutputPath() / addToStore(). */
+std::string makeFixedOutputCA(FileIngestionMethod method, const Hash & hash);
+
+std::string renderContentAddress(ContentAddress ca);
+
+std::string renderContentAddress(std::optional ca);
+
+ContentAddress parseContentAddress(std::string_view rawCa);
+
+std::optional parseContentAddressOpt(std::string_view rawCaOpt);
+
+}
diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc
index b2e37c5b5..cc72600bf 100644
--- a/src/libstore/daemon.cc
+++ b/src/libstore/daemon.cc
@@ -78,10 +78,10 @@ struct TunnelLogger : public Logger
if (ei.level > verbosity) return;
std::stringstream oss;
- oss << ei;
+ showErrorInfo(oss, ei, false);
StringSink buf;
- buf << STDERR_NEXT << oss.str() << "\n"; // (fs.s + "\n");
+ buf << STDERR_NEXT << oss.str() << "\n";
enqueueMsg(*buf.s);
}
@@ -347,6 +347,15 @@ static void performOp(TunnelLogger * logger, ref store,
break;
}
+ case wopQueryDerivationOutputMap: {
+ auto path = store->parseStorePath(readString(from));
+ logger->startWork();
+ OutputPathMap outputs = store->queryDerivationOutputMap(path);
+ logger->stopWork();
+ writeOutputPathMap(*store, to, outputs);
+ break;
+ }
+
case wopQueryDeriver: {
auto path = store->parseStorePath(readString(from));
logger->startWork();
@@ -652,7 +661,7 @@ static void performOp(TunnelLogger * logger, ref store,
if (GET_PROTOCOL_MINOR(clientVersion) >= 16) {
to << info->ultimate
<< info->sigs
- << info->ca;
+ << renderContentAddress(info->ca);
}
} else {
assert(GET_PROTOCOL_MINOR(clientVersion) >= 17);
@@ -710,7 +719,8 @@ static void performOp(TunnelLogger * logger, ref store,
info.references = readStorePaths(*store, from);
from >> info.registrationTime >> info.narSize >> info.ultimate;
info.sigs = readStrings(from);
- from >> info.ca >> repair >> dontCheckSigs;
+ info.ca = parseContentAddressOpt(readString(from));
+ from >> repair >> dontCheckSigs;
if (!trusted && dontCheckSigs)
dontCheckSigs = false;
if (!trusted)
diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc
index b95f7bfdc..42551ef6b 100644
--- a/src/libstore/derivations.cc
+++ b/src/libstore/derivations.cc
@@ -8,25 +8,6 @@
namespace nix {
-
-void DerivationOutput::parseHashInfo(FileIngestionMethod & recursive, Hash & hash) const
-{
- recursive = FileIngestionMethod::Flat;
- string algo = hashAlgo;
-
- if (string(algo, 0, 2) == "r:") {
- recursive = FileIngestionMethod::Recursive;
- algo = string(algo, 2);
- }
-
- HashType hashType = parseHashType(algo);
- if (hashType == htUnknown)
- throw Error("unknown hash algorithm '%s'", algo);
-
- hash = Hash(this->hash, hashType);
-}
-
-
const StorePath & BasicDerivation::findOutput(const string & id) const
{
auto i = outputs.find(id);
@@ -120,6 +101,34 @@ static StringSet parseStrings(std::istream & str, bool arePaths)
}
+static DerivationOutput parseDerivationOutput(const Store & store, istringstream_nocopy & str)
+{
+ expect(str, ","); auto path = store.parseStorePath(parsePath(str));
+ expect(str, ","); auto hashAlgo = parseString(str);
+ expect(str, ","); const auto hash = parseString(str);
+ expect(str, ")");
+
+ std::optional fsh;
+ if (hashAlgo != "") {
+ auto method = FileIngestionMethod::Flat;
+ if (string(hashAlgo, 0, 2) == "r:") {
+ method = FileIngestionMethod::Recursive;
+ hashAlgo = string(hashAlgo, 2);
+ }
+ const HashType hashType = parseHashType(hashAlgo);
+ fsh = FixedOutputHash {
+ .method = std::move(method),
+ .hash = Hash(hash, hashType),
+ };
+ }
+
+ return DerivationOutput {
+ .path = std::move(path),
+ .hash = std::move(fsh),
+ };
+}
+
+
static Derivation parseDerivation(const Store & store, const string & s)
{
Derivation drv;
@@ -129,15 +138,8 @@ static Derivation parseDerivation(const Store & store, const string & s)
/* Parse the list of outputs. */
while (!endOfList(str)) {
expect(str, "("); std::string id = parseString(str);
- expect(str, ","); auto path = store.parseStorePath(parsePath(str));
- expect(str, ","); auto hashAlgo = parseString(str);
- expect(str, ","); auto hash = parseString(str);
- expect(str, ")");
- drv.outputs.emplace(id, DerivationOutput {
- .path = std::move(path),
- .hashAlgo = std::move(hashAlgo),
- .hash = std::move(hash)
- });
+ auto output = parseDerivationOutput(store, str);
+ drv.outputs.emplace(std::move(id), std::move(output));
}
/* Parse the list of input derivations. */
@@ -263,8 +265,9 @@ string Derivation::unparse(const Store & store, bool maskOutputs,
if (first) first = false; else s += ',';
s += '('; printUnquotedString(s, i.first);
s += ','; printUnquotedString(s, maskOutputs ? "" : store.printStorePath(i.second.path));
- s += ','; printUnquotedString(s, i.second.hashAlgo);
- s += ','; printUnquotedString(s, i.second.hash);
+ s += ','; printUnquotedString(s, i.second.hash ? i.second.hash->printMethodAlgo() : "");
+ s += ','; printUnquotedString(s,
+ i.second.hash ? i.second.hash->hash.to_string(Base16, false) : "");
s += ')';
}
@@ -320,7 +323,7 @@ bool BasicDerivation::isFixedOutput() const
{
return outputs.size() == 1 &&
outputs.begin()->first == "out" &&
- outputs.begin()->second.hash != "";
+ outputs.begin()->second.hash;
}
@@ -353,8 +356,8 @@ Hash hashDerivationModulo(Store & store, const Derivation & drv, bool maskOutput
if (drv.isFixedOutput()) {
DerivationOutputs::const_iterator i = drv.outputs.begin();
return hashString(htSHA256, "fixed:out:"
- + i->second.hashAlgo + ":"
- + i->second.hash + ":"
+ + i->second.hash->printMethodAlgo() + ":"
+ + i->second.hash->hash.to_string(Base16, false) + ":"
+ store.printStorePath(i->second.path));
}
@@ -397,6 +400,31 @@ StorePathSet BasicDerivation::outputPaths() const
return paths;
}
+static DerivationOutput readDerivationOutput(Source & in, const Store & store)
+{
+ auto path = store.parseStorePath(readString(in));
+ auto hashAlgo = readString(in);
+ auto hash = readString(in);
+
+ std::optional fsh;
+ if (hashAlgo != "") {
+ auto method = FileIngestionMethod::Flat;
+ if (string(hashAlgo, 0, 2) == "r:") {
+ method = FileIngestionMethod::Recursive;
+ hashAlgo = string(hashAlgo, 2);
+ }
+ auto hashType = parseHashType(hashAlgo);
+ fsh = FixedOutputHash {
+ .method = std::move(method),
+ .hash = Hash(hash, hashType),
+ };
+ }
+
+ return DerivationOutput {
+ .path = std::move(path),
+ .hash = std::move(fsh),
+ };
+}
StringSet BasicDerivation::outputNames() const
{
@@ -413,14 +441,8 @@ Source & readDerivation(Source & in, const Store & store, BasicDerivation & drv)
auto nr = readNum(in);
for (size_t n = 0; n < nr; n++) {
auto name = readString(in);
- auto path = store.parseStorePath(readString(in));
- auto hashAlgo = readString(in);
- auto hash = readString(in);
- drv.outputs.emplace(name, DerivationOutput {
- .path = std::move(path),
- .hashAlgo = std::move(hashAlgo),
- .hash = std::move(hash)
- });
+ auto output = readDerivationOutput(in, store);
+ drv.outputs.emplace(std::move(name), std::move(output));
}
drv.inputSrcs = readStorePaths(store, in);
@@ -441,8 +463,16 @@ Source & readDerivation(Source & in, const Store & store, BasicDerivation & drv)
void writeDerivation(Sink & out, const Store & store, const BasicDerivation & drv)
{
out << drv.outputs.size();
- for (auto & i : drv.outputs)
- out << i.first << store.printStorePath(i.second.path) << i.second.hashAlgo << i.second.hash;
+ for (auto & i : drv.outputs) {
+ out << i.first
+ << store.printStorePath(i.second.path);
+ if (i.second.hash) {
+ out << i.second.hash->printMethodAlgo()
+ << i.second.hash->hash.to_string(Base16, false);
+ } else {
+ out << "" << "";
+ }
+ }
writeStorePaths(store, out, drv.inputSrcs);
out << drv.platform << drv.builder << drv.args;
out << drv.env.size();
diff --git a/src/libstore/derivations.hh b/src/libstore/derivations.hh
index d349c6d4d..68c53c1ff 100644
--- a/src/libstore/derivations.hh
+++ b/src/libstore/derivations.hh
@@ -1,8 +1,9 @@
#pragma once
+#include "path.hh"
#include "types.hh"
#include "hash.hh"
-#include "store-api.hh"
+#include "content-address.hh"
#include