diff options
author | Klaus Aehlig <klaus.aehlig@huawei.com> | 2024-08-05 11:16:16 +0200 |
---|---|---|
committer | Klaus Aehlig <klaus.aehlig@huawei.com> | 2024-08-05 16:13:45 +0200 |
commit | 68d4f5bb1d2dc953c1bccad96d7c6707159e0790 (patch) | |
tree | 86134584b9c978bdcefa9fcf61cfa01ebe48faf3 | |
parent | 731b383412bfc1a53cfd89e47b24111f5b22e5e5 (diff) | |
download | justbuild-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.md | 3 | ||||
-rw-r--r-- | doc/concepts/expressions.md | 11 | ||||
-rw-r--r-- | src/buildtool/build_engine/expression/evaluator.cpp | 26 | ||||
-rw-r--r-- | test/buildtool/build_engine/expression/expression.test.cpp | 66 |
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); |