diff options
-rw-r--r-- | doc/concepts/expressions.org | 4 | ||||
-rw-r--r-- | src/buildtool/build_engine/expression/evaluator.cpp | 29 | ||||
-rw-r--r-- | test/end-to-end/TARGETS | 1 | ||||
-rw-r--r-- | test/end-to-end/user-errors/TARGETS | 8 | ||||
-rwxr-xr-x | test/end-to-end/user-errors/flat-stage.sh | 64 |
5 files changed, 100 insertions, 6 deletions
diff --git a/doc/concepts/expressions.org b/doc/concepts/expressions.org index cfe058ba..c8d2a9a5 100644 --- a/doc/concepts/expressions.org +++ b/doc/concepts/expressions.org @@ -253,7 +253,9 @@ those) argument(s) to obtain the final result. argument ~"flat"~ (default ~false~) evaluates to a true value, the keys are instead replaced by the path concatenation of the ~"subdir"~ argument and the base name of the old key. It is an - error if conflicts occur in this way. + error if conflicts occur in this way; in case of such a user + error, the argument ~"msg"~ is also evaluated and the result + of that evaluatino reported in the error message. ***** Binary functions diff --git a/src/buildtool/build_engine/expression/evaluator.cpp b/src/buildtool/build_engine/expression/evaluator.cpp index 25723627..479de3b8 100644 --- a/src/buildtool/build_engine/expression/evaluator.cpp +++ b/src/buildtool/build_engine/expression/evaluator.cpp @@ -560,11 +560,30 @@ auto ToSubdirExpr(SubExprEvaluator&& eval, std::filesystem::path k{el.first}; auto new_path = subdir / k.filename(); if (result.contains(new_path) && !(result[new_path] == el.second)) { - throw Evaluator::EvaluationError{fmt::format( - "Flat staging of {} to subdir {} conflicts on path {}", - d->ToString(), - subdir.string(), - new_path.string())}; + // Check if the user specifed an error message for that case, + // otherwise just generate a generic error message. + auto msg_expr = expr->Map().Find("msg"); + if (not msg_expr) { + throw Evaluator::EvaluationError{fmt::format( + "Flat staging of {} to subdir {} conflicts on path {}", + d->ToString(), + subdir.string(), + new_path.string())}; + } + std::string msg; + try { + auto msg_val = eval(msg_expr->get(), env); + msg = msg_val->ToString(); + } catch (std::exception const&) { + msg = + "[non evaluating term] " + msg_expr->get()->ToString(); + } + std::stringstream ss{}; + ss << msg << std::endl; + ss << "Reason: flat staging to subdir " << subdir.string() + << " conflicts on path " << new_path.string() << std::endl; + ss << "Map to flatly stage was " << d->ToString() << std::endl; + throw Evaluator::EvaluationError(ss.str(), false, true); } result[new_path] = el.second; } diff --git a/test/end-to-end/TARGETS b/test/end-to-end/TARGETS index 03b76c0e..720834fd 100644 --- a/test/end-to-end/TARGETS +++ b/test/end-to-end/TARGETS @@ -7,6 +7,7 @@ [ [["./", "actions", "TESTS"], "actions"] , [["./", "generated-binary", "TESTS"], "generated-binary"] , [["./", "targets", "TESTS"], "targets"] + , [["./", "user-errors", "TESTS"], "user-errors"] ] } } diff --git a/test/end-to-end/user-errors/TARGETS b/test/end-to-end/user-errors/TARGETS new file mode 100644 index 00000000..cb1b881e --- /dev/null +++ b/test/end-to-end/user-errors/TARGETS @@ -0,0 +1,8 @@ +{ "flat-stage": + { "type": ["@", "rules", "shell/test", "script"] + , "name": ["flat-stage"] + , "test": ["flat-stage.sh"] + , "deps": [["test/end-to-end", "tool-under-test"]] + } +, "TESTS": {"type": "install", "tainted": ["test"], "deps": ["flat-stage"]} +} diff --git a/test/end-to-end/user-errors/flat-stage.sh b/test/end-to-end/user-errors/flat-stage.sh new file mode 100755 index 00000000..e0ee2780 --- /dev/null +++ b/test/end-to-end/user-errors/flat-stage.sh @@ -0,0 +1,64 @@ +#!/bin/sh +set -e + +mkdir .tool-root +touch ROOT +mkdir subdir +touch foo.txt subdir/foo.txt +cat > RULES <<'EOI' +{ "data": + { "target_fields": ["srcs"] + , "string_fields": ["flat"] + , "expression": + { "type": "let*" + , "bindings": + [ [ "srcs" + , { "type": "map_union" + , "$1": + { "type": "foreach" + , "var": "x" + , "range": {"type": "FIELD", "name": "srcs"} + , "body": + {"type": "DEP_ARTIFACTS", "dep": {"type": "var", "name": "x"}} + } + } + ] + , ["internal information", "DeBuG-InFoRmAtIoN"] + , [ "result" + , { "type": "to_subdir" + , "subdir": "data" + , "flat": {"type": "FIELD", "name": "flat"} + , "msg": + [ "DataRuleSpecificErrorMessage" + , {"type": "var", "name": "internal information"} + ] + , "$1": {"type": "var", "name": "srcs"} + } + ] + ] + , "body": + {"type": "RESULT", "artifacts": {"type": "var", "name": "result"}} + } + } +} +EOI + +cat > TARGETS <<'EOI' +{ "full": {"type": "data", "srcs": ["foo.txt", "subdir/foo.txt"]} +, "flat": + {"type": "data", "srcs": ["foo.txt", "subdir/foo.txt"], "flat": ["YES"]} +} +EOI + + +bin/tool-under-test build --local_build_root .tool-root -f build.log full 2>&1 +echo +grep 'DataRuleSpecificErrorMessage' build.log && exit 1 || : +grep 'DeBuG-InFoRmAtIoN' build.log && exit 1 || : + +bin/tool-under-test build --local_build_root .tool-root -f build.log flat 2>&1 && exit 1 || : +echo +grep 'DataRuleSpecificErrorMessage' build.log +grep "DeBuG-InFoRmAtIoN" build.log + +echo OK |