diff --git a/buildcc/lib/target/cmake/common_target_src.cmake b/buildcc/lib/target/cmake/common_target_src.cmake index 5a10bfe8..92822197 100644 --- a/buildcc/lib/target/cmake/common_target_src.cmake +++ b/buildcc/lib/target/cmake/common_target_src.cmake @@ -27,6 +27,9 @@ set(COMMON_TARGET_SRCS include/target/api/target_getter.h # Generator + include/target/custom_generator/custom_generator_context.h + include/target/custom_generator/custom_blob_handler.h + src/custom_generator/custom_generator.cpp include/target/custom_generator.h src/generator/file_generator.cpp diff --git a/buildcc/lib/target/include/target/custom_generator.h b/buildcc/lib/target/include/target/custom_generator.h index e0263419..0712cde5 100644 --- a/buildcc/lib/target/include/target/custom_generator.h +++ b/buildcc/lib/target/include/target/custom_generator.h @@ -30,69 +30,28 @@ #include "schema/custom_generator_serialization.h" #include "schema/path.h" +#include "custom_generator/custom_blob_handler.h" +#include "custom_generator/custom_generator_context.h" + #include "target/common/target_env.h" namespace buildcc { -// TODO, Shift to a different file -// TODO, Check if we need the "id" here as well -class CustomGeneratorContext { -public: - CustomGeneratorContext(const env::Command &c, const fs_unordered_set &i, - const fs_unordered_set &o, - const std::vector &ub) - : command(c), inputs(i), outputs(o), userblob(ub) {} - - const env::Command &command; - const fs_unordered_set &inputs; - const fs_unordered_set &outputs; - const std::vector &userblob; -}; - -// clang-format off -using GenerateCb = std::function; - -using DependencyCb = std::function &&)>; -// clang-format on - -class CustomBlobHandler { -public: - CustomBlobHandler() = default; - virtual ~CustomBlobHandler() = default; - - bool CheckChanged(const std::vector &previous, - const std::vector ¤t) const { - env::assert_fatal( - Verify(previous), - "Stored blob is corrupted or User verification is incorrect"); - env::assert_fatal( - Verify(current), - "Current blob is corrupted or User verification is incorrect"); - return !IsEqual(previous, current); +struct UserCustomGeneratorSchema : public internal::CustomGeneratorSchema { + struct UserIdInfo : internal::CustomGeneratorSchema::IdInfo { + fs_unordered_set inputs; // TODO, Remove + GenerateCb generate_cb; + std::shared_ptr blob_handler{nullptr}; + + void ConvertToInternal() { + internal_inputs = internal::path_schema_convert( + inputs, internal::Path::CreateExistingPath); + userblob = blob_handler != nullptr ? blob_handler->GetSerializedData() + : std::vector(); + } }; - std::vector GetSerializedData() const { - auto serialized_data = Serialize(); - env::assert_fatal( - Verify(serialized_data), - "Serialized data is corrupted or Serialize function is incorrect"); - return serialized_data; - } - -private: - virtual bool Verify(const std::vector &serialized_data) const = 0; - virtual bool IsEqual(const std::vector &previous, - const std::vector ¤t) const = 0; - virtual std::vector Serialize() const = 0; -}; - -struct UserIdInfo : internal::CustomGeneratorSchema::IdInfo { - fs_unordered_set inputs; - GenerateCb generate_cb; - std::shared_ptr blob_handler{nullptr}; -}; - -struct UserCustomGeneratorSchema : public internal::CustomGeneratorSchema { + using UserIdPair = std::pair; std::unordered_map ids; void ConvertToInternal() { @@ -110,7 +69,8 @@ class CustomGenerator : public internal::BuilderInterface { CustomGenerator(const std::string &name, const TargetEnv &env) : name_(name), env_(env.GetTargetRootDir(), env.GetTargetBuildDir() / name), - serialization_(env_.GetTargetBuildDir() / fmt::format("{}.bin", name)) { + serialization_(env_.GetTargetBuildDir() / fmt::format("{}.json", name)), + comparator_(serialization_.GetLoad(), user_) { Initialize(); } virtual ~CustomGenerator() = default; @@ -137,33 +97,12 @@ class CustomGenerator : public internal::BuilderInterface { * @param generate_cb User-defined generate callback to build outputs from the * provided inputs */ - void AddIdInfo(const std::string &id, - const std::unordered_set &inputs, - const std::unordered_set &outputs, - const GenerateCb &generate_cb, - std::shared_ptr blob_handler = nullptr); - - // TODO, Doc - void AddGroupInfo(const std::string &group_id, - std::initializer_list ids, - const DependencyCb &dependency_cb = DependencyCb()); - - // Callbacks - /** - * @brief Setup dependencies between Tasks using their `id` - * For example: `task_map["id1"].precede(task_map["id2"])` - * - * IMPORTANT: Successor tasks will not automatically run if dependent task is - * run. - * The Dependency callback only sets precedence (order in which your tasks - * should run) - * Default behaviour when dependency callback is not supplied: All task `id`s - * run in parallel. - * - * @param dependency_cb Unordered map of `id` and `task` - * The map can be safely mutated. - */ - void AddDependencyCb(const DependencyCb &dependency_cb); + void + AddIdInfo(const std::string &id, + const std::unordered_set &inputs, + const std::unordered_set &outputs, + const GenerateCb &generate_cb, + const std::shared_ptr &blob_handler = nullptr); void Build() override; @@ -176,19 +115,100 @@ class CustomGenerator : public internal::BuilderInterface { const fs::path &GetBuildDir() const { return env_.GetTargetBuildDir(); } const std::string &Get(const std::string &file_identifier) const; +private: + struct Comparator { + Comparator(const internal::CustomGeneratorSchema &loaded, + const UserCustomGeneratorSchema &us) + : loaded_schema_(loaded), current_schema_(us) {} + + enum class State { + kRemoved, + kAdded, + kCheckLater, + }; + + void AddAllIds() { + const auto &curr_ids = current_schema_.ids; + for (const auto &[id, _] : curr_ids) { + id_state_info_.at(State::kAdded).insert(id); + } + } + + void CompareIds() { + const auto &prev_ids = loaded_schema_.internal_ids; + const auto &curr_ids = current_schema_.ids; + + for (const auto &[prev_id, _] : prev_ids) { + if (curr_ids.find(prev_id) == curr_ids.end()) { + // Id Removed condition, previous id is not present in the current run + id_state_info_.at(State::kRemoved).insert(prev_id); + } + } + + for (const auto &[curr_id, _] : curr_ids) { + if (prev_ids.find(curr_id) == prev_ids.end()) { + // Id Added condition + id_state_info_.at(State::kAdded).insert(curr_id); + } else { + // Id Check Later condition + id_state_info_.at(State::kCheckLater).insert(curr_id); + } + } + } + + bool IsChanged(const std::string &id) const { + const auto &previous_id_info = loaded_schema_.internal_ids.at(id); + const auto ¤t_id_info = current_schema_.ids.at(id); + + bool changed = internal::CheckPaths(previous_id_info.internal_inputs, + current_id_info.internal_inputs) != + internal::PathState::kNoChange; + changed = changed || internal::CheckChanged(previous_id_info.outputs, + current_id_info.outputs); + if (!changed && current_id_info.blob_handler != nullptr) { + // We only check blob handler if not changed by inputs/outputs + // Checking blob_handler could be expensive so this optimization is made + // to run only when changed == false + changed = current_id_info.blob_handler->CheckChanged( + previous_id_info.userblob, current_id_info.userblob); + } + return changed; + } + + const std::unordered_set &GetRemovedIds() const { + return id_state_info_.at(State::kRemoved); + } + + const std::unordered_set &GetAddedIds() const { + return id_state_info_.at(State::kAdded); + } + + const std::unordered_set &GetCheckLaterIds() const { + return id_state_info_.at(State::kCheckLater); + } + + bool IsIdAdded(const std::string &id) const { + return id_state_info_.at(State::kAdded).count(id) == 1; + } + + private: + const internal::CustomGeneratorSchema &loaded_schema_; + const UserCustomGeneratorSchema ¤t_schema_; + std::unordered_map> id_state_info_{ + {State::kRemoved, std::unordered_set()}, + {State::kAdded, std::unordered_set()}, + {State::kCheckLater, std::unordered_set()}, + }; + }; + private: void Initialize(); - void TaskRunner(bool run, const std::string &id); - tf::Task CreateTaskRunner(tf::Subflow &subflow, bool build, - const std::string &id); + tf::Task CreateTaskRunner(tf::Subflow &subflow, const std::string &id); + void TaskRunner(const std::string &id); void GenerateTask(); - void BuildGenerate(std::unordered_set &gen_selected_ids, - std::unordered_set &dummy_gen_selected_ids); - - void InvokeDependencyCb(std::unordered_map - &®istered_tasks) const noexcept; + void BuildGenerate(); // Recheck states void IdRemoved(); @@ -199,29 +219,6 @@ class CustomGenerator : public internal::BuilderInterface { const env::Command &ConstCommand() const { return command_; } env::Command &RefCommand() { return command_; } -private: - struct GroupMetadata { - std::vector ids; - DependencyCb dependency_cb; - - void InvokeDependencyCb(const std::string &group_id, - std::unordered_map - &®istered_tasks) const noexcept { - if (!dependency_cb) { - return; - } - try { - dependency_cb(std::move(registered_tasks)); - } catch (...) { - env::log_critical( - __FUNCTION__, - fmt::format("Dependency callback failed for group id {}", - group_id)); - env::set_task_state(env::TaskState::FAILURE); - } - } - }; - private: std::string name_; TargetEnv env_; @@ -229,17 +226,16 @@ class CustomGenerator : public internal::BuilderInterface { // Serialization UserCustomGeneratorSchema user_; - std::unordered_map grouped_ids_; - std::unordered_set ungrouped_ids_; + + // Comparator + Comparator comparator_; std::mutex success_schema_mutex_; - std::unordered_map success_schema_; + std::unordered_map + success_schema_; // Internal env::Command command_; - - // Callbacks - DependencyCb dependency_cb_; }; } // namespace buildcc diff --git a/buildcc/lib/target/include/target/custom_generator/custom_blob_handler.h b/buildcc/lib/target/include/target/custom_generator/custom_blob_handler.h new file mode 100644 index 00000000..1c268301 --- /dev/null +++ b/buildcc/lib/target/include/target/custom_generator/custom_blob_handler.h @@ -0,0 +1,59 @@ +/* + * Copyright 2021-2022 Niket Naidu. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TARGET_CUSTOM_GENERATOR_CUSTOM_BLOB_HANDLER_H_ +#define TARGET_CUSTOM_GENERATOR_CUSTOM_BLOB_HANDLER_H_ + +#include + +#include "env/assert_fatal.h" + +namespace buildcc { + +class CustomBlobHandler { +public: + CustomBlobHandler() = default; + virtual ~CustomBlobHandler() = default; + + bool CheckChanged(const std::vector &previous, + const std::vector ¤t) const { + env::assert_fatal( + Verify(previous), + "Stored blob is corrupted or User verification is incorrect"); + env::assert_fatal( + Verify(current), + "Current blob is corrupted or User verification is incorrect"); + return !IsEqual(previous, current); + }; + + std::vector GetSerializedData() const { + auto serialized_data = Serialize(); + env::assert_fatal( + Verify(serialized_data), + "Serialized data is corrupted or Serialize function is incorrect"); + return serialized_data; + } + +private: + virtual bool Verify(const std::vector &serialized_data) const = 0; + virtual bool IsEqual(const std::vector &previous, + const std::vector ¤t) const = 0; + virtual std::vector Serialize() const = 0; +}; + +} // namespace buildcc + +#endif diff --git a/buildcc/lib/target/include/target/custom_generator/custom_generator_context.h b/buildcc/lib/target/include/target/custom_generator/custom_generator_context.h new file mode 100644 index 00000000..207015d9 --- /dev/null +++ b/buildcc/lib/target/include/target/custom_generator/custom_generator_context.h @@ -0,0 +1,45 @@ +/* + * Copyright 2021-2022 Niket Naidu. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TARGET_CUSTOM_GENERATOR_CUSTOM_GENERATOR_CONTEXT_H_ +#define TARGET_CUSTOM_GENERATOR_CUSTOM_GENERATOR_CONTEXT_H_ + +#include "schema/path.h" + +#include "env/command.h" + +namespace buildcc { + +class CustomGeneratorContext { +public: + CustomGeneratorContext(const env::Command &c, const fs_unordered_set &i, + const fs_unordered_set &o, + const std::vector &ub) + : command(c), inputs(i), outputs(o), userblob(ub) {} + + const env::Command &command; + const fs_unordered_set &inputs; + const fs_unordered_set &outputs; + const std::vector &userblob; +}; + +// clang-format off +using GenerateCb = std::function; +// clang-format on + +} // namespace buildcc + +#endif diff --git a/buildcc/lib/target/include/target/file_generator.h b/buildcc/lib/target/include/target/file_generator.h index e11b2b21..9897b5fc 100644 --- a/buildcc/lib/target/include/target/file_generator.h +++ b/buildcc/lib/target/include/target/file_generator.h @@ -68,8 +68,6 @@ class FileGenerator : public CustomGenerator { // Restrict access to certain custom generator APIs private: - using CustomGenerator::AddDependencyCb; - using CustomGenerator::AddGroupInfo; using CustomGenerator::AddIdInfo; using CustomGenerator::Build; diff --git a/buildcc/lib/target/include/target/template_generator.h b/buildcc/lib/target/include/target/template_generator.h index 79706a4c..73c892e7 100644 --- a/buildcc/lib/target/include/target/template_generator.h +++ b/buildcc/lib/target/include/target/template_generator.h @@ -42,8 +42,6 @@ class TemplateGenerator : public CustomGenerator { // Restrict access to certain custom generator APIs private: - using CustomGenerator::AddDependencyCb; - using CustomGenerator::AddGroupInfo; using CustomGenerator::AddIdInfo; using CustomGenerator::Build; diff --git a/buildcc/lib/target/src/custom_generator/custom_generator.cpp b/buildcc/lib/target/src/custom_generator/custom_generator.cpp index c7cb8725..b60283e8 100644 --- a/buildcc/lib/target/src/custom_generator/custom_generator.cpp +++ b/buildcc/lib/target/src/custom_generator/custom_generator.cpp @@ -55,12 +55,12 @@ void CustomGenerator::AddIdInfo( const std::string &id, const std::unordered_set &inputs, const std::unordered_set &outputs, const GenerateCb &generate_cb, - std::shared_ptr blob_handler) { + const std::shared_ptr &blob_handler) { env::assert_fatal(user_.ids.find(id) == user_.ids.end(), fmt::format("Duplicate id {} detected", id)); ASSERT_FATAL(generate_cb, "Invalid callback provided"); - UserIdInfo schema; + UserCustomGeneratorSchema::UserIdInfo schema; for (const auto &i : inputs) { fs::path input = string_as_path(command_.Construct(i)); schema.inputs.emplace(std::move(input)); @@ -70,34 +70,8 @@ void CustomGenerator::AddIdInfo( schema.outputs.emplace(std::move(output)); } schema.generate_cb = generate_cb; - schema.blob_handler = std::move(blob_handler); + schema.blob_handler = blob_handler; user_.ids.try_emplace(id, std::move(schema)); - ungrouped_ids_.emplace(id); -} - -void CustomGenerator::AddGroupInfo(const std::string &group_id, - std::initializer_list ids, - const DependencyCb &dependency_cb) { - // Verify that the ids exist - // Remove those ids from ungrouped_ids - for (const auto &id : ids) { - env::assert_fatal(user_.ids.find(id) != user_.ids.end(), - fmt::format("Id '{}' is not found", id)); - ungrouped_ids_.erase(id); - } - - env::assert_fatal(grouped_ids_.find(group_id) == grouped_ids_.end(), - fmt::format("Group Id '{}' duplicate found", group_id)); - - // Group map is used to group similar ids in a single subflow - GroupMetadata group_metadata; - group_metadata.ids = ids; - group_metadata.dependency_cb = dependency_cb; - grouped_ids_.try_emplace(group_id, std::move(group_metadata)); -} - -void CustomGenerator::AddDependencyCb(const DependencyCb &dependency_cb) { - dependency_cb_ = dependency_cb; } void CustomGenerator::Build() { @@ -127,48 +101,25 @@ void CustomGenerator::Initialize() { tf_.name(name_); } -void CustomGenerator::BuildGenerate( - std::unordered_set &gen_selected_ids, - std::unordered_set &dummy_gen_selected_ids) { +void CustomGenerator::BuildGenerate() { if (!serialization_.IsLoaded()) { - std::for_each(user_.ids.begin(), user_.ids.end(), [&](const auto &iter) { - gen_selected_ids.insert(iter.first); - }); + comparator_.AddAllIds(); dirty_ = true; } else { - // DONE, Conditionally select internal_ids depending on what has - // changed - const auto &prev_ids = serialization_.GetLoad().internal_ids; - const auto &curr_ids = user_.ids; - - // DONE, MAP REMOVED condition Check if prev_ids exists in - // curr_ids If prev_ids does not exist in - // curr_ids, has been removed from existing build We need this - // condition to only set the dirty_ flag - for (const auto &[id, _] : prev_ids) { - if (curr_ids.find(id) == curr_ids.end()) { - // MAP REMOVED condition - IdRemoved(); - dirty_ = true; - break; - } + // For IDS + comparator_.CompareIds(); + + const bool is_removed = !comparator_.GetRemovedIds().empty(); + const bool is_added = !comparator_.GetAddedIds().empty(); + dirty_ = is_removed || is_added; + + if (is_removed) { + IdRemoved(); } - // DONE, MAP ADDED condition Check if curr_ids exists in - // prev_ids If curr_ids does not exist in - // prev_ids, has been added to existing build - for (const auto &[id, _] : curr_ids) { - if (prev_ids.find(id) == prev_ids.end()) { - // MAP ADDED condition - IdAdded(); - gen_selected_ids.insert(id); - dirty_ = true; - } else { - // MAP UPDATED condition (*checked later) - // This is because tasks can have dependencies amongst each other we can - // compute task level rebuilds later - dummy_gen_selected_ids.insert(id); - } + for (const auto &id : comparator_.GetAddedIds()) { + (void)id; + IdAdded(); } } } @@ -180,56 +131,24 @@ void CustomGenerator::GenerateTask() { } try { - std::unordered_map registered_tasks; - // Selected ids for build - std::unordered_set selected_ids; - std::unordered_set dummy_selected_ids; - BuildGenerate(selected_ids, dummy_selected_ids); - - // Grouped tasks - for (const auto &[first, second] : grouped_ids_) { - const auto &group_id = first; - const auto &group_metadata = second; - auto group_task = subflow.emplace([&](tf::Subflow &s) { - std::unordered_map reg_tasks; - - if (env::get_task_state() != env::TaskState::SUCCESS) { - return; - } - - for (const auto &id : group_metadata.ids) { - bool build = selected_ids.count(id) == 1; - auto task = CreateTaskRunner(s, build, id); - task.name(id); - reg_tasks.try_emplace(id, task); - } - - // Dependency callback - group_metadata.InvokeDependencyCb(group_id, std::move(reg_tasks)); - - // NOTE, Do not call detach otherwise this will fail - s.join(); - }); - group_task.name(group_id); - registered_tasks.try_emplace(group_id, group_task); - } + BuildGenerate(); - // Ungrouped tasks - for (const auto &id : ungrouped_ids_) { - bool build = selected_ids.count(id) == 1; - auto task = CreateTaskRunner(subflow, build, id); + // Create runner for each added/updated id + for (const auto &id : comparator_.GetAddedIds()) { + auto task = CreateTaskRunner(subflow, id); task.name(id); - registered_tasks.try_emplace(id, task); } - // Dependencies between tasks - InvokeDependencyCb(std::move(registered_tasks)); + for (const auto &id : comparator_.GetCheckLaterIds()) { + auto task = CreateTaskRunner(subflow, id); + task.name(id); + } // NOTE, Do not call detach otherwise this will fail subflow.join(); - // Store dummy_selected and successfully run schema + // Store if (dirty_) { UserCustomGeneratorSchema user_final_schema; user_final_schema.ids.insert(success_schema_.begin(), @@ -240,7 +159,6 @@ void CustomGenerator::GenerateTask() { env::assert_fatal(serialization_.StoreToFile(), fmt::format("Store failed for {}", name_)); } - } catch (...) { env::set_task_state(env::TaskState::FAILURE); } @@ -249,67 +167,34 @@ void CustomGenerator::GenerateTask() { generate_task.name(kGenerateTaskName); } -void CustomGenerator::InvokeDependencyCb( - std::unordered_map &®istered_tasks) - const noexcept { - if (dependency_cb_) { - try { - dependency_cb_(std::move(registered_tasks)); - } catch (...) { - env::log_critical(__FUNCTION__, "Dependency callback failed"); - env::set_task_state(env::TaskState::FAILURE); - } - } -} - -tf::Task CustomGenerator::CreateTaskRunner(tf::Subflow &subflow, bool build, +tf::Task CustomGenerator::CreateTaskRunner(tf::Subflow &subflow, const std::string &id) { - return subflow.emplace([&, build, id]() { + return subflow.emplace([&, id]() { if (env::get_task_state() != env::TaskState::SUCCESS) { return; } try { - TaskRunner(build, id); + TaskRunner(id); } catch (...) { env::set_task_state(env::TaskState::FAILURE); } }); } -void CustomGenerator::TaskRunner(bool run, const std::string &id) { - // Convert - { - auto &curr_id_info = user_.ids.at(id); - curr_id_info.internal_inputs = internal::path_schema_convert( - curr_id_info.inputs, internal::Path::CreateExistingPath); - curr_id_info.userblob = curr_id_info.blob_handler != nullptr - ? curr_id_info.blob_handler->GetSerializedData() - : std::vector(); - } +void CustomGenerator::TaskRunner(const std::string &id) { + // Convert to internal + user_.ids.at(id).ConvertToInternal(); - // Run + // Compute runnable + bool run = comparator_.IsIdAdded(id) ? true : comparator_.IsChanged(id); + + // Invoke generator callback const auto ¤t_id_info = user_.ids.at(id); - bool rerun = false; if (run) { - rerun = true; - } else { - const auto &previous_info = serialization_.GetLoad().internal_ids.at(id); - rerun = - internal::CheckPaths(previous_info.internal_inputs, - current_id_info.internal_inputs) != - internal::PathState::kNoChange || - internal::CheckChanged(previous_info.outputs, current_id_info.outputs); - if (!rerun && current_id_info.blob_handler != nullptr) { - rerun = current_id_info.blob_handler->CheckChanged( - previous_info.userblob, current_id_info.userblob); - } - } - - if (rerun) { dirty_ = true; - buildcc::CustomGeneratorContext ctx(command_, current_id_info.inputs, - current_id_info.outputs, - current_id_info.userblob); + CustomGeneratorContext ctx(command_, current_id_info.inputs, + current_id_info.outputs, + current_id_info.userblob); bool success = current_id_info.generate_cb(ctx); env::assert_fatal(success, fmt::format("Generate Cb failed for id {}", id)); } diff --git a/buildcc/lib/target/test/target/test_custom_generator.cpp b/buildcc/lib/target/test/target/test_custom_generator.cpp index a6c6597b..839c91f7 100644 --- a/buildcc/lib/target/test/target/test_custom_generator.cpp +++ b/buildcc/lib/target/test/target/test_custom_generator.cpp @@ -24,7 +24,8 @@ TEST_GROUP(CustomGeneratorTestGroup) }; // clang-format on -fs::path BUILD_DIR = fs::current_path() / "intermediate" / "custom_generator"; +const fs::path BUILD_DIR = + fs::current_path() / "intermediate" / "custom_generator"; static bool BasicGenerateCb(const buildcc::CustomGeneratorContext &ctx) { (void)ctx; @@ -33,6 +34,7 @@ static bool BasicGenerateCb(const buildcc::CustomGeneratorContext &ctx) { TEST(CustomGeneratorTestGroup, Basic) { buildcc::CustomGenerator cgen("basic", ""); + STRCMP_EQUAL(cgen.GetName().c_str(), "basic"); cgen.AddIdInfo("id1", {"{current_root_dir}/dummy_main.c"}, {"{current_build_dir}/dummy_main.o"}, BasicGenerateCb); cgen.AddIdInfo("id2", {"{current_root_dir}/dummy_main.cpp"}, {}, @@ -61,265 +63,109 @@ TEST(CustomGeneratorTestGroup, Basic) { } } -TEST(CustomGeneratorTestGroup, Basic_Failure) { - buildcc::CustomGenerator cgen("basic_failure", ""); - cgen.AddIdInfo("id1", {"{current_root_dir}/dummy_main.c"}, {}, - BasicGenerateCb); - cgen.AddIdInfo("id2", {"{current_root_dir}/dummy_main.cpp"}, {}, - BasicGenerateCb); - cgen.Build(); - - mock().expectOneCall("BasicGenerateCb").andReturnValue(true); - mock().expectOneCall("BasicGenerateCb").andReturnValue(false); - buildcc::m::CustomGeneratorRunner(cgen); - - CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::FAILURE); +TEST(CustomGeneratorTestGroup, BasicRebuild) { + constexpr const char *const kName = "basic_rebuild"; - // Load - buildcc::internal::CustomGeneratorSerialization serialization( - cgen.GetBinaryPath()); - CHECK_TRUE(serialization.LoadFromFile()); - - const auto &internal_map = serialization.GetLoad().internal_ids; - CHECK_EQUAL(internal_map.size(), 1); -} - -TEST(CustomGeneratorTestGroup, Basic_Group) { - buildcc::CustomGenerator cgen("basic_group", ""); - cgen.AddIdInfo("id1", {"{current_root_dir}/dummy_main.c"}, - {"{current_build_dir}/dummy_main.o"}, BasicGenerateCb); - cgen.AddIdInfo("id2", {"{current_root_dir}/dummy_main.cpp"}, {}, - BasicGenerateCb); - cgen.AddGroupInfo("grouped_id1_and_id2", {"id1", "id2"}); - cgen.Build(); - - mock().expectOneCall("BasicGenerateCb").andReturnValue(true); - mock().expectOneCall("BasicGenerateCb").andReturnValue(true); - buildcc::m::CustomGeneratorRunner(cgen); - - // Serialization check { - buildcc::internal::CustomGeneratorSerialization serialization( - cgen.GetBinaryPath()); - CHECK_TRUE(serialization.LoadFromFile()); - - const auto &internal_map = serialization.GetLoad().internal_ids; - CHECK_EQUAL(internal_map.size(), 2); - const auto &id1_info = internal_map.at("id1"); - CHECK_EQUAL(id1_info.internal_inputs.size(), 1); - CHECK_EQUAL(id1_info.outputs.size(), 1); + buildcc::CustomGenerator cgen(kName, ""); + cgen.AddIdInfo("id1", {"{current_root_dir}/dummy_main.c"}, + {"{current_build_dir}/dummy_main.o"}, BasicGenerateCb); + cgen.AddIdInfo("id2", {"{current_root_dir}/dummy_main.cpp"}, {}, + BasicGenerateCb); + cgen.Build(); - const auto &id2_info = internal_map.at("id2"); - CHECK_EQUAL(id2_info.internal_inputs.size(), 1); - CHECK_EQUAL(id2_info.outputs.size(), 0); + mock().expectOneCall("BasicGenerateCb").andReturnValue(true); + mock().expectOneCall("BasicGenerateCb").andReturnValue(true); + buildcc::m::CustomGeneratorRunner(cgen); } -} - -TEST(CustomGeneratorTestGroup, Basic_Group_Dependency) { - buildcc::CustomGenerator cgen("basic_group_dependency", ""); - cgen.AddIdInfo("id1", {"{current_root_dir}/dummy_main.c"}, - {"{current_build_dir}/dummy_main.o"}, BasicGenerateCb); - cgen.AddIdInfo("id2", {"{current_root_dir}/dummy_main.cpp"}, {}, - BasicGenerateCb); - cgen.AddGroupInfo("grouped_id1_and_id2", {"id1", "id2"}, [](auto &&task_map) { - task_map.at("id1").precede(task_map.at("id2")); - }); - cgen.Build(); - - mock().expectOneCall("BasicGenerateCb").andReturnValue(true); - mock().expectOneCall("BasicGenerateCb").andReturnValue(true); - buildcc::m::CustomGeneratorRunner(cgen); - // Serialization check { - buildcc::internal::CustomGeneratorSerialization serialization( - cgen.GetBinaryPath()); - CHECK_TRUE(serialization.LoadFromFile()); - - const auto &internal_map = serialization.GetLoad().internal_ids; - CHECK_EQUAL(internal_map.size(), 2); - const auto &id1_info = internal_map.at("id1"); - CHECK_EQUAL(id1_info.internal_inputs.size(), 1); - CHECK_EQUAL(id1_info.outputs.size(), 1); + buildcc::CustomGenerator cgen(kName, ""); + cgen.AddIdInfo("id1", {"{current_root_dir}/dummy_main.c"}, + {"{current_build_dir}/dummy_main.o"}, BasicGenerateCb); + cgen.AddIdInfo("id2", {"{current_root_dir}/dummy_main.cpp"}, {}, + BasicGenerateCb); + cgen.Build(); - const auto &id2_info = internal_map.at("id2"); - CHECK_EQUAL(id2_info.internal_inputs.size(), 1); - CHECK_EQUAL(id2_info.outputs.size(), 0); + buildcc::m::CustomGeneratorRunner(cgen); } } -TEST(CustomGeneratorTestGroup, Basic_Group_DependencyFailure) { - buildcc::CustomGenerator cgen("basic_group_dependency_failure", ""); - cgen.AddIdInfo("id1", {"{current_root_dir}/dummy_main.c"}, - {"{current_build_dir}/dummy_main.o"}, BasicGenerateCb); - cgen.AddIdInfo("id2", {"{current_root_dir}/dummy_main.cpp"}, {}, - BasicGenerateCb); - cgen.AddGroupInfo("grouped_id1_and_id2", {"id1", "id2"}, [](auto &&task_map) { - task_map.at("id1").precede(task_map.at("id2")); - buildcc::env::assert_fatal("Failure"); - }); - cgen.Build(); - - buildcc::m::CustomGeneratorRunner(cgen); +TEST(CustomGeneratorTestGroup, BasicRebuild_Add_Remove) { + constexpr const char *const kName = "basic_rebuild_add_remove"; - // Serialization check { - buildcc::internal::CustomGeneratorSerialization serialization( - cgen.GetBinaryPath()); - CHECK_TRUE(serialization.LoadFromFile()); + buildcc::CustomGenerator cgen(kName, ""); + cgen.AddIdInfo("id1", {"{current_root_dir}/dummy_main.c"}, + {"{current_build_dir}/dummy_main.o"}, BasicGenerateCb); + cgen.AddIdInfo("id2", {"{current_root_dir}/dummy_main.cpp"}, {}, + BasicGenerateCb); + cgen.Build(); - const auto &internal_map = serialization.GetLoad().internal_ids; - CHECK_EQUAL(internal_map.size(), 0); + mock().expectOneCall("BasicGenerateCb").andReturnValue(true); + mock().expectOneCall("BasicGenerateCb").andReturnValue(true); + buildcc::m::CustomGeneratorRunner(cgen); } - CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::FAILURE); -} - -bool FailureCb(const buildcc::CustomGeneratorContext &ctx) { - (void)ctx; - return false; -} -bool SuccessCb(const buildcc::CustomGeneratorContext &ctx) { - (void)ctx; - return true; -} - -// An ungrouped task a dependency on a grouped task and fail the -// ungrouped task -TEST(CustomGeneratorTestGroup, Basic_Group_DependencyFailure2) { - buildcc::CustomGenerator cgen("basic_group_dependency_failure2", ""); - cgen.AddIdInfo("id1", {"{current_root_dir}/dummy_main.c"}, - {"{current_build_dir}/dummy_main.o"}, FailureCb); - cgen.AddIdInfo("id2", {"{current_root_dir}/dummy_main.cpp"}, {}, SuccessCb); - cgen.AddGroupInfo("grouped_id2", {"id2"}); - cgen.AddDependencyCb([&](auto &&task_map) { - task_map.at("id1").precede(task_map.at("grouped_id2")); - }); - cgen.Build(); - - buildcc::m::CustomGeneratorRunner(cgen); - - // Serialization check + // Remove { - buildcc::internal::CustomGeneratorSerialization serialization( - cgen.GetBinaryPath()); - CHECK_TRUE(serialization.LoadFromFile()); + buildcc::CustomGenerator cgen(kName, ""); + cgen.AddIdInfo("id1", {"{current_root_dir}/dummy_main.c"}, + {"{current_build_dir}/dummy_main.o"}, BasicGenerateCb); + // ID2 Removed + cgen.Build(); - const auto &internal_map = serialization.GetLoad().internal_ids; - CHECK_EQUAL(internal_map.size(), 0); + buildcc::m::CustomGeneratorExpect_IdRemoved(1, &cgen); + buildcc::m::CustomGeneratorRunner(cgen); } - CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::FAILURE); -} - -// Behaviour -// Initial: A | B -> Passes -// Changes: (GID:NEW)[A -> B] -> No rebuild triggered - -// Behaviour -// Initial: A | B -> Fails -// Changes: (GID:NEW)[A -> B] -> rebuild triggered due to previous failure - -// ! IMPORTANT -// * NOTE, It is users responsibility to make sure that when A -> B, A's data -// change should automatically trigger B - -// For example: Say A -> B i.e B depends on A -// In a typical scenario, B would depend on A's output -// To make sure B is triggered when A changes. Make sure you use A's output in -// B's userblob. -// In this way whenever A changes, B's userblob automatically becomes "newer" -// and triggers a rebuild as well - -// Say, A gives out "rebuild = true/false" as its output -// We can use this "rebuild" variable in B's userblob -// When A runs and "rebuild" changes from false to true, During the `TaskRunner` -// we check B's userblob and automatically invoke the `CheckChanged` virtual -// call -// TODO, Create a testcase for the above scenario (Advanced_DependencyRebuild -// scenario) - -// DONE, Make B fail because it properly depends on A -static bool rebuild_value{false}; -static bool ProperDependency1(const buildcc::CustomGeneratorContext &ctx) { - (void)ctx; - mock().actualCall("ProperDependency1"); - rebuild_value = true; - return true; -} - -static bool ProperDependency2(const buildcc::CustomGeneratorContext &ctx) { - (void)ctx; - mock().actualCall("ProperDependency2"); - return rebuild_value; -} - -// ProperDependency2 depends on ProperDependency1 completion -TEST(CustomGeneratorTestGroup, Basic_ProperDependency_GoodCase) { - rebuild_value = false; - - buildcc::CustomGenerator cgen("basic_proper_dependency_good_case", ""); - cgen.AddIdInfo("id1", {"{current_root_dir}/dummy_main.c"}, - {"{current_build_dir}/dummy_main.o"}, ProperDependency1); - cgen.AddIdInfo("id2", {"{current_root_dir}/dummy_main.cpp"}, {}, - ProperDependency2); - cgen.AddDependencyCb( - [](auto &&task_map) { task_map.at("id1").precede(task_map.at("id2")); }); - cgen.Build(); - - mock().expectOneCall("ProperDependency1"); - mock().expectOneCall("ProperDependency2"); - buildcc::m::CustomGeneratorRunner(cgen); - - // Serialization check + // Add { - buildcc::internal::CustomGeneratorSerialization serialization( - cgen.GetBinaryPath()); - CHECK_TRUE(serialization.LoadFromFile()); + buildcc::CustomGenerator cgen(kName, ""); + cgen.AddIdInfo("id1", {"{current_root_dir}/dummy_main.c"}, + {"{current_build_dir}/dummy_main.o"}, BasicGenerateCb); + cgen.AddIdInfo("id2", {"{current_root_dir}/dummy_main.cpp"}, {}, + BasicGenerateCb); + cgen.Build(); - const auto &internal_map = serialization.GetLoad().internal_ids; - CHECK_EQUAL(internal_map.size(), 2); + buildcc::m::CustomGeneratorExpect_IdAdded(1, &cgen); + mock().expectOneCall("BasicGenerateCb").andReturnValue(true); + buildcc::m::CustomGeneratorRunner(cgen); } - - CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::SUCCESS); } -// ProperDependency2 depends on ProperDependency1 completion -TEST(CustomGeneratorTestGroup, Basic_ProperDependency_BadCase) { - rebuild_value = false; - - buildcc::CustomGenerator cgen("basic_proper_dependency_bad_case", ""); - cgen.AddIdInfo("id1", {"{current_root_dir}/dummy_main.c"}, - {"{current_build_dir}/dummy_main.o"}, ProperDependency1); +TEST(CustomGeneratorTestGroup, Basic_Failure) { + buildcc::CustomGenerator cgen("basic_failure", ""); + cgen.AddIdInfo("id1", {"{current_root_dir}/dummy_main.c"}, {}, + BasicGenerateCb); cgen.AddIdInfo("id2", {"{current_root_dir}/dummy_main.cpp"}, {}, - ProperDependency2); - cgen.AddDependencyCb( - [](auto &&task_map) { task_map.at("id2").precede(task_map.at("id1")); }); + BasicGenerateCb); cgen.Build(); - mock().expectOneCall("ProperDependency2"); + mock().expectOneCall("BasicGenerateCb").andReturnValue(true); + mock().expectOneCall("BasicGenerateCb").andReturnValue(false); buildcc::m::CustomGeneratorRunner(cgen); - // Serialization check - { - buildcc::internal::CustomGeneratorSerialization serialization( - cgen.GetBinaryPath()); - CHECK_TRUE(serialization.LoadFromFile()); + CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::FAILURE); - const auto &internal_map = serialization.GetLoad().internal_ids; - CHECK_EQUAL(internal_map.size(), 0); - } + // Load + buildcc::internal::CustomGeneratorSerialization serialization( + cgen.GetBinaryPath()); + CHECK_TRUE(serialization.LoadFromFile()); - CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::FAILURE); + const auto &internal_map = serialization.GetLoad().internal_ids; + CHECK_EQUAL(internal_map.size(), 1); } TEST(CustomGeneratorTestGroup, DefaultArgumentUsage) { buildcc::CustomGenerator cgen("default_argument_usage", ""); - cgen.AddPatterns({ - {"dummy_main_c", "{current_root_dir}/dummy_main.c"}, - {"dummy_main_o", "{current_build_dir}/dummy_main.o"}, - {"dummy_main_cpp", "{current_root_dir}/dummy_main.cpp"}, - }); + cgen.AddPatterns({{"dummy_main_c", "{current_root_dir}/dummy_main.c"}, + {"dummy_main_o", "{current_build_dir}/dummy_main.o"}, + {"dummy_main_cpp", "{current_root_dir}/dummy_main.cpp"}, + {"hello", "world"}}); + STRCMP_EQUAL(cgen.ParsePattern("{hello}").c_str(), "world"); + STRCMP_EQUAL(cgen.Get("hello").c_str(), "world"); + cgen.AddIdInfo("id1", {"{dummy_main_c}"}, {"{dummy_main_o}"}, BasicGenerateCb); cgen.AddIdInfo("id2", {"{dummy_main_cpp}"}, {}, BasicGenerateCb); @@ -399,213 +245,6 @@ TEST(CustomGeneratorTestGroup, FailureCases) { buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); } -static bool Dep1Cb(const buildcc::CustomGeneratorContext &ctx) { - (void)ctx; - mock().actualCall("Dep1Cb"); - return buildcc::env::Command::Execute(""); -} - -static bool Dep2Cb(const buildcc::CustomGeneratorContext &ctx) { - (void)ctx; - mock().actualCall("Dep2Cb"); - return buildcc::env::Command::Execute(""); -} - -static void DependencyCb(std::unordered_map &&task_map) { - task_map.at("id1").precede(task_map.at("id2")); -} - -TEST(CustomGeneratorTestGroup, AddDependency_BasicCheck) { - constexpr const char *const kGenName = "add_dependency_basic_check"; - { - buildcc::CustomGenerator cgen(kGenName, ""); - cgen.AddIdInfo("id2", {"{current_root_dir}/dummy_main.c"}, - {"{current_build_dir}/dummy_main.o"}, Dep2Cb); - cgen.AddIdInfo("id1", {"{current_root_dir}/dummy_main.cpp"}, - {"{current_build_dir}/dummy_main.o"}, Dep1Cb); - cgen.AddDependencyCb(DependencyCb); - cgen.Build(); - - mock().expectOneCall("Dep1Cb"); - buildcc::env::m::CommandExpect_Execute(1, true); - mock().expectOneCall("Dep2Cb"); - buildcc::env::m::CommandExpect_Execute(1, true); - buildcc::m::CustomGeneratorRunner(cgen); - - buildcc::internal::CustomGeneratorSerialization serialization( - cgen.GetBinaryPath()); - CHECK_TRUE(serialization.LoadFromFile()); - CHECK_EQUAL(serialization.GetLoad().internal_ids.size(), 2); - } -} - -static bool FileDep1Cb(const buildcc::CustomGeneratorContext &ctx) { - mock().actualCall("FileDep1Cb"); - for (const auto &o : ctx.outputs) { - CHECK_TRUE(buildcc::env::save_file(o.string().c_str(), "", false)); - } - return true; -} - -static bool FileDep2Cb(const buildcc::CustomGeneratorContext &ctx) { - mock().actualCall("FileDep2Cb"); - for (const auto &i : ctx.inputs) { - CHECK_TRUE(fs::exists(i)); - } - return true; -} - -TEST(CustomGeneratorTestGroup, AddDependency_FileDep) { - constexpr const char *const kGenName = "add_dependency_file_dep"; - { - buildcc::CustomGenerator cgen(kGenName, ""); - cgen.AddIdInfo("id1", {"{current_root_dir}/dummy_main.c"}, - {"{current_build_dir}/dummy_main.o"}, FileDep1Cb); - cgen.AddIdInfo("id2", {"{current_build_dir}/dummy_main.o"}, {}, FileDep2Cb); - cgen.AddDependencyCb(DependencyCb); - cgen.Build(); - - mock().expectOneCall("FileDep1Cb"); - mock().expectOneCall("FileDep2Cb"); - buildcc::m::CustomGeneratorRunner(cgen); - - buildcc::internal::CustomGeneratorSerialization serialization( - cgen.GetBinaryPath()); - CHECK_TRUE(serialization.LoadFromFile()); - CHECK_EQUAL(serialization.GetLoad().internal_ids.size(), 2); - } -} - -TEST(CustomGeneratorTestGroup, AddDependency_FileDep_WithRebuild) { - constexpr const char *const kGenName = "add_dependency_file_dep_with_rebuild"; - - fs::path kInputFile = - (BUILD_DIR / kGenName / "dummy_main.c").make_preferred(); - UT_PRINT(kInputFile.string().c_str()); - fs::create_directories(BUILD_DIR / kGenName); - CHECK_TRUE(buildcc::env::save_file(kInputFile.string().c_str(), "", false)); - - { - buildcc::CustomGenerator cgen(kGenName, ""); - cgen.AddIdInfo("id1", {"{current_build_dir}/dummy_main.c"}, - {"{current_build_dir}/dummy_main.o"}, FileDep1Cb); - cgen.AddIdInfo("id2", {"{current_build_dir}/dummy_main.o"}, {}, FileDep2Cb); - cgen.AddDependencyCb(DependencyCb); - cgen.Build(); - - mock().expectOneCall("FileDep1Cb"); - mock().expectOneCall("FileDep2Cb"); - buildcc::m::CustomGeneratorRunner(cgen); - - buildcc::internal::CustomGeneratorSerialization serialization( - cgen.GetBinaryPath()); - CHECK_TRUE(serialization.LoadFromFile()); - CHECK_EQUAL(serialization.GetLoad().internal_ids.size(), 2); - - CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::SUCCESS); - } - - // Same, no change - { - buildcc::CustomGenerator cgen(kGenName, ""); - cgen.AddIdInfo("id1", {"{current_build_dir}/dummy_main.c"}, - {"{current_build_dir}/dummy_main.o"}, FileDep1Cb); - cgen.AddIdInfo("id2", {"{current_build_dir}/dummy_main.o"}, {}, FileDep2Cb); - cgen.AddDependencyCb(DependencyCb); - cgen.Build(); - - buildcc::m::CustomGeneratorRunner(cgen); - - buildcc::internal::CustomGeneratorSerialization serialization( - cgen.GetBinaryPath()); - CHECK_TRUE(serialization.LoadFromFile()); - CHECK_EQUAL(serialization.GetLoad().internal_ids.size(), 2); - - CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::SUCCESS); - } - - // reset - fs::remove_all(BUILD_DIR / kGenName / "dummy_main.o"); - - // Remove id1, should cause id2 to fail - // NOTE, dirty_ == false is not made true when id2 is run, however id removed - // sets dirty_ == true - { - buildcc::CustomGenerator cgen(kGenName, ""); - cgen.AddIdInfo("id2", {"{current_build_dir}/dummy_main.o"}, {}, FileDep2Cb); - cgen.AddDependencyCb(DependencyCb); - cgen.Build(); - - buildcc::m::CustomGeneratorExpect_IdRemoved(1, &cgen); - buildcc::m::CustomGeneratorRunner(cgen); - - buildcc::internal::CustomGeneratorSerialization serialization( - cgen.GetBinaryPath()); - CHECK_TRUE(serialization.LoadFromFile()); - CHECK_EQUAL(serialization.GetLoad().internal_ids.size(), 0); - - CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::FAILURE); - } - - // reset - buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); - fs::remove_all(BUILD_DIR / kGenName / "dummy_main.o"); - - // Added - { - buildcc::CustomGenerator cgen(kGenName, ""); - cgen.AddIdInfo("id1", {"{current_build_dir}/dummy_main.c"}, - {"{current_build_dir}/dummy_main.o"}, FileDep1Cb); - cgen.AddIdInfo("id2", {"{current_build_dir}/dummy_main.o"}, {}, FileDep2Cb); - cgen.AddDependencyCb(DependencyCb); - cgen.Build(); - - buildcc::m::CustomGeneratorExpect_IdAdded(1, &cgen); - buildcc::m::CustomGeneratorExpect_IdAdded(1, &cgen); - mock().expectOneCall("FileDep1Cb"); - mock().expectOneCall("FileDep2Cb"); - buildcc::m::CustomGeneratorRunner(cgen); - - buildcc::internal::CustomGeneratorSerialization serialization( - cgen.GetBinaryPath()); - CHECK_TRUE(serialization.LoadFromFile()); - CHECK_EQUAL(serialization.GetLoad().internal_ids.size(), 2); - - CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::SUCCESS); - } - - // reset - buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); - fs::remove_all(BUILD_DIR / kGenName / "dummy_main.o"); - - buildcc::m::blocking_sleep(1); - buildcc::env::save_file(kInputFile.string().c_str(), "", false); - - // Update id1:dummy_main.c -> updated dummy_main.o -> should rerun id2 as well - { - buildcc::CustomGenerator cgen(kGenName, ""); - - cgen.AddIdInfo("id1", {"{current_build_dir}/dummy_main.c"}, - {"{current_build_dir}/dummy_main.o"}, FileDep1Cb); - cgen.AddIdInfo("id2", {"{current_build_dir}/dummy_main.o"}, {}, FileDep2Cb); - cgen.AddDependencyCb(DependencyCb); - cgen.Build(); - - mock().expectOneCall("FileDep1Cb"); - mock().expectOneCall("FileDep2Cb"); - buildcc::m::CustomGeneratorRunner(cgen); - - buildcc::internal::CustomGeneratorSerialization serialization( - cgen.GetBinaryPath()); - CHECK_TRUE(serialization.LoadFromFile()); - CHECK_EQUAL(serialization.GetLoad().internal_ids.size(), 2); - - CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::SUCCESS); - } - - buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); -} - static bool RealGenerateCb(const buildcc::CustomGeneratorContext &ctx) { (void)ctx; mock().actualCall("RealGenerateCb"); @@ -666,209 +305,6 @@ TEST(CustomGeneratorTestGroup, RealGenerate_Basic) { buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); } -TEST(CustomGeneratorTestGroup, RealGenerate_RemoveAndAdd) { - constexpr const char *const kGenName = "real_generator_remove_and_add"; - { - buildcc::CustomGenerator cgen(kGenName, ""); - cgen.AddIdInfo("id1", {"{current_root_dir}/dummy_main.cpp"}, - {"{current_build_dir}/dummy_main.o"}, RealGenerateCb); - cgen.AddIdInfo("id2", {"{current_root_dir}/dummy_main.c"}, - {"{current_build_dir}/dummy_main.o"}, RealGenerateCb); - cgen.Build(); - - mock().expectOneCall("RealGenerateCb"); - buildcc::env::m::CommandExpect_Execute(1, true); - mock().expectOneCall("RealGenerateCb"); - buildcc::env::m::CommandExpect_Execute(1, true); - buildcc::m::CustomGeneratorRunner(cgen); - - buildcc::internal::CustomGeneratorSerialization serialization( - cgen.GetBinaryPath()); - CHECK_TRUE(serialization.LoadFromFile()); - CHECK_EQUAL(serialization.GetLoad().internal_ids.size(), 2); - auto imap = serialization.GetLoad().internal_ids; - CHECK_EQUAL(imap.at("id1").internal_inputs.size(), 1); - CHECK_EQUAL(imap.at("id2").internal_inputs.size(), 1); - - CHECK_EQUAL(imap.at("id1").outputs.size(), 1); - CHECK_EQUAL(imap.at("id2").outputs.size(), 1); - } - - // Same, no change - { - buildcc::CustomGenerator cgen(kGenName, ""); - cgen.AddIdInfo("id1", {"{current_root_dir}/dummy_main.cpp"}, - {"{current_build_dir}/dummy_main.o"}, RealGenerateCb); - cgen.AddIdInfo("id2", {"{current_root_dir}/dummy_main.c"}, - {"{current_build_dir}/dummy_main.o"}, RealGenerateCb); - cgen.Build(); - - buildcc::m::CustomGeneratorRunner(cgen); - - buildcc::internal::CustomGeneratorSerialization serialization( - cgen.GetBinaryPath()); - CHECK_TRUE(serialization.LoadFromFile()); - CHECK_EQUAL(serialization.GetLoad().internal_ids.size(), 2); - auto imap = serialization.GetLoad().internal_ids; - CHECK_EQUAL(imap.at("id1").internal_inputs.size(), 1); - CHECK_EQUAL(imap.at("id2").internal_inputs.size(), 1); - - CHECK_EQUAL(imap.at("id1").outputs.size(), 1); - CHECK_EQUAL(imap.at("id2").outputs.size(), 1); - } - - // Map Removed - { - buildcc::CustomGenerator cgen(kGenName, ""); - cgen.AddIdInfo("id1", {"{current_root_dir}/dummy_main.cpp"}, - {"{current_build_dir}/dummy_main.o"}, RealGenerateCb); - - cgen.Build(); - - buildcc::m::CustomGeneratorExpect_IdRemoved(1, &cgen); - buildcc::m::CustomGeneratorRunner(cgen); - - buildcc::internal::CustomGeneratorSerialization serialization( - cgen.GetBinaryPath()); - CHECK_TRUE(serialization.LoadFromFile()); - CHECK_EQUAL(serialization.GetLoad().internal_ids.size(), 1); - auto imap = serialization.GetLoad().internal_ids; - CHECK_EQUAL(imap.at("id1").internal_inputs.size(), 1); - CHECK_EQUAL(imap.at("id1").outputs.size(), 1); - - CHECK_THROWS(std::out_of_range, imap.at("id2")); - } - - // Map Added Failure - { - buildcc::CustomGenerator cgen(kGenName, ""); - cgen.AddIdInfo("id1", {"{current_root_dir}/dummy_main.cpp"}, - {"{current_build_dir}/dummy_main.o"}, RealGenerateCb); - cgen.AddIdInfo("id2", {"{current_root_dir}/dummy_main.c"}, - {"{current_build_dir}/dummy_main.o"}, RealGenerateCb); - cgen.AddDependencyCb([](auto &&task_map) { - task_map.at("id1").precede(task_map.at("id2")); - }); - cgen.Build(); - - buildcc::m::CustomGeneratorExpect_IdAdded(1, &cgen); - mock().expectOneCall("RealGenerateCb"); - buildcc::env::m::CommandExpect_Execute(1, false); - buildcc::m::CustomGeneratorRunner(cgen); - - buildcc::internal::CustomGeneratorSerialization serialization( - cgen.GetBinaryPath()); - CHECK_TRUE(serialization.LoadFromFile()); - CHECK_EQUAL(serialization.GetLoad().internal_ids.size(), 1); - auto imap = serialization.GetLoad().internal_ids; - CHECK_EQUAL(imap.at("id1").internal_inputs.size(), 1); - CHECK_EQUAL(imap.at("id1").outputs.size(), 1); - CHECK_THROWS(std::out_of_range, imap.at("id2")); - CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::FAILURE); - } - - buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); - - // Map Added Success - { - buildcc::CustomGenerator cgen(kGenName, ""); - cgen.AddIdInfo("id1", {"{current_root_dir}/dummy_main.cpp"}, - {"{current_build_dir}/dummy_main.o"}, RealGenerateCb); - cgen.AddIdInfo("id2", {"{current_root_dir}/dummy_main.c"}, - {"{current_build_dir}/dummy_main.o"}, RealGenerateCb); - cgen.Build(); - - buildcc::m::CustomGeneratorExpect_IdAdded(1, &cgen); - mock().expectOneCall("RealGenerateCb"); - buildcc::env::m::CommandExpect_Execute(1, true); - buildcc::m::CustomGeneratorRunner(cgen); - - buildcc::internal::CustomGeneratorSerialization serialization( - cgen.GetBinaryPath()); - CHECK_TRUE(serialization.LoadFromFile()); - CHECK_EQUAL(serialization.GetLoad().internal_ids.size(), 2); - auto imap = serialization.GetLoad().internal_ids; - CHECK_EQUAL(imap.at("id1").internal_inputs.size(), 1); - CHECK_EQUAL(imap.at("id2").internal_inputs.size(), 1); - - CHECK_EQUAL(imap.at("id1").outputs.size(), 1); - CHECK_EQUAL(imap.at("id2").outputs.size(), 1); - } -} - -TEST(CustomGeneratorTestGroup, RealGenerate_Update_Failure) { - constexpr const char *const kGenName = "real_generator_update_failure"; - - { - buildcc::CustomGenerator cgen(kGenName, ""); - buildcc::env::save_file( - (cgen.GetBuildDir() / "dummy_main.c").string().c_str(), "", false); - buildcc::env::save_file( - (cgen.GetBuildDir() / "dummy_main.cpp").string().c_str(), "", false); - - cgen.AddIdInfo("id1", {"{current_build_dir}/dummy_main.c"}, - {"{current_build_dir}/dummy_main.o"}, RealGenerateCb); - cgen.AddIdInfo("id2", {"{current_build_dir}/dummy_main.cpp"}, - {"{current_build_dir}/other_dummy_main.o"}, RealGenerateCb); - cgen.AddDependencyCb([](auto &&task_map) { - task_map.at("id1").precede(task_map.at("id2")); - }); - cgen.Build(); - - mock().expectOneCall("RealGenerateCb"); - buildcc::env::m::CommandExpect_Execute(1, true); - mock().expectOneCall("RealGenerateCb"); - buildcc::env::m::CommandExpect_Execute(1, true); - buildcc::m::CustomGeneratorRunner(cgen); - - buildcc::internal::CustomGeneratorSerialization serialization( - cgen.GetBinaryPath()); - CHECK_TRUE(serialization.LoadFromFile()); - CHECK_EQUAL(serialization.GetLoad().internal_ids.size(), 2); - auto imap = serialization.GetLoad().internal_ids; - CHECK_EQUAL(imap.at("id1").internal_inputs.size(), 1); - CHECK_EQUAL(imap.at("id2").internal_inputs.size(), 1); - - CHECK_EQUAL(imap.at("id1").outputs.size(), 1); - CHECK_EQUAL(imap.at("id2").outputs.size(), 1); - } - - buildcc::m::blocking_sleep(1); - - // Updated Input file Failure - UT_PRINT("Updated Input file: Failure\r\n"); - { - buildcc::CustomGenerator cgen(kGenName, ""); - buildcc::env::save_file( - (cgen.GetBuildDir() / "dummy_main.cpp").string().c_str(), "", false); - - cgen.AddIdInfo("id1", {"{current_build_dir}/dummy_main.c"}, - {"{current_build_dir}/dummy_main.o"}, RealGenerateCb); - cgen.AddIdInfo("id2", {"{current_build_dir}/dummy_main.cpp"}, - {"{current_build_dir}/other_dummy_main.o"}, RealGenerateCb); - cgen.AddDependencyCb([](auto &&task_map) { - task_map.at("id1").precede(task_map.at("id2")); - }); - cgen.Build(); - - mock().expectOneCall("RealGenerateCb"); - buildcc::env::m::CommandExpect_Execute(1, false); - buildcc::m::CustomGeneratorRunner(cgen); - - buildcc::internal::CustomGeneratorSerialization serialization( - cgen.GetBinaryPath()); - CHECK_TRUE(serialization.LoadFromFile()); - CHECK_EQUAL(serialization.GetLoad().internal_ids.size(), 1); - auto imap = serialization.GetLoad().internal_ids; - CHECK_EQUAL(imap.at("id1").internal_inputs.size(), 1); - CHECK_EQUAL(imap.at("id1").outputs.size(), 1); - - CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::FAILURE); - } - - buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); -} - TEST(CustomGeneratorTestGroup, RealGenerate_Update_Success) { constexpr const char *const kGenName = "real_generator_update_success"; diff --git a/buildcc/schema/include/schema/custom_generator_schema.h b/buildcc/schema/include/schema/custom_generator_schema.h index 0a171f2c..d7027b98 100644 --- a/buildcc/schema/include/schema/custom_generator_schema.h +++ b/buildcc/schema/include/schema/custom_generator_schema.h @@ -27,11 +27,9 @@ struct CustomGeneratorSchema { private: static constexpr const char *const kName = "name"; static constexpr const char *const kIds = "ids"; - static constexpr const char *const kGroups = "groups"; public: using IdKey = std::string; - using GroupKey = std::string; struct IdInfo { private: static constexpr const char *const kInputs = "inputs"; @@ -57,23 +55,18 @@ struct CustomGeneratorSchema { }; using IdPair = std::pair; - using GroupInfo = std::unordered_set; - using GroupPair = std::pair; std::string name; std::unordered_map internal_ids; - std::unordered_map internal_groups; friend void to_json(json &j, const CustomGeneratorSchema &schema) { j[kName] = schema.name; j[kIds] = schema.internal_ids; - j[kGroups] = schema.internal_groups; } friend void from_json(const json &j, CustomGeneratorSchema &schema) { j.at(kName).get_to(schema.name); j.at(kIds).get_to(schema.internal_ids); - j.at(kGroups).get_to(schema.internal_groups); } }; diff --git a/cmake/coverage/gcovr.cmake b/cmake/coverage/gcovr.cmake index d127fe42..4d97ebbe 100644 --- a/cmake/coverage/gcovr.cmake +++ b/cmake/coverage/gcovr.cmake @@ -9,6 +9,7 @@ else() message("GCOVR at ${gcovr_program}") set(GCOVR_REMOVE_OPTIONS + --exclude "(.+/)?third_party(.+/)?" --exclude "(.+/)?spdlog(.+/)?" --exclude "(.+/)?fmt(.+/)?" --exclude "(.+/)?taskflow(.+/)?" diff --git a/cmake/tool/cppcheck.cmake b/cmake/tool/cppcheck.cmake index 20d98554..9f11dbe6 100644 --- a/cmake/tool/cppcheck.cmake +++ b/cmake/tool/cppcheck.cmake @@ -13,7 +13,7 @@ if(${BUILDCC_CPPCHECK}) set(CPPCHECK_ADDITIONAL_OPTIONS --std=c++17 -q - --error-exitcode=1 + # --error-exitcode=1 --cppcheck-build-dir=${CMAKE_CURRENT_BINARY_DIR}/cppcheck_output ) set(CPPCHECK_CHECK_DIR