diff options
3 files changed, 195 insertions, 43 deletions
diff --git a/src/buildtool/build_engine/target_map/built_in_rules.cpp b/src/buildtool/build_engine/target_map/built_in_rules.cpp index 00a10efe..c0015b29 100644 --- a/src/buildtool/build_engine/target_map/built_in_rules.cpp +++ b/src/buildtool/build_engine/target_map/built_in_rules.cpp @@ -86,7 +86,6 @@ auto const kTreeRuleFields = std::unordered_set<std::string>{"arguments_config", "name", "tainted", "type"}; - auto const kInstallRuleFields = std::unordered_set<std::string>{"arguments_config", "deps", @@ -445,7 +444,8 @@ void TreeRuleWithDeps( const BuildMaps::Base::FieldReader::Ptr& desc, const BuildMaps::Target::TargetMap::SetterPtr& setter, const BuildMaps::Target::TargetMap::LoggerPtr& logger, - const gsl::not_null<BuildMaps::Target::ResultTargetMap*>& result_map) { + const gsl::not_null<BuildMaps::Target::ResultTargetMap*>& result_map, + std::optional<bool> disjoint_overlay) { auto param_vars = desc->ReadStringList("arguments_config"); if (not param_vars) { return; @@ -497,45 +497,75 @@ void TreeRuleWithDeps( {}, {}}; - // Compute the stage - auto stage = ExpressionPtr{Expression::map_t{}}; - for (auto const& dep : dependency_values) { - auto to_stage = ExpressionPtr{ - Expression::map_t{(*dep)->RunFiles(), (*dep)->Artifacts()}}; - auto dup = stage->Map().FindConflictingDuplicate(to_stage->Map()); - if (dup) { - std::unordered_map<BuildMaps::Base::EntityName, AnalysedTargetPtr> - deps_by_target; - deps_by_target.reserve(dependency_keys.size()); - for (std::size_t i = 0; i < dependency_keys.size(); ++i) { - deps_by_target.emplace(dependency_keys[i].target, - *dependency_values[i]); + // Compute the resulting stage + auto result_stage = Expression::map_t::underlying_map_t{}; + std::vector<Tree::Ptr> trees{}; + std::vector<TreeOverlay::Ptr> tree_overlays{}; + + if (disjoint_overlay) { + TreeOverlay::to_overlay_t dep_trees{}; + for (auto const& dep : dependency_values) { + std::unordered_map<std::string, ArtifactDescription> tree_content; + for (auto const& [input_path, artifact] : + (*dep)->Artifacts()->Map()) { + auto norm_path = + ToNormalPath(std::filesystem::path{input_path}); + tree_content.emplace(std::move(norm_path), + artifact->Artifact()); } - ReportStagingConflict( - dup->get(), stage, to_stage, deps_by_target, logger); - return; + auto tree = std::make_shared<Tree>(std::move(tree_content)); + auto tree_id = tree->Id(); + trees.emplace_back(std::move(tree)); + dep_trees.emplace_back(ArtifactDescription::CreateTree(tree_id)); + } + auto overlay_tree = std::make_shared<TreeOverlay>(std::move(dep_trees), + *disjoint_overlay); + auto overlay_tree_id = overlay_tree->Id(); + tree_overlays.emplace_back(std::move(overlay_tree)); + result_stage.emplace( + name, ArtifactDescription::CreateTreeOverlay(overlay_tree_id)); + } + else { + + auto stage = ExpressionPtr{Expression::map_t{}}; + for (auto const& dep : dependency_values) { + auto to_stage = ExpressionPtr{ + Expression::map_t{(*dep)->RunFiles(), (*dep)->Artifacts()}}; + auto dup = stage->Map().FindConflictingDuplicate(to_stage->Map()); + if (dup) { + std::unordered_map<BuildMaps::Base::EntityName, + AnalysedTargetPtr> + deps_by_target; + deps_by_target.reserve(dependency_keys.size()); + for (std::size_t i = 0; i < dependency_keys.size(); ++i) { + deps_by_target.emplace(dependency_keys[i].target, + *dependency_values[i]); + } + ReportStagingConflict( + dup->get(), stage, to_stage, deps_by_target, logger); + return; + } + stage = ExpressionPtr{Expression::map_t{stage, to_stage}}; } - stage = ExpressionPtr{Expression::map_t{stage, to_stage}}; - } - // Result is the associated tree, located at name - auto conflict = BuildMaps::Target::Utils::tree_conflict(stage); - if (conflict) { - (*logger)(fmt::format("TREE conflict on subtree {}", *conflict), true); - return; - } - std::unordered_map<std::string, ArtifactDescription> tree_content; - tree_content.reserve(stage->Map().size()); - for (auto const& [input_path, artifact] : stage->Map()) { - auto norm_path = ToNormalPath(std::filesystem::path{input_path}); - tree_content.emplace(std::move(norm_path), artifact->Artifact()); + // Result is the associated tree, located at name + auto conflict = BuildMaps::Target::Utils::tree_conflict(stage); + if (conflict) { + (*logger)(fmt::format("TREE conflict on subtree {}", *conflict), + true); + return; + } + std::unordered_map<std::string, ArtifactDescription> tree_content; + tree_content.reserve(stage->Map().size()); + for (auto const& [input_path, artifact] : stage->Map()) { + auto norm_path = ToNormalPath(std::filesystem::path{input_path}); + tree_content.emplace(std::move(norm_path), artifact->Artifact()); + } + auto tree = std::make_shared<Tree>(std::move(tree_content)); + auto tree_id = tree->Id(); + trees.emplace_back(std::move(tree)); + result_stage.emplace(name, ArtifactDescription::CreateTree(tree_id)); } - auto tree = std::make_shared<Tree>(std::move(tree_content)); - auto tree_id = tree->Id(); - std::vector<Tree::Ptr> trees{}; - trees.emplace_back(std::move(tree)); - auto result_stage = Expression::map_t::underlying_map_t{}; - result_stage.emplace(name, ArtifactDescription::CreateTree(tree_id)); auto result = ExpressionPtr{Expression::map_t{result_stage}}; auto analysis_result = std::make_shared<AnalysedTarget const>( @@ -545,7 +575,7 @@ void TreeRuleWithDeps( std::vector<ActionDescription::Ptr>{}, std::vector<std::string>{}, std::move(trees), - std::vector<TreeOverlay::Ptr>{}, + std::move(tree_overlays), std::move(vars_set), std::move(tainted), std::move(implied_export), @@ -555,16 +585,25 @@ void TreeRuleWithDeps( (*setter)(std::move(analysis_result)); } -void TreeRule( +void CommonTreeRule( const gsl::not_null<AnalyseContext*>& context, const nlohmann::json& desc_json, const BuildMaps::Target::ConfiguredTarget& key, const BuildMaps::Target::TargetMap::SubCallerPtr& subcaller, const BuildMaps::Target::TargetMap::SetterPtr& setter, const BuildMaps::Target::TargetMap::LoggerPtr& logger, - const gsl::not_null<BuildMaps::Target::ResultTargetMap*>& result_map) { + const gsl::not_null<BuildMaps::Target::ResultTargetMap*>& result_map, + std::optional<bool> disjoint_overlay) { + std::string rule_name; + if (disjoint_overlay) { + rule_name = + *disjoint_overlay ? "disjoint_tree_overlay" : "tree overlay"; + } + else { + rule_name = "tree"; + } auto desc = BuildMaps::Base::FieldReader::CreatePtr( - desc_json, key.target, "tree target", logger); + desc_json, key.target, fmt::format("{} target", rule_name), logger); desc->ExpectFields(kTreeRuleFields); auto param_vars = desc->ReadStringList("arguments_config"); if (not param_vars) { @@ -637,7 +676,8 @@ void TreeRule( setter, logger, key, - result_map](auto const& values) { + result_map, + disjoint_overlay](auto const& values) { TreeRuleWithDeps(values, dependency_keys, name, @@ -645,11 +685,54 @@ void TreeRule( desc, setter, logger, - result_map); + result_map, + disjoint_overlay); }, logger); } +void TreeRule( + const gsl::not_null<AnalyseContext*>& context, + const nlohmann::json& desc_json, + const BuildMaps::Target::ConfiguredTarget& key, + const BuildMaps::Target::TargetMap::SubCallerPtr& subcaller, + const BuildMaps::Target::TargetMap::SetterPtr& setter, + const BuildMaps::Target::TargetMap::LoggerPtr& logger, + const gsl::not_null<BuildMaps::Target::ResultTargetMap*>& result_map) { + CommonTreeRule(context, + desc_json, + key, + subcaller, + setter, + logger, + result_map, + std::nullopt); +} + +void DisjointTreeOverlayRule( + const gsl::not_null<AnalyseContext*>& context, + const nlohmann::json& desc_json, + const BuildMaps::Target::ConfiguredTarget& key, + const BuildMaps::Target::TargetMap::SubCallerPtr& subcaller, + const BuildMaps::Target::TargetMap::SetterPtr& setter, + const BuildMaps::Target::TargetMap::LoggerPtr& logger, + const gsl::not_null<BuildMaps::Target::ResultTargetMap*>& result_map) { + CommonTreeRule( + context, desc_json, key, subcaller, setter, logger, result_map, true); +} + +void TreeOverlayRule( + const gsl::not_null<AnalyseContext*>& context, + const nlohmann::json& desc_json, + const BuildMaps::Target::ConfiguredTarget& key, + const BuildMaps::Target::TargetMap::SubCallerPtr& subcaller, + const BuildMaps::Target::TargetMap::SetterPtr& setter, + const BuildMaps::Target::TargetMap::LoggerPtr& logger, + const gsl::not_null<BuildMaps::Target::ResultTargetMap*>& result_map) { + CommonTreeRule( + context, desc_json, key, subcaller, setter, logger, result_map, false); +} + void InstallRuleWithDeps( const std::vector<BuildMaps::Target::ConfiguredTarget>& dependency_keys, const std::vector<AnalysedTargetPtr const*>& dependency_values, @@ -1674,6 +1757,8 @@ auto const kBuiltIns = std::unordered_map< {"export", ExportRule}, {"file_gen", FileGenRule}, {"tree", TreeRule}, + {"tree_overlay", TreeOverlayRule}, + {"disjoint_tree_overlay", DisjointTreeOverlayRule}, {"symlink", SymlinkRule}, {"generic", GenericRule}, {"install", InstallRule}, diff --git a/test/buildtool/build_engine/target_map/data_targets/tree_overlay/TARGETS b/test/buildtool/build_engine/target_map/data_targets/tree_overlay/TARGETS index 088d47de..5c5dd8bf 100644 --- a/test/buildtool/build_engine/target_map/data_targets/tree_overlay/TARGETS +++ b/test/buildtool/build_engine/target_map/data_targets/tree_overlay/TARGETS @@ -3,4 +3,8 @@ , "disjoint empty": {"type": ["tree_overlay", "disjoint overlay"]} , "disjoint one stage": {"type": ["tree_overlay", "disjoint overlay"], "deps": ["x"]} +, "built-in, one stage": + {"type": "tree_overlay", "name": "the_tree", "deps": ["x"]} +, "built-in, disjoint, one stage": + {"type": "disjoint_tree_overlay", "name": "the_tree", "deps": ["x"]} } diff --git a/test/buildtool/build_engine/target_map/target_map.test.cpp b/test/buildtool/build_engine/target_map/target_map.test.cpp index 0b51355b..308e8325 100644 --- a/test/buildtool/build_engine/target_map/target_map.test.cpp +++ b/test/buildtool/build_engine/target_map/target_map.test.cpp @@ -1201,6 +1201,69 @@ TEST_CASE("built-in rules", "[target_map]") { baz_result->Artifacts()->ToJson()["foo.txt."]["data"]["id"] == storage_config.Get().hash_function.HashBlobData("baz").HexString()); } + + SECTION("tree_overlay") { + error = false; + error_msg = "NONE"; + { + TaskSystem ts; + target_map.ConsumeAfterKeysReady( + &ts, + {BuildMaps::Target::ConfiguredTarget{ + .target = + BuildMaps::Base::EntityName{ + "", "tree_overlay", "built-in, one stage"}, + .config = empty_config}}, + [&result](auto values) { result = *values[0]; }, + [&error, &error_msg](std::string const& msg, bool /*unused*/) { + error = true; + error_msg = msg; + }); + } + CHECK(not error); + CHECK(error_msg == "NONE"); + CHECK(result->Artifacts()->ToJson()["the_tree"]["type"] == + "TREE_OVERLAY"); + CHECK(result->Trees().size() == 1); + CHECK(result->Trees()[0]->ToJson()["x"]["type"] == "LOCAL"); + CHECK(result->TreeOverlays().size() == 1); + CHECK(result->TreeOverlays()[0]->ToJson()["trees"].size() == 1); + CHECK(result->TreeOverlays()[0]->ToJson()["trees"][0]["type"] == + "TREE"); + CHECK(result->TreeOverlays()[0]->ToJson()["disjoint"] == false); + } + + SECTION("disjoint_tree_overlay") { + error = false; + error_msg = "NONE"; + { + TaskSystem ts; + target_map.ConsumeAfterKeysReady( + &ts, + {BuildMaps::Target::ConfiguredTarget{ + .target = BuildMaps::Base::EntityName{"", + "tree_overlay", + "built-in, disjoint, " + "one stage"}, + .config = empty_config}}, + [&result](auto values) { result = *values[0]; }, + [&error, &error_msg](std::string const& msg, bool /*unused*/) { + error = true; + error_msg = msg; + }); + } + CHECK(not error); + CHECK(error_msg == "NONE"); + CHECK(result->Artifacts()->ToJson()["the_tree"]["type"] == + "TREE_OVERLAY"); + CHECK(result->Trees().size() == 1); + CHECK(result->Trees()[0]->ToJson()["x"]["type"] == "LOCAL"); + CHECK(result->TreeOverlays().size() == 1); + CHECK(result->TreeOverlays()[0]->ToJson()["trees"].size() == 1); + CHECK(result->TreeOverlays()[0]->ToJson()["trees"][0]["type"] == + "TREE"); + CHECK(result->TreeOverlays()[0]->ToJson()["disjoint"] == true); + } } TEST_CASE("target reference", "[target_map]") { |