summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKlaus Aehlig <klaus.aehlig@huawei.com>2024-08-05 11:16:16 +0200
committerKlaus Aehlig <klaus.aehlig@huawei.com>2024-08-05 16:13:45 +0200
commit68d4f5bb1d2dc953c1bccad96d7c6707159e0790 (patch)
tree86134584b9c978bdcefa9fcf61cfa01ebe48faf3
parent731b383412bfc1a53cfd89e47b24111f5b22e5e5 (diff)
downloadjustbuild-68d4f5bb1d2dc953c1bccad96d7c6707159e0790.tar.gz
Expression language: add expression from_subdir
... allowing to select only the keys in a specific subdir, and move the them to top-level.
-rw-r--r--CHANGELOG.md3
-rw-r--r--doc/concepts/expressions.md11
-rw-r--r--src/buildtool/build_engine/expression/evaluator.cpp26
-rw-r--r--test/buildtool/build_engine/expression/expression.test.cpp66
4 files changed, 99 insertions, 7 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 747c735d..56709f0f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,7 +21,8 @@ A feature release on top of `1.3.0`, backwards compatible.
at the old location will not be used anymore while still using
disk space.
- The expression language has been extended to contain quote and
- quasi-quote expressions.
+ quasi-quote expressions, as well as a new built-in function
+ `"from_subdir"`.
### Fixes
diff --git a/doc/concepts/expressions.md b/doc/concepts/expressions.md
index aaea3d27..c75f00dc 100644
--- a/doc/concepts/expressions.md
+++ b/doc/concepts/expressions.md
@@ -353,6 +353,17 @@ those) argument(s) to obtain the final result.
is an error if the values for keys in conflicting positions
are name-containing.
+ - `"from_subdir"` The argument has to be a map (not necessarily of
+ artifacts). The keys of this map, as well as the value of keyword
+ argument `"subdir"` (string, default `"."`) are interpreted as
+ paths; only those key-value pairs of the argument map are kept
+ where the key refers to an entry in the specified `"subdir"`,
+ and for those the path relative to the subdir is taken as new
+ key. Those paths relative to the subdir are taken in canonical
+ form; it is an error if non-trivial conflicts arise that way,
+ i.e., if two keys that are kept normalize to the same relative
+ path while the repsective values are different.
+
##### Binary functions
- `"=="` The result is `true` is the arguments are equal,
diff --git a/src/buildtool/build_engine/expression/evaluator.cpp b/src/buildtool/build_engine/expression/evaluator.cpp
index 707e9dc6..852d8c37 100644
--- a/src/buildtool/build_engine/expression/evaluator.cpp
+++ b/src/buildtool/build_engine/expression/evaluator.cpp
@@ -925,6 +925,31 @@ auto ToSubdirExpr(SubExprEvaluator&& eval,
return ExpressionPtr{Expression::map_t{result}};
}
+auto FromSubdirExpr(SubExprEvaluator&& eval,
+ ExpressionPtr const& expr,
+ Configuration const& env) -> ExpressionPtr {
+ auto d = eval(expr["$1"], env);
+ auto s = eval(expr->Get("subdir", "."s), env);
+ std::filesystem::path subdir{s->String()};
+ auto result = Expression::map_t::underlying_map_t{};
+ for (auto const& el : d->Map()) {
+ auto new_path = ToNormalPath(
+ std::filesystem::path(el.first).lexically_relative(subdir));
+ if (PathIsNonUpwards(new_path)) {
+ auto new_key = new_path.string();
+ if (auto it = result.find(new_key);
+ it != result.end() &&
+ (!((it->second == el.second) && el.second->IsCacheable()))) {
+ throw Evaluator::EvaluationError{
+ fmt::format("Staging conflict for path {}",
+ nlohmann::json(new_key).dump())};
+ }
+ result.emplace(std::move(new_key), el.second);
+ }
+ }
+ return ExpressionPtr{Expression::map_t{result}};
+}
+
auto ForeachExpr(SubExprEvaluator&& eval,
ExpressionPtr const& expr,
Configuration const& env) -> ExpressionPtr {
@@ -1259,6 +1284,7 @@ auto const kBuiltInFunctions =
return Union</*kDisjoint=*/false>(exp);
})},
{"to_subdir", ToSubdirExpr},
+ {"from_subdir", FromSubdirExpr},
{"foreach", ForeachExpr},
{"foreach_map", ForeachMapExpr},
{"foldl", FoldLeftExpr},
diff --git a/test/buildtool/build_engine/expression/expression.test.cpp b/test/buildtool/build_engine/expression/expression.test.cpp
index 40f19e88..938bd63f 100644
--- a/test/buildtool/build_engine/expression/expression.test.cpp
+++ b/test/buildtool/build_engine/expression/expression.test.cpp
@@ -1335,14 +1335,14 @@ TEST_CASE("Expression Evaluation", "[expression]") { // NOLINT
, "$1": "PLACEHOLDER" })"_json);
REQUIRE(expr);
- auto literal_foo = Expression::FromJson(
- R"({"type": "'", "$1": {"foo":true}})"_json);
+ auto literal_foo =
+ Expression::FromJson(R"({"type": "'", "$1": {"foo":true}})"_json);
REQUIRE(literal_foo);
- auto literal_foo_false = Expression::FromJson(
- R"({"type": "'", "$1": {"foo":false}})"_json);
+ auto literal_foo_false =
+ Expression::FromJson(R"({"type": "'", "$1": {"foo":false}})"_json);
REQUIRE(literal_foo_false);
- auto literal_bar = Expression::FromJson(
- R"({"type": "'", "$1": {"bar":false}})"_json);
+ auto literal_bar =
+ Expression::FromJson(R"({"type": "'", "$1": {"bar":false}})"_json);
REQUIRE(literal_bar);
expr = Replace(expr, "$1", list_t{literal_foo, literal_bar});
@@ -1460,6 +1460,60 @@ TEST_CASE("Expression Evaluation", "[expression]") { // NOLINT
CHECK_FALSE(expr.Evaluate(env, fcts));
}
+ SECTION("from_subdir") {
+ auto expr = Expression::FromJson(R"(
+ {"type": "from_subdir", "subdir": "foo"
+ , "$1": {"type": "'", "$1":
+ { "foo/a/b/c": "abc.txt"
+ , "foo/a/other": "other.txt"
+ , "foo/top": "top.xt"
+ , "foo/a/b/../d/e": "make canonical"
+ , "bar/a/b/c": "ignore bar/a/b/c"
+ , "bar/a/b/../b/c": "also ingnore other path"
+ }}}
+ )"_json);
+ REQUIRE(expr);
+
+ auto result = expr.Evaluate(env, fcts);
+ REQUIRE(result);
+ CHECK(result == Expression::FromJson(R"(
+ { "a/b/c": "abc.txt"
+ , "a/other": "other.txt"
+ , "top": "top.xt"
+ , "a/d/e": "make canonical"
+ }
+ )"_json));
+ }
+
+ SECTION("from_subdir trivial conflict") {
+ auto expr = Expression::FromJson(R"(
+ {"type": "from_subdir", "subdir": "foo"
+ , "$1": {"type": "'", "$1":
+ { "foo/a/b/c": "abc.txt"
+ , "foo/a/b/../b/c": "abc.txt"
+ }}}
+ )"_json);
+ REQUIRE(expr);
+
+ auto result = expr.Evaluate(env, fcts);
+ REQUIRE(result);
+ CHECK(result == Expression::FromJson(R"(
+ {"a/b/c": "abc.txt"}
+ )"_json));
+ }
+
+ SECTION("from_subdir conflict") {
+ auto expr = Expression::FromJson(R"(
+ {"type": "from_subdir", "subdir": "foo"
+ , "$1": {"type": "'", "$1":
+ { "foo/a/b/c": "one value"
+ , "foo/a/b/../b/c": "different value"
+ }}}
+ )"_json);
+ REQUIRE(expr);
+ CHECK_FALSE(expr.Evaluate(env, fcts));
+ }
+
fcts = FunctionMap::MakePtr(
fcts, "concat", [](auto&& eval, auto const& expr, auto const& env) {
auto p1 = eval(expr->Get("$1", ""s), env);