summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/concepts/expressions.org5
-rw-r--r--src/buildtool/build_engine/expression/TARGETS1
-rw-r--r--src/buildtool/build_engine/expression/evaluator.cpp39
-rw-r--r--test/buildtool/build_engine/expression/expression.test.cpp12
4 files changed, 50 insertions, 7 deletions
diff --git a/doc/concepts/expressions.org b/doc/concepts/expressions.org
index c8d2a9a5..50737413 100644
--- a/doc/concepts/expressions.org
+++ b/doc/concepts/expressions.org
@@ -255,7 +255,10 @@ those) argument(s) to obtain the final result.
~"subdir"~ argument and the base name of the old key. It is an
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.
+ of that evaluation reported in the error message. Note that
+ conflicts can also occur in non-flat staging if two keys are
+ different as strings, but name the same path (like ~"foo.txt"~
+ and ~"./foo.txt"~), and are assigned different values.
***** Binary functions
diff --git a/src/buildtool/build_engine/expression/TARGETS b/src/buildtool/build_engine/expression/TARGETS
index 2a72ed8c..6acd30c2 100644
--- a/src/buildtool/build_engine/expression/TARGETS
+++ b/src/buildtool/build_engine/expression/TARGETS
@@ -39,6 +39,7 @@
, ["src/utils/cpp", "hex_string"]
, ["src/utils/cpp", "concepts"]
, ["src/utils/cpp", "atomic"]
+ , ["src/utils/cpp", "path"]
, ["@", "gsl-lite", "", "gsl-lite"]
]
, "stage": ["src", "buildtool", "build_engine", "expression"]
diff --git a/src/buildtool/build_engine/expression/evaluator.cpp b/src/buildtool/build_engine/expression/evaluator.cpp
index 479de3b8..cfd0be31 100644
--- a/src/buildtool/build_engine/expression/evaluator.cpp
+++ b/src/buildtool/build_engine/expression/evaluator.cpp
@@ -10,6 +10,7 @@
#include "fmt/core.h"
#include "src/buildtool/build_engine/expression/configuration.hpp"
#include "src/buildtool/build_engine/expression/function_map.hpp"
+#include "src/utils/cpp/path.hpp"
namespace {
@@ -558,8 +559,8 @@ auto ToSubdirExpr(SubExprEvaluator&& eval,
if (flat) {
for (auto const& el : d->Map()) {
std::filesystem::path k{el.first};
- auto new_path = subdir / k.filename();
- if (result.contains(new_path) && !(result[new_path] == el.second)) {
+ auto new_key = ToNormalPath(subdir / k.filename()).string();
+ if (result.contains(new_key) && !(result[new_key] == el.second)) {
// 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");
@@ -568,7 +569,7 @@ auto ToSubdirExpr(SubExprEvaluator&& eval,
"Flat staging of {} to subdir {} conflicts on path {}",
d->ToString(),
subdir.string(),
- new_path.string())};
+ new_key)};
}
std::string msg;
try {
@@ -581,16 +582,42 @@ auto ToSubdirExpr(SubExprEvaluator&& eval,
std::stringstream ss{};
ss << msg << std::endl;
ss << "Reason: flat staging to subdir " << subdir.string()
- << " conflicts on path " << new_path.string() << std::endl;
+ << " conflicts on path " << new_key << std::endl;
ss << "Map to flatly stage was " << d->ToString() << std::endl;
throw Evaluator::EvaluationError(ss.str(), false, true);
}
- result[new_path] = el.second;
+ result[new_key] = el.second;
}
}
else {
for (auto const& el : d->Map()) {
- result[(subdir / el.first).string()] = el.second;
+ auto new_key = ToNormalPath(subdir / el.first).string();
+ if (auto it = result.find(new_key);
+ it != result.end() && !(it->second == el.second)) {
+ auto msg_expr = expr->Map().Find("msg");
+ if (not msg_expr) {
+ throw Evaluator::EvaluationError{fmt::format(
+ "Staging of {} to subdir {} conflicts on path {}",
+ d->ToString(),
+ subdir.string(),
+ new_key)};
+ }
+ 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: staging to subdir " << subdir.string()
+ << " conflicts on new path " << new_key << std::endl;
+ ss << "Map to stage was " << d->ToString() << std::endl;
+ throw Evaluator::EvaluationError(ss.str(), false, true);
+ }
+ result.emplace(std::move(new_key), el.second);
}
}
return ExpressionPtr{Expression::map_t{result}};
diff --git a/test/buildtool/build_engine/expression/expression.test.cpp b/test/buildtool/build_engine/expression/expression.test.cpp
index d80d8861..cbc8c7bf 100644
--- a/test/buildtool/build_engine/expression/expression.test.cpp
+++ b/test/buildtool/build_engine/expression/expression.test.cpp
@@ -1103,6 +1103,18 @@ TEST_CASE("Expression Evaluation", "[expression]") { // NOLINT
R"({"prefix/foo": "hello", "prefix/bar": "world"})"_json));
}
+ SECTION("to_subdir expression with conflict") {
+ auto expr = Expression::FromJson(R"(
+ { "type": "to_subdir"
+ , "subdir": "prefix"
+ , "$1": { "type": "literal"
+ , "$1": { "foo": "hello"
+ , "./foo": "world" }}})"_json);
+ REQUIRE(expr);
+
+ CHECK_FALSE(expr.Evaluate(env, fcts));
+ }
+
SECTION("flat to_subdir without proper conflict") {
auto expr = Expression::FromJson(R"(
{ "type": "to_subdir"