Forbid drvPath in strictDerivation outputs attribute

builtins.strictDerivation returns an attribute set with drvPath and
output paths. For some reason, current implementation forbids drv
instead of drvPath.
This commit is contained in:
Ivan Trubach 2024-05-08 18:09:51 +03:00
parent fcbc36cf78
commit 081faeda8c
2 changed files with 145 additions and 65 deletions

View file

@ -1184,11 +1184,11 @@ static void derivationStrictInternal(
.debugThrow(); .debugThrow();
/* !!! Check whether j is a valid attribute /* !!! Check whether j is a valid attribute
name. */ name. */
/* Derivations cannot be named drv, because /* Derivations cannot be named drvPath, because
then we'd have an attribute drvPath in we already have an attribute drvPath in
the resulting set. */ the resulting set (see state.sDrvPath). */
if (j == "drv") if (j == "drvPath")
state.error<EvalError>("invalid derivation output name 'drv'") state.error<EvalError>("invalid derivation output name 'drvPath'")
.atPos(v) .atPos(v)
.debugThrow(); .debugThrow();
outputs.insert(j); outputs.insert(j);

View file

@ -102,6 +102,74 @@ namespace nix {
, type \ , type \
) )
#define ASSERT_TRACE3(args, type, message, context1, context2) \
ASSERT_THROW( \
std::string expr(args); \
std::string name = expr.substr(0, expr.find(" ")); \
try { \
Value v = eval("builtins." args); \
state.forceValueDeep(v); \
} catch (BaseError & e) { \
ASSERT_EQ(PrintToString(e.info().msg), \
PrintToString(message)); \
ASSERT_EQ(e.info().traces.size(), 3) << "while testing " args << std::endl << e.what(); \
auto trace = e.info().traces.rbegin(); \
ASSERT_EQ(PrintToString(trace->hint), \
PrintToString(context1)); \
++trace; \
ASSERT_EQ(PrintToString(trace->hint), \
PrintToString(context2)); \
++trace; \
ASSERT_EQ(PrintToString(trace->hint), \
PrintToString(HintFmt("while calling the '%s' builtin", name))); \
throw; \
} \
, type \
)
#define ASSERT_TRACE4(args, type, message, context1, context2, context3) \
ASSERT_THROW( \
std::string expr(args); \
std::string name = expr.substr(0, expr.find(" ")); \
try { \
Value v = eval("builtins." args); \
state.forceValueDeep(v); \
} catch (BaseError & e) { \
ASSERT_EQ(PrintToString(e.info().msg), \
PrintToString(message)); \
ASSERT_EQ(e.info().traces.size(), 4) << "while testing " args << std::endl << e.what(); \
auto trace = e.info().traces.rbegin(); \
ASSERT_EQ(PrintToString(trace->hint), \
PrintToString(context1)); \
++trace; \
ASSERT_EQ(PrintToString(trace->hint), \
PrintToString(context2)); \
++trace; \
ASSERT_EQ(PrintToString(trace->hint), \
PrintToString(context3)); \
++trace; \
ASSERT_EQ(PrintToString(trace->hint), \
PrintToString(HintFmt("while calling the '%s' builtin", name))); \
throw; \
} \
, type \
)
// We assume that expr starts with "builtins.derivationStrict { name =",
// otherwise the name attribute position (1, 29) would be invalid.
#define DERIVATION_TRACE_HINTFMT(name) \
HintFmt("while evaluating derivation '%s'\n" \
" whose name attribute is located at %s", \
name, Pos(1, 29, Pos::String{.source = make_ref<std::string>(expr)}))
// To keep things simple, we also assume that derivation name is "foo".
#define ASSERT_DERIVATION_TRACE1(args, type, message) \
ASSERT_TRACE2(args, type, message, DERIVATION_TRACE_HINTFMT("foo"))
#define ASSERT_DERIVATION_TRACE2(args, type, message, context) \
ASSERT_TRACE3(args, type, message, context, DERIVATION_TRACE_HINTFMT("foo"))
#define ASSERT_DERIVATION_TRACE3(args, type, message, context1, context2) \
ASSERT_TRACE4(args, type, message, context1, context2, DERIVATION_TRACE_HINTFMT("foo"))
TEST_F(ErrorTraceTest, genericClosure) { TEST_F(ErrorTraceTest, genericClosure) {
ASSERT_TRACE2("genericClosure 1", ASSERT_TRACE2("genericClosure 1",
TypeError, TypeError,
@ -1185,7 +1253,6 @@ namespace nix {
} }
/* // Needs different ASSERTs
TEST_F(ErrorTraceTest, derivationStrict) { TEST_F(ErrorTraceTest, derivationStrict) {
ASSERT_TRACE2("derivationStrict \"\"", ASSERT_TRACE2("derivationStrict \"\"",
TypeError, TypeError,
@ -1197,102 +1264,115 @@ namespace nix {
HintFmt("attribute '%s' missing", "name"), HintFmt("attribute '%s' missing", "name"),
HintFmt("in the attrset passed as argument to builtins.derivationStrict")); HintFmt("in the attrset passed as argument to builtins.derivationStrict"));
ASSERT_TRACE2("derivationStrict { name = 1; }", ASSERT_TRACE3("derivationStrict { name = 1; }",
TypeError, TypeError,
HintFmt("expected a string but found %s: %s", "an integer", "1"), HintFmt("expected a string but found %s: %s", "an integer", Uncolored(ANSI_CYAN "1" ANSI_NORMAL)),
HintFmt("while evaluating the `name` attribute passed to builtins.derivationStrict")); HintFmt("while evaluating the `name` attribute passed to builtins.derivationStrict"),
HintFmt("while evaluating the derivation attribute 'name'"));
ASSERT_TRACE2("derivationStrict { name = \"foo\"; }", ASSERT_DERIVATION_TRACE1("derivationStrict { name = \"foo\"; }",
TypeError, EvalError,
HintFmt("required attribute 'builder' missing"), HintFmt("required attribute 'builder' missing"));
HintFmt("while evaluating derivation 'foo'"));
ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; __structuredAttrs = 15; }", ASSERT_DERIVATION_TRACE2("derivationStrict { name = \"foo\"; builder = 1; __structuredAttrs = 15; }",
TypeError, TypeError,
HintFmt("expected a Boolean but found %s: %s", "an integer", "15"), HintFmt("expected a Boolean but found %s: %s", "an integer", Uncolored(ANSI_CYAN "15" ANSI_NORMAL)),
HintFmt("while evaluating the `__structuredAttrs` attribute passed to builtins.derivationStrict")); HintFmt("while evaluating the `__structuredAttrs` attribute passed to builtins.derivationStrict"));
ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; __ignoreNulls = 15; }", ASSERT_DERIVATION_TRACE2("derivationStrict { name = \"foo\"; builder = 1; __ignoreNulls = 15; }",
TypeError, TypeError,
HintFmt("expected a Boolean but found %s: %s", "an integer", "15"), HintFmt("expected a Boolean but found %s: %s", "an integer", Uncolored(ANSI_CYAN "15" ANSI_NORMAL)),
HintFmt("while evaluating the `__ignoreNulls` attribute passed to builtins.derivationStrict")); HintFmt("while evaluating the `__ignoreNulls` attribute passed to builtins.derivationStrict"));
ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; outputHashMode = 15; }", ASSERT_DERIVATION_TRACE2("derivationStrict { name = \"foo\"; builder = 1; outputHashMode = 15; }",
TypeError, EvalError,
HintFmt("invalid value '15' for 'outputHashMode' attribute"), HintFmt("invalid value '%s' for 'outputHashMode' attribute", "15"),
HintFmt("while evaluating the attribute 'outputHashMode' of derivation 'foo'")); HintFmt("while evaluating attribute '%s' of derivation '%s'", "outputHashMode", "foo"));
ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; outputHashMode = \"custom\"; }", ASSERT_DERIVATION_TRACE2("derivationStrict { name = \"foo\"; builder = 1; outputHashMode = \"custom\"; }",
TypeError, EvalError,
HintFmt("invalid value 'custom' for 'outputHashMode' attribute"), HintFmt("invalid value '%s' for 'outputHashMode' attribute", "custom"),
HintFmt("while evaluating the attribute 'outputHashMode' of derivation 'foo'")); HintFmt("while evaluating attribute '%s' of derivation '%s'", "outputHashMode", "foo"));
ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = {}; }", ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = {}; }",
TypeError, TypeError,
HintFmt("cannot coerce %s to a string: %s", "a set", "{ }"), HintFmt("cannot coerce %s to a string: { }", "a set"),
HintFmt("while evaluating the attribute 'system' of derivation 'foo'")); HintFmt(""),
HintFmt("while evaluating attribute '%s' of derivation '%s'", "system", "foo"));
ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = {}; }", ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = {}; }",
TypeError, TypeError,
HintFmt("cannot coerce %s to a string: %s", "a set", "{ }"), HintFmt("cannot coerce %s to a string: { }", "a set"),
HintFmt("while evaluating the attribute 'outputs' of derivation 'foo'")); HintFmt(""),
HintFmt("while evaluating attribute '%s' of derivation '%s'", "outputs", "foo"));
ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"drv\"; }", ASSERT_DERIVATION_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"drvPath\"; }",
TypeError, EvalError,
HintFmt("invalid derivation output name 'drv'"), HintFmt("invalid derivation output name 'drvPath'"),
HintFmt("while evaluating the attribute 'outputs' of derivation 'foo'")); HintFmt("while evaluating attribute '%s' of derivation '%s'", "outputs", "foo"));
ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = []; }", ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; outputs = \"out\"; __structuredAttrs = true; }",
TypeError, EvalError,
HintFmt("expected a list but found %s: %s", "a string", "\"out\""),
HintFmt(""),
HintFmt("while evaluating attribute '%s' of derivation '%s'", "outputs", "foo"));
ASSERT_DERIVATION_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = []; }",
EvalError,
HintFmt("derivation cannot have an empty set of outputs"), HintFmt("derivation cannot have an empty set of outputs"),
HintFmt("while evaluating the attribute 'outputs' of derivation 'foo'")); HintFmt("while evaluating attribute '%s' of derivation '%s'", "outputs", "foo"));
ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = [ \"drv\" ]; }", ASSERT_DERIVATION_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = [ \"drvPath\" ]; }",
TypeError, EvalError,
HintFmt("invalid derivation output name 'drv'"), HintFmt("invalid derivation output name 'drvPath'"),
HintFmt("while evaluating the attribute 'outputs' of derivation 'foo'")); HintFmt("while evaluating attribute '%s' of derivation '%s'", "outputs", "foo"));
ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = [ \"out\" \"out\" ]; }", ASSERT_DERIVATION_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = [ \"out\" \"out\" ]; }",
TypeError, EvalError,
HintFmt("duplicate derivation output 'out'"), HintFmt("duplicate derivation output '%s'", "out"),
HintFmt("while evaluating the attribute 'outputs' of derivation 'foo'")); HintFmt("while evaluating attribute '%s' of derivation '%s'", "outputs", "foo"));
ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; __contentAddressed = \"true\"; }", ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; __contentAddressed = \"true\"; }",
TypeError, TypeError,
HintFmt("expected a Boolean but found %s: %s", "a string", "\"true\""), HintFmt("expected a Boolean but found %s: %s", "a string", "\"true\""),
HintFmt("while evaluating the attribute '__contentAddressed' of derivation 'foo'")); HintFmt(""),
HintFmt("while evaluating attribute '%s' of derivation '%s'", "__contentAddressed", "foo"));
ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; __impure = \"true\"; }", ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; __impure = \"true\"; }",
TypeError, TypeError,
HintFmt("expected a Boolean but found %s: %s", "a string", "\"true\""), HintFmt("expected a Boolean but found %s: %s", "a string", "\"true\""),
HintFmt("while evaluating the attribute '__impure' of derivation 'foo'")); HintFmt(""),
HintFmt("while evaluating attribute '%s' of derivation '%s'", "__impure", "foo"));
ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; __impure = \"true\"; }", ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; __impure = \"true\"; }",
TypeError, TypeError,
HintFmt("expected a Boolean but found %s: %s", "a string", "\"true\""), HintFmt("expected a Boolean but found %s: %s", "a string", "\"true\""),
HintFmt("while evaluating the attribute '__impure' of derivation 'foo'")); HintFmt(""),
HintFmt("while evaluating attribute '%s' of derivation '%s'", "__impure", "foo"));
ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; args = \"foo\"; }", ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; args = \"foo\"; }",
TypeError, TypeError,
HintFmt("expected a list but found %s: %s", "a string", "\"foo\""), HintFmt("expected a list but found %s: %s", "a string", "\"foo\""),
HintFmt("while evaluating the attribute 'args' of derivation 'foo'")); HintFmt(""),
HintFmt("while evaluating attribute '%s' of derivation '%s'", "args", "foo"));
ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; args = [ {} ]; }", ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; args = [ {} ]; }",
TypeError, TypeError,
HintFmt("cannot coerce %s to a string: %s", "a set", "{ }"), HintFmt("cannot coerce %s to a string: { }", "a set"),
HintFmt("while evaluating an element of the argument list")); HintFmt("while evaluating an element of the argument list"),
HintFmt("while evaluating attribute '%s' of derivation '%s'", "args", "foo"));
ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; args = [ \"a\" {} ]; }", ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; args = [ \"a\" {} ]; }",
TypeError, TypeError,
HintFmt("cannot coerce %s to a string: %s", "a set", "{ }"), HintFmt("cannot coerce %s to a string: { }", "a set"),
HintFmt("while evaluating an element of the argument list")); HintFmt("while evaluating an element of the argument list"),
HintFmt("while evaluating attribute '%s' of derivation '%s'", "args", "foo"));
ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; FOO = {}; }", ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; FOO = {}; }",
TypeError, TypeError,
HintFmt("cannot coerce %s to a string: %s", "a set", "{ }"), HintFmt("cannot coerce %s to a string: { }", "a set"),
HintFmt("while evaluating the attribute 'FOO' of derivation 'foo'")); HintFmt(""),
HintFmt("while evaluating attribute '%s' of derivation '%s'", "FOO", "foo"));
} }
*/
} /* namespace nix */ } /* namespace nix */