summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/concepts/expressions.org4
-rw-r--r--src/buildtool/build_engine/expression/evaluator.cpp29
-rw-r--r--test/end-to-end/TARGETS1
-rw-r--r--test/end-to-end/user-errors/TARGETS8
-rwxr-xr-xtest/end-to-end/user-errors/flat-stage.sh64
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