diff --git a/buildcc/buildcc.h b/buildcc/buildcc.h index b4262684..39420747 100644 --- a/buildcc/buildcc.h +++ b/buildcc/buildcc.h @@ -24,8 +24,12 @@ #include "env/assert_fatal.h" #include "env/logging.h" #include "env/host_os.h" +#include "env/host_compiler.h" #include "env/util.h" +// +#include "command/command.h" + // Base #include "toolchain/toolchain.h" #include "target/target.h" diff --git a/buildcc/lib/target/cmake/mock_target.cmake b/buildcc/lib/target/cmake/mock_target.cmake index e75b4ed7..4ca4566a 100644 --- a/buildcc/lib/target/cmake/mock_target.cmake +++ b/buildcc/lib/target/cmake/mock_target.cmake @@ -1,17 +1,29 @@ add_library(mock_target STATIC + # Utils + src/util/util.cpp + + # Generator + src/generator/generator_loader.cpp + src/generator/generator_storer.cpp + src/generator/generator.cpp + + # Generator mocks + mock/generator/task.cpp + mock/generator/recheck_states.cpp + + # Target + src/target/target_loader.cpp + src/target/target_storer.cpp src/target/target.cpp src/target/source.cpp src/target/include_dir.cpp src/target/lib.cpp src/target/build.cpp src/target/flags.cpp + + # Target mocks mock/target/recheck_states.cpp mock/target/tasks.cpp - - src/fbs/fbs_loader.cpp - src/fbs/fbs_storer.cpp - - src/util/util.cpp ) target_include_directories(mock_target PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include diff --git a/buildcc/lib/target/cmake/target.cmake b/buildcc/lib/target/cmake/target.cmake index 2603ce76..3be095ff 100644 --- a/buildcc/lib/target/cmake/target.cmake +++ b/buildcc/lib/target/cmake/target.cmake @@ -1,23 +1,35 @@ set(TARGET_SRCS + # Interfaces + include/target/loader_interface.h + include/target/builder_interface.h + + # Utils + src/util/util.cpp + include/target/path.h + include/target/util.h + + # Generator + src/generator/generator_loader.cpp + src/generator/generator_storer.cpp + src/generator/generator.cpp + src/generator/task.cpp + src/generator/recheck_states.cpp + include/target/generator_loader.h + include/target/generator.h + + # Target + src/target/target_loader.cpp + src/target/target_storer.cpp src/target/target.cpp src/target/source.cpp src/target/include_dir.cpp src/target/lib.cpp src/target/build.cpp src/target/flags.cpp - src/target/recheck_states.cpp src/target/tasks.cpp - - src/fbs/fbs_loader.cpp - src/fbs/fbs_storer.cpp - - src/util/util.cpp - + include/target/target_loader.h include/target/target.h - include/target/fbs_loader.h - include/target/path.h - include/target/util.h ) if(${BUILDCC_BUILD_AS_SINGLE_LIB}) diff --git a/buildcc/lib/target/include/target/builder_interface.h b/buildcc/lib/target/include/target/builder_interface.h new file mode 100644 index 00000000..43e66221 --- /dev/null +++ b/buildcc/lib/target/include/target/builder_interface.h @@ -0,0 +1,97 @@ +/* + * Copyright 2021 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_BUILDER_INTERFACE_H_ +#define TARGET_BUILDER_INTERFACE_H_ + +#include + +#include "target/path.h" +#include "target/util.h" + +namespace buildcc::base { + +class BuilderInterface { + +public: + virtual void Build() = 0; + +protected: + template + void RecheckChanged( + const T &previous, const T ¤t, + const std::function &callback = []() {}) { + if (dirty_) { + return; + } + + if (previous != current) { + callback(); + dirty_ = true; + } + } + + void RecheckPaths(const internal::path_unordered_set &previous_path, + const internal::path_unordered_set ¤t_path, + const std::function &path_removed_cb, + const std::function &path_added_cb, + const std::function &path_updated_cb) { + if (dirty_) { + return; + } + + // * Old path is removed + const bool removed = + internal::is_previous_paths_different(previous_path, current_path); + if (removed) { + path_removed_cb(); + dirty_ = true; + return; + } + + for (const auto &path : current_path) { + auto iter = previous_path.find(path); + + if (iter == previous_path.end()) { + // * New path added + path_added_cb(); + dirty_ = true; + } else { + // * Path is updated + if (path.GetLastWriteTimestamp() > iter->GetLastWriteTimestamp()) { + path_updated_cb(); + dirty_ = true; + } else { + // * Do nothing + } + } + + if (dirty_) { + break; + } + } + } + +private: + virtual bool Store() = 0; + +protected: + bool dirty_{false}; +}; + +} // namespace buildcc::base + +#endif diff --git a/buildcc/lib/target/include/target/generator.h b/buildcc/lib/target/include/target/generator.h new file mode 100644 index 00000000..b70b3bc0 --- /dev/null +++ b/buildcc/lib/target/include/target/generator.h @@ -0,0 +1,92 @@ +/* + * Copyright 2021 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_GENERATOR_H_ +#define TARGET_GENERATOR_H_ + +#include +#include +#include + +#include "taskflow/taskflow.hpp" + +#include "env/env.h" + +#include "target/builder_interface.h" + +#include "target/generator_loader.h" +#include "target/path.h" + +namespace buildcc::base { + +struct UserGenInfo { + std::string name; + internal::fs_unordered_set inputs; + internal::fs_unordered_set outputs; + std::vector commands; + bool parallel{false}; + + explicit UserGenInfo(const std::string &n, + const internal::fs_unordered_set &i, + const internal::fs_unordered_set &o, + const std::vector &c, bool p) + : name(n), inputs(i), outputs(o), commands(c), parallel(p) {} +}; + +class Generator : public BuilderInterface { +public: + Generator(const std::string &name, const fs::path &path) + : loader_(name, path) {} + Generator(const Generator &generator) = delete; + + void AddGenInfo(const UserGenInfo &info); + void Build() override; + + // Getter + fs::path GetBinaryPath() const { return loader_.GetBinaryPath(); } + tf::Taskflow &GetTaskflow() { return tf_; } + +private: + void GenerateTask(); + // Convert from UserGenInfo to internal::GenInfo + void Convert(); + std::vector BuildGenerate(); + bool Regenerate(std::vector &generated_files); + + bool Store() override; + + // Recheck states + void InputRemoved(); + void InputAdded(); + void InputUpdated(); + + void OutputChanged(); + void CommandChanged(); + +private: + std::string name_; + std::unordered_map user_info_; + std::unordered_map current_info_; + + internal::GeneratorLoader loader_; + + tf::Taskflow tf_; + tf::Task build_task_; +}; + +} // namespace buildcc::base + +#endif diff --git a/buildcc/lib/target/include/target/generator_loader.h b/buildcc/lib/target/include/target/generator_loader.h new file mode 100644 index 00000000..d6344120 --- /dev/null +++ b/buildcc/lib/target/include/target/generator_loader.h @@ -0,0 +1,72 @@ +/* + * Copyright 2021 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_GENERATOR_LOADER_H_ +#define TARGET_GENERATOR_LOADER_H_ + +#include "target/loader_interface.h" + +#include +#include +#include + +#include "fmt/format.h" + +#include "target/path.h" + +namespace buildcc::internal { + +struct GenInfo { + std::string name; + path_unordered_set inputs; + fs_unordered_set outputs; + std::vector commands; + bool parallel{false}; + + explicit GenInfo(const std::string &n, const path_unordered_set &i, + const fs_unordered_set &o, const std::vector &c, + bool p) + : name(n), inputs(i), outputs(o), commands(c), parallel(p) {} +}; + +typedef std::unordered_map geninfo_unordered_map; + +class GeneratorLoader : public LoaderInterface { +public: + GeneratorLoader(const std::string &name, const fs::path &path) + : name_(name), path_(path) {} + + GeneratorLoader(const GeneratorLoader &loader) = delete; + + bool Load() override; + + // Getters + fs::path GetBinaryPath() const override { + return path_ / fmt::format("{}.bin", name_); + } + + const geninfo_unordered_map &GetLoadedInfo() { return loaded_info_; } + +private: + std::string name_; + fs::path path_; + + geninfo_unordered_map loaded_info_; +}; + +} // namespace buildcc::internal + +#endif diff --git a/buildcc/lib/target/include/target/loader_interface.h b/buildcc/lib/target/include/target/loader_interface.h new file mode 100644 index 00000000..1123262f --- /dev/null +++ b/buildcc/lib/target/include/target/loader_interface.h @@ -0,0 +1,39 @@ +/* + * Copyright 2021 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_LOADER_INTERFACE_H_ +#define TARGET_LOADER_INTERFACE_H_ + +#include + +namespace fs = std::filesystem; + +namespace buildcc::internal { + +class LoaderInterface { +public: + virtual bool Load() = 0; + virtual fs::path GetBinaryPath() const = 0; + + bool IsLoaded() const { return loaded_; }; + +protected: + bool loaded_{false}; +}; + +} // namespace buildcc::internal + +#endif diff --git a/buildcc/lib/target/include/target/private/schema_util.h b/buildcc/lib/target/include/target/private/schema_util.h new file mode 100644 index 00000000..0fd15a1c --- /dev/null +++ b/buildcc/lib/target/include/target/private/schema_util.h @@ -0,0 +1,128 @@ +/* + * Copyright 2021 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_PRIVATE_SCHEMA_UTIL_H_ +#define TARGET_PRIVATE_SCHEMA_UTIL_H_ + +#include "path_generated.h" + +#include + +#include "target/path.h" + +namespace fbs = schema::internal; + +namespace buildcc::internal { + +// Extract APIs for LOAD + +inline void ExtractPath( + const flatbuffers::Vector> + *fbs_paths, + buildcc::internal::path_unordered_set &out) { + if (fbs_paths == nullptr) { + return; + } + + for (auto iter = fbs_paths->begin(); iter != fbs_paths->end(); iter++) { + out.insert(buildcc::internal::Path::CreateNewPath( + iter->pathname()->c_str(), iter->last_write_timestamp())); + } +} + +inline void +Extract(const flatbuffers::Vector> + *fbs_paths, + fs_unordered_set &out) { + if (fbs_paths == nullptr) { + return; + } + + for (auto iter = fbs_paths->begin(); iter != fbs_paths->end(); iter++) { + out.insert(iter->str()); + } +} + +template +inline void +Extract(const flatbuffers::Vector> + *fbs_paths, + std::unordered_set &out) { + if (fbs_paths == nullptr) { + return; + } + + for (auto iter = fbs_paths->begin(); iter != fbs_paths->end(); iter++) { + out.insert(iter->str()); + } +} + +template +inline void +Extract(const flatbuffers::Vector> + *fbs_paths, + std::vector &out) { + if (fbs_paths == nullptr) { + return; + } + + for (auto iter = fbs_paths->begin(); iter != fbs_paths->end(); iter++) { + out.push_back(iter->str()); + } +} + +// Create APIs for STORE + +inline std::vector> +CreateFbsVectorPath(flatbuffers::FlatBufferBuilder &builder, + const buildcc::internal::path_unordered_set &pathlist) { + std::vector> paths; + for (const auto &p : pathlist) { + auto fbs_file = fbs::CreatePathDirect( + builder, p.GetPathname().string().c_str(), p.GetLastWriteTimestamp()); + paths.push_back(fbs_file); + } + return paths; +} + +template +inline std::vector> +CreateFbsVectorString(flatbuffers::FlatBufferBuilder &builder, + const T &strlist) { + std::vector> strs; + std::transform( + strlist.begin(), strlist.end(), std::back_inserter(strs), + [&](const std::string &str) -> flatbuffers::Offset { + return builder.CreateString(str); + }); + return strs; +} + +inline std::vector> +CreateFbsVectorString(flatbuffers::FlatBufferBuilder &builder, + const buildcc::internal::fs_unordered_set &fslist) { + std::vector> strs; + std::transform( + fslist.begin(), fslist.end(), std::back_inserter(strs), + [&](const fs::path &p) -> flatbuffers::Offset { + return builder.CreateString(p.string()); + }); + return strs; +} + +} // namespace buildcc::internal + +#endif diff --git a/buildcc/lib/target/include/target/target.h b/buildcc/lib/target/include/target/target.h index 7e8bc3de..ed25deaa 100644 --- a/buildcc/lib/target/include/target/target.h +++ b/buildcc/lib/target/include/target/target.h @@ -26,9 +26,11 @@ #include #include +#include "target/builder_interface.h" + // Internal -#include "target/fbs_loader.h" #include "target/path.h" +#include "target/target_loader.h" #include "command/command.h" #include "env/env.h" @@ -39,8 +41,6 @@ namespace buildcc::base { -namespace fs = std::filesystem; - enum class FileExtType { Asm, C, @@ -55,7 +55,7 @@ enum class TargetType { DynamicLibrary, }; -class Target { +class Target : public BuilderInterface { public: // TODO, Consider making these std::string_view for string literals @@ -92,7 +92,7 @@ class Target { Target(const Target &target) = delete; // Builders - void Build(); + void Build() override; // Setters @@ -246,17 +246,12 @@ class Target { const std::unordered_set &previous_external_libs, const std::unordered_set ¤t_external_libs); - // Helper function - void RecheckChanged(const std::unordered_set &previous, - const std::unordered_set ¤t, - const std::function &callback); - // Tasks void CompileTask(); void LinkTask(); // Fbs - bool Store(); + bool Store() override; // Callbacks void SourceRemoved(); @@ -282,7 +277,7 @@ class Target { internal::path_unordered_set current_source_files_; // NOTE, Always store the absolute source path -> absolute compiled source // path here - std::unordered_map + std::unordered_map current_object_files_; internal::path_unordered_set current_header_files_; @@ -314,7 +309,6 @@ class Target { Command command_; // Build states - bool dirty_ = false; bool first_build_ = false; bool rebuild_ = false; diff --git a/buildcc/lib/target/include/target/fbs_loader.h b/buildcc/lib/target/include/target/target_loader.h similarity index 90% rename from buildcc/lib/target/include/target/fbs_loader.h rename to buildcc/lib/target/include/target/target_loader.h index 9e2e1753..7a9bb5c8 100644 --- a/buildcc/lib/target/include/target/fbs_loader.h +++ b/buildcc/lib/target/include/target/target_loader.h @@ -14,20 +14,22 @@ * limitations under the License. */ -#ifndef TARGET_FBS_LOADER_H_ -#define TARGET_FBS_LOADER_H_ +#ifndef TARGET_TARGET_LOADER_H_ +#define TARGET_TARGET_LOADER_H_ + +#include "target/loader_interface.h" -#include #include #include -#include "target/path.h" +#include "fmt/format.h" -namespace fs = std::filesystem; +#include "target/path.h" namespace buildcc::internal { -class FbsLoader { +// TODO, Change name to TargetLoader +class FbsLoader : public LoaderInterface { public: explicit FbsLoader(const std::string &name, const fs::path &relative_path) : name_(name), relative_path_(relative_path) { @@ -37,11 +39,12 @@ class FbsLoader { FbsLoader(const FbsLoader &loader) = delete; public: - bool Load(); + bool Load() override; // Getters - bool IsLoaded() const { return loaded_; } - fs::path GetBinaryPath() const { return relative_path_ / (name_ + ".bin"); } + fs::path GetBinaryPath() const override { + return relative_path_ / fmt::format("{}.bin", name_); + } const path_unordered_set &GetLoadedSources() const { return loaded_sources_; } const path_unordered_set &GetLoadedHeaders() const { return loaded_headers_; } @@ -85,7 +88,6 @@ class FbsLoader { private: std::string name_; fs::path relative_path_; - bool loaded_ = false; path_unordered_set loaded_sources_; path_unordered_set loaded_headers_; diff --git a/buildcc/lib/target/mock/expect_generator.h b/buildcc/lib/target/mock/expect_generator.h new file mode 100644 index 00000000..30c4d2ac --- /dev/null +++ b/buildcc/lib/target/mock/expect_generator.h @@ -0,0 +1,17 @@ +#ifndef TARGET_MOCK_EXPECT_GENERATOR_H_ +#define TARGET_MOCK_EXPECT_GENERATOR_H_ + +#include "target/generator.h" + +namespace buildcc::base::m { + +void GeneratorExpect_InputRemoved(unsigned int calls, Generator *generator); +void GeneratorExpect_InputAdded(unsigned int calls, Generator *generator); +void GeneratorExpect_InputUpdated(unsigned int calls, Generator *generator); + +void GeneratorExpect_OutputChanged(unsigned int calls, Generator *generator); +void GeneratorExpect_CommandChanged(unsigned int calls, Generator *generator); + +} // namespace buildcc::base::m + +#endif diff --git a/buildcc/lib/target/mock/generator/recheck_states.cpp b/buildcc/lib/target/mock/generator/recheck_states.cpp new file mode 100644 index 00000000..1b4a0cf1 --- /dev/null +++ b/buildcc/lib/target/mock/generator/recheck_states.cpp @@ -0,0 +1,59 @@ +#include "target/generator.h" + +#include "expect_generator.h" + +#include "CppUTestExt/MockSupport.h" + +namespace buildcc::base { + +static constexpr const char *const INPUT_REMOVED_FUNCTION = + "Generator::InputRemoved"; +static constexpr const char *const INPUT_ADDED_FUNCTION = + "Generator::InputAdded"; +static constexpr const char *const INPUT_UPDATED_FUNCTION = + "Generator::InputUpdated"; + +static constexpr const char *const OUTPUT_CHANGED_FUNCTION = + "Generator::OutputChanged"; +static constexpr const char *const COMMAND_CHANGED_FUNCTION = + "Generator::CommandChanged"; + +void Generator::InputRemoved() { + mock().actualCall(INPUT_REMOVED_FUNCTION).onObject(this); +} +void Generator::InputAdded() { + mock().actualCall(INPUT_ADDED_FUNCTION).onObject(this); +} +void Generator::InputUpdated() { + mock().actualCall(INPUT_UPDATED_FUNCTION).onObject(this); +} + +void Generator::OutputChanged() { + mock().actualCall(OUTPUT_CHANGED_FUNCTION).onObject(this); +} +void Generator::CommandChanged() { + mock().actualCall(COMMAND_CHANGED_FUNCTION).onObject(this); +} + +namespace m { + +void GeneratorExpect_InputRemoved(unsigned int calls, Generator *generator) { + mock().expectNCalls(calls, INPUT_REMOVED_FUNCTION).onObject(generator); +} +void GeneratorExpect_InputAdded(unsigned int calls, Generator *generator) { + mock().expectNCalls(calls, INPUT_ADDED_FUNCTION).onObject(generator); +} +void GeneratorExpect_InputUpdated(unsigned int calls, Generator *generator) { + mock().expectNCalls(calls, INPUT_UPDATED_FUNCTION).onObject(generator); +} + +void GeneratorExpect_OutputChanged(unsigned int calls, Generator *generator) { + mock().expectNCalls(calls, OUTPUT_CHANGED_FUNCTION).onObject(generator); +} +void GeneratorExpect_CommandChanged(unsigned int calls, Generator *generator) { + mock().expectNCalls(calls, COMMAND_CHANGED_FUNCTION).onObject(generator); +} + +} // namespace m + +} // namespace buildcc::base diff --git a/buildcc/lib/target/mock/generator/task.cpp b/buildcc/lib/target/mock/generator/task.cpp new file mode 100644 index 00000000..4bad5c62 --- /dev/null +++ b/buildcc/lib/target/mock/generator/task.cpp @@ -0,0 +1,27 @@ +#include "target/generator.h" + +#include "command/command.h" + +namespace buildcc::base { + +void Generator::GenerateTask() { + Convert(); + + const auto generated_files = BuildGenerate(); + + if (!dirty_) { + return; + } + + // NOTE, info->parallel is not checked + for (const auto &info : generated_files) { + for (const auto &command : info->commands) { + bool success = Command::Execute(command); + env::assert_fatal(success, fmt::format("{} failed", command)); + } + } + + Store(); +} + +} // namespace buildcc::base diff --git a/buildcc/lib/target/src/fbs/fbs_storer.cpp b/buildcc/lib/target/src/fbs/fbs_storer.cpp deleted file mode 100644 index 6cc32e30..00000000 --- a/buildcc/lib/target/src/fbs/fbs_storer.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2021 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. - */ - -#include "target/target.h" - -#include -#include - -#include "env/logging.h" -#include "env/util.h" - -#include "target_generated.h" - -namespace fbs = schema::internal; - -namespace { - -fbs::TargetType get_fbs_target_type(buildcc::base::TargetType type) { - return (fbs::TargetType)type; -} - -std::vector> -get_fbs_vector_path(flatbuffers::FlatBufferBuilder &builder, - const buildcc::internal::path_unordered_set &pathlist) { - std::vector> paths; - for (const auto &p : pathlist) { - auto fbs_file = fbs::CreatePathDirect( - builder, p.GetPathname().string().c_str(), p.GetLastWriteTimestamp()); - paths.push_back(fbs_file); - } - return paths; -} - -std::vector> -get_fbs_vector_string(flatbuffers::FlatBufferBuilder &builder, - const std::unordered_set &strlist) { - std::vector> strs; - std::transform( - strlist.begin(), strlist.end(), std::back_inserter(strs), - [&](const std::string &str) -> flatbuffers::Offset { - return builder.CreateString(str); - }); - return strs; -} - -std::vector> -get_fbs_vector_string(flatbuffers::FlatBufferBuilder &builder, - const buildcc::internal::fs_unordered_set &fslist) { - std::vector> strs; - std::transform( - fslist.begin(), fslist.end(), std::back_inserter(strs), - [&](const fs::path &p) -> flatbuffers::Offset { - return builder.CreateString(p.string()); - }); - return strs; -} - -} // namespace - -namespace buildcc::base { - -bool Target::Store() { - env::log_trace(name_, __FUNCTION__); - - flatbuffers::FlatBufferBuilder builder; - - auto fbs_target_type = get_fbs_target_type(type_); - - auto fbs_source_files = get_fbs_vector_path(builder, current_source_files_); - auto fbs_header_files = get_fbs_vector_path(builder, current_header_files_); - auto fbs_lib_deps = get_fbs_vector_path(builder, current_lib_deps_); - - auto fbs_external_lib_deps = - get_fbs_vector_string(builder, current_external_lib_deps_); - - auto fbs_include_dirs = get_fbs_vector_string(builder, current_include_dirs_); - auto fbs_lib_dirs = get_fbs_vector_string(builder, current_lib_dirs_); - - auto fbs_preprocessor_flags = - get_fbs_vector_string(builder, current_preprocessor_flags_); - auto fbs_common_compile_flags = - get_fbs_vector_string(builder, current_common_compile_flags_); - auto fbs_c_compile_flags = - get_fbs_vector_string(builder, current_c_compile_flags_); - auto fbs_cpp_compile_flags = - get_fbs_vector_string(builder, current_cpp_compile_flags_); - auto fbs_link_flags = get_fbs_vector_string(builder, current_link_flags_); - - auto fbs_compile_dependencies = - get_fbs_vector_path(builder, current_compile_dependencies_); - auto fbs_link_dependencies = - get_fbs_vector_path(builder, current_link_dependencies_); - - auto fbs_target = fbs::CreateTargetDirect( - builder, name_.c_str(), fbs_target_type, &fbs_source_files, - &fbs_header_files, &fbs_lib_deps, &fbs_external_lib_deps, - &fbs_include_dirs, &fbs_lib_dirs, &fbs_preprocessor_flags, - &fbs_common_compile_flags, &fbs_c_compile_flags, &fbs_cpp_compile_flags, - &fbs_link_flags, &fbs_compile_dependencies, &fbs_link_dependencies); - fbs::FinishTargetBuffer(builder, fbs_target); - - auto file_path = GetBinaryPath(); - return env::SaveFile(file_path.string().c_str(), - (const char *)builder.GetBufferPointer(), - builder.GetSize(), true); -} - -} // namespace buildcc::base diff --git a/buildcc/lib/target/src/generator/generator.cpp b/buildcc/lib/target/src/generator/generator.cpp new file mode 100644 index 00000000..2c72012a --- /dev/null +++ b/buildcc/lib/target/src/generator/generator.cpp @@ -0,0 +1,99 @@ +/* + * Copyright 2021 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. + */ + +#include "target/generator.h" + +#include + +#include "env/assert_fatal.h" + +namespace buildcc::base { + +void Generator::AddGenInfo(const UserGenInfo &info) { + env::assert_fatal( + user_info_.find(info.name) == user_info_.end(), + fmt::format("'{}' information already registered", info.name)); + user_info_.emplace(info.name, info); +} + +void Generator::Build() { GenerateTask(); } + +// PRIVATE + +void Generator::Convert() { + for (const auto &user_info : user_info_) { + internal::path_unordered_set current_inputs; + for (const auto &user_inputs : user_info.second.inputs) { + current_inputs.emplace(internal::Path::CreateExistingPath(user_inputs)); + } + current_info_.emplace( + user_info.first, + internal::GenInfo(user_info.second.name, current_inputs, + user_info.second.outputs, user_info.second.commands, + user_info.second.parallel)); + } +} + +std::vector Generator::BuildGenerate() { + const bool loaded = loader_.Load(); + + std::vector generated_files; + bool build = false; + if (!loaded) { + std::for_each( + current_info_.cbegin(), current_info_.cend(), + [&](const auto &p) { generated_files.push_back(&(p.second)); }); + build = true; + } else { + build = Regenerate(generated_files); + } + + dirty_ = build; + return generated_files; +} + +bool Generator::Regenerate( + std::vector &generated_files) { + bool build = false; + const auto &previous_info = loader_.GetLoadedInfo(); + + for (const auto &p : current_info_) { + try { + const internal::GenInfo &loaded_geninfo = previous_info.at(p.first); + RecheckPaths( + loaded_geninfo.inputs, p.second.inputs, [&]() { InputRemoved(); }, + [&]() { InputAdded(); }, [&]() { InputUpdated(); }); + RecheckChanged(loaded_geninfo.outputs, p.second.outputs, + [&]() { OutputChanged(); }); + RecheckChanged(loaded_geninfo.commands, p.second.commands, + [&]() { CommandChanged(); }); + + if (dirty_) { + generated_files.push_back(&(p.second)); + build = true; + } + dirty_ = false; + } catch (const std::out_of_range &e) { + // This means that current_info has more items than + // previous_info + generated_files.push_back(&(p.second)); + build = true; + } + } + return build; +} + +} // namespace buildcc::base diff --git a/buildcc/lib/target/src/generator/generator_loader.cpp b/buildcc/lib/target/src/generator/generator_loader.cpp new file mode 100644 index 00000000..6adb5f06 --- /dev/null +++ b/buildcc/lib/target/src/generator/generator_loader.cpp @@ -0,0 +1,65 @@ +/* + * Copyright 2021 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. + */ + +#include "target/generator_loader.h" + +#include "env/logging.h" +#include "env/util.h" + +// Private +#include "target/private/schema_util.h" + +#include "generator_generated.h" + +namespace buildcc::internal { + +bool GeneratorLoader::Load() { + env::log_trace(name_, __FUNCTION__); + + auto file_path = GetBinaryPath(); + std::string buffer; + bool is_loaded = env::LoadFile(file_path.string().c_str(), true, &buffer); + if (!is_loaded) { + return false; + } + + flatbuffers::Verifier verifier((const uint8_t *)buffer.c_str(), + buffer.length()); + const bool is_verified = fbs::VerifyGeneratorBuffer(verifier); + if (!is_verified) { + return false; + } + + const auto *generator = fbs::GetGenerator((const void *)buffer.c_str()); + + const auto *info = generator->info(); + for (auto iter = info->cbegin(); iter != info->cend(); iter++) { + path_unordered_set i; + fs_unordered_set o; + std::vector c; + ExtractPath(iter->inputs(), i); + Extract(iter->outputs(), o); + Extract(iter->commands(), c); + loaded_info_.insert( + std::make_pair(iter->name()->c_str(), GenInfo(iter->name()->c_str(), i, + o, c, iter->parallel()))); + } + + loaded_ = true; + return true; +} + +} // namespace buildcc::internal diff --git a/buildcc/lib/target/src/generator/generator_storer.cpp b/buildcc/lib/target/src/generator/generator_storer.cpp new file mode 100644 index 00000000..833e06b2 --- /dev/null +++ b/buildcc/lib/target/src/generator/generator_storer.cpp @@ -0,0 +1,59 @@ +/* + * Copyright 2021 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. + */ + +#include "target/generator.h" + +#include "flatbuffers/flatbuffers.h" + +#include "env/util.h" + +#include "generator_generated.h" +#include "target/private/schema_util.h" + +namespace fbs = schema::internal; + +namespace buildcc::base { + +bool Generator::Store() { + env::log_trace(name_, __FUNCTION__); + + flatbuffers::FlatBufferBuilder builder; + + std::vector> fbs_generator_list; + for (const auto &info : current_info_) { + const auto &geninfo = info.second; + auto fbs_inputs = internal::CreateFbsVectorPath(builder, geninfo.inputs); + auto fbs_outputs = + internal::CreateFbsVectorString(builder, geninfo.outputs); + auto fbs_commands = + internal::CreateFbsVectorString(builder, geninfo.commands); + auto fbs_geninfo = + fbs::CreateGenInfoDirect(builder, geninfo.name.c_str(), &fbs_inputs, + &fbs_outputs, &fbs_commands, geninfo.parallel); + fbs_generator_list.push_back(fbs_geninfo); + } + + auto fbs_generator = + fbs::CreateGeneratorDirect(builder, name_.c_str(), &fbs_generator_list); + fbs::FinishGeneratorBuffer(builder, fbs_generator); + + auto file_path = GetBinaryPath(); + return env::SaveFile(file_path.string().c_str(), + (const char *)builder.GetBufferPointer(), + builder.GetSize(), true); +} + +} // namespace buildcc::base diff --git a/buildcc/lib/target/src/generator/recheck_states.cpp b/buildcc/lib/target/src/generator/recheck_states.cpp new file mode 100644 index 00000000..57149745 --- /dev/null +++ b/buildcc/lib/target/src/generator/recheck_states.cpp @@ -0,0 +1,28 @@ +/* + * Copyright 2021 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. + */ + +#include "target/generator.h" + +namespace buildcc::base { + +void Generator::InputRemoved() {} +void Generator::InputAdded() {} +void Generator::InputUpdated() {} + +void Generator::OutputChanged() {} +void Generator::CommandChanged() {} + +} // namespace buildcc::base diff --git a/buildcc/lib/target/src/generator/task.cpp b/buildcc/lib/target/src/generator/task.cpp new file mode 100644 index 00000000..b720cfb8 --- /dev/null +++ b/buildcc/lib/target/src/generator/task.cpp @@ -0,0 +1,57 @@ +/* + * Copyright 2021 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. + */ + +#include "target/generator.h" + +#include "command/command.h" + +namespace buildcc::base { + +void Generator::GenerateTask() { + build_task_ = tf_.emplace([&](tf::Subflow &subflow) { + Convert(); + const auto generated_files = BuildGenerate(); + if (!dirty_) { + return; + } + + for (const auto &info : generated_files) { + if (info->parallel) { + subflow + .for_each(info->commands.cbegin(), info->commands.cend(), + [](const std::string &command) { + bool success = Command::Execute(command); + env::assert_fatal(success, + fmt::format("{} failed", command)); + }) + .name(info->name); + } else { + subflow + .emplace([&]() { + for (const auto &command : info->commands) { + bool success = Command::Execute(command); + env::assert_fatal(success, fmt::format("{} failed", command)); + } + }) + .name(info->name); + } + } + Store(); + }); + build_task_.name(fmt::format("BuildTask:{}", name_)); +} + +} // namespace buildcc::base diff --git a/buildcc/lib/target/src/target/source.cpp b/buildcc/lib/target/src/target/source.cpp index 6ba19a9b..4cb85e9d 100644 --- a/buildcc/lib/target/src/target/source.cpp +++ b/buildcc/lib/target/src/target/source.cpp @@ -39,8 +39,7 @@ void Target::AddSourceAbsolute(const fs::path &absolute_input_filepath, const auto absolute_compiled_source = internal::Path::CreateNewPath(absolute_output_filepath); fs::create_directories(absolute_compiled_source.GetPathname().parent_path()); - current_object_files_.insert( - {absolute_source.native(), absolute_compiled_source}); + current_object_files_.insert({absolute_source, absolute_compiled_source}); } void Target::GlobSourcesAbsolute(const fs::path &absolute_input_path, diff --git a/buildcc/lib/target/src/target/target.cpp b/buildcc/lib/target/src/target/target.cpp index a652ad16..a2e92b7b 100644 --- a/buildcc/lib/target/src/target/target.cpp +++ b/buildcc/lib/target/src/target/target.cpp @@ -157,10 +157,10 @@ const std::string &Target::GetCompiler(const fs::path &source) const { const internal::Path & Target::GetCompiledSourcePath(const fs::path &source) const { - const auto fiter = current_object_files_.find(source.native()); + const auto fiter = current_object_files_.find(source); env::assert_fatal(fiter != current_object_files_.end(), fmt::format("{} not found", source.string())); - return current_object_files_.at(source.native()); + return current_object_files_.at(source); } internal::path_unordered_set Target::GetCompiledSources() const { @@ -185,53 +185,15 @@ void Target::Initialize() { // Rechecks void Target::RecheckPaths(const internal::path_unordered_set &previous_path, const internal::path_unordered_set ¤t_path) { - // * Compile sources / Target already requires rebuild - if (dirty_) { - return; - } - - // * Old path is removed - const bool removed = - internal::is_previous_paths_different(previous_path, current_path); - if (removed) { - PathRemoved(); - dirty_ = true; - return; - } - - for (const auto &path : current_path) { - auto iter = previous_path.find(path); - - if (iter == previous_path.end()) { - // * New path added - PathAdded(); - dirty_ = true; - } else { - // * Path is updated - if (path.GetLastWriteTimestamp() > iter->GetLastWriteTimestamp()) { - PathUpdated(); - dirty_ = true; - } else { - // * Do nothing - } - } - - if (dirty_) { - break; - } - } + BuilderInterface::RecheckPaths( + previous_path, current_path, [&]() { PathRemoved(); }, + [&]() { PathAdded(); }, [&]() { PathUpdated(); }); } void Target::RecheckDirs(const internal::fs_unordered_set &previous_dirs, const internal::fs_unordered_set ¤t_dirs) { - if (dirty_) { - return; - } - - if (previous_dirs != current_dirs) { - DirChanged(); - dirty_ = true; - } + RecheckChanged(previous_dirs, current_dirs, + std::bind(&Target::DirChanged, this)); } void Target::RecheckFlags( @@ -248,17 +210,4 @@ void Target::RecheckExternalLib( std::bind(&Target::ExternalLibChanged, this)); } -void Target::RecheckChanged(const std::unordered_set &previous, - const std::unordered_set ¤t, - const std::function &callback) { - if (dirty_) { - return; - } - - if (previous != current) { - callback(); - dirty_ = true; - } -} - } // namespace buildcc::base diff --git a/buildcc/lib/target/src/fbs/fbs_loader.cpp b/buildcc/lib/target/src/target/target_loader.cpp similarity index 73% rename from buildcc/lib/target/src/fbs/fbs_loader.cpp rename to buildcc/lib/target/src/target/target_loader.cpp index de743c7a..71acaa70 100644 --- a/buildcc/lib/target/src/fbs/fbs_loader.cpp +++ b/buildcc/lib/target/src/target/target_loader.cpp @@ -14,44 +14,14 @@ * limitations under the License. */ -#include "target/fbs_loader.h" - -#include "target_generated.h" +#include "target/target_loader.h" #include "env/logging.h" #include "env/util.h" -namespace fbs = schema::internal; - -namespace { -void ExtractPath( - const flatbuffers::Vector> - *fbs_paths, - buildcc::internal::path_unordered_set &out) { - if (fbs_paths == nullptr) { - return; - } - - for (auto iter = fbs_paths->begin(); iter != fbs_paths->end(); iter++) { - out.insert(buildcc::internal::Path::CreateNewPath( - iter->pathname()->c_str(), iter->last_write_timestamp())); - } -} - -template -void Extract(const flatbuffers::Vector> - *fbs_paths, - T &out) { - if (fbs_paths == nullptr) { - return; - } - - for (auto iter = fbs_paths->begin(); iter != fbs_paths->end(); iter++) { - out.insert(iter->str()); - } -} - -} // namespace +// Private +#include "target/private/schema_util.h" +#include "target_generated.h" namespace buildcc::internal { diff --git a/buildcc/lib/target/src/target/target_storer.cpp b/buildcc/lib/target/src/target/target_storer.cpp new file mode 100644 index 00000000..e2903b00 --- /dev/null +++ b/buildcc/lib/target/src/target/target_storer.cpp @@ -0,0 +1,91 @@ +/* + * Copyright 2021 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. + */ + +#include "target/target.h" + +#include +#include + +#include "env/logging.h" +#include "env/util.h" + +#include "target/private/schema_util.h" +#include "target_generated.h" + +namespace fbs = schema::internal; + +namespace { + +fbs::TargetType CreateFbsTargetType(buildcc::base::TargetType type) { + return (fbs::TargetType)type; +} + +} // namespace + +namespace buildcc::base { + +bool Target::Store() { + env::log_trace(name_, __FUNCTION__); + + flatbuffers::FlatBufferBuilder builder; + + auto fbs_target_type = CreateFbsTargetType(type_); + + auto fbs_source_files = + internal::CreateFbsVectorPath(builder, current_source_files_); + auto fbs_header_files = + internal::CreateFbsVectorPath(builder, current_header_files_); + auto fbs_lib_deps = internal::CreateFbsVectorPath(builder, current_lib_deps_); + + auto fbs_external_lib_deps = + internal::CreateFbsVectorString(builder, current_external_lib_deps_); + + auto fbs_include_dirs = + internal::CreateFbsVectorString(builder, current_include_dirs_); + auto fbs_lib_dirs = + internal::CreateFbsVectorString(builder, current_lib_dirs_); + + auto fbs_preprocessor_flags = + internal::CreateFbsVectorString(builder, current_preprocessor_flags_); + auto fbs_common_compile_flags = + internal::CreateFbsVectorString(builder, current_common_compile_flags_); + auto fbs_c_compile_flags = + internal::CreateFbsVectorString(builder, current_c_compile_flags_); + auto fbs_cpp_compile_flags = + internal::CreateFbsVectorString(builder, current_cpp_compile_flags_); + auto fbs_link_flags = + internal::CreateFbsVectorString(builder, current_link_flags_); + + auto fbs_compile_dependencies = + internal::CreateFbsVectorPath(builder, current_compile_dependencies_); + auto fbs_link_dependencies = + internal::CreateFbsVectorPath(builder, current_link_dependencies_); + + auto fbs_target = fbs::CreateTargetDirect( + builder, name_.c_str(), fbs_target_type, &fbs_source_files, + &fbs_header_files, &fbs_lib_deps, &fbs_external_lib_deps, + &fbs_include_dirs, &fbs_lib_dirs, &fbs_preprocessor_flags, + &fbs_common_compile_flags, &fbs_c_compile_flags, &fbs_cpp_compile_flags, + &fbs_link_flags, &fbs_compile_dependencies, &fbs_link_dependencies); + fbs::FinishTargetBuffer(builder, fbs_target); + + auto file_path = GetBinaryPath(); + return env::SaveFile(file_path.string().c_str(), + (const char *)builder.GetBufferPointer(), + builder.GetSize(), true); +} + +} // namespace buildcc::base diff --git a/buildcc/lib/target/test/target/CMakeLists.txt b/buildcc/lib/target/test/target/CMakeLists.txt index 978dc499..f63b0b27 100644 --- a/buildcc/lib/target/test/target/CMakeLists.txt +++ b/buildcc/lib/target/test/target/CMakeLists.txt @@ -8,6 +8,15 @@ target_link_libraries(target_interface INTERFACE mock_target ) +# Generator +add_executable(test_generator + test_generator.cpp +) +target_link_libraries(test_generator PRIVATE target_interface) + +add_test(NAME test_generator COMMAND test_generator + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +) # Test base target add_executable(test_base_target diff --git a/buildcc/lib/target/test/target/test_generator.cpp b/buildcc/lib/target/test/target/test_generator.cpp new file mode 100644 index 00000000..ced24dfe --- /dev/null +++ b/buildcc/lib/target/test/target/test_generator.cpp @@ -0,0 +1,377 @@ +#include "target/generator.h" + +#include + +#include "expect_command.h" +#include "expect_generator.h" + +#include "env/util.h" + +// NOTE, Make sure all these includes are AFTER the system and header includes +#include "CppUTest/CommandLineTestRunner.h" +#include "CppUTest/MemoryLeakDetectorNewMacros.h" +#include "CppUTest/TestHarness.h" +#include "CppUTest/Utest.h" +#include "CppUTestExt/MockSupport.h" + +// clang-format off +TEST_GROUP(GeneratorTestGroup) +{ + void teardown() { + mock().clear(); + } +}; +// clang-format on + +fs::path BUILD_DIR = "intermediate/generator"; + +TEST(GeneratorTestGroup, Generator_AddInfo) { + fs::path TEST_BUILD_DIR = BUILD_DIR / "AddInfo"; + fs::create_directories(TEST_BUILD_DIR); + + buildcc::base::Generator generator("custom_file_generator", TEST_BUILD_DIR); + generator.AddGenInfo(buildcc::base::UserGenInfo( + "gcc_1", + { + "data/dummy_main.c", + }, + {TEST_BUILD_DIR / "dummy_main.exe"}, + {"gcc -o intermediate/generator/AddInfo/dummy_main.exe " + "data/dummy_main.c"}, + true)); + + CHECK_THROWS(buildcc::env::assert_exception, + generator.AddGenInfo( + buildcc::base::UserGenInfo("gcc_1", {}, {}, {}, true))); +} + +TEST(GeneratorTestGroup, Generator_Build) { + fs::path TEST_BUILD_DIR = BUILD_DIR / "Build"; + fs::create_directories(TEST_BUILD_DIR); + + buildcc::base::Generator generator("custom_file_generator", TEST_BUILD_DIR); + generator.AddGenInfo(buildcc::base::UserGenInfo( + "gcc_1", + { + "data/dummy_main.c", + }, + {TEST_BUILD_DIR / "dummy_main.exe"}, + {"gcc -o intermediate/generator/Build/dummy_main.exe " + "data/dummy_main.c"}, + true)); + + buildcc::m::CommandExpect_Execute(1, true); + generator.Build(); + + mock().checkExpectations(); +} + +TEST(GeneratorTestGroup, Generator_Rebuild) { + fs::path TEST_BUILD_DIR = BUILD_DIR / "Rebuild"; + fs::create_directories(TEST_BUILD_DIR); + + { + buildcc::base::Generator generator("custom_file_generator", TEST_BUILD_DIR); + generator.AddGenInfo(buildcc::base::UserGenInfo( + "gcc_1", + { + "data/dummy_main.c", + }, + {TEST_BUILD_DIR / "dummy_main.exe"}, + {"gcc -o intermediate/generator/Rebuild/dummy_main.exe " + "data/dummy_main.c"}, + true)); + + buildcc::m::CommandExpect_Execute(1, true); + generator.Build(); + } + + { + buildcc::base::Generator generator("custom_file_generator", TEST_BUILD_DIR); + generator.AddGenInfo(buildcc::base::UserGenInfo( + "gcc_1", + { + "data/dummy_main.c", + }, + {TEST_BUILD_DIR / "dummy_main.exe"}, + {"gcc -o intermediate/generator/Rebuild/dummy_main.exe " + "data/dummy_main.c"}, + true)); + + generator.Build(); + } + + mock().checkExpectations(); +} + +TEST(GeneratorTestGroup, Generator_Rebuild_PreviousNotFound) { + fs::path TEST_BUILD_DIR = BUILD_DIR / "Rebuild_PreviousNotFound"; + fs::create_directories(TEST_BUILD_DIR); + + { + buildcc::base::Generator generator("custom_file_generator", TEST_BUILD_DIR); + generator.AddGenInfo(buildcc::base::UserGenInfo( + "gcc_1", + { + "data/dummy_main.c", + }, + {TEST_BUILD_DIR / "dummy_main.exe"}, + {"gcc -o " + "intermediate/generator/Rebuild_PreviousNotFound/dummy_main.exe " + "data/dummy_main.c"}, + true)); + + buildcc::m::CommandExpect_Execute(1, true); + generator.Build(); + } + + { + buildcc::base::Generator generator("custom_file_generator", TEST_BUILD_DIR); + generator.AddGenInfo(buildcc::base::UserGenInfo( + "gcc_1", + { + "data/dummy_main.c", + }, + {TEST_BUILD_DIR / "dummy_main.exe"}, + {"gcc -o " + "intermediate/generator/Rebuild_PreviousNotFound/dummy_main.exe " + "data/dummy_main.c"}, + true)); + + // Current Info is NEWER than old + generator.AddGenInfo(buildcc::base::UserGenInfo( + "gcc_2", + { + "data/dummy_main.c", + }, + {TEST_BUILD_DIR / "dummy_main.exe"}, + {"gcc -o " + "intermediate/generator/Rebuild_PreviousNotFound/dummy_main.exe " + "data/dummy_main.c"}, + true)); + + buildcc::m::CommandExpect_Execute(1, true); + generator.Build(); + } + + mock().checkExpectations(); +} + +TEST(GeneratorTestGroup, Generator_Rebuild_Inputs) { + fs::path TEST_BUILD_DIR = BUILD_DIR / "Rebuild_Inputs"; + fs::create_directories(TEST_BUILD_DIR); + + { + buildcc::base::Generator generator("custom_file_generator", TEST_BUILD_DIR); + generator.AddGenInfo(buildcc::base::UserGenInfo( + "gcc_1", + { + "data/dummy_main.c", + }, + {TEST_BUILD_DIR / "dummy_main.exe"}, + {"gcc -o intermediate/generator/Rebuild_Inputs/dummy_main.exe " + "data/dummy_main.c"}, + true)); + + buildcc::m::CommandExpect_Execute(1, true); + generator.Build(); + } + + { + buildcc::base::Generator generator("custom_file_generator", TEST_BUILD_DIR); + generator.AddGenInfo(buildcc::base::UserGenInfo( + "gcc_1", + { + "data/dummy_main.c", + "data/new_source.cpp", + }, + {TEST_BUILD_DIR / "dummy_main.exe"}, + {"gcc -o intermediate/generator/Rebuild_Inputs/dummy_main.exe " + "data/dummy_main.c"}, + true)); + + buildcc::base::m::GeneratorExpect_InputAdded(1, &generator); + buildcc::m::CommandExpect_Execute(1, true); + generator.Build(); + } + + { + buildcc::base::Generator generator("custom_file_generator", TEST_BUILD_DIR); + generator.AddGenInfo(buildcc::base::UserGenInfo( + "gcc_1", + { + "data/new_source.cpp", + }, + {TEST_BUILD_DIR / "dummy_main.exe"}, + {"gcc -o intermediate/generator/Rebuild_Inputs/dummy_main.exe " + "data/dummy_main.c"}, + true)); + + buildcc::base::m::GeneratorExpect_InputRemoved(1, &generator); + buildcc::m::CommandExpect_Execute(1, true); + generator.Build(); + } + + sleep(1); + bool saved = buildcc::env::SaveFile("data/new_source.cpp", "", false); + CHECK_TRUE(saved); + + { + buildcc::base::Generator generator("custom_file_generator", TEST_BUILD_DIR); + generator.AddGenInfo(buildcc::base::UserGenInfo( + "gcc_1", + { + "data/new_source.cpp", + }, + {TEST_BUILD_DIR / "dummy_main.exe"}, + {"gcc -o intermediate/generator/Rebuild_Inputs/dummy_main.exe " + "data/dummy_main.c"}, + true)); + + buildcc::base::m::GeneratorExpect_InputUpdated(1, &generator); + buildcc::m::CommandExpect_Execute(1, true); + generator.Build(); + } + + mock().checkExpectations(); +} + +TEST(GeneratorTestGroup, Generator_Rebuild_Outputs) { + fs::path TEST_BUILD_DIR = BUILD_DIR / "Rebuild_Outputs"; + fs::create_directories(TEST_BUILD_DIR); + + { + buildcc::base::Generator generator("custom_file_generator", TEST_BUILD_DIR); + generator.AddGenInfo(buildcc::base::UserGenInfo( + "gcc_1", + { + "data/dummy_main.c", + }, + { + TEST_BUILD_DIR / "dummy_main.o", + TEST_BUILD_DIR / "dummy_main.exe", + }, + {"gcc -o intermediate/generator/Rebuild_Outputs/dummy_main.exe " + "data/dummy_main.c"}, + true)); + + buildcc::m::CommandExpect_Execute(1, true); + generator.Build(); + } + + { + buildcc::base::Generator generator("custom_file_generator", TEST_BUILD_DIR); + generator.AddGenInfo(buildcc::base::UserGenInfo( + "gcc_1", + { + "data/dummy_main.c", + }, + {TEST_BUILD_DIR / "dummy_main.exe"}, + {"gcc -o intermediate/generator/Rebuild_Outputs/dummy_main.exe " + "data/dummy_main.c"}, + true)); + + buildcc::base::m::GeneratorExpect_OutputChanged(1, &generator); + buildcc::m::CommandExpect_Execute(1, true); + generator.Build(); + } + + mock().checkExpectations(); +} + +TEST(GeneratorTestGroup, Generator_Rebuild_Commands) { + fs::path TEST_BUILD_DIR = BUILD_DIR / "Rebuild_Commands"; + fs::create_directories(TEST_BUILD_DIR); + + { + buildcc::base::Generator generator("custom_file_generator", TEST_BUILD_DIR); + generator.AddGenInfo(buildcc::base::UserGenInfo( + "gcc_1", + { + "data/dummy_main.c", + }, + { + TEST_BUILD_DIR / "dummy_main.o", + TEST_BUILD_DIR / "dummy_main.exe", + }, + {"gcc -o intermediate/generator/Rebuild_Commands/dummy_main.exe " + "data/dummy_main.c"}, + true)); + + buildcc::m::CommandExpect_Execute(1, true); + generator.Build(); + } + + { + buildcc::base::Generator generator("custom_file_generator", TEST_BUILD_DIR); + generator.AddGenInfo(buildcc::base::UserGenInfo( + "gcc_1", + { + "data/dummy_main.c", + }, + { + TEST_BUILD_DIR / "dummy_main.o", + TEST_BUILD_DIR / "dummy_main.exe", + }, + { + "gcc -c -o intermediate/generator/Rebuild_Commands/dummy_main.o " + "data/dummy_main.c", + "gcc -o intermediate/generator/Rebuild_Commands/dummy_main.exe " + "intermediate/generator/Rebuild_Commands/dummy_main.o", + }, + true)); + + buildcc::base::m::GeneratorExpect_CommandChanged(1, &generator); + buildcc::m::CommandExpect_Execute(1, true); + buildcc::m::CommandExpect_Execute(1, true); + generator.Build(); + } + + mock().checkExpectations(); +} + +// Input for second generator is output of first generator +TEST(GeneratorTestGroup, Generator_DoubleDependency) { + fs::path TEST_BUILD_DIR = BUILD_DIR / "DoubleDependency"; + fs::create_directories(TEST_BUILD_DIR); + + buildcc::base::Generator ogen("custom_object_generator", TEST_BUILD_DIR); + ogen.AddGenInfo(buildcc::base::UserGenInfo( + "gcc_1", + { + "data/dummy_main.c", + }, + {TEST_BUILD_DIR / "dummy_main.o"}, + {"gcc -c -o intermediate/generator/DoubleDependency/dummy_main.o " + "data/dummy_main.c"}, + true)); + + buildcc::m::CommandExpect_Execute(1, true); + ogen.Build(); + + // * NOTE, dummy_main.o is an input to `custom_exe_generator` + buildcc::env::SaveFile((TEST_BUILD_DIR / "dummy_main.o").string().c_str(), "", + false); + + buildcc::base::Generator egen("custom_exe_generator", TEST_BUILD_DIR); + egen.AddGenInfo(buildcc::base::UserGenInfo( + "gcc_1", + { + TEST_BUILD_DIR / "dummy_main.o", + }, + {TEST_BUILD_DIR / "dummy_main.o"}, + {"gcc -o intermediate/generator/DoubleDependency/dummy_main.exe " + "intermediate/generator/DoubleDependency/dummy_main.o"}, + true)); + + buildcc::m::CommandExpect_Execute(1, true); + egen.Build(); + + mock().checkExpectations(); +} + +int main(int ac, char **av) { + fs::remove_all(BUILD_DIR); + fs::create_directories(BUILD_DIR); + return CommandLineTestRunner::RunAllTests(ac, av); +} diff --git a/buildcc/lib/target/test/target/test_target_c_compile_flags.cpp b/buildcc/lib/target/test/target/test_target_c_compile_flags.cpp index b9299ac6..7d6c0090 100644 --- a/buildcc/lib/target/test/target/test_target_c_compile_flags.cpp +++ b/buildcc/lib/target/test/target/test_target_c_compile_flags.cpp @@ -8,7 +8,7 @@ #include "env/env.h" // -#include "target/fbs_loader.h" +#include "target/target_loader.h" // Third Party diff --git a/buildcc/lib/target/test/target/test_target_cpp_compile_flags.cpp b/buildcc/lib/target/test/target/test_target_cpp_compile_flags.cpp index 27df7c16..6d67a46c 100644 --- a/buildcc/lib/target/test/target/test_target_cpp_compile_flags.cpp +++ b/buildcc/lib/target/test/target/test_target_cpp_compile_flags.cpp @@ -8,7 +8,7 @@ #include "env/env.h" // -#include "target/fbs_loader.h" +#include "target/target_loader.h" // Third Party diff --git a/buildcc/lib/target/test/target/test_target_external_lib.cpp b/buildcc/lib/target/test/target/test_target_external_lib.cpp index 6ffd0e90..2c558d99 100644 --- a/buildcc/lib/target/test/target/test_target_external_lib.cpp +++ b/buildcc/lib/target/test/target/test_target_external_lib.cpp @@ -7,7 +7,7 @@ #include "target/target.h" -#include "target/fbs_loader.h" +#include "target/target_loader.h" #include #include diff --git a/buildcc/lib/target/test/target/test_target_lib_dep.cpp b/buildcc/lib/target/test/target/test_target_lib_dep.cpp index 7e3ebe0c..eff66c1d 100644 --- a/buildcc/lib/target/test/target/test_target_lib_dep.cpp +++ b/buildcc/lib/target/test/target/test_target_lib_dep.cpp @@ -9,7 +9,7 @@ #include "target/target.h" // -#include "target/fbs_loader.h" +#include "target/target_loader.h" #include #include diff --git a/buildcc/lib/target/test/target/test_target_link_flags.cpp b/buildcc/lib/target/test/target/test_target_link_flags.cpp index d76f290d..16631d00 100644 --- a/buildcc/lib/target/test/target/test_target_link_flags.cpp +++ b/buildcc/lib/target/test/target/test_target_link_flags.cpp @@ -8,7 +8,7 @@ #include "env/env.h" // -#include "target/fbs_loader.h" +#include "target/target_loader.h" // Third Party diff --git a/buildcc/lib/target/test/target/test_target_preprocessor_flags.cpp b/buildcc/lib/target/test/target/test_target_preprocessor_flags.cpp index 578614a0..a0462376 100644 --- a/buildcc/lib/target/test/target/test_target_preprocessor_flags.cpp +++ b/buildcc/lib/target/test/target/test_target_preprocessor_flags.cpp @@ -8,7 +8,7 @@ #include "env/env.h" // -#include "target/fbs_loader.h" +#include "target/target_loader.h" // Third Party diff --git a/buildcc/schema/CMakeLists.txt b/buildcc/schema/CMakeLists.txt index c1a6b604..cbbe4b23 100644 --- a/buildcc/schema/CMakeLists.txt +++ b/buildcc/schema/CMakeLists.txt @@ -3,10 +3,12 @@ set(SCHEMA_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated CACHE PATH "Generate set(FBS_FILES ${CMAKE_CURRENT_SOURCE_DIR}/path.fbs + ${CMAKE_CURRENT_SOURCE_DIR}/generator.fbs ${CMAKE_CURRENT_SOURCE_DIR}/target.fbs ) set(FBS_GEN_FILES ${SCHEMA_BUILD_DIR}/path_generated.h + ${SCHEMA_BUILD_DIR}/generator_generated.h ${SCHEMA_BUILD_DIR}/target_generated.h ) set(FBS_GEN_OPTIONS diff --git a/buildcc/schema/generator.fbs b/buildcc/schema/generator.fbs new file mode 100644 index 00000000..0eef1713 --- /dev/null +++ b/buildcc/schema/generator.fbs @@ -0,0 +1,33 @@ +// Copyright 2021 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. + +include "path.fbs"; + +namespace schema.internal; + +table GenInfo { + name:string (key); + inputs:[Path]; + outputs:[string]; + commands:[string]; + parallel:bool; +} + +// Each generator consists of many relational files of [input] - [output] - [commands] +table Generator { + name:string (key); + info:[GenInfo]; +} + +root_type Generator;