diff --git a/.github/workflows/linux_gcc_cmake_build.yml b/.github/workflows/linux_gcc_cmake_build.yml index 4b25ca9c..2f3de693 100644 --- a/.github/workflows/linux_gcc_cmake_build.yml +++ b/.github/workflows/linux_gcc_cmake_build.yml @@ -26,7 +26,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true @@ -89,7 +89,7 @@ jobs: cpack -C Release -G ZIP - name: Upload CPack - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: "BuildExe_Linux" path: ${{github.workspace}}/${{env.BUILD_FOLDER_DEV_ALL}}/BuildCC-0.1.1-Linux.zip @@ -156,7 +156,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true @@ -191,7 +191,7 @@ jobs: working-directory: ${{github.workspace}}/${{env.BUILD_FOLDER_DEV_SINGLE}} run: cmake --build . --target cppcheck_static_analysis - - name: Build Debug for test + - name: Build Debug and test # Linux has 2 cores run: | cmake --build --list-presets @@ -298,7 +298,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true diff --git a/.github/workflows/msvc-analysis.yml b/.github/workflows/msvc-analysis.yml index a2628f34..5590b87c 100644 --- a/.github/workflows/msvc-analysis.yml +++ b/.github/workflows/msvc-analysis.yml @@ -25,11 +25,11 @@ env: jobs: analyze: name: Analyze - runs-on: windows-latest + runs-on: windows-2019 steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: submodules: true @@ -61,7 +61,7 @@ jobs: # Upload SARIF file as an Artifact to download and view - name: Upload SARIF as an Artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: sarif-file path: ${{ steps.run-analysis.outputs.sarif }} diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 5d6eecae..59c8c35a 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -13,7 +13,7 @@ jobs: deploy: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true diff --git a/.github/workflows/win_cmake_build.yml b/.github/workflows/win_cmake_build.yml index 2bcadad4..3844fb97 100644 --- a/.github/workflows/win_cmake_build.yml +++ b/.github/workflows/win_cmake_build.yml @@ -24,7 +24,7 @@ jobs: runs-on: windows-2019 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true @@ -44,7 +44,13 @@ jobs: cmake --list-presets cmake --preset=${{env.BUILD_MSVC_PRESET}} - - name: Build + - name: Build Debug and test + working-directory: ${{github.workspace}}/${{env.BUILD_FOLDER_MSVC_DEV_ALL}} + run: | + cmake --build . --parallel 2 --config Debug + ctest . --parallel 2 -C Debug + + - name: Build Release # Linux has 2 cores run: | cmake --build --list-presets @@ -82,7 +88,7 @@ jobs: cpack -C Release -G ZIP - name: Upload CPack - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: "BuildExe_Win" path: ${{github.workspace}}/${{env.BUILD_FOLDER_MSVC_DEV_ALL}}/BuildCC-0.1.1-win64.zip @@ -156,7 +162,7 @@ jobs: runs-on: windows-2019 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true diff --git a/.gitmodules b/.gitmodules index 7deee242..97699a1c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ -[submodule "flatbuffers"] - path = third_party/flatbuffers - url = https://github.com/google/flatbuffers.git +[submodule "json"] + path = third_party/json + url = https://github.com/nlohmann/json.git [submodule "spdlog"] path = third_party/spdlog url = https://github.com/gabime/spdlog.git @@ -19,3 +19,6 @@ [submodule "tiny-process-library"] path = third_party/tiny-process-library url = https://gitlab.com/eidheim/tiny-process-library.git +[submodule "optional"] + path = third_party/tl_optional + url = https://github.com/TartanLlama/optional.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 68e47918..669219e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,8 +12,6 @@ project(BuildCC # User options option(BUILDCC_INSTALL "Enable BuildCC Installation" ON) -option(BUILDCC_FLATBUFFERS_FLATC "Build Flatbuffer::Flatc Compiler" ON) - option(BUILDCC_BUILD_AS_SINGLE_LIB "Build all internal libs and modules as part of the buildcc library" ON) option(BUILDCC_BUILD_AS_INTERFACE "Build all internal libs and modules seperately and link" OFF) @@ -34,7 +32,6 @@ option(BUILDCC_DOCUMENTATION "Enable Documentation" OFF) # Compiler options # NOTE, This option is required for clang compilers, architecture x86_64-pc-windows-msvc -# Flatbuffers library uses `std::system` internally which causes a deprecated error option(BUILDCC_NO_DEPRECATED "Disable Deprecated" OFF) if (${BUILDCC_NO_DEPRECATED}) add_compile_options("-Wno-deprecated") @@ -42,7 +39,7 @@ endif() # Testing set(BUILD_TESTING OFF CACHE BOOL "Third Party modules use these options") -if (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU" AND ${BUILDCC_TESTING}) +if (${BUILDCC_TESTING}) set(TESTING ON) message("Enabling unit-testing") message("Compiler identification: ${CMAKE_CXX_COMPILER_ID}") @@ -72,12 +69,13 @@ include(cmake/tool/cppcheck.cmake) include(cmake/tool/doxygen.cmake) # Libraries -include(cmake/target/flatbuffers.cmake) +include(cmake/target/json.cmake) include(cmake/target/fmt.cmake) include(cmake/target/spdlog.cmake) include(cmake/target/cli11.cmake) include(cmake/target/taskflow.cmake) include(cmake/target/tpl.cmake) +include(cmake/target/tl_optional.cmake) if (${TESTING}) include(cmake/target/cpputest.cmake) @@ -85,7 +83,7 @@ endif() # Coverage -if (${TESTING}) +if (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU" AND ${TESTING}) include(cmake/coverage/lcov.cmake) include(cmake/coverage/gcovr.cmake) endif() diff --git a/CMakePresets.json b/CMakePresets.json index 68fb595c..6b1d4c4b 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -17,7 +17,6 @@ "CMAKE_C_COMPILER": "gcc", "CMAKE_CXX_COMPILER": "g++", "BUILDCC_INSTALL": true, - "BUILDCC_FLATBUFFERS_FLATC": true, "BUILDCC_BUILD_AS_SINGLE_LIB": true, "BUILDCC_BUILD_AS_INTERFACE": true, "BUILDCC_BUILDEXE": true, @@ -42,7 +41,6 @@ "CMAKE_C_COMPILER": "gcc", "CMAKE_CXX_COMPILER": "g++", "BUILDCC_INSTALL": true, - "BUILDCC_FLATBUFFERS_FLATC": true, "BUILDCC_BUILD_AS_SINGLE_LIB": true, "BUILDCC_BUILD_AS_INTERFACE": false, "BUILDCC_BUILDEXE": true, @@ -67,7 +65,6 @@ "CMAKE_C_COMPILER": "gcc", "CMAKE_CXX_COMPILER": "g++", "BUILDCC_INSTALL": true, - "BUILDCC_FLATBUFFERS_FLATC": true, "BUILDCC_BUILD_AS_SINGLE_LIB": false, "BUILDCC_BUILD_AS_INTERFACE": true, "BUILDCC_BUILDEXE": false, @@ -92,7 +89,6 @@ "CMAKE_C_COMPILER": "clang", "CMAKE_CXX_COMPILER": "clang++", "BUILDCC_INSTALL": true, - "BUILDCC_FLATBUFFERS_FLATC": true, "BUILDCC_BUILD_AS_SINGLE_LIB": true, "BUILDCC_BUILD_AS_INTERFACE": true, "BUILDCC_BUILDEXE": true, @@ -114,14 +110,13 @@ "binaryDir": "${sourceDir}/_build_msvc_dev_all", "cacheVariables": { "BUILDCC_INSTALL": true, - "BUILDCC_FLATBUFFERS_FLATC": true, "BUILDCC_BUILD_AS_SINGLE_LIB": true, "BUILDCC_BUILD_AS_INTERFACE": true, "BUILDCC_BUILDEXE": true, "BUILDCC_BOOTSTRAP_THROUGH_CMAKE": true, "BUILDCC_PRECOMPILE_HEADERS": true, "BUILDCC_EXAMPLES": true, - "BUILDCC_TESTING": false, + "BUILDCC_TESTING": true, "BUILDCC_CLANGTIDY": false, "BUILDCC_CPPCHECK": false, "BUILDCC_DOCUMENTATION": false, @@ -136,7 +131,6 @@ "binaryDir": "${sourceDir}/_build_msvc_analysis", "cacheVariables": { "BUILDCC_INSTALL": false, - "BUILDCC_FLATBUFFERS_FLATC": true, "BUILDCC_BUILD_AS_SINGLE_LIB": true, "BUILDCC_BUILD_AS_INTERFACE": false, "BUILDCC_BUILDEXE": false, diff --git a/DEPENDENCIES.md b/DEPENDENCIES.md index 224bf0ab..b19d3a63 100644 --- a/DEPENDENCIES.md +++ b/DEPENDENCIES.md @@ -1,10 +1,22 @@ # Dependencies +These third party libraries are added as submodules since they aren't meant to be modified by this project. + +### Adding a submodule + +`git submodule add [git_url] third_party/[foldername]` + +### Removing a submodule + +- `git rm --cached path_to_submodule` (no trailing slash) +- Delete relevant line from `.gitmodules` file +- Delete relevant section from `.git/config` file + ## Main - fmt (Formatting) - spdlog (Logging) -- flatbuffers (Serialization) +- json (Serialization) - CLI11 (Argument Parsing) - Taskflow (Parallel Programming) @@ -12,6 +24,13 @@ - cpputest (Unit Testing / Mocking) +## Utility + +- tl_optional (Better optional support) + - Synced with branch origin/master (May 2, 2021) + - Commit Id: c28fcf74d207fc667c4ed3dbae4c251ea551c8c1 + - Needed fix: #45 + ## Tools - [x] clangformat (auto) diff --git a/README.md b/README.md index f6f5bf57..d3abdfa3 100644 --- a/README.md +++ b/README.md @@ -27,13 +27,14 @@ Build C, C++ and ASM files in C++ - `C++17 filesystem` library support - `C++11 thread` library support - Third Party Libraries (See License below) - - Flatbuffers v2.0.0 + - Nlohmann::Json v3.11.2 - Taskflow v3.1.0 - CLI11 v2.1.0 - Tiny Process Library v2.0.4 - fmt v8.0.1 - spdlog v1.9.2 - CppUTest v4.0 + - Tl::Optional (master) # General Information @@ -170,11 +171,12 @@ _BuildCC_ is licensed under the Apache License, Version 2.0. See [LICENSE](LICEN > Developers who would like to suggest an alternative library, raise an issue with the **license** and **advantages** clearly outlined. -- [Fmtlib](https://github.com/fmtlib/fmt) (Formatting) [MIT License] [Header Only] -- [Spdlog](https://github.com/gabime/spdlog) (Logging) [MIT License] [Header Only] +- [Fmtlib](https://github.com/fmtlib/fmt) (Formatting) [MIT License] +- [Spdlog](https://github.com/gabime/spdlog) (Logging) [MIT License] - [Tiny Process Library](https://gitlab.com/eidheim/tiny-process-library) (Process handling) [MIT License] - [Taskflow](https://github.com/taskflow/taskflow) (Parallel Programming) [MIT License] [Header Only] - See also [3rd-Party](https://github.com/taskflow/taskflow/tree/master/3rd-party) used by Taskflow -- [Flatbuffers](https://github.com/google/flatbuffers) (Serialization) [Apache-2.0 License] [Header Only] +- [Nlohmann::Json](https://github.com/nlohmann/json) (JSON Serialization) [MIT License] [Header Only] - [CLI11](https://github.com/CLIUtils/CLI11) (Argument Parsing) [BSD-3-Clause License] [Header Only] - [CppUTest](https://github.com/cpputest/cpputest) (Unit Testing/Mocking) [BSD-3-Clause License] +- [Tl::Optional](https://github.com/TartanLlama/optional) (Optional support) [CC0-1.0 License] diff --git a/bootstrap/CMakeLists.txt b/bootstrap/CMakeLists.txt index 4878df29..924fa351 100644 --- a/bootstrap/CMakeLists.txt +++ b/bootstrap/CMakeLists.txt @@ -2,12 +2,13 @@ add_executable(buildcc_lib_bootstrap main.buildcc.cpp ) target_sources(buildcc_lib_bootstrap PRIVATE - src/build_flatbuffers.cpp + src/build_nlohmann_json.cpp src/build_cli11.cpp src/build_fmtlib.cpp src/build_spdlog.cpp src/build_taskflow.cpp src/build_tpl.cpp + src/build_tl_optional.cpp src/build_buildcc.cpp ) diff --git a/bootstrap/include/bootstrap/build_buildcc.h b/bootstrap/include/bootstrap/build_buildcc.h index 340066a5..bef6fdf3 100644 --- a/bootstrap/include/bootstrap/build_buildcc.h +++ b/bootstrap/include/bootstrap/build_buildcc.h @@ -20,20 +20,19 @@ #include "buildcc.h" #include "build_cli11.h" -#include "build_flatbuffers.h" #include "build_fmtlib.h" +#include "build_nlohmann_json.h" #include "build_spdlog.h" #include "build_taskflow.h" +#include "build_tl_optional.h" #include "build_tpl.h" namespace buildcc { -void schema_gen_cb(BaseGenerator &generator, const BaseTarget &flatc_exe); - -void buildcc_cb(BaseTarget &target, const BaseGenerator &schema_gen, - const TargetInfo &flatbuffers_ho, const TargetInfo &fmt_ho, - const TargetInfo &spdlog_ho, const TargetInfo &cli11_ho, - const TargetInfo &taskflow_ho, const BaseTarget &tpl); +void buildcc_cb(BaseTarget &target, const TargetInfo &nlohmann_json_ho, + const TargetInfo &fmt_ho, const TargetInfo &spdlog_ho, + const TargetInfo &cli11_ho, const TargetInfo &taskflow_ho, + const TargetInfo &tl_optional_ho, const BaseTarget &tpl); /** * @brief @@ -42,26 +41,25 @@ void buildcc_cb(BaseTarget &target, const BaseGenerator &schema_gen, class BuildBuildCC { public: // TargetInfo / Header Only - static constexpr const char *const kFlatbuffersHoName = "flatbuffers_ho"; + static constexpr const char *const kNlohmannJsonHoName = "nlohmann_json_ho"; static constexpr const char *const kCli11HoName = "cli11_ho"; static constexpr const char *const kFmtHoName = "fmtlib_ho"; static constexpr const char *const kSpdlogHoName = "spdlog_ho"; static constexpr const char *const kTaskflowHoName = "taskflow_ho"; + static constexpr const char *const kTlOptionalHoName = "tl_optional_ho"; // Executable static constexpr const char *const kFlatcExeName = "flatc"; - // Generator - static constexpr const char *const kSchemaGenName = "schema_gen"; - // Libraries static constexpr const char *const kTplLibName = "libtpl"; static constexpr const char *const kBuildccLibName = "libbuildcc"; public: - BuildBuildCC(Register ®, const BaseToolchain &toolchain, - const TargetEnv &env) - : reg_(reg), toolchain_(toolchain), env_(env) {} + BuildBuildCC(const BaseToolchain &toolchain, const TargetEnv &env) + : toolchain_(toolchain), env_(env) { + Initialize(); + } BuildBuildCC(const BuildBuildCC &) = delete; void Setup(const ArgToolchainState &state); @@ -75,11 +73,25 @@ class BuildBuildCC { } private: - Register ®_; + void Initialize(); + TargetInfo &GetNlohmannJsonHo() { + return storage_.Ref(kNlohmannJsonHoName); + } + TargetInfo &GetCli11Ho() { return storage_.Ref(kCli11HoName); } + TargetInfo &GetFmtHo() { return storage_.Ref(kFmtHoName); } + TargetInfo &GetSpdlogHo() { return storage_.Ref(kSpdlogHoName); } + TargetInfo &GetTaskflowHo() { + return storage_.Ref(kTaskflowHoName); + } + TargetInfo &GetTlOptionalHo() { + return storage_.Ref(kTlOptionalHoName); + } + +private: const BaseToolchain &toolchain_; TargetEnv env_; - PersistentStorage storage_; + ScopedStorage storage_; }; } // namespace buildcc diff --git a/buildcc/lib/target/src/generator/recheck_states.cpp b/bootstrap/include/bootstrap/build_nlohmann_json.h similarity index 76% rename from buildcc/lib/target/src/generator/recheck_states.cpp rename to bootstrap/include/bootstrap/build_nlohmann_json.h index 5196c81f..135275f0 100644 --- a/buildcc/lib/target/src/generator/recheck_states.cpp +++ b/bootstrap/include/bootstrap/build_nlohmann_json.h @@ -14,15 +14,15 @@ * limitations under the License. */ -#include "target/generator.h" +#ifndef BOOTSTRAP_BUILD_NLOHMANN_JSON_H_ +#define BOOTSTRAP_BUILD_NLOHMANN_JSON_H_ -namespace buildcc { +#include "buildcc.h" -void Generator::InputRemoved() {} -void Generator::InputAdded() {} -void Generator::InputUpdated() {} +namespace buildcc { -void Generator::OutputChanged() {} -void Generator::CommandChanged() {} +void nlohmann_json_ho_cb(TargetInfo &info); } // namespace buildcc + +#endif diff --git a/buildcc/lib/target/include/target/common/target_type.h b/bootstrap/include/bootstrap/build_tl_optional.h similarity index 72% rename from buildcc/lib/target/include/target/common/target_type.h rename to bootstrap/include/bootstrap/build_tl_optional.h index ff8d05a1..4ca39e01 100644 --- a/buildcc/lib/target/include/target/common/target_type.h +++ b/bootstrap/include/bootstrap/build_tl_optional.h @@ -14,17 +14,15 @@ * limitations under the License. */ -#ifndef TARGET_COMMON_TARGET_TYPE_H_ -#define TARGET_COMMON_TARGET_TYPE_H_ +#ifndef BOOTSTRAP_BUILD_TL_OPTIONAL_H_ +#define BOOTSTRAP_BUILD_TL_OPTIONAL_H_ + +#include "buildcc.h" namespace buildcc { -enum class TargetType { - Executable, ///< Executable Target type - StaticLibrary, ///< Static library target type - DynamicLibrary, ///< Dynamic library target type -}; +void tl_optional_ho_cb(TargetInfo &info); -} +} // namespace buildcc #endif diff --git a/bootstrap/include/bootstrap/build_tpl.h b/bootstrap/include/bootstrap/build_tpl.h index 9695f7d3..9d00cc5e 100644 --- a/bootstrap/include/bootstrap/build_tpl.h +++ b/bootstrap/include/bootstrap/build_tpl.h @@ -21,13 +21,7 @@ namespace buildcc { -struct TplConfig { - TplConfig() = default; - - OsId os_id{OsId::Linux}; -}; - -void tpl_cb(BaseTarget &target, const TplConfig &config = TplConfig()); +void tpl_cb(BaseTarget &target); } // namespace buildcc diff --git a/bootstrap/main.buildcc.cpp b/bootstrap/main.buildcc.cpp index 8ec1d5a7..4eafe255 100644 --- a/bootstrap/main.buildcc.cpp +++ b/bootstrap/main.buildcc.cpp @@ -17,10 +17,10 @@ #include "buildcc.h" #include "bootstrap/build_cli11.h" -#include "bootstrap/build_flatbuffers.h" #include "bootstrap/build_fmtlib.h" #include "bootstrap/build_spdlog.h" #include "bootstrap/build_taskflow.h" +#include "bootstrap/build_tl_optional.h" #include "bootstrap/build_tpl.h" #include "bootstrap/build_buildcc.h" @@ -33,37 +33,36 @@ static void hybrid_simple_example_cb(BaseTarget &target, const BaseTarget &libbuildcc); int main(int argc, char **argv) { - Args args; ArgToolchain custom_toolchain_arg; - args.AddToolchain("host", "Host Toolchain", custom_toolchain_arg); - args.Parse(argc, argv); + Args::Init() + .AddToolchain("host", "Host Toolchain", custom_toolchain_arg) + .Parse(argc, argv); - Register reg(args); - reg.Clean(clean_cb); - - BaseToolchain toolchain = custom_toolchain_arg.ConstructToolchain(); + Reg::Init(); + Reg::Call(Args::Clean()).Func(clean_cb); + auto &toolchain = custom_toolchain_arg.ConstructToolchain(); BuildBuildCC buildcc( - reg, toolchain, - TargetEnv(env::get_project_root_dir(), env::get_project_build_dir())); - buildcc.Setup(custom_toolchain_arg.state); + toolchain, TargetEnv(Project::GetRootDir(), Project::GetBuildDir())); + auto &buildcc_lib = buildcc.GetBuildcc(); - const auto &buildcc_lib = buildcc.GetBuildcc(); ExecutableTarget_generic buildcc_hybrid_simple_example( "buildcc_hybrid_simple_example", toolchain, "example/hybrid/simple"); - reg.Build(custom_toolchain_arg.state, hybrid_simple_example_cb, - buildcc_hybrid_simple_example, buildcc_lib); - reg.Dep(buildcc_hybrid_simple_example, buildcc_lib); + Reg::Toolchain(custom_toolchain_arg.state) + .Func([&]() { toolchain.Verify(); }) + .BuildPackage(buildcc) + .Build(hybrid_simple_example_cb, buildcc_hybrid_simple_example, + buildcc_lib) + .Dep(buildcc_hybrid_simple_example, buildcc_lib); // Runners - reg.RunBuild(); - reg.RunTest(); + Reg::Run(); // - Clang Compile Commands plugin::ClangCompileCommands({&buildcc_lib}).Generate(); // - Plugin Graph - std::string output = reg.GetTaskflow().dump(); + std::string output = Reg::GetTaskflow().dump(); const bool saved = env::save_file("graph.dot", output, false); env::assert_fatal(saved, "Could not save graph.dot file"); diff --git a/bootstrap/src/build_buildcc.cpp b/bootstrap/src/build_buildcc.cpp index 23fe6df6..38c2072c 100644 --- a/bootstrap/src/build_buildcc.cpp +++ b/bootstrap/src/build_buildcc.cpp @@ -18,62 +18,48 @@ namespace buildcc { -void schema_gen_cb(BaseGenerator &generator, const BaseTarget &flatc_exe) { - generator.AddInput("{gen_root_dir}/path.fbs", "path_fbs"); - generator.AddInput("{gen_root_dir}/generator.fbs", "generator_fbs"); - generator.AddInput("{gen_root_dir}/target.fbs", "target_fbs"); - - generator.AddOutput("{gen_build_dir}/path_generated.h"); - generator.AddOutput("{gen_build_dir}/generator_generated.h"); - generator.AddOutput("{gen_build_dir}/target_generated.h"); - - generator.AddDefaultArguments({ - {"flatc_compiler", fmt::format("{}", flatc_exe.GetTargetPath())}, - }); - // generator.AddCommand("{flatc_compiler} --help"); - generator.AddCommand( - "{flatc_compiler} -o {gen_build_dir} -I {gen_root_dir} --gen-object-api " - "--cpp {path_fbs} {generator_fbs} {target_fbs}"); - - generator.Build(); -} - -void buildcc_cb(BaseTarget &target, const BaseGenerator &schema_gen, - const TargetInfo &flatbuffers_ho, const TargetInfo &fmt_ho, - const TargetInfo &spdlog_ho, const TargetInfo &cli11_ho, - const TargetInfo &taskflow_ho, const BaseTarget &tpl) { +void buildcc_cb(BaseTarget &target, const TargetInfo &nlohmann_json_ho, + const TargetInfo &fmt_ho, const TargetInfo &spdlog_ho, + const TargetInfo &cli11_ho, const TargetInfo &taskflow_ho, + const TargetInfo &tl_optional_ho, const BaseTarget &tpl) { // NOTE, Build as single lib target.AddIncludeDir("", true); - const std::string &schema_build_dir = - schema_gen.GetValueByIdentifier("gen_build_dir"); - target.AddIncludeDirAbsolute(schema_build_dir, true); // ENV target.GlobSources("lib/env/src"); target.AddIncludeDir("lib/env/include"); target.GlobHeaders("lib/env/include/env"); + // SCHEMA + target.GlobSources("schema/src"); + target.AddIncludeDir("schema/include"); + target.GlobHeaders("schema/include/schema"); + target.GlobHeaders("schema/include/schema/interface"); + // TOOLCHAIN target.GlobSources("lib/toolchain/src/api"); + target.GlobSources("lib/toolchain/src/common"); + target.GlobSources("lib/toolchain/src/toolchain"); target.AddIncludeDir("lib/toolchain/include"); target.GlobHeaders("lib/toolchain/include/toolchain"); target.GlobHeaders("lib/toolchain/include/toolchain/api"); + target.GlobHeaders("lib/toolchain/include/toolchain/common"); // TARGET target.GlobSources("lib/target/src/common"); + target.GlobSources("lib/target/src/custom_generator"); target.GlobSources("lib/target/src/generator"); target.GlobSources("lib/target/src/api"); + target.GlobSources("lib/target/src/target_info"); target.GlobSources("lib/target/src/target"); target.GlobSources("lib/target/src/target/friend"); target.AddIncludeDir("lib/target/include"); target.GlobHeaders("lib/target/include/target"); target.GlobHeaders("lib/target/include/target/api"); - target.GlobHeaders("lib/target/include/target/base"); target.GlobHeaders("lib/target/include/target/common"); target.GlobHeaders("lib/target/include/target/friend"); target.GlobHeaders("lib/target/include/target/interface"); - target.GlobHeaders("lib/target/include/target/private"); // ARGS target.GlobSources("lib/args/src"); @@ -81,6 +67,7 @@ void buildcc_cb(BaseTarget &target, const BaseGenerator &schema_gen, target.GlobHeaders("lib/args/include/args"); // Specialized Toolchains + target.GlobSources("toolchains/src"); target.AddIncludeDir("toolchains/include"); target.GlobHeaders("toolchains/include/toolchains"); @@ -100,8 +87,8 @@ void buildcc_cb(BaseTarget &target, const BaseGenerator &schema_gen, SyncOption::HeaderFiles, }; - // FLATBUFFERS HO - target.Insert(flatbuffers_ho, kInsertOptions); + // NLOHMANN JSON HO + target.Insert(nlohmann_json_ho, kInsertOptions); // FMT HO target.Insert(fmt_ho, kInsertOptions); @@ -115,6 +102,9 @@ void buildcc_cb(BaseTarget &target, const BaseGenerator &schema_gen, // TASKFLOW HO target.Insert(taskflow_ho, kInsertOptions); + // TL OPTIONAL HO + target.Insert(tl_optional_ho, kInsertOptions); + // TPL LIB target.AddLibDep(tpl); target.Insert(tpl, kInsertOptions); @@ -122,7 +112,6 @@ void buildcc_cb(BaseTarget &target, const BaseGenerator &schema_gen, if constexpr (env::is_win()) { // TODO, Clang switch (target.GetToolchain().GetId()) { - case ToolchainId::Gcc: case ToolchainId::MinGW: { target.AddPreprocessorFlag("-DFMT_HEADER_ONLY=1"); target.AddPreprocessorFlag("-DSPDLOG_FMT_EXTERNAL"); @@ -138,7 +127,7 @@ void buildcc_cb(BaseTarget &target, const BaseGenerator &schema_gen, } } - if constexpr (env::is_linux()) { + if constexpr (env::is_linux() || env::is_unix() || env::is_clang()) { // TODO, Clang switch (target.GetToolchain().GetId()) { case ToolchainId::Gcc: { @@ -181,79 +170,83 @@ static void global_flags_cb(TargetInfo &global_info, } } -void BuildBuildCC::Setup(const ArgToolchainState &state) { - auto &flatc_exe = storage_.Add( - kFlatcExeName, kFlatcExeName, toolchain_, - TargetEnv(env_.GetTargetRootDir() / "third_party" / "flatbuffers", - env_.GetTargetBuildDir())); - - reg_.CallbackIf(state, global_flags_cb, flatc_exe, toolchain_); - reg_.Build(state, build_flatc_exe_cb, flatc_exe); - - // Schema - auto &schema_gen = storage_.Add( - kSchemaGenName, kSchemaGenName, - TargetEnv(env_.GetTargetRootDir() / "buildcc" / "schema", - env_.GetTargetBuildDir() / toolchain_.GetName())); - reg_.Build(schema_gen_cb, schema_gen, flatc_exe); - reg_.Dep(schema_gen, flatc_exe); - - // Flatbuffers HO lib - auto &flatbuffers_ho_lib = storage_.Add( - kFlatbuffersHoName, - TargetEnv(env_.GetTargetRootDir() / "third_party" / "flatbuffers", +void BuildBuildCC::Initialize() { + // Nlohmann json HO lib + (void)storage_.Add( + kNlohmannJsonHoName, toolchain_, + TargetEnv(env_.GetTargetRootDir() / "third_party" / "json", env_.GetTargetBuildDir())); - reg_.CallbackIf(state, flatbuffers_ho_cb, flatbuffers_ho_lib); // CLI11 HO lib - auto &cli11_ho_lib = storage_.Add( - kCli11HoName, TargetEnv(env_.GetTargetRootDir() / "third_party" / "CLI11", - env_.GetTargetBuildDir())); - reg_.CallbackIf(state, cli11_ho_cb, cli11_ho_lib); + (void)storage_.Add( + kCli11HoName, toolchain_, + TargetEnv(env_.GetTargetRootDir() / "third_party" / "CLI11", + env_.GetTargetBuildDir())); // fmt HO lib - auto &fmt_ho_lib = storage_.Add( - kFmtHoName, TargetEnv(env_.GetTargetRootDir() / "third_party" / "fmt", - env_.GetTargetBuildDir())); - reg_.CallbackIf(state, fmt_ho_cb, fmt_ho_lib); + (void)storage_.Add( + kFmtHoName, toolchain_, + TargetEnv(env_.GetTargetRootDir() / "third_party" / "fmt", + env_.GetTargetBuildDir())); // spdlog HO lib - auto &spdlog_ho_lib = storage_.Add( - kSpdlogHoName, + (void)storage_.Add( + kSpdlogHoName, toolchain_, TargetEnv(env_.GetTargetRootDir() / "third_party" / "spdlog", env_.GetTargetBuildDir())); - reg_.CallbackIf(state, spdlog_ho_cb, spdlog_ho_lib); // taskflow HO lib - auto &taskflow_ho_lib = storage_.Add( - kTaskflowHoName, + (void)storage_.Add( + kTaskflowHoName, toolchain_, TargetEnv(env_.GetTargetRootDir() / "third_party" / "taskflow", env_.GetTargetBuildDir())); - reg_.CallbackIf(state, taskflow_ho_cb, taskflow_ho_lib); + + // tl optional HO lib + (void)storage_.Add( + kTlOptionalHoName, toolchain_, + TargetEnv(env_.GetTargetRootDir() / "third_party" / "tl_optional", + env_.GetTargetBuildDir())); // Tiny-process-library lib // TODO, Make this a generic selection between StaticTarget and // DynamicTarget - auto &tpl_lib = storage_.Add( + (void)storage_.Add( kTplLibName, kTplLibName, toolchain_, TargetEnv(env_.GetTargetRootDir() / "third_party" / "tiny-process-library", env_.GetTargetBuildDir())); - reg_.CallbackIf(state, global_flags_cb, tpl_lib, toolchain_); - TplConfig tpl_config; - tpl_config.os_id = get_host_os(); - reg_.Build(state, tpl_cb, tpl_lib, tpl_config); + // BuildCC lib // TODO, Make this a generic selection between StaticTarget and // DynamicTarget - auto &buildcc_lib = storage_.Add( + (void)storage_.Add( kBuildccLibName, kBuildccLibName, toolchain_, TargetEnv(env_.GetTargetRootDir() / "buildcc", env_.GetTargetBuildDir())); - reg_.CallbackIf(state, global_flags_cb, buildcc_lib, toolchain_); - reg_.Build(state, buildcc_cb, buildcc_lib, schema_gen, flatbuffers_ho_lib, - fmt_ho_lib, spdlog_ho_lib, cli11_ho_lib, taskflow_ho_lib, tpl_lib); - reg_.Dep(buildcc_lib, schema_gen); - reg_.Dep(buildcc_lib, tpl_lib); +} + +void BuildBuildCC::Setup(const ArgToolchainState &state) { + auto &nlohmann_json_ho_lib = GetNlohmannJsonHo(); + auto &cli11_ho_lib = GetCli11Ho(); + auto &fmt_ho_lib = GetFmtHo(); + auto &spdlog_ho_lib = GetSpdlogHo(); + auto &taskflow_ho_lib = GetTaskflowHo(); + auto &tl_optional_ho_lib = GetTlOptionalHo(); + auto &tpl_lib = GetTpl(); + auto &buildcc_lib = GetBuildcc(); + Reg::Toolchain(state) + .Func(nlohmann_json_ho_cb, nlohmann_json_ho_lib) + .Func(cli11_ho_cb, cli11_ho_lib) + .Func(fmt_ho_cb, fmt_ho_lib) + .Func(spdlog_ho_cb, spdlog_ho_lib) + .Func(taskflow_ho_cb, taskflow_ho_lib) + .Func(tl_optional_ho_cb, tl_optional_ho_lib) + .Func(global_flags_cb, tpl_lib, toolchain_) + .Build(tpl_cb, tpl_lib) + .Func(global_flags_cb, buildcc_lib, toolchain_) + .Build(buildcc_cb, buildcc_lib, nlohmann_json_ho_lib, fmt_ho_lib, + spdlog_ho_lib, cli11_ho_lib, taskflow_ho_lib, tl_optional_ho_lib, + tpl_lib) + .Dep(buildcc_lib, tpl_lib); } } // namespace buildcc diff --git a/bootstrap/src/build_nlohmann_json.cpp b/bootstrap/src/build_nlohmann_json.cpp new file mode 100644 index 00000000..7bf5ce17 --- /dev/null +++ b/bootstrap/src/build_nlohmann_json.cpp @@ -0,0 +1,34 @@ +/* + * 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. + */ + +#include "bootstrap/build_nlohmann_json.h" + +namespace buildcc { + +void nlohmann_json_ho_cb(TargetInfo &info) { + info.AddIncludeDir("include"); + info.GlobHeaders("include/nlohmann"); + info.GlobHeaders("include/nlohmann/thirdparty/hedley"); + info.GlobHeaders("include/nlohmann/detail"); + info.GlobHeaders("include/nlohmann/detail/conversions"); + info.GlobHeaders("include/nlohmann/detail/input"); + info.GlobHeaders("include/nlohmann/detail/iterators"); + info.GlobHeaders("include/nlohmann/detail/meta"); + info.GlobHeaders("include/nlohmann/detail/meta/call_std"); + info.GlobHeaders("include/nlohmann/detail/output"); +} + +} // namespace buildcc diff --git a/bootstrap/src/build_tl_optional.cpp b/bootstrap/src/build_tl_optional.cpp new file mode 100644 index 00000000..3ac8926c --- /dev/null +++ b/bootstrap/src/build_tl_optional.cpp @@ -0,0 +1,26 @@ +/* + * 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. + */ + +#include "bootstrap/build_tl_optional.h" + +namespace buildcc { + +void tl_optional_ho_cb(TargetInfo &info) { + info.AddIncludeDir("include"); + info.GlobHeaders("include/tl"); +} + +} // namespace buildcc diff --git a/bootstrap/src/build_tpl.cpp b/bootstrap/src/build_tpl.cpp index 0bd53b14..847161b1 100644 --- a/bootstrap/src/build_tpl.cpp +++ b/bootstrap/src/build_tpl.cpp @@ -18,22 +18,17 @@ namespace buildcc { -void tpl_cb(BaseTarget &target, const TplConfig &config) { +void tpl_cb(BaseTarget &target) { target.AddSource("process.cpp"); target.AddIncludeDir(""); target.AddHeader("process.hpp"); - switch (config.os_id) { - case OsId::Win: + if constexpr (env::is_win()) { target.AddSource("process_win.cpp"); - break; - case OsId::Linux: - case OsId::Unix: - case OsId::Mac: + } + + if constexpr (env::is_linux() || env::is_unix() || env::is_clang()) { target.AddSource("process_unix.cpp"); - break; - default: - break; } target.Build(); diff --git a/buildcc/CMakeLists.txt b/buildcc/CMakeLists.txt index 860404be..074288d2 100644 --- a/buildcc/CMakeLists.txt +++ b/buildcc/CMakeLists.txt @@ -2,9 +2,6 @@ set(BUILDCC_INSTALL_LIB_PREFIX "lib/cmake") set(BUILDCC_INSTALL_HEADER_PREFIX "include/buildcc") -# Flatbuffers schema -add_subdirectory(schema) - if(${BUILDCC_BUILD_AS_SINGLE_LIB}) add_library(buildcc STATIC buildcc.h @@ -14,23 +11,26 @@ if(${BUILDCC_BUILD_AS_SINGLE_LIB}) $ ) target_link_libraries(buildcc PUBLIC - fmt::fmt-header-only - flatbuffers_header_only + fmt::fmt + tl::optional + nlohmann_json::nlohmann_json Taskflow CLI11::CLI11 ) target_link_libraries(buildcc PRIVATE - spdlog::spdlog_header_only + spdlog::spdlog tiny-process-library::tiny-process-library ) target_compile_options(buildcc PRIVATE ${BUILD_COMPILE_FLAGS}) target_link_options(buildcc PRIVATE ${BUILD_LINK_FLAGS}) if(${BUILDCC_PRECOMPILE_HEADERS}) target_precompile_headers(buildcc INTERFACE buildcc.h) - target_precompile_headers(buildcc INTERFACE ${FBS_DIR}/pch/pch.h) endif() endif() +# Schema +add_subdirectory(schema) + # Environment add_subdirectory(lib/env) @@ -65,7 +65,6 @@ if (${BUILDCC_BUILD_AS_INTERFACE}) ) if(${BUILDCC_PRECOMPILE_HEADERS}) target_precompile_headers(buildcc_i INTERFACE buildcc.h) - target_precompile_headers(buildcc_i INTERFACE ${FBS_DIR}/pch/pch.h) endif() endif() diff --git a/buildcc/buildcc.h b/buildcc/buildcc.h index 0959cea4..a48551cb 100644 --- a/buildcc/buildcc.h +++ b/buildcc/buildcc.h @@ -27,19 +27,18 @@ #include "env/host_compiler.h" #include "env/util.h" #include "env/command.h" +#include "env/storage.h" // Base #include "toolchain/toolchain.h" -#include "target/generator.h" +#include "target/custom_generator.h" +#include "target/file_generator.h" +#include "target/template_generator.h" #include "target/target_info.h" #include "target/target.h" // Specialized Toolchain -#include "toolchains/toolchain_gcc.h" -#include "toolchains/toolchain_msvc.h" -#include "toolchains/toolchain_mingw.h" - -// TODO, Add more specialized toolchains here +#include "toolchains/toolchain_specialized.h" // Specialized Targets #include "targets/target_gcc.h" @@ -57,6 +56,5 @@ // BuildCC Modules #include "args/args.h" #include "args/register.h" -#include "args/persistent_storage.h" #endif diff --git a/buildcc/lib/args/CMakeLists.txt b/buildcc/lib/args/CMakeLists.txt index c2f2d3a3..04b82e0d 100644 --- a/buildcc/lib/args/CMakeLists.txt +++ b/buildcc/lib/args/CMakeLists.txt @@ -8,6 +8,7 @@ add_library(mock_args ) target_include_directories(mock_args PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/mock ) target_compile_options(mock_args PUBLIC ${TEST_COMPILE_FLAGS} ${BUILD_COMPILE_FLAGS} @@ -19,10 +20,11 @@ target_link_libraries(mock_args PUBLIC CLI11::CLI11 mock_target + mock_toolchain_specialized CppUTest CppUTestExt - gcov + ${TEST_LINK_LIBS} ) # Tests @@ -40,20 +42,12 @@ target_link_libraries(test_register PRIVATE mock_args ) -add_executable(test_persistent_storage - test/test_persistent_storage.cpp -) -target_link_libraries(test_persistent_storage PRIVATE mock_args) - add_test(NAME test_args COMMAND test_args WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test ) add_test(NAME test_register COMMAND test_register WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test ) -add_test(NAME test_persistent_storage COMMAND test_persistent_storage - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test -) endif() set(ARGS_SRCS @@ -63,7 +57,6 @@ set(ARGS_SRCS src/tasks.cpp include/args/args.h include/args/register.h - include/args/persistent_storage.h ) if(${BUILDCC_BUILD_AS_SINGLE_LIB}) @@ -90,6 +83,8 @@ if(${BUILDCC_BUILD_AS_INTERFACE}) ) target_link_libraries(args PRIVATE target + + toolchain_specialized ) target_compile_options(args PRIVATE ${BUILD_COMPILE_FLAGS}) target_link_options(args PRIVATE ${BUILD_LINK_FLAGS}) diff --git a/buildcc/lib/args/include/args/args.h b/buildcc/lib/args/include/args/args.h index b57e2a6a..bb32faf3 100644 --- a/buildcc/lib/args/include/args/args.h +++ b/buildcc/lib/args/include/args/args.h @@ -25,19 +25,21 @@ // BuildCC #include "env/logging.h" -#include "toolchain/toolchain.h" +#include "toolchains/toolchain_specialized.h" + +#include "target/common/target_config.h" namespace fs = std::filesystem; namespace buildcc { /** - * @brief Toolchain State used by the Register module to selectively build or - * test targets + * @brief Toolchain State used to selectively build and test targets */ struct ArgToolchainState { - bool build{false}; - bool test{false}; + ArgToolchainState(bool b = false, bool t = false) : build(b), test(t) {} + bool build; + bool test; }; /** @@ -45,36 +47,27 @@ struct ArgToolchainState { * command line * Bundled with Toolchain State */ -struct ArgToolchain { - ArgToolchain(){}; +class ArgToolchain { +public: + ArgToolchain() + : ArgToolchain(ToolchainId::Undefined, "", ToolchainExecutables(), + ToolchainConfig()) {} ArgToolchain(ToolchainId initial_id, const std::string &initial_name, - const std::string &initial_asm_compiler, - const std::string &initial_c_compiler, - const std::string &initial_cpp_compiler, - const std::string &initial_archiver, - const std::string &initial_linker) - : id(initial_id), name(initial_name), asm_compiler(initial_asm_compiler), - c_compiler(initial_c_compiler), cpp_compiler(initial_cpp_compiler), - archiver(initial_archiver), linker(initial_linker) {} + const ToolchainExecutables &initial_executables, + const ToolchainConfig &initial_config) + : id(initial_id), name(initial_name), executables(initial_executables), + config(initial_config) {} - /** - * @brief Construct a BaseToolchain from the arguments supplied through the - * command line information - */ - BaseToolchain ConstructToolchain() const { - BaseToolchain toolchain(id, name, asm_compiler, c_compiler, cpp_compiler, - archiver, linker); - return toolchain; + Toolchain &ConstructToolchain() { + return Toolchain_generic::New(id, name, executables, config); } +public: ArgToolchainState state; - ToolchainId id{ToolchainId::Undefined}; - std::string name{""}; - std::string asm_compiler{""}; - std::string c_compiler{""}; - std::string cpp_compiler{""}; - std::string archiver{""}; - std::string linker{""}; + ToolchainId id; + std::string name; + ToolchainExecutables executables; + ToolchainConfig config; }; // NOTE, Incomplete without pch_compile_command @@ -82,35 +75,71 @@ struct ArgToolchain { struct ArgTarget { ArgTarget(){}; - std::string compile_command{""}; - std::string link_command{""}; + TargetConfig GetTargetConfig() { + TargetConfig config; + config.compile_command = compile_command; + config.link_command = link_command; + return config; + } + + std::string compile_command; + std::string link_command; +}; + +struct ArgCustom { + virtual void Add(CLI::App &app) = 0; }; class Args { +private: + class Instance; + struct Internal; + public: -public: - Args() { Initialize(); } + Args() = delete; Args(const Args &) = delete; + Args(Args &&) = delete; + + static Instance &Init(); + static void Deinit(); + + // Getters + static bool IsInit(); + static bool IsParsed(); + static bool Clean(); + static env::LogLevel GetLogLevel(); + + static const fs::path &GetProjectRootDir(); + static const fs::path &GetProjectBuildDir(); + +private: + static void RootArgs(); + static Internal &RefInternal(); + static CLI::App &RefApp(); + +private: + static std::unique_ptr internal_; +}; +class Args::Instance { +public: /** * @brief Parse command line information to CLI11 * * @param argc from int main(int argc, char ** argv) * @param argv from int main(int argc, char ** argv) */ - void Parse(int argc, const char *const *argv); - - /** - * @brief Modifiable reference to CLI::App (CLI11) - */ - CLI::App &Ref() { return app_; } + static void Parse(int argc, const char *const *argv); /** - * @brief Constant reference to CLI::App (CLI11) + * @brief Add toolchain with a unique name and description + * + * @param out Receive the toolchain information through the CLI + * @param initial Set the default toolchain information as a fallback */ - const CLI::App &ConstRef() const { return app_; } - - // Setters + Instance &AddToolchain(const std::string &name, + const std::string &description, ArgToolchain &out, + const ArgToolchain &initial = ArgToolchain()); /** * @brief Add toolchain with a unique name and description @@ -118,43 +147,29 @@ class Args { * @param out Receive the toolchain information through the CLI * @param initial Set the default toolchain information as a fallback */ - void AddToolchain(const std::string &name, const std::string &description, - ArgToolchain &out, - const ArgToolchain &initial = ArgToolchain()); + Instance &AddTarget(const std::string &name, const std::string &description, + ArgTarget &out, const ArgTarget &initial = ArgTarget()); /** - * @brief Add Target config commands with a unique name and description + * @brief Custom callback for data * - * @param out Receive the target command information through the CLI - * @param initial Set the default target command information as a fallback - * - * TODO, Update with other options for TargetConfig + * @param add_cb Add callback that exposes underlying CLI::App */ - void AddTarget(const std::string &name, const std::string &description, - ArgTarget &out, const ArgTarget &initial = ArgTarget()); - - // Getters - bool Clean() const { return clean_; } - env::LogLevel GetLogLevel() const { return loglevel_; } + Instance &AddCustomCallback(const std::function &add_cb); - const fs::path &GetProjectRootDir() const { return project_root_dir_; } - const fs::path &GetProjectBuildDir() const { return project_build_dir_; } - -private: - void Initialize(); - void RootArgs(); + /** + * @brief Add custom data + * + * @param data Derive from `buildcc::ArgCustom` and override the `Add` API + */ + Instance &AddCustomData(ArgCustom &data); +}; -private: - // Required parameters - bool clean_{false}; - env::LogLevel loglevel_{env::LogLevel::Info}; - fs::path project_root_dir_{""}; - fs::path project_build_dir_{"_internal"}; - - // Internal - CLI::App app_{"BuildCC buildsystem"}; - CLI::App *toolchain_{nullptr}; - CLI::App *target_{nullptr}; +struct Args::Internal { + Instance instance; + CLI::App app{"BuildCC Buildsystem"}; + CLI::App *toolchain{nullptr}; + CLI::App *target{nullptr}; }; } // namespace buildcc diff --git a/buildcc/lib/args/include/args/register.h b/buildcc/lib/args/include/args/register.h index 04210789..76bfa311 100644 --- a/buildcc/lib/args/include/args/register.h +++ b/buildcc/lib/args/include/args/register.h @@ -21,32 +21,51 @@ #include #include "args.h" +#include "args/register/test_info.h" -#include "target/generator.h" +#include "target/custom_generator.h" +#include "target/file_generator.h" #include "target/target.h" -#include "args/register/test_info.h" - #include "taskflow/taskflow.hpp" namespace buildcc { -class Register { +class Reg { +private: + class Instance; + class CallbackInstance; + class ToolchainInstance; + public: - Register(const Args &args) : args_(args) { Initialize(); } - Register(const Register &) = delete; + Reg() = delete; + Reg(const Reg &) = delete; + Reg(Reg &&) = delete; - /** - * @brief Folders and files that need to be cleaned when `clean == true` - */ - void Clean(const std::function &clean_cb); + static void Init(); + static void Deinit(); + static void Run(const std::function &post_build_cb = + std::function()); + static CallbackInstance Call(bool condition = true); + static ToolchainInstance Toolchain(const ArgToolchainState &condition); + static const tf::Taskflow &GetTaskflow(); + +private: + static Instance &Ref(); + +private: + static std::unique_ptr instance_; +}; + +class Reg::Instance { +public: /** * @brief Generic register callback with variable arguments * Can be used to organize code into functional chunks */ template - void Callback(const C &build_cb, Params &&...params) { + static void Callback(const C &build_cb, Params &&...params) { build_cb(std::forward(params)...); } @@ -56,54 +75,28 @@ class Register { * Can be used to add Toolchain-Target specific information */ template - void CallbackIf(bool expression, const C &build_cb, Params &&...params) { + static void CallbackIf(bool expression, const C &build_cb, + Params &&...params) { if (expression) { Callback(build_cb, std::forward(params)...); } } - /** - * @brief Generic register callback that is run when `toolchain_state.build == - * true` - * Can be used to add Toolchain-Target specific information - */ - template - void CallbackIf(const ArgToolchainState &toolchain_state, const C &build_cb, - Params &&...params) { - CallbackIf(toolchain_state.build, build_cb, - std::forward(params)...); - } + template + void Build(const C &build_cb, T &builder, Params &&...params) { + constexpr bool is_supported_base = + std::is_base_of_v; + static_assert(is_supported_base, + "Build only supports Generator, Target and derivatives"); - /** - * @brief Register the Target to be built - */ - template - void Build(const ArgToolchainState &toolchain_state, const C &build_cb, - BaseTarget &target, Params &&...params) { - tf::Task task; - CallbackIf( - toolchain_state, - [&](BaseTarget <arget, Params &&...lparams) { - build_cb(ltarget, std::forward(lparams)...); - task = BuildTargetTask(ltarget); - }, - target, std::forward(params)...); - BuildStoreTask(target.GetUniqueId(), task); - } - - /** - * @brief Register the generator to be built - */ - template - void Build(const C &build_cb, BaseGenerator &generator, Params &&...params) { - build_cb(generator, std::forward(params)...); - tf::Task task = BuildGeneratorTask(generator); - BuildStoreTask(generator.GetUniqueId(), task); + build_cb(builder, std::forward(params)...); + tf::Task task = BuildTask(builder); + BuildStoreTask(builder.GetUniqueId(), task); } /** * @brief Setup dependency between 2 Targets - * PreReq: Call `Register::Build` before calling `Register::Dep` + * PreReq: Call `Reg::Instance::Build` before calling `Reg::Instance::Dep` * * Target runs after dependency is built */ @@ -111,8 +104,8 @@ class Register { const internal::BuilderInterface &dependency); /** - * @brief Register the Target to be run - * PreReq: Call `Register::Build` before calling `Register::Test` + * @brief Instance the Target to be run + * PreReq: Call `Reg::Instance::Build` before calling `Reg::Instance::Test` * PreReq: Requires ArgToolchainState::build && ArgToolchainState::test to be * true * @@ -120,19 +113,18 @@ class Register { * We can add more fmt::format arguments using the TestConfig arguments * parameter */ - void Test(const ArgToolchainState &toolchain_state, - const std::string &command, const BaseTarget &target, + void Test(const std::string &command, const BaseTarget &target, const TestConfig &config = TestConfig()); /** * @brief Builds the targets that have been dynamically added through - * `Register::Build` + * `Reg::Instance::Build` */ void RunBuild(); /** * @brief Runs the targets that have been dynamically added through - * `Register::Test` + * `Reg::Instance::Test` */ void RunTest(); @@ -142,30 +134,71 @@ class Register { const tf::Taskflow &GetTaskflow() const { return build_tf_; } private: -private: - void Initialize(); - - // Setup env:: defaults - void Env(); - // BuildTasks - tf::Task BuildTargetTask(BaseTarget &target); - tf::Task BuildGeneratorTask(BaseGenerator &generator); + tf::Task BuildTask(BaseTarget &target); + tf::Task BuildTask(CustomGenerator &generator); void BuildStoreTask(const std::string &unique_id, const tf::Task &task); private: - const Args &args_; - // Build tf::Taskflow build_tf_{"Targets"}; std::unordered_map build_; - - // Tests std::unordered_map tests_; +}; + +class Reg::CallbackInstance { +public: + CallbackInstance(bool condition = true) : condition_(condition) {} + + // Duplicated code + template + CallbackInstance &Func(const C &cb, Params &&...params) { + Instance::CallbackIf(condition_, cb, std::forward(params)...); + return *this; + } + + template + CallbackInstance &Build(const C &build_cb, T &builder, Params &&...params) { + if (condition_) { + Ref().Build(build_cb, builder, std::forward(params)...); + }; + return *this; + } + +private: + bool condition_; +}; - // - tf::Executor executor_; +class Reg::ToolchainInstance { +public: + ToolchainInstance(const ArgToolchainState &condition) + : condition_(condition) {} + + template + ToolchainInstance &Func(const C &cb, Params &&...params) { + Instance::CallbackIf(condition_.build, cb, std::forward(params)...); + return *this; + } + + template + ToolchainInstance &Build(const C &build_cb, T &builder, Params &&...params) { + if (condition_.build) { + Ref().Build(build_cb, builder, std::forward(params)...); + }; + return *this; + } + // TODO, Update/Change this + template ToolchainInstance &BuildPackage(P &package) { + return Func([&]() { package.Setup(condition_); }); + } + ToolchainInstance &Dep(const internal::BuilderInterface &target, + const internal::BuilderInterface &dependency); + ToolchainInstance &Test(const std::string &command, const BaseTarget &target, + const TestConfig &config = TestConfig()); + +private: + ArgToolchainState condition_; }; } // namespace buildcc diff --git a/buildcc/lib/args/include/args/register/test_info.h b/buildcc/lib/args/include/args/register/test_info.h index 50d61f41..dffda12f 100644 --- a/buildcc/lib/args/include/args/register/test_info.h +++ b/buildcc/lib/args/include/args/register/test_info.h @@ -17,10 +17,14 @@ #ifndef ARGS_REGISTER_TEST_INFO_H_ #define ARGS_REGISTER_TEST_INFO_H_ +#include +#include + +#include "env/optional.h" + #include "target/target.h" -#include -#include +namespace fs = std::filesystem; namespace buildcc { @@ -35,7 +39,7 @@ struct TestOutput { }; /** - * @brief Configure your Register::Test to get test output + * @brief Configure your `Reg::Instance::Test` to get test output * * @param output_type Select your output type (behaviour) * @param redirect_stdout User stdout redirection @@ -64,15 +68,18 @@ struct TestOutput { struct TestConfig { public: /** - * @brief Configure your Register::Test using TestConfig + * @brief Configure your `Reg::Instance::Test` using TestConfig * * @param arguments fmt::format args passed to test commands * @param working_directory Working directory from which the test runs * @param output Output from tests */ + // ! FIXME, warning: base class ‘struct + // tl::detail::optional_operations_base’ + // should be explicitly initialized in the copy constructor TestConfig( const std::unordered_map &arguments = {}, - const std::optional &working_directory = {}, + const env::optional &working_directory = {}, const TestOutput &output = TestOutput()) : arguments_(arguments), working_directory_(working_directory), output_(output) {} @@ -80,14 +87,14 @@ struct TestConfig { const std::unordered_map &GetArguments() const { return arguments_; } - const std::optional &GetWorkingDirectory() const { + const env::optional &GetWorkingDirectory() const { return working_directory_; } const TestOutput &GetTestOutput() const { return output_; } private: std::unordered_map arguments_; - std::optional working_directory_{}; + env::optional working_directory_; TestOutput output_; }; diff --git a/buildcc/lib/args/mock/parse.cpp b/buildcc/lib/args/mock/parse.cpp index 210a7530..db3a1c6f 100644 --- a/buildcc/lib/args/mock/parse.cpp +++ b/buildcc/lib/args/mock/parse.cpp @@ -4,9 +4,9 @@ namespace buildcc { -void Args::Parse(int argc, const char *const *argv) { +void Args::Instance::Parse(int argc, const char *const *argv) { try { - app_.parse(argc, argv); + RefApp().parse(argc, argv); } catch (const CLI::ParseError &e) { env::assert_fatal(e.what()); } diff --git a/buildcc/lib/args/mock/tasks.cpp b/buildcc/lib/args/mock/tasks.cpp index 939e0100..b1fbb92b 100644 --- a/buildcc/lib/args/mock/tasks.cpp +++ b/buildcc/lib/args/mock/tasks.cpp @@ -4,20 +4,18 @@ namespace buildcc { -tf::Task Register::BuildTargetTask(BaseTarget &target) { +tf::Task Reg::Instance::BuildTask(BaseTarget &target) { mock().actualCall(fmt::format("BuildTask_{}", target.GetName()).c_str()); return build_tf_.placeholder().name(target.GetUniqueId()); } - -tf::Task Register::BuildGeneratorTask(BaseGenerator &generator) { - mock().actualCall( - fmt::format("BuildGeneratorTask_{}", generator.GetName()).c_str()); +tf::Task Reg::Instance::BuildTask(CustomGenerator &generator) { + mock().actualCall(fmt::format("BuildTask_{}", generator.GetName()).c_str()); return build_tf_.placeholder().name(generator.GetUniqueId()); } -void Register::RunBuild() {} +void Reg::Instance::RunBuild() {} -void Register::RunTest() { +void Reg::Instance::RunTest() { std::for_each(tests_.begin(), tests_.end(), [](const auto &p) { p.second.TestRunner(); }); } diff --git a/buildcc/lib/args/src/args.cpp b/buildcc/lib/args/src/args.cpp index ad1605b0..ce0b05c3 100644 --- a/buildcc/lib/args/src/args.cpp +++ b/buildcc/lib/args/src/args.cpp @@ -18,6 +18,10 @@ namespace { +// Error messages +constexpr const char *const kArgsNotInit = + "Initialize Args using the Args::Init API"; + // Groups constexpr const char *const kRootGroup = "Root"; @@ -75,7 +79,7 @@ const std::unordered_map kLogLevelMap{ {"critical", buildcc::env::LogLevel::Critical}, }; -const std::unordered_map kToolchainIdMap{ +const std::unordered_map kToolchainIdMap{ {"gcc", buildcc::ToolchainId::Gcc}, {"msvc", buildcc::ToolchainId::Msvc}, {"mingw", buildcc::ToolchainId::MinGW}, @@ -84,59 +88,55 @@ const std::unordered_map kToolchainIdMap{ {"undefined", buildcc::ToolchainId::Undefined}, }; +// Static variables +bool clean_{false}; +buildcc::env::LogLevel loglevel_{buildcc::env::LogLevel::Info}; +fs::path project_root_dir_{""}; +fs::path project_build_dir_{"_internal"}; + } // namespace namespace buildcc { -void Args::AddToolchain(const std::string &name, const std::string &description, - ArgToolchain &out, const ArgToolchain &initial) { - CLI::App *t_user = - toolchain_->add_subcommand(name, description)->group(kToolchainGroup); - t_user->add_flag(kToolchainBuildParam, out.state.build); - t_user->add_flag(kToolchainTestParam, out.state.test); - - t_user->add_option(kToolchainIdParam, out.id, kToolchainIdDesc) - ->transform(CLI::CheckedTransformer(kToolchainIdMap, CLI::ignore_case)) - ->default_val(initial.id); - t_user->add_option(kToolchainNameParam, out.name)->default_val(initial.name); - t_user->add_option(kToolchainAsmCompilerParam, out.asm_compiler) - ->default_val(initial.asm_compiler); - t_user->add_option(kToolchainCCompilerParam, out.c_compiler) - ->default_val(initial.c_compiler); - t_user->add_option(kToolchainCppCompilerParam, out.cpp_compiler) - ->default_val(initial.cpp_compiler); - t_user->add_option(kToolchainArchiverParam, out.archiver) - ->default_val(initial.archiver); - t_user->add_option(kToolchainLinkerParam, out.linker) - ->default_val(initial.linker); +std::unique_ptr Args::internal_; + +Args::Instance &Args::Init() { + if (!internal_) { + internal_ = std::make_unique(); + auto &app = RefApp(); + internal_->toolchain = + app.add_subcommand(kToolchainSubcommand, kToolchainDesc); + internal_->target = app.add_subcommand(kTargetSubcommand, kTargetDesc); + RootArgs(); + } + return internal_->instance; } -void Args::AddTarget(const std::string &name, const std::string &description, - ArgTarget &out, const ArgTarget &initial) { - CLI::App *target_user = - target_->add_subcommand(name, description)->group(kTargetGroup); - target_user->add_option(kTargetCompileCommandParam, out.compile_command) - ->default_val(initial.compile_command); - target_user->add_option(kTargetLinkCommandParam, out.link_command) - ->default_val(initial.link_command); +void Args::Deinit() { internal_.reset(nullptr); } + +bool Args::IsInit() { return static_cast(internal_); } +bool Args::IsParsed() { + if (!IsInit()) { + return false; + } + return RefApp().parsed(); } +bool Args::Clean() { return clean_; } +env::LogLevel Args::GetLogLevel() { return loglevel_; } -// Private +const fs::path &Args::GetProjectRootDir() { return project_root_dir_; } +const fs::path &Args::GetProjectBuildDir() { return project_build_dir_; } -void Args::Initialize() { - RootArgs(); - toolchain_ = app_.add_subcommand(kToolchainSubcommand, kToolchainDesc); - target_ = app_.add_subcommand(kTargetSubcommand, kTargetDesc); -} +// Private void Args::RootArgs() { - app_.set_help_all_flag(kHelpAllParam, kHelpAllDesc); + auto &app = RefApp(); + app.set_help_all_flag(kHelpAllParam, kHelpAllDesc); - app_.set_config(kConfigParam, "", kConfigDesc) - ->expected(kMinFiles, kMaxFiles); + app.set_config(kConfigParam, "", kConfigDesc)->expected(kMinFiles, kMaxFiles); // Root flags - auto *root_group = app_.add_option_group(kRootGroup); + auto *root_group = app.add_option_group(kRootGroup); root_group->add_flag(kCleanParam, clean_, kCleanDesc); root_group->add_option(kLoglevelParam, loglevel_, kLoglevelDesc) @@ -149,4 +149,83 @@ void Args::RootArgs() { ->required(); } +Args::Internal &Args::RefInternal() { + env::assert_fatal(internal_ != nullptr, kArgsNotInit); + return *internal_; +} +CLI::App &Args::RefApp() { return RefInternal().app; } + +// Args::Instance + +/** + * @brief Add toolchain with a unique name and description + * + * @param out Receive the toolchain information through the CLI + * @param initial Set the default toolchain information as a fallback + */ +Args::Instance &Args::Instance::AddToolchain(const std::string &name, + const std::string &description, + ArgToolchain &out, + const ArgToolchain &initial) { + CLI::App *toolchain = RefInternal().toolchain; + CLI::App *t_user = + toolchain->add_subcommand(name, description)->group(kToolchainGroup); + + // State + t_user->add_flag(kToolchainBuildParam, out.state.build); + t_user->add_flag(kToolchainTestParam, out.state.test); + + // Id, Name, Executables + t_user->add_option(kToolchainIdParam, out.id, kToolchainIdDesc) + ->transform(CLI::CheckedTransformer(kToolchainIdMap, CLI::ignore_case)) + ->default_val(initial.id); + t_user->add_option(kToolchainNameParam, out.name)->default_val(initial.name); + t_user->add_option(kToolchainAsmCompilerParam, out.executables.assembler) + ->default_val(initial.executables.assembler); + t_user->add_option(kToolchainCCompilerParam, out.executables.c_compiler) + ->default_val(initial.executables.c_compiler); + t_user->add_option(kToolchainCppCompilerParam, out.executables.cpp_compiler) + ->default_val(initial.executables.cpp_compiler); + t_user->add_option(kToolchainArchiverParam, out.executables.archiver) + ->default_val(initial.executables.archiver); + t_user->add_option(kToolchainLinkerParam, out.executables.linker) + ->default_val(initial.executables.linker); + + // TODO, Add toolchain config + return *this; +} + +/** + * @brief Add toolchain with a unique name and description + * + * @param out Receive the toolchain information through the CLI + * @param initial Set the default toolchain information as a fallback + */ +Args::Instance &Args::Instance::AddTarget(const std::string &name, + const std::string &description, + ArgTarget &out, + const ArgTarget &initial) { + CLI::App *target = RefInternal().target; + CLI::App *targetuser = + target->add_subcommand(name, description)->group(kTargetGroup); + targetuser->add_option(kTargetCompileCommandParam, out.compile_command) + ->default_val(initial.compile_command); + targetuser->add_option(kTargetLinkCommandParam, out.link_command) + ->default_val(initial.link_command); + return *this; +} + +Args::Instance &Args::Instance::AddCustomCallback( + const std::function &add_cb) { + auto &app = RefApp(); + add_cb(app); + return *this; +} + +Args::Instance &Args::Instance::AddCustomData(ArgCustom &data) { + auto &app = RefApp(); + data.Add(app); + return *this; +} + } // namespace buildcc diff --git a/buildcc/lib/args/src/parse.cpp b/buildcc/lib/args/src/parse.cpp index 637ee6f4..0d2aa186 100644 --- a/buildcc/lib/args/src/parse.cpp +++ b/buildcc/lib/args/src/parse.cpp @@ -18,11 +18,12 @@ namespace buildcc { -void Args::Parse(int argc, const char *const *argv) { +void Args::Instance::Parse(int argc, const char *const *argv) { + auto &app = RefApp(); try { - app_.parse(argc, argv); + app.parse(argc, argv); } catch (const CLI::ParseError &e) { - exit(app_.exit(e)); + exit(app.exit(e)); } } diff --git a/buildcc/lib/args/src/register.cpp b/buildcc/lib/args/src/register.cpp index a697fc92..e141541c 100644 --- a/buildcc/lib/args/src/register.cpp +++ b/buildcc/lib/args/src/register.cpp @@ -23,9 +23,15 @@ #include "env/assert_fatal.h" #include "env/env.h" +#include "env/storage.h" namespace fs = std::filesystem; +namespace { +constexpr const char *const kRegkNotInit = + "Initialize Reg using the Reg::Init API"; +} + namespace { void DepDetectDuplicate(const tf::Task &target_task, const std::string &match) { @@ -60,24 +66,95 @@ void DepDetectCyclicDependency(const tf::Task &target_task, namespace buildcc { -void Register::Clean(const std::function &clean_cb) { - if (args_.Clean()) { - clean_cb(); +std::unique_ptr Reg::instance_; + +void SystemInit() { + Project::Init(fs::current_path() / Args::GetProjectRootDir(), + fs::current_path() / Args::GetProjectBuildDir()); + env::set_log_level(Args::GetLogLevel()); + + // Top down (what is init first gets deinit last) + std::atexit([]() { + Project::Deinit(); + Reg::Deinit(); + Args::Deinit(); + Storage::Clear(); + }); +} + +void Reg::Init() { + if (!instance_) { + instance_ = std::make_unique(); + env::assert_fatal(static_cast(instance_), "Reg::Init failed"); + env::assert_fatal(Args::IsParsed(), "Setup your Args"); + + // Initialize everything else here + SystemInit(); } } -void Register::Dep(const internal::BuilderInterface &target, - const internal::BuilderInterface &dependency) { +void Reg::Deinit() { + instance_.reset(nullptr); + Project::Deinit(); +} + +void Reg::Run(const std::function &post_build_cb) { + auto &ref = Ref(); + ref.RunBuild(); + if (post_build_cb) { + post_build_cb(); + } + ref.RunTest(); +} + +const tf::Taskflow &Reg::GetTaskflow() { return Ref().GetTaskflow(); } + +Reg::Instance &Reg::Ref() { + env::assert_fatal(instance_ != nullptr, kRegkNotInit); + return *instance_; +} + +// Reg::ToolchainInstance + +Reg::ToolchainInstance Reg::Toolchain(const ArgToolchainState &condition) { + env::assert_fatal(instance_ != nullptr, kRegkNotInit); + return ToolchainInstance(condition); +} + +Reg::ToolchainInstance & +Reg::ToolchainInstance::Dep(const internal::BuilderInterface &target, + const internal::BuilderInterface &dependency) { + if (condition_.build) { + Ref().Dep(target, dependency); + } + return *this; +} + +Reg::ToolchainInstance &Reg::ToolchainInstance::Test(const std::string &command, + const BaseTarget &target, + const TestConfig &config) { + if (condition_.build && condition_.test) { + Ref().Test(command, target, config); + } + return *this; +} + +// Reg::CallbackInstance + +Reg::CallbackInstance Reg::Call(bool condition) { + env::assert_fatal(instance_ != nullptr, kRegkNotInit); + return CallbackInstance(condition); +} + +// Reg::Instance + +void Reg::Instance::Dep(const internal::BuilderInterface &target, + const internal::BuilderInterface &dependency) { const auto target_iter = build_.find(target.GetUniqueId()); const auto dep_iter = build_.find(dependency.GetUniqueId()); env::assert_fatal(!(target_iter == build_.end() || dep_iter == build_.end()), - "Call Register::Build API on target and " - "dependency before Register::Dep API"); - - // empty tasks -> not built so skip - if (target_iter->second.empty() || dep_iter->second.empty()) { - return; - } + "Call Instance::Build API on target and " + "dependency before Instance::Dep API"); const std::string &dep_unique_id = dependency.GetUniqueId(); DepDetectDuplicate(target_iter->second, dep_unique_id); @@ -87,17 +164,12 @@ void Register::Dep(const internal::BuilderInterface &target, target_iter->second.succeed(dep_iter->second); } -void Register::Test(const ArgToolchainState &toolchain_state, - const std::string &command, const BaseTarget &target, - const TestConfig &config) { - if (!(toolchain_state.build && toolchain_state.test)) { - return; - } - +void Reg::Instance::Test(const std::string &command, const BaseTarget &target, + const TestConfig &config) { const auto target_iter = build_.find(target.GetUniqueId()); env::assert_fatal( !(target_iter == build_.end()), - "Call Register::Build API on target before Register::Test API"); + "Call Instance::Build API on target before Instance::Test API"); const bool added = tests_.emplace(target.GetUniqueId(), TestInfo(target, command, config)) @@ -108,22 +180,14 @@ void Register::Test(const ArgToolchainState &toolchain_state, // Private -void Register::BuildStoreTask(const std::string &unique_id, - const tf::Task &task) { +void Reg::Instance::BuildStoreTask(const std::string &unique_id, + const tf::Task &task) { const bool stored = build_.emplace(unique_id, task).second; env::assert_fatal( - stored, fmt::format("Duplicate `Register::Build` call detected for '{}'", + stored, fmt::format("Duplicate `Instance::Build` call detected for '{}'", unique_id)); } -void Register::Initialize() { Env(); } - -void Register::Env() { - env::init(fs::current_path() / args_.GetProjectRootDir(), - fs::current_path() / args_.GetProjectBuildDir()); - env::set_log_level(args_.GetLogLevel()); -} - // void TestInfo::TestRunner() const { diff --git a/buildcc/lib/args/src/tasks.cpp b/buildcc/lib/args/src/tasks.cpp index 97e9cc4a..8328671d 100644 --- a/buildcc/lib/args/src/tasks.cpp +++ b/buildcc/lib/args/src/tasks.cpp @@ -15,37 +15,40 @@ */ #include "args/register.h" + #include "env/logging.h" #include "env/util.h" namespace buildcc { -tf::Task Register::BuildTargetTask(BaseTarget &target) { +tf::Task Reg::Instance::BuildTask(BaseTarget &target) { return build_tf_.composed_of(target.GetTaskflow()).name(target.GetUniqueId()); } -tf::Task Register::BuildGeneratorTask(BaseGenerator &generator) { +tf::Task Reg::Instance::BuildTask(CustomGenerator &generator) { return build_tf_.composed_of(generator.GetTaskflow()) .name(generator.GetUniqueId()); } -void Register::RunBuild() { - env::log_info(__FUNCTION__, fmt::format("Running with {} workers", - executor_.num_workers())); - executor_.run(build_tf_); - executor_.wait_for_all(); +void Reg::Instance::RunBuild() { + tf::Executor executor; + env::log_info(__FUNCTION__, + fmt::format("Running with {} workers", executor.num_workers())); + executor.run(build_tf_); + executor.wait_for_all(); env::assert_fatal(env::get_task_state() == env::TaskState::SUCCESS, "Task state is not successful!"); } -void Register::RunTest() { +void Reg::Instance::RunTest() { tf::Taskflow test_tf{"Tests"}; test_tf.for_each( tests_.begin(), tests_.end(), [](const std::pair &p) { p.second.TestRunner(); }); - executor_.run(test_tf); - executor_.wait_for_all(); + tf::Executor executor; + executor.run(test_tf); + executor.wait_for_all(); } } // namespace buildcc diff --git a/buildcc/lib/args/test/test_args.cpp b/buildcc/lib/args/test/test_args.cpp index 21bf1a75..8b519174 100644 --- a/buildcc/lib/args/test/test_args.cpp +++ b/buildcc/lib/args/test/test_args.cpp @@ -9,6 +9,9 @@ // clang-format off TEST_GROUP(ArgsTestGroup) { + void teardown() { + buildcc::Args::Deinit(); + } }; // clang-format on @@ -16,22 +19,30 @@ TEST(ArgsTestGroup, Args_BasicParse) { std::vector av{"", "--config", "configs/basic_parse.toml"}; int argc = av.size(); - buildcc::Args args; - args.Parse(argc, av.data()); + CHECK_FALSE(buildcc::Args::IsInit()); + CHECK_FALSE(buildcc::Args::IsParsed()); - STRCMP_EQUAL(args.GetProjectRootDir().string().c_str(), "root"); - STRCMP_EQUAL(args.GetProjectBuildDir().string().c_str(), "build"); - CHECK(args.GetLogLevel() == buildcc::env::LogLevel::Trace); - CHECK_TRUE(args.Clean()); + (void)buildcc::Args::Init(); + auto &instance = buildcc::Args::Init(); // Second init does nothing when + // already initialized + instance.Parse(argc, av.data()); + + STRCMP_EQUAL(buildcc::Args::GetProjectRootDir().string().c_str(), "root"); + STRCMP_EQUAL(buildcc::Args::GetProjectBuildDir().string().c_str(), "build"); + CHECK(buildcc::Args::GetLogLevel() == buildcc::env::LogLevel::Trace); + CHECK_TRUE(buildcc::Args::Clean()); + CHECK_TRUE(buildcc::Args::IsInit()); + CHECK_TRUE(buildcc::Args::IsParsed()); } TEST(ArgsTestGroup, Args_BasicExit) { + UT_PRINT("Args_BasicExit\r\n"); std::vector av{"", "--config", "configs/basic_parse.toml", "--help"}; int argc = av.size(); - buildcc::Args args; - CHECK_THROWS(std::exception, args.Parse(argc, av.data())); + auto &instance = buildcc::Args::Init(); + CHECK_THROWS(std::exception, instance.Parse(argc, av.data())); } TEST(ArgsTestGroup, Args_MultiToml) { @@ -39,13 +50,12 @@ TEST(ArgsTestGroup, Args_MultiToml) { "--config", "configs/no_clean.toml"}; int argc = av.size(); - buildcc::Args args; - args.Parse(argc, av.data()); + buildcc::Args::Init().Parse(argc, av.data()); - STRCMP_EQUAL(args.GetProjectRootDir().string().c_str(), "root"); - STRCMP_EQUAL(args.GetProjectBuildDir().string().c_str(), "build"); - CHECK(args.GetLogLevel() == buildcc::env::LogLevel::Trace); - CHECK_FALSE(args.Clean()); + STRCMP_EQUAL(buildcc::Args::GetProjectRootDir().string().c_str(), "root"); + STRCMP_EQUAL(buildcc::Args::GetProjectBuildDir().string().c_str(), "build"); + CHECK(buildcc::Args::GetLogLevel() == buildcc::env::LogLevel::Trace); + CHECK_FALSE(buildcc::Args::Clean()); } TEST(ArgsTestGroup, Args_CustomToolchain) { @@ -53,26 +63,26 @@ TEST(ArgsTestGroup, Args_CustomToolchain) { "--config", "configs/gcc_toolchain.toml"}; int argc = av.size(); - buildcc::Args args; buildcc::ArgToolchain gcc_toolchain; - args.AddToolchain("gcc", "Generic gcc toolchain", gcc_toolchain); - args.Parse(argc, av.data()); + buildcc::Args::Init() + .AddToolchain("gcc", "Generic gcc toolchain", gcc_toolchain) + .Parse(argc, av.data()); - STRCMP_EQUAL(args.GetProjectRootDir().string().c_str(), "root"); - STRCMP_EQUAL(args.GetProjectBuildDir().string().c_str(), "build"); - CHECK(args.GetLogLevel() == buildcc::env::LogLevel::Trace); - CHECK_TRUE(args.Clean()); + STRCMP_EQUAL(buildcc::Args::GetProjectRootDir().string().c_str(), "root"); + STRCMP_EQUAL(buildcc::Args::GetProjectBuildDir().string().c_str(), "build"); + CHECK(buildcc::Args::GetLogLevel() == buildcc::env::LogLevel::Trace); + CHECK_TRUE(buildcc::Args::Clean()); // Toolchain CHECK_TRUE(gcc_toolchain.state.build); CHECK_FALSE(gcc_toolchain.state.test); - CHECK(gcc_toolchain.id == buildcc::Toolchain::Id::Gcc); + CHECK(gcc_toolchain.id == buildcc::ToolchainId::Gcc); STRCMP_EQUAL(gcc_toolchain.name.c_str(), "gcc"); - STRCMP_EQUAL(gcc_toolchain.asm_compiler.c_str(), "as"); - STRCMP_EQUAL(gcc_toolchain.c_compiler.c_str(), "gcc"); - STRCMP_EQUAL(gcc_toolchain.cpp_compiler.c_str(), "g++"); - STRCMP_EQUAL(gcc_toolchain.archiver.c_str(), "ar"); - STRCMP_EQUAL(gcc_toolchain.linker.c_str(), "ld"); + STRCMP_EQUAL(gcc_toolchain.executables.assembler.c_str(), "as"); + STRCMP_EQUAL(gcc_toolchain.executables.c_compiler.c_str(), "gcc"); + STRCMP_EQUAL(gcc_toolchain.executables.cpp_compiler.c_str(), "g++"); + STRCMP_EQUAL(gcc_toolchain.executables.archiver.c_str(), "ar"); + STRCMP_EQUAL(gcc_toolchain.executables.linker.c_str(), "ld"); } TEST(ArgsTestGroup, Args_MultipleCustomToolchain) { @@ -87,55 +97,56 @@ TEST(ArgsTestGroup, Args_MultipleCustomToolchain) { }; int argc = av.size(); - buildcc::Args args; buildcc::ArgToolchain gcc_toolchain; buildcc::ArgToolchain msvc_toolchain; - args.AddToolchain("gcc", "Generic gcc toolchain", gcc_toolchain); - args.AddToolchain("msvc", "Generic msvc toolchain", msvc_toolchain); - args.Parse(argc, av.data()); + buildcc::Args::Init() + .AddToolchain("gcc", "Generic gcc toolchain", gcc_toolchain) + .AddToolchain("msvc", "Generic msvc toolchain", msvc_toolchain) + .Parse(argc, av.data()); - STRCMP_EQUAL(args.GetProjectRootDir().string().c_str(), "root"); - STRCMP_EQUAL(args.GetProjectBuildDir().string().c_str(), "build"); - CHECK(args.GetLogLevel() == buildcc::env::LogLevel::Trace); - CHECK_TRUE(args.Clean()); + STRCMP_EQUAL(buildcc::Args::GetProjectRootDir().string().c_str(), "root"); + STRCMP_EQUAL(buildcc::Args::GetProjectBuildDir().string().c_str(), "build"); + CHECK(buildcc::Args::GetLogLevel() == buildcc::env::LogLevel::Trace); + CHECK_TRUE(buildcc::Args::Clean()); // Toolchain // GCC CHECK_TRUE(gcc_toolchain.state.build); CHECK_FALSE(gcc_toolchain.state.test); - CHECK(gcc_toolchain.id == buildcc::Toolchain::Id::Gcc); + CHECK(gcc_toolchain.id == buildcc::ToolchainId::Gcc); STRCMP_EQUAL(gcc_toolchain.name.c_str(), "gcc"); - STRCMP_EQUAL(gcc_toolchain.asm_compiler.c_str(), "as"); - STRCMP_EQUAL(gcc_toolchain.c_compiler.c_str(), "gcc"); - STRCMP_EQUAL(gcc_toolchain.cpp_compiler.c_str(), "g++"); - STRCMP_EQUAL(gcc_toolchain.archiver.c_str(), "ar"); - STRCMP_EQUAL(gcc_toolchain.linker.c_str(), "ld"); + STRCMP_EQUAL(gcc_toolchain.executables.assembler.c_str(), "as"); + STRCMP_EQUAL(gcc_toolchain.executables.c_compiler.c_str(), "gcc"); + STRCMP_EQUAL(gcc_toolchain.executables.cpp_compiler.c_str(), "g++"); + STRCMP_EQUAL(gcc_toolchain.executables.archiver.c_str(), "ar"); + STRCMP_EQUAL(gcc_toolchain.executables.linker.c_str(), "ld"); // MSVC CHECK_TRUE(msvc_toolchain.state.build); CHECK_TRUE(msvc_toolchain.state.test); - CHECK(msvc_toolchain.id == buildcc::Toolchain::Id::Msvc); + CHECK(msvc_toolchain.id == buildcc::ToolchainId::Msvc); STRCMP_EQUAL(msvc_toolchain.name.c_str(), "msvc"); - STRCMP_EQUAL(msvc_toolchain.asm_compiler.c_str(), "cl"); - STRCMP_EQUAL(msvc_toolchain.c_compiler.c_str(), "cl"); - STRCMP_EQUAL(msvc_toolchain.cpp_compiler.c_str(), "cl"); - STRCMP_EQUAL(msvc_toolchain.archiver.c_str(), "lib"); - STRCMP_EQUAL(msvc_toolchain.linker.c_str(), "link"); + STRCMP_EQUAL(msvc_toolchain.executables.assembler.c_str(), "cl"); + STRCMP_EQUAL(msvc_toolchain.executables.c_compiler.c_str(), "cl"); + STRCMP_EQUAL(msvc_toolchain.executables.cpp_compiler.c_str(), "cl"); + STRCMP_EQUAL(msvc_toolchain.executables.archiver.c_str(), "lib"); + STRCMP_EQUAL(msvc_toolchain.executables.linker.c_str(), "link"); } TEST(ArgsTestGroup, Args_DuplicateCustomToolchain) { - buildcc::Args args; buildcc::ArgToolchain gcc_toolchain; buildcc::ArgToolchain other_gcc_toolchain; - args.AddToolchain("gcc", "Generic gcc toolchain", gcc_toolchain); + auto &instance = buildcc::Args::Init().AddToolchain( + "gcc", "Generic gcc toolchain", gcc_toolchain); // CLI11 Throws an exception when multiple toolchains with same name are added // NOTE, This behaviour does not need to be tested since it is provided by // CLI11 // This test is as an example of wrong usage by the user - CHECK_THROWS(std::exception, args.AddToolchain("gcc", "Other gcc toolchain", - other_gcc_toolchain)); + CHECK_THROWS(std::exception, + (instance.AddToolchain("gcc", "Other gcc toolchain ", + other_gcc_toolchain))); } TEST(ArgsTestGroup, Args_CustomTarget) { @@ -150,28 +161,28 @@ TEST(ArgsTestGroup, Args_CustomTarget) { }; int argc = av.size(); - buildcc::Args args; buildcc::ArgToolchain gcc_toolchain; buildcc::ArgTarget gcc_target; - args.AddToolchain("gcc", "Generic gcc toolchain", gcc_toolchain); - args.AddTarget("gcc", "Generic gcc target", gcc_target); - args.Parse(argc, av.data()); + buildcc::Args::Init() + .AddToolchain("gcc", "Generic gcc toolchain", gcc_toolchain) + .AddTarget("gcc", "Generic gcc target", gcc_target) + .Parse(argc, av.data()); - STRCMP_EQUAL(args.GetProjectRootDir().string().c_str(), "root"); - STRCMP_EQUAL(args.GetProjectBuildDir().string().c_str(), "build"); - CHECK(args.GetLogLevel() == buildcc::env::LogLevel::Trace); - CHECK_TRUE(args.Clean()); + STRCMP_EQUAL(buildcc::Args::GetProjectRootDir().string().c_str(), "root"); + STRCMP_EQUAL(buildcc::Args::GetProjectBuildDir().string().c_str(), "build"); + CHECK(buildcc::Args::GetLogLevel() == buildcc::env::LogLevel::Trace); + CHECK_TRUE(buildcc::Args::Clean()); // Toolchain CHECK_TRUE(gcc_toolchain.state.build); CHECK_FALSE(gcc_toolchain.state.test); - CHECK(gcc_toolchain.id == buildcc::Toolchain::Id::Gcc); + CHECK(gcc_toolchain.id == buildcc::ToolchainId::Gcc); STRCMP_EQUAL(gcc_toolchain.name.c_str(), "gcc"); - STRCMP_EQUAL(gcc_toolchain.asm_compiler.c_str(), "as"); - STRCMP_EQUAL(gcc_toolchain.c_compiler.c_str(), "gcc"); - STRCMP_EQUAL(gcc_toolchain.cpp_compiler.c_str(), "g++"); - STRCMP_EQUAL(gcc_toolchain.archiver.c_str(), "ar"); - STRCMP_EQUAL(gcc_toolchain.linker.c_str(), "ld"); + STRCMP_EQUAL(gcc_toolchain.executables.assembler.c_str(), "as"); + STRCMP_EQUAL(gcc_toolchain.executables.c_compiler.c_str(), "gcc"); + STRCMP_EQUAL(gcc_toolchain.executables.cpp_compiler.c_str(), "g++"); + STRCMP_EQUAL(gcc_toolchain.executables.archiver.c_str(), "ar"); + STRCMP_EQUAL(gcc_toolchain.executables.linker.c_str(), "ld"); // Target STRCMP_EQUAL(gcc_target.compile_command.c_str(), @@ -198,34 +209,35 @@ TEST(ArgsTestGroup, Args_MultipleCustomTarget) { }; int argc = av.size(); - buildcc::Args args; buildcc::ArgToolchain gcc_toolchain; buildcc::ArgTarget gcc_target; - args.AddToolchain("gcc", "Generic gcc toolchain", gcc_toolchain); - args.AddTarget("gcc", "Generic gcc target", gcc_target); buildcc::ArgToolchain msvc_toolchain; buildcc::ArgTarget msvc_target; - args.AddToolchain("msvc", "Generic msvc toolchain", msvc_toolchain); - args.AddTarget("msvc", "Generic msvc target", msvc_target); - args.Parse(argc, av.data()); - STRCMP_EQUAL(args.GetProjectRootDir().string().c_str(), "root"); - STRCMP_EQUAL(args.GetProjectBuildDir().string().c_str(), "build"); - CHECK(args.GetLogLevel() == buildcc::env::LogLevel::Trace); - CHECK_TRUE(args.Clean()); + buildcc::Args::Init() + .AddToolchain("gcc", "Generic gcc toolchain", gcc_toolchain) + .AddTarget("gcc", "Generic gcc target", gcc_target) + .AddToolchain("msvc", "Generic msvc toolchain", msvc_toolchain) + .AddTarget("msvc", "Generic msvc target", msvc_target) + .Parse(argc, av.data()); + + STRCMP_EQUAL(buildcc::Args::GetProjectRootDir().string().c_str(), "root"); + STRCMP_EQUAL(buildcc::Args::GetProjectBuildDir().string().c_str(), "build"); + CHECK(buildcc::Args::GetLogLevel() == buildcc::env::LogLevel::Trace); + CHECK_TRUE(buildcc::Args::Clean()); // GCC // Toolchain CHECK_TRUE(gcc_toolchain.state.build); CHECK_FALSE(gcc_toolchain.state.test); - CHECK(gcc_toolchain.id == buildcc::Toolchain::Id::Gcc); + CHECK(gcc_toolchain.id == buildcc::ToolchainId::Gcc); STRCMP_EQUAL(gcc_toolchain.name.c_str(), "gcc"); - STRCMP_EQUAL(gcc_toolchain.asm_compiler.c_str(), "as"); - STRCMP_EQUAL(gcc_toolchain.c_compiler.c_str(), "gcc"); - STRCMP_EQUAL(gcc_toolchain.cpp_compiler.c_str(), "g++"); - STRCMP_EQUAL(gcc_toolchain.archiver.c_str(), "ar"); - STRCMP_EQUAL(gcc_toolchain.linker.c_str(), "ld"); + STRCMP_EQUAL(gcc_toolchain.executables.assembler.c_str(), "as"); + STRCMP_EQUAL(gcc_toolchain.executables.c_compiler.c_str(), "gcc"); + STRCMP_EQUAL(gcc_toolchain.executables.cpp_compiler.c_str(), "g++"); + STRCMP_EQUAL(gcc_toolchain.executables.archiver.c_str(), "ar"); + STRCMP_EQUAL(gcc_toolchain.executables.linker.c_str(), "ld"); // Target STRCMP_EQUAL(gcc_target.compile_command.c_str(), @@ -240,13 +252,13 @@ TEST(ArgsTestGroup, Args_MultipleCustomTarget) { // Toolchain CHECK_TRUE(msvc_toolchain.state.build); CHECK_TRUE(msvc_toolchain.state.test); - CHECK(msvc_toolchain.id == buildcc::Toolchain::Id::Msvc); + CHECK(msvc_toolchain.id == buildcc::ToolchainId::Msvc); STRCMP_EQUAL(msvc_toolchain.name.c_str(), "msvc"); - STRCMP_EQUAL(msvc_toolchain.asm_compiler.c_str(), "cl"); - STRCMP_EQUAL(msvc_toolchain.c_compiler.c_str(), "cl"); - STRCMP_EQUAL(msvc_toolchain.cpp_compiler.c_str(), "cl"); - STRCMP_EQUAL(msvc_toolchain.archiver.c_str(), "lib"); - STRCMP_EQUAL(msvc_toolchain.linker.c_str(), "link"); + STRCMP_EQUAL(msvc_toolchain.executables.assembler.c_str(), "cl"); + STRCMP_EQUAL(msvc_toolchain.executables.c_compiler.c_str(), "cl"); + STRCMP_EQUAL(msvc_toolchain.executables.cpp_compiler.c_str(), "cl"); + STRCMP_EQUAL(msvc_toolchain.executables.archiver.c_str(), "lib"); + STRCMP_EQUAL(msvc_toolchain.executables.linker.c_str(), "link"); // Target STRCMP_EQUAL(msvc_target.compile_command.c_str(), @@ -257,6 +269,65 @@ TEST(ArgsTestGroup, Args_MultipleCustomTarget) { "{compiled_sources}"); } +TEST(ArgsTestGroup, Args_CustomCallback) { + std::vector av{"", + "--config", + "configs/basic_parse.toml", + "--random_bool", + "true", + "--random_string", + "hello world"}; + int argc = av.size(); + + bool random_bool{false}; + std::string random_string; + auto &instance = buildcc::Args::Init(); + instance.AddCustomCallback([&](CLI::App &app) { + app.add_option("--random_bool", random_bool, "Random bool"); + app.add_option("--random_string", random_string, "Random string"); + }); + instance.Parse(argc, av.data()); + + STRCMP_EQUAL(buildcc::Args::GetProjectRootDir().string().c_str(), "root"); + STRCMP_EQUAL(buildcc::Args::GetProjectBuildDir().string().c_str(), "build"); + CHECK(buildcc::Args::GetLogLevel() == buildcc::env::LogLevel::Trace); + CHECK_TRUE(buildcc::Args::Clean()); + CHECK_TRUE(random_bool); + STRCMP_EQUAL(random_string.c_str(), "hello world"); +} + +TEST(ArgsTestGroup, Args_CustomData) { + struct RandomGroupedData : public buildcc::ArgCustom { + void Add(CLI::App &app) override { + app.add_option("--random_bool", random_bool, "Random bool"); + app.add_option("--random_string", random_string, "Random string"); + } + + bool random_bool{false}; + std::string random_string; + }; + + std::vector av{"", + "--config", + "configs/basic_parse.toml", + "--random_bool", + "true", + "--random_string", + "hello world"}; + int argc = av.size(); + + RandomGroupedData grouped_data; + buildcc::Args::Init().AddCustomData(grouped_data).Parse(argc, av.data()); + + STRCMP_EQUAL(buildcc::Args::GetProjectRootDir().string().c_str(), "root"); + STRCMP_EQUAL(buildcc::Args::GetProjectBuildDir().string().c_str(), "build"); + CHECK(buildcc::Args::GetLogLevel() == buildcc::env::LogLevel::Trace); + CHECK_TRUE(buildcc::Args::Clean()); + CHECK_TRUE(grouped_data.random_bool); + STRCMP_EQUAL(grouped_data.random_string.c_str(), "hello world"); +} + int main(int ac, char **av) { + MemoryLeakWarningPlugin::destroyGlobalDetector(); return CommandLineTestRunner::RunAllTests(ac, av); } diff --git a/buildcc/lib/args/test/test_persistent_storage.cpp b/buildcc/lib/args/test/test_persistent_storage.cpp deleted file mode 100644 index 16079c4e..00000000 --- a/buildcc/lib/args/test/test_persistent_storage.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "args/persistent_storage.h" - -#include "target/generator.h" -#include "target/target.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(PersistentStorageTestGroup) -{ -}; -// clang-format on - -buildcc::BaseToolchain gcc(buildcc::ToolchainId::Gcc, "gcc", "as", "gcc", "g++", - "ar", "ld"); - -TEST(PersistentStorageTestGroup, BasicUsage) { - buildcc::PersistentStorage persistent; - persistent.Add("target_identifier", "target_name", - buildcc::TargetType::Executable, gcc, ""); - persistent.Add("generator_identifier", - "generator_name", ""); - - // Usage - persistent.ConstRef("target_identifier").GetName(); - persistent.Ref("generator_identifier").GetTaskflow(); - - // Automatic cleanup here -} - -TEST(PersistentStorageTestGroup, IncorrectUsage) { - buildcc::PersistentStorage persistent; - persistent.Add("target_identifier", "target_name", - buildcc::TargetType::Executable, gcc, ""); - - // We try to cast to a different type! - CHECK_THROWS(std::exception, - persistent.Ref("target_identifier")); - - // We use a wrong identifier - CHECK_THROWS(std::exception, - persistent.Ref("generator_identifier")); -} - -std::string &toReference(std::string *pointer) { return *pointer; } - -TEST(PersistentStorageTestGroup, NullptrDelete) { - buildcc::PersistentStorage persistent; - persistent.Remove(nullptr); -} - -int main(int ac, char **av) { - buildcc::env::init(fs::current_path(), fs::current_path()); - return CommandLineTestRunner::RunAllTests(ac, av); -} diff --git a/buildcc/lib/args/test/test_register.cpp b/buildcc/lib/args/test/test_register.cpp index 62e4ad38..04d8661e 100644 --- a/buildcc/lib/args/test/test_register.cpp +++ b/buildcc/lib/args/test/test_register.cpp @@ -14,9 +14,11 @@ // clang-format off TEST_GROUP(RegisterTestGroup) { - void teardown() { - mock().clear(); - } + void teardown() { + buildcc::Reg::Deinit(); + buildcc::Args::Deinit(); + mock().clear(); + } }; // clang-format on @@ -24,15 +26,15 @@ TEST(RegisterTestGroup, Register_Initialize) { std::vector av{"", "--config", "configs/basic_parse.toml"}; int argc = av.size(); - buildcc::Args args; - args.Parse(argc, av.data()); + buildcc::Args::Init().Parse(argc, av.data()); - STRCMP_EQUAL(args.GetProjectRootDir().string().c_str(), "root"); - STRCMP_EQUAL(args.GetProjectBuildDir().string().c_str(), "build"); - CHECK(args.GetLogLevel() == buildcc::env::LogLevel::Trace); - CHECK_TRUE(args.Clean()); + STRCMP_EQUAL(buildcc::Args::GetProjectRootDir().string().c_str(), "root"); + STRCMP_EQUAL(buildcc::Args::GetProjectBuildDir().string().c_str(), "build"); + CHECK(buildcc::Args::GetLogLevel() == buildcc::env::LogLevel::Trace); + CHECK_TRUE(buildcc::Args::Clean()); - buildcc::Register reg(args); + buildcc::Reg::Init(); + buildcc::Reg::Init(); // Second init does nothing } TEST(RegisterTestGroup, Register_Clean) { @@ -40,17 +42,20 @@ TEST(RegisterTestGroup, Register_Clean) { std::vector av{"", "--config", "configs/basic_parse.toml"}; int argc = av.size(); - buildcc::Args args; - args.Parse(argc, av.data()); + buildcc::Args::Init().Parse(argc, av.data()); - STRCMP_EQUAL(args.GetProjectRootDir().string().c_str(), "root"); - STRCMP_EQUAL(args.GetProjectBuildDir().string().c_str(), "build"); - CHECK(args.GetLogLevel() == buildcc::env::LogLevel::Trace); - CHECK_TRUE(args.Clean()); + STRCMP_EQUAL(buildcc::Args::GetProjectRootDir().string().c_str(), "root"); + STRCMP_EQUAL(buildcc::Args::GetProjectBuildDir().string().c_str(), "build"); + CHECK(buildcc::Args::GetLogLevel() == buildcc::env::LogLevel::Trace); + CHECK_TRUE(buildcc::Args::Clean()); - buildcc::Register reg(args); + buildcc::Reg::Init(); mock().expectOneCall("CleanCb"); - reg.Clean([]() { mock().actualCall("CleanCb"); }); + buildcc::Reg::Call(buildcc::Args::Clean()).Func([]() { + mock().actualCall("CleanCb"); + }); + buildcc::Reg::Deinit(); + buildcc::Args::Deinit(); } { @@ -63,16 +68,19 @@ TEST(RegisterTestGroup, Register_Clean) { }; int argc = av.size(); - buildcc::Args args; - args.Parse(argc, av.data()); + buildcc::Args::Init().Parse(argc, av.data()); - STRCMP_EQUAL(args.GetProjectRootDir().string().c_str(), "root"); - STRCMP_EQUAL(args.GetProjectBuildDir().string().c_str(), "build"); - CHECK(args.GetLogLevel() == buildcc::env::LogLevel::Trace); - CHECK_FALSE(args.Clean()); + STRCMP_EQUAL(buildcc::Args::GetProjectRootDir().string().c_str(), "root"); + STRCMP_EQUAL(buildcc::Args::GetProjectBuildDir().string().c_str(), "build"); + CHECK(buildcc::Args::GetLogLevel() == buildcc::env::LogLevel::Trace); + CHECK_FALSE(buildcc::Args::Clean()); - buildcc::Register reg(args); - reg.Clean([]() { mock().actualCall("CleanCb"); }); + buildcc::Reg::Init(); + buildcc::Reg::Call(buildcc::Args::Clean()).Func([]() { + mock().actualCall("CleanCb"); + }); + buildcc::Reg::Deinit(); + buildcc::Args::Deinit(); } mock().checkExpectations(); @@ -86,43 +94,92 @@ TEST(RegisterTestGroup, Register_Build) { }; int argc = av.size(); - buildcc::Args args; buildcc::ArgToolchain gcc_toolchain; buildcc::ArgToolchain msvc_toolchain; - args.AddToolchain("gcc", "Generic gcc toolchain", gcc_toolchain); - args.AddToolchain("msvc", "Generic msvc toolchain", msvc_toolchain); - args.Parse(argc, av.data()); + buildcc::Args::Init() + .AddToolchain("gcc", "Generic gcc toolchain", gcc_toolchain) + .AddToolchain("msvc", "Generic msvc toolchain", msvc_toolchain) + .Parse(argc, av.data()); - STRCMP_EQUAL(args.GetProjectRootDir().string().c_str(), "root"); - STRCMP_EQUAL(args.GetProjectBuildDir().string().c_str(), "build"); - CHECK(args.GetLogLevel() == buildcc::env::LogLevel::Trace); - CHECK_TRUE(args.Clean()); + STRCMP_EQUAL(buildcc::Args::GetProjectRootDir().string().c_str(), "root"); + STRCMP_EQUAL(buildcc::Args::GetProjectBuildDir().string().c_str(), "build"); + CHECK(buildcc::Args::GetLogLevel() == buildcc::env::LogLevel::Trace); + CHECK_TRUE(buildcc::Args::Clean()); // Make dummy toolchain and target - buildcc::env::init(fs::current_path(), fs::current_path()); - buildcc::Toolchain toolchain(buildcc::Toolchain::Id::Gcc, "", "", "", "", "", - ""); + buildcc::Project::Init(fs::current_path(), fs::current_path()); + buildcc::Toolchain toolchain( + buildcc::ToolchainId::Gcc, "", + buildcc::ToolchainExecutables("", "", "", "", "")); buildcc::BaseTarget target("dummyT", buildcc::TargetType::Executable, toolchain, ""); { buildcc::ArgToolchainState state{false, false}; - buildcc::Register reg(args); - reg.Build( - state, [](buildcc::BaseTarget &target) { (void)target; }, target); + buildcc::Reg::Init(); + buildcc::Reg::Toolchain(state).Build( + [](buildcc::BaseTarget &target) { (void)target; }, target); + (void)buildcc::Reg::GetTaskflow(); + buildcc::Reg::Deinit(); + CHECK_THROWS(std::exception, buildcc::Reg::GetTaskflow()); } { buildcc::ArgToolchainState state{true, true}; - buildcc::Register reg(args); + buildcc::Reg::Init(); mock().expectNCalls(1, "BuildTask_dummyT"); - reg.Build( - state, [](buildcc::BaseTarget &target) { (void)target; }, target); + buildcc::Reg::Toolchain(state).Build( + [](buildcc::BaseTarget &target) { (void)target; }, target); + buildcc::Reg::Deinit(); } - buildcc::env::deinit(); + buildcc::Project::Deinit(); + mock().checkExpectations(); +} + +TEST(RegisterTestGroup, Register_Run_PostCb) { + std::vector av{ + "", + "--config", + "configs/basic_parse.toml", + }; + int argc = av.size(); + + buildcc::ArgToolchain gcc_toolchain; + buildcc::ArgToolchain msvc_toolchain; + buildcc::Args::Init() + .AddToolchain("gcc", "Generic gcc toolchain", gcc_toolchain) + .AddToolchain("msvc", "Generic msvc toolchain", msvc_toolchain) + .Parse(argc, av.data()); + + STRCMP_EQUAL(buildcc::Args::GetProjectRootDir().string().c_str(), "root"); + STRCMP_EQUAL(buildcc::Args::GetProjectBuildDir().string().c_str(), "build"); + CHECK(buildcc::Args::GetLogLevel() == buildcc::env::LogLevel::Trace); + CHECK_TRUE(buildcc::Args::Clean()); + + // Make dummy toolchain and target + buildcc::Project::Init(fs::current_path(), fs::current_path()); + buildcc::Toolchain toolchain( + buildcc::ToolchainId::Gcc, "", + buildcc::ToolchainExecutables("", "", "", "", "")); + buildcc::BaseTarget target("dummyT", buildcc::TargetType::Executable, + toolchain, ""); + + { + buildcc::ArgToolchainState state{false, false}; + + buildcc::Reg::Init(); + buildcc::Reg::Toolchain(state).Build( + [](buildcc::BaseTarget &target) { (void)target; }, target); + + mock().expectOneCall("Build_PostCb"); + buildcc::Reg::Run([]() { mock().actualCall("Build_PostCb"); }); + buildcc::Reg::Deinit(); + } + + buildcc::Project::Deinit(); mock().checkExpectations(); } @@ -134,22 +191,23 @@ TEST(RegisterTestGroup, Register_NoBuildAndDep) { }; int argc = av.size(); - buildcc::Args args; buildcc::ArgToolchain gcc_toolchain; buildcc::ArgToolchain msvc_toolchain; - args.AddToolchain("gcc", "Generic gcc toolchain", gcc_toolchain); - args.AddToolchain("msvc", "Generic msvc toolchain", msvc_toolchain); - args.Parse(argc, av.data()); + buildcc::Args::Init() + .AddToolchain("gcc", "Generic gcc toolchain", gcc_toolchain) + .AddToolchain("msvc", "Generic msvc toolchain", msvc_toolchain) + .Parse(argc, av.data()); - STRCMP_EQUAL(args.GetProjectRootDir().string().c_str(), "root"); - STRCMP_EQUAL(args.GetProjectBuildDir().string().c_str(), "build"); - CHECK(args.GetLogLevel() == buildcc::env::LogLevel::Trace); - CHECK_TRUE(args.Clean()); + STRCMP_EQUAL(buildcc::Args::GetProjectRootDir().string().c_str(), "root"); + STRCMP_EQUAL(buildcc::Args::GetProjectBuildDir().string().c_str(), "build"); + CHECK(buildcc::Args::GetLogLevel() == buildcc::env::LogLevel::Trace); + CHECK_TRUE(buildcc::Args::Clean()); // Make dummy toolchain and target - buildcc::env::init(fs::current_path(), fs::current_path()); - buildcc::Toolchain toolchain(buildcc::Toolchain::Id::Gcc, "", "", "", "", "", - ""); + buildcc::Project::Init(fs::current_path(), fs::current_path()); + buildcc::Toolchain toolchain( + buildcc::ToolchainId::Gcc, "", + buildcc::ToolchainExecutables("", "", "", "", "")); buildcc::BaseTarget target("dummyT", buildcc::TargetType::Executable, toolchain, ""); buildcc::BaseTarget dependency("depT", buildcc::TargetType::Executable, @@ -167,46 +225,51 @@ TEST(RegisterTestGroup, Register_NoBuildAndDep) { // T0D0 { - buildcc::Register reg(args); - CHECK_THROWS(std::exception, reg.Dep(target, dependency)); + buildcc::Reg::Init(); + CHECK_THROWS(std::exception, + buildcc::Reg::Toolchain(trueState).Dep(target, dependency)); + buildcc::Reg::Deinit(); } // T0D1 { - buildcc::Register reg(args); + buildcc::Reg::Init(); mock().expectNCalls(1, "BuildTask_depT"); - reg.Build( - trueState, [](buildcc::BaseTarget &target) { (void)target; }, - dependency); + buildcc::Reg::Toolchain(trueState).Build( + [](buildcc::BaseTarget &target) { (void)target; }, dependency); - CHECK_THROWS(std::exception, reg.Dep(target, dependency)); + CHECK_THROWS(std::exception, + buildcc::Reg::Toolchain(trueState).Dep(target, dependency)); + buildcc::Reg::Deinit(); } // T1D0 { - buildcc::Register reg(args); + buildcc::Reg::Init(); mock().expectNCalls(1, "BuildTask_dummyT"); - reg.Build( - trueState, [](buildcc::BaseTarget &target) { (void)target; }, target); + buildcc::Reg::Toolchain(trueState).Build( + [](buildcc::BaseTarget &target) { (void)target; }, target); - CHECK_THROWS(std::exception, reg.Dep(target, dependency)); + CHECK_THROWS(std::exception, + buildcc::Reg::Toolchain(trueState).Dep(target, dependency)); + buildcc::Reg::Deinit(); } // T1D1 { - buildcc::Register reg(args); + buildcc::Reg::Init(); mock().expectNCalls(1, "BuildTask_dummyT"); mock().expectNCalls(1, "BuildTask_depT"); - reg.Build( - trueState, [](buildcc::BaseTarget &target) { (void)target; }, target); - reg.Build( - trueState, [](buildcc::BaseTarget &target) { (void)target; }, - dependency); + buildcc::Reg::Toolchain(trueState).Build( + [](buildcc::BaseTarget &target) { (void)target; }, target); + buildcc::Reg::Toolchain(trueState).Build( + [](buildcc::BaseTarget &target) { (void)target; }, dependency); - reg.Dep(target, dependency); + buildcc::Reg::Toolchain(trueState).Dep(target, dependency); + buildcc::Reg::Deinit(); } - buildcc::env::deinit(); + buildcc::Project::Deinit(); mock().checkExpectations(); } @@ -218,22 +281,23 @@ TEST(RegisterTestGroup, Register_BuildAndDep) { }; int argc = av.size(); - buildcc::Args args; buildcc::ArgToolchain gcc_toolchain; buildcc::ArgToolchain msvc_toolchain; - args.AddToolchain("gcc", "Generic gcc toolchain", gcc_toolchain); - args.AddToolchain("msvc", "Generic msvc toolchain", msvc_toolchain); - args.Parse(argc, av.data()); + buildcc::Args::Init() + .AddToolchain("gcc", "Generic gcc toolchain", gcc_toolchain) + .AddToolchain("msvc", "Generic msvc toolchain", msvc_toolchain) + .Parse(argc, av.data()); - STRCMP_EQUAL(args.GetProjectRootDir().string().c_str(), "root"); - STRCMP_EQUAL(args.GetProjectBuildDir().string().c_str(), "build"); - CHECK(args.GetLogLevel() == buildcc::env::LogLevel::Trace); - CHECK_TRUE(args.Clean()); + STRCMP_EQUAL(buildcc::Args::GetProjectRootDir().string().c_str(), "root"); + STRCMP_EQUAL(buildcc::Args::GetProjectBuildDir().string().c_str(), "build"); + CHECK(buildcc::Args::GetLogLevel() == buildcc::env::LogLevel::Trace); + CHECK_TRUE(buildcc::Args::Clean()); // Make dummy toolchain and target - buildcc::env::init(fs::current_path(), fs::current_path()); - buildcc::Toolchain toolchain(buildcc::Toolchain::Id::Gcc, "", "", "", "", "", - ""); + buildcc::Project::Init(fs::current_path(), fs::current_path()); + buildcc::Toolchain toolchain( + buildcc::ToolchainId::Gcc, "", + buildcc::ToolchainExecutables("", "", "", "", "")); buildcc::BaseTarget target("dummyT", buildcc::TargetType::Executable, toolchain, ""); buildcc::BaseTarget dependency("depT", buildcc::TargetType::Executable, @@ -251,57 +315,59 @@ TEST(RegisterTestGroup, Register_BuildAndDep) { // T0D0 { - buildcc::Register reg(args); - reg.Build( - falseState, [](buildcc::BaseTarget &target) { (void)target; }, target); - reg.Build( - falseState, [](buildcc::BaseTarget &target) { (void)target; }, - dependency); - - reg.Dep(target, dependency); + buildcc::Reg::Init(); + buildcc::Reg::Toolchain(falseState) + .Build([](buildcc::BaseTarget &target) { (void)target; }, target) + .Build([](buildcc::BaseTarget &target) { (void)target; }, dependency) + .Dep(target, dependency); + buildcc::Reg::Deinit(); } // T0D1 { - buildcc::Register reg(args); - reg.Build( - falseState, [](buildcc::BaseTarget &target) { (void)target; }, target); + buildcc::Reg::Init(); + buildcc::Reg::Toolchain(falseState) + .Build([](buildcc::BaseTarget &target) { (void)target; }, target); mock().expectNCalls(1, "BuildTask_depT"); - reg.Build( - trueState, [](buildcc::BaseTarget &target) { (void)target; }, - dependency); - - reg.Dep(target, dependency); + // In this case, target is not built so Dep throws + // Bad usage + CHECK_THROWS(std::exception, + buildcc::Reg::Toolchain(trueState) + .Build([](buildcc::BaseTarget &target) { (void)target; }, + dependency) + .Dep(target, dependency)); + buildcc::Reg::Deinit(); } // T1D0 { - buildcc::Register reg(args); + buildcc::Reg::Init(); mock().expectNCalls(1, "BuildTask_dummyT"); - reg.Build( - trueState, [](buildcc::BaseTarget &target) { (void)target; }, target); - reg.Build( - falseState, [](buildcc::BaseTarget &target) { (void)target; }, - dependency); + buildcc::Reg::Toolchain(trueState).Build( + [](buildcc::BaseTarget &target) { (void)target; }, target); + buildcc::Reg::Toolchain(falseState) + .Build([](buildcc::BaseTarget &target) { (void)target; }, dependency); - reg.Dep(target, dependency); + // In this case dependency is not built + // Bad usage + CHECK_THROWS(std::exception, + buildcc::Reg::Toolchain(trueState).Dep(target, dependency)); + buildcc::Reg::Deinit(); } // T1D1 { - buildcc::Register reg(args); + buildcc::Reg::Init(); mock().expectNCalls(1, "BuildTask_dummyT"); mock().expectNCalls(1, "BuildTask_depT"); - reg.Build( - trueState, [](buildcc::BaseTarget &target) { (void)target; }, target); - reg.Build( - trueState, [](buildcc::BaseTarget &target) { (void)target; }, - dependency); - - reg.Dep(target, dependency); + buildcc::Reg::Toolchain(trueState) + .Build([](buildcc::BaseTarget &target) { (void)target; }, target) + .Build([](buildcc::BaseTarget &target) { (void)target; }, dependency) + .Dep(target, dependency); + buildcc::Reg::Deinit(); } - buildcc::env::deinit(); + buildcc::Project::Deinit(); mock().checkExpectations(); } @@ -313,22 +379,23 @@ TEST(RegisterTestGroup, Register_DepDuplicate) { }; int argc = av.size(); - buildcc::Args args; buildcc::ArgToolchain gcc_toolchain; buildcc::ArgToolchain msvc_toolchain; - args.AddToolchain("gcc", "Generic gcc toolchain", gcc_toolchain); - args.AddToolchain("msvc", "Generic msvc toolchain", msvc_toolchain); - args.Parse(argc, av.data()); + buildcc::Args::Init() + .AddToolchain("gcc", "Generic gcc toolchain", gcc_toolchain) + .AddToolchain("msvc", "Generic msvc toolchain", msvc_toolchain) + .Parse(argc, av.data()); - STRCMP_EQUAL(args.GetProjectRootDir().string().c_str(), "root"); - STRCMP_EQUAL(args.GetProjectBuildDir().string().c_str(), "build"); - CHECK(args.GetLogLevel() == buildcc::env::LogLevel::Trace); - CHECK_TRUE(args.Clean()); + STRCMP_EQUAL(buildcc::Args::GetProjectRootDir().string().c_str(), "root"); + STRCMP_EQUAL(buildcc::Args::GetProjectBuildDir().string().c_str(), "build"); + CHECK(buildcc::Args::GetLogLevel() == buildcc::env::LogLevel::Trace); + CHECK_TRUE(buildcc::Args::Clean()); // Make dummy toolchain and target - buildcc::env::init(fs::current_path(), fs::current_path()); - buildcc::Toolchain toolchain(buildcc::Toolchain::Id::Gcc, "", "", "", "", "", - ""); + buildcc::Project::Init(fs::current_path(), fs::current_path()); + buildcc::Toolchain toolchain( + buildcc::ToolchainId::Gcc, "", + buildcc::ToolchainExecutables("", "", "", "", "")); buildcc::BaseTarget target("dummyT", buildcc::TargetType::Executable, toolchain, ""); buildcc::BaseTarget dependency("depT", buildcc::TargetType::Executable, @@ -340,44 +407,44 @@ TEST(RegisterTestGroup, Register_DepDuplicate) { // Duplicate dependency with 2 Targets { - buildcc::Register reg(args); + buildcc::Reg::Init(); mock().expectNCalls(1, "BuildTask_dummyT"); mock().expectNCalls(1, "BuildTask_depT"); - reg.Build( - trueState, [](buildcc::BaseTarget &target) { (void)target; }, target); - reg.Build( - trueState, [](buildcc::BaseTarget &target) { (void)target; }, - dependency); - - reg.Dep(target, dependency); - CHECK_THROWS(std::exception, reg.Dep(target, dependency)); + buildcc::Reg::Toolchain(trueState) + .Build([](buildcc::BaseTarget &target) { (void)target; }, target) + .Build([](buildcc::BaseTarget &target) { (void)target; }, dependency) + .Dep(target, dependency); + CHECK_THROWS(std::exception, + buildcc::Reg::Toolchain(trueState).Dep(target, dependency)); + buildcc::Reg::Deinit(); } // Duplicate dependency with 3 Targets { - buildcc::Register reg(args); + buildcc::Reg::Init(); mock().expectNCalls(1, "BuildTask_dummyT"); mock().expectNCalls(1, "BuildTask_depT"); mock().expectNCalls(1, "BuildTask_dep2T"); - reg.Build( - trueState, [](buildcc::BaseTarget &target) { (void)target; }, target); - reg.Build( - trueState, [](buildcc::BaseTarget &target) { (void)target; }, - dependency); - reg.Build( - trueState, [](buildcc::BaseTarget &target) { (void)target; }, - dependency2); - - reg.Dep(dependency, dependency2); - reg.Dep(target, dependency); - reg.Dep(target, dependency2); - - CHECK_THROWS(std::exception, reg.Dep(target, dependency)); - CHECK_THROWS(std::exception, reg.Dep(target, dependency2)); + buildcc::Reg::Toolchain(trueState).Build( + [](buildcc::BaseTarget &target) { (void)target; }, target); + buildcc::Reg::Toolchain(trueState).Build( + [](buildcc::BaseTarget &target) { (void)target; }, dependency); + buildcc::Reg::Toolchain(trueState).Build( + [](buildcc::BaseTarget &target) { (void)target; }, dependency2); + + buildcc::Reg::Toolchain(trueState).Dep(dependency, dependency2); + buildcc::Reg::Toolchain(trueState).Dep(target, dependency); + buildcc::Reg::Toolchain(trueState).Dep(target, dependency2); + + CHECK_THROWS(std::exception, + buildcc::Reg::Toolchain(trueState).Dep(target, dependency)); + CHECK_THROWS(std::exception, + buildcc::Reg::Toolchain(trueState).Dep(target, dependency2)); + buildcc::Reg::Deinit(); } - buildcc::env::deinit(); + buildcc::Project::Deinit(); mock().checkExpectations(); } @@ -389,22 +456,23 @@ TEST(RegisterTestGroup, Register_DepCyclic) { }; int argc = av.size(); - buildcc::Args args; buildcc::ArgToolchain gcc_toolchain; buildcc::ArgToolchain msvc_toolchain; - args.AddToolchain("gcc", "Generic gcc toolchain", gcc_toolchain); - args.AddToolchain("msvc", "Generic msvc toolchain", msvc_toolchain); - args.Parse(argc, av.data()); + buildcc::Args::Init() + .AddToolchain("gcc", "Generic gcc toolchain", gcc_toolchain) + .AddToolchain("msvc", "Generic msvc toolchain", msvc_toolchain) + .Parse(argc, av.data()); - STRCMP_EQUAL(args.GetProjectRootDir().string().c_str(), "root"); - STRCMP_EQUAL(args.GetProjectBuildDir().string().c_str(), "build"); - CHECK(args.GetLogLevel() == buildcc::env::LogLevel::Trace); - CHECK_TRUE(args.Clean()); + STRCMP_EQUAL(buildcc::Args::GetProjectRootDir().string().c_str(), "root"); + STRCMP_EQUAL(buildcc::Args::GetProjectBuildDir().string().c_str(), "build"); + CHECK(buildcc::Args::GetLogLevel() == buildcc::env::LogLevel::Trace); + CHECK_TRUE(buildcc::Args::Clean()); // Make dummy toolchain and target - buildcc::env::init(fs::current_path(), fs::current_path()); - buildcc::Toolchain toolchain(buildcc::Toolchain::Id::Gcc, "", "", "", "", "", - ""); + buildcc::Project::Init(fs::current_path(), fs::current_path()); + buildcc::Toolchain toolchain( + buildcc::ToolchainId::Gcc, "", + buildcc::ToolchainExecutables("", "", "", "", "")); buildcc::BaseTarget target("dummyT", buildcc::TargetType::Executable, toolchain, ""); buildcc::BaseTarget dependency("depT", buildcc::TargetType::Executable, @@ -416,43 +484,44 @@ TEST(RegisterTestGroup, Register_DepCyclic) { // Immediate cyclic depdendency { - buildcc::Register reg(args); + buildcc::Reg::Init(); mock().expectNCalls(1, "BuildTask_dummyT"); mock().expectNCalls(1, "BuildTask_depT"); - reg.Build( - trueState, [](buildcc::BaseTarget &target) { (void)target; }, target); - reg.Build( - trueState, [](buildcc::BaseTarget &target) { (void)target; }, - dependency); - - reg.Dep(target, dependency); - CHECK_THROWS(std::exception, reg.Dep(dependency, target)); + buildcc::Reg::Toolchain(trueState).Build( + [](buildcc::BaseTarget &target) { (void)target; }, target); + buildcc::Reg::Toolchain(trueState).Build( + [](buildcc::BaseTarget &target) { (void)target; }, dependency); + + buildcc::Reg::Toolchain(trueState).Dep(target, dependency); + CHECK_THROWS(std::exception, + buildcc::Reg::Toolchain(trueState).Dep(dependency, target)); + buildcc::Reg::Deinit(); } // Duplicate dependency with 3 Targets { - buildcc::Register reg(args); + buildcc::Reg::Init(); mock().expectNCalls(1, "BuildTask_dummyT"); mock().expectNCalls(1, "BuildTask_depT"); mock().expectNCalls(1, "BuildTask_dep2T"); - reg.Build( - trueState, [](buildcc::BaseTarget &target) { (void)target; }, target); - reg.Build( - trueState, [](buildcc::BaseTarget &target) { (void)target; }, - dependency); - reg.Build( - trueState, [](buildcc::BaseTarget &target) { (void)target; }, - dependency2); + buildcc::Reg::Toolchain(trueState).Build( + [](buildcc::BaseTarget &target) { (void)target; }, target); + buildcc::Reg::Toolchain(trueState).Build( + [](buildcc::BaseTarget &target) { (void)target; }, dependency); + buildcc::Reg::Toolchain(trueState).Build( + [](buildcc::BaseTarget &target) { (void)target; }, dependency2); - reg.Dep(dependency, dependency2); - reg.Dep(target, dependency); + buildcc::Reg::Toolchain(trueState).Dep(dependency, dependency2); + buildcc::Reg::Toolchain(trueState).Dep(target, dependency); // dependency2 -> dependency -> target -> dependency2 - CHECK_THROWS(std::exception, reg.Dep(dependency2, target)); + CHECK_THROWS(std::exception, + buildcc::Reg::Toolchain(trueState).Dep(dependency2, target)); + buildcc::Reg::Deinit(); } - buildcc::env::deinit(); + buildcc::Project::Deinit(); mock().checkExpectations(); } @@ -465,22 +534,23 @@ TEST(RegisterTestGroup, Register_Test) { }; int argc = av.size(); - buildcc::Args args; buildcc::ArgToolchain gcc_toolchain; buildcc::ArgToolchain msvc_toolchain; - args.AddToolchain("gcc", "Generic gcc toolchain", gcc_toolchain); - args.AddToolchain("msvc", "Generic msvc toolchain", msvc_toolchain); - args.Parse(argc, av.data()); + buildcc::Args::Init() + .AddToolchain("gcc", "Generic gcc toolchain", gcc_toolchain) + .AddToolchain("msvc", "Generic msvc toolchain", msvc_toolchain) + .Parse(argc, av.data()); - STRCMP_EQUAL(args.GetProjectRootDir().string().c_str(), "root"); - STRCMP_EQUAL(args.GetProjectBuildDir().string().c_str(), "build"); - CHECK(args.GetLogLevel() == buildcc::env::LogLevel::Trace); - CHECK_TRUE(args.Clean()); + STRCMP_EQUAL(buildcc::Args::GetProjectRootDir().string().c_str(), "root"); + STRCMP_EQUAL(buildcc::Args::GetProjectBuildDir().string().c_str(), "build"); + CHECK(buildcc::Args::GetLogLevel() == buildcc::env::LogLevel::Trace); + CHECK_TRUE(buildcc::Args::Clean()); // Make dummy toolchain and target - buildcc::env::init(fs::current_path(), fs::current_path()); - buildcc::Toolchain toolchain(buildcc::Toolchain::Id::Gcc, "", "", "", "", "", - ""); + buildcc::Project::Init(fs::current_path(), fs::current_path()); + buildcc::Toolchain toolchain( + buildcc::ToolchainId::Gcc, "", + buildcc::ToolchainExecutables("", "", "", "", "")); buildcc::BaseTarget target("dummyT", buildcc::TargetType::Executable, toolchain, ""); buildcc::BaseTarget dependency("depT", buildcc::TargetType::Executable, @@ -498,46 +568,51 @@ TEST(RegisterTestGroup, Register_Test) { // FF { - buildcc::Register reg(args); - reg.Test(stateFail, "{executable}", target); + buildcc::Reg::Init(); + buildcc::Reg::Toolchain(stateFail).Test("{executable}", target); + buildcc::Reg::Deinit(); } // TF { - buildcc::Register reg(args); - reg.Test(state1, "{executable}", target); + buildcc::Reg::Init(); + buildcc::Reg::Toolchain(state1).Test("{executable}", target); + buildcc::Reg::Deinit(); } // FT { - buildcc::Register reg(args); - reg.Test(state2, "{executable}", target); + buildcc::Reg::Init(); + buildcc::Reg::Toolchain(state2).Test("{executable}", target); + buildcc::Reg::Deinit(); } // TT - // Register::Build not called + // Reg::Instance::Build not called { - buildcc::Register reg(args); - CHECK_THROWS(std::exception, - reg.Test(stateSuccess, "{executable}", target)); + buildcc::Reg::Init(); + CHECK_THROWS( + std::exception, + buildcc::Reg::Toolchain(stateSuccess).Test("{executable}", target)); + buildcc::Reg::Deinit(); } // Correct Usage { - buildcc::Register reg(args); + buildcc::Reg::Init(); mock().expectNCalls(1, "BuildTask_dummyT"); - reg.Build( - stateSuccess, [](buildcc::BaseTarget &target) { (void)target; }, - target); - reg.Test(stateSuccess, "{executable}", target); + buildcc::Reg::Toolchain(stateSuccess) + .Build([](buildcc::BaseTarget &target) { (void)target; }, target); + buildcc::Reg::Toolchain(stateSuccess).Test("{executable}", target); std::vector stdout_data; std::vector stderr_data; buildcc::env::m::CommandExpect_Execute(1, true, &stdout_data, &stderr_data); - reg.RunTest(); + buildcc::Reg::Run(); + buildcc::Reg::Deinit(); } - buildcc::env::deinit(); + buildcc::Project::Deinit(); mock().checkExpectations(); } @@ -550,22 +625,23 @@ TEST(RegisterTestGroup, Register_TestWithOutput) { }; int argc = av.size(); - buildcc::Args args; buildcc::ArgToolchain gcc_toolchain; buildcc::ArgToolchain msvc_toolchain; - args.AddToolchain("gcc", "Generic gcc toolchain", gcc_toolchain); - args.AddToolchain("msvc", "Generic msvc toolchain", msvc_toolchain); - args.Parse(argc, av.data()); + buildcc::Args::Init() + .AddToolchain("gcc", "Generic gcc toolchain", gcc_toolchain) + .AddToolchain("msvc", "Generic msvc toolchain", msvc_toolchain) + .Parse(argc, av.data()); - STRCMP_EQUAL(args.GetProjectRootDir().string().c_str(), "root"); - STRCMP_EQUAL(args.GetProjectBuildDir().string().c_str(), "build"); - CHECK(args.GetLogLevel() == buildcc::env::LogLevel::Trace); - CHECK_TRUE(args.Clean()); + STRCMP_EQUAL(buildcc::Args::GetProjectRootDir().string().c_str(), "root"); + STRCMP_EQUAL(buildcc::Args::GetProjectBuildDir().string().c_str(), "build"); + CHECK(buildcc::Args::GetLogLevel() == buildcc::env::LogLevel::Trace); + CHECK_TRUE(buildcc::Args::Clean()); // Make dummy toolchain and target - buildcc::env::init(fs::current_path(), fs::current_path()); - buildcc::Toolchain toolchain(buildcc::Toolchain::Id::Gcc, "", "", "", "", "", - ""); + buildcc::Project::Init(fs::current_path(), fs::current_path()); + buildcc::Toolchain toolchain( + buildcc::ToolchainId::Gcc, "", + buildcc::ToolchainExecutables("", "", "", "", "")); buildcc::BaseTarget target("dummyT", buildcc::TargetType::Executable, toolchain, ""); buildcc::BaseTarget dependency("depT", buildcc::TargetType::Executable, @@ -575,113 +651,119 @@ TEST(RegisterTestGroup, Register_TestWithOutput) { // TestOutput::Type::DefaultBehaviour { - buildcc::Register reg(args); + buildcc::Reg::Init(); mock().expectNCalls(1, "BuildTask_dummyT"); - reg.Build( - stateSuccess, [](buildcc::BaseTarget &target) { (void)target; }, - target); - reg.Test( - stateSuccess, "{executable}", target, - buildcc::TestConfig( - {}, {}, - buildcc::TestOutput(buildcc::TestOutput::Type::DefaultBehaviour))); + buildcc::Reg::Toolchain(stateSuccess) + .Build([](buildcc::BaseTarget &target) { (void)target; }, target); + buildcc::Reg::Toolchain(stateSuccess) + .Test("{executable}", target, + buildcc::TestConfig( + {}, {}, + buildcc::TestOutput( + buildcc::TestOutput::Type::DefaultBehaviour))); buildcc::env::m::CommandExpect_Execute(1, true); - reg.RunTest(); + buildcc::Reg::Run(); + buildcc::Reg::Deinit(); } // TestOutput::Type::TestPrintOnStderr { - buildcc::Register reg(args); + buildcc::Reg::Init(); mock().expectNCalls(1, "BuildTask_dummyT"); - reg.Build( - stateSuccess, [](buildcc::BaseTarget &target) { (void)target; }, - target); - reg.Test( - stateSuccess, "{executable}", target, - buildcc::TestConfig( - {}, {}, - buildcc::TestOutput(buildcc::TestOutput::Type::TestPrintOnStderr))); + buildcc::Reg::Toolchain(stateSuccess) + .Build([](buildcc::BaseTarget &target) { (void)target; }, target); + buildcc::Reg::Toolchain(stateSuccess) + .Test("{executable}", target, + buildcc::TestConfig( + {}, {}, + buildcc::TestOutput( + buildcc::TestOutput::Type::TestPrintOnStderr))); std::vector stderr_data; buildcc::env::m::CommandExpect_Execute(1, true, nullptr, &stderr_data); - reg.RunTest(); + buildcc::Reg::Run(); + buildcc::Reg::Deinit(); } // TestOutput::Type::TestPrintOnStdout { - buildcc::Register reg(args); + buildcc::Reg::Init(); mock().expectNCalls(1, "BuildTask_dummyT"); - reg.Build( - stateSuccess, [](buildcc::BaseTarget &target) { (void)target; }, - target); - reg.Test( - stateSuccess, "{executable}", target, - buildcc::TestConfig( - {}, {}, - buildcc::TestOutput(buildcc::TestOutput::Type::TestPrintOnStdout))); + buildcc::Reg::Toolchain(stateSuccess) + .Build([](buildcc::BaseTarget &target) { (void)target; }, target); + buildcc::Reg::Toolchain(stateSuccess) + .Test("{executable}", target, + buildcc::TestConfig( + {}, {}, + buildcc::TestOutput( + buildcc::TestOutput::Type::TestPrintOnStdout))); std::vector stdout_data; buildcc::env::m::CommandExpect_Execute(1, true, &stdout_data, nullptr); - reg.RunTest(); + buildcc::Reg::Run(); + buildcc::Reg::Deinit(); } // TestOutput::Type::TestPrintOnStderrAndStdout { - buildcc::Register reg(args); + buildcc::Reg::Init(); mock().expectNCalls(1, "BuildTask_dummyT"); - reg.Build( - stateSuccess, [](buildcc::BaseTarget &target) { (void)target; }, - target); - reg.Test(stateSuccess, "{executable}", target, - buildcc::TestConfig( - {}, {}, - buildcc::TestOutput( - buildcc::TestOutput::Type::TestPrintOnStderrAndStdout))); + buildcc::Reg::Toolchain(stateSuccess) + .Build([](buildcc::BaseTarget &target) { (void)target; }, target); + buildcc::Reg::Toolchain(stateSuccess) + .Test("{executable}", target, + buildcc::TestConfig( + {}, {}, + buildcc::TestOutput( + buildcc::TestOutput::Type::TestPrintOnStderrAndStdout))); std::vector stdout_data; std::vector stderr_data; buildcc::env::m::CommandExpect_Execute(1, true, &stdout_data, &stderr_data); - reg.RunTest(); + buildcc::Reg::Run(); + buildcc::Reg::Deinit(); } // TestOutput::Type::UserRedirect { - buildcc::Register reg(args); + buildcc::Reg::Init(); mock().expectNCalls(1, "BuildTask_dummyT"); - reg.Build( - stateSuccess, [](buildcc::BaseTarget &target) { (void)target; }, - target); - reg.Test(stateSuccess, "{executable}", target, - buildcc::TestConfig( - {}, {}, - buildcc::TestOutput(buildcc::TestOutput::Type::UserRedirect, - nullptr, nullptr))); + buildcc::Reg::Toolchain(stateSuccess) + .Build([](buildcc::BaseTarget &target) { (void)target; }, target); + buildcc::Reg::Toolchain(stateSuccess) + .Test("{executable}", target, + buildcc::TestConfig( + {}, {}, + buildcc::TestOutput(buildcc::TestOutput::Type::UserRedirect, + nullptr, nullptr))); buildcc::env::m::CommandExpect_Execute(1, true); - reg.RunTest(); + buildcc::Reg::Run(); + buildcc::Reg::Deinit(); } // TestOutput::Type::UserRedirect { - buildcc::Register reg(args); + buildcc::Reg::Init(); mock().expectNCalls(1, "BuildTask_dummyT"); - reg.Build( - stateSuccess, [](buildcc::BaseTarget &target) { (void)target; }, - target); - reg.Test( - stateSuccess, "{executable}", target, - buildcc::TestConfig( - {}, {}, buildcc::TestOutput(buildcc::TestOutput::Type(65535)))); - CHECK_THROWS(std::exception, reg.RunTest()); + buildcc::Reg::Toolchain(stateSuccess) + .Build([](buildcc::BaseTarget &target) { (void)target; }, target); + buildcc::Reg::Toolchain(stateSuccess) + .Test( + "{executable}", target, + buildcc::TestConfig( + {}, {}, buildcc::TestOutput(buildcc::TestOutput::Type(65535)))); + CHECK_THROWS(std::exception, buildcc::Reg::Run()); + buildcc::Reg::Deinit(); } - buildcc::env::deinit(); + buildcc::Project::Deinit(); mock().checkExpectations(); } int main(int ac, char **av) { - MemoryLeakWarningPlugin::turnOffNewDeleteOverloads(); + MemoryLeakWarningPlugin::destroyGlobalDetector(); buildcc::env::m::VectorStringCopier copier; mock().installCopier(TEST_VECTOR_STRING_TYPE, copier); return CommandLineTestRunner::RunAllTests(ac, av); diff --git a/buildcc/lib/env/CMakeLists.txt b/buildcc/lib/env/CMakeLists.txt index ecc6080b..4bba5a42 100644 --- a/buildcc/lib/env/CMakeLists.txt +++ b/buildcc/lib/env/CMakeLists.txt @@ -6,6 +6,7 @@ if (${TESTING}) src/env.cpp src/task_state.cpp + src/storage.cpp src/command.cpp mock/execute.cpp @@ -15,17 +16,21 @@ if (${TESTING}) ${CMAKE_CURRENT_SOURCE_DIR}/mock/include ) target_link_libraries(mock_env PUBLIC - fmt::fmt-header-only + fmt::fmt + tl::optional Taskflow CppUTest CppUTestExt - gcov + ${TEST_LINK_LIBS} ) target_compile_options(mock_env PUBLIC ${TEST_COMPILE_FLAGS} ${BUILD_COMPILE_FLAGS}) target_link_options(mock_env PUBLIC ${TEST_LINK_FLAGS} ${BUILD_LINK_FLAGS}) # Tests + add_executable(test_static_project test/test_static_project.cpp) + target_link_libraries(test_static_project PRIVATE mock_env) + add_executable(test_env_util test/test_env_util.cpp) target_link_libraries(test_env_util PRIVATE mock_env) @@ -35,17 +40,27 @@ if (${TESTING}) add_executable(test_command test/test_command.cpp) target_link_libraries(test_command PRIVATE mock_env) + add_executable(test_storage test/test_storage.cpp) + target_link_libraries(test_storage PRIVATE mock_env) + + add_executable(test_assert_fatal test/test_assert_fatal.cpp) + target_link_libraries(test_assert_fatal PRIVATE mock_env) + + add_test(NAME test_static_project COMMAND test_static_project) add_test(NAME test_env_util COMMAND test_env_util) add_test(NAME test_task_state COMMAND test_task_state) add_test(NAME test_command COMMAND test_command) + add_test(NAME test_storage COMMAND test_storage) + add_test(NAME test_assert_fatal COMMAND test_assert_fatal) endif() set(ENV_SRCS + include/env/optional.h + src/env.cpp src/assert_fatal.cpp src/logging.cpp include/env/assert_fatal.h - include/env/assert_throw.h include/env/env.h include/env/logging.h include/env/util.h @@ -60,6 +75,9 @@ set(ENV_SRCS src/command.cpp src/execute.cpp include/env/command.h + + src/storage.cpp + include/env/storage.h ) if(${BUILDCC_BUILD_AS_SINGLE_LIB}) @@ -81,11 +99,14 @@ if(${BUILDCC_BUILD_AS_INTERFACE}) $ $ ) - target_link_libraries(env PUBLIC fmt::fmt-header-only) + target_link_libraries(env PUBLIC + fmt::fmt + tl::optional + ) target_compile_options(env PRIVATE ${BUILD_COMPILE_FLAGS}) target_link_options(env PRIVATE ${BUILD_LINK_FLAGS}) target_link_libraries(env PRIVATE - spdlog::spdlog_header_only + spdlog::spdlog tiny-process-library::tiny-process-library ) endif() diff --git a/buildcc/lib/env/include/env/assert_fatal.h b/buildcc/lib/env/include/env/assert_fatal.h index 481e7a3c..9f048066 100644 --- a/buildcc/lib/env/include/env/assert_fatal.h +++ b/buildcc/lib/env/include/env/assert_fatal.h @@ -24,7 +24,11 @@ namespace buildcc::env { /** - * @brief During Release -> std::terminate + * @brief During Release -> + * NOT THREADED : std::exit + * THREADED : throw std::exception (it is wrong to exit + * when in a threaded state. We want to handle the exception and gracefully + * exit) * During Unit Test -> throw std::exception */ [[noreturn]] void assert_handle_fatal(); diff --git a/buildcc/lib/env/include/env/assert_throw.h b/buildcc/lib/env/include/env/assert_throw.h deleted file mode 100644 index b070f22e..00000000 --- a/buildcc/lib/env/include/env/assert_throw.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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 ENV_ASSERT_THROW_H_ -#define ENV_ASSERT_THROW_H_ - -#include - -#include "logging.h" - -namespace buildcc::env { - -/** - * @brief Compile time expr asserts fatally when false - */ -template -inline void assert_throw([[maybe_unused]] const char *message) { - if constexpr (!expr) { - env::log_critical("assert", message); - // TODO, If needed specialize this - throw std::exception(); - } -} - -/** - * @brief Compile time expr asserts fatally when false - */ -template inline void assert_throw(const std::string &message) { - assert_throw(message.c_str()); -} - -/** - * @brief Runtime expr asserts fatally when false - */ -inline void assert_throw(bool expression, const char *message) { - if (!expression) { - assert_throw(message); - } -} - -/** - * @brief Runtime expr asserts fatally when false - */ -inline void assert_throw(bool expression, const std::string &message) { - assert_throw(expression, message.c_str()); -} - -} // namespace buildcc::env - -/** - * @brief Runtime expr assert throws when false - */ -#define ASSERT_THROW(expr, message) \ - ((expr) ? static_cast(0) : buildcc::env::assert_throw(message)) - -#endif diff --git a/buildcc/lib/env/include/env/command.h b/buildcc/lib/env/include/env/command.h index 0c54a355..c054e6b3 100644 --- a/buildcc/lib/env/include/env/command.h +++ b/buildcc/lib/env/include/env/command.h @@ -18,11 +18,12 @@ #define ENV_COMMAND_H_ #include -#include #include #include #include +#include "env/optional.h" + namespace fs = std::filesystem; namespace buildcc::env { @@ -74,7 +75,7 @@ class Command { // TODO, Update this to get an integer exit code number instead of boolean // value static bool Execute(const std::string &command, - const std::optional &working_directory = {}, + const optional &working_directory = {}, std::vector *stdout_data = nullptr, std::vector *stderr_data = nullptr); diff --git a/buildcc/lib/env/include/env/env.h b/buildcc/lib/env/include/env/env.h index efb6ead1..c260b28c 100644 --- a/buildcc/lib/env/include/env/env.h +++ b/buildcc/lib/env/include/env/env.h @@ -23,22 +23,27 @@ namespace fs = std::filesystem; -namespace buildcc::env { - -/** - * @brief Initialize project environment - * - * @param project_root_dir Root directory for source files - * @param project_build_dir Directory for intermediate build files - */ -void init(const fs::path &project_root_dir, const fs::path &project_build_dir); -void deinit(); - -// Getters -bool is_init(); -const fs::path &get_project_root_dir(); -const fs::path &get_project_build_dir(); - -} // namespace buildcc::env +namespace buildcc { + +class Project { +public: + Project() = delete; + Project(const Project &) = delete; + Project(Project &&) = delete; + static void Init(const fs::path &project_root_dir, + const fs::path &project_build_dir); + static void Deinit(); + + static bool IsInit(); + static const fs::path &GetRootDir(); + static const fs::path &GetBuildDir(); + +private: + static bool &GetStaticInit(); + static fs::path &GetStaticRootDir(); + static fs::path &GetStaticBuildDir(); +}; + +} // namespace buildcc #endif diff --git a/buildcc/lib/target/include/target/interface/loader_interface.h b/buildcc/lib/env/include/env/optional.h similarity index 58% rename from buildcc/lib/target/include/target/interface/loader_interface.h rename to buildcc/lib/env/include/env/optional.h index 9bf838bd..ed594535 100644 --- a/buildcc/lib/target/include/target/interface/loader_interface.h +++ b/buildcc/lib/env/include/env/optional.h @@ -14,27 +14,15 @@ * limitations under the License. */ -#ifndef TARGET_INTERFACE_LOADER_INTERFACE_H_ -#define TARGET_INTERFACE_LOADER_INTERFACE_H_ +#ifndef ENV_OPTIONAL_H_ +#define ENV_OPTIONAL_H_ -#include +#include "tl/optional.hpp" -namespace fs = std::filesystem; +namespace buildcc::env { -namespace buildcc::internal { +template using optional = tl::optional; -class LoaderInterface { -public: - virtual bool Load() = 0; - - const fs::path &GetBinaryPath() const { return binary_path_; }; - bool IsLoaded() const noexcept { return loaded_; }; - -protected: - bool loaded_{false}; - fs::path binary_path_; -}; - -} // namespace buildcc::internal +} // namespace buildcc::env #endif diff --git a/buildcc/lib/args/include/args/persistent_storage.h b/buildcc/lib/env/include/env/storage.h similarity index 56% rename from buildcc/lib/args/include/args/persistent_storage.h rename to buildcc/lib/env/include/env/storage.h index 573c0172..b5407df8 100644 --- a/buildcc/lib/args/include/args/persistent_storage.h +++ b/buildcc/lib/env/include/env/storage.h @@ -14,10 +14,11 @@ * limitations under the License. */ -#ifndef ARGS_PERSISTENT_STORAGE_H_ -#define ARGS_PERSISTENT_STORAGE_H_ +#ifndef ENV_STORAGE_H_ +#define ENV_STORAGE_H_ #include +#include #include #include #include @@ -28,16 +29,12 @@ namespace buildcc { -class PersistentStorage { +class ScopedStorage { public: - PersistentStorage() {} - ~PersistentStorage() { - for (const auto &ptr_iter : ptrs_) { - ptr_iter.second.destructor(); - } - ptrs_.clear(); - env::assert_fatal(ptrs_.empty(), "Memory not deallocated"); - } + ScopedStorage() {} + ~ScopedStorage() { Clear(); } + + ScopedStorage(const ScopedStorage &) = delete; template T &Add(const std::string &identifier, Params &&...params) { @@ -55,9 +52,16 @@ class PersistentStorage { return *ptr; } - template void Remove(T *ptr) { delete ptr; } + void Clear() { + for (const auto &ptr_iter : ptrs_) { + ptr_iter.second.destructor(); + } + ptrs_.clear(); + } template const T &ConstRef(const std::string &identifier) const { + env::assert_fatal(Contains(identifier), + fmt::format("Could not find '{}'", identifier)); const PtrMetadata &metadata = ptrs_.at(identifier); env::assert_fatal( typeid(T).name() == metadata.typeid_name, @@ -69,19 +73,37 @@ class PersistentStorage { // https://stackoverflow.com/questions/123758/how-do-i-remove-code-duplication-between-similar-const-and-non-const-member-func/123995 template T &Ref(const std::string &identifier) { return const_cast( - static_cast(*this).ConstRef(identifier)); + static_cast(*this).ConstRef(identifier)); + } + + bool Contains(const std::string &identifier) const { + return (ptrs_.find(identifier) != ptrs_.end()); + } + + template bool Valid(const std::string &identifier) const { + if (!Contains(identifier)) { + return false; + } + const PtrMetadata &metadata = ptrs_.at(identifier); + if (typeid(T).name() != metadata.typeid_name) { + return false; + } + return true; } +protected: + template void Remove(T *ptr) { delete ptr; } + private: /** * @brief * @param ptr Can hold data of any type - * @param typeid_name We cannot store a template type so this is the next best - * thing - * @param desstructor Destructor callback to delete ptr + * @param typeid_name We cannot store a template type so this is the next + * best thing + * @param destructor Destructor callback to delete ptr */ struct PtrMetadata { - void *ptr; + void *ptr{nullptr}; std::string typeid_name; std::function destructor; }; @@ -90,6 +112,43 @@ class PersistentStorage { std::unordered_map ptrs_; }; +class Storage { +public: + Storage() = delete; + Storage(const Storage &) = delete; + Storage(Storage &&) = delete; + + template + static T &Add(const std::string &identifier, Params &&...params) { + return Ref().Add(identifier, std::forward(params)...); + } + + static void Clear() { Ref().Clear(); } + + template + static const T &ConstRef(const std::string &identifier) { + return Ref().ConstRef(identifier); + } + + template static T &Ref(const std::string &identifier) { + return Ref().Ref(identifier); + } + + static bool Contains(const std::string &identifier) { + return Ref().Contains(identifier); + } + + template static bool Valid(const std::string &identifier) { + return Ref().Valid(identifier); + } + +private: + static ScopedStorage &Ref(); + +private: + static ScopedStorage internal_; +}; + } // namespace buildcc #endif diff --git a/buildcc/lib/env/include/env/util.h b/buildcc/lib/env/include/env/util.h index be467b34..45a71448 100644 --- a/buildcc/lib/env/include/env/util.h +++ b/buildcc/lib/env/include/env/util.h @@ -49,7 +49,7 @@ namespace buildcc::env { */ inline bool save_file(const char *name, const char *buf, size_t len, bool binary) { - if (buf == nullptr) { + if (name == nullptr || buf == nullptr) { return false; } std::ofstream ofs(name, binary ? std::ofstream::binary : std::ofstream::out); diff --git a/buildcc/lib/env/mock/execute.cpp b/buildcc/lib/env/mock/execute.cpp index 642411e4..7fc7f59b 100644 --- a/buildcc/lib/env/mock/execute.cpp +++ b/buildcc/lib/env/mock/execute.cpp @@ -12,7 +12,7 @@ static constexpr const char *const STDERR_DATA_STRING = "stderr_data"; // command bool Command::Execute(const std::string &command, - const std::optional &working_directory, + const optional &working_directory, std::vector *stdout_data, std::vector *stderr_data) { (void)command; diff --git a/buildcc/lib/env/src/assert_fatal.cpp b/buildcc/lib/env/src/assert_fatal.cpp index 7d7becf3..c1f6dd62 100644 --- a/buildcc/lib/env/src/assert_fatal.cpp +++ b/buildcc/lib/env/src/assert_fatal.cpp @@ -17,9 +17,30 @@ #include "env/assert_fatal.h" #include +#include + +namespace { + +std::thread::id main_id = std::this_thread::get_id(); + +bool IsRunningOnThread() { + bool threaded = true; + if (std::this_thread::get_id() == main_id) { + threaded = false; + } + return threaded; +} + +} // namespace namespace buildcc::env { -[[noreturn]] void assert_handle_fatal() { std::terminate(); } +[[noreturn]] void assert_handle_fatal() { + if (!IsRunningOnThread()) { + std::exit(1); + } else { + throw std::exception(); + } +} } // namespace buildcc::env diff --git a/buildcc/lib/env/src/command.cpp b/buildcc/lib/env/src/command.cpp index 6620c37e..2962aa24 100644 --- a/buildcc/lib/env/src/command.cpp +++ b/buildcc/lib/env/src/command.cpp @@ -61,7 +61,14 @@ std::string Command::Construct( }); // Construct your command - return fmt::vformat(pattern, store); + std::string ret; + try { + ret = fmt::vformat(pattern, store); + } catch (const std::exception &e) { + env::assert_fatal( + fmt::format("Construct command failed: {}", e.what())); + } + return ret; } } // namespace buildcc::env diff --git a/buildcc/lib/env/src/env.cpp b/buildcc/lib/env/src/env.cpp index ffb0f008..64ed8404 100644 --- a/buildcc/lib/env/src/env.cpp +++ b/buildcc/lib/env/src/env.cpp @@ -17,38 +17,45 @@ #include "env/env.h" #include "env/logging.h" -namespace { +namespace buildcc { -fs::path root_dir_{""}; -fs::path build_dir_{""}; -bool init_ = false; - -} // namespace - -namespace buildcc::env { - -void init(const fs::path &project_root_dir, const fs::path &project_build_dir) { +void Project::Init(const fs::path &project_root_dir, + const fs::path &project_build_dir) { // State - root_dir_ = project_root_dir; - build_dir_ = project_build_dir; - root_dir_.make_preferred(); - build_dir_.make_preferred(); + fs::path root_dir = project_root_dir; + fs::path build_dir = project_build_dir; + root_dir.make_preferred(); + build_dir.make_preferred(); - init_ = true; + GetStaticRootDir() = root_dir; + GetStaticBuildDir() = build_dir; + GetStaticInit() = true; // Logging - set_log_pattern("%^[%l]%$ %v"); - set_log_level(LogLevel::Info); + env::set_log_pattern("%^[%l]%$ %v"); + env::set_log_level(env::LogLevel::Info); } - -void deinit() { - root_dir_ = ""; - build_dir_ = ""; - init_ = false; +void Project::Deinit() { + GetStaticRootDir() = ""; + GetStaticBuildDir() = ""; + GetStaticInit() = false; } -bool is_init(void) { return init_; } -const fs::path &get_project_root_dir() { return root_dir_; } -const fs::path &get_project_build_dir() { return build_dir_; } +bool Project::IsInit() { return GetStaticInit(); } +const fs::path &Project::GetRootDir() { return GetStaticRootDir(); } +const fs::path &Project::GetBuildDir() { return GetStaticBuildDir(); } + +bool &Project::GetStaticInit() { + static bool is_init = false; + return is_init; +} +fs::path &Project::GetStaticRootDir() { + static fs::path root_dir = ""; + return root_dir; +} +fs::path &Project::GetStaticBuildDir() { + static fs::path build_dir = ""; + return build_dir; +} -} // namespace buildcc::env +} // namespace buildcc diff --git a/buildcc/lib/env/src/execute.cpp b/buildcc/lib/env/src/execute.cpp index 60fe41cf..4281d442 100644 --- a/buildcc/lib/env/src/execute.cpp +++ b/buildcc/lib/env/src/execute.cpp @@ -28,8 +28,8 @@ namespace tpl = TinyProcessLib; namespace { -tpl::Process::string_type -get_working_directory(const std::optional &working_directory) { +tpl::Process::string_type get_working_directory( + const buildcc::env::optional &working_directory) { #ifdef UNICODE return working_directory.value_or(tpl::Process::string_type()).wstring(); #else @@ -42,7 +42,7 @@ get_working_directory(const std::optional &working_directory) { namespace buildcc::env { bool Command::Execute(const std::string &command, - const std::optional &working_directory, + const optional &working_directory, std::vector *stdout_data, std::vector *stderr_data) { env::assert_fatal(!command.empty(), "Empty command"); diff --git a/buildcc/lib/env/src/storage.cpp b/buildcc/lib/env/src/storage.cpp new file mode 100644 index 00000000..0da3ac5e --- /dev/null +++ b/buildcc/lib/env/src/storage.cpp @@ -0,0 +1,25 @@ +/* + * 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. + */ + +#include "env/storage.h" + +namespace buildcc { + +ScopedStorage Storage::internal_; + +ScopedStorage &Storage::Ref() { return internal_; } + +} // namespace buildcc diff --git a/buildcc/lib/env/test/test_assert_fatal.cpp b/buildcc/lib/env/test/test_assert_fatal.cpp new file mode 100644 index 00000000..67e3b986 --- /dev/null +++ b/buildcc/lib/env/test/test_assert_fatal.cpp @@ -0,0 +1,72 @@ +#include + +#include "taskflow/taskflow.hpp" + +// 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" + +namespace { + +std::thread::id my_main_thread = std::this_thread::get_id(); + +bool IsRunningInThread() { + bool threaded = true; + if (std::this_thread::get_id() == my_main_thread) { + threaded = false; + } + return threaded; +} + +void assert_handle_fatal() { + if (IsRunningInThread()) { + mock().actualCall("assert_handle_fatal_threaded"); + } else { + mock().actualCall("assert_handle_fatal_main"); + } +} + +} // namespace + +// clang-format off +TEST_GROUP(AssertFatalTestGroup) +{ + void teardown() { + mock().clear(); + } +}; +// clang-format on + +TEST(AssertFatalTestGroup, AssertFatal_IsThreadedCheck) { + CHECK_FALSE(IsRunningInThread()); + + tf::Taskflow tf; + tf.emplace([]() { CHECK_TRUE(IsRunningInThread()); }); + + tf::Executor ex(1); + ex.run(tf); + ex.wait_for_all(); +} + +TEST(AssertFatalTestGroup, AssertFatal_Threaded) { + mock().expectOneCall("assert_handle_fatal_threaded"); + + tf::Taskflow tf; + tf.emplace([]() { assert_handle_fatal(); }); + + tf::Executor ex(1); + ex.run(tf); + ex.wait_for_all(); +} + +TEST(AssertFatalTestGroup, AssertFatal_NotThreaded) { + mock().expectOneCall("assert_handle_fatal_main"); + assert_handle_fatal(); +} + +int main(int ac, char **av) { + return CommandLineTestRunner::RunAllTests(ac, av); +} diff --git a/buildcc/lib/env/test/test_env_util.cpp b/buildcc/lib/env/test/test_env_util.cpp index cd1fe63c..3e6d867d 100644 --- a/buildcc/lib/env/test/test_env_util.cpp +++ b/buildcc/lib/env/test/test_env_util.cpp @@ -33,13 +33,6 @@ TEST(EnvUtilTestGroup, Util_SaveFile_NullptrName) { CHECK_FALSE(save); } -TEST(EnvUtilTestGroup, Util_SaveFile_BadWrite) { - constexpr const char *const FILENAME = "BadWrite.txt"; - fs::remove(FILENAME); - bool save = buildcc::env::save_file(FILENAME, "Hello", -1, false); - CHECK_FALSE(save); -} - TEST(EnvUtilTestGroup, Util_SaveFile_GoodWrite) { constexpr const char *const FILENAME = "GoodWrite.txt"; fs::remove(FILENAME); @@ -47,13 +40,6 @@ TEST(EnvUtilTestGroup, Util_SaveFile_GoodWrite) { CHECK_TRUE(save); } -TEST(EnvUtilTestGroup, Util_SaveFile_BadWrite_Binary) { - constexpr const char *const FILENAME = "BadWrite_Binary.txt"; - fs::remove(FILENAME); - bool save = buildcc::env::save_file(FILENAME, "Hello", -1, true); - CHECK_FALSE(save); -} - TEST(EnvUtilTestGroup, Util_SaveFile_GoodWrite_Binary) { constexpr const char *const FILENAME = "GoodWrite_Binary.txt"; fs::remove(FILENAME); @@ -69,22 +55,6 @@ TEST(EnvUtilTestGroup, Util_SaveFile_CheckDirectory) { CHECK_FALSE(save); } -TEST(EnvUtilTestGroup, Util_SaveFile_CannotWrite) { - constexpr const char *const FILENAME = "CannotWrite.txt"; - fs::remove(FILENAME); - bool save = buildcc::env::save_file(FILENAME, "Hello", false); - CHECK_TRUE(save); - - std::error_code err; - fs::permissions(FILENAME, fs::perms::none, err); - if (err) { - FAIL("Cannot disable file permissions"); - } - - save = buildcc::env::save_file(FILENAME, "Hello", false); - CHECK_FALSE(save); -} - // Load File TEST(EnvUtilTestGroup, Util_LoadFile_CheckDirectory) { // NOTE, This is a directory @@ -143,6 +113,8 @@ TEST(EnvUtilTestGroup, Util_LoadFile_ReadTxt) { CHECK_TRUE(load); } +#if defined(__GNUC__) && !defined(__MINGW32__) && !defined(__MINGW64__) + TEST(EnvUtilTestGroup, Util_LoadFile_CannotOpen) { constexpr const char *const FILENAME = "CannotOpen.txt"; buildcc::env::save_file(FILENAME, "Random Data", false); @@ -159,6 +131,38 @@ TEST(EnvUtilTestGroup, Util_LoadFile_CannotOpen) { CHECK_FALSE(load); } +TEST(EnvUtilTestGroup, Util_SaveFile_BadWrite_Binary) { + constexpr const char *const FILENAME = "BadWrite_Binary.txt"; + fs::remove(FILENAME); + bool save = buildcc::env::save_file(FILENAME, "Hello", -1, true); + CHECK_FALSE(save); +} + +TEST(EnvUtilTestGroup, Util_SaveFile_BadWrite) { + constexpr const char *const FILENAME = "BadWrite.txt"; + fs::remove(FILENAME); + bool save = buildcc::env::save_file(FILENAME, "Hello", -1, false); + CHECK_FALSE(save); +} + +TEST(EnvUtilTestGroup, Util_SaveFile_CannotWrite) { + constexpr const char *const FILENAME = "CannotWrite.txt"; + fs::remove(FILENAME); + bool save = buildcc::env::save_file(FILENAME, "Hello", false); + CHECK_TRUE(save); + + std::error_code err; + fs::permissions(FILENAME, fs::perms::none, err); + if (err) { + FAIL("Cannot disable file permissions"); + } + + save = buildcc::env::save_file(FILENAME, "Hello", false); + CHECK_FALSE(save); +} + +#endif + TEST(EnvUtilTestGroup, Util_Split) { { std::vector paths = buildcc::env::split("", ':'); diff --git a/buildcc/lib/env/test/test_static_project.cpp b/buildcc/lib/env/test/test_static_project.cpp new file mode 100644 index 00000000..b5b4a5aa --- /dev/null +++ b/buildcc/lib/env/test/test_static_project.cpp @@ -0,0 +1,28 @@ +#include "env/env.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" + +// clang-format off +TEST_GROUP(StaticProjectTestGroup) +{ + void setup() { + } +}; +// clang-format on + +TEST(StaticProjectTestGroup, ProjectInitialized) { + CHECK_FALSE(buildcc::Project::IsInit()); + buildcc::Project::Init(fs::current_path(), fs::current_path()); + CHECK_TRUE(buildcc::Project::IsInit()); + buildcc::Project::Deinit(); + CHECK_FALSE(buildcc::Project::IsInit()); +} + +int main(int ac, char **av) { + MemoryLeakWarningPlugin::turnOffNewDeleteOverloads(); + return CommandLineTestRunner::RunAllTests(ac, av); +} diff --git a/buildcc/lib/env/test/test_storage.cpp b/buildcc/lib/env/test/test_storage.cpp new file mode 100644 index 00000000..4c38b134 --- /dev/null +++ b/buildcc/lib/env/test/test_storage.cpp @@ -0,0 +1,118 @@ +#include "env/storage.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(ScopedStorageTestGroup) +{ +}; + +TEST_GROUP(StorageTestGroup) +{ + void setup() { + MemoryLeakWarningPlugin::saveAndDisableNewDeleteOverloads(); + } + void teardown() { + buildcc::Storage::Clear(); + MemoryLeakWarningPlugin::restoreNewDeleteOverloads(); + } +}; +// clang-format on + +class MyScopedStorage : public buildcc::ScopedStorage { +public: + // We want to unit test this + template void Remove(T *ptr) { + this->ScopedStorage::Remove(ptr); + } +}; + +class BigObj {}; + +class BigObjWithParameters { +public: + BigObjWithParameters(const std::string &name, int id, const BigObj &obj) + : name_(name) { + (void)id; + (void)obj; + } + + const std::string &GetName() const { return name_; } + +private: + std::string name_; +}; + +static BigObj obj; + +TEST(ScopedStorageTestGroup, BasicUsage) { + MyScopedStorage storage; + storage.Add("identifier", "name", 10, obj); + storage.Add("identifier2", "name2", 12, obj); + + // Usage + storage.ConstRef("identifier").GetName(); + storage.Ref("identifier2").GetName(); + + CHECK_TRUE(storage.Contains("identifier")); + CHECK_FALSE(storage.Contains("identifier_does_not_exist")); + + CHECK_TRUE(storage.Valid("identifier")); + CHECK_FALSE(storage.Valid("wrong_identifier")); + CHECK_FALSE(storage.Valid("identifier")); + + storage.Clear(); + CHECK_FALSE(storage.Contains("identifier")); + + // Automatic cleanup here +} + +TEST(ScopedStorageTestGroup, IncorrectUsage) { + MyScopedStorage storage; + storage.Add("identifier", "name", 10, obj); + + // We try to cast to a different type! + CHECK_THROWS(std::exception, storage.Ref("identifier")); + + // We use a wrong identifier + CHECK_THROWS(std::exception, + storage.Ref("identifier2")); +} + +TEST(ScopedStorageTestGroup, NullptrDelete) { + MyScopedStorage storage; + storage.Remove(nullptr); +} + +// + +TEST(StorageTestGroup, BasicUsage) { + buildcc::Storage::Add("identifier", "name", 10, obj); + buildcc::Storage::Add("identifier2", "name2", 12, obj); + + // Usage + const auto &bigobj = + buildcc::Storage::ConstRef("identifier").GetName(); + const auto &bigobj2 = + buildcc::Storage::Ref("identifier2").GetName(); + + STRCMP_EQUAL(bigobj.c_str(), "name"); + STRCMP_EQUAL(bigobj2.c_str(), "name2"); + + CHECK_TRUE(buildcc::Storage::Contains("identifier")); + CHECK_FALSE(buildcc::Storage::Contains("identifier_does_not_exist")); + + CHECK_TRUE(buildcc::Storage::Valid("identifier")); + CHECK_FALSE( + buildcc::Storage::Valid("wrong_identifier")); + CHECK_FALSE(buildcc::Storage::Valid("identifier")); +} + +int main(int ac, char **av) { + return CommandLineTestRunner::RunAllTests(ac, av); +} diff --git a/buildcc/lib/target/CMakeLists.txt b/buildcc/lib/target/CMakeLists.txt index a1cf05c2..e530fde4 100644 --- a/buildcc/lib/target/CMakeLists.txt +++ b/buildcc/lib/target/CMakeLists.txt @@ -4,7 +4,6 @@ include(cmake/common_target_src.cmake) if (${TESTING}) set(TARGET_DIR "${CMAKE_CURRENT_SOURCE_DIR}") include(cmake/mock_target.cmake) - add_subdirectory(test/path) add_subdirectory(test/target) endif() diff --git a/buildcc/lib/target/cmake/common_target_src.cmake b/buildcc/lib/target/cmake/common_target_src.cmake index 5a662c46..69558576 100644 --- a/buildcc/lib/target/cmake/common_target_src.cmake +++ b/buildcc/lib/target/cmake/common_target_src.cmake @@ -1,52 +1,43 @@ set(COMMON_TARGET_SRCS # Interfaces - include/target/interface/loader_interface.h include/target/interface/builder_interface.h # Common src/common/target_config.cpp src/common/target_state.cpp - include/target/common/target_file_ext.h include/target/common/target_config.h include/target/common/target_state.h include/target/common/target_env.h - include/target/common/target_type.h - - src/common/util.cpp include/target/common/util.h - include/target/common/path.h - # API - src/api/source_api.cpp - src/api/include_api.cpp src/api/lib_api.cpp - src/api/pch_api.cpp - src/api/flag_api.cpp - src/api/deps_api.cpp include/target/api/source_api.h include/target/api/include_api.h include/target/api/lib_api.h include/target/api/pch_api.h - include/target/api/flag_api.h include/target/api/deps_api.h src/api/sync_api.cpp include/target/api/sync_api.h - src/api/target_info_getter.cpp src/api/target_getter.cpp - include/target/api/target_info_getter.h include/target/api/target_getter.h - # Base Generator - src/generator/generator_loader.cpp - src/generator/generator_storer.cpp - include/target/base/generator_loader.h - # Generator - src/generator/generator.cpp - include/target/generator.h + 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 + include/target/file_generator.h + src/generator/template_generator.cpp + include/target/template_generator.h + + # Target Info + src/target_info/target_info.cpp + include/target/target_info.h # Target friend src/target/friend/compile_pch.cpp @@ -56,15 +47,9 @@ set(COMMON_TARGET_SRCS include/target/friend/compile_object.h include/target/friend/link_target.h - # Base Target - src/target/target_loader.cpp - src/target/target_storer.cpp - include/target/base/target_loader.h - include/target/base/target_storer.h - # Target src/target/target.cpp src/target/build.cpp - include/target/target_info.h + src/target/tasks.cpp include/target/target.h ) diff --git a/buildcc/lib/target/cmake/mock_target.cmake b/buildcc/lib/target/cmake/mock_target.cmake index 367fc0ac..a648c983 100644 --- a/buildcc/lib/target/cmake/mock_target.cmake +++ b/buildcc/lib/target/cmake/mock_target.cmake @@ -1,33 +1,28 @@ add_library(mock_target STATIC ${COMMON_TARGET_SRCS} - - # Generator mocks - src/generator/task.cpp - mock/generator/runner.cpp - mock/generator/recheck_states.cpp + # Custom Generator mocks + mock/custom_generator/runner.cpp + mock/custom_generator/recheck_states.cpp # Target mocks - src/target/tasks.cpp mock/target/runner.cpp mock/target/recheck_states.cpp ) target_include_directories(mock_target PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/mock - ${SCHEMA_BUILD_DIR} ) target_compile_options(mock_target PUBLIC ${TEST_COMPILE_FLAGS} ${BUILD_COMPILE_FLAGS}) target_link_options(mock_target PUBLIC ${TEST_LINK_FLAGS} ${BUILD_LINK_FLAGS}) target_link_libraries(mock_target PUBLIC - flatbuffers_header_only Taskflow mock_toolchain CppUTest CppUTestExt - gcov + ${TEST_LINK_LIBS} ) # https://github.com/msys2/MINGW-packages/issues/2303 @@ -36,5 +31,3 @@ if (${MINGW}) message(WARNING "-Wl,--allow-multiple-definition for MINGW") target_link_options(mock_target PUBLIC -Wl,--allow-multiple-definition) endif() - -add_dependencies(mock_target fbs_to_header) diff --git a/buildcc/lib/target/cmake/target.cmake b/buildcc/lib/target/cmake/target.cmake index c67dd500..dc31284a 100644 --- a/buildcc/lib/target/cmake/target.cmake +++ b/buildcc/lib/target/cmake/target.cmake @@ -1,11 +1,9 @@ set(TARGET_SRCS ${COMMON_TARGET_SRCS} - src/generator/task.cpp - src/generator/recheck_states.cpp + src/custom_generator/recheck_states.cpp src/target/recheck_states.cpp - src/target/tasks.cpp ) if(${BUILDCC_BUILD_AS_SINGLE_LIB}) @@ -16,10 +14,6 @@ if(${BUILDCC_BUILD_AS_SINGLE_LIB}) $ $ ) - target_include_directories(buildcc PRIVATE - ${SCHEMA_BUILD_DIR} - ) - add_dependencies(buildcc fbs_to_header) endif() if(${BUILDCC_BUILD_AS_INTERFACE}) @@ -33,14 +27,8 @@ if(${BUILDCC_BUILD_AS_INTERFACE}) ) target_link_libraries(target PUBLIC toolchain - flatbuffers_header_only Taskflow ) - - target_include_directories(target PRIVATE - ${SCHEMA_BUILD_DIR} - ) target_compile_options(target PRIVATE ${BUILD_COMPILE_FLAGS}) target_link_options(target PRIVATE ${BUILD_LINK_FLAGS}) - add_dependencies(target fbs_to_header) endif() diff --git a/buildcc/lib/target/include/target/api/deps_api.h b/buildcc/lib/target/include/target/api/deps_api.h index 16ba7707..dfb5bb99 100644 --- a/buildcc/lib/target/include/target/api/deps_api.h +++ b/buildcc/lib/target/include/target/api/deps_api.h @@ -19,43 +19,73 @@ #include +#include "schema/path.h" + namespace fs = std::filesystem; namespace buildcc::internal { // Requires -// - TargetStorer -// - TargetState -// - TargetEnv +// User::CompileDependencies +// User::LinkDependencies +// TargetEnv template class DepsApi { public: // TODO, AddPchDependency // TODO, Rename AddObjectDependency // TODO, Rename AddTargetDependency + std::vector GetCompileDependencies() const { + const auto &t = static_cast(*this); + return t.user_.compile_dependencies.GetPaths(); + } + + std::vector GetLinkDependencies() const { + const auto &t = static_cast(*this); + return t.user_.link_dependencies.GetPaths(); + } + /** * @brief Recompile sources to object if compile dependency is removed, added * or newer from the previous build */ - void AddCompileDependency(const fs::path &relative_path); + void AddCompileDependencyAbsolute(const fs::path &absolute_path) { + auto &t = static_cast(*this); + + t.user_.compile_dependencies.Emplace(absolute_path, ""); + } /** * @brief Recompile sources to object if compile dependency is removed, added * or newer from the previous build */ - void AddCompileDependencyAbsolute(const fs::path &absolute_path); + void AddCompileDependency(const fs::path &relative_path) { + auto &t = static_cast(*this); + + fs::path absolute_path = t.env_.GetTargetRootDir() / relative_path; + AddCompileDependencyAbsolute(absolute_path); + } /** * @brief Relink target if link dependency is removed, added or newer from * previous build */ - void AddLinkDependency(const fs::path &relative_path); + void AddLinkDependencyAbsolute(const fs::path &absolute_path) { + auto &t = static_cast(*this); + + t.user_.link_dependencies.Emplace(absolute_path, ""); + } /** * @brief Relink target if link dependency is removed, added or newer from * previous build */ - void AddLinkDependencyAbsolute(const fs::path &absolute_path); + void AddLinkDependency(const fs::path &relative_path) { + auto &t = static_cast(*this); + + fs::path absolute_path = t.env_.GetTargetRootDir() / relative_path; + AddLinkDependencyAbsolute(absolute_path); + } }; } // namespace buildcc::internal diff --git a/buildcc/lib/target/include/target/api/flag_api.h b/buildcc/lib/target/include/target/api/flag_api.h deleted file mode 100644 index a51ae94f..00000000 --- a/buildcc/lib/target/include/target/api/flag_api.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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_API_FLAG_API_H_ -#define TARGET_API_FLAG_API_H_ - -#include - -namespace buildcc::internal { - -// Requires -// - TargetStorer -// - TargetState -template class FlagApi { -public: - void AddPreprocessorFlag(const std::string &flag); - void AddCommonCompileFlag(const std::string &flag); - void AddPchCompileFlag(const std::string &flag); - void AddPchObjectFlag(const std::string &flag); - void AddAsmCompileFlag(const std::string &flag); - void AddCCompileFlag(const std::string &flag); - void AddCppCompileFlag(const std::string &flag); - void AddLinkFlag(const std::string &flag); -}; - -} // namespace buildcc::internal - -#endif diff --git a/buildcc/lib/target/include/target/api/include_api.h b/buildcc/lib/target/include/target/api/include_api.h index 94307e81..d6702dc1 100644 --- a/buildcc/lib/target/include/target/api/include_api.h +++ b/buildcc/lib/target/include/target/api/include_api.h @@ -19,28 +19,83 @@ #include +#include "schema/path.h" + namespace fs = std::filesystem; namespace buildcc::internal { // Requires -// - TargetStorer -// - TargetState -// - TargetConfig -// - TargetEnv +// Toolchain +// User::Headers +// User::IncludeDirs +// TargetEnv template class IncludeApi { public: + std::vector GetHeaderFiles() const { + const auto &t = static_cast(*this); + return t.user_.headers.GetPaths(); + } + + const std::vector &GetIncludeDirs() const { + const auto &t = static_cast(*this); + return t.user_.include_dirs.GetPaths(); + } + + void AddHeaderAbsolute(const fs::path &absolute_filepath) { + auto &t = static_cast(*this); + + t.toolchain_.GetConfig().ExpectsValidHeader(absolute_filepath); + t.user_.headers.Emplace(absolute_filepath, ""); + } + + void GlobHeadersAbsolute(const fs::path &absolute_path) { + auto &t = static_cast(*this); + + for (const auto &p : fs::directory_iterator(absolute_path)) { + if (t.toolchain_.GetConfig().IsValidHeader(p.path())) { + AddHeaderAbsolute(p.path()); + } + } + } + + void AddIncludeDirAbsolute(const fs::path &absolute_include_dir, + bool glob_headers = false) { + auto &t = static_cast(*this); + + t.user_.include_dirs.Emplace(absolute_include_dir); + + if (glob_headers) { + GlobHeadersAbsolute(absolute_include_dir); + } + } + void AddHeader(const fs::path &relative_filename, - const fs::path &relative_to_target_path = ""); - void AddHeaderAbsolute(const fs::path &absolute_filepath); + const fs::path &relative_to_target_path = "") { + auto &t = static_cast(*this); + + // Check Source + fs::path absolute_filepath = + t.env_.GetTargetRootDir() / relative_to_target_path / relative_filename; + AddHeaderAbsolute(absolute_filepath); + } - void GlobHeaders(const fs::path &relative_to_target_path = ""); - void GlobHeadersAbsolute(const fs::path &absolute_path); + void GlobHeaders(const fs::path &relative_to_target_path = "") { + auto &t = static_cast(*this); + + fs::path absolute_path = + t.env_.GetTargetRootDir() / relative_to_target_path; + GlobHeadersAbsolute(absolute_path); + } void AddIncludeDir(const fs::path &relative_include_dir, - bool glob_headers = false); - void AddIncludeDirAbsolute(const fs::path &absolute_include_dir, - bool glob_headers = false); + bool glob_headers = false) { + auto &t = static_cast(*this); + + const fs::path absolute_include_dir = + t.env_.GetTargetRootDir() / relative_include_dir; + AddIncludeDirAbsolute(absolute_include_dir, glob_headers); + } }; } // namespace buildcc::internal diff --git a/buildcc/lib/target/include/target/api/lib_api.h b/buildcc/lib/target/include/target/api/lib_api.h index 834ec3e7..5506bdf1 100644 --- a/buildcc/lib/target/include/target/api/lib_api.h +++ b/buildcc/lib/target/include/target/api/lib_api.h @@ -19,6 +19,9 @@ #include #include +#include + +#include "schema/path.h" namespace fs = std::filesystem; @@ -31,17 +34,47 @@ class Target; namespace buildcc::internal { // Requires -// - TargetStorer -// - TargetState -// - TargetEnv -// T::GetTargetPath +// User::LibDirs +// User::Libs +// User::ExternalLibs +// TargetEnv +// Target::GetTargetPath template class LibApi { public: - void AddLibDep(const Target &lib_dep); - void AddLibDep(const std::string &lib_dep); + std::vector GetLibDeps() const { + const auto &t = static_cast(*this); + return t.user_.libs.GetPaths(); + } + + const std::vector &GetExternalLibDeps() const { + const auto &t = static_cast(*this); + return t.user_.external_libs; + } + + const std::vector &GetLibDirs() const { + const auto &t = static_cast(*this); + return t.user_.lib_dirs.GetPaths(); + } - void AddLibDir(const fs::path &relative_lib_dir); - void AddLibDirAbsolute(const fs::path &absolute_lib_dir); + void AddLibDirAbsolute(const fs::path &absolute_lib_dir) { + auto &t = static_cast(*this); + t.user_.lib_dirs.Emplace(absolute_lib_dir); + } + + void AddLibDir(const fs::path &relative_lib_dir) { + auto &t = static_cast(*this); + fs::path final_lib_dir = t.env_.GetTargetRootDir() / relative_lib_dir; + AddLibDirAbsolute(final_lib_dir); + } + + void AddLibDep(const std::string &lib_dep) { + auto &t = static_cast(*this); + t.user_.external_libs.push_back(lib_dep); + } + + // Target class has been forward declared + // This is because this file is meant to be used by `TargetInfo` and `Target` + void AddLibDep(const Target &lib_dep); }; } // namespace buildcc::internal diff --git a/buildcc/lib/target/include/target/api/pch_api.h b/buildcc/lib/target/include/target/api/pch_api.h index 0955ff95..dec92f8d 100644 --- a/buildcc/lib/target/include/target/api/pch_api.h +++ b/buildcc/lib/target/include/target/api/pch_api.h @@ -19,20 +19,42 @@ #include +#include "schema/path.h" + namespace fs = std::filesystem; namespace buildcc::internal { // Requires -// - TargetStorer -// - TargetState -// - TargetConfig -// - TargetEnv +// Toolchain +// User::Pchs +// TargetEnv template class PchApi { public: + std::vector GetPchFiles() const { + const auto &t = static_cast(*this); + return t.user_.pchs.GetPaths(); + } + + void AddPchAbsolute(const fs::path &absolute_filepath) { + auto &t = static_cast(*this); + + t.toolchain_.GetConfig().ExpectsValidHeader(absolute_filepath); + + const fs::path absolute_pch = fs::path(absolute_filepath).make_preferred(); + t.user_.pchs.Emplace(absolute_pch, ""); + } + void AddPch(const fs::path &relative_filename, - const fs::path &relative_to_target_path = ""); - void AddPchAbsolute(const fs::path &absolute_filepath); + const fs::path &relative_to_target_path = "") { + auto &t = static_cast(*this); + + // Compute the absolute source path + fs::path absolute_pch = + t.env_.GetTargetRootDir() / relative_to_target_path / relative_filename; + + AddPchAbsolute(absolute_pch); + } }; } // namespace buildcc::internal diff --git a/buildcc/lib/target/include/target/api/source_api.h b/buildcc/lib/target/include/target/api/source_api.h index f90c2118..d3674158 100644 --- a/buildcc/lib/target/include/target/api/source_api.h +++ b/buildcc/lib/target/include/target/api/source_api.h @@ -19,23 +19,61 @@ #include +#include "schema/path.h" + namespace fs = std::filesystem; namespace buildcc::internal { // Requires -// - TargetStorer -// - TargetState -// - TargetConfig -// - TargetEnv +// Toolchain +// User::Sources +// TargetEnv template class SourceApi { public: - void AddSourceAbsolute(const fs::path &absolute_source); - void GlobSourcesAbsolute(const fs::path &absolute_source_dir); + std::vector GetSourceFiles() const { + const auto &t = static_cast(*this); + return t.user_.sources.GetPaths(); + } + + void AddSourceAbsolute(const fs::path &absolute_source) { + auto &t = static_cast(*this); + + t.toolchain_.GetConfig().ExpectsValidSource(absolute_source); + t.user_.sources.Emplace(absolute_source, ""); + } + + void GlobSourcesAbsolute(const fs::path &absolute_source_dir) { + auto &t = static_cast(*this); + + for (const auto &p : fs::directory_iterator(absolute_source_dir)) { + if (t.toolchain_.GetConfig().IsValidSource(p.path())) { + AddSourceAbsolute(p.path()); + } + } + } void AddSource(const fs::path &relative_source, - const fs::path &relative_to_target_path = ""); - void GlobSources(const fs::path &relative_to_target_path = ""); + const fs::path &relative_to_target_path = "") { + auto &t = static_cast(*this); + + // Compute the absolute source path + fs::path absolute_source = + t.env_.GetTargetRootDir() / relative_to_target_path / relative_source; + AddSourceAbsolute(absolute_source); + } + + void GlobSources(const fs::path &relative_to_target_path = "") { + auto &t = static_cast(*this); + + fs::path absolute_input_path = + t.env_.GetTargetRootDir() / relative_to_target_path; + for (const auto &p : fs::directory_iterator(absolute_input_path)) { + if (t.toolchain_.GetConfig().IsValidSource(p.path())) { + AddSourceAbsolute(p.path()); + } + } + } }; } // namespace buildcc::internal diff --git a/buildcc/lib/target/include/target/api/sync_api.h b/buildcc/lib/target/include/target/api/sync_api.h index 37910d67..c374caab 100644 --- a/buildcc/lib/target/include/target/api/sync_api.h +++ b/buildcc/lib/target/include/target/api/sync_api.h @@ -43,7 +43,6 @@ enum class SyncOption { // Requires // - TargetStorer -// - TargetState template class SyncApi { public: /** diff --git a/buildcc/lib/target/include/target/api/target_getter.h b/buildcc/lib/target/include/target/api/target_getter.h index b9230c81..7a05a79f 100644 --- a/buildcc/lib/target/include/target/api/target_getter.h +++ b/buildcc/lib/target/include/target/api/target_getter.h @@ -20,10 +20,13 @@ #include #include -#include "target/common/target_type.h" +#include "schema/target_type.h" #include "toolchain/toolchain.h" +#include "target/common/target_config.h" +#include "target/common/target_state.h" + #include "taskflow/taskflow.hpp" namespace fs = std::filesystem; @@ -32,6 +35,13 @@ namespace buildcc::internal { template class TargetGetter { public: + // Target State + const TargetState &GetState() const; + bool IsBuilt() const; + + // Target Config + const TargetConfig &GetConfig() const; + const std::string &GetName() const; const Toolchain &GetToolchain() const; TargetType GetType() const; @@ -64,8 +74,6 @@ template class TargetGetter { // TODO, Add GetPchCommand if required const std::string &GetCompileCommand(const fs::path &source) const; const std::string &GetLinkCommand() const; - - tf::Taskflow &GetTaskflow(); }; }; // namespace buildcc::internal diff --git a/buildcc/lib/target/include/target/api/target_info_getter.h b/buildcc/lib/target/include/target/api/target_info_getter.h deleted file mode 100644 index 7c6f191c..00000000 --- a/buildcc/lib/target/include/target/api/target_info_getter.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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_API_TARGET_INFO_GETTER_H_ -#define TARGET_API_TARGET_INFO_GETTER_H_ - -#include "target/common/path.h" - -#include "target/common/target_config.h" -#include "target/common/target_state.h" - -namespace buildcc::internal { - -// Requires -// - TargetStorer -// - TargetState -// - TargetEnv -// - TargetConfig -template class TargetInfoGetter { -public: - // Target State - const TargetState &GetState() const; - bool IsBuilt() const; - bool IsLocked() const; - - // Target Env - const fs::path &GetTargetRootDir() const; - const fs::path &GetTargetBuildDir() const; - - // Target Config - const TargetConfig &GetConfig() const; - - // Target Storer - const fs_unordered_set &GetSourceFiles() const; - const fs_unordered_set &GetHeaderFiles() const; - const fs_unordered_set &GetPchFiles() const; - const std::vector &GetLibDeps() const; - const std::vector &GetExternalLibDeps() const; - const fs_unordered_set &GetIncludeDirs() const; - const fs_unordered_set &GetLibDirs() const; - const std::unordered_set &GetPreprocessorFlags() const; - const std::unordered_set &GetCommonCompileFlags() const; - const std::unordered_set &GetPchCompileFlags() const; - const std::unordered_set &GetPchObjectFlags() const; - const std::unordered_set &GetAsmCompileFlags() const; - const std::unordered_set &GetCCompileFlags() const; - const std::unordered_set &GetCppCompileFlags() const; - const std::unordered_set &GetLinkFlags() const; - const fs_unordered_set &GetCompileDependencies() const; - const fs_unordered_set &GetLinkDependencies() const; -}; - -} // namespace buildcc::internal - -#endif diff --git a/buildcc/lib/target/include/target/base/generator_loader.h b/buildcc/lib/target/include/target/base/generator_loader.h deleted file mode 100644 index a3bc0e85..00000000 --- a/buildcc/lib/target/include/target/base/generator_loader.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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_BASE_GENERATOR_LOADER_H_ -#define TARGET_BASE_GENERATOR_LOADER_H_ - -#include "target/interface/loader_interface.h" - -#include -#include -#include - -#include "fmt/format.h" - -#include "target/common/path.h" - -namespace buildcc::internal { - -class GeneratorLoader : public LoaderInterface { -public: - GeneratorLoader(const std::string &name, const fs::path &absolute_path) - : name_(name), path_(absolute_path) { - binary_path_ = absolute_path / fmt::format("{}.bin", name); - } - - GeneratorLoader(const GeneratorLoader &loader) = delete; - - bool Load() override; - - // Getters - const internal::path_unordered_set &GetLoadedInputFiles() const noexcept { - return loaded_input_files_; - } - - const fs_unordered_set &GetLoadedOutputFiles() const noexcept { - return loaded_output_files_; - } - - const std::vector &GetLoadedCommands() const noexcept { - return loaded_commands_; - } - -private: - std::string name_; - fs::path path_; - - internal::path_unordered_set loaded_input_files_; - fs_unordered_set loaded_output_files_; - std::vector loaded_commands_; -}; - -} // namespace buildcc::internal - -#endif diff --git a/buildcc/lib/target/include/target/base/target_loader.h b/buildcc/lib/target/include/target/base/target_loader.h deleted file mode 100644 index 9bf1161f..00000000 --- a/buildcc/lib/target/include/target/base/target_loader.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * 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_BASE_TARGET_LOADER_H_ -#define TARGET_BASE_TARGET_LOADER_H_ - -#include "target/interface/loader_interface.h" - -#include -#include - -#include "fmt/format.h" - -#include "target/common/path.h" - -namespace buildcc::internal { - -class TargetLoader : public LoaderInterface { -public: - explicit TargetLoader(const std::string &name, const fs::path &relative_path) - : name_(name), relative_path_(relative_path) { - binary_path_ = relative_path / fmt::format("{}.bin", name); - Initialize(); - } - - TargetLoader(const TargetLoader &loader) = delete; - -public: - bool Load() override; - - // Getters - const path_unordered_set &GetLoadedSources() const noexcept { - return loaded_sources_; - } - const path_unordered_set &GetLoadedHeaders() const noexcept { - return loaded_headers_; - } - const path_unordered_set &GetLoadedPchs() const noexcept { - return loaded_pchs_; - } - const path_unordered_set &GetLoadedLibDeps() const noexcept { - return loaded_lib_deps_; - } - const std::unordered_set & - GetLoadedExternalLibDeps() const noexcept { - return loaded_external_lib_dirs_; - } - - const fs_unordered_set &GetLoadedIncludeDirs() const noexcept { - return loaded_include_dirs_; - } - const fs_unordered_set &GetLoadedLibDirs() const noexcept { - return loaded_lib_dirs_; - } - const std::unordered_set & - GetLoadedPreprocessorFlags() const noexcept { - return loaded_preprocessor_flags_; - } - const std::unordered_set & - GetLoadedCommonCompileFlags() const noexcept { - return loaded_common_compile_flags_; - } - const std::unordered_set & - GetLoadedPchCompileFlags() const noexcept { - return loaded_pch_compile_flags_; - } - const std::unordered_set & - GetLoadedPchObjectFlags() const noexcept { - return loaded_pch_object_flags_; - } - const std::unordered_set & - GetLoadedAsmCompileFlags() const noexcept { - return loaded_asm_compile_flags_; - } - const std::unordered_set & - GetLoadedCCompileFlags() const noexcept { - return loaded_c_compile_flags_; - } - const std::unordered_set & - GetLoadedCppCompileFlags() const noexcept { - return loaded_cpp_compile_flags_; - } - const std::unordered_set &GetLoadedLinkFlags() const noexcept { - return loaded_link_flags_; - } - - const path_unordered_set &GetLoadedCompileDependencies() const noexcept { - return loaded_compile_dependencies_; - } - const path_unordered_set &GetLoadedLinkDependencies() const noexcept { - return loaded_link_dependencies_; - } - - bool GetLoadedPchCompiled() const noexcept { return loaded_pch_compiled_; } - bool GetLoadedTargetLinked() const noexcept { return loaded_target_linked_; } - -private: - void Initialize(); - -private: - std::string name_; - fs::path relative_path_; - - path_unordered_set loaded_sources_; - path_unordered_set loaded_headers_; - path_unordered_set loaded_pchs_; - path_unordered_set loaded_lib_deps_; - - std::unordered_set loaded_external_lib_dirs_; - - fs_unordered_set loaded_include_dirs_; - fs_unordered_set loaded_lib_dirs_; - - std::unordered_set loaded_preprocessor_flags_; - std::unordered_set loaded_common_compile_flags_; - std::unordered_set loaded_pch_compile_flags_; - std::unordered_set loaded_pch_object_flags_; - std::unordered_set loaded_asm_compile_flags_; - std::unordered_set loaded_c_compile_flags_; - std::unordered_set loaded_cpp_compile_flags_; - std::unordered_set loaded_link_flags_; - - path_unordered_set loaded_compile_dependencies_; - path_unordered_set loaded_link_dependencies_; - - bool loaded_pch_compiled_{false}; - bool loaded_target_linked_{false}; -}; - -} // namespace buildcc::internal - -#endif diff --git a/buildcc/lib/target/include/target/base/target_storer.h b/buildcc/lib/target/include/target/base/target_storer.h deleted file mode 100644 index c8cceea6..00000000 --- a/buildcc/lib/target/include/target/base/target_storer.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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_BASE_TARGET_STORER_H_ -#define TARGET_BASE_TARGET_STORER_H_ - -#include - -#include "target/common/path.h" - -namespace buildcc::internal { - -struct TargetStorer { - internal::RelationalPathFiles current_source_files; - internal::RelationalPathFiles current_header_files; - internal::RelationalPathFiles current_pch_files; - - // NOTE, Order matters (BuildCC takes care of the order here) - std::vector current_user_lib_deps; - internal::path_unordered_set current_internal_lib_deps; - - fs_unordered_set current_include_dirs; - fs_unordered_set current_lib_dirs; - - // NOTE, Order matters (user takes care of the order here) - std::vector current_user_external_lib_deps; - std::unordered_set current_internal_external_lib_deps; - - std::unordered_set current_preprocessor_flags; - std::unordered_set current_common_compile_flags; - std::unordered_set current_pch_compile_flags; - std::unordered_set current_pch_object_flags; - std::unordered_set current_asm_compile_flags; - std::unordered_set current_c_compile_flags; - std::unordered_set current_cpp_compile_flags; - std::unordered_set current_link_flags; - - internal::RelationalPathFiles current_compile_dependencies; - internal::RelationalPathFiles current_link_dependencies; - - bool pch_compiled{false}; - bool target_linked{false}; -}; - -} // namespace buildcc::internal - -#endif diff --git a/buildcc/lib/target/include/target/common/path.h b/buildcc/lib/target/include/target/common/path.h deleted file mode 100644 index faa6e0f8..00000000 --- a/buildcc/lib/target/include/target/common/path.h +++ /dev/null @@ -1,212 +0,0 @@ -/* - * 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_COMMON_PATH_H_ -#define TARGET_COMMON_PATH_H_ - -#include -#include - -// The Path class defined below is meant to be used with Sets -#include - -// Env -#include "env/assert_throw.h" - -// Third party -#include "fmt/format.h" - -namespace fs = std::filesystem; - -namespace buildcc::internal { - -class Path { -public: - /** - * @brief Create a Existing Path object and sets last_write_timstamp to file - * timestamp - * NOTE, Throws buildcc::env::assert_exception if file not found - * - * @param pathname - * @return Path - */ - // TODO, Discuss if we should return `std::optional` instead of asserting - static Path CreateExistingPath(const fs::path &pathname) { - std::error_code errcode; - uint64_t last_write_timestamp = - std::filesystem::last_write_time(pathname, errcode) - .time_since_epoch() - .count(); - env::assert_throw(errcode.value() == 0, - fmt::format("{} not found", pathname)); - - return Path(pathname, last_write_timestamp); - } - - static Path CreateNewPath(const fs::path &pathname, - uint64_t last_write_timestamp) noexcept { - return Path(pathname, last_write_timestamp); - } - - /** - * @brief Create a New Path object and sets last_write_timestamp to 0 - * - * @param pathname - * @return Path - */ - static Path CreateNewPath(const fs::path &pathname) { - return Path(pathname, 0); - } - - // Getters - std::uint64_t GetLastWriteTimestamp() const noexcept { - return last_write_timestamp_; - } - const fs::path &GetPathname() const noexcept { return pathname_; } - - /** - * @brief Get fs::path as std::string while keeping the preferred os - * path delimiters - * '\\' for windows and '/' for linux - * - * @return std::string - */ - std::string GetPathAsString() const { return GetPathname().string(); } - - /** - * @brief Get fs::path as std::string for display - * Converts '\\' to '/' for conformity - * - * @return std::string - */ - std::string GetPathAsStringForDisplay() const { - return Quote(ConvertPathToString()); - } - - // Used during find operation - bool operator==(const Path &p) const { - return GetPathname() == p.GetPathname(); - } - - bool operator==(const fs::path &pathname) const { - return GetPathname() == pathname; - } - -private: - explicit Path(const fs::path &pathname, std::uint64_t last_write_timestamp) - : pathname_(pathname), last_write_timestamp_(last_write_timestamp) { - pathname_.make_preferred(); - } - - std::string Quote(const std::string &str) const { - if (str.find(" ") == std::string::npos) { - return str; - } - return fmt::format("\"{}\"", str); - } - - std::string ConvertPathToString() const { - std::string pstr = pathname_.string(); - std::replace(pstr.begin(), pstr.end(), '\\', '/'); - return pstr; - } - -private: - fs::path pathname_; - std::uint64_t last_write_timestamp_; -}; - -// Used by Path -class PathHash { -public: - size_t operator()(const Path &p) const { - return fs::hash_value(p.GetPathname()); - } - - size_t operator()(const fs::path &p) const { return fs::hash_value(p); } -}; - -typedef std::unordered_set path_unordered_set; -typedef std::unordered_set fs_unordered_set; - -// * Relation between -// - internal timestamp verified files (Path + Timestamp) -// - user facing file paths (Only Path) -// ? Why has this been done? -// We cannot guarantee that filepaths would be present -// when the user is defining the build -// The input to a Generator / Target might also be generated! -// We must only verify the File timestamp AFTER dependent Generator(s) / -// Target(s) have been built -// ? Why not do everything inside path_unordered_set? -// Users might want to query just the `fs_unordered_set` instead of the entire -// internal::path_unordered_set (The timestamp is internal information that the -// user does not need) -// In this case we opt for runtime (speed) optimization instead of memory -// optimization by caching the `user` information and `internal` information -// together -struct RelationalPathFiles { - RelationalPathFiles() {} - RelationalPathFiles(const path_unordered_set &i, const fs_unordered_set &u) - : internal(i), user(u) {} - - /** - * @brief Convert from fs_unordered_set to path_unordered_set - * Can assert throw if file does not exist when calling `CreateExistingPath` - */ - void Convert() { - if (done_once) { - return; - } - - done_once = true; - for (const auto &p : user) { - internal.emplace(Path::CreateExistingPath(p)); - } - } - -public: - path_unordered_set internal; - fs_unordered_set user; - -private: - bool done_once{false}; -}; - -} // namespace buildcc::internal - -namespace buildcc { - -inline std::string path_as_string(const fs::path &p) { - return internal::Path::CreateNewPath(p).GetPathAsString(); -} - -typedef internal::fs_unordered_set fs_unordered_set; - -} // namespace buildcc - -// FMT specialization - -template <> struct fmt::formatter : formatter { - template - auto format(const fs::path &p, FormatContext &ctx) { - return formatter::format( - buildcc::internal::Path::CreateNewPath(p).GetPathAsStringForDisplay(), - ctx); - } -}; - -#endif diff --git a/buildcc/lib/target/include/target/common/target_config.h b/buildcc/lib/target/include/target/common/target_config.h index 172c52b0..a09d2595 100644 --- a/buildcc/lib/target/include/target/common/target_config.h +++ b/buildcc/lib/target/include/target/common/target_config.h @@ -21,8 +21,6 @@ #include #include -#include "target/common/target_file_ext.h" - namespace fs = std::filesystem; namespace buildcc { @@ -30,77 +28,13 @@ namespace buildcc { struct TargetConfig { TargetConfig() = default; - /** - * @brief Get the valid file extension from a path - * - * See TargetConfig::valid_c_ext, TargetConfig::valid_cpp_ext, - * TargetConfig::valid_asm_ext, TargetConfig::valid_header_ext - * - * @param filepath Absolute / Relative path of the file - * @return TargetFileExt File path detected as per TargetConfig::valid_* - * variables - */ - TargetFileExt GetFileExt(const fs::path &filepath) const; - - /** - * @brief Checks for C/C++ source file validity. - * - * See TargetConfig::valid_c_ext, TargetConfig::valid_cpp_ext, - * TargetConfig::valid_asm_ext - * - * @param filepath Absolute / Relative path of file - * @return true If file extension belongs to the above valid_* list - * @return false If file extension does not belong to the above valid_* list - */ - bool IsValidSource(const fs::path &filepath) const; - - /** - * @brief Checks for Header file validity - * - * See TargetConfig::valid_header_ext - * - * @param filepath Absolute / Relative path of file - * @return true If file extension belongs to above valid_* list - * @return false If file extension does not belong to above valid_* list - */ - bool IsValidHeader(const fs::path &filepath) const; - - /** - * @brief Expects Source file validity - * - * env::assert_fatal if not a valid source - * - * @param filepath Absolute / Relative path of file - */ - void ExpectsValidSource(const fs::path &filepath) const; - - /** - * @brief Expects header file validity - * - * env::assert_fatal if not a valid header - * - * @param filepath Absolute / Relative path of file - */ - void ExpectsValidHeader(const fs::path &filepath) const; - std::string target_ext{""}; - std::string obj_ext{".o"}; - std::string pch_header_ext{".h"}; - std::string pch_compile_ext{".gch"}; - - std::string prefix_include_dir{"-I"}; - std::string prefix_lib_dir{"-L"}; // clang-format off std::string pch_command{"{compiler} {preprocessor_flags} {include_dirs} {common_compile_flags} {pch_compile_flags} {compile_flags} -o {output} -c {input}"}; std::string compile_command{"{compiler} {preprocessor_flags} {include_dirs} {common_compile_flags} {pch_object_flags} {compile_flags} -o {output} -c {input}"}; std::string link_command{"{cpp_compiler} {link_flags} {compiled_sources} -o {output} {lib_dirs} {lib_deps}"}; // clang-format on - - std::unordered_set valid_c_ext{".c"}; - std::unordered_set valid_cpp_ext{".cpp", ".cxx", ".cc"}; - std::unordered_set valid_asm_ext{".s", ".S", ".asm"}; - std::unordered_set valid_header_ext{".h", ".hpp"}; }; } // namespace buildcc diff --git a/buildcc/lib/target/include/target/common/target_env.h b/buildcc/lib/target/include/target/common/target_env.h index e6f129e0..dacdb847 100644 --- a/buildcc/lib/target/include/target/common/target_env.h +++ b/buildcc/lib/target/include/target/common/target_env.h @@ -35,14 +35,14 @@ class TargetEnv { * @brief Change the relative root path for a particular Generator / Target * * Absolute root now changes to - * `env::get_project_root_dir() / target_relative_to_env_root` + * `Project::GetRootDir() / target_relative_to_env_root` * * Absolute build dir remains the same. * * Can be used implicitly * * @param target_relative_to_env_root Change root dir with respect to - * env::get_project_root_dir() + * Project::GetRootDir() */ TargetEnv(const char *target_relative_to_env_root) : TargetEnv(fs::path(target_relative_to_env_root)) {} @@ -53,12 +53,11 @@ class TargetEnv { * Only explicit usage allowed * * @param target_relative_to_env_root Change root dir with respect to - * env::get_project_root_dir() + * Project::GetRootDir() */ explicit TargetEnv(const fs::path &target_relative_to_env_root) - : target_root_dir_(env::get_project_root_dir() / - target_relative_to_env_root), - target_build_dir_(env::get_project_build_dir()), relative_(true) {} + : target_root_dir_(Project::GetRootDir() / target_relative_to_env_root), + target_build_dir_(Project::GetBuildDir()), relative_(true) {} /** * @brief Change the absolute root and build path for a particular Generator / @@ -83,6 +82,25 @@ class TargetEnv { bool relative_{false}; }; +namespace internal { + +// Requires +// TargetEnv +template class TargetEnvApi { +public: + const fs::path &GetTargetRootDir() const { + const auto &t = static_cast(*this); + return t.env_.GetTargetRootDir(); + } + + const fs::path &GetTargetBuildDir() const { + const auto &t = static_cast(*this); + return t.env_.GetTargetBuildDir(); + } +}; + +} // namespace internal + } // namespace buildcc #endif diff --git a/buildcc/lib/target/include/target/common/target_state.h b/buildcc/lib/target/include/target/common/target_state.h index cc7e06eb..bd211456 100644 --- a/buildcc/lib/target/include/target/common/target_state.h +++ b/buildcc/lib/target/include/target/common/target_state.h @@ -17,35 +17,28 @@ #ifndef TARGET_COMMON_TARGET_STATE_H_ #define TARGET_COMMON_TARGET_STATE_H_ -#include "target/common/target_file_ext.h" +#include "toolchain/common/file_ext.h" namespace buildcc { -// TODO, Seperate TargetState into lock_ and other internal boolean variables -// NOTE, This is because apart from lock_ every other parameter is updated -// during `Target::Build` -// TargetInfo does not have a `Build` method, it is only meant to hold -// information struct TargetState { - void SetSourceState(TargetFileExt file_extension); - void SetPch(); - void SetLock(); - - void ExpectsUnlock() const; - void ExpectsLock() const; - // TODO, IsLocked + void BuildCompleted(); + void SourceDetected(FileExt file_extension); + void PchDetected(); + bool IsBuilt() const { return build_; } bool ContainsPch() const { return contains_pch_; } - - // TODO, Make these private getters - bool contains_asm{false}; - bool contains_c{false}; - bool contains_cpp{false}; - bool build{false}; - bool lock{false}; + bool ContainsAsm() const { return contains_asm_; } + bool ContainsC() const { return contains_c_; } + bool ContainsCpp() const { return contains_cpp_; } private: + bool build_{false}; + bool contains_pch_{false}; + bool contains_asm_{false}; + bool contains_c_{false}; + bool contains_cpp_{false}; }; } // namespace buildcc diff --git a/buildcc/lib/target/include/target/common/util.h b/buildcc/lib/target/include/target/common/util.h index c95f7daa..30191e89 100644 --- a/buildcc/lib/target/include/target/common/util.h +++ b/buildcc/lib/target/include/target/common/util.h @@ -20,7 +20,7 @@ #include #include -#include "target/common/path.h" +#include "schema/path.h" namespace buildcc::internal { @@ -29,10 +29,15 @@ template std::string aggregate(const T &list) { return fmt::format("{}", fmt::join(list, " ")); } -std::string aggregate(const buildcc::fs_unordered_set &paths); - -std::string aggregate_with_prefix(const std::string &prefix, - const fs_unordered_set &dirs); +template +std::string aggregate_with_prefix(const std::string &prefix, const T &list) { + std::vector agg_list; + for (const auto &l : list) { + auto formatted_output = fmt::format("{}{}", prefix, l); + agg_list.emplace_back(std::move(formatted_output)); + } + return aggregate(agg_list); +} } // namespace buildcc::internal diff --git a/buildcc/lib/target/include/target/custom_generator.h b/buildcc/lib/target/include/target/custom_generator.h new file mode 100644 index 00000000..a9dec033 --- /dev/null +++ b/buildcc/lib/target/include/target/custom_generator.h @@ -0,0 +1,141 @@ +/* + * 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_H_ +#define TARGET_CUSTOM_GENERATOR_H_ + +#include +#include +#include +#include + +#include "env/command.h" +#include "env/task_state.h" + +#include "target/interface/builder_interface.h" + +#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 { + +struct UserCustomGeneratorSchema : public internal::CustomGeneratorSchema { + struct UserIdInfo : internal::CustomGeneratorSchema::IdInfo { + void ConvertToInternal() { + inputs.ComputeHashForAll(); + userblob = blob_handler != nullptr ? blob_handler->GetSerializedData() + : std::vector(); + } + + GenerateCb generate_cb; + std::shared_ptr blob_handler{nullptr}; + }; + + void ConvertToInternal() { + for (auto &[id_key, id_info] : ids) { + id_info.ConvertToInternal(); + auto [_, success] = internal_ids.try_emplace(id_key, id_info); + env::assert_fatal(success, fmt::format("Could not save {}", id_key)); + } + } + + std::unordered_map ids; +}; + +class CustomGenerator : public internal::BuilderInterface { +public: + CustomGenerator(const std::string &name, const TargetEnv &env) + : name_(name), + env_(env.GetTargetRootDir(), env.GetTargetBuildDir() / name), + serialization_(env_.GetTargetBuildDir() / + fmt::format("{}.json", name)) { + Initialize(); + } + virtual ~CustomGenerator() = default; + CustomGenerator(const CustomGenerator &) = delete; + + // TODO, Doc + void AddPattern(const std::string &identifier, const std::string &pattern); + + // TODO, Doc + void + AddPatterns(const std::unordered_map &pattern_map); + + // TODO, Doc + std::string ParsePattern(const std::string &pattern, + const std::unordered_map + &arguments = {}) const; + + /** + * @brief Single Generator task for inputs->generate_cb->outputs + * + * @param id Unique id associated with Generator task + * @param inputs File inputs + * @param outputs File outputs + * @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, + const std::shared_ptr &blob_handler = nullptr); + + void Build() override; + + // Getters + const std::string &GetName() const { return name_; } + const fs::path &GetBinaryPath() const { + return serialization_.GetSerializedFile(); + } + const fs::path &GetRootDir() const { return env_.GetTargetRootDir(); } + const fs::path &GetBuildDir() const { return env_.GetTargetBuildDir(); } + const std::string &Get(const std::string &file_identifier) const; + +private: + void Initialize(); + void GenerateTask(); + + // Recheck states + void IdRemoved(); + void IdAdded(); + void IdUpdated(); + +protected: + const env::Command &ConstCommand() const { return command_; } + env::Command &RefCommand() { return command_; } + +private: + std::string name_; + TargetEnv env_; + internal::CustomGeneratorSerialization serialization_; + + // Serialization + UserCustomGeneratorSchema user_; + + // Internal + env::Command command_; +}; + +} // namespace buildcc + +#endif 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..3b64d23c --- /dev/null +++ b/buildcc/lib/target/include/target/custom_generator/custom_blob_handler.h @@ -0,0 +1,107 @@ +/* + * 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 { + +/** + * @brief Abstract class for serializing additional data for which rebuilds + * might be triggered i.e data that is not input/output files + * TODO, Add examples here + * + */ +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; +}; + +/** + * @brief Typed Custom Blob handler which automatically performs Serialization + * and Deserialization as long as it is JSON serializable + * + * NOTE: Type data is stored as a reference (to avoid copying large amount of + * data) when constructing TypedCustomBlobHandler + * + * @tparam Type should be JSON serializable (see nlohmann::json compatible + * objects) + */ +template +class TypedCustomBlobHandler : public CustomBlobHandler { +public: + explicit TypedCustomBlobHandler(const Type &data) : data_(data) {} + + // serialized_data has already been verified + static Type Deserialize(const std::vector &serialized_data) { + json j = json::from_msgpack(serialized_data, true, false); + Type deserialized; + j.get_to(deserialized); + return deserialized; + } + +private: + const Type &data_; + + bool Verify(const std::vector &serialized_data) const override { + json j = json::from_msgpack(serialized_data, true, false); + return !j.is_discarded(); + } + + bool IsEqual(const std::vector &previous, + const std::vector ¤t) const override { + return Deserialize(previous) == Deserialize(current); + } + + std::vector Serialize() const override { + json j = data_; + return json::to_msgpack(j); + } +}; + +} // 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..53bd59a6 --- /dev/null +++ b/buildcc/lib/target/include/target/custom_generator/custom_generator_context.h @@ -0,0 +1,46 @@ +/* + * 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 std::vector &i, + const std::vector &o, + const std::vector &ub) + : command(c), inputs(i), outputs(o), userblob(ub) {} + + const env::Command &command; + const std::vector &inputs; + const std::vector &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 new file mode 100644 index 00000000..9897b5fc --- /dev/null +++ b/buildcc/lib/target/include/target/file_generator.h @@ -0,0 +1,83 @@ +/* + * 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_FILE_GENERATOR_H_ +#define TARGET_FILE_GENERATOR_H_ + +#include "target/custom_generator.h" + +namespace buildcc { + +class FileGenerator : public CustomGenerator { +public: + using CustomGenerator::CustomGenerator; + ~FileGenerator() override = default; + FileGenerator(const FileGenerator &) = delete; + + /** + * @brief Add absolute input path pattern to generator + * NOTE: We can use {current_root_dir} and {current_build_dir} in the + * absolute_input_pattern + * + * If `identifier` is supplied it is added to default arguments as a key + * Example: fmt::format("{identifier}") -> "absolute_input_pattern" + */ + void AddInput(const std::string &absolute_input_pattern); + + /** + * @brief Add absolute output path pattern to generator + * NOTE: We can use {current_root_dir} and {current_build_dir} in the + * absolute_output_pattern + * + * If `identifier` is supplied it is added to default arguments as a key + * Example: fmt::format("{identifier}") -> "absolute_output_pattern" + */ + void AddOutput(const std::string &absolute_output_pattern); + + /** + * @brief Add a command_pattern that is fed to `Command::Execute` internally + * + * NOTE: The order of all commands are maintained (`std::vector::push_back`) + * + * If you would like to run the commands in parallel, set `parallel == true` + * in the constructor + */ + void AddCommand( + const std::string &command_pattern, + const std::unordered_map &arguments = {}); + + /** + * @brief Build FileGenerator Tasks + * + * Use `GetTaskflow` for the registered tasks + */ + void Build() override; + + // Restrict access to certain custom generator APIs +private: + using CustomGenerator::AddIdInfo; + using CustomGenerator::Build; + +private: + // + std::unordered_set inputs_; + std::unordered_set outputs_; + std::vector commands_; +}; + +} // namespace buildcc + +#endif diff --git a/buildcc/lib/target/include/target/friend/compile_object.h b/buildcc/lib/target/include/target/friend/compile_object.h index f99c154e..3e2e8ec8 100644 --- a/buildcc/lib/target/include/target/friend/compile_object.h +++ b/buildcc/lib/target/include/target/friend/compile_object.h @@ -19,7 +19,7 @@ #include -#include "target/common/path.h" +#include "schema/path.h" #include "taskflow/core/task.hpp" #include "taskflow/taskflow.hpp" @@ -54,29 +54,28 @@ class CompileObject { void Task(); const ObjectData &GetObjectData(const fs::path &absolute_source) const; - const std::unordered_map & - GetObjectDataMap() const { + const std::unordered_map &GetObjectDataMap() const { return object_files_; } - fs_unordered_set GetCompiledSources() const; + std::vector GetCompiledSources() const; tf::Task &GetTask() { return compile_task_; } private: fs::path ConstructObjectPath(const fs::path &absolute_source_file) const; - void BuildObjectCompile(std::vector &source_files, - std::vector &dummy_source_files); + void BuildObjectCompile(std::vector &source_files, + std::vector &dummy_source_files); void PreObjectCompile(); - void CompileSources(std::vector &source_files); - void RecompileSources(std::vector &source_files, - std::vector &dummy_source_files); + void CompileSources(std::vector &source_files); + void RecompileSources(std::vector &source_files, + std::vector &dummy_source_files); private: Target &target_; - std::unordered_map object_files_; + std::unordered_map object_files_; tf::Task compile_task_; }; diff --git a/buildcc/lib/target/include/target/generator.h b/buildcc/lib/target/include/target/generator.h deleted file mode 100644 index 0a9b463b..00000000 --- a/buildcc/lib/target/include/target/generator.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * 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_GENERATOR_H_ -#define TARGET_GENERATOR_H_ - -#include -#include -#include -#include -#include - -#include "taskflow/taskflow.hpp" - -#include "env/env.h" -#include "env/task_state.h" - -#include "env/command.h" - -#include "target/interface/builder_interface.h" - -#include "target/base/generator_loader.h" - -#include "target/common/path.h" -#include "target/common/target_env.h" - -namespace buildcc { - -class Generator : public internal::BuilderInterface { -public: - Generator(const std::string &name, const TargetEnv &env, - bool parallel = false) - : name_(name), generator_root_dir_(env.GetTargetRootDir()), - generator_build_dir_(env.GetTargetBuildDir() / name), - loader_(name, generator_build_dir_), parallel_(parallel) { - Initialize(); - } - virtual ~Generator() {} - Generator(const Generator &generator) = delete; - - /** - * @brief Add default arguments for input, output and command requirements - * - * @param arguments Key-Value pair for arguments - */ - void AddDefaultArguments( - const std::unordered_map &arguments); - - /** - * @brief Add absolute input path pattern to generator - * NOTE: We can use {gen_root_dir} and {gen_build_dir} in the - * absolute_input_pattern - * - * If `identifier` is supplied it is added to default arguments as a key - * Example: fmt::format("{identifier}") -> "absolute_input_pattern" - */ - void AddInput(const std::string &absolute_input_pattern, - const char *identifier = nullptr); - - /** - * @brief Add absolute output path pattern to generator - * NOTE: We can use {gen_root_dir} and {gen_build_dir} in the - * absolute_output_pattern - * - * If `identifier` is supplied it is added to default arguments as a key - * Example: fmt::format("{identifier}") -> "absolute_output_pattern" - */ - void AddOutput(const std::string &absolute_output_pattern, - const char *identifier = nullptr); - - /** - * @brief Add a command_pattern that is fed to `Command::Execute` internally - * - * NOTE: The order of all commands are maintained (`std::vector::push_back`) - * - * If you would like to run the commands in parallel, set `parallel == true` - * in the constructor - */ - void AddCommand( - const std::string &command_pattern, - const std::unordered_map &arguments = {}); - - /** - * @brief Build Generator Tasks - * - * Use `GetTaskflow` for the registered tasks - */ - void Build() override; - - // Getter - const fs::path &GetBinaryPath() const { return loader_.GetBinaryPath(); } - tf::Taskflow &GetTaskflow() { return tf_; } - - const std::string &GetName() const { return name_; } - env::TaskState GetTaskState() const { return task_state_; } - - const std::string & - GetValueByIdentifier(const std::string &file_identifier) const; - -private: - void Initialize(); - - void GenerateTask(); - void Convert(); - void BuildGenerate(); - - bool Store() override; - - // Recheck states - void InputRemoved(); - void InputAdded(); - void InputUpdated(); - - void OutputChanged(); - void CommandChanged(); - -private: - // Constructor - std::string name_; - fs::path generator_root_dir_; - fs::path generator_build_dir_; - internal::GeneratorLoader loader_; - - // Serialization - internal::RelationalPathFiles current_input_files_; - fs_unordered_set current_output_files_; - std::vector current_commands_; - bool parallel_{false}; - - // Internal - std::mutex task_state_mutex_; - env::TaskState task_state_{env::TaskState::SUCCESS}; - env::Command command_; - tf::Taskflow tf_; -}; - -typedef Generator BaseGenerator; - -} // namespace buildcc - -#endif diff --git a/buildcc/lib/target/include/target/interface/builder_interface.h b/buildcc/lib/target/include/target/interface/builder_interface.h index d3ea59e9..c020fd91 100644 --- a/buildcc/lib/target/include/target/interface/builder_interface.h +++ b/buildcc/lib/target/include/target/interface/builder_interface.h @@ -21,97 +21,26 @@ #include #include +#include "taskflow/taskflow.hpp" + #include "env/assert_fatal.h" -#include "target/common/path.h" #include "target/common/util.h" namespace buildcc::internal { -// TODO, 1. Consider updating Recheck* APIs - do not modify internal `dirty_` -// flag -// TODO, 2. Consider removing dependency on target/common/util.h -// TODO, 3. Consider making Recheck* APIs free namespaced functions instead of -// only within the scope of BuilderInterfaces (See TODO 1. and 2. first) class BuilderInterface { public: virtual void Build() = 0; const std::string &GetUniqueId() const { return unique_id_; } - -protected: - template - void RecheckChanged( - const T &previous, const T ¤t, - const std::function &callback = []() {}) { - ASSERT_FATAL(callback, "Bad 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 = []() {}) { - ASSERT_FATAL(path_removed_cb, "Bad function: path_removed_cb"); - ASSERT_FATAL(path_added_cb, "Bad function: path_added_cb"); - ASSERT_FATAL(path_updated_cb, "Bad function: path_updated_cb"); - - if (dirty_) { - return; - } - - // * Old path is removed - const bool removed = - std::any_of(previous_path.begin(), previous_path.end(), - [&](const internal::Path &p) { - return current_path.find(p) == current_path.end(); - }); - if (removed) { - path_removed_cb(); - dirty_ = true; - return; - } - - dirty_ = std::any_of( - current_path.cbegin(), current_path.cend(), - [&](const internal::Path &p) -> bool { - bool dirty = false; - const auto find = previous_path.find(p); - const bool added_cond = (find == previous_path.end()); - if (added_cond) { - path_added_cb(); - dirty = true; - } else { - const bool updated_cond = - (p.GetLastWriteTimestamp() > find->GetLastWriteTimestamp()); - if (updated_cond) { - path_updated_cb(); - dirty = true; - } else { - dirty = false; - } - } - return dirty; - }); - } - -private: - virtual bool Store() = 0; + tf::Taskflow &GetTaskflow() { return tf_; } protected: bool dirty_{false}; std::string unique_id_; + tf::Taskflow tf_; }; } // namespace buildcc::internal diff --git a/buildcc/lib/target/include/target/private/schema_util.h b/buildcc/lib/target/include/target/private/schema_util.h deleted file mode 100644 index d962e83e..00000000 --- a/buildcc/lib/target/include/target/private/schema_util.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * 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_PRIVATE_SCHEMA_UTIL_H_ -#define TARGET_PRIVATE_SCHEMA_UTIL_H_ - -#include "path_generated.h" - -#include - -#include "target/common/path.h" - -namespace fbs = schema::internal; - -namespace buildcc::internal { - -// extract APIs for LOAD - -inline void extract_path( - 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> -create_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.GetPathAsString().c_str(), - p.GetLastWriteTimestamp()); - paths.push_back(fbs_file); - } - return paths; -} - -template -inline std::vector> -create_fbs_vector_string(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> -create_fbs_vector_string(flatbuffers::FlatBufferBuilder &builder, - const buildcc::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(path_as_string(p)); - }); - 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 edcad7e7..1e0ab020 100644 --- a/buildcc/lib/target/include/target/target.h +++ b/buildcc/lib/target/include/target/target.h @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -35,7 +34,7 @@ #include "target/target_info.h" // Common -#include "target/common/target_type.h" +#include "schema/target_type.h" // Friend #include "target/friend/compile_object.h" @@ -43,9 +42,8 @@ #include "target/friend/link_target.h" // Internal -#include "target/base/target_loader.h" -#include "target/base/target_storer.h" -#include "target/common/path.h" +#include "schema/path.h" +#include "schema/target_serialization.h" // Env #include "env/env.h" @@ -70,24 +68,20 @@ class Target : public internal::BuilderInterface, explicit Target(const std::string &name, TargetType type, const Toolchain &toolchain, const TargetEnv &env, const TargetConfig &config = TargetConfig()) - : TargetInfo( - TargetEnv(env.GetTargetRootDir(), - env.GetTargetBuildDir() / toolchain.GetName() / name), - config), - name_(name), type_(type), toolchain_(toolchain), - loader_(name, env_.GetTargetBuildDir()), compile_pch_(*this), - compile_object_(*this), link_target_(*this) { + : TargetInfo(toolchain, TargetEnv(env.GetTargetRootDir(), + env.GetTargetBuildDir() / + toolchain.GetName() / name)), + name_(name), type_(type), config_(config), + serialization_(env_.GetTargetBuildDir() / fmt::format("{}.bin", name)), + compile_pch_(*this), compile_object_(*this), link_target_(*this) { Initialize(); } - virtual ~Target() {} + virtual ~Target() = default; Target(const Target &target) = delete; // Builders void Build() override; - // Getters - env::TaskState GetTaskState() const noexcept { return task_state_; } - private: friend class internal::CompilePch; friend class internal::CompileObject; @@ -99,32 +93,11 @@ class Target : public internal::BuilderInterface, void Initialize(); // - std::optional SelectCompileFlags(TargetFileExt ext) const; - std::optional SelectCompiler(TargetFileExt ext) const; - - // Recompilation checks - void RecheckPaths(const internal::path_unordered_set &previous_path, - const internal::path_unordered_set ¤t_path); - void RecheckDirs(const fs_unordered_set &previous_dirs, - const fs_unordered_set ¤t_dirs); - void RecheckFlags(const std::unordered_set &previous_flags, - const std::unordered_set ¤t_flags); - void RecheckExternalLib( - const std::unordered_set &previous_external_libs, - const std::unordered_set ¤t_external_libs); - - // Fbs - bool Store() override; + env::optional SelectCompileFlags(FileExt ext) const; + env::optional SelectCompiler(FileExt ext) const; // Tasks - void SetTaskStateFailure(); - int GetTaskStateAsInt() const noexcept { - return static_cast(task_state_); - } - - void StartTask(); void EndTask(); - tf::Task CheckStateTask(); void TaskDeps(); // Callbacks for unit tests @@ -135,6 +108,7 @@ class Target : public internal::BuilderInterface, void PathAdded(); void PathUpdated(); + void PathChanged(); void DirChanged(); void FlagChanged(); void ExternalLibChanged(); @@ -142,27 +116,19 @@ class Target : public internal::BuilderInterface, private: std::string name_; TargetType type_; - const Toolchain &toolchain_; - internal::TargetLoader loader_; - - // Friend classes + TargetConfig config_; + internal::TargetSerialization serialization_; internal::CompilePch compile_pch_; internal::CompileObject compile_object_; internal::LinkTarget link_target_; + // + TargetState state_; + env::Command command_; + // Task states tf::Task target_start_task_; tf::Task target_end_task_; - - std::mutex task_state_mutex_; - env::TaskState task_state_{env::TaskState::SUCCESS}; - - std::mutex compiled_source_files_mutex_; - internal::path_unordered_set compiled_source_files_; - - // - env::Command command_; - tf::Taskflow tf_; }; typedef Target BaseTarget; diff --git a/buildcc/lib/target/include/target/target_info.h b/buildcc/lib/target/include/target/target_info.h index 022df822..b8758ce4 100644 --- a/buildcc/lib/target/include/target/target_info.h +++ b/buildcc/lib/target/include/target/target_info.h @@ -19,21 +19,18 @@ #include -#include "target/base/target_storer.h" -#include "target/common/target_config.h" +#include "toolchain/toolchain.h" + #include "target/common/target_env.h" -#include "target/common/target_state.h" #include "target/api/deps_api.h" -#include "target/api/flag_api.h" #include "target/api/include_api.h" #include "target/api/lib_api.h" #include "target/api/pch_api.h" -#include "target/api/sync_api.h" - #include "target/api/source_api.h" +#include "target/api/sync_api.h" -#include "target/api/target_info_getter.h" +#include "schema/target_serialization.h" namespace buildcc { @@ -48,32 +45,31 @@ class TargetInfo : public internal::SourceApi, public internal::FlagApi, public internal::DepsApi, public internal::SyncApi, - public internal::TargetInfoGetter { + public internal::TargetEnvApi { public: - TargetInfo(const TargetEnv &env, const TargetConfig &config = TargetConfig()) - : env_(env), config_(config) {} + TargetInfo(const BaseToolchain &toolchain, const TargetEnv &env) + : toolchain_(toolchain), env_(env) { + Initialize(); + } private: - // Inputs friend class internal::SourceApi; friend class internal::IncludeApi; friend class internal::LibApi; friend class internal::PchApi; friend class internal::FlagApi; friend class internal::DepsApi; - - // Feature friend class internal::SyncApi; - - // Getters - friend class internal::TargetInfoGetter; + friend class internal::TargetEnvApi; protected: + const BaseToolchain &toolchain_; TargetEnv env_; - TargetConfig config_; - internal::TargetStorer storer_; - TargetState state_; + internal::TargetSchema user_; + +private: + void Initialize(); }; typedef TargetInfo BaseTargetInfo; diff --git a/buildcc/lib/target/include/target/template_generator.h b/buildcc/lib/target/include/target/template_generator.h new file mode 100644 index 00000000..73c892e7 --- /dev/null +++ b/buildcc/lib/target/include/target/template_generator.h @@ -0,0 +1,60 @@ +/* + * 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_TEMPLATE_GENERATOR_H_ +#define TARGET_TEMPLATE_GENERATOR_H_ + +#include + +#include "target/custom_generator.h" + +namespace buildcc { + +class TemplateGenerator : public CustomGenerator { +public: + using CustomGenerator::CustomGenerator; + ~TemplateGenerator() override = default; + TemplateGenerator(const TemplateGenerator &) = delete; + + void AddTemplate(std::string_view absolute_input_pattern, + std::string_view absolute_output_pattern); + std::string Parse(const std::string &pattern) const; + + /** + * @brief Build FileGenerator Tasks + * + * Use `GetTaskflow` for the registered tasks + */ + void Build() override; + + // Restrict access to certain custom generator APIs +private: + using CustomGenerator::AddIdInfo; + using CustomGenerator::Build; + +private: + struct TemplateInfo { + std::string input_pattern; + std::string output_pattern; + }; + +private: + std::vector template_infos_; +}; + +} // namespace buildcc + +#endif diff --git a/buildcc/lib/target/mock/custom_generator/recheck_states.cpp b/buildcc/lib/target/mock/custom_generator/recheck_states.cpp new file mode 100644 index 00000000..c8c6471c --- /dev/null +++ b/buildcc/lib/target/mock/custom_generator/recheck_states.cpp @@ -0,0 +1,43 @@ +#include "target/custom_generator.h" + +#include "expect_custom_generator.h" + +#include "CppUTestExt/MockSupport.h" + +namespace buildcc { + +static constexpr const char *const ID_REMOVED_FUNCTION = + "CustomGenerator::IdRemoved"; +static constexpr const char *const ID_ADDED_FUNCTION = + "CustomGenerator::IdAdded"; +static constexpr const char *const ID_UPDATED_FUNCTION = + "CustomGenerator::IdUpdated"; + +void CustomGenerator::IdRemoved() { + mock().actualCall(ID_REMOVED_FUNCTION).onObject(this); +} +void CustomGenerator::IdAdded() { + mock().actualCall(ID_ADDED_FUNCTION).onObject(this); +} +void CustomGenerator::IdUpdated() { + mock().actualCall(ID_UPDATED_FUNCTION).onObject(this); +} + +namespace m { + +void CustomGeneratorExpect_IdRemoved(unsigned int calls, + CustomGenerator *generator) { + mock().expectNCalls(calls, ID_REMOVED_FUNCTION).onObject(generator); +} +void CustomGeneratorExpect_IdAdded(unsigned int calls, + CustomGenerator *generator) { + mock().expectNCalls(calls, ID_ADDED_FUNCTION).onObject(generator); +} +void CustomGeneratorExpect_IdUpdated(unsigned int calls, + CustomGenerator *generator) { + mock().expectNCalls(calls, ID_UPDATED_FUNCTION).onObject(generator); +} + +} // namespace m + +} // namespace buildcc diff --git a/buildcc/lib/target/mock/custom_generator/runner.cpp b/buildcc/lib/target/mock/custom_generator/runner.cpp new file mode 100644 index 00000000..8014c851 --- /dev/null +++ b/buildcc/lib/target/mock/custom_generator/runner.cpp @@ -0,0 +1,13 @@ +#include "target/custom_generator.h" + +#include "expect_custom_generator.h" + +namespace buildcc::m { + +void CustomGeneratorRunner(CustomGenerator &custom_generator) { + tf::Executor executor(1); + executor.run(custom_generator.GetTaskflow()); + executor.wait_for_all(); +} + +} // namespace buildcc::m diff --git a/buildcc/lib/target/mock/expect_custom_generator.h b/buildcc/lib/target/mock/expect_custom_generator.h new file mode 100644 index 00000000..f54ed17c --- /dev/null +++ b/buildcc/lib/target/mock/expect_custom_generator.h @@ -0,0 +1,23 @@ +#ifndef TARGET_MOCK_EXPECT_CUSTOM_GENERATOR_H_ +#define TARGET_MOCK_EXPECT_CUSTOM_GENERATOR_H_ + +#include "target/custom_generator.h" + +namespace buildcc::m { + +/** + * @brief Runs the generator using Taskflow with 1 thread + * CppUTest cannot mock with multiple threads + */ +void CustomGeneratorRunner(CustomGenerator &custom_generator); + +void CustomGeneratorExpect_IdRemoved(unsigned int calls, + CustomGenerator *generator); +void CustomGeneratorExpect_IdAdded(unsigned int calls, + CustomGenerator *generator); +void CustomGeneratorExpect_IdUpdated(unsigned int calls, + CustomGenerator *generator); + +} // namespace buildcc::m + +#endif diff --git a/buildcc/lib/target/mock/expect_generator.h b/buildcc/lib/target/mock/expect_generator.h deleted file mode 100644 index b41cd780..00000000 --- a/buildcc/lib/target/mock/expect_generator.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef TARGET_MOCK_EXPECT_GENERATOR_H_ -#define TARGET_MOCK_EXPECT_GENERATOR_H_ - -#include "target/generator.h" - -namespace buildcc::m { - -/** - * @brief Runs the generator using Taskflow with 1 thread - * CppUTest cannot mock with multiple threads - */ -void GeneratorRunner(Generator &generator); - -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::m - -#endif diff --git a/buildcc/lib/target/mock/expect_target.h b/buildcc/lib/target/mock/expect_target.h index 8b398371..816e3175 100644 --- a/buildcc/lib/target/mock/expect_target.h +++ b/buildcc/lib/target/mock/expect_target.h @@ -19,6 +19,7 @@ void TargetExpect_PathRemoved(unsigned int calls, Target *target); void TargetExpect_PathAdded(unsigned int calls, Target *target); void TargetExpect_PathUpdated(unsigned int calls, Target *target); +void TargetExpect_PathChanged(unsigned int calls, Target *target); void TargetExpect_DirChanged(unsigned int calls, Target *target); void TargetExpect_FlagChanged(unsigned int calls, Target *target); void TargetExpect_ExternalLibChanged(unsigned int calls, Target *target); diff --git a/buildcc/lib/target/mock/generator/recheck_states.cpp b/buildcc/lib/target/mock/generator/recheck_states.cpp deleted file mode 100644 index ba424dae..00000000 --- a/buildcc/lib/target/mock/generator/recheck_states.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "target/generator.h" - -#include "expect_generator.h" - -#include "CppUTestExt/MockSupport.h" - -namespace buildcc { - -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 diff --git a/buildcc/lib/target/mock/generator/runner.cpp b/buildcc/lib/target/mock/generator/runner.cpp deleted file mode 100644 index 3ca990e8..00000000 --- a/buildcc/lib/target/mock/generator/runner.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "target/generator.h" - -#include "expect_generator.h" - -namespace buildcc::m { - -void GeneratorRunner(Generator &generator) { - tf::Executor executor(1); - executor.run(generator.GetTaskflow()); - executor.wait_for_all(); -} - -} // namespace buildcc::m diff --git a/buildcc/lib/target/mock/target/recheck_states.cpp b/buildcc/lib/target/mock/target/recheck_states.cpp index 40586006..f7ff1f34 100644 --- a/buildcc/lib/target/mock/target/recheck_states.cpp +++ b/buildcc/lib/target/mock/target/recheck_states.cpp @@ -13,12 +13,8 @@ static constexpr const char *const SOURCE_ADDED_FUNCTION = static constexpr const char *const SOURCE_UPDATED_FUNCTION = "Target::SourceUpdated"; -static constexpr const char *const PATH_REMOVED_FUNCTION = - "Target::PathRemoved"; -static constexpr const char *const PATH_ADDED_FUNCTION = "Target::PathAdded"; -static constexpr const char *const PATH_UPDATED_FUNCTION = - "Target::PathUpdated"; - +static constexpr const char *const PATH_CHANGED_FUNCTION = + "Target::PathChanged"; static constexpr const char *const DIR_CHANGED_FUNCTION = "Target::DirChanged"; static constexpr const char *const FLAG_CHANGED_FUNCTION = "Target::FlagChanged"; @@ -37,14 +33,12 @@ void Target::SourceUpdated() { } // Path rechecks -void Target::PathRemoved() { - mock().actualCall(PATH_REMOVED_FUNCTION).onObject(this); -} -void Target::PathAdded() { - mock().actualCall(PATH_ADDED_FUNCTION).onObject(this); -} -void Target::PathUpdated() { - mock().actualCall(PATH_UPDATED_FUNCTION).onObject(this); +void Target::PathRemoved() { PathChanged(); } +void Target::PathAdded() { PathChanged(); } +void Target::PathUpdated() { PathChanged(); } + +void Target::PathChanged() { + mock().actualCall(PATH_CHANGED_FUNCTION).onObject(this); } void Target::DirChanged() { @@ -72,13 +66,17 @@ void TargetExpect_SourceUpdated(unsigned int calls, Target *target) { } void TargetExpect_PathRemoved(unsigned int calls, Target *target) { - mock().expectNCalls(calls, PATH_REMOVED_FUNCTION).onObject(target); + TargetExpect_PathChanged(calls, target); } void TargetExpect_PathAdded(unsigned int calls, Target *target) { - mock().expectNCalls(calls, PATH_ADDED_FUNCTION).onObject(target); + TargetExpect_PathChanged(calls, target); } void TargetExpect_PathUpdated(unsigned int calls, Target *target) { - mock().expectNCalls(calls, PATH_UPDATED_FUNCTION).onObject(target); + TargetExpect_PathChanged(calls, target); +} + +void TargetExpect_PathChanged(unsigned int calls, Target *target) { + mock().expectNCalls(calls, PATH_CHANGED_FUNCTION).onObject(target); } void TargetExpect_DirChanged(unsigned int calls, Target *target) { diff --git a/buildcc/lib/target/mock/test_target_util.h b/buildcc/lib/target/mock/test_target_util.h new file mode 100644 index 00000000..70bad12a --- /dev/null +++ b/buildcc/lib/target/mock/test_target_util.h @@ -0,0 +1,22 @@ +#ifndef TARGET_MOCK_TEST_TARGET_UTIL_H_ +#define TARGET_MOCK_TEST_TARGET_UTIL_H_ + +#ifdef _WIN32 +#include +#else +#include +#endif + +namespace buildcc::m { + +inline void blocking_sleep(int seconds) { +#ifdef _WIN32 + Sleep(seconds * 1000); +#else + sleep(seconds); +#endif +} + +} // namespace buildcc::m + +#endif diff --git a/buildcc/lib/target/src/api/deps_api.cpp b/buildcc/lib/target/src/api/deps_api.cpp deleted file mode 100644 index 63e59647..00000000 --- a/buildcc/lib/target/src/api/deps_api.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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. - */ - -#include "target/api/deps_api.h" - -#include "target/target_info.h" - -namespace buildcc::internal { - -template -void DepsApi::AddCompileDependencyAbsolute(const fs::path &absolute_path) { - T &t = static_cast(*this); - - t.state_.ExpectsUnlock(); - t.storer_.current_compile_dependencies.user.insert(absolute_path); -} -template -void DepsApi::AddLinkDependencyAbsolute(const fs::path &absolute_path) { - T &t = static_cast(*this); - - t.state_.ExpectsUnlock(); - t.storer_.current_link_dependencies.user.insert(absolute_path); -} - -template -void DepsApi::AddCompileDependency(const fs::path &relative_path) { - T &t = static_cast(*this); - - fs::path absolute_path = t.env_.GetTargetRootDir() / relative_path; - AddCompileDependencyAbsolute(absolute_path); -} - -template -void DepsApi::AddLinkDependency(const fs::path &relative_path) { - T &t = static_cast(*this); - - fs::path absolute_path = t.env_.GetTargetRootDir() / relative_path; - AddLinkDependencyAbsolute(absolute_path); -} - -template class DepsApi; - -} // namespace buildcc::internal diff --git a/buildcc/lib/target/src/api/flag_api.cpp b/buildcc/lib/target/src/api/flag_api.cpp deleted file mode 100644 index abcfbcf7..00000000 --- a/buildcc/lib/target/src/api/flag_api.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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. - */ - -#include "target/api/flag_api.h" - -#include "target/target_info.h" - -namespace buildcc::internal { - -template -void FlagApi::AddPreprocessorFlag(const std::string &flag) { - T &t = static_cast(*this); - - t.state_.ExpectsUnlock(); - t.storer_.current_preprocessor_flags.insert(flag); -} -template -void FlagApi::AddCommonCompileFlag(const std::string &flag) { - T &t = static_cast(*this); - - t.state_.ExpectsUnlock(); - t.storer_.current_common_compile_flags.insert(flag); -} -template -void FlagApi::AddPchCompileFlag(const std::string &flag) { - T &t = static_cast(*this); - - t.state_.ExpectsUnlock(); - t.storer_.current_pch_compile_flags.insert(flag); -} -template -void FlagApi::AddPchObjectFlag(const std::string &flag) { - T &t = static_cast(*this); - - t.state_.ExpectsUnlock(); - t.storer_.current_pch_object_flags.insert(flag); -} -template -void FlagApi::AddAsmCompileFlag(const std::string &flag) { - T &t = static_cast(*this); - - t.state_.ExpectsUnlock(); - t.storer_.current_asm_compile_flags.insert(flag); -} -template -void FlagApi::AddCCompileFlag(const std::string &flag) { - T &t = static_cast(*this); - - t.state_.ExpectsUnlock(); - t.storer_.current_c_compile_flags.insert(flag); -} -template -void FlagApi::AddCppCompileFlag(const std::string &flag) { - T &t = static_cast(*this); - - t.state_.ExpectsUnlock(); - t.storer_.current_cpp_compile_flags.insert(flag); -} -template void FlagApi::AddLinkFlag(const std::string &flag) { - T &t = static_cast(*this); - - t.state_.ExpectsUnlock(); - t.storer_.current_link_flags.insert(flag); -} - -template class FlagApi; - -} // namespace buildcc::internal diff --git a/buildcc/lib/target/src/api/include_api.cpp b/buildcc/lib/target/src/api/include_api.cpp deleted file mode 100644 index bb9aa1b1..00000000 --- a/buildcc/lib/target/src/api/include_api.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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. - */ - -#include "target/api/include_api.h" - -#include "target/target_info.h" - -namespace buildcc::internal { - -template -void IncludeApi::AddHeaderAbsolute(const fs::path &absolute_filepath) { - T &t = static_cast(*this); - - t.state_.ExpectsUnlock(); - t.config_.ExpectsValidHeader(absolute_filepath); - t.storer_.current_header_files.user.insert(absolute_filepath); -} - -template -void IncludeApi::AddHeader(const fs::path &relative_filename, - const fs::path &relative_to_target_path) { - T &t = static_cast(*this); - - // Check Source - fs::path absolute_filepath = - t.env_.GetTargetRootDir() / relative_to_target_path / relative_filename; - AddHeaderAbsolute(absolute_filepath); -} - -template -void IncludeApi::GlobHeaders(const fs::path &relative_to_target_path) { - T &t = static_cast(*this); - - fs::path absolute_path = t.env_.GetTargetRootDir() / relative_to_target_path; - GlobHeadersAbsolute(absolute_path); -} - -template -void IncludeApi::GlobHeadersAbsolute(const fs::path &absolute_path) { - T &t = static_cast(*this); - - for (const auto &p : fs::directory_iterator(absolute_path)) { - if (t.config_.IsValidHeader(p.path())) { - AddHeaderAbsolute(p.path()); - } - } -} - -template -void IncludeApi::AddIncludeDir(const fs::path &relative_include_dir, - bool glob_headers) { - T &t = static_cast(*this); - - const fs::path absolute_include_dir = - t.env_.GetTargetRootDir() / relative_include_dir; - AddIncludeDirAbsolute(absolute_include_dir, glob_headers); -} - -template -void IncludeApi::AddIncludeDirAbsolute(const fs::path &absolute_include_dir, - bool glob_headers) { - T &t = static_cast(*this); - - t.state_.ExpectsUnlock(); - t.storer_.current_include_dirs.insert(absolute_include_dir); - - if (glob_headers) { - GlobHeadersAbsolute(absolute_include_dir); - } -} - -template class IncludeApi; - -} // namespace buildcc::internal diff --git a/buildcc/lib/target/src/api/lib_api.cpp b/buildcc/lib/target/src/api/lib_api.cpp index 0814927c..0d24ca8c 100644 --- a/buildcc/lib/target/src/api/lib_api.cpp +++ b/buildcc/lib/target/src/api/lib_api.cpp @@ -21,34 +21,10 @@ namespace buildcc::internal { -template -void LibApi::AddLibDir(const fs::path &relative_lib_dir) { - T &t = static_cast(*this); - - fs::path final_lib_dir = t.env_.GetTargetRootDir() / relative_lib_dir; - AddLibDirAbsolute(final_lib_dir); -} - -template -void LibApi::AddLibDirAbsolute(const fs::path &absolute_lib_dir) { - T &t = static_cast(*this); - - t.state_.ExpectsUnlock(); - t.storer_.current_lib_dirs.insert(absolute_lib_dir); -} - template void LibApi::AddLibDep(const BaseTarget &lib_dep) { T &t = static_cast(*this); - t.state_.ExpectsUnlock(); - t.storer_.current_user_lib_deps.push_back(lib_dep.GetTargetPath()); -} - -template void LibApi::AddLibDep(const std::string &lib_dep) { - T &t = static_cast(*this); - - t.state_.ExpectsUnlock(); - t.storer_.current_user_external_lib_deps.push_back(lib_dep); + t.user_.libs.Emplace(lib_dep.GetTargetPath(), ""); } template class LibApi; diff --git a/buildcc/lib/target/src/api/pch_api.cpp b/buildcc/lib/target/src/api/pch_api.cpp deleted file mode 100644 index 885c5469..00000000 --- a/buildcc/lib/target/src/api/pch_api.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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. - */ - -#include "target/api/pch_api.h" - -#include "target/target_info.h" - -namespace buildcc::internal { - -template -void PchApi::AddPchAbsolute(const fs::path &absolute_filepath) { - T &t = static_cast(*this); - - t.state_.ExpectsUnlock(); - t.config_.ExpectsValidHeader(absolute_filepath); - - const fs::path absolute_pch = fs::path(absolute_filepath).make_preferred(); - t.storer_.current_pch_files.user.insert(absolute_pch); -} - -template -void PchApi::AddPch(const fs::path &relative_filename, - const fs::path &relative_to_target_path) { - T &t = static_cast(*this); - - // Compute the absolute source path - fs::path absolute_pch = - t.env_.GetTargetRootDir() / relative_to_target_path / relative_filename; - - AddPchAbsolute(absolute_pch); -} - -template class PchApi; - -} // namespace buildcc::internal diff --git a/buildcc/lib/target/src/api/source_api.cpp b/buildcc/lib/target/src/api/source_api.cpp deleted file mode 100644 index cce228df..00000000 --- a/buildcc/lib/target/src/api/source_api.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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. - */ - -#include "target/api/source_api.h" - -#include "target/target_info.h" - -namespace buildcc::internal { - -template -void SourceApi::AddSourceAbsolute(const fs::path &absolute_source) { - T &t = static_cast(*this); - - t.state_.ExpectsUnlock(); - t.config_.ExpectsValidSource(absolute_source); - t.storer_.current_source_files.user.emplace( - fs::path(absolute_source).make_preferred()); -} - -template -void SourceApi::GlobSourcesAbsolute(const fs::path &absolute_source_dir) { - T &t = static_cast(*this); - - for (const auto &p : fs::directory_iterator(absolute_source_dir)) { - if (t.config_.IsValidSource(p.path())) { - AddSourceAbsolute(p.path()); - } - } -} - -template -void SourceApi::AddSource( - const fs::path &relative_source, - const std::filesystem::path &relative_to_target_path) { - T &t = static_cast(*this); - - // Compute the absolute source path - fs::path absolute_source = - t.env_.GetTargetRootDir() / relative_to_target_path / relative_source; - AddSourceAbsolute(absolute_source); -} - -template -void SourceApi::GlobSources(const fs::path &relative_to_target_path) { - T &t = static_cast(*this); - - fs::path absolute_input_path = - t.env_.GetTargetRootDir() / relative_to_target_path; - for (const auto &p : fs::directory_iterator(absolute_input_path)) { - if (t.config_.IsValidSource(p.path())) { - AddSourceAbsolute(p.path()); - } - } -} - -// -template class SourceApi; - -} // namespace buildcc::internal diff --git a/buildcc/lib/target/src/api/sync_api.cpp b/buildcc/lib/target/src/api/sync_api.cpp index d5efc349..7cecfc06 100644 --- a/buildcc/lib/target/src/api/sync_api.cpp +++ b/buildcc/lib/target/src/api/sync_api.cpp @@ -40,76 +40,61 @@ template template void SyncApi::SpecializedCopy(TargetType target, std::initializer_list options) { - T &t = static_cast(*this); - t.state_.ExpectsUnlock(); + auto &t = static_cast(*this); for (const SyncOption o : options) { switch (o) { case SyncOption::PreprocessorFlags: - t.storer_.current_preprocessor_flags = - std::move(target.storer_.current_preprocessor_flags); + t.user_.preprocessor_flags = std::move(target.user_.preprocessor_flags); break; case SyncOption::CommonCompileFlags: - t.storer_.current_common_compile_flags = - std::move(target.storer_.current_common_compile_flags); + t.user_.common_compile_flags = + std::move(target.user_.common_compile_flags); break; case SyncOption::PchCompileFlags: - t.storer_.current_pch_compile_flags = - std::move(target.storer_.current_pch_compile_flags); + t.user_.pch_compile_flags = std::move(target.user_.pch_compile_flags); break; case SyncOption::PchObjectFlags: - t.storer_.current_pch_object_flags = - std::move(target.storer_.current_pch_object_flags); + t.user_.pch_object_flags = std::move(target.user_.pch_object_flags); break; case SyncOption::AsmCompileFlags: - t.storer_.current_asm_compile_flags = - std::move(target.storer_.current_asm_compile_flags); + t.user_.asm_compile_flags = std::move(target.user_.asm_compile_flags); break; case SyncOption::CCompileFlags: - t.storer_.current_c_compile_flags = - std::move(target.storer_.current_c_compile_flags); + t.user_.c_compile_flags = std::move(target.user_.c_compile_flags); break; case SyncOption::CppCompileFlags: - t.storer_.current_cpp_compile_flags = - std::move(target.storer_.current_cpp_compile_flags); + t.user_.cpp_compile_flags = std::move(target.user_.cpp_compile_flags); break; case SyncOption::LinkFlags: - t.storer_.current_link_flags = - std::move(target.storer_.current_link_flags); + t.user_.link_flags = std::move(target.user_.link_flags); break; case SyncOption::CompileDependencies: - t.storer_.current_compile_dependencies.user = - std::move(target.storer_.current_compile_dependencies.user); + t.user_.compile_dependencies = + std::move(target.user_.compile_dependencies); break; case SyncOption::LinkDependencies: - t.storer_.current_link_dependencies.user = - std::move(target.storer_.current_link_dependencies.user); + t.user_.link_dependencies = std::move(target.user_.link_dependencies); break; case SyncOption::SourceFiles: - t.storer_.current_source_files.user = - std::move(target.storer_.current_source_files.user); + t.user_.sources = std::move(target.user_.sources); break; case SyncOption::HeaderFiles: - t.storer_.current_header_files.user = - std::move(target.storer_.current_header_files.user); + t.user_.headers = std::move(target.user_.headers); break; case SyncOption::PchFiles: - t.storer_.current_pch_files.user = - std::move(target.storer_.current_pch_files.user); + t.user_.pchs = std::move(target.user_.pchs); break; case SyncOption::LibDeps: - t.storer_.current_user_lib_deps = - std::move(target.storer_.current_user_lib_deps); + t.user_.libs = std::move(target.user_.libs); break; case SyncOption::IncludeDirs: - t.storer_.current_include_dirs = - std::move(target.storer_.current_include_dirs); + t.user_.include_dirs = std::move(target.user_.include_dirs); break; case SyncOption::LibDirs: - t.storer_.current_lib_dirs = std::move(target.storer_.current_lib_dirs); + t.user_.lib_dirs = std::move(target.user_.lib_dirs); break; case SyncOption::ExternalLibDeps: - t.storer_.current_user_external_lib_deps = - std::move(target.storer_.current_user_external_lib_deps); + t.user_.external_libs = std::move(target.user_.external_libs); break; default: env::assert_fatal("Invalid Option added"); @@ -135,121 +120,88 @@ template template void SyncApi::SpecializedInsert(TargetType target, std::initializer_list options) { - T &t = static_cast(*this); - t.state_.ExpectsUnlock(); + auto &t = static_cast(*this); for (const SyncOption o : options) { switch (o) { case SyncOption::PreprocessorFlags: - t.storer_.current_preprocessor_flags.insert( - std::make_move_iterator( - target.storer_.current_preprocessor_flags.begin()), - std::make_move_iterator( - target.storer_.current_preprocessor_flags.end())); + t.user_.preprocessor_flags.insert( + t.user_.preprocessor_flags.end(), + std::make_move_iterator(target.user_.preprocessor_flags.begin()), + std::make_move_iterator(target.user_.preprocessor_flags.end())); break; case SyncOption::CommonCompileFlags: - t.storer_.current_common_compile_flags.insert( - std::make_move_iterator( - target.storer_.current_common_compile_flags.begin()), - std::make_move_iterator( - target.storer_.current_common_compile_flags.end())); + t.user_.common_compile_flags.insert( + t.user_.common_compile_flags.end(), + std::make_move_iterator(target.user_.common_compile_flags.begin()), + std::make_move_iterator(target.user_.common_compile_flags.end())); break; case SyncOption::PchCompileFlags: - t.storer_.current_pch_compile_flags.insert( - std::make_move_iterator( - target.storer_.current_pch_compile_flags.begin()), - std::make_move_iterator( - target.storer_.current_pch_compile_flags.end())); + t.user_.pch_compile_flags.insert( + t.user_.pch_compile_flags.end(), + std::make_move_iterator(target.user_.pch_compile_flags.begin()), + std::make_move_iterator(target.user_.pch_compile_flags.end())); break; case SyncOption::PchObjectFlags: - t.storer_.current_pch_object_flags.insert( - std::make_move_iterator( - target.storer_.current_pch_object_flags.begin()), - std::make_move_iterator( - target.storer_.current_pch_object_flags.end())); + t.user_.pch_object_flags.insert( + t.user_.pch_object_flags.end(), + std::make_move_iterator(target.user_.pch_object_flags.begin()), + std::make_move_iterator(target.user_.pch_object_flags.end())); break; case SyncOption::AsmCompileFlags: - t.storer_.current_asm_compile_flags.insert( - std::make_move_iterator( - target.storer_.current_asm_compile_flags.begin()), - std::make_move_iterator( - target.storer_.current_asm_compile_flags.end())); + t.user_.asm_compile_flags.insert( + t.user_.asm_compile_flags.end(), + std::make_move_iterator(target.user_.asm_compile_flags.begin()), + std::make_move_iterator(target.user_.asm_compile_flags.end())); break; case SyncOption::CCompileFlags: - t.storer_.current_c_compile_flags.insert( - std::make_move_iterator( - target.storer_.current_c_compile_flags.begin()), - std::make_move_iterator( - target.storer_.current_c_compile_flags.end())); + t.user_.c_compile_flags.insert( + t.user_.c_compile_flags.end(), + std::make_move_iterator(target.user_.c_compile_flags.begin()), + std::make_move_iterator(target.user_.c_compile_flags.end())); break; case SyncOption::CppCompileFlags: - t.storer_.current_cpp_compile_flags.insert( - std::make_move_iterator( - target.storer_.current_cpp_compile_flags.begin()), - std::make_move_iterator( - target.storer_.current_cpp_compile_flags.end())); + t.user_.cpp_compile_flags.insert( + t.user_.cpp_compile_flags.end(), + std::make_move_iterator(target.user_.cpp_compile_flags.begin()), + std::make_move_iterator(target.user_.cpp_compile_flags.end())); break; case SyncOption::LinkFlags: - t.storer_.current_link_flags.insert( - std::make_move_iterator(target.storer_.current_link_flags.begin()), - std::make_move_iterator(target.storer_.current_link_flags.end())); + t.user_.link_flags.insert( + t.user_.link_flags.end(), + std::make_move_iterator(target.user_.link_flags.begin()), + std::make_move_iterator(target.user_.link_flags.end())); break; case SyncOption::CompileDependencies: - t.storer_.current_compile_dependencies.user.insert( - std::make_move_iterator( - target.storer_.current_compile_dependencies.user.begin()), - std::make_move_iterator( - target.storer_.current_compile_dependencies.user.end())); + t.user_.compile_dependencies.Insert( + std::move(target.user_.compile_dependencies)); break; case SyncOption::LinkDependencies: - t.storer_.current_link_dependencies.user.insert( - std::make_move_iterator( - target.storer_.current_link_dependencies.user.begin()), - std::make_move_iterator( - target.storer_.current_link_dependencies.user.end())); + t.user_.link_dependencies.Insert( + std::move(target.user_.link_dependencies)); break; case SyncOption::SourceFiles: - t.storer_.current_source_files.user.insert( - std::make_move_iterator( - target.storer_.current_source_files.user.begin()), - std::make_move_iterator( - target.storer_.current_source_files.user.end())); + t.user_.sources.Insert(std::move(target.user_.sources)); break; case SyncOption::HeaderFiles: - t.storer_.current_header_files.user.insert( - std::make_move_iterator( - target.storer_.current_header_files.user.begin()), - std::make_move_iterator( - target.storer_.current_header_files.user.end())); + t.user_.headers.Insert(std::move(target.user_.headers)); break; case SyncOption::PchFiles: - t.storer_.current_pch_files.user.insert( - std::make_move_iterator( - target.storer_.current_pch_files.user.begin()), - std::make_move_iterator(target.storer_.current_pch_files.user.end())); + t.user_.pchs.Insert(std::move(target.user_.pchs)); break; case SyncOption::LibDeps: - t.storer_.current_user_lib_deps.insert( - t.storer_.current_user_lib_deps.end(), - std::make_move_iterator(target.storer_.current_user_lib_deps.begin()), - std::make_move_iterator(target.storer_.current_user_lib_deps.end())); + t.user_.libs.Insert(std::move(target.user_.libs)); break; case SyncOption::IncludeDirs: - t.storer_.current_include_dirs.insert( - std::make_move_iterator(target.storer_.current_include_dirs.begin()), - std::make_move_iterator(target.storer_.current_include_dirs.end())); + t.user_.include_dirs.Insert(std::move(target.user_.include_dirs)); break; case SyncOption::LibDirs: - t.storer_.current_lib_dirs.insert( - std::make_move_iterator(target.storer_.current_lib_dirs.begin()), - std::make_move_iterator(target.storer_.current_lib_dirs.end())); + t.user_.lib_dirs.Insert(std::move(target.user_.lib_dirs)); break; case SyncOption::ExternalLibDeps: - t.storer_.current_user_external_lib_deps.insert( - t.storer_.current_user_external_lib_deps.end(), - std::make_move_iterator( - target.storer_.current_user_external_lib_deps.begin()), - std::make_move_iterator( - target.storer_.current_user_external_lib_deps.end())); + t.user_.external_libs.insert( + t.user_.external_libs.end(), + std::make_move_iterator(target.user_.external_libs.begin()), + std::make_move_iterator(target.user_.external_libs.end())); break; default: env::assert_fatal("Invalid Option added"); diff --git a/buildcc/lib/target/src/api/target_getter.cpp b/buildcc/lib/target/src/api/target_getter.cpp index 83cad46e..dc25a826 100644 --- a/buildcc/lib/target/src/api/target_getter.cpp +++ b/buildcc/lib/target/src/api/target_getter.cpp @@ -20,74 +20,73 @@ namespace buildcc::internal { -template const fs::path &TargetGetter::GetBinaryPath() const { - const T &t = static_cast(*this); +// Target State +template const TargetState &TargetGetter::GetState() const { + const auto &t = static_cast(*this); + return t.state_; +} - return t.loader_.GetBinaryPath(); +template bool TargetGetter::IsBuilt() const { + const auto &t = static_cast(*this); + return t.state_.IsBuilt(); } -template const fs::path &TargetGetter::GetTargetPath() const { - const T &t = static_cast(*this); +// Target Config +template const TargetConfig &TargetGetter::GetConfig() const { + const auto &t = static_cast(*this); + return t.config_; +} +template const fs::path &TargetGetter::GetBinaryPath() const { + const auto &t = static_cast(*this); + return t.serialization_.GetSerializedFile(); +} + +template const fs::path &TargetGetter::GetTargetPath() const { + const auto &t = static_cast(*this); return t.link_target_.GetOutput(); } template const fs::path &TargetGetter::GetPchHeaderPath() const { - const T &t = static_cast(*this); - + const auto &t = static_cast(*this); return t.compile_pch_.GetHeaderPath(); } template const fs::path &TargetGetter::GetPchCompilePath() const { - const T &t = static_cast(*this); - + const auto &t = static_cast(*this); return t.compile_pch_.GetCompilePath(); } template const std::string &TargetGetter::GetName() const { - const T &t = static_cast(*this); - + const auto &t = static_cast(*this); return t.name_; } template const Toolchain &TargetGetter::GetToolchain() const { - const T &t = static_cast(*this); - + const auto &t = static_cast(*this); return t.toolchain_; } template TargetType TargetGetter::GetType() const { - const T &t = static_cast(*this); - + const auto &t = static_cast(*this); return t.type_; } template const std::string & TargetGetter::GetCompileCommand(const fs::path &source) const { - const T &t = static_cast(*this); - - t.state_.ExpectsLock(); + const auto &t = static_cast(*this); return t.compile_object_.GetObjectData(source).command; } template const std::string &TargetGetter::GetLinkCommand() const { - const T &t = static_cast(*this); - - t.state_.ExpectsLock(); + const auto &t = static_cast(*this); return t.link_target_.GetCommand(); } -template tf::Taskflow &TargetGetter::GetTaskflow() { - T &t = static_cast(*this); - - t.state_.ExpectsLock(); - return t.tf_; -} - template class TargetGetter; } // namespace buildcc::internal diff --git a/buildcc/lib/target/src/api/target_info_getter.cpp b/buildcc/lib/target/src/api/target_info_getter.cpp deleted file mode 100644 index 10b18a2f..00000000 --- a/buildcc/lib/target/src/api/target_info_getter.cpp +++ /dev/null @@ -1,196 +0,0 @@ -/* - * 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. - */ - -#include "target/api/target_info_getter.h" - -#include "target/target_info.h" - -namespace buildcc::internal { - -// Target State -template const TargetState &TargetInfoGetter::GetState() const { - const T &t = static_cast(*this); - - return t.state_; -} - -template bool TargetInfoGetter::IsBuilt() const { - const T &t = static_cast(*this); - - return t.state_.build; -} - -template bool TargetInfoGetter::IsLocked() const { - const T &t = static_cast(*this); - - return t.state_.lock; -} - -// Target Env -template -const fs::path &TargetInfoGetter::GetTargetRootDir() const { - const T &t = static_cast(*this); - - return t.env_.GetTargetRootDir(); -} - -template -const fs::path &TargetInfoGetter::GetTargetBuildDir() const { - const T &t = static_cast(*this); - - return t.env_.GetTargetBuildDir(); -} - -// Target Config -template -const TargetConfig &TargetInfoGetter::GetConfig() const { - const T &t = static_cast(*this); - - return t.config_; -} - -// Target Storer -template -const fs_unordered_set &TargetInfoGetter::GetSourceFiles() const { - const T &t = static_cast(*this); - - return t.storer_.current_source_files.user; -} - -template -const fs_unordered_set &TargetInfoGetter::GetHeaderFiles() const { - const T &t = static_cast(*this); - - return t.storer_.current_header_files.user; -} - -template -const fs_unordered_set &TargetInfoGetter::GetPchFiles() const { - const T &t = static_cast(*this); - - return t.storer_.current_pch_files.user; -} - -template -const std::vector &TargetInfoGetter::GetLibDeps() const { - const T &t = static_cast(*this); - - return t.storer_.current_user_lib_deps; -} - -template -const std::vector & -TargetInfoGetter::GetExternalLibDeps() const { - const T &t = static_cast(*this); - - return t.storer_.current_user_external_lib_deps; -} - -template -const fs_unordered_set &TargetInfoGetter::GetIncludeDirs() const { - const T &t = static_cast(*this); - - return t.storer_.current_include_dirs; -} - -template -const fs_unordered_set &TargetInfoGetter::GetLibDirs() const { - const T &t = static_cast(*this); - - return t.storer_.current_lib_dirs; -} - -template -const std::unordered_set & -TargetInfoGetter::GetPreprocessorFlags() const { - const T &t = static_cast(*this); - - return t.storer_.current_preprocessor_flags; -} - -template -const std::unordered_set & -TargetInfoGetter::GetCommonCompileFlags() const { - const T &t = static_cast(*this); - - return t.storer_.current_common_compile_flags; -} - -template -const std::unordered_set & -TargetInfoGetter::GetPchCompileFlags() const { - const T &t = static_cast(*this); - - return t.storer_.current_pch_compile_flags; -} - -template -const std::unordered_set & -TargetInfoGetter::GetPchObjectFlags() const { - const T &t = static_cast(*this); - - return t.storer_.current_pch_object_flags; -} - -template -const std::unordered_set & -TargetInfoGetter::GetAsmCompileFlags() const { - const T &t = static_cast(*this); - - return t.storer_.current_asm_compile_flags; -} - -template -const std::unordered_set & -TargetInfoGetter::GetCCompileFlags() const { - const T &t = static_cast(*this); - - return t.storer_.current_c_compile_flags; -} - -template -const std::unordered_set & -TargetInfoGetter::GetCppCompileFlags() const { - const T &t = static_cast(*this); - - return t.storer_.current_cpp_compile_flags; -} - -template -const std::unordered_set & -TargetInfoGetter::GetLinkFlags() const { - const T &t = static_cast(*this); - - return t.storer_.current_link_flags; -} - -template -const fs_unordered_set &TargetInfoGetter::GetCompileDependencies() const { - const T &t = static_cast(*this); - - return t.storer_.current_compile_dependencies.user; -} - -template -const fs_unordered_set &TargetInfoGetter::GetLinkDependencies() const { - const T &t = static_cast(*this); - - return t.storer_.current_link_dependencies.user; -} - -template class TargetInfoGetter; - -} // namespace buildcc::internal diff --git a/buildcc/lib/target/src/common/target_config.cpp b/buildcc/lib/target/src/common/target_config.cpp index 2b67f290..23207ac1 100644 --- a/buildcc/lib/target/src/common/target_config.cpp +++ b/buildcc/lib/target/src/common/target_config.cpp @@ -18,71 +18,8 @@ #include "env/assert_fatal.h" -#include "target/common/path.h" +#include "schema/path.h" #include "fmt/format.h" -namespace buildcc { - -TargetFileExt TargetConfig::GetFileExt(const fs::path &filepath) const { - if (!filepath.has_extension()) { - return TargetFileExt::Invalid; - } - - TargetFileExt type = TargetFileExt::Invalid; - const std::string ext = filepath.extension().string(); - - if (valid_c_ext.count(ext) == 1) { - type = TargetFileExt::C; - } else if (valid_cpp_ext.count(ext) == 1) { - type = TargetFileExt::Cpp; - } else if (valid_asm_ext.count(ext) == 1) { - type = TargetFileExt::Asm; - } else if (valid_header_ext.count(ext) == 1) { - type = TargetFileExt::Header; - } - - return type; -} - -bool TargetConfig::IsValidSource(const fs::path &filepath) const { - if (!filepath.has_extension()) { - return false; - } - - const std::string ext = filepath.extension().string(); - bool valid = false; - if ((valid_c_ext.find(ext) != valid_c_ext.end()) || - (valid_cpp_ext.find(ext) != valid_cpp_ext.end()) || - (valid_asm_ext.find(ext) != valid_asm_ext.end())) { - valid = true; - } - return valid; -} - -void TargetConfig::ExpectsValidSource(const fs::path &filepath) const { - env::assert_fatal( - IsValidSource(filepath), - fmt::format("{} does not have a valid source extension", filepath)); -} - -bool TargetConfig::IsValidHeader(const fs::path &filepath) const { - if (!filepath.has_extension()) { - return {}; - } - - const std::string ext = filepath.extension().string(); - bool valid = false; - if ((valid_header_ext.find(ext) != valid_header_ext.end())) { - valid = true; - } - return valid; -} - -void TargetConfig::ExpectsValidHeader(const fs::path &filepath) const { - env::assert_fatal( - IsValidHeader(filepath), - fmt::format("{} does not have a valid header extension", filepath)); -} - -} // namespace buildcc +namespace buildcc {} // namespace buildcc diff --git a/buildcc/lib/target/src/common/target_state.cpp b/buildcc/lib/target/src/common/target_state.cpp index d2740547..a8e5cc65 100644 --- a/buildcc/lib/target/src/common/target_state.cpp +++ b/buildcc/lib/target/src/common/target_state.cpp @@ -20,34 +20,26 @@ namespace buildcc { -void TargetState::SetSourceState(TargetFileExt file_extension) { +void TargetState::BuildCompleted() { build_ = true; } + +void TargetState::SourceDetected(FileExt file_extension) { switch (file_extension) { - case TargetFileExt::Asm: - contains_asm = true; + case FileExt::Asm: + contains_asm_ = true; break; - case TargetFileExt::C: - contains_c = true; + case FileExt::C: + contains_c_ = true; break; - case TargetFileExt::Cpp: - contains_cpp = true; + case FileExt::Cpp: + contains_cpp_ = true; break; - case TargetFileExt::Header: - case TargetFileExt::Invalid: + case FileExt::Header: + case FileExt::Invalid: default: break; } } -void TargetState::SetPch() { contains_pch_ = true; } - -void TargetState::SetLock() { lock = true; } - -void TargetState::ExpectsUnlock() const { - env::assert_fatal(!lock, "Cannot use this function when lock == true"); -} - -void TargetState::ExpectsLock() const { - env::assert_fatal(lock, "Cannot use this function when lock == false"); -} +void TargetState::PchDetected() { contains_pch_ = true; } } // namespace buildcc diff --git a/buildcc/lib/target/src/common/util.cpp b/buildcc/lib/target/src/common/util.cpp deleted file mode 100644 index 881e610a..00000000 --- a/buildcc/lib/target/src/common/util.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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. - */ - -#include "target/common/util.h" - -#include "env/assert_fatal.h" -#include "env/env.h" - -#include "fmt/format.h" - -namespace buildcc::internal { - -// Aggregates - -std::string aggregate(const buildcc::fs_unordered_set &paths) { - std::vector agg; - std::transform( - paths.begin(), paths.end(), std::back_inserter(agg), - [](const fs::path &p) -> std::string { return fmt::format("{}", p); }); - return aggregate(agg); -} - -std::string aggregate_with_prefix(const std::string &prefix, - const fs_unordered_set &dirs) { - std::vector agg; - std::transform(dirs.begin(), dirs.end(), std::back_inserter(agg), - [&](const fs::path &dir) -> std::string { - return fmt::format("{}{}", prefix, fmt::format("{}", dir)); - }); - return aggregate(agg); -} - -} // namespace buildcc::internal diff --git a/buildcc/lib/target/src/custom_generator/custom_generator.cpp b/buildcc/lib/target/src/custom_generator/custom_generator.cpp new file mode 100644 index 00000000..138bb9f3 --- /dev/null +++ b/buildcc/lib/target/src/custom_generator/custom_generator.cpp @@ -0,0 +1,317 @@ +/* + * 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. + */ + +#include "target/custom_generator.h" + +namespace { + +constexpr const char *const kGenerateTaskName = "Generate"; +constexpr const char *const kProjectRootDirName = "project_root_dir"; +constexpr const char *const kProjectBuildDirName = "project_build_dir"; +constexpr const char *const kCurrentRootDirName = "current_root_dir"; +constexpr const char *const kCurrentBuildDirName = "current_build_dir"; + +} // namespace + +namespace buildcc { + +struct Comparator { + Comparator(const internal::CustomGeneratorSchema &loaded, + const UserCustomGeneratorSchema ¤t) + : loaded_(loaded), current_(current) {} + + enum class State { + kRemoved, + kAdded, + kCheckLater, + }; + + void AddAllIds() { + const auto &curr_ids = current_.ids; + for (const auto &[id, _] : curr_ids) { + id_state_info_.at(State::kAdded).insert(id); + } + } + + void CompareAndAddIds() { + const auto &prev_ids = loaded_.internal_ids; + const auto &curr_ids = current_.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_.internal_ids.at(id); + const auto ¤t_id_info = current_.ids.at(id); + + bool changed = !previous_id_info.inputs.IsEqual(current_id_info.inputs) || + !previous_id_info.outputs.IsEqual(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 buildcc::internal::CustomGeneratorSchema &loaded_; + const buildcc::UserCustomGeneratorSchema ¤t_; + std::unordered_map> id_state_info_{ + {State::kRemoved, std::unordered_set()}, + {State::kAdded, std::unordered_set()}, + {State::kCheckLater, std::unordered_set()}, + }; +}; + +struct TaskState { + bool should_run{false}; + bool run_success{false}; +}; + +struct TaskFunctor { + TaskFunctor(const std::string &id, + UserCustomGeneratorSchema::UserIdInfo &id_info, + const Comparator &comparator, const env::Command &command, + TaskState &state) + : id_(id), id_info_(id_info), comparator(comparator), command_(command), + state_(state) {} + + void operator()() { + if (env::get_task_state() != env::TaskState::SUCCESS) { + return; + } + try { + id_info_.ConvertToInternal(); + // Compute runnable + state_.should_run = + comparator.IsIdAdded(id_) ? true : comparator.IsChanged(id_); + + // Invoke generator callback + if (state_.should_run) { + const auto input_paths = id_info_.inputs.GetPaths(); + CustomGeneratorContext ctx(command_, input_paths, + id_info_.outputs.GetPaths(), + id_info_.userblob); + + bool success = id_info_.generate_cb(ctx); + env::assert_fatal(success, + fmt::format("Generate Cb failed for id {}", id_)); + } + state_.run_success = true; + } catch (...) { + env::set_task_state(env::TaskState::FAILURE); + } + } + +private: + const std::string &id_; + UserCustomGeneratorSchema::UserIdInfo &id_info_; + + const Comparator &comparator; + const env::Command &command_; + + TaskState &state_; +}; + +bool ComputeBuild(const internal::CustomGeneratorSerialization &serialization, + Comparator &comparator, + std::function &&id_removed_cb, + std::function &&id_added_cb) { + bool build = false; + if (!serialization.IsLoaded()) { + comparator.AddAllIds(); + build = true; + } else { + comparator.CompareAndAddIds(); + const bool is_removed = !comparator.GetRemovedIds().empty(); + const bool is_added = !comparator.GetAddedIds().empty(); + build = is_removed || is_added; + + if (is_removed) { + id_removed_cb(); + } + + for (const auto &id : comparator.GetAddedIds()) { + (void)id; + id_added_cb(); + } + } + return build; +} + +void CustomGenerator::AddPattern(const std::string &identifier, + const std::string &pattern) { + command_.AddDefaultArgument(identifier, command_.Construct(pattern)); +} + +void CustomGenerator::AddPatterns( + const std::unordered_map &pattern_map) { + for (const auto &[identifier, pattern] : pattern_map) { + AddPattern(identifier, pattern); + } +} + +std::string CustomGenerator::ParsePattern( + const std::string &pattern, + const std::unordered_map &arguments) const { + return command_.Construct(pattern, arguments); +} + +const std::string & +CustomGenerator::Get(const std::string &file_identifier) const { + return command_.GetDefaultValueByKey(file_identifier); +} + +void CustomGenerator::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) { + env::assert_fatal(user_.ids.find(id) == user_.ids.end(), + fmt::format("Duplicate id {} detected", id)); + ASSERT_FATAL(generate_cb, "Invalid callback provided"); + + UserCustomGeneratorSchema::UserIdInfo schema; + for (const auto &i : inputs) { + auto input = command_.Construct(i); + schema.inputs.Emplace(input, ""); + } + for (const auto &o : outputs) { + auto output = command_.Construct(o); + schema.outputs.Emplace(output); + } + schema.generate_cb = generate_cb; + schema.blob_handler = blob_handler; + user_.ids.try_emplace(id, std::move(schema)); +} + +void CustomGenerator::Build() { + (void)serialization_.LoadFromFile(); + GenerateTask(); +} + +// PRIVATE +void CustomGenerator::Initialize() { + // Checks + env::assert_fatal( + Project::IsInit(), + "Environment is not initialized. Use the buildcc::Project::Init API"); + + // + fs::create_directories(env_.GetTargetBuildDir()); + command_.AddDefaultArguments({ + {kProjectRootDirName, path_as_string(Project::GetRootDir())}, + {kProjectBuildDirName, path_as_string(Project::GetBuildDir())}, + {kCurrentRootDirName, path_as_string(env_.GetTargetRootDir())}, + {kCurrentBuildDirName, path_as_string(env_.GetTargetBuildDir())}, + }); + + // + unique_id_ = name_; + tf_.name(name_); +} + +void CustomGenerator::GenerateTask() { + tf::Task generate_task = tf_.emplace([&](tf::Subflow &subflow) { + if (env::get_task_state() != env::TaskState::SUCCESS) { + return; + } + + try { + // Selected ids for build + Comparator comparator(serialization_.GetLoad(), user_); + dirty_ = ComputeBuild( + serialization_, comparator, [this]() { IdRemoved(); }, + [this]() { IdAdded(); }); + + std::unordered_map states; + + // Create runner for each added/updated id + for (const auto &id : comparator.GetAddedIds()) { + states.try_emplace(id, TaskState()); + auto &id_info = user_.ids.at(id); + TaskFunctor functor(id, id_info, comparator, command_, states.at(id)); + subflow.emplace(functor).name(id); + } + + for (const auto &id : comparator.GetCheckLaterIds()) { + states.try_emplace(id, TaskState()); + auto &id_info = user_.ids.at(id); + TaskFunctor functor(id, id_info, comparator, command_, states.at(id)); + subflow.emplace(functor).name(id); + } + + // NOTE, Do not call detach otherwise this will fail + subflow.join(); + + UserCustomGeneratorSchema user_final_schema; + for (const auto &[id, state] : states) { + dirty_ = dirty_ || state.should_run; + if (state.run_success) { + user_final_schema.ids.try_emplace(id, user_.ids.at(id)); + } + } + + // Store + if (dirty_) { + user_final_schema.ConvertToInternal(); + + serialization_.UpdateStore(user_final_schema); + env::assert_fatal(serialization_.StoreToFile(), + fmt::format("Store failed for {}", name_)); + } + } catch (...) { + env::set_task_state(env::TaskState::FAILURE); + } + }); + generate_task.name(kGenerateTaskName); +} + +} // namespace buildcc diff --git a/buildcc/lib/target/src/custom_generator/recheck_states.cpp b/buildcc/lib/target/src/custom_generator/recheck_states.cpp new file mode 100644 index 00000000..0ccc632c --- /dev/null +++ b/buildcc/lib/target/src/custom_generator/recheck_states.cpp @@ -0,0 +1,25 @@ +/* + * 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. + */ + +#include "target/custom_generator.h" + +namespace buildcc { + +void CustomGenerator::IdRemoved() {} +void CustomGenerator::IdAdded() {} +void CustomGenerator::IdUpdated() {} + +} // namespace buildcc diff --git a/buildcc/lib/target/src/generator/file_generator.cpp b/buildcc/lib/target/src/generator/file_generator.cpp new file mode 100644 index 00000000..3256ad2b --- /dev/null +++ b/buildcc/lib/target/src/generator/file_generator.cpp @@ -0,0 +1,73 @@ +/* + * 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. + */ + +#include "target/file_generator.h" + +#include + +#include "env/assert_fatal.h" + +namespace { + +bool FileGeneratorGenerateCb(const buildcc::CustomGeneratorContext &ctx) { + (void)ctx; + bool success = true; + std::vector commands = + buildcc::TypedCustomBlobHandler>::Deserialize( + ctx.userblob); + for (const auto &c : commands) { + bool executed = buildcc::env::Command::Execute(c); + if (!executed) { + success = false; + buildcc::env::log_critical(__FUNCTION__, + fmt::format("Failed to run command {}", c)); + break; + } + } + return success; +} + +} // namespace + +namespace buildcc { + +void FileGenerator::AddInput(const std::string &absolute_input_pattern) { + inputs_.emplace(absolute_input_pattern); +} + +void FileGenerator::AddOutput(const std::string &absolute_output_pattern) { + outputs_.emplace(absolute_output_pattern); +} + +void FileGenerator::AddCommand( + const std::string &command_pattern, + const std::unordered_map &arguments) { + std::string constructed_command = ParsePattern(command_pattern, arguments); + commands_.emplace_back(std::move(constructed_command)); +} + +void FileGenerator::Build() { + auto file_blob_handler = + std::make_shared>>( + commands_); + AddIdInfo("Generate", inputs_, outputs_, FileGeneratorGenerateCb, + file_blob_handler); + this->CustomGenerator::Build(); +} + +// PRIVATE + +} // namespace buildcc diff --git a/buildcc/lib/target/src/generator/generator.cpp b/buildcc/lib/target/src/generator/generator.cpp deleted file mode 100644 index caa2ada9..00000000 --- a/buildcc/lib/target/src/generator/generator.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - * 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. - */ - -#include "target/generator.h" - -#include - -#include "env/assert_fatal.h" - -namespace buildcc { - -void Generator::AddDefaultArguments( - const std::unordered_map &arguments) { - command_.AddDefaultArguments(arguments); -} - -void Generator::AddInput(const std::string &absolute_input_pattern, - const char *identifier) { - std::string absolute_input_string = - command_.Construct(absolute_input_pattern); - const auto absolute_input_path = - internal::Path::CreateNewPath(absolute_input_string); - current_input_files_.user.insert(absolute_input_path.GetPathname()); - - if (identifier != nullptr) { - command_.AddDefaultArgument(identifier, - absolute_input_path.GetPathAsString()); - } -} - -void Generator::AddOutput(const std::string &absolute_output_pattern, - const char *identifier) { - std::string absolute_output_string = - command_.Construct(absolute_output_pattern); - const auto absolute_output_path = - internal::Path::CreateNewPath(absolute_output_string); - current_output_files_.insert(absolute_output_path.GetPathname()); - - if (identifier != nullptr) { - command_.AddDefaultArgument(identifier, - absolute_output_path.GetPathAsString()); - } -} - -void Generator::AddCommand( - const std::string &command_pattern, - const std::unordered_map &arguments) { - std::string constructed_command = - command_.Construct(command_pattern, arguments); - current_commands_.emplace_back(std::move(constructed_command)); -} - -void Generator::Build() { - (void)loader_.Load(); - - GenerateTask(); -} - -const std::string & -Generator::GetValueByIdentifier(const std::string &file_identifier) const { - return command_.GetDefaultValueByKey(file_identifier); -} - -// PRIVATE - -void Generator::Initialize() { - // Checks - env::assert_fatal( - env::is_init(), - "Environment is not initialized. Use the buildcc::env::init API"); - - // - fs::create_directories(generator_build_dir_); - command_.AddDefaultArguments({ - {"gen_root_dir", path_as_string(generator_root_dir_)}, - {"gen_build_dir", path_as_string(generator_build_dir_)}, - }); - - // - unique_id_ = name_; - tf_.name(name_); -} - -void Generator::Convert() { current_input_files_.Convert(); } - -void Generator::BuildGenerate() { - if (!loader_.IsLoaded()) { - dirty_ = true; - } else { - RecheckPaths( - loader_.GetLoadedInputFiles(), current_input_files_.internal, - [&]() { InputRemoved(); }, [&]() { InputAdded(); }, - [&]() { InputUpdated(); }); - RecheckChanged(loader_.GetLoadedOutputFiles(), current_output_files_, - [&]() { OutputChanged(); }); - RecheckChanged(loader_.GetLoadedCommands(), current_commands_, - [&]() { CommandChanged(); }); - } -} - -} // namespace buildcc diff --git a/buildcc/lib/target/src/generator/generator_loader.cpp b/buildcc/lib/target/src/generator/generator_loader.cpp deleted file mode 100644 index 0a54b820..00000000 --- a/buildcc/lib/target/src/generator/generator_loader.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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. - */ - -#include "target/base/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::load_file(path_as_string(file_path).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()); - - extract_path(generator->inputs(), loaded_input_files_); - extract(generator->outputs(), loaded_output_files_); - extract(generator->commands(), loaded_commands_); - - 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 deleted file mode 100644 index a652e5ff..00000000 --- a/buildcc/lib/target/src/generator/generator_storer.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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. - */ - -#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 { - -bool Generator::Store() { - env::log_trace(name_, __FUNCTION__); - - flatbuffers::FlatBufferBuilder builder; - - auto fbs_input_files = - internal::create_fbs_vector_path(builder, current_input_files_.internal); - auto fbs_output_files = - internal::create_fbs_vector_string(builder, current_output_files_); - auto fbs_commands = - internal::create_fbs_vector_string(builder, current_commands_); - - auto fbs_generator = - fbs::CreateGeneratorDirect(builder, name_.c_str(), &fbs_input_files, - &fbs_output_files, &fbs_commands); - fbs::FinishGeneratorBuffer(builder, fbs_generator); - - const fs::path file_path = GetBinaryPath(); - return env::save_file(path_as_string(file_path).c_str(), - (const char *)builder.GetBufferPointer(), - builder.GetSize(), true); -} - -} // namespace buildcc diff --git a/buildcc/lib/target/src/generator/task.cpp b/buildcc/lib/target/src/generator/task.cpp deleted file mode 100644 index 8ee2fb8c..00000000 --- a/buildcc/lib/target/src/generator/task.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - * 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. - */ - -#include "target/generator.h" - -#include "env/command.h" - -namespace { -constexpr const char *const kStartGeneratorTaskName = "Start Generator"; -constexpr const char *const kEndGeneratorTaskName = "End Generator"; - -constexpr const char *const kCommandTaskName = "Command"; -constexpr const char *const kGenerateTaskName = "Generate"; - -} // namespace - -namespace buildcc { - -void Generator::GenerateTask() { - tf::Task start_task = tf_.emplace([this]() { - switch (env::get_task_state()) { - case env::TaskState::SUCCESS: - try { - Convert(); - BuildGenerate(); - } catch (...) { - task_state_ = env::TaskState::FAILURE; - } - break; - default: - task_state_ = env::TaskState::FAILURE; - break; - } - return static_cast(task_state_); - }); - start_task.name(kStartGeneratorTaskName); - - tf::Task generate_task = tf_.emplace([&](tf::Subflow &subflow) { - auto run_command = [this](const std::string &command) { - try { - bool success = env::Command::Execute(command); - env::assert_throw(success, fmt::format("{} failed", command)); - } catch (...) { - std::lock_guard guard(task_state_mutex_); - task_state_ = env::TaskState::FAILURE; - } - }; - - tf::Task command_task; - if (dirty_) { - if (parallel_) { - command_task = subflow.for_each(current_commands_.cbegin(), - current_commands_.cend(), run_command); - } else { - command_task = subflow.emplace([&, run_command]() { - for (const auto &command : current_commands_) { - run_command(command); - } - }); - } - } else { - command_task = subflow.placeholder(); - } - command_task.name(kCommandTaskName); - - // Graph Generation - for (const auto &i : current_input_files_.user) { - std::string name = - fmt::format("{}", i.lexically_relative(env::get_project_root_dir())); - tf::Task task = subflow.placeholder().name(name); - task.precede(command_task); - } - - for (const auto &o : current_output_files_) { - std::string name = - fmt::format("{}", o.lexically_relative(env::get_project_root_dir())); - tf::Task task = subflow.placeholder().name(name); - task.succeed(command_task); - } - }); - generate_task.name(kGenerateTaskName); - - tf::Task end_task = tf_.emplace([this]() { - // task_state_ != env::TaskState::SUCCESS - // We do not need to Store, leave the serialized store with the previous - // values - - // NOTE, Only store if the above state is marked dirty_ AND task_state_ == - // SUCCESS - if (task_state_ == env::TaskState::SUCCESS) { - if (dirty_) { - try { - env::assert_throw(Store(), fmt::format("Store failed for {}", name_)); - } catch (...) { - task_state_ = env::TaskState::FAILURE; - } - } - } - - // Update Env task state when NOT SUCCESS only - if (task_state_ != env::TaskState::SUCCESS) { - env::set_task_state(task_state_); - } - }); - end_task.name(kEndGeneratorTaskName); - - // Dependencies - start_task.precede(generate_task, end_task); - generate_task.precede(end_task); -} - -} // namespace buildcc diff --git a/buildcc/lib/target/src/generator/template_generator.cpp b/buildcc/lib/target/src/generator/template_generator.cpp new file mode 100644 index 00000000..45cab89d --- /dev/null +++ b/buildcc/lib/target/src/generator/template_generator.cpp @@ -0,0 +1,75 @@ +/* + * 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. + */ + +#include "target/template_generator.h" + +#include "env/env.h" + +namespace { + +bool template_generate_cb(const buildcc::CustomGeneratorContext &ctx) { + std::string pattern_data; + const fs::path &input = *ctx.inputs.begin(); + const fs::path &output = *ctx.outputs.begin(); + + bool success = + buildcc::env::load_file(input.string().c_str(), false, &pattern_data); + if (success) { + std::string parsed_data = ctx.command.Construct(pattern_data); + success = + buildcc::env::save_file(output.string().c_str(), parsed_data, false); + } + + if (!success) { + buildcc::env::log_critical( + __FUNCTION__, fmt::format("Failed to parse {} -> {}", input, output)); + } + return success; +} + +} // namespace + +namespace buildcc { + +void TemplateGenerator::AddTemplate(std::string_view absolute_input_pattern, + std::string_view absolute_output_pattern) { + TemplateInfo info; + info.input_pattern = absolute_input_pattern; + info.output_pattern = absolute_output_pattern; + template_infos_.emplace_back(std::move(info)); +} + +std::string TemplateGenerator::Parse(const std::string &pattern) const { + return ParsePattern(pattern); +} + +/** + * @brief Build FileGenerator Tasks + * + * Use `GetTaskflow` for the registered tasks + */ +void TemplateGenerator::Build() { + for (const auto &info : template_infos_) { + std::string name = string_as_path(ParsePattern(info.input_pattern)) + .lexically_relative(Project::GetRootDir()) + .string(); + AddIdInfo(name, {info.input_pattern}, {info.output_pattern}, + template_generate_cb); + } + this->CustomGenerator::Build(); +} + +} // namespace buildcc diff --git a/buildcc/lib/target/src/target/build.cpp b/buildcc/lib/target/src/target/build.cpp index 82e00a53..18ae8fa4 100644 --- a/buildcc/lib/target/src/target/build.cpp +++ b/buildcc/lib/target/src/target/build.cpp @@ -54,31 +54,28 @@ namespace buildcc { void Target::Build() { env::log_trace(name_, __FUNCTION__); - state_.ExpectsUnlock(); - state_.SetLock(); - // PCH state - if (!storer_.current_pch_files.user.empty()) { - state_.SetPch(); + if (!user_.pchs.GetPathInfos().empty()) { + state_.PchDetected(); } // Source - Object relation // Source state - for (const auto &abs_source : storer_.current_source_files.user) { + for (const auto &source_info : user_.sources.GetPathInfos()) { // Set state - state_.SetSourceState(config_.GetFileExt(abs_source)); + state_.SourceDetected(toolchain_.GetConfig().GetFileExt(source_info.path)); // Relate input source with output object - compile_object_.AddObjectData(abs_source); + compile_object_.AddObjectData(source_info.path); } // Target default arguments command_.AddDefaultArguments({ - {kIncludeDirs, internal::aggregate_with_prefix(config_.prefix_include_dir, - GetIncludeDirs())}, - {kLibDirs, - internal::aggregate_with_prefix(config_.prefix_lib_dir, GetLibDirs())}, - + {kIncludeDirs, + internal::aggregate_with_prefix>( + toolchain_.GetConfig().prefix_include_dir, GetIncludeDirs())}, + {kLibDirs, internal::aggregate_with_prefix>( + toolchain_.GetConfig().prefix_lib_dir, GetLibDirs())}, {kPreprocessorFlags, internal::aggregate(GetPreprocessorFlags())}, {kCommonCompileFlags, internal::aggregate(GetCommonCompileFlags())}, // TODO, Cache more flags here @@ -86,7 +83,7 @@ void Target::Build() { {kLinkFlags, internal::aggregate(GetLinkFlags())}, // Toolchain executables here - {kAsmCompiler, fmt::format("{}", fs::path(toolchain_.GetAsmCompiler()))}, + {kAsmCompiler, fmt::format("{}", fs::path(toolchain_.GetAssembler()))}, {kCCompiler, fmt::format("{}", fs::path(toolchain_.GetCCompiler()))}, {kCppCompiler, fmt::format("{}", fs::path(toolchain_.GetCppCompiler()))}, {kArchiver, fmt::format("{}", fs::path(toolchain_.GetArchiver()))}, @@ -94,11 +91,7 @@ void Target::Build() { }); // Load the serialized file - (void)loader_.Load(); - - // Target State Tasks - StartTask(); - EndTask(); + (void)serialization_.LoadFromFile(); // PCH Compile if (state_.ContainsPch()) { @@ -118,6 +111,9 @@ void Target::Build() { }); } + // Target State Tasks + EndTask(); + // Compile Command compile_object_.CacheCompileCommands(); compile_object_.Task(); diff --git a/buildcc/lib/target/src/target/friend/compile_object.cpp b/buildcc/lib/target/src/target/friend/compile_object.cpp index a2c418f7..f905426f 100644 --- a/buildcc/lib/target/src/target/friend/compile_object.cpp +++ b/buildcc/lib/target/src/target/friend/compile_object.cpp @@ -34,24 +34,25 @@ void CompileObject::AddObjectData(const fs::path &absolute_source_path) { ConstructObjectPath(absolute_source_path); fs::create_directories(absolute_object_path.parent_path()); - object_files_.emplace(absolute_source_path, - ObjectData(absolute_object_path, "")); + object_files_.try_emplace( + internal::PathInfo::ToPathString(absolute_source_path), + absolute_object_path, ""); } void CompileObject::CacheCompileCommands() { - for (auto &object_iter : object_files_) { - const fs::path &absolute_current_source = object_iter.first; + for (auto &[absolute_current_source, object_data] : object_files_) { const std::string output = fmt::format("{}", GetObjectData(absolute_current_source).output); const std::string input = fmt::format("{}", absolute_current_source); - const auto type = target_.config_.GetFileExt(absolute_current_source); + const auto type = + target_.toolchain_.GetConfig().GetFileExt(absolute_current_source); const std::string selected_aggregated_compile_flags = target_.SelectCompileFlags(type).value_or(""); const std::string selected_compiler = fmt::format("{}", fs::path(target_.SelectCompiler(type).value_or(""))); - object_iter.second.command = target_.command_.Construct( + object_data.command = target_.command_.Construct( target_.GetConfig().compile_command, { {kCompiler, selected_compiler}, @@ -62,28 +63,30 @@ void CompileObject::CacheCompileCommands() { } } -fs_unordered_set CompileObject::GetCompiledSources() const { - fs_unordered_set compiled_sources; - for (const auto &p : object_files_) { - compiled_sources.insert(p.second.output); +std::vector CompileObject::GetCompiledSources() const { + std::vector compiled_sources; + for (const auto &[_, object_data] : object_files_) { + compiled_sources.push_back(object_data.output); } return compiled_sources; } const CompileObject::ObjectData & CompileObject::GetObjectData(const fs::path &absolute_source) const { - const auto fiter = object_files_.find(absolute_source); + const auto sanitized_source = + internal::PathInfo::ToPathString(absolute_source); + const auto fiter = object_files_.find(sanitized_source); env::assert_fatal(fiter != object_files_.end(), fmt::format("{} not found", absolute_source)); - return object_files_.at(absolute_source); + return object_files_.at(sanitized_source); } // PRIVATE // NOTE: If RELATIVE TargetEnv supplied -// {target_root_dir} => `env::get_project_root_dir()` / +// {target_root_dir} => `Project::GetRootDir()` / // `target_relative_to_root` -// {target_build_dir} => `env::get_project_build_dir()` / `toolchain.GetName()` +// {target_build_dir} => `Project::GetBuildDir()` / `toolchain.GetName()` // / `name` // Scenarios @@ -156,39 +159,49 @@ CompileObject::ConstructObjectPath(const fs::path &absolute_source_file) const { fs::path absolute_compiled_source = target_.GetTargetBuildDir() / relative; absolute_compiled_source.replace_filename( fmt::format("{}{}", absolute_source_file.filename().string(), - target_.GetConfig().obj_ext)); + target_.toolchain_.GetConfig().obj_ext)); return absolute_compiled_source; } void CompileObject::BuildObjectCompile( - std::vector &source_files, - std::vector &dummy_source_files) { + std::vector &source_files, + std::vector &dummy_source_files) { PreObjectCompile(); - const auto &loader = target_.loader_; - const auto &storer = target_.storer_; + const auto &serialization = target_.serialization_; + const auto &load_target_schema = serialization.GetLoad(); + const auto &user_target_schema = target_.user_; - if (!loader.IsLoaded()) { + if (!serialization.IsLoaded()) { target_.dirty_ = true; } else { - target_.RecheckFlags(loader.GetLoadedPreprocessorFlags(), - target_.GetPreprocessorFlags()); - target_.RecheckFlags(loader.GetLoadedCommonCompileFlags(), - target_.GetCommonCompileFlags()); - target_.RecheckFlags(loader.GetLoadedPchObjectFlags(), - target_.GetPchObjectFlags()); - target_.RecheckFlags(loader.GetLoadedAsmCompileFlags(), - target_.GetAsmCompileFlags()); - target_.RecheckFlags(loader.GetLoadedCCompileFlags(), - target_.GetCCompileFlags()); - target_.RecheckFlags(loader.GetLoadedCppCompileFlags(), - target_.GetCppCompileFlags()); - target_.RecheckDirs(loader.GetLoadedIncludeDirs(), - target_.GetIncludeDirs()); - target_.RecheckPaths(loader.GetLoadedHeaders(), - storer.current_header_files.internal); - target_.RecheckPaths(loader.GetLoadedCompileDependencies(), - storer.current_compile_dependencies.internal); + if (target_.dirty_) { + } else if (!(load_target_schema.preprocessor_flags == + user_target_schema.preprocessor_flags) || + !(load_target_schema.common_compile_flags == + user_target_schema.common_compile_flags) || + !(load_target_schema.pch_object_flags == + user_target_schema.pch_object_flags) || + !(load_target_schema.asm_compile_flags == + user_target_schema.asm_compile_flags) || + !(load_target_schema.c_compile_flags == + user_target_schema.c_compile_flags) || + !(load_target_schema.cpp_compile_flags == + user_target_schema.cpp_compile_flags)) { + target_.dirty_ = true; + target_.FlagChanged(); + } else if (!(load_target_schema.include_dirs == + user_target_schema.include_dirs)) { + target_.dirty_ = true; + target_.DirChanged(); + } else if (!(load_target_schema.headers == user_target_schema.headers)) { + target_.dirty_ = true; + target_.PathChanged(); + } else if (!(load_target_schema.compile_dependencies == + user_target_schema.compile_dependencies)) { + target_.dirty_ = true; + target_.PathChanged(); + } } if (target_.dirty_) { @@ -199,71 +212,58 @@ void CompileObject::BuildObjectCompile( } void CompileObject::PreObjectCompile() { - auto &storer = target_.storer_; + auto &target_user_schema = target_.user_; // Convert user_source_files to current_source_files - storer.current_source_files.Convert(); + target_user_schema.sources.ComputeHashForAll(); // Convert user_header_files to current_header_files - storer.current_header_files.Convert(); + target_user_schema.headers.ComputeHashForAll(); // Convert user_compile_dependencies to current_compile_dependencies - storer.current_compile_dependencies.Convert(); + target_user_schema.compile_dependencies.ComputeHashForAll(); } -void CompileObject::CompileSources(std::vector &source_files) { - const auto &storer = target_.storer_; - source_files = - std::vector(storer.current_source_files.internal.begin(), - storer.current_source_files.internal.end()); +void CompileObject::CompileSources( + std::vector &source_files) { + const auto &target_user_schema = target_.user_; + target_user_schema.sources.GetPathInfos(); + source_files = target_user_schema.sources.GetPathInfos(); } void CompileObject::RecompileSources( - std::vector &source_files, - std::vector &dummy_source_files) { - const auto &loader = target_.loader_; - const auto &storer = target_.storer_; - - const auto &previous_source_files = loader.GetLoadedSources(); - - // * Cannot find previous source in current source files - const bool is_source_removed = - std::any_of(previous_source_files.begin(), previous_source_files.end(), - [&](const internal::Path &p) { - return storer.current_source_files.internal.find(p) == - storer.current_source_files.internal.end(); - }); - - if (is_source_removed) { - target_.dirty_ = true; - target_.SourceRemoved(); - } - - for (const auto ¤t_file : storer.current_source_files.internal) { - // const auto ¤t_source = current_file.GetPathname(); - - // Find current_file in the loaded sources - auto iter = previous_source_files.find(current_file); - - if (iter == previous_source_files.end()) { - // *1 New source file added to build - source_files.push_back(current_file); + std::vector &source_files, + std::vector &dummy_source_files) { + const auto &serialization = target_.serialization_; + const auto &user_target_schema = target_.user_; + auto previous_source_files = + serialization.GetLoad().sources.GetUnorderedPathInfos(); + + for (const auto ¤t_path_info : + user_target_schema.sources.GetPathInfos()) { + const auto ¤t_path = current_path_info.path; + if (previous_source_files.count(current_path) == 0) { + // Added + source_files.push_back(current_path_info); target_.dirty_ = true; target_.SourceAdded(); } else { - // *2 Current file is updated - if (current_file.GetLastWriteTimestamp() > - iter->GetLastWriteTimestamp()) { - source_files.push_back(current_file); + if (!(previous_source_files.at(current_path) == current_path_info.hash)) { + // Updated + source_files.push_back(current_path_info); target_.dirty_ = true; target_.SourceUpdated(); } else { - // ELSE - // *3 Do nothing - dummy_source_files.push_back(current_file); + dummy_source_files.push_back(current_path_info); } + previous_source_files.erase(current_path); } } + + if (!previous_source_files.empty()) { + target_.dirty_ = true; + target_.SourceRemoved(); + } } } // namespace buildcc::internal diff --git a/buildcc/lib/target/src/target/friend/compile_pch.cpp b/buildcc/lib/target/src/target/friend/compile_pch.cpp index 2d4057ba..be3b5141 100644 --- a/buildcc/lib/target/src/target/friend/compile_pch.cpp +++ b/buildcc/lib/target/src/target/friend/compile_pch.cpp @@ -16,7 +16,7 @@ #include "target/friend/compile_pch.h" -#include "target/common/path.h" +#include "schema/path.h" #include "target/target.h" #include "env/util.h" @@ -40,7 +40,7 @@ constexpr const char *const kFormat = R"(// Generated by BuildCC )"; void AggregateToFile(const fs::path &filename, - const buildcc::fs_unordered_set &header_files) { + const std::vector &header_files) { std::string aggregated_includes; for (const auto &hf : header_files) { std::string temp = fmt::format("#include \"{}\"\r\n", hf); @@ -54,7 +54,7 @@ void AggregateToFile(const fs::path &filename, }); bool success = buildcc::env::save_file( buildcc::path_as_string(filename).c_str(), constructed_output, false); - buildcc::env::assert_throw(success, "Could not save pch file"); + buildcc::env::assert_fatal(success, "Could not save pch file"); } } // namespace @@ -64,7 +64,7 @@ namespace buildcc::internal { // PUBLIC void CompilePch::CacheCompileCommand() { - source_path_ = ConstructSourcePath(target_.GetState().contains_cpp); + source_path_ = ConstructSourcePath(target_.GetState().ContainsCpp()); command_ = ConstructCompileCommand(); } @@ -73,29 +73,36 @@ void CompilePch::CacheCompileCommand() { void CompilePch::BuildCompile() { PreCompile(); - const auto &loader = target_.loader_; + const auto &serialization = target_.serialization_; + const auto &load_target_schema = serialization.GetLoad(); + const auto &user_target_schema = target_.user_; - if (!loader.IsLoaded()) { + if (!serialization.IsLoaded()) { target_.dirty_ = true; } else { - target_.RecheckFlags(loader.GetLoadedPreprocessorFlags(), - target_.GetPreprocessorFlags()); - target_.RecheckFlags(loader.GetLoadedCommonCompileFlags(), - target_.GetCommonCompileFlags()); - target_.RecheckFlags(loader.GetLoadedCCompileFlags(), - target_.GetCCompileFlags()); - target_.RecheckFlags(loader.GetLoadedCppCompileFlags(), - target_.GetCppCompileFlags()); - target_.RecheckDirs(loader.GetLoadedIncludeDirs(), - target_.GetIncludeDirs()); - target_.RecheckPaths(loader.GetLoadedHeaders(), - target_.storer_.current_header_files.internal); - - target_.RecheckFlags(loader.GetLoadedPchCompileFlags(), - target_.GetPchCompileFlags()); - target_.RecheckPaths(loader.GetLoadedPchs(), - target_.storer_.current_pch_files.internal); - if (!loader.GetLoadedPchCompiled()) { + if (!(load_target_schema.preprocessor_flags == + user_target_schema.preprocessor_flags) || + !(load_target_schema.common_compile_flags == + user_target_schema.common_compile_flags) || + !(load_target_schema.pch_compile_flags == + user_target_schema.pch_compile_flags) || + !(load_target_schema.c_compile_flags == + user_target_schema.c_compile_flags) || + !(load_target_schema.cpp_compile_flags == + user_target_schema.cpp_compile_flags)) { + target_.dirty_ = true; + target_.FlagChanged(); + } else if (!(load_target_schema.include_dirs == + user_target_schema.include_dirs)) { + target_.dirty_ = true; + target_.DirChanged(); + } else if (!(load_target_schema.headers == user_target_schema.headers) || + !(load_target_schema.pchs == user_target_schema.pchs)) { + target_.dirty_ = true; + target_.PathChanged(); + } else if (!load_target_schema.pch_compiled) { + // TODO, Replace this with fs::exists to check if compiled pch file is + // present or no target_.dirty_ = true; } } @@ -106,23 +113,23 @@ void CompilePch::BuildCompile() { const std::string p = fmt::format("{}", source_path_); const bool save = env::save_file(p.c_str(), {"//Generated by BuildCC"}, false); - env::assert_throw(save, fmt::format("Could not save {}", p)); + env::assert_fatal(save, fmt::format("Could not save {}", p)); } bool success = env::Command::Execute(command_); - env::assert_throw(success, "Failed to compile pch"); - target_.storer_.pch_compiled = true; + env::assert_fatal(success, "Failed to compile pch"); } } fs::path CompilePch::ConstructHeaderPath() const { return target_.GetTargetBuildDir() / - fmt::format("buildcc_pch{}", target_.GetConfig().pch_header_ext); + fmt::format("buildcc_pch{}", + target_.toolchain_.GetConfig().pch_header_ext); } fs::path CompilePch::ConstructCompilePath() const { return ConstructHeaderPath().replace_extension( - fmt::format("{}{}", target_.GetConfig().pch_header_ext, - target_.GetConfig().pch_compile_ext)); + fmt::format("{}{}", target_.toolchain_.GetConfig().pch_header_ext, + target_.toolchain_.GetConfig().pch_compile_ext)); } fs::path CompilePch::ConstructSourcePath(bool has_cpp) const { @@ -132,22 +139,22 @@ fs::path CompilePch::ConstructSourcePath(bool has_cpp) const { fs::path CompilePch::ConstructObjectPath() const { return ConstructHeaderPath().replace_extension( - fmt::format("{}", target_.GetConfig().obj_ext)); + fmt::format("{}", target_.toolchain_.GetConfig().obj_ext)); } std::string CompilePch::ConstructCompileCommand() const { - std::string compiler = target_.GetState().contains_cpp + std::string compiler = target_.GetState().ContainsCpp() ? target_.GetToolchain().GetCppCompiler() : target_.GetToolchain().GetCCompiler(); compiler = fmt::format("{}", fs::path(compiler)); - const TargetFileExt file_ext_type = - target_.GetState().contains_cpp ? TargetFileExt::Cpp : TargetFileExt::C; + const FileExt file_ext_type = + target_.GetState().ContainsCpp() ? FileExt::Cpp : FileExt::C; const std::string compile_flags = target_.SelectCompileFlags(file_ext_type).value_or(""); const std::string pch_compile_path = fmt::format("{}", compile_path_); const std::string pch_header_path = fmt::format("{}", header_path_); const std::string pch_source_path = fmt::format("{}", source_path_); - return target_.command_.Construct(target_.config_.pch_command, + return target_.command_.Construct(target_.GetConfig().pch_command, { {kCompiler, compiler}, {kCompileFlags, compile_flags}, @@ -158,9 +165,11 @@ std::string CompilePch::ConstructCompileCommand() const { } void CompilePch::PreCompile() { - target_.storer_.current_header_files.Convert(); + auto &target_user_schema = target_.user_; + + target_user_schema.headers.ComputeHashForAll(); - target_.storer_.current_pch_files.Convert(); + target_user_schema.pchs.ComputeHashForAll(); } } // namespace buildcc::internal diff --git a/buildcc/lib/target/src/target/friend/link_target.cpp b/buildcc/lib/target/src/target/friend/link_target.cpp index 42b4208d..fb5219ab 100644 --- a/buildcc/lib/target/src/target/friend/link_target.cpp +++ b/buildcc/lib/target/src/target/friend/link_target.cpp @@ -34,18 +34,16 @@ void LinkTarget::CacheLinkCommand() { internal::aggregate(target_.compile_object_.GetCompiledSources()); const std::string output_target = fmt::format("{}", output_); - - const auto &storer = target_.storer_; + const auto &target_user_schema = target_.user_; command_ = target_.command_.Construct( - target_.config_.link_command, + target_.GetConfig().link_command, { {kOutput, output_target}, {kCompiledSources, aggregated_compiled_sources}, - // NOTE, This needs to be ORDERED {kLibDeps, - fmt::format( - "{} {}", internal::aggregate(storer.current_user_lib_deps), - internal::aggregate(storer.current_user_external_lib_deps))}, + fmt::format("{} {}", + internal::aggregate(target_user_schema.libs.GetPaths()), + internal::aggregate(target_user_schema.external_libs))}, }); } @@ -60,48 +58,52 @@ fs::path LinkTarget::ConstructOutputPath() const { } void LinkTarget::PreLink() { - auto &storer = target_.storer_; - - for (const auto &p : storer.current_user_lib_deps) { - storer.current_internal_lib_deps.emplace( - internal::Path::CreateExistingPath(p)); - } + auto &target_user_schema = target_.user_; - storer.current_internal_external_lib_deps.insert( - storer.current_user_external_lib_deps.begin(), - storer.current_user_external_lib_deps.end()); + target_user_schema.libs.ComputeHashForAll(); - storer.current_link_dependencies.Convert(); + target_user_schema.link_dependencies.ComputeHashForAll(); } void LinkTarget::BuildLink() { PreLink(); - const auto &loader = target_.loader_; - const auto &storer = target_.storer_; + const auto &serialization = target_.serialization_; + const auto &target_load_schema = serialization.GetLoad(); + const auto &target_user_schema = target_.user_; - if (!loader.IsLoaded()) { + if (!serialization.IsLoaded()) { target_.dirty_ = true; } else { - target_.RecheckFlags(loader.GetLoadedLinkFlags(), target_.GetLinkFlags()); - target_.RecheckDirs(loader.GetLoadedLibDirs(), target_.GetLibDirs()); - target_.RecheckExternalLib(loader.GetLoadedExternalLibDeps(), - storer.current_internal_external_lib_deps); - target_.RecheckPaths(loader.GetLoadedLinkDependencies(), - storer.current_link_dependencies.internal); - - // NOTE, This needs to be UNORDERED - target_.RecheckPaths(loader.GetLoadedLibDeps(), - storer.current_internal_lib_deps); - if (!loader.GetLoadedTargetLinked()) { + if (target_.dirty_) { + // Skip all the other else if checks + } else if (!(target_load_schema.link_flags == + target_user_schema.link_flags)) { + target_.dirty_ = true; + target_.FlagChanged(); + } else if (!(target_load_schema.lib_dirs == target_user_schema.lib_dirs)) { + target_.dirty_ = true; + target_.DirChanged(); + } else if (!(target_load_schema.external_libs == + target_user_schema.external_libs)) { + target_.dirty_ = true; + target_.ExternalLibChanged(); + } else if (!(target_load_schema.link_dependencies == + target_user_schema.link_dependencies) || + !(target_load_schema.libs == target_user_schema.libs)) { + target_.dirty_ = true; + target_.PathChanged(); + } else if (!target_load_schema.target_linked) { + // TODO, Replace this with fs::exists to check if linked target is present + // or no target_.dirty_ = true; } } if (target_.dirty_) { bool success = env::Command::Execute(command_); - env::assert_throw(success, "Failed to link target"); - target_.storer_.target_linked = true; + env::assert_fatal(success, "Failed to link target"); + target_.serialization_.UpdateTargetCompiled(); } } diff --git a/buildcc/lib/target/src/target/recheck_states.cpp b/buildcc/lib/target/src/target/recheck_states.cpp index 42bf53c0..4285fe0f 100644 --- a/buildcc/lib/target/src/target/recheck_states.cpp +++ b/buildcc/lib/target/src/target/recheck_states.cpp @@ -30,6 +30,7 @@ void Target::PathRemoved() {} void Target::PathAdded() {} void Target::PathUpdated() {} +void Target::PathChanged() {} void Target::DirChanged() {} void Target::FlagChanged() {} void Target::ExternalLibChanged() {} diff --git a/buildcc/lib/target/src/target/target.cpp b/buildcc/lib/target/src/target/target.cpp index fe069f31..48481daf 100644 --- a/buildcc/lib/target/src/target/target.cpp +++ b/buildcc/lib/target/src/target/target.cpp @@ -49,55 +49,27 @@ namespace buildcc { void Target::Initialize() { // Checks env::assert_fatal( - env::is_init(), - "Environment is not initialized. Use the buildcc::env::init API"); + Project::IsInit(), + "Environment is not initialized. Use the buildcc::Project::Init API"); env::assert_fatal(IsValidTargetType(type_), "Invalid Target Type"); fs::create_directories(GetTargetBuildDir()); // String updates unique_id_ = fmt::format("[{}] {}", toolchain_.GetName(), name_); std::string path = fmt::format( - "{}", GetTargetPath().lexically_relative(env::get_project_build_dir())); + "{}", GetTargetPath().lexically_relative(Project::GetBuildDir())); tf_.name(path); } -// Rechecks -void Target::RecheckPaths(const internal::path_unordered_set &previous_path, - const internal::path_unordered_set ¤t_path) { - BuilderInterface::RecheckPaths( - previous_path, current_path, [&]() { PathRemoved(); }, - [&]() { PathAdded(); }, [&]() { PathUpdated(); }); -} - -void Target::RecheckDirs(const fs_unordered_set &previous_dirs, - const fs_unordered_set ¤t_dirs) { - RecheckChanged(previous_dirs, current_dirs, - std::bind(&Target::DirChanged, this)); -} - -void Target::RecheckFlags( - const std::unordered_set &previous_flags, - const std::unordered_set ¤t_flags) { - RecheckChanged(previous_flags, current_flags, - std::bind(&Target::FlagChanged, this)); -} - -void Target::RecheckExternalLib( - const std::unordered_set &previous_external_libs, - const std::unordered_set ¤t_external_libs) { - RecheckChanged(previous_external_libs, current_external_libs, - std::bind(&Target::ExternalLibChanged, this)); -} - -std::optional Target::SelectCompileFlags(TargetFileExt ext) const { +env::optional Target::SelectCompileFlags(FileExt ext) const { switch (ext) { - case TargetFileExt::Asm: + case FileExt::Asm: return internal::aggregate(GetAsmCompileFlags()); break; - case TargetFileExt::C: + case FileExt::C: return internal::aggregate(GetCCompileFlags()); break; - case TargetFileExt::Cpp: + case FileExt::Cpp: return internal::aggregate(GetCppCompileFlags()); break; default: @@ -106,15 +78,15 @@ std::optional Target::SelectCompileFlags(TargetFileExt ext) const { return {}; } -std::optional Target::SelectCompiler(TargetFileExt ext) const { +env::optional Target::SelectCompiler(FileExt ext) const { switch (ext) { - case TargetFileExt::Asm: - return GetToolchain().GetAsmCompiler(); + case FileExt::Asm: + return GetToolchain().GetAssembler(); break; - case TargetFileExt::C: + case FileExt::C: return GetToolchain().GetCCompiler(); break; - case TargetFileExt::Cpp: + case FileExt::Cpp: return GetToolchain().GetCppCompiler(); break; default: diff --git a/buildcc/lib/target/src/target/target_loader.cpp b/buildcc/lib/target/src/target/target_loader.cpp deleted file mode 100644 index 35d40d13..00000000 --- a/buildcc/lib/target/src/target/target_loader.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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. - */ - -#include "target/base/target_loader.h" - -#include "env/logging.h" -#include "env/util.h" - -// Private -#include "target/private/schema_util.h" -#include "target_generated.h" - -namespace buildcc::internal { - -// Public functions -bool TargetLoader::Load() { - env::log_trace(name_, __FUNCTION__); - - auto file_path = GetBinaryPath(); - std::string buffer; - bool is_loaded = - env::load_file(path_as_string(file_path).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::VerifyTargetBuffer(verifier); - if (!is_verified) { - return false; - } - - const auto *target = fbs::GetTarget((const void *)buffer.c_str()); - // target->name()->c_str(); - // target->type(); - - extract_path(target->source_files(), loaded_sources_); - extract_path(target->header_files(), loaded_headers_); - extract_path(target->pch_files(), loaded_pchs_); - extract_path(target->lib_deps(), loaded_lib_deps_); - - extract(target->external_lib_deps(), loaded_external_lib_dirs_); - - extract(target->include_dirs(), loaded_include_dirs_); - extract(target->lib_dirs(), loaded_lib_dirs_); - - extract(target->preprocessor_flags(), loaded_preprocessor_flags_); - extract(target->common_compile_flags(), loaded_common_compile_flags_); - extract(target->pch_compile_flags(), loaded_pch_compile_flags_); - extract(target->pch_object_flags(), loaded_pch_object_flags_); - extract(target->asm_compile_flags(), loaded_asm_compile_flags_); - extract(target->c_compile_flags(), loaded_c_compile_flags_); - extract(target->cpp_compile_flags(), loaded_cpp_compile_flags_); - extract(target->link_flags(), loaded_link_flags_); - - extract_path(target->compile_dependencies(), loaded_compile_dependencies_); - extract_path(target->link_dependencies(), loaded_link_dependencies_); - - loaded_pch_compiled_ = target->pch_compiled(); - loaded_target_linked_ = target->target_linked(); - - loaded_ = true; - return true; -} - -// Private functions -void TargetLoader::Initialize() {} - -} // namespace buildcc::internal diff --git a/buildcc/lib/target/src/target/target_storer.cpp b/buildcc/lib/target/src/target/target_storer.cpp deleted file mode 100644 index 65793748..00000000 --- a/buildcc/lib/target/src/target/target_storer.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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. - */ - -#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::TargetType type) { - return (fbs::TargetType)type; -} - -} // namespace - -namespace buildcc { - -bool Target::Store() { - env::log_trace(name_, __FUNCTION__); - - flatbuffers::FlatBufferBuilder builder; - - auto fbs_target_type = CreateFbsTargetType(type_); - - auto fbs_source_files = internal::create_fbs_vector_path( - builder, storer_.current_source_files.internal); - auto fbs_header_files = internal::create_fbs_vector_path( - builder, storer_.current_header_files.internal); - auto fbs_pch_files = internal::create_fbs_vector_path( - builder, storer_.current_pch_files.internal); - // NOTE, This can be UNORDERED - auto fbs_lib_deps = internal::create_fbs_vector_path( - builder, storer_.current_internal_lib_deps); - - // NOTE, This can be UNORDERED - auto fbs_external_lib_deps = internal::create_fbs_vector_string( - builder, storer_.current_internal_external_lib_deps); - - auto fbs_include_dirs = - internal::create_fbs_vector_string(builder, storer_.current_include_dirs); - auto fbs_lib_dirs = - internal::create_fbs_vector_string(builder, storer_.current_lib_dirs); - - auto fbs_preprocessor_flags = internal::create_fbs_vector_string( - builder, storer_.current_preprocessor_flags); - auto fbs_common_compile_flags = internal::create_fbs_vector_string( - builder, storer_.current_common_compile_flags); - auto fbs_pch_compile_flags = internal::create_fbs_vector_string( - builder, storer_.current_pch_compile_flags); - auto fbs_pch_object_flags = internal::create_fbs_vector_string( - builder, storer_.current_pch_object_flags); - auto fbs_asm_compile_flags = internal::create_fbs_vector_string( - builder, storer_.current_asm_compile_flags); - auto fbs_c_compile_flags = internal::create_fbs_vector_string( - builder, storer_.current_c_compile_flags); - auto fbs_cpp_compile_flags = internal::create_fbs_vector_string( - builder, storer_.current_cpp_compile_flags); - auto fbs_link_flags = - internal::create_fbs_vector_string(builder, storer_.current_link_flags); - - auto fbs_compile_dependencies = internal::create_fbs_vector_path( - builder, storer_.current_compile_dependencies.internal); - auto fbs_link_dependencies = internal::create_fbs_vector_path( - builder, storer_.current_link_dependencies.internal); - - auto fbs_target = fbs::CreateTargetDirect( - builder, name_.c_str(), fbs_target_type, &fbs_source_files, - &fbs_header_files, &fbs_pch_files, &fbs_lib_deps, &fbs_external_lib_deps, - &fbs_include_dirs, &fbs_lib_dirs, &fbs_preprocessor_flags, - &fbs_common_compile_flags, &fbs_pch_compile_flags, &fbs_pch_object_flags, - &fbs_asm_compile_flags, &fbs_c_compile_flags, &fbs_cpp_compile_flags, - &fbs_link_flags, &fbs_compile_dependencies, &fbs_link_dependencies, - storer_.pch_compiled, storer_.target_linked); - fbs::FinishTargetBuffer(builder, fbs_target); - - auto file_path = GetBinaryPath(); - return env::save_file(path_as_string(file_path).c_str(), - (const char *)builder.GetBufferPointer(), - builder.GetSize(), true); -} - -} // namespace buildcc diff --git a/buildcc/lib/target/src/target/tasks.cpp b/buildcc/lib/target/src/target/tasks.cpp index 772d31e9..ef9f3e04 100644 --- a/buildcc/lib/target/src/target/tasks.cpp +++ b/buildcc/lib/target/src/target/tasks.cpp @@ -36,39 +36,7 @@ constexpr const char *const kLinkTaskName = "Target"; } // namespace -namespace buildcc { - -void Target::SetTaskStateFailure() { - std::lock_guard guard(task_state_mutex_); - task_state_ = env::TaskState::FAILURE; -} - -tf::Task Target::CheckStateTask() { - // NOTE, For now we only have 2 states - // 0 -> SUCCESS - // 1 -> FAILURE - // * When more states are added make sure to handle them explicitly - return tf_.emplace([&]() { return GetTaskStateAsInt(); }) - .name(kCheckTaskName); -} - -void Target::StartTask() { - // Return 0 for success - // Return 1 for failure - target_start_task_ = tf_.emplace([&]() { - switch (env::get_task_state()) { - case env::TaskState::SUCCESS: - break; - default: - SetTaskStateFailure(); - break; - }; - return GetTaskStateAsInt(); - }); - target_start_task_.name(kStartTaskName); -} - -} // namespace buildcc +namespace buildcc {} // namespace buildcc namespace buildcc::internal { @@ -78,16 +46,21 @@ namespace buildcc::internal { // 3. Successfully compiled sources are added to `compiled_pch_files_` void CompilePch::Task() { task_ = target_.tf_.emplace([&](tf::Subflow &subflow) { + if (env::get_task_state() != env::TaskState::SUCCESS) { + return; + } + try { BuildCompile(); + target_.serialization_.UpdatePchCompiled(target_.user_); } catch (...) { - target_.SetTaskStateFailure(); + env::set_task_state(env::TaskState::FAILURE); } // For Graph generation for (const auto &p : target_.GetPchFiles()) { - std::string name = - fmt::format("{}", p.lexically_relative(env::get_project_root_dir())); + std::string name = fmt::format( + "{}", fs::path(p).lexically_relative(Project::GetRootDir())); subflow.placeholder().name(name); } }); @@ -106,49 +79,47 @@ void CompilePch::Task() { // serialization schema void CompileObject::Task() { compile_task_ = target_.tf_.emplace([&](tf::Subflow &subflow) { - std::vector selected_source_files; - std::vector selected_dummy_source_files; + if (env::get_task_state() != env::TaskState::SUCCESS) { + return; + } + + std::vector selected_source_files; + std::vector selected_dummy_source_files; try { BuildObjectCompile(selected_source_files, selected_dummy_source_files); - target_.compiled_source_files_.clear(); - target_.compiled_source_files_.insert(selected_dummy_source_files.begin(), - selected_dummy_source_files.end()); + for (const auto &path_info : selected_dummy_source_files) { + target_.serialization_.AddSource(path_info.path, path_info.hash); + } - for (const auto &s : selected_source_files) { - std::string name = fmt::format("{}", s.GetPathname().lexically_relative( - env::get_project_root_dir())); + for (const auto &path_info : selected_source_files) { + std::string name = fmt::format( + "{}", + fs::path(path_info.path).lexically_relative(Project::GetRootDir())); (void)subflow - .emplace([this, s]() { + .emplace([this, path_info]() { try { bool success = env::Command::Execute( - GetObjectData(s.GetPathname()).command); - env::assert_throw(success, "Could not compile source"); - - // NOTE, If conmpilation is successful we update the source - // files - std::lock_guard guard( - target_.compiled_source_files_mutex_); - target_.compiled_source_files_.insert(s); + GetObjectData(path_info.path).command); + env::assert_fatal(success, "Could not compile source"); + target_.serialization_.AddSource(path_info.path, + path_info.hash); } catch (...) { - target_.SetTaskStateFailure(); - - // NOTE, If compilation fails, we do not need to update the - // source files + env::set_task_state(env::TaskState::FAILURE); } }) .name(name); } // For graph generation - for (const auto &ds : selected_dummy_source_files) { - std::string name = fmt::format( - "{}", - ds.GetPathname().lexically_relative(env::get_project_root_dir())); + for (const auto &dummy_path_info : selected_dummy_source_files) { + std::string name = + fmt::format("{}", fs::path(dummy_path_info.path) + .lexically_relative(Project::GetRootDir())); (void)subflow.placeholder().name(name); } } catch (...) { - target_.SetTaskStateFailure(); + env::set_task_state(env::TaskState::FAILURE); } }); compile_task_.name(kCompileTaskName); @@ -160,10 +131,13 @@ void CompileObject::Task() { // 3. Successfully linking the target sets link state void LinkTarget::Task() { task_ = target_.tf_.emplace([&]() { + if (env::get_task_state() != env::TaskState::SUCCESS) { + return; + } try { BuildLink(); } catch (...) { - target_.SetTaskStateFailure(); + env::set_task_state(env::TaskState::FAILURE); } }); task_.name(kLinkTaskName); @@ -177,36 +151,23 @@ void Target::EndTask() { target_end_task_ = tf_.emplace([&]() { if (dirty_) { try { - storer_.current_source_files.internal = compiled_source_files_; - env::assert_throw(Store(), + serialization_.UpdateStore(user_); + env::assert_fatal(serialization_.StoreToFile(), fmt::format("Store failed for {}", GetName())); - state_.build = true; + state_.BuildCompleted(); } catch (...) { - SetTaskStateFailure(); + env::set_task_state(env::TaskState::FAILURE); } } - - // Update env task state - if (task_state_ != env::TaskState::SUCCESS) { - env::set_task_state(GetTaskState()); - } }); target_end_task_.name(kEndTaskName); } void Target::TaskDeps() { if (state_.ContainsPch()) { - target_start_task_.precede(compile_pch_.GetTask(), target_end_task_); - tf::Task pch_check_state_task = CheckStateTask(); - compile_pch_.GetTask().precede(pch_check_state_task); - pch_check_state_task.precede(compile_object_.GetTask(), target_end_task_); - } else { - target_start_task_.precede(compile_object_.GetTask(), target_end_task_); + compile_pch_.GetTask().precede(compile_object_.GetTask()); } - - tf::Task object_check_state_task = CheckStateTask(); - compile_object_.GetTask().precede(object_check_state_task); - object_check_state_task.precede(link_target_.GetTask(), target_end_task_); + compile_object_.GetTask().precede(link_target_.GetTask()); link_target_.GetTask().precede(target_end_task_); } diff --git a/buildcc/lib/target/src/target_info/target_info.cpp b/buildcc/lib/target/src/target_info/target_info.cpp new file mode 100644 index 00000000..2fc83cce --- /dev/null +++ b/buildcc/lib/target/src/target_info/target_info.cpp @@ -0,0 +1,50 @@ +/* + * 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. + */ + +#include "target/target_info.h" + +namespace buildcc { + +// PRIVATE + +void TargetInfo::Initialize() { + std::for_each(toolchain_.GetPreprocessorFlags().begin(), + toolchain_.GetPreprocessorFlags().end(), + [&](const std::string &flag) { AddPreprocessorFlag(flag); }); + std::for_each(toolchain_.GetCommonCompileFlags().begin(), + toolchain_.GetCommonCompileFlags().end(), + [&](const std::string &flag) { AddCommonCompileFlag(flag); }); + std::for_each(toolchain_.GetPchCompileFlags().begin(), + toolchain_.GetPchCompileFlags().end(), + [&](const std::string &flag) { AddPchCompileFlag(flag); }); + std::for_each(toolchain_.GetPchObjectFlags().begin(), + toolchain_.GetPchObjectFlags().end(), + [&](const std::string &flag) { AddPchObjectFlag(flag); }); + std::for_each(toolchain_.GetAsmCompileFlags().begin(), + toolchain_.GetAsmCompileFlags().end(), + [&](const std::string &flag) { AddAsmCompileFlag(flag); }); + std::for_each(toolchain_.GetCCompileFlags().begin(), + toolchain_.GetCCompileFlags().end(), + [&](const std::string &flag) { AddCCompileFlag(flag); }); + std::for_each(toolchain_.GetCppCompileFlags().begin(), + toolchain_.GetCppCompileFlags().end(), + [&](const std::string &flag) { AddCppCompileFlag(flag); }); + std::for_each(toolchain_.GetLinkFlags().begin(), + toolchain_.GetLinkFlags().end(), + [&](const std::string &flag) { AddLinkFlag(flag); }); +} + +} // namespace buildcc diff --git a/buildcc/lib/target/test/path/CMakeLists.txt b/buildcc/lib/target/test/path/CMakeLists.txt deleted file mode 100644 index 1b5103ce..00000000 --- a/buildcc/lib/target/test/path/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -configure_file(constants.h.in ${CMAKE_CURRENT_BINARY_DIR}/generated/constants.h @ONLY) - -set(TEST_NAME "test_path") -add_executable( - ${TEST_NAME} - ${TEST_NAME}.cpp -) -target_include_directories(${TEST_NAME} PRIVATE - ${TARGET_DIR}/include - ${CMAKE_CURRENT_BINARY_DIR}/generated -) -target_link_libraries(${TEST_NAME} PRIVATE - mock_env - CppUTest - CppUTestExt - gcov -) -target_compile_options(${TEST_NAME} PRIVATE ${TEST_COMPILE_FLAGS}) -target_link_options(${TEST_NAME} PRIVATE ${TEST_LINK_FLAGS}) - -add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) diff --git a/buildcc/lib/target/test/path/constants.h.in b/buildcc/lib/target/test/path/constants.h.in deleted file mode 100644 index 9da788ce..00000000 --- a/buildcc/lib/target/test/path/constants.h.in +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -// clang-format off -inline constexpr char const *BUILD_SCRIPT_SOURCE = "@CMAKE_CURRENT_SOURCE_DIR@"; diff --git a/buildcc/lib/target/test/path/path_main.cpp b/buildcc/lib/target/test/path/path_main.cpp deleted file mode 100644 index 18a69789..00000000 --- a/buildcc/lib/target/test/path/path_main.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include - -int main() { - std::cout << "Hello World from test" << std::endl; - return 0; -} diff --git a/buildcc/lib/target/test/path/test_path.cpp b/buildcc/lib/target/test/path/test_path.cpp deleted file mode 100644 index dff2ec52..00000000 --- a/buildcc/lib/target/test/path/test_path.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// Internal -#include "target/common/path.h" - -#include "env/assert_fatal.h" - -#include "constants.h" - -#include -#include - -// 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" - -// clang-format off -TEST_GROUP(PathTestGroup) -{ -}; -// clang-format on - -static const auto current_file_path = - (fs::path(BUILD_SCRIPT_SOURCE) / "path_main.cpp").make_preferred(); - -TEST(PathTestGroup, Path_ExistingPathStaticConstructor) { - auto existing_path = - buildcc::internal::Path::CreateExistingPath(current_file_path); - STRCMP_EQUAL(existing_path.GetPathname().string().c_str(), - current_file_path.string().c_str()); - // * NOTE, Last write timestamp changes whenever we resave or re-download - // This would not work well with Git - // UNSIGNED_LONGLONGS_EQUAL(existing_path.GetLastWriteTimestamp(), - // 13623997187709551616ULL); -} - -TEST(PathTestGroup, Path_ExistingPathStaticConstructor_ThrowFileException) { - CHECK_THROWS(std::exception, buildcc::internal::Path::CreateExistingPath( - "random_path_main.cpp")); -} - -TEST(PathTestGroup, PathConstructor_NewPathStaticConstructor) { - buildcc::internal::Path p = - buildcc::internal::Path::CreateNewPath("random_path_main.cpp", 12345ULL); - STRCMP_EQUAL(p.GetPathname().string().c_str(), "random_path_main.cpp"); - UNSIGNED_LONGLONGS_EQUAL(p.GetLastWriteTimestamp(), 12345ULL); -} - -TEST(PathTestGroup, Path_EqualityOperator) { - buildcc::internal::Path p = - buildcc::internal::Path::CreateExistingPath(current_file_path); - STRCMP_EQUAL(p.GetPathname().string().c_str(), - current_file_path.string().c_str()); - - buildcc::internal::Path newp = - buildcc::internal::Path::CreateNewPath(current_file_path, 12345ULL); - - // NOTE, Equality does not match the last_write_timestamp - // ONLY matches the string - CHECK(p == newp); - CHECK(p == current_file_path); - CHECK(p == current_file_path); -} - -TEST(PathTestGroup, Path_UnorderedSet) { - std::unordered_set - unique_paths; - - // Check inserts - CHECK_TRUE(unique_paths - .insert(buildcc::internal::Path::CreateExistingPath( - current_file_path)) - .second); - CHECK_FALSE(unique_paths - .insert(buildcc::internal::Path::CreateNewPath( - current_file_path, 12345ULL)) - .second); - CHECK_TRUE(unique_paths - .insert(buildcc::internal::Path::CreateNewPath( - "random_path_main.cpp", 98765ULL)) - .second); - - // Check finds - // * NOTE, Only matches pathname - CHECK_FALSE(unique_paths.find(buildcc::internal::Path::CreateExistingPath( - current_file_path)) == unique_paths.end()); - - CHECK_FALSE(unique_paths.find(buildcc::internal::Path::CreateNewPath( - current_file_path, 1111ULL)) == unique_paths.end()); - CHECK_FALSE(unique_paths.find(buildcc::internal::Path::CreateNewPath( - "random_path_main.cpp", 12345ULL)) == unique_paths.end()); - CHECK_TRUE(unique_paths.find(buildcc::internal::Path::CreateNewPath( - "incorrect_path_main.cpp", 0000ULL)) == unique_paths.end()); -} - -int main(int ac, char **av) { - return CommandLineTestRunner::RunAllTests(ac, av); -} diff --git a/buildcc/lib/target/test/target/CMakeLists.txt b/buildcc/lib/target/test/target/CMakeLists.txt index 2797dfc6..8e73ca20 100644 --- a/buildcc/lib/target/test/target/CMakeLists.txt +++ b/buildcc/lib/target/test/target/CMakeLists.txt @@ -8,35 +8,56 @@ target_link_libraries(target_interface INTERFACE mock_target ) -# Interfaces -add_executable(test_builder_interface - test_builder_interface.cpp +# Toolchain + +add_executable(test_toolchain_flag_api + test_toolchain_flag_api.cpp ) -target_link_libraries(test_builder_interface PRIVATE target_interface) +target_link_libraries(test_toolchain_flag_api PRIVATE target_interface) -add_test(NAME test_builder_interface COMMAND test_builder_interface) +add_test(NAME test_toolchain_flag_api COMMAND test_toolchain_flag_api) -# Common -add_executable(test_target_config - test_target_config.cpp +# Interfaces +add_executable(test_serialization_interface + test_serialization_interface.cpp ) -target_link_libraries(test_target_config PRIVATE target_interface) +target_link_libraries(test_serialization_interface PRIVATE target_interface) +add_test(NAME test_serialization_interface COMMAND test_serialization_interface + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +) + +# Common add_executable(test_target_state test_target_state.cpp ) target_link_libraries(test_target_state PRIVATE target_interface) -add_test(NAME test_target_config COMMAND test_target_config) add_test(NAME test_target_state COMMAND test_target_state) # Generator -add_executable(test_generator - test_generator.cpp +add_executable(test_custom_generator + test_custom_generator.cpp +) +target_link_libraries(test_custom_generator PRIVATE target_interface) + +add_executable(test_file_generator + test_file_generator.cpp +) +target_link_libraries(test_file_generator PRIVATE target_interface) + +add_executable(test_template_generator + test_template_generator.cpp ) -target_link_libraries(test_generator PRIVATE target_interface) +target_link_libraries(test_template_generator PRIVATE target_interface) -add_test(NAME test_generator COMMAND test_generator +add_test(NAME test_custom_generator COMMAND test_custom_generator + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +) +add_test(NAME test_file_generator COMMAND test_file_generator + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +) +add_test(NAME test_template_generator COMMAND test_template_generator WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) @@ -109,11 +130,6 @@ add_executable(test_target_user_deps ) target_link_libraries(test_target_user_deps PRIVATE target_interface) -add_executable(test_target_lock - test_target_lock.cpp -) -target_link_libraries(test_target_lock PRIVATE target_interface) - add_executable(test_target_sync test_target_sync.cpp ) @@ -135,7 +151,6 @@ add_test(NAME test_target_external_lib COMMAND test_target_external_lib) add_test(NAME test_target_flags COMMAND test_target_flags) add_test(NAME test_target_user_deps COMMAND test_target_user_deps) -add_test(NAME test_target_lock COMMAND test_target_lock) add_test(NAME test_target_sync COMMAND test_target_sync) diff --git a/buildcc/lib/target/test/target/constants.h.in b/buildcc/lib/target/test/target/constants.h.in index 4dbc78de..16c03b9d 100644 --- a/buildcc/lib/target/test/target/constants.h.in +++ b/buildcc/lib/target/test/target/constants.h.in @@ -17,8 +17,6 @@ inline constexpr char const * BUILD_TARGET_FLAG_INTERMEDIATE_DIR = "@CMAKE_CURRE inline constexpr char const * BUILD_TARGET_USER_DEPS_INTERMEDIATE_DIR = "@CMAKE_CURRENT_SOURCE_DIR@/intermediate/target_user_deps"; -inline constexpr char const * BUILD_TARGET_LOCK_INTERMEDIATE_DIR = "@CMAKE_CURRENT_SOURCE_DIR@/intermediate/target_lock"; - inline constexpr char const * BUILD_TARGET_SYNC_INTERMEDIATE_DIR = "@CMAKE_CURRENT_SOURCE_DIR@/intermediate/target_sync"; inline constexpr char const * BUILD_TARGET_FAILURE_STATES_BUILD_DIR = "@CMAKE_CURRENT_SOURCE_DIR@/intermediate/target_failure_states"; diff --git a/buildcc/lib/target/test/target/data/template/default_values.txt.in b/buildcc/lib/target/test/target/data/template/default_values.txt.in new file mode 100644 index 00000000..fa8699fd --- /dev/null +++ b/buildcc/lib/target/test/target/data/template/default_values.txt.in @@ -0,0 +1,4 @@ +{current_root_dir} +{current_build_dir} +{project_root_dir} +{project_build_dir} diff --git a/buildcc/lib/target/test/target/data/template/hello_world.txt.in b/buildcc/lib/target/test/target/data/template/hello_world.txt.in new file mode 100644 index 00000000..cd2ccb82 --- /dev/null +++ b/buildcc/lib/target/test/target/data/template/hello_world.txt.in @@ -0,0 +1 @@ +{hello} {world} diff --git a/buildcc/lib/target/test/target/test_base_target.cpp b/buildcc/lib/target/test/target/test_base_target.cpp index 582f1a8e..a00c5a24 100644 --- a/buildcc/lib/target/test/target/test_base_target.cpp +++ b/buildcc/lib/target/test/target/test_base_target.cpp @@ -21,13 +21,15 @@ TEST_GROUP(TargetBaseTestGroup) } }; // clang-format on -static const buildcc::Toolchain gcc(buildcc::Toolchain::Id::Gcc, "gcc", "as", - "gcc", "g++", "ar", "ld"); +static buildcc::Toolchain gcc(buildcc::ToolchainId::Gcc, "gcc", + buildcc::ToolchainExecutables("as", "gcc", "g++", + "ar", "ld")); TEST(TargetBaseTestGroup, InvalidTargetType) { constexpr const char *const INVALID_NAME = "Invalid.random"; - buildcc::env::init(BUILD_SCRIPT_SOURCE, BUILD_TARGET_BASE_INTERMEDIATE_DIR); + buildcc::Project::Init(BUILD_SCRIPT_SOURCE, + BUILD_TARGET_BASE_INTERMEDIATE_DIR); auto intermediate_path = fs::path(BUILD_TARGET_BASE_INTERMEDIATE_DIR) / gcc.GetName(); @@ -37,7 +39,7 @@ TEST(TargetBaseTestGroup, InvalidTargetType) { std::exception, buildcc::BaseTarget(INVALID_NAME, (buildcc::TargetType)3, gcc, "")); - buildcc::env::deinit(); + buildcc::Project::Deinit(); } TEST(TargetBaseTestGroup, NoEnvInit) { @@ -49,9 +51,10 @@ TEST(TargetBaseTestGroup, NoEnvInit) { } TEST(TargetBaseTestGroup, TargetConfig_BadCompileCommand) { - buildcc::env::init(BUILD_SCRIPT_SOURCE, BUILD_TARGET_BASE_INTERMEDIATE_DIR); + buildcc::Project::Init(BUILD_SCRIPT_SOURCE, + BUILD_TARGET_BASE_INTERMEDIATE_DIR); fs::path target_source_intermediate_path = - buildcc::env::get_project_build_dir() / gcc.GetName(); + buildcc::Project::GetBuildDir() / gcc.GetName(); constexpr const char *const NAME = "BadCompileCommand.exe"; auto intermediate_path = target_source_intermediate_path / NAME; @@ -68,13 +71,14 @@ TEST(TargetBaseTestGroup, TargetConfig_BadCompileCommand) { CHECK_THROWS(std::exception, simple.Build()); } - buildcc::env::deinit(); + buildcc::Project::Deinit(); } TEST(TargetBaseTestGroup, TargetConfig_BadLinkCommand) { - buildcc::env::init(BUILD_SCRIPT_SOURCE, BUILD_TARGET_BASE_INTERMEDIATE_DIR); + buildcc::Project::Init(BUILD_SCRIPT_SOURCE, + BUILD_TARGET_BASE_INTERMEDIATE_DIR); fs::path target_source_intermediate_path = - buildcc::env::get_project_build_dir() / gcc.GetName(); + buildcc::Project::GetBuildDir() / gcc.GetName(); constexpr const char *const NAME = "BadCompileCommand.exe"; auto intermediate_path = target_source_intermediate_path / NAME; @@ -90,7 +94,7 @@ TEST(TargetBaseTestGroup, TargetConfig_BadLinkCommand) { CHECK_THROWS(std::exception, simple.Build()); } - buildcc::env::deinit(); + buildcc::Project::Deinit(); mock().checkExpectations(); } diff --git a/buildcc/lib/target/test/target/test_builder_interface.cpp b/buildcc/lib/target/test/target/test_builder_interface.cpp deleted file mode 100644 index 8c832b36..00000000 --- a/buildcc/lib/target/test/target/test_builder_interface.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include "target/interface/builder_interface.h" - -#include "target/common/path.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" - -// TestBuilderInterface - -class TestBuilderInterface : public buildcc::internal::BuilderInterface { -public: - void Build() override {} - - void RecheckChangedIncorrectCb() { - BuilderInterface::RecheckChanged(previous_, current_, - std::function()); - } - void RecheckPathIncorrectRemoveCb() { - BuilderInterface::RecheckPaths(previous_, current_, - std::function()); - } - void RecheckPathIncorrectAddCb() { - BuilderInterface::RecheckPaths( - previous_, current_, []() {}, std::function()); - } - void RecheckPathIncorrectUpdateCb() { - BuilderInterface::RecheckPaths( - previous_, current_, []() {}, []() {}, std::function()); - } - -private: - bool Store() override { return false; } - buildcc::internal::path_unordered_set previous_; - buildcc::internal::path_unordered_set current_; -}; - -// clang-format off -TEST_GROUP(TestBuilderInterfaceGroup) -{ -}; -// clang-format on - -TEST(TestBuilderInterfaceGroup, IncorrectRecheckChangedCb) { - TestBuilderInterface tbi; - CHECK_THROWS(std::exception, tbi.RecheckChangedIncorrectCb()); -} - -TEST(TestBuilderInterfaceGroup, IncorrectRemoveRecheckCb) { - TestBuilderInterface tbi; - CHECK_THROWS(std::exception, tbi.RecheckPathIncorrectRemoveCb()); -} - -TEST(TestBuilderInterfaceGroup, IncorrectAddRecheckCb) { - TestBuilderInterface tbi; - CHECK_THROWS(std::exception, tbi.RecheckPathIncorrectAddCb()); -} - -TEST(TestBuilderInterfaceGroup, IncorrectUpdateRecheckCb) { - TestBuilderInterface tbi; - CHECK_THROWS(std::exception, tbi.RecheckPathIncorrectUpdateCb()); -} - -int main(int ac, char **av) { - return CommandLineTestRunner::RunAllTests(ac, av); -} diff --git a/buildcc/lib/target/test/target/test_compile_object.cpp b/buildcc/lib/target/test/target/test_compile_object.cpp index 0284b395..d3cdcdff 100644 --- a/buildcc/lib/target/test/target/test_compile_object.cpp +++ b/buildcc/lib/target/test/target/test_compile_object.cpp @@ -15,8 +15,9 @@ TEST_GROUP(TargetCompileObjectTestGroup) }; // clang-format on -static buildcc::Toolchain gcc(buildcc::Toolchain::Id::Gcc, "gcc", "as", "gcc", - "g++", "ar", "ld"); +static buildcc::Toolchain gcc(buildcc::ToolchainId::Gcc, "gcc", + buildcc::ToolchainExecutables("as", "gcc", "g++", + "ar", "ld")); TEST(TargetCompileObjectTestGroup, CacheCompileCommand_Invalid) { buildcc::BaseTarget target("CacheCompileCommand_Invalid", @@ -30,8 +31,9 @@ TEST(TargetCompileObjectTestGroup, CacheCompileCommand_Invalid) { } int main(int ac, char **av) { - buildcc::env::init(fs::current_path(), fs::current_path() / "intermediate" / - "target_compile_object"); - fs::remove_all(buildcc::env::get_project_build_dir()); + buildcc::Project::Init(fs::current_path(), fs::current_path() / + "intermediate" / + "target_compile_object"); + fs::remove_all(buildcc::Project::GetBuildDir()); return CommandLineTestRunner::RunAllTests(ac, av); } diff --git a/buildcc/lib/target/test/target/test_custom_generator.cpp b/buildcc/lib/target/test/target/test_custom_generator.cpp new file mode 100644 index 00000000..38c2c317 --- /dev/null +++ b/buildcc/lib/target/test/target/test_custom_generator.cpp @@ -0,0 +1,490 @@ +#include "target/custom_generator.h" + +#include "expect_command.h" +#include "expect_custom_generator.h" +#include "test_target_util.h" + +#include + +// 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(CustomGeneratorTestGroup) +{ + void teardown() { + mock().checkExpectations(); + mock().clear(); + buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); + } +}; +// clang-format on + +const fs::path BUILD_DIR = + fs::current_path() / "intermediate" / "custom_generator"; + +static bool BasicGenerateCb(const buildcc::CustomGeneratorContext &ctx) { + (void)ctx; + return mock().actualCall("BasicGenerateCb").returnBoolValue(); +} + +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"}, {}, + BasicGenerateCb); + 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.inputs.GetPathInfos().size(), 1); + CHECK_EQUAL(id1_info.outputs.GetPaths().size(), 1); + + const auto &id2_info = internal_map.at("id2"); + CHECK_EQUAL(id2_info.inputs.GetPathInfos().size(), 1); + CHECK_EQUAL(id2_info.outputs.GetPaths().size(), 0); + } +} + +TEST(CustomGeneratorTestGroup, BasicRebuild) { + constexpr const char *const kName = "basic_rebuild"; + + { + 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(); + + mock().expectOneCall("BasicGenerateCb").andReturnValue(true); + mock().expectOneCall("BasicGenerateCb").andReturnValue(true); + buildcc::m::CustomGeneratorRunner(cgen); + } + + { + 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(); + + buildcc::m::CustomGeneratorRunner(cgen); + } +} + +TEST(CustomGeneratorTestGroup, BasicRebuild_Add_Remove) { + constexpr const char *const kName = "basic_rebuild_add_remove"; + + { + 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(); + + mock().expectOneCall("BasicGenerateCb").andReturnValue(true); + mock().expectOneCall("BasicGenerateCb").andReturnValue(true); + buildcc::m::CustomGeneratorRunner(cgen); + } + + // Remove + { + buildcc::CustomGenerator cgen(kName, ""); + cgen.AddIdInfo("id1", {"{current_root_dir}/dummy_main.c"}, + {"{current_build_dir}/dummy_main.o"}, BasicGenerateCb); + // ID2 Removed + cgen.Build(); + + buildcc::m::CustomGeneratorExpect_IdRemoved(1, &cgen); + buildcc::m::CustomGeneratorRunner(cgen); + } + + // Add + { + 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(); + + buildcc::m::CustomGeneratorExpect_IdAdded(1, &cgen); + mock().expectOneCall("BasicGenerateCb").andReturnValue(true); + buildcc::m::CustomGeneratorRunner(cgen); + } +} + +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); + + // 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, 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"}, + {"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); + 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.inputs.GetPathInfos().size(), 1); + CHECK_EQUAL(id1_info.outputs.GetPaths().size(), 1); + + const auto &id2_info = internal_map.at("id2"); + CHECK_EQUAL(id2_info.inputs.GetPathInfos().size(), 1); + CHECK_EQUAL(id2_info.outputs.GetPaths().size(), 0); + } +} + +TEST(CustomGeneratorTestGroup, FailureCases) { + { + buildcc::CustomGenerator cgen("failure_no_cb", ""); + buildcc::GenerateCb cb; + CHECK_THROWS(std::exception, cgen.AddIdInfo("id1", {}, {}, cb)); + } + + buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); + + { + buildcc::CustomGenerator cgen("failure_cannot_save", ""); + fs::create_directory( + cgen.GetBinaryPath()); // make a folder so that file cannot be saved + + cgen.AddIdInfo("id1", {}, {}, BasicGenerateCb); + cgen.AddIdInfo("id2", {}, {}, BasicGenerateCb); + cgen.Build(); + + mock().expectOneCall("BasicGenerateCb").andReturnValue(true); + mock().expectOneCall("BasicGenerateCb").andReturnValue(true); + buildcc::m::CustomGeneratorRunner(cgen); + + CHECK_TRUE(buildcc::env::get_task_state() == + buildcc::env::TaskState::FAILURE); + } + + buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); + + { + buildcc::CustomGenerator cgen("gen_task_not_run_no_io", ""); + cgen.Build(); + + buildcc::m::CustomGeneratorRunner(cgen); + } + + buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); + + { + buildcc::env::set_task_state(buildcc::env::TaskState::FAILURE); + + buildcc::CustomGenerator cgen("gen_task_state_failure", ""); + cgen.AddIdInfo("id1", {}, {}, BasicGenerateCb); + cgen.Build(); + buildcc::m::CustomGeneratorRunner(cgen); + + CHECK_TRUE(buildcc::env::get_task_state() == + buildcc::env::TaskState::FAILURE); + } + + buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); +} + +static bool RealGenerateCb(const buildcc::CustomGeneratorContext &ctx) { + (void)ctx; + mock().actualCall("RealGenerateCb"); + return buildcc::env::Command::Execute(""); +} + +TEST(CustomGeneratorTestGroup, RealGenerate_Basic) { + constexpr const char *const kGenName = "real_generator_basic"; + { + 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); + + fs::remove_all(cgen.GetBinaryPath()); + } + + { + buildcc::CustomGenerator cgen(kGenName, ""); + cgen.AddIdInfo("id1", {"{current_root_dir}/dummy_main.cpp"}, {}, + RealGenerateCb); + cgen.AddIdInfo("id2", {"{current_root_dir}/dummy_main.c"}, {}, + RealGenerateCb); + cgen.Build(); + + mock().expectOneCall("RealGenerateCb"); + buildcc::env::m::CommandExpect_Execute(1, false); + + // Since there is an error above, second command does not execute (note, + // this is the behaviour in a single thread that is why we can + // check sequentially) + buildcc::m::CustomGeneratorRunner(cgen); + + CHECK_TRUE(buildcc::env::get_task_state() == + buildcc::env::TaskState::FAILURE); + + buildcc::internal::CustomGeneratorSerialization serialization( + cgen.GetBinaryPath()); + CHECK_TRUE(serialization.LoadFromFile()); + CHECK_EQUAL(serialization.GetLoad().internal_ids.size(), 0); + + fs::remove_all(cgen.GetBinaryPath()); + } + + buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); +} + +TEST(CustomGeneratorTestGroup, RealGenerate_Update_Success) { + constexpr const char *const kGenName = "real_generator_update_success"; + + { + 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.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").inputs.GetPathInfos().size(), 1); + CHECK_EQUAL(imap.at("id2").inputs.GetPathInfos().size(), 1); + + CHECK_EQUAL(imap.at("id1").outputs.GetPaths().size(), 1); + CHECK_EQUAL(imap.at("id2").outputs.GetPaths().size(), 1); + } + + buildcc::m::blocking_sleep(1); + + // Updated Input file Success + UT_PRINT("Updated Input file: Success\r\n"); + { + buildcc::CustomGenerator cgen(kGenName, ""); + buildcc::env::save_file( + (cgen.GetBuildDir() / "dummy_main.cpp").string().c_str(), "", false); + + auto last_write_timestamp = static_cast( + fs::last_write_time(cgen.GetBuildDir() / "dummy_main.cpp") + .time_since_epoch() + .count()); + + 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.Build(); + + 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").inputs.GetPathInfos().size(), 1); + CHECK_EQUAL(imap.at("id1").outputs.GetPaths().size(), 1); + + CHECK_EQUAL(imap.at("id2").inputs.GetPathInfos().size(), 1); + CHECK_EQUAL(imap.at("id2").outputs.GetPaths().size(), 1); + + STRCMP_EQUAL(std::to_string(last_write_timestamp).c_str(), + imap.at("id2").inputs.GetPathInfos()[0].hash.c_str()); + + CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::SUCCESS); + } + + // Updated Output file Success + UT_PRINT("Updated Output file: Success\r\n"); + { + buildcc::CustomGenerator cgen(kGenName, ""); + + 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}/rename_dummy_main.o"}, RealGenerateCb); + cgen.Build(); + + 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").inputs.GetPathInfos().size(), 1); + CHECK_EQUAL(imap.at("id1").outputs.GetPaths().size(), 1); + + CHECK_EQUAL(imap.at("id2").inputs.GetPathInfos().size(), 1); + CHECK_EQUAL(imap.at("id2").outputs.GetPaths().size(), 1); + + CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::SUCCESS); + } +} + +class MyCustomBlobHandler : public buildcc::CustomBlobHandler { +public: + MyCustomBlobHandler(int32_t my_recheck_value) + : recheck_value(my_recheck_value) {} + +private: + int32_t recheck_value = 0; + +private: + bool Verify(const std::vector &serialized_data) const override { + json j = json::from_msgpack(serialized_data); + return !j.is_discarded(); + } + + bool IsEqual(const std::vector &previous, + const std::vector ¤t) const override { + return Deserialize(previous) == Deserialize(current); + } + + std::vector Serialize() const override { + json j = recheck_value; + return json::to_msgpack(j); + } + + // serialized_data has already been verified + int32_t Deserialize(const std::vector &serialized_data) const { + json j = json::from_msgpack(serialized_data); + int32_t deserialized; + j.get_to(deserialized); + return deserialized; + } +}; + +TEST(CustomGeneratorTestGroup, RealGenerate_BasicBlobRecheck) { + constexpr const char *const kGenName = "real_generator_basic_blob_recheck"; + { + buildcc::CustomGenerator cgen(kGenName, ""); + cgen.AddIdInfo("id1", {"{current_root_dir}/dummy_main.cpp"}, + {"{current_build_dir}/dummy_main.o"}, RealGenerateCb, + std::make_shared(12)); + cgen.Build(); + + 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(), 1); + } + + // Rebuild + { + buildcc::CustomGenerator cgen(kGenName, ""); + cgen.AddIdInfo("id1", {"{current_root_dir}/dummy_main.cpp"}, + {"{current_build_dir}/dummy_main.o"}, RealGenerateCb, + std::make_shared(200)); + cgen.Build(); + + 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(), 1); + } + + buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); +} + +int main(int ac, char **av) { + fs::remove_all(BUILD_DIR); + buildcc::Project::Init(fs::current_path() / "data", BUILD_DIR); + return CommandLineTestRunner::RunAllTests(ac, av); +} diff --git a/buildcc/lib/target/test/target/test_file_generator.cpp b/buildcc/lib/target/test/target/test_file_generator.cpp new file mode 100644 index 00000000..278aa7a6 --- /dev/null +++ b/buildcc/lib/target/test/target/test_file_generator.cpp @@ -0,0 +1,467 @@ +#include "target/file_generator.h" + +#include "expect_command.h" +#include "expect_custom_generator.h" +#include "test_target_util.h" + +#include "taskflow/taskflow.hpp" + +#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(FileGeneratorTestGroup) +{ + void teardown() { + buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); + mock().clear(); + } +}; +// clang-format on + +fs::path BUILD_DIR = fs::current_path() / "intermediate" / "file_generator"; + +TEST(FileGeneratorTestGroup, Generator_Build) { + constexpr const char *const NAME = "Build"; + buildcc::FileGenerator generator(NAME, ""); + + generator.AddPatterns({ + {"compiler", "gcc"}, + }); + + generator.AddInput("{current_root_dir}/dummy_main.c"); + generator.AddOutput("{current_build_dir}/dummy_main.exe"); + generator.AddCommand("{compiler} -o {current_build_dir}/dummy_main.exe " + "{current_root_dir}/dummy_main.c"); + + buildcc::env::m::CommandExpect_Execute(1, true); + generator.Build(); + buildcc::m::CustomGeneratorRunner(generator); + + mock().checkExpectations(); +} + +TEST(FileGeneratorTestGroup, Generator_Identifier) { + constexpr const char *const NAME = "Identifier"; + buildcc::FileGenerator generator(NAME, ""); + + generator.AddPatterns({ + {"compiler", "gcc"}, + }); + generator.AddPattern("dummy_main_c", "{current_root_dir}/dummy_main.c"); + generator.AddPattern("dummy_main_exe", "{current_build_dir}/dummy_main.exe"); + + generator.AddInput("{dummy_main_c}"); + generator.AddOutput("{dummy_main_exe}"); + generator.AddCommand("{compiler} -o {dummy_main_exe} {dummy_main_c}"); + + buildcc::env::m::CommandExpect_Execute(1, true); + generator.Build(); + buildcc::m::CustomGeneratorRunner(generator); + + mock().checkExpectations(); +} + +TEST(FileGeneratorTestGroup, Generator_Rebuild) { + constexpr const char *const NAME = "Rebuild"; + { + buildcc::FileGenerator generator(NAME, ""); + generator.AddInput("{current_root_dir}/dummy_main.c"); + generator.AddOutput("{current_build_dir}/dummy_main.exe"); + generator.AddCommand("{compiler} -o {current_build_dir}/dummy_main.exe " + "{current_root_dir}/dummy_main.c", + { + {"compiler", "gcc"}, + }); + + buildcc::env::m::CommandExpect_Execute(1, true); + generator.Build(); + buildcc::m::CustomGeneratorRunner(generator); + } + + { + buildcc::FileGenerator generator(NAME, ""); + generator.AddInput("{current_root_dir}/dummy_main.c"); + generator.AddOutput("{current_build_dir}/dummy_main.exe"); + generator.AddCommand("{compiler} -o {current_build_dir}/dummy_main.exe " + "{current_root_dir}/dummy_main.c", + { + {"compiler", "gcc"}, + }); + + generator.Build(); + buildcc::m::CustomGeneratorRunner(generator); + } + + mock().checkExpectations(); +} + +TEST(FileGeneratorTestGroup, Generator_Rebuild_Inputs) { + constexpr const char *const NAME = "Rebuild_Inputs"; + + { + buildcc::FileGenerator generator(NAME, ""); + generator.AddInput("{current_root_dir}/new_source.cpp"); + generator.AddOutput("{current_build_dir}/new_source.exe"); + generator.AddCommand("gcc -o {current_build_dir}/new_source.exe " + "{current_root_dir}/new_source.cpp"); + + buildcc::env::m::CommandExpect_Execute(1, true); + generator.Build(); + buildcc::m::CustomGeneratorRunner(generator); + CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::SUCCESS); + } + + // Removed + { + buildcc::FileGenerator generator(NAME, ""); + generator.AddOutput("{current_build_dir}/new_source.exe"); + generator.AddCommand("gcc -o {current_build_dir}/new_source.exe " + "{current_root_dir}/new_source.cpp"); + + buildcc::env::m::CommandExpect_Execute(1, true); + generator.Build(); + buildcc::m::CustomGeneratorRunner(generator); + CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::SUCCESS); + } + + // Added + { + buildcc::FileGenerator generator(NAME, ""); + generator.AddInput("{current_root_dir}/new_source.cpp"); + generator.AddOutput("{current_build_dir}/new_source.cpp.exe"); + generator.AddCommand("gcc -o {current_build_dir}/new_source.cpp.exe " + "{current_root_dir}/new_source.cpp"); + + buildcc::env::m::CommandExpect_Execute(1, true); + generator.Build(); + buildcc::m::CustomGeneratorRunner(generator); + } + + buildcc::m::blocking_sleep(1); + bool saved = buildcc::env::save_file( + (buildcc::Project::GetRootDir() / "new_source.cpp").string().c_str(), "", + false); + CHECK_TRUE(saved); + + // Updated + { + buildcc::FileGenerator generator(NAME, ""); + generator.AddInput("{current_root_dir}/new_source.cpp"); + generator.AddOutput("{current_build_dir}/new_source.cpp.exe"); + generator.AddCommand("gcc -o {current_build_dir}/new_source.cpp.exe " + "{current_root_dir}/new_source.cpp"); + + buildcc::env::m::CommandExpect_Execute(1, true); + generator.Build(); + buildcc::m::CustomGeneratorRunner(generator); + } + + mock().checkExpectations(); +} + +TEST(FileGeneratorTestGroup, Generator_Rebuild_Outputs) { + constexpr const char *const NAME = "Rebuild_Outputs"; + { + buildcc::FileGenerator generator(NAME, ""); + generator.AddInput("{current_root_dir}/dummy_main.c"); + generator.AddOutput("{current_build_dir}/dummy_main.exe"); + generator.AddCommand("{compiler} -o {current_build_dir}/dummy_main.exe " + "{current_root_dir}/dummy_main.c", + { + {"compiler", "gcc"}, + }); + + buildcc::env::m::CommandExpect_Execute(1, true); + generator.Build(); + buildcc::m::CustomGeneratorRunner(generator); + } + + { + buildcc::FileGenerator generator(NAME, ""); + generator.AddInput("{current_root_dir}/dummy_main.c"); + generator.AddCommand("{compiler} -o {current_build_dir}/dummy_main.exe " + "{current_root_dir}/dummy_main.c", + { + {"compiler", "gcc"}, + }); + + buildcc::env::m::CommandExpect_Execute(1, true); + generator.Build(); + buildcc::m::CustomGeneratorRunner(generator); + } + + { + buildcc::FileGenerator generator(NAME, ""); + generator.AddInput("{current_root_dir}/dummy_main.c"); + generator.AddOutput("{current_build_dir}/dummy_main.exe"); + generator.AddCommand("{compiler} -o {current_build_dir}/dummy_main.exe " + "{current_root_dir}/dummy_main.c", + { + {"compiler", "gcc"}, + }); + + buildcc::env::m::CommandExpect_Execute(1, true); + generator.Build(); + buildcc::m::CustomGeneratorRunner(generator); + } + + mock().checkExpectations(); +} + +TEST(FileGeneratorTestGroup, Generator_Rebuild_Commands) { + constexpr const char *const NAME = "Rebuild_Commands"; + { + buildcc::FileGenerator generator(NAME, ""); + generator.AddInput("{current_root_dir}/dummy_main.c"); + generator.AddOutput("{current_build_dir}/dummy_main.exe"); + generator.AddCommand("{compiler} -o {current_build_dir}/dummy_main.exe " + "{current_root_dir}/dummy_main.c", + { + {"compiler", "gcc"}, + }); + + buildcc::env::m::CommandExpect_Execute(1, true); + generator.Build(); + buildcc::m::CustomGeneratorRunner(generator); + } + + { + buildcc::FileGenerator generator(NAME, ""); + generator.AddInput("{current_root_dir}/dummy_main.c"); + generator.AddOutput("{current_build_dir}/dummy_main.exe"); + generator.AddCommand("{compiler} {current_root_dir}/dummy_main.c", + { + {"compiler", "gcc"}, + }); + + buildcc::env::m::CommandExpect_Execute(1, true); + generator.Build(); + buildcc::m::CustomGeneratorRunner(generator); + } + + { + buildcc::FileGenerator generator(NAME, ""); + generator.AddInput("{current_root_dir}/dummy_main.c"); + generator.AddOutput("{current_build_dir}/dummy_main.exe"); + generator.AddCommand("gcc -o {current_build_dir}/dummy_main.exe " + "{current_root_dir}/dummy_main.c"); + + buildcc::env::m::CommandExpect_Execute(1, true); + generator.Build(); + buildcc::m::CustomGeneratorRunner(generator); + } + + mock().checkExpectations(); +} + +TEST(FileGeneratorTestGroup, Generator_AddDefaultArguments) { + constexpr const char *const NAME = "AddDefaultArgument"; + buildcc::FileGenerator generator(NAME, ""); + + generator.AddPatterns({ + {"key", "value"}, + }); + const std::string &value = generator.Get("key"); + STRCMP_EQUAL(value.c_str(), "value"); + STRCMP_EQUAL(generator.GetName().c_str(), "AddDefaultArgument"); +} + +// FAILURE STATES + +TEST(FileGeneratorTestGroup, Generator_FailedEnvTaskState) { + buildcc::env::set_task_state(buildcc::env::TaskState::FAILURE); + + constexpr const char *const NAME = "FailedEnvTaskState"; + buildcc::FileGenerator generator(NAME, ""); + + generator.AddPatterns({ + {"compiler", "gcc"}, + }); + + generator.AddInput("{current_root_dir}/dummy_main.c"); + generator.AddOutput("{current_build_dir}/dummy_main.exe"); + generator.AddCommand("{compiler} -o {current_build_dir}/dummy_main.exe " + "{current_root_dir}/dummy_main.c"); + + generator.Build(); + buildcc::m::CustomGeneratorRunner(generator); + + mock().checkExpectations(); + + buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); +} + +TEST(FileGeneratorTestGroup, Generator_FailedGenerateConvert) { + constexpr const char *const NAME = "FailedGenerateConvert"; + buildcc::FileGenerator generator(NAME, ""); + + generator.AddPatterns({ + {"compiler", "gcc"}, + }); + + generator.AddInput("{current_root_dir}/this_file_does_not_exist.c"); + generator.AddOutput("{current_build_dir}/dummy_main.exe"); + generator.AddCommand("{compiler} -o {current_build_dir}/dummy_main.exe " + "{current_root_dir}/dummy_main.c"); + + generator.Build(); + buildcc::m::CustomGeneratorRunner(generator); + + CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::FAILURE); + + mock().checkExpectations(); + + buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); +} + +TEST(FileGeneratorTestGroup, Generator_FailedGenerateCommand) { + constexpr const char *const NAME = "FailedGenerateCommand"; + buildcc::FileGenerator generator(NAME, ""); + + generator.AddPatterns({ + {"compiler", "gcc"}, + }); + + generator.AddInput("{current_root_dir}/dummy_main.c"); + generator.AddOutput("{current_build_dir}/dummy_main.exe"); + generator.AddCommand("{compiler} -o {current_build_dir}/dummy_main.exe " + "{current_root_dir}/dummy_main.c"); + + buildcc::env::m::CommandExpect_Execute(1, false); + generator.Build(); + buildcc::m::CustomGeneratorRunner(generator); + + mock().checkExpectations(); + + buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); +} + +TEST(FileGeneratorTestGroup, Generator_FailedStore) { + constexpr const char *const NAME = "FailedStore"; + const fs::path test_build_dir = buildcc::Project::GetBuildDir() / NAME; + + buildcc::FileGenerator generator(NAME, ""); + fs::remove_all(test_build_dir); + + generator.AddPatterns({ + {"compiler", "gcc"}, + }); + + generator.AddInput("{current_root_dir}/dummy_main.c"); + generator.AddOutput("{current_build_dir}/dummy_main.exe"); + generator.AddCommand("{compiler} -o {current_build_dir}/dummy_main.exe " + "{current_root_dir}/dummy_main.c"); + + buildcc::env::m::CommandExpect_Execute(1, true); + generator.Build(); + buildcc::m::CustomGeneratorRunner(generator); + + // CHECK(generator.GetTaskState() == buildcc::env::TaskState::FAILURE); + CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::FAILURE); + + mock().checkExpectations(); + + buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); +} + +TEST(FileGeneratorTestGroup, FailedEnvTaskState_Rebuild) { + buildcc::env::set_task_state(buildcc::env::TaskState::FAILURE); + + constexpr const char *const NAME = "FailedEnvTaskState_Rebuild"; + { + buildcc::FileGenerator generator(NAME, ""); + + generator.AddPatterns({ + {"compiler", "gcc"}, + }); + + generator.AddInput("{current_root_dir}/dummy_main.c"); + generator.AddOutput("{current_build_dir}/dummy_main.exe"); + generator.AddCommand("{compiler} -o {current_build_dir}/dummy_main.exe " + "{current_root_dir}/dummy_main.c"); + + generator.Build(); + buildcc::m::CustomGeneratorRunner(generator); + } + + // reset + buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); + + // rebuild + { + buildcc::FileGenerator generator(NAME, ""); + + generator.AddPatterns({ + {"compiler", "gcc"}, + }); + + generator.AddInput("{current_root_dir}/dummy_main.c"); + generator.AddOutput("{current_build_dir}/dummy_main.exe"); + generator.AddCommand("{compiler} -o {current_build_dir}/dummy_main.exe " + "{current_root_dir}/dummy_main.c"); + + generator.Build(); + buildcc::env::m::CommandExpect_Execute(1, true); + buildcc::m::CustomGeneratorRunner(generator); + } + + mock().checkExpectations(); +} + +TEST(FileGeneratorTestGroup, FailedGenerateCommand_Rebuild) { + constexpr const char *const NAME = "FailedGenerateCommand_Rebuild"; + + { + buildcc::FileGenerator generator(NAME, ""); + + generator.AddPatterns({ + {"compiler", "gcc"}, + }); + + generator.AddInput("{current_root_dir}/dummy_main.c"); + generator.AddOutput("{current_build_dir}/dummy_main.exe"); + generator.AddCommand("{compiler} -o {current_build_dir}/dummy_main.exe " + "{current_root_dir}/dummy_main.c"); + + buildcc::env::m::CommandExpect_Execute(1, false); + generator.Build(); + buildcc::m::CustomGeneratorRunner(generator); + } + + // reset + buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); + + // rebuild + { + buildcc::FileGenerator generator(NAME, ""); + + generator.AddPatterns({ + {"compiler", "gcc"}, + }); + + generator.AddInput("{current_root_dir}/dummy_main.c"); + generator.AddOutput("{current_build_dir}/dummy_main.exe"); + generator.AddCommand("{compiler} -o {current_build_dir}/dummy_main.exe " + "{current_root_dir}/dummy_main.c"); + + buildcc::m::CustomGeneratorExpect_IdAdded(1, &generator); + buildcc::env::m::CommandExpect_Execute(1, true); + generator.Build(); + buildcc::m::CustomGeneratorRunner(generator); + } + + mock().checkExpectations(); +} + +int main(int ac, char **av) { + fs::remove_all(BUILD_DIR); + buildcc::Project::Init(fs::current_path() / "data", BUILD_DIR); + return CommandLineTestRunner::RunAllTests(ac, av); +} diff --git a/buildcc/lib/target/test/target/test_generator.cpp b/buildcc/lib/target/test/target/test_generator.cpp deleted file mode 100644 index 877b0deb..00000000 --- a/buildcc/lib/target/test/target/test_generator.cpp +++ /dev/null @@ -1,487 +0,0 @@ -#include "target/generator.h" - -#include - -#include "expect_command.h" -#include "expect_generator.h" - -#include "taskflow/taskflow.hpp" - -#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 = fs::current_path() / "intermediate" / "generator"; - -TEST(GeneratorTestGroup, Generator_Build) { - constexpr const char *const NAME = "Build"; - buildcc::Generator generator(NAME, ""); - - generator.AddDefaultArguments({ - {"compiler", "gcc"}, - }); - - generator.AddInput("{gen_root_dir}/dummy_main.c"); - generator.AddOutput("{gen_build_dir}/dummy_main.exe"); - generator.AddCommand("{compiler} -o {gen_build_dir}/dummy_main.exe " - "{gen_root_dir}/dummy_main.c"); - - buildcc::env::m::CommandExpect_Execute(1, true); - generator.Build(); - buildcc::m::GeneratorRunner(generator); - - mock().checkExpectations(); -} - -TEST(GeneratorTestGroup, Generator_BuildParallel) { - constexpr const char *const NAME = "BuildParallel"; - buildcc::Generator generator(NAME, "", true); - - generator.AddDefaultArguments({ - {"compiler", "gcc"}, - }); - - generator.AddInput("{gen_root_dir}/dummy_main.c"); - generator.AddOutput("{gen_build_dir}/dummy_main.exe"); - generator.AddCommand("{compiler} -o {gen_build_dir}/dummy_main.exe " - "{gen_root_dir}/dummy_main.c"); - - buildcc::env::m::CommandExpect_Execute(1, true); - generator.Build(); - buildcc::m::GeneratorRunner(generator); - - mock().checkExpectations(); -} - -TEST(GeneratorTestGroup, Generator_Identifier) { - constexpr const char *const NAME = "Identifier"; - buildcc::Generator generator(NAME, ""); - - generator.AddDefaultArguments({ - {"compiler", "gcc"}, - }); - - generator.AddInput("{gen_root_dir}/dummy_main.c", "dummy_main_c"); - generator.AddOutput("{gen_build_dir}/dummy_main.exe", "dummy_main_exe"); - generator.AddCommand("{compiler} -o {dummy_main_exe} {dummy_main_c}"); - - buildcc::env::m::CommandExpect_Execute(1, true); - generator.Build(); - buildcc::m::GeneratorRunner(generator); - - mock().checkExpectations(); -} - -TEST(GeneratorTestGroup, Generator_Rebuild) { - constexpr const char *const NAME = "Rebuild"; - { - buildcc::Generator generator(NAME, ""); - generator.AddInput("{gen_root_dir}/dummy_main.c"); - generator.AddOutput("{gen_build_dir}/dummy_main.exe"); - generator.AddCommand("{compiler} -o {gen_build_dir}/dummy_main.exe " - "{gen_root_dir}/dummy_main.c", - { - {"compiler", "gcc"}, - }); - - buildcc::env::m::CommandExpect_Execute(1, true); - generator.Build(); - buildcc::m::GeneratorRunner(generator); - } - - { - buildcc::Generator generator(NAME, ""); - generator.AddInput("{gen_root_dir}/dummy_main.c"); - generator.AddOutput("{gen_build_dir}/dummy_main.exe"); - generator.AddCommand("{compiler} -o {gen_build_dir}/dummy_main.exe " - "{gen_root_dir}/dummy_main.c", - { - {"compiler", "gcc"}, - }); - - generator.Build(); - buildcc::m::GeneratorRunner(generator); - } - - mock().checkExpectations(); -} - -TEST(GeneratorTestGroup, Generator_Rebuild_Inputs) { - constexpr const char *const NAME = "Rebuild_Inputs"; - - { - buildcc::Generator generator(NAME, ""); - generator.AddInput("{gen_root_dir}/new_source.cpp"); - generator.AddOutput("{gen_build_dir}/new_source.exe"); - generator.AddCommand("gcc -o {gen_build_dir}/new_source.exe " - "{gen_root_dir}/new_source.cpp"); - - buildcc::env::m::CommandExpect_Execute(1, true); - generator.Build(); - buildcc::m::GeneratorRunner(generator); - } - - // Removed - { - buildcc::Generator generator(NAME, ""); - generator.AddOutput("{gen_build_dir}/new_source.exe"); - generator.AddCommand("gcc -o {gen_build_dir}/new_source.exe " - "{gen_root_dir}/new_source.cpp"); - - buildcc::m::GeneratorExpect_InputRemoved(1, &generator); - buildcc::env::m::CommandExpect_Execute(1, true); - generator.Build(); - buildcc::m::GeneratorRunner(generator); - } - - // Added - { - buildcc::Generator generator(NAME, ""); - generator.AddInput("{gen_root_dir}/new_source.cpp"); - generator.AddOutput("{gen_build_dir}/new_source.cpp.exe"); - generator.AddCommand("gcc -o {gen_build_dir}/new_source.cpp.exe " - "{gen_root_dir}/new_source.cpp"); - - buildcc::m::GeneratorExpect_InputAdded(1, &generator); - buildcc::env::m::CommandExpect_Execute(1, true); - generator.Build(); - buildcc::m::GeneratorRunner(generator); - } - - sleep(1); - bool saved = buildcc::env::save_file( - (buildcc::env::get_project_root_dir() / "new_source.cpp") - .string() - .c_str(), - "", false); - CHECK_TRUE(saved); - - // Updated - { - buildcc::Generator generator(NAME, ""); - generator.AddInput("{gen_root_dir}/new_source.cpp"); - generator.AddOutput("{gen_build_dir}/new_source.cpp.exe"); - generator.AddCommand("gcc -o {gen_build_dir}/new_source.cpp.exe " - "{gen_root_dir}/new_source.cpp"); - buildcc::m::GeneratorExpect_InputUpdated(1, &generator); - buildcc::env::m::CommandExpect_Execute(1, true); - generator.Build(); - buildcc::m::GeneratorRunner(generator); - } - - mock().checkExpectations(); -} - -TEST(GeneratorTestGroup, Generator_Rebuild_Outputs) { - constexpr const char *const NAME = "Rebuild_Outputs"; - { - buildcc::Generator generator(NAME, ""); - generator.AddInput("{gen_root_dir}/dummy_main.c"); - generator.AddOutput("{gen_build_dir}/dummy_main.exe"); - generator.AddCommand("{compiler} -o {gen_build_dir}/dummy_main.exe " - "{gen_root_dir}/dummy_main.c", - { - {"compiler", "gcc"}, - }); - - buildcc::env::m::CommandExpect_Execute(1, true); - generator.Build(); - buildcc::m::GeneratorRunner(generator); - } - - { - buildcc::Generator generator(NAME, ""); - generator.AddInput("{gen_root_dir}/dummy_main.c"); - generator.AddCommand("{compiler} -o {gen_build_dir}/dummy_main.exe " - "{gen_root_dir}/dummy_main.c", - { - {"compiler", "gcc"}, - }); - - buildcc::m::GeneratorExpect_OutputChanged(1, &generator); - buildcc::env::m::CommandExpect_Execute(1, true); - generator.Build(); - buildcc::m::GeneratorRunner(generator); - } - - { - buildcc::Generator generator(NAME, ""); - generator.AddInput("{gen_root_dir}/dummy_main.c"); - generator.AddOutput("{gen_build_dir}/dummy_main.exe"); - generator.AddCommand("{compiler} -o {gen_build_dir}/dummy_main.exe " - "{gen_root_dir}/dummy_main.c", - { - {"compiler", "gcc"}, - }); - - buildcc::m::GeneratorExpect_OutputChanged(1, &generator); - buildcc::env::m::CommandExpect_Execute(1, true); - generator.Build(); - buildcc::m::GeneratorRunner(generator); - } - - mock().checkExpectations(); -} - -TEST(GeneratorTestGroup, Generator_Rebuild_Commands) { - constexpr const char *const NAME = "Rebuild_Commands"; - { - buildcc::Generator generator(NAME, ""); - generator.AddInput("{gen_root_dir}/dummy_main.c"); - generator.AddOutput("{gen_build_dir}/dummy_main.exe"); - generator.AddCommand("{compiler} -o {gen_build_dir}/dummy_main.exe " - "{gen_root_dir}/dummy_main.c", - { - {"compiler", "gcc"}, - }); - - buildcc::env::m::CommandExpect_Execute(1, true); - generator.Build(); - buildcc::m::GeneratorRunner(generator); - } - - { - buildcc::Generator generator(NAME, ""); - generator.AddInput("{gen_root_dir}/dummy_main.c"); - generator.AddOutput("{gen_build_dir}/dummy_main.exe"); - generator.AddCommand("{compiler} {gen_root_dir}/dummy_main.c", - { - {"compiler", "gcc"}, - }); - - buildcc::m::GeneratorExpect_CommandChanged(1, &generator); - buildcc::env::m::CommandExpect_Execute(1, true); - generator.Build(); - buildcc::m::GeneratorRunner(generator); - } - - { - buildcc::Generator generator(NAME, ""); - generator.AddInput("{gen_root_dir}/dummy_main.c"); - generator.AddOutput("{gen_build_dir}/dummy_main.exe"); - generator.AddCommand("gcc -o {gen_build_dir}/dummy_main.exe " - "{gen_root_dir}/dummy_main.c"); - - buildcc::m::GeneratorExpect_CommandChanged(1, &generator); - buildcc::env::m::CommandExpect_Execute(1, true); - generator.Build(); - buildcc::m::GeneratorRunner(generator); - } - - mock().checkExpectations(); -} - -TEST(GeneratorTestGroup, Generator_AddDefaultArguments) { - constexpr const char *const NAME = "AddDefaultArgument"; - buildcc::Generator generator(NAME, ""); - - generator.AddDefaultArguments({ - {"key", "value"}, - }); - const std::string &value = generator.GetValueByIdentifier("key"); - STRCMP_EQUAL(value.c_str(), "value"); - STRCMP_EQUAL(generator.GetName().c_str(), "AddDefaultArgument"); -} - -// FAILURE STATES - -TEST(GeneratorTestGroup, Generator_FailedEnvTaskState) { - buildcc::env::set_task_state(buildcc::env::TaskState::FAILURE); - - constexpr const char *const NAME = "FailedEnvTaskState"; - buildcc::Generator generator(NAME, "", true); - - generator.AddDefaultArguments({ - {"compiler", "gcc"}, - }); - - generator.AddInput("{gen_root_dir}/dummy_main.c"); - generator.AddOutput("{gen_build_dir}/dummy_main.exe"); - generator.AddCommand("{compiler} -o {gen_build_dir}/dummy_main.exe " - "{gen_root_dir}/dummy_main.c"); - - generator.Build(); - buildcc::m::GeneratorRunner(generator); - - mock().checkExpectations(); - - buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); -} - -TEST(GeneratorTestGroup, Generator_FailedGenerateConvert) { - constexpr const char *const NAME = "FailedGenerateConvert"; - buildcc::Generator generator(NAME, "", false); - - generator.AddDefaultArguments({ - {"compiler", "gcc"}, - }); - - generator.AddInput("{gen_root_dir}/this_file_does_not_exist.c"); - generator.AddOutput("{gen_build_dir}/dummy_main.exe"); - generator.AddCommand("{compiler} -o {gen_build_dir}/dummy_main.exe " - "{gen_root_dir}/dummy_main.c"); - - generator.Build(); - buildcc::m::GeneratorRunner(generator); - - mock().checkExpectations(); - - buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); -} - -TEST(GeneratorTestGroup, Generator_FailedGenerateCommand) { - constexpr const char *const NAME = "FailedGenerateCommand"; - buildcc::Generator generator(NAME, "", false); - - generator.AddDefaultArguments({ - {"compiler", "gcc"}, - }); - - generator.AddInput("{gen_root_dir}/dummy_main.c"); - generator.AddOutput("{gen_build_dir}/dummy_main.exe"); - generator.AddCommand("{compiler} -o {gen_build_dir}/dummy_main.exe " - "{gen_root_dir}/dummy_main.c"); - - buildcc::env::m::CommandExpect_Execute(1, false); - generator.Build(); - buildcc::m::GeneratorRunner(generator); - - mock().checkExpectations(); - - buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); -} - -TEST(GeneratorTestGroup, Generator_FailedStore) { - constexpr const char *const NAME = "FailedStore"; - const fs::path test_build_dir = buildcc::env::get_project_build_dir() / NAME; - - buildcc::Generator generator(NAME, "", false); - fs::remove_all(test_build_dir); - - generator.AddDefaultArguments({ - {"compiler", "gcc"}, - }); - - generator.AddInput("{gen_root_dir}/dummy_main.c"); - generator.AddOutput("{gen_build_dir}/dummy_main.exe"); - generator.AddCommand("{compiler} -o {gen_build_dir}/dummy_main.exe " - "{gen_root_dir}/dummy_main.c"); - - buildcc::env::m::CommandExpect_Execute(1, true); - generator.Build(); - buildcc::m::GeneratorRunner(generator); - - CHECK(generator.GetTaskState() == buildcc::env::TaskState::FAILURE); - - mock().checkExpectations(); - - buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); -} - -TEST(GeneratorTestGroup, FailedEnvTaskState_Rebuild) { - buildcc::env::set_task_state(buildcc::env::TaskState::FAILURE); - - constexpr const char *const NAME = "FailedEnvTaskState_Rebuild"; - { - buildcc::Generator generator(NAME, "", true); - - generator.AddDefaultArguments({ - {"compiler", "gcc"}, - }); - - generator.AddInput("{gen_root_dir}/dummy_main.c"); - generator.AddOutput("{gen_build_dir}/dummy_main.exe"); - generator.AddCommand("{compiler} -o {gen_build_dir}/dummy_main.exe " - "{gen_root_dir}/dummy_main.c"); - - generator.Build(); - buildcc::m::GeneratorRunner(generator); - } - - // reset - buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); - - // rebuild - { - buildcc::Generator generator(NAME, "", true); - - generator.AddDefaultArguments({ - {"compiler", "gcc"}, - }); - - generator.AddInput("{gen_root_dir}/dummy_main.c"); - generator.AddOutput("{gen_build_dir}/dummy_main.exe"); - generator.AddCommand("{compiler} -o {gen_build_dir}/dummy_main.exe " - "{gen_root_dir}/dummy_main.c"); - - generator.Build(); - buildcc::env::m::CommandExpect_Execute(1, true); - buildcc::m::GeneratorRunner(generator); - } - - mock().checkExpectations(); -} - -TEST(GeneratorTestGroup, FailedGenerateCommand_Rebuild) { - constexpr const char *const NAME = "FailedGenerateCommand_Rebuild"; - - { - buildcc::Generator generator(NAME, "", false); - - generator.AddDefaultArguments({ - {"compiler", "gcc"}, - }); - - generator.AddInput("{gen_root_dir}/dummy_main.c"); - generator.AddOutput("{gen_build_dir}/dummy_main.exe"); - generator.AddCommand("{compiler} -o {gen_build_dir}/dummy_main.exe " - "{gen_root_dir}/dummy_main.c"); - - buildcc::env::m::CommandExpect_Execute(1, false); - generator.Build(); - buildcc::m::GeneratorRunner(generator); - } - - // reset - buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); - - // rebuild - { - buildcc::Generator generator(NAME, "", false); - - generator.AddDefaultArguments({ - {"compiler", "gcc"}, - }); - - generator.AddInput("{gen_root_dir}/dummy_main.c"); - generator.AddOutput("{gen_build_dir}/dummy_main.exe"); - generator.AddCommand("{compiler} -o {gen_build_dir}/dummy_main.exe " - "{gen_root_dir}/dummy_main.c"); - - buildcc::env::m::CommandExpect_Execute(1, true); - generator.Build(); - buildcc::m::GeneratorRunner(generator); - } - - mock().checkExpectations(); -} - -int main(int ac, char **av) { - fs::remove_all(BUILD_DIR); - buildcc::env::init(fs::current_path() / "data", BUILD_DIR); - return CommandLineTestRunner::RunAllTests(ac, av); -} diff --git a/buildcc/lib/target/test/target/test_serialization_interface.cpp b/buildcc/lib/target/test/target/test_serialization_interface.cpp new file mode 100644 index 00000000..e95db8d1 --- /dev/null +++ b/buildcc/lib/target/test/target/test_serialization_interface.cpp @@ -0,0 +1,115 @@ +#include "schema/interface/serialization_interface.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" + +class TestSerializationInterface + : public buildcc::internal::SerializationInterface { +public: + TestSerializationInterface(const fs::path &serialized_file) + : SerializationInterface(serialized_file) {} + + void VerifyExpectation(int calls, bool return_value) { + mock() + .expectNCalls(calls, "verify") + .onObject(this) + .andReturnValue(return_value); + } + + void LoadExpectation(int calls, bool return_value) { + mock() + .expectNCalls(calls, "load") + .onObject(this) + .andReturnValue(return_value); + } + + void StoreExpectation(int calls, bool return_value) { + mock() + .expectNCalls(calls, "store") + .onObject(this) + .andReturnValue(return_value); + } + +private: + bool Verify(const std::string &serialized_data) override { + (void)serialized_data; + return mock().actualCall("verify").onObject(this).returnBoolValue(); + } + + bool Load(const std::string &serialized_data) override { + (void)serialized_data; + return mock().actualCall("load").onObject(this).returnBoolValue(); + } + + bool Store(const fs::path &absolute_serialized_file) override { + (void)absolute_serialized_file; + return mock().actualCall("store").onObject(this).returnBoolValue(); + } +}; + +// clang-format off +TEST_GROUP(TestSerializationInterfaceGroup) +{ + void teardown() { + mock().clear(); + } +}; +// clang-format on + +TEST(TestSerializationInterfaceGroup, Verify_False) { + TestSerializationInterface test_serialization_interface( + fs::current_path() / "data" / "dummy_main.c"); + + test_serialization_interface.VerifyExpectation(1, false); + bool loaded = test_serialization_interface.LoadFromFile(); + CHECK_FALSE(loaded); +} + +TEST(TestSerializationInterfaceGroup, Load_False) { + TestSerializationInterface test_serialization_interface( + fs::current_path() / "data" / "dummy_main.c"); + + test_serialization_interface.VerifyExpectation(1, true); + test_serialization_interface.LoadExpectation(1, false); + bool loaded = test_serialization_interface.LoadFromFile(); + CHECK_FALSE(loaded); +} + +TEST(TestSerializationInterfaceGroup, Load_True) { + TestSerializationInterface test_serialization_interface( + fs::current_path() / "data" / "dummy_main.c"); + + test_serialization_interface.VerifyExpectation(1, true); + test_serialization_interface.LoadExpectation(1, true); + bool loaded = test_serialization_interface.LoadFromFile(); + CHECK_TRUE(loaded); +} + +TEST(TestSerializationInterfaceGroup, Store_False) { + TestSerializationInterface test_serialization_interface( + fs::current_path() / "data" / "dummy_main.c"); + test_serialization_interface.StoreExpectation(1, false); + bool stored = test_serialization_interface.StoreToFile(); + CHECK_FALSE(stored); +} + +TEST(TestSerializationInterfaceGroup, Store_True) { + TestSerializationInterface test_serialization_interface( + fs::current_path() / "data" / "dummy_main.c"); + test_serialization_interface.StoreExpectation(1, true); + bool stored = test_serialization_interface.StoreToFile(); + CHECK_TRUE(stored); + + std::string serialized_file = + test_serialization_interface.GetSerializedFile().string(); + std::string compare = (fs::current_path() / "data" / "dummy_main.c").string(); + STRCMP_EQUAL(serialized_file.c_str(), compare.c_str()); +} + +int main(int ac, char **av) { + return CommandLineTestRunner::RunAllTests(ac, av); +} diff --git a/buildcc/lib/target/test/target/test_target_config.cpp b/buildcc/lib/target/test/target/test_target_config.cpp deleted file mode 100644 index e0fcfcf0..00000000 --- a/buildcc/lib/target/test/target/test_target_config.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "target/common/target_config.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" - -// clang-format off -TEST_GROUP(TargetConfigTestGroup) -{ -}; -// clang-format on - -TEST(TargetConfigTestGroup, GetFileExt) { - buildcc::TargetConfig target_config; - - buildcc::TargetFileExt ext; - - ext = target_config.GetFileExt("file.asm"); - CHECK(ext == buildcc::TargetFileExt::Asm); - - ext = target_config.GetFileExt("file.c"); - CHECK(ext == buildcc::TargetFileExt::C); - - ext = target_config.GetFileExt("file.cpp"); - CHECK(ext == buildcc::TargetFileExt::Cpp); - - ext = target_config.GetFileExt("file.h"); - CHECK(ext == buildcc::TargetFileExt::Header); - - ext = target_config.GetFileExt("file.invalid"); - CHECK(ext == buildcc::TargetFileExt::Invalid); - - ext = target_config.GetFileExt("random/directory"); - CHECK(ext == buildcc::TargetFileExt::Invalid); -} - -int main(int ac, char **av) { - return CommandLineTestRunner::RunAllTests(ac, av); -} 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 8500de88..e6f76cef 100644 --- a/buildcc/lib/target/test/target/test_target_external_lib.cpp +++ b/buildcc/lib/target/test/target/test_target_external_lib.cpp @@ -7,10 +7,9 @@ #include "target/target.h" -#include "target/base/target_loader.h" +#include "schema/target_serialization.h" #include -#include // NOTE, Make sure all these includes are AFTER the system and header includes #include "CppUTest/CommandLineTestRunner.h" @@ -28,8 +27,9 @@ TEST_GROUP(TargetTestExternalLib) }; // clang-format on -static const buildcc::Toolchain gcc(buildcc::Toolchain::Id::Gcc, "gcc", "as", - "gcc", "g++", "ar", "ld"); +static buildcc::Toolchain gcc(buildcc::ToolchainId::Gcc, "gcc", + buildcc::ToolchainExecutables("as", "gcc", "g++", + "ar", "ld")); static const fs::path intermediate_path = fs::path(BUILD_TARGET_EXTERNAL_LIB_INTERMEDIATE_DIR) / gcc.GetName(); @@ -53,12 +53,12 @@ TEST(TargetTestExternalLib, TestAddLibDir) { mock().checkExpectations(); // Verify binary - buildcc::internal::TargetLoader loader(EXENAME, exe.GetTargetBuildDir()); - bool loaded = loader.Load(); + buildcc::internal::TargetSerialization serialization( + exe.GetTargetBuildDir() / (std::string(EXENAME) + ".bin")); + bool loaded = serialization.LoadFromFile(); CHECK_TRUE(loaded); - - CHECK_EQUAL(loader.GetLoadedLibDirs().size(), 1); - CHECK_EQUAL(loader.GetLoadedExternalLibDeps().size(), 0); + CHECK_EQUAL(serialization.GetLoad().lib_dirs.GetPaths().size(), 1); + CHECK_EQUAL(serialization.GetLoad().external_libs.size(), 0); } TEST(TargetTestExternalLib, TestAddExternalLibDep_Simple) { @@ -81,12 +81,13 @@ TEST(TargetTestExternalLib, TestAddExternalLibDep_Simple) { mock().checkExpectations(); // Verify binary - buildcc::internal::TargetLoader loader(EXENAME, exe.GetTargetBuildDir()); - bool loaded = loader.Load(); + buildcc::internal::TargetSerialization serialization( + exe.GetTargetBuildDir() / (std::string(EXENAME) + ".bin")); + bool loaded = serialization.LoadFromFile(); CHECK_TRUE(loaded); - CHECK_EQUAL(loader.GetLoadedLibDirs().size(), 1); - CHECK_EQUAL(loader.GetLoadedExternalLibDeps().size(), 1); + CHECK_EQUAL(serialization.GetLoad().lib_dirs.GetPaths().size(), 1); + CHECK_EQUAL(serialization.GetLoad().external_libs.size(), 1); } TEST(TargetTestExternalLib, TestAddExternalLibDep_RebuildChanged) { @@ -141,7 +142,7 @@ TEST(TargetTestExternalLib, TestAddExternalLibDep_RebuildChanged) { } int main(int ac, char **av) { - buildcc::env::init(BUILD_SCRIPT_SOURCE, - BUILD_TARGET_EXTERNAL_LIB_INTERMEDIATE_DIR); + buildcc::Project::Init(BUILD_SCRIPT_SOURCE, + BUILD_TARGET_EXTERNAL_LIB_INTERMEDIATE_DIR); return CommandLineTestRunner::RunAllTests(ac, av); } diff --git a/buildcc/lib/target/test/target/test_target_failure_states.cpp b/buildcc/lib/target/test/target/test_target_failure_states.cpp index 4b9bac5a..114ec908 100644 --- a/buildcc/lib/target/test/target/test_target_failure_states.cpp +++ b/buildcc/lib/target/test/target/test_target_failure_states.cpp @@ -25,8 +25,9 @@ TEST_GROUP(TargetTestFailureStates) }; // clang-format on -buildcc::Toolchain gcc(buildcc::Toolchain::Id::Gcc, "gcc", "as", "gcc", "g++", - "ar", "ld"); +static buildcc::Toolchain gcc(buildcc::ToolchainId::Gcc, "gcc", + buildcc::ToolchainExecutables("as", "gcc", "g++", + "ar", "ld")); TEST(TargetTestFailureStates, StartTaskEnvFailure) { buildcc::env::set_task_state(buildcc::env::TaskState::FAILURE); @@ -39,7 +40,7 @@ TEST(TargetTestFailureStates, StartTaskEnvFailure) { target.Build(); buildcc::m::TargetRunner(target); - CHECK(target.GetTaskState() == buildcc::env::TaskState::FAILURE); + CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::FAILURE); } TEST(TargetTestFailureStates, CompilePchFailure) { @@ -54,7 +55,7 @@ TEST(TargetTestFailureStates, CompilePchFailure) { buildcc::env::m::CommandExpect_Execute(1, false); // PCH compile buildcc::m::TargetRunner(target); - CHECK(target.GetTaskState() == buildcc::env::TaskState::FAILURE); + CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::FAILURE); } TEST(TargetTestFailureStates, CompileObjectFailure) { @@ -71,7 +72,7 @@ TEST(TargetTestFailureStates, CompileObjectFailure) { buildcc::env::m::CommandExpect_Execute(1, true); // compile buildcc::m::TargetRunner(target); - CHECK(target.GetTaskState() == buildcc::env::TaskState::FAILURE); + CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::FAILURE); } TEST(TargetTestFailureStates, CompileObject_FileNotFoundFailure) { @@ -84,7 +85,7 @@ TEST(TargetTestFailureStates, CompileObject_FileNotFoundFailure) { target.Build(); buildcc::m::TargetRunner(target); - CHECK(target.GetTaskState() == buildcc::env::TaskState::FAILURE); + CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::FAILURE); } TEST(TargetTestFailureStates, LinkTargetFailure) { @@ -100,7 +101,7 @@ TEST(TargetTestFailureStates, LinkTargetFailure) { buildcc::env::m::CommandExpect_Execute(1, false); // link buildcc::m::TargetRunner(target); - CHECK(target.GetTaskState() == buildcc::env::TaskState::FAILURE); + CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::FAILURE); } TEST(TargetTestFailureStates, EndTaskStoreFailure) { @@ -118,7 +119,7 @@ TEST(TargetTestFailureStates, EndTaskStoreFailure) { buildcc::env::m::CommandExpect_Execute(1, true); // link buildcc::m::TargetRunner(target); - CHECK(target.GetTaskState() == buildcc::env::TaskState::FAILURE); + CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::FAILURE); } // TODO, Test failure rebuilds! @@ -137,7 +138,7 @@ TEST(TargetTestFailureStates, StartTaskEnvFailure_Rebuild) { target.Build(); buildcc::m::TargetRunner(target); - CHECK(target.GetTaskState() == buildcc::env::TaskState::FAILURE); + CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::FAILURE); CHECK_FALSE(target.IsBuilt()); } @@ -156,7 +157,7 @@ TEST(TargetTestFailureStates, StartTaskEnvFailure_Rebuild) { buildcc::env::m::CommandExpect_Execute(1, true); // link buildcc::m::TargetRunner(target); - CHECK(target.GetTaskState() == buildcc::env::TaskState::SUCCESS); + CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::SUCCESS); CHECK_TRUE(target.IsBuilt()); } } @@ -176,7 +177,7 @@ TEST(TargetTestFailureStates, CompilePchFailure_Rebuild) { buildcc::env::m::CommandExpect_Execute(1, false); // PCH compile buildcc::m::TargetRunner(target); - CHECK(target.GetTaskState() == buildcc::env::TaskState::FAILURE); + CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::FAILURE); } // Reset @@ -193,13 +194,14 @@ TEST(TargetTestFailureStates, CompilePchFailure_Rebuild) { target.AddPch("include/include_header.h"); target.Build(); + buildcc::m::TargetExpect_PathAdded(1, &target); buildcc::env::m::CommandExpect_Execute(1, true); // PCH compile buildcc::env::m::CommandExpect_Execute(1, true); // Object compile buildcc::env::m::CommandExpect_Execute(1, true); // Link target buildcc::m::TargetRunner(target); - CHECK(target.GetTaskState() == buildcc::env::TaskState::SUCCESS); + CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::SUCCESS); } } @@ -220,7 +222,7 @@ TEST(TargetTestFailureStates, CompileObjectFailure_Rebuild) { buildcc::env::m::CommandExpect_Execute(1, true); // compile buildcc::m::TargetRunner(target); - CHECK(target.GetTaskState() == buildcc::env::TaskState::FAILURE); + CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::FAILURE); } // Reset @@ -251,7 +253,7 @@ TEST(TargetTestFailureStates, CompileObjectFailure_Rebuild) { buildcc::env::m::CommandExpect_Execute(1, true); // link buildcc::m::TargetRunner(target); - CHECK(target.GetTaskState() == buildcc::env::TaskState::SUCCESS); + CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::SUCCESS); } } @@ -270,7 +272,7 @@ TEST(TargetTestFailureStates, LinkTargetFailure_Rebuild) { buildcc::env::m::CommandExpect_Execute(1, false); // link buildcc::m::TargetRunner(target); - CHECK(target.GetTaskState() == buildcc::env::TaskState::FAILURE); + CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::FAILURE); } // Reset @@ -288,13 +290,13 @@ TEST(TargetTestFailureStates, LinkTargetFailure_Rebuild) { buildcc::env::m::CommandExpect_Execute(1, true); // link buildcc::m::TargetRunner(target); - CHECK(target.GetTaskState() == buildcc::env::TaskState::SUCCESS); + CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::SUCCESS); } } int main(int ac, char **av) { - buildcc::env::init(BUILD_SCRIPT_SOURCE, - BUILD_TARGET_FAILURE_STATES_BUILD_DIR); - fs::remove_all(buildcc::env::get_project_build_dir()); + buildcc::Project::Init(BUILD_SCRIPT_SOURCE, + BUILD_TARGET_FAILURE_STATES_BUILD_DIR); + fs::remove_all(buildcc::Project::GetBuildDir()); return CommandLineTestRunner::RunAllTests(ac, av); } diff --git a/buildcc/lib/target/test/target/test_target_flags.cpp b/buildcc/lib/target/test/target/test_target_flags.cpp index 8b6cb218..af1d1b91 100644 --- a/buildcc/lib/target/test/target/test_target_flags.cpp +++ b/buildcc/lib/target/test/target/test_target_flags.cpp @@ -8,7 +8,7 @@ #include "env/env.h" // -#include "target/base/target_loader.h" +#include "schema/target_serialization.h" // Third Party @@ -21,8 +21,9 @@ // Constants -static const buildcc::Toolchain gcc(buildcc::Toolchain::Id::Gcc, "gcc", "as", - "gcc", "g++", "ar", "ld"); +static buildcc::Toolchain gcc(buildcc::ToolchainId::Gcc, "gcc", + buildcc::ToolchainExecutables("as", "gcc", "g++", + "ar", "ld")); // ------------- PREPROCESSOR FLAGS --------------- @@ -30,6 +31,7 @@ static const buildcc::Toolchain gcc(buildcc::Toolchain::Id::Gcc, "gcc", "as", TEST_GROUP(TargetTestPreprocessorFlagGroup) { void teardown() { + mock().checkExpectations(); mock().clear(); } }; @@ -62,11 +64,12 @@ TEST(TargetTestPreprocessorFlagGroup, Target_AddPreprocessorFlag) { mock().checkExpectations(); // Verify binary - buildcc::internal::TargetLoader loader(NAME, simple.GetTargetBuildDir()); - bool loaded = loader.Load(); + buildcc::internal::TargetSerialization serialization( + simple.GetTargetBuildDir() / (std::string(NAME) + ".bin")); + bool loaded = serialization.LoadFromFile(); CHECK_TRUE(loaded); - CHECK_EQUAL(loader.GetLoadedPreprocessorFlags().size(), 1); + CHECK_EQUAL(serialization.GetLoad().preprocessor_flags.size(), 1); } TEST(TargetTestPreprocessorFlagGroup, Target_ChangedPreprocessorFlag) { @@ -145,26 +148,54 @@ TEST(TargetTestCommonCompileFlagsGroup, Target_AddCommonCompileFlag) { // Delete fs::remove_all(intermediate_path); - buildcc::BaseTarget simple(NAME, buildcc::TargetType::Executable, gcc, - "data"); - simple.AddSource(DUMMY_MAIN); - simple.AddCommonCompileFlag("-O0"); - simple.AddCommonCompileFlag("-g"); + { + buildcc::BaseTarget simple(NAME, buildcc::TargetType::Executable, gcc, + "data"); + simple.AddSource(DUMMY_MAIN); + simple.AddCommonCompileFlag("-O0"); + simple.AddCommonCompileFlag("-g"); - buildcc::env::m::CommandExpect_Execute(1, true); - buildcc::env::m::CommandExpect_Execute(1, true); - simple.Build(); - buildcc::m::TargetRunner(simple); - CHECK_TRUE(simple.IsBuilt()); + buildcc::env::m::CommandExpect_Execute(1, true); + buildcc::env::m::CommandExpect_Execute(1, true); + simple.Build(); + buildcc::m::TargetRunner(simple); + CHECK_TRUE(simple.IsBuilt()); - mock().checkExpectations(); + mock().checkExpectations(); - // Verify binary - buildcc::internal::TargetLoader loader(NAME, simple.GetTargetBuildDir()); - bool loaded = loader.Load(); - CHECK_TRUE(loaded); + // Verify binary + buildcc::internal::TargetSerialization serialization( + simple.GetTargetBuildDir() / (std::string(NAME) + ".bin")); + bool loaded = serialization.LoadFromFile(); + CHECK_TRUE(loaded); + + CHECK_EQUAL(serialization.GetLoad().common_compile_flags.size(), 2); + } + + // Trigger Rebuild for Common Compile flag + { + buildcc::BaseTarget simple(NAME, buildcc::TargetType::Executable, gcc, + "data"); + simple.AddSource(DUMMY_MAIN); + simple.AddCommonCompileFlag("-O0"); + + buildcc::m::TargetExpect_FlagChanged(1, &simple); + buildcc::env::m::CommandExpect_Execute(1, true); + buildcc::env::m::CommandExpect_Execute(1, true); + simple.Build(); + buildcc::m::TargetRunner(simple); + CHECK_TRUE(simple.IsBuilt()); + + mock().checkExpectations(); + + // Verify binary + buildcc::internal::TargetSerialization serialization( + simple.GetTargetBuildDir() / (std::string(NAME) + ".bin")); + bool loaded = serialization.LoadFromFile(); + CHECK_TRUE(loaded); - CHECK_EQUAL(loader.GetLoadedCommonCompileFlags().size(), 2); + CHECK_EQUAL(serialization.GetLoad().common_compile_flags.size(), 1); + } } TEST(TargetTestCommonCompileFlagsGroup, Target_ChangedCommonCompileFlag) { @@ -274,11 +305,12 @@ TEST(TargetTestAsmCompileFlagGroup, Target_AddCompileFlag) { mock().checkExpectations(); // Verify binary - buildcc::internal::TargetLoader loader(NAME, simple.GetTargetBuildDir()); - bool loaded = loader.Load(); + buildcc::internal::TargetSerialization serialization( + simple.GetTargetBuildDir() / (std::string(NAME) + ".bin")); + bool loaded = serialization.LoadFromFile(); CHECK_TRUE(loaded); - CHECK_EQUAL(loader.GetLoadedAsmCompileFlags().size(), 2); + CHECK_EQUAL(serialization.GetLoad().asm_compile_flags.size(), 2); } TEST(TargetTestAsmCompileFlagGroup, Target_ChangedCompileFlag) { @@ -388,11 +420,12 @@ TEST(TargetTestCCompileFlagsGroup, Target_AddCompileFlag) { mock().checkExpectations(); // Verify binary - buildcc::internal::TargetLoader loader(NAME, simple.GetTargetBuildDir()); - bool loaded = loader.Load(); + buildcc::internal::TargetSerialization serialization( + simple.GetTargetBuildDir() / (std::string(NAME) + ".bin")); + bool loaded = serialization.LoadFromFile(); CHECK_TRUE(loaded); - CHECK_EQUAL(loader.GetLoadedCCompileFlags().size(), 1); + CHECK_EQUAL(serialization.GetLoad().c_compile_flags.size(), 1); } TEST(TargetTestCCompileFlagsGroup, Target_ChangedCompileFlag) { @@ -471,25 +504,53 @@ TEST(TargetTestCppCompileFlagsGroup, Target_AddCompileFlag) { // Delete fs::remove_all(intermediate_path); - buildcc::BaseTarget simple(NAME, buildcc::TargetType::Executable, gcc, - "data"); - simple.AddSource(DUMMY_MAIN); - simple.AddCppCompileFlag("-std=c++17"); + { + buildcc::BaseTarget simple(NAME, buildcc::TargetType::Executable, gcc, + "data"); + simple.AddSource(DUMMY_MAIN); + simple.AddCppCompileFlag("-std=c++17"); - buildcc::env::m::CommandExpect_Execute(1, true); - buildcc::env::m::CommandExpect_Execute(1, true); - simple.Build(); - buildcc::m::TargetRunner(simple); - CHECK_TRUE(simple.IsBuilt()); + buildcc::env::m::CommandExpect_Execute(1, true); + buildcc::env::m::CommandExpect_Execute(1, true); + simple.Build(); + buildcc::m::TargetRunner(simple); + CHECK_TRUE(simple.IsBuilt()); - mock().checkExpectations(); + mock().checkExpectations(); - // Verify binary - buildcc::internal::TargetLoader loader(NAME, simple.GetTargetBuildDir()); - bool loaded = loader.Load(); - CHECK_TRUE(loaded); + // Verify binary + buildcc::internal::TargetSerialization serialization( + simple.GetTargetBuildDir() / (std::string(NAME) + ".bin")); + bool loaded = serialization.LoadFromFile(); + CHECK_TRUE(loaded); + + CHECK_EQUAL(serialization.GetLoad().cpp_compile_flags.size(), 1); + } + + // Trigger rebuild for Cpp Compile flag + { + buildcc::BaseTarget simple(NAME, buildcc::TargetType::Executable, gcc, + "data"); + simple.AddSource(DUMMY_MAIN); + simple.AddCppCompileFlag("-std=c++20"); - CHECK_EQUAL(loader.GetLoadedCppCompileFlags().size(), 1); + buildcc::m::TargetExpect_FlagChanged(1, &simple); + buildcc::env::m::CommandExpect_Execute(1, true); + buildcc::env::m::CommandExpect_Execute(1, true); + simple.Build(); + buildcc::m::TargetRunner(simple); + CHECK_TRUE(simple.IsBuilt()); + + mock().checkExpectations(); + + // Verify binary + buildcc::internal::TargetSerialization serialization( + simple.GetTargetBuildDir() / (std::string(NAME) + ".bin")); + bool loaded = serialization.LoadFromFile(); + CHECK_TRUE(loaded); + + CHECK_EQUAL(serialization.GetLoad().cpp_compile_flags.size(), 1); + } } TEST(TargetTestCppCompileFlagsGroup, Target_ChangedCompileFlag) { @@ -582,11 +643,12 @@ TEST(TargetTestLinkFlagsGroup, Target_AddLinkFlag) { mock().checkExpectations(); // Verify binary - buildcc::internal::TargetLoader loader(NAME, simple.GetTargetBuildDir()); - bool loaded = loader.Load(); + buildcc::internal::TargetSerialization serialization( + simple.GetTargetBuildDir() / (std::string(NAME) + ".bin")); + bool loaded = serialization.LoadFromFile(); CHECK_TRUE(loaded); - CHECK_EQUAL(loader.GetLoadedLinkFlags().size(), 1); + CHECK_EQUAL(serialization.GetLoad().link_flags.size(), 1); } TEST(TargetTestLinkFlagsGroup, Target_ChangedLinkFlag) { @@ -640,6 +702,7 @@ TEST(TargetTestLinkFlagsGroup, Target_ChangedLinkFlag) { } int main(int ac, char **av) { - buildcc::env::init(BUILD_SCRIPT_SOURCE, BUILD_TARGET_FLAG_INTERMEDIATE_DIR); + buildcc::Project::Init(BUILD_SCRIPT_SOURCE, + BUILD_TARGET_FLAG_INTERMEDIATE_DIR); return CommandLineTestRunner::RunAllTests(ac, av); } diff --git a/buildcc/lib/target/test/target/test_target_include_dir.cpp b/buildcc/lib/target/test/target/test_target_include_dir.cpp index 2a65112a..2636f52e 100644 --- a/buildcc/lib/target/test/target/test_target_include_dir.cpp +++ b/buildcc/lib/target/test/target/test_target_include_dir.cpp @@ -26,8 +26,9 @@ TEST_GROUP(TargetTestIncludeDirGroup) }; // clang-format on -static const buildcc::Toolchain gcc(buildcc::Toolchain::Id::Gcc, "gcc", "as", - "gcc", "g++", "ar", "ld"); +static buildcc::Toolchain gcc(buildcc::ToolchainId::Gcc, "gcc", + buildcc::ToolchainExecutables("as", "gcc", "g++", + "ar", "ld")); static const fs::path target_include_dir_intermediate_path = fs::path(BUILD_TARGET_INCLUDE_DIR_INTERMEDIATE_DIR) / gcc.GetName(); @@ -93,12 +94,14 @@ TEST(TargetTestIncludeDirGroup, TargetBuildIncludeDir) { // Delete fs::remove_all(intermediate_path); - auto dummy_c_file = buildcc::internal::Path::CreateExistingPath( - (source_path / DUMMY_MAIN_C).make_preferred().string()); - auto include_header_file = buildcc::internal::Path::CreateExistingPath( - (source_path / INCLUDE_HEADER_SOURCE).make_preferred().string()); - auto include_header_path = - (source_path / RELATIVE_INCLUDE_DIR).make_preferred(); + auto dummy_c_file = buildcc::internal::PathInfo::ToPathString( + fs::path(source_path / DUMMY_MAIN_C).string()); + + auto include_header_file = buildcc::internal::PathInfo::ToPathString( + fs::path(source_path / INCLUDE_HEADER_SOURCE).string()); + + auto include_header_path = buildcc::internal::PathInfo::ToPathString( + fs::path(source_path / RELATIVE_INCLUDE_DIR).string()); { buildcc::BaseTarget include_compile(NAME, buildcc::TargetType::Executable, @@ -106,7 +109,7 @@ TEST(TargetTestIncludeDirGroup, TargetBuildIncludeDir) { include_compile.AddSource(DUMMY_MAIN_C); include_compile.AddSource(INCLUDE_HEADER_SOURCE); include_compile.AddIncludeDir(RELATIVE_INCLUDE_DIR); - // Duplicate include directory + // Duplicate include directory (will be reflected) include_compile.AddIncludeDir(RELATIVE_INCLUDE_DIR); buildcc::env::m::CommandExpect_Execute(2, true); @@ -114,21 +117,25 @@ TEST(TargetTestIncludeDirGroup, TargetBuildIncludeDir) { include_compile.Build(); buildcc::m::TargetRunner(include_compile); - buildcc::internal::TargetLoader loader(NAME, intermediate_path); - bool is_loaded = loader.Load(); + buildcc::internal::TargetSerialization serialization( + intermediate_path / (std::string(NAME) + ".bin")); + bool is_loaded = serialization.LoadFromFile(); CHECK_TRUE(is_loaded); - const auto &loaded_sources = loader.GetLoadedSources(); - const auto &loaded_dirs = loader.GetLoadedIncludeDirs(); + const auto &loaded_sources = + serialization.GetLoad().sources.GetUnorderedPathInfos(); + const auto &loaded_dirs = serialization.GetLoad().include_dirs.GetPaths(); CHECK_EQUAL(loaded_sources.size(), 2); - CHECK_EQUAL(loaded_dirs.size(), 1); + CHECK_EQUAL(loaded_dirs.size(), 2); CHECK_FALSE(loaded_sources.find(dummy_c_file) == loaded_sources.end()); CHECK_FALSE(loaded_sources.find(include_header_file) == loaded_sources.end()); - CHECK_FALSE(loaded_dirs.find(include_header_path.string()) == - loaded_dirs.end()); + std::unordered_set unordered_loaded_dirs(loaded_dirs.begin(), + loaded_dirs.end()); + CHECK_FALSE(unordered_loaded_dirs.find(include_header_path) == + unordered_loaded_dirs.end()); } { // * 1 Adding new include directory @@ -146,11 +153,13 @@ TEST(TargetTestIncludeDirGroup, TargetBuildIncludeDir) { include_compile.Build(); buildcc::m::TargetRunner(include_compile); - buildcc::internal::TargetLoader loader(NAME, intermediate_path); - bool is_loaded = loader.Load(); + buildcc::internal::TargetSerialization serialization( + intermediate_path / (std::string(NAME) + ".bin")); + bool is_loaded = serialization.LoadFromFile(); CHECK_TRUE(is_loaded); - const auto &loaded_sources = loader.GetLoadedSources(); - const auto &loaded_dirs = loader.GetLoadedIncludeDirs(); + const auto &loaded_sources = + serialization.GetLoad().sources.GetUnorderedPathInfos(); + const auto &loaded_dirs = serialization.GetLoad().include_dirs.GetPaths(); CHECK_EQUAL(loaded_sources.size(), 2); CHECK_EQUAL(loaded_dirs.size(), 2); @@ -158,8 +167,11 @@ TEST(TargetTestIncludeDirGroup, TargetBuildIncludeDir) { CHECK_FALSE(loaded_sources.find(dummy_c_file) == loaded_sources.end()); CHECK_FALSE(loaded_sources.find(include_header_file) == loaded_sources.end()); - CHECK_FALSE(loaded_dirs.find(include_header_path.string()) == - loaded_dirs.end()); + + std::unordered_set unordered_loaded_dirs(loaded_dirs.begin(), + loaded_dirs.end()); + CHECK_FALSE(unordered_loaded_dirs.find(include_header_path) == + unordered_loaded_dirs.end()); } { // * Remove include directory @@ -175,11 +187,13 @@ TEST(TargetTestIncludeDirGroup, TargetBuildIncludeDir) { include_compile.Build(); buildcc::m::TargetRunner(include_compile); - buildcc::internal::TargetLoader loader(NAME, intermediate_path); - bool is_loaded = loader.Load(); + buildcc::internal::TargetSerialization serialization( + intermediate_path / (std::string(NAME) + ".bin")); + bool is_loaded = serialization.LoadFromFile(); CHECK_TRUE(is_loaded); - const auto &loaded_sources = loader.GetLoadedSources(); - const auto &loaded_dirs = loader.GetLoadedIncludeDirs(); + const auto &loaded_sources = + serialization.GetLoad().sources.GetUnorderedPathInfos(); + const auto &loaded_dirs = serialization.GetLoad().include_dirs.GetPaths(); CHECK_EQUAL(loaded_sources.size(), 2); CHECK_EQUAL(loaded_dirs.size(), 1); @@ -187,8 +201,11 @@ TEST(TargetTestIncludeDirGroup, TargetBuildIncludeDir) { CHECK_FALSE(loaded_sources.find(dummy_c_file) == loaded_sources.end()); CHECK_FALSE(loaded_sources.find(include_header_file) == loaded_sources.end()); - CHECK_FALSE(loaded_dirs.find(include_header_path.string()) == - loaded_dirs.end()); + + std::unordered_set unordered_loaded_dirs(loaded_dirs.begin(), + loaded_dirs.end()); + CHECK_FALSE(unordered_loaded_dirs.find(include_header_path) == + unordered_loaded_dirs.end()); } mock().checkExpectations(); @@ -208,10 +225,6 @@ TEST(TargetTestIncludeDirGroup, TargetBuildHeaderFile) { // Delete fs::remove_all(intermediate_path); - auto dummy_c_file = - buildcc::internal::Path::CreateExistingPath((source_path / DUMMY_MAIN_C)); - auto include_header_file = buildcc::internal::Path::CreateExistingPath( - (source_path / INCLUDE_HEADER_SOURCE)); auto include_header_path = (source_path / RELATIVE_INCLUDE_DIR).make_preferred(); @@ -228,12 +241,14 @@ TEST(TargetTestIncludeDirGroup, TargetBuildHeaderFile) { add_header.Build(); buildcc::m::TargetRunner(add_header); - buildcc::internal::TargetLoader loader(NAME, intermediate_path); - bool is_loaded = loader.Load(); + buildcc::internal::TargetSerialization serialization( + intermediate_path / (std::string(NAME) + ".bin")); + bool is_loaded = serialization.LoadFromFile(); CHECK_TRUE(is_loaded); - CHECK_EQUAL(loader.GetLoadedSources().size(), 2); - CHECK_EQUAL(loader.GetLoadedIncludeDirs().size(), 1); - CHECK_EQUAL(loader.GetLoadedHeaders().size(), 0); + CHECK_EQUAL(serialization.GetLoad().sources.GetUnorderedPathInfos().size(), + 2); + CHECK_EQUAL(serialization.GetLoad().include_dirs.GetPaths().size(), 1); + CHECK_EQUAL(serialization.GetLoad().headers.GetPaths().size(), 0); } // Add header @@ -251,12 +266,14 @@ TEST(TargetTestIncludeDirGroup, TargetBuildHeaderFile) { add_header.Build(); buildcc::m::TargetRunner(add_header); - buildcc::internal::TargetLoader loader(NAME, intermediate_path); - bool is_loaded = loader.Load(); + buildcc::internal::TargetSerialization serialization( + intermediate_path / (std::string(NAME) + ".bin")); + bool is_loaded = serialization.LoadFromFile(); CHECK_TRUE(is_loaded); - CHECK_EQUAL(loader.GetLoadedSources().size(), 2); - CHECK_EQUAL(loader.GetLoadedIncludeDirs().size(), 1); - CHECK_EQUAL(loader.GetLoadedHeaders().size(), 1); + CHECK_EQUAL(serialization.GetLoad().sources.GetUnorderedPathInfos().size(), + 2); + CHECK_EQUAL(serialization.GetLoad().include_dirs.GetPaths().size(), 1); + CHECK_EQUAL(serialization.GetLoad().headers.GetPaths().size(), 1); } // Update header @@ -280,12 +297,14 @@ TEST(TargetTestIncludeDirGroup, TargetBuildHeaderFile) { add_header.Build(); buildcc::m::TargetRunner(add_header); - buildcc::internal::TargetLoader loader(NAME, intermediate_path); - bool is_loaded = loader.Load(); + buildcc::internal::TargetSerialization serialization( + intermediate_path / (std::string(NAME) + ".bin")); + bool is_loaded = serialization.LoadFromFile(); CHECK_TRUE(is_loaded); - CHECK_EQUAL(loader.GetLoadedSources().size(), 2); - CHECK_EQUAL(loader.GetLoadedIncludeDirs().size(), 1); - CHECK_EQUAL(loader.GetLoadedHeaders().size(), 1); + CHECK_EQUAL(serialization.GetLoad().sources.GetUnorderedPathInfos().size(), + 2); + CHECK_EQUAL(serialization.GetLoad().include_dirs.GetPaths().size(), 1); + CHECK_EQUAL(serialization.GetLoad().headers.GetPaths().size(), 1); } // Remove header @@ -302,19 +321,21 @@ TEST(TargetTestIncludeDirGroup, TargetBuildHeaderFile) { add_header.Build(); buildcc::m::TargetRunner(add_header); - buildcc::internal::TargetLoader loader(NAME, intermediate_path); - bool is_loaded = loader.Load(); + buildcc::internal::TargetSerialization serialization( + intermediate_path / (std::string(NAME) + ".bin")); + bool is_loaded = serialization.LoadFromFile(); CHECK_TRUE(is_loaded); - CHECK_EQUAL(loader.GetLoadedSources().size(), 2); - CHECK_EQUAL(loader.GetLoadedIncludeDirs().size(), 1); - CHECK_EQUAL(loader.GetLoadedHeaders().size(), 0); + CHECK_EQUAL(serialization.GetLoad().sources.GetUnorderedPathInfos().size(), + 2); + CHECK_EQUAL(serialization.GetLoad().include_dirs.GetPaths().size(), 1); + CHECK_EQUAL(serialization.GetLoad().headers.GetPaths().size(), 0); } mock().checkExpectations(); } int main(int ac, char **av) { - buildcc::env::init(BUILD_SCRIPT_SOURCE, - BUILD_TARGET_INCLUDE_DIR_INTERMEDIATE_DIR); + buildcc::Project::Init(BUILD_SCRIPT_SOURCE, + BUILD_TARGET_INCLUDE_DIR_INTERMEDIATE_DIR); return CommandLineTestRunner::RunAllTests(ac, av); } 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 4d2cf140..21078e7d 100644 --- a/buildcc/lib/target/test/target/test_target_lib_dep.cpp +++ b/buildcc/lib/target/test/target/test_target_lib_dep.cpp @@ -5,14 +5,14 @@ #include "expect_command.h" #include "expect_target.h" +#include "test_target_util.h" #include "target/target.h" // -#include "target/base/target_loader.h" +#include "schema/target_serialization.h" #include -#include // NOTE, Make sure all these includes are AFTER the system and header includes #include "CppUTest/CommandLineTestRunner.h" @@ -30,8 +30,9 @@ TEST_GROUP(TargetTestLibDep) }; // clang-format on -static const buildcc::Toolchain gcc(buildcc::Toolchain::Id::Gcc, "gcc", "as", - "gcc", "g++", "ar", "ld"); +static buildcc::Toolchain gcc(buildcc::ToolchainId::Gcc, "gcc", + buildcc::ToolchainExecutables("as", "gcc", "g++", + "ar", "ld")); static const fs::path intermediate_path = fs::path(BUILD_TARGET_LIB_DEP_INTERMEDIATE_DIR) / gcc.GetName(); @@ -54,13 +55,13 @@ TEST(TargetTestLibDep, StaticLibrary_SimpleBuildTest) { mock().checkExpectations(); // Verify binary - buildcc::internal::TargetLoader loader(STATIC_NAME, - foolib.GetTargetBuildDir()); - bool loaded = loader.Load(); + buildcc::internal::TargetSerialization serialization( + foolib.GetTargetBuildDir() / (std::string(STATIC_NAME) + ".bin")); + bool loaded = serialization.LoadFromFile(); CHECK_TRUE(loaded); - CHECK_EQUAL(loader.GetLoadedSources().size(), 1); - CHECK_EQUAL(loader.GetLoadedIncludeDirs().size(), 1); + CHECK_EQUAL(serialization.GetLoad().sources.GetPathInfos().size(), 1); + CHECK_EQUAL(serialization.GetLoad().include_dirs.GetPaths().size(), 1); } TEST(TargetTestLibDep, TargetDep_RebuildTest) { @@ -234,7 +235,7 @@ TEST(TargetTestLibDep, TargetDep_UpdateExistingLibraryTest) { buildcc::m::TargetRunner(foolib); // * To make sure that save_file is newer - sleep(1); + buildcc::m::blocking_sleep(1); bool saved = buildcc::env::save_file( foolib.GetTargetPath().string().c_str(), std::string{""}, false); CHECK_TRUE(saved); @@ -255,7 +256,7 @@ TEST(TargetTestLibDep, TargetDep_UpdateExistingLibraryTest) { } int main(int ac, char **av) { - buildcc::env::init(BUILD_SCRIPT_SOURCE, - BUILD_TARGET_LIB_DEP_INTERMEDIATE_DIR); + buildcc::Project::Init(BUILD_SCRIPT_SOURCE, + BUILD_TARGET_LIB_DEP_INTERMEDIATE_DIR); return CommandLineTestRunner::RunAllTests(ac, av); } diff --git a/buildcc/lib/target/test/target/test_target_lock.cpp b/buildcc/lib/target/test/target/test_target_lock.cpp deleted file mode 100644 index 944e5b10..00000000 --- a/buildcc/lib/target/test/target/test_target_lock.cpp +++ /dev/null @@ -1,124 +0,0 @@ -#include "constants.h" - -#include "expect_command.h" -#include "expect_target.h" - -#include "target/target.h" - -#include "env/env.h" - -// Third Party - -// 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(TargetTestLock) -{ - void teardown() { - mock().clear(); - } -}; -// clang-format on - -static const buildcc::Toolchain gcc(buildcc::Toolchain::Id::Gcc, "gcc", "as", - "gcc", "g++", "ar", "ld"); - -TEST(TargetTestLock, LockState) { - constexpr const char *const NAME = "LockState.exe"; - buildcc::BaseTarget exe(NAME, buildcc::TargetType::Executable, gcc, "data"); - - CHECK_FALSE(exe.IsLocked()); - - buildcc::env::m::CommandExpect_Execute(1, true); - exe.Build(); - buildcc::m::TargetRunner(exe); - - CHECK_TRUE(exe.IsLocked()); - - mock().checkExpectations(); -} - -TEST(TargetTestLock, Lock_Build) { - constexpr const char *const NAME = "Lock_Build.exe"; - buildcc::BaseTarget exe(NAME, buildcc::TargetType::Executable, gcc, "data"); - - buildcc::env::m::CommandExpect_Execute(1, true); - exe.Build(); - buildcc::m::TargetRunner(exe); - - CHECK_THROWS(std::exception, exe.Build()); - - mock().checkExpectations(); -} - -TEST(TargetTestLock, Lock_APIs) { - constexpr const char *const NAME = "Lock_APIs.exe"; - buildcc::BaseTarget exe(NAME, buildcc::TargetType::Executable, gcc, "data"); - - buildcc::env::m::CommandExpect_Execute(1, true); - exe.Build(); - buildcc::m::TargetRunner(exe); - - mock().checkExpectations(); - - // Sources - CHECK_THROWS(std::exception, exe.AddSource("dummy_main.c")); - CHECK_THROWS(std::exception, exe.GlobSources("")); - - // Headers & Dirs - CHECK_THROWS(std::exception, exe.AddHeader("include/include_header.h")); - CHECK_THROWS(std::exception, exe.GlobHeaders("include/")); - CHECK_THROWS(std::exception, exe.AddIncludeDir("include/")); - CHECK_THROWS(std::exception, exe.AddLibDir("include/")); - - // LibDep - CHECK_THROWS(std::exception, exe.AddLibDep("-lpthread")); - CHECK_THROWS(std::exception, exe.AddLibDep(exe)); - - // Flags - CHECK_THROWS(std::exception, exe.AddPreprocessorFlag("-DTESTING=TRUE")); - CHECK_THROWS(std::exception, exe.AddCommonCompileFlag("-Os")); - CHECK_THROWS(std::exception, exe.AddAsmCompileFlag("-march=arm")); - CHECK_THROWS(std::exception, exe.AddCCompileFlag("-std=c11")); - CHECK_THROWS(std::exception, exe.AddCppCompileFlag("-std=c++17")); - CHECK_THROWS(std::exception, exe.AddLinkFlag("-nostd")); - - // Rebuild - CHECK_THROWS(std::exception, exe.AddCompileDependency("dummy_main.c")); - CHECK_THROWS(std::exception, exe.AddLinkDependency("dummy_main.c")); -} - -TEST(TargetTestLock, Unlock_APIs) { - constexpr const char *const NAME = "Unlock_APIs.exe"; - buildcc::BaseTarget exe(NAME, buildcc::TargetType::Executable, gcc, "data"); - - CHECK_THROWS(std::exception, - exe.GetCompileCommand(exe.GetTargetRootDir() / "dummy_main.c")); - CHECK_THROWS(std::exception, exe.GetLinkCommand()); - CHECK_THROWS(std::exception, exe.GetTaskflow()); - - exe.AddSource("dummy_main.c"); - buildcc::env::m::CommandExpect_Execute(1, true); - buildcc::env::m::CommandExpect_Execute(1, true); - exe.Build(); - buildcc::m::TargetRunner(exe); - mock().checkExpectations(); - - exe.GetCompileCommand(exe.GetTargetRootDir() / "dummy_main.c"); - exe.GetLinkCommand(); - exe.GetTaskflow(); -} - -int main(int ac, char **av) { - const fs::path target_source_intermediate_path = - fs::path(BUILD_TARGET_LOCK_INTERMEDIATE_DIR) / gcc.GetName(); - fs::remove_all(target_source_intermediate_path); - - buildcc::env::init(BUILD_SCRIPT_SOURCE, BUILD_TARGET_LOCK_INTERMEDIATE_DIR); - return CommandLineTestRunner::RunAllTests(ac, av); -} diff --git a/buildcc/lib/target/test/target/test_target_pch.cpp b/buildcc/lib/target/test/target/test_target_pch.cpp index 4ce19edf..57385eb5 100644 --- a/buildcc/lib/target/test/target/test_target_pch.cpp +++ b/buildcc/lib/target/test/target/test_target_pch.cpp @@ -1,11 +1,10 @@ #include -#include - #include "constants.h" #include "expect_command.h" #include "expect_target.h" +#include "test_target_util.h" #include "target/target.h" @@ -26,13 +25,15 @@ TEST_GROUP(TargetPchTestGroup) { void teardown() { + mock().checkExpectations(); mock().clear(); } }; // clang-format on -static const buildcc::Toolchain gcc(buildcc::Toolchain::Id::Gcc, "gcc", "as", - "gcc", "g++", "ar", "ld"); +static buildcc::Toolchain gcc(buildcc::ToolchainId::Gcc, "gcc", + buildcc::ToolchainExecutables("as", "gcc", "g++", + "ar", "ld")); TEST(TargetPchTestGroup, Target_AddPch) { constexpr const char *const NAME = "AddPch.exe"; @@ -128,7 +129,7 @@ TEST(TargetPchTestGroup, Target_AddPch_Rebuild) { // Rebuild: Updated { - sleep(1); + buildcc::m::blocking_sleep(1); fs::path filename = fs::path(BUILD_SCRIPT_SOURCE) / "data" / "pch/pch_header_1.h"; bool save = buildcc::env::save_file(filename.string().c_str(), "", false); @@ -221,7 +222,7 @@ TEST(TargetPchTestGroup, Target_AddPch_CppRebuild) { // Rebuild: Updated { - sleep(1); + buildcc::m::blocking_sleep(1); fs::path filename = fs::path(BUILD_SCRIPT_SOURCE) / "data" / "pch/pch_header_1.h"; bool save = buildcc::env::save_file(filename.string().c_str(), "", false); @@ -246,12 +247,245 @@ TEST(TargetPchTestGroup, Target_AddPch_CppRebuild) { mock().checkExpectations(); } -TEST(TargetPchTestGroup, Target_AddPchCompileFlag_Build) { - constexpr const char *const NAME = "AddPchCompileFlag_Build.exe"; +TEST(TargetPchTestGroup, Target_AddPch_IncludeDirsRebuild) { + constexpr const char *const NAME = "AddPch_IncludeDirsRebuild.exe"; + + { + buildcc::BaseTarget target(NAME, buildcc::TargetType::Executable, gcc, + "data"); + target.AddPch("pch/pch_header_1.h"); + target.AddIncludeDir("pch"); + + buildcc::env::m::CommandExpect_Execute(1, true); + buildcc::env::m::CommandExpect_Execute(1, true); + target.Build(); + buildcc::m::TargetRunner(target); + bool exists = fs::exists(target.GetPchHeaderPath()); + CHECK_TRUE(exists); + } + + // No Change + { + buildcc::BaseTarget target(NAME, buildcc::TargetType::Executable, gcc, + "data"); + target.AddPch("pch/pch_header_1.h"); + target.AddIncludeDir("pch"); + + target.Build(); + buildcc::m::TargetRunner(target); + bool exists = fs::exists(target.GetPchHeaderPath()); + CHECK_TRUE(exists); + } + + // Remove + { + buildcc::BaseTarget target(NAME, buildcc::TargetType::Executable, gcc, + "data"); + target.AddPch("pch/pch_header_1.h"); + // target.AddIncludeDir("pch"); + + buildcc::m::TargetExpect_DirChanged(1, &target); + buildcc::env::m::CommandExpect_Execute(1, true); + buildcc::env::m::CommandExpect_Execute(1, true); + target.Build(); + buildcc::m::TargetRunner(target); + bool exists = fs::exists(target.GetPchHeaderPath()); + CHECK_TRUE(exists); + } + + // Added + { + buildcc::BaseTarget target(NAME, buildcc::TargetType::Executable, gcc, + "data"); + target.AddPch("pch/pch_header_1.h"); + target.AddIncludeDir("pch"); + + buildcc::m::TargetExpect_DirChanged(1, &target); + buildcc::env::m::CommandExpect_Execute(1, true); + buildcc::env::m::CommandExpect_Execute(1, true); + target.Build(); + buildcc::m::TargetRunner(target); + bool exists = fs::exists(target.GetPchHeaderPath()); + CHECK_TRUE(exists); + } +} + +TEST(TargetPchTestGroup, Target_AddPch_HeadersRebuild) { + constexpr const char *const NAME = "AddPch_HeadersRebuild.exe"; + + { + buildcc::BaseTarget target(NAME, buildcc::TargetType::Executable, gcc, + "data"); + target.AddPch("pch/pch_header_1.h"); + target.AddHeader("pch/pch_header_1.h"); + + buildcc::env::m::CommandExpect_Execute(1, true); + buildcc::env::m::CommandExpect_Execute(1, true); + target.Build(); + buildcc::m::TargetRunner(target); + bool exists = fs::exists(target.GetPchHeaderPath()); + CHECK_TRUE(exists); + } + + // No Change + { + buildcc::BaseTarget target(NAME, buildcc::TargetType::Executable, gcc, + "data"); + target.AddPch("pch/pch_header_1.h"); + target.AddHeader("pch/pch_header_1.h"); + + target.Build(); + buildcc::m::TargetRunner(target); + bool exists = fs::exists(target.GetPchHeaderPath()); + CHECK_TRUE(exists); + } + + // Remove + { + buildcc::BaseTarget target(NAME, buildcc::TargetType::Executable, gcc, + "data"); + target.AddPch("pch/pch_header_1.h"); + // target.AddHeader("pch/pch_header_1.h"); + + buildcc::m::TargetExpect_PathChanged(1, &target); + buildcc::env::m::CommandExpect_Execute(1, true); + buildcc::env::m::CommandExpect_Execute(1, true); + target.Build(); + buildcc::m::TargetRunner(target); + bool exists = fs::exists(target.GetPchHeaderPath()); + CHECK_TRUE(exists); + } + + // Added + { + buildcc::BaseTarget target(NAME, buildcc::TargetType::Executable, gcc, + "data"); + target.AddPch("pch/pch_header_1.h"); + target.AddHeader("pch/pch_header_1.h"); + + buildcc::m::TargetExpect_PathChanged(1, &target); + buildcc::env::m::CommandExpect_Execute(1, true); + buildcc::env::m::CommandExpect_Execute(1, true); + target.Build(); + buildcc::m::TargetRunner(target); + bool exists = fs::exists(target.GetPchHeaderPath()); + CHECK_TRUE(exists); + } +} + +TEST(TargetPchTestGroup, Target_AddPchs_FlagsRebuild) { + constexpr const char *const NAME = "Target_AddPchs_FlagsRebuild.exe"; + + { + buildcc::BaseTarget target(NAME, buildcc::TargetType::Executable, gcc, + "data"); + target.AddPch("pch/pch_header_1.h"); + + buildcc::env::m::CommandExpect_Execute(1, true); + buildcc::env::m::CommandExpect_Execute(1, true); + target.Build(); + buildcc::m::TargetRunner(target); + bool exists = fs::exists(target.GetPchHeaderPath()); + CHECK_TRUE(exists); + } + + // Added Preprocessor flag + { + buildcc::BaseTarget target(NAME, buildcc::TargetType::Executable, gcc, + "data"); + target.AddPreprocessorFlag("-H"); + target.AddPch("pch/pch_header_1.h"); + + buildcc::m::TargetExpect_FlagChanged(1, &target); + buildcc::env::m::CommandExpect_Execute(1, true); + buildcc::env::m::CommandExpect_Execute(1, true); + target.Build(); + buildcc::m::TargetRunner(target); + bool exists = fs::exists(target.GetPchHeaderPath()); + CHECK_TRUE(exists); + } + + // Added CommonCompileFlag + { + buildcc::BaseTarget target(NAME, buildcc::TargetType::Executable, gcc, + "data"); + target.AddPreprocessorFlag("-H"); + target.AddCommonCompileFlag("-H"); + target.AddPch("pch/pch_header_1.h"); + + buildcc::m::TargetExpect_FlagChanged(1, &target); + buildcc::env::m::CommandExpect_Execute(1, true); + buildcc::env::m::CommandExpect_Execute(1, true); + target.Build(); + buildcc::m::TargetRunner(target); + bool exists = fs::exists(target.GetPchHeaderPath()); + CHECK_TRUE(exists); + } + + // Added PchCompileFlag + { + buildcc::BaseTarget target(NAME, buildcc::TargetType::Executable, gcc, + "data"); + target.AddPreprocessorFlag("-H"); + target.AddCommonCompileFlag("-H"); + target.AddPchCompileFlag("-H"); + target.AddPch("pch/pch_header_1.h"); + + buildcc::m::TargetExpect_FlagChanged(1, &target); + buildcc::env::m::CommandExpect_Execute(1, true); + buildcc::env::m::CommandExpect_Execute(1, true); + target.Build(); + buildcc::m::TargetRunner(target); + bool exists = fs::exists(target.GetPchHeaderPath()); + CHECK_TRUE(exists); + } + + // Added CCompileFlag + { + buildcc::BaseTarget target(NAME, buildcc::TargetType::Executable, gcc, + "data"); + target.AddPreprocessorFlag("-H"); + target.AddCommonCompileFlag("-H"); + target.AddPchCompileFlag("-H"); + target.AddCCompileFlag("-H"); + target.AddPch("pch/pch_header_1.h"); + + buildcc::m::TargetExpect_FlagChanged(1, &target); + buildcc::env::m::CommandExpect_Execute(1, true); + buildcc::env::m::CommandExpect_Execute(1, true); + target.Build(); + buildcc::m::TargetRunner(target); + bool exists = fs::exists(target.GetPchHeaderPath()); + CHECK_TRUE(exists); + } + + // Keep CCompileFlag, Added CppCompileFlag + { + buildcc::BaseTarget target(NAME, buildcc::TargetType::Executable, gcc, + "data"); + target.AddPreprocessorFlag("-H"); + target.AddCommonCompileFlag("-H"); + target.AddPchCompileFlag("-H"); + target.AddCCompileFlag("-H"); + target.AddCppCompileFlag("-H"); + target.AddPch("pch/pch_header_1.h"); + + buildcc::m::TargetExpect_FlagChanged(1, &target); + buildcc::env::m::CommandExpect_Execute(1, true); + buildcc::env::m::CommandExpect_Execute(1, true); + target.Build(); + buildcc::m::TargetRunner(target); + bool exists = fs::exists(target.GetPchHeaderPath()); + CHECK_TRUE(exists); + } +} + +TEST(TargetPchTestGroup, Target_AddPchObjectFlag_Build) { + constexpr const char *const NAME = "AddPchObjectFlag_Build.exe"; buildcc::BaseTarget target(NAME, buildcc::TargetType::Executable, gcc, "data"); - target.AddPchCompileFlag("-H"); + target.AddPchObjectFlag("-H"); target.AddPch("pch/pch_header_1.h"); target.AddPch("pch/pch_header_2.h"); @@ -261,27 +495,30 @@ TEST(TargetPchTestGroup, Target_AddPchCompileFlag_Build) { buildcc::m::TargetRunner(target); bool exists = fs::exists(target.GetPchHeaderPath()); CHECK_TRUE(exists); - CHECK_EQUAL(target.GetPchCompileFlags().size(), 1); + CHECK_EQUAL(target.GetPchObjectFlags().size(), 1); mock().checkExpectations(); } -TEST(TargetPchTestGroup, Target_AddPchObjectFlag_Build) { - constexpr const char *const NAME = "AddPchObjectFlag_Build.exe"; - +TEST(TargetPchTestGroup, Target_BadPch) { + constexpr const char *const NAME = "Target_BadPch.exe"; buildcc::BaseTarget target(NAME, buildcc::TargetType::Executable, gcc, "data"); - target.AddPchObjectFlag("-H"); target.AddPch("pch/pch_header_1.h"); target.AddPch("pch/pch_header_2.h"); - buildcc::env::m::CommandExpect_Execute(1, true); - buildcc::env::m::CommandExpect_Execute(1, true); + buildcc::env::set_task_state(buildcc::env::TaskState::FAILURE); + target.Build(); buildcc::m::TargetRunner(target); bool exists = fs::exists(target.GetPchHeaderPath()); - CHECK_TRUE(exists); - CHECK_EQUAL(target.GetPchObjectFlags().size(), 1); + CHECK_FALSE(exists); + + buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); + + // Save file + exists = fs::exists(target.GetPchHeaderPath()); + CHECK_FALSE(exists); mock().checkExpectations(); } @@ -291,6 +528,7 @@ int main(int ac, char **av) { fs::path(BUILD_TARGET_PCH_INTERMEDIATE_DIR) / gcc.GetName(); fs::remove_all(target_source_intermediate_path); - buildcc::env::init(BUILD_SCRIPT_SOURCE, BUILD_TARGET_PCH_INTERMEDIATE_DIR); + buildcc::Project::Init(BUILD_SCRIPT_SOURCE, + BUILD_TARGET_PCH_INTERMEDIATE_DIR); return CommandLineTestRunner::RunAllTests(ac, av); } diff --git a/buildcc/lib/target/test/target/test_target_source.cpp b/buildcc/lib/target/test/target/test_target_source.cpp index c68f6ea4..4e34eb28 100644 --- a/buildcc/lib/target/test/target/test_target_source.cpp +++ b/buildcc/lib/target/test/target/test_target_source.cpp @@ -1,9 +1,8 @@ #include "constants.h" -#include - #include "expect_command.h" #include "expect_target.h" +#include "test_target_util.h" #include "target/target.h" @@ -28,8 +27,9 @@ TEST_GROUP(TargetTestSourceGroup) }; // clang-format on -static const buildcc::Toolchain gcc(buildcc::Toolchain::Id::Gcc, "gcc", "as", - "gcc", "g++", "ar", "ld"); +static buildcc::Toolchain gcc(buildcc::ToolchainId::Gcc, "gcc", + buildcc::ToolchainExecutables("as", "gcc", "g++", + "ar", "ld")); static const fs::path target_source_intermediate_path = fs::path(BUILD_TARGET_SOURCE_INTERMEDIATE_DIR) / gcc.GetName(); @@ -103,18 +103,19 @@ TEST(TargetTestSourceGroup, Target_Build_SourceCompile) { buildcc::env::m::CommandExpect_Execute(1, true); // link buildcc::m::TargetRunner(simple); - CHECK(simple.GetTaskState() == buildcc::env::TaskState::SUCCESS); + CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::SUCCESS); mock().checkExpectations(); - buildcc::internal::TargetLoader loader(NAME, intermediate_path); - bool is_loaded = loader.Load(); + buildcc::internal::TargetSerialization serialization(simple.GetBinaryPath()); + bool is_loaded = serialization.LoadFromFile(); CHECK_TRUE(is_loaded); - const auto &loaded_sources = loader.GetLoadedSources(); + const auto &loaded_sources = + serialization.GetLoad().sources.GetUnorderedPathInfos(); CHECK_EQUAL(loaded_sources.size(), 1); - auto dummy_file = buildcc::internal::Path::CreateExistingPath( - (source_path / DUMMY_MAIN).make_preferred().string()); + auto dummy_file = buildcc::internal::PathInfo::ToPathString( + fs::path(source_path / DUMMY_MAIN).string()); CHECK_FALSE(loaded_sources.find(dummy_file) == loaded_sources.end()); } @@ -129,12 +130,12 @@ TEST(TargetTestSourceGroup, Target_Build_SourceRecompile) { // Delete fs::remove_all(intermediate_path); - auto dummy_c_file = buildcc::internal::Path::CreateExistingPath( - (source_path / DUMMY_MAIN_C).make_preferred().string()); - auto dummy_cpp_file = buildcc::internal::Path::CreateExistingPath( - (source_path / DUMMY_MAIN_CPP).make_preferred().string()); - auto new_source_file = buildcc::internal::Path::CreateExistingPath( - (source_path / NEW_SOURCE).make_preferred().string()); + auto dummy_c_file = buildcc::internal::PathInfo::ToPathString( + (source_path / DUMMY_MAIN_C).string()); + auto dummy_cpp_file = buildcc::internal::PathInfo::ToPathString( + (source_path / DUMMY_MAIN_CPP).string()); + auto new_source_file = buildcc::internal::PathInfo::ToPathString( + (source_path / NEW_SOURCE).string()); { buildcc::BaseTarget simple(NAME, buildcc::TargetType::Executable, gcc, @@ -150,11 +151,13 @@ TEST(TargetTestSourceGroup, Target_Build_SourceRecompile) { simple.Build(); buildcc::m::TargetRunner(simple); - buildcc::internal::TargetLoader loader(NAME, intermediate_path); - bool is_loaded = loader.Load(); + buildcc::internal::TargetSerialization serialization( + simple.GetBinaryPath()); + bool is_loaded = serialization.LoadFromFile(); CHECK_TRUE(is_loaded); - const auto &loaded_sources = loader.GetLoadedSources(); + const auto &loaded_sources = + serialization.GetLoad().sources.GetUnorderedPathInfos(); CHECK_EQUAL(loaded_sources.size(), 2); CHECK_FALSE(loaded_sources.find(dummy_c_file) == loaded_sources.end()); @@ -181,17 +184,19 @@ TEST(TargetTestSourceGroup, Target_Build_SourceRecompile) { simple.Build(); buildcc::m::TargetRunner(simple); - buildcc::internal::TargetLoader loader(NAME, intermediate_path); - bool is_loaded = loader.Load(); + buildcc::internal::TargetSerialization serialization( + simple.GetBinaryPath()); + bool is_loaded = serialization.LoadFromFile(); CHECK_TRUE(is_loaded); - const auto &loaded_sources = loader.GetLoadedSources(); + const auto &loaded_sources = + serialization.GetLoad().sources.GetUnorderedPathInfos(); CHECK_EQUAL(loaded_sources.size(), 2); CHECK_FALSE(loaded_sources.find(dummy_cpp_file) == loaded_sources.end()); CHECK_FALSE(loaded_sources.find(new_source_file) == loaded_sources.end()); } { - sleep(1); + buildcc::m::blocking_sleep(1); // * Force copy to trigger recompile for NEW_SOURCE // *2 Current file is updated @@ -211,11 +216,13 @@ TEST(TargetTestSourceGroup, Target_Build_SourceRecompile) { simple.Build(); buildcc::m::TargetRunner(simple); - buildcc::internal::TargetLoader loader(NAME, intermediate_path); - bool is_loaded = loader.Load(); + buildcc::internal::TargetSerialization serialization( + simple.GetBinaryPath()); + bool is_loaded = serialization.LoadFromFile(); CHECK_TRUE(is_loaded); - const auto &loaded_sources = loader.GetLoadedSources(); + const auto &loaded_sources = + serialization.GetLoad().sources.GetUnorderedPathInfos(); CHECK_EQUAL(loaded_sources.size(), 2); CHECK_FALSE(loaded_sources.find(dummy_cpp_file) == loaded_sources.end()); CHECK_FALSE(loaded_sources.find(new_source_file) == loaded_sources.end()); @@ -240,10 +247,13 @@ TEST(TargetTestSourceGroup, Target_CompileCommand_Throws) { // Throws when you call CompileCommand before Build CHECK_THROWS(std::exception, simple.GetCompileCommand(p)); + // Link Command will be empty before Build + STRCMP_EQUAL(simple.GetLinkCommand().c_str(), ""); } } int main(int ac, char **av) { - buildcc::env::init(BUILD_SCRIPT_SOURCE, BUILD_TARGET_SOURCE_INTERMEDIATE_DIR); + buildcc::Project::Init(BUILD_SCRIPT_SOURCE, + BUILD_TARGET_SOURCE_INTERMEDIATE_DIR); return CommandLineTestRunner::RunAllTests(ac, av); } diff --git a/buildcc/lib/target/test/target/test_target_source_out_of_root.cpp b/buildcc/lib/target/test/target/test_target_source_out_of_root.cpp index e2155224..5381f659 100644 --- a/buildcc/lib/target/test/target/test_target_source_out_of_root.cpp +++ b/buildcc/lib/target/test/target/test_target_source_out_of_root.cpp @@ -26,8 +26,9 @@ TEST_GROUP(TargetTestSourceOutOfRootGroup) }; // clang-format on -static const buildcc::Toolchain gcc(buildcc::Toolchain::Id::Gcc, "gcc", "as", - "gcc", "g++", "ar", "ld"); +static buildcc::Toolchain gcc(buildcc::ToolchainId::Gcc, "gcc", + buildcc::ToolchainExecutables("as", "gcc", "g++", + "ar", "ld")); static const fs::path target_source_intermediate_path = fs::path(BUILD_TARGET_SOURCE_OUT_OF_ROOT_INTERMEDIATE_DIR) / gcc.GetName(); @@ -87,7 +88,7 @@ TEST(TargetTestSourceOutOfRootGroup, GlobAbsolute_OutOfRootSource) { int main(int ac, char **av) { std::filesystem::create_directories(fs::path(BUILD_SCRIPT_SOURCE) / "data" / "random dir"); - buildcc::env::init(fs::path(BUILD_SCRIPT_SOURCE) / "data" / "random dir", - BUILD_TARGET_SOURCE_OUT_OF_ROOT_INTERMEDIATE_DIR); + buildcc::Project::Init(fs::path(BUILD_SCRIPT_SOURCE) / "data" / "random dir", + BUILD_TARGET_SOURCE_OUT_OF_ROOT_INTERMEDIATE_DIR); return CommandLineTestRunner::RunAllTests(ac, av); } diff --git a/buildcc/lib/target/test/target/test_target_state.cpp b/buildcc/lib/target/test/target/test_target_state.cpp index 33900732..ec8806c9 100644 --- a/buildcc/lib/target/test/target/test_target_state.cpp +++ b/buildcc/lib/target/test/target/test_target_state.cpp @@ -1,5 +1,5 @@ -#include "target/common/target_file_ext.h" #include "target/common/target_state.h" +#include "toolchain/common/file_ext.h" // NOTE, Make sure all these includes are AFTER the system and header includes #include "CppUTest/CommandLineTestRunner.h" @@ -16,21 +16,21 @@ TEST_GROUP(TargetStateTestGroup) TEST(TargetStateTestGroup, SetSourceState) { buildcc::TargetState target_state; - CHECK_FALSE(target_state.contains_c); - target_state.SetSourceState(buildcc::TargetFileExt::C); - CHECK_TRUE(target_state.contains_c); + CHECK_FALSE(target_state.ContainsC()); + target_state.SourceDetected(buildcc::FileExt::C); + CHECK_TRUE(target_state.ContainsC()); - CHECK_FALSE(target_state.contains_cpp); - target_state.SetSourceState(buildcc::TargetFileExt::Cpp); - CHECK_TRUE(target_state.contains_cpp); + CHECK_FALSE(target_state.ContainsCpp()); + target_state.SourceDetected(buildcc::FileExt::Cpp); + CHECK_TRUE(target_state.ContainsCpp()); - CHECK_FALSE(target_state.contains_asm); - target_state.SetSourceState(buildcc::TargetFileExt::Asm); - CHECK_TRUE(target_state.contains_asm); + CHECK_FALSE(target_state.ContainsAsm()); + target_state.SourceDetected(buildcc::FileExt::Asm); + CHECK_TRUE(target_state.ContainsAsm()); // Ignored - target_state.SetSourceState(buildcc::TargetFileExt::Header); - target_state.SetSourceState(buildcc::TargetFileExt::Invalid); + target_state.SourceDetected(buildcc::FileExt::Header); + target_state.SourceDetected(buildcc::FileExt::Invalid); } int main(int ac, char **av) { diff --git a/buildcc/lib/target/test/target/test_target_sync.cpp b/buildcc/lib/target/test/target/test_target_sync.cpp index 9bceb565..50e7f207 100644 --- a/buildcc/lib/target/test/target/test_target_sync.cpp +++ b/buildcc/lib/target/test/target/test_target_sync.cpp @@ -19,8 +19,9 @@ TEST_GROUP(TargetTestSyncGroup) }; // clang-format on -buildcc::Toolchain gcc(buildcc::Toolchain::Id::Gcc, "gcc", "as", "gcc", "g++", - "ar", "ldd"); +static buildcc::Toolchain gcc(buildcc::ToolchainId::Gcc, "gcc", + buildcc::ToolchainExecutables("as", "gcc", "g++", + "ar", "ld")); TEST(TargetTestSyncGroup, CopyByConstRef) { buildcc::BaseTarget srcTarget("srcTarget", buildcc::TargetType::Executable, @@ -317,7 +318,8 @@ TEST(TargetTestSyncGroup, InsertCrash) { } int main(int ac, char **av) { - buildcc::env::init(BUILD_SCRIPT_SOURCE, BUILD_TARGET_SYNC_INTERMEDIATE_DIR); - fs::remove_all(buildcc::env::get_project_build_dir()); + buildcc::Project::Init(BUILD_SCRIPT_SOURCE, + BUILD_TARGET_SYNC_INTERMEDIATE_DIR); + fs::remove_all(buildcc::Project::GetBuildDir()); return CommandLineTestRunner::RunAllTests(ac, av); } diff --git a/buildcc/lib/target/test/target/test_target_user_deps.cpp b/buildcc/lib/target/test/target/test_target_user_deps.cpp index a6410a9a..686ca0d9 100644 --- a/buildcc/lib/target/test/target/test_target_user_deps.cpp +++ b/buildcc/lib/target/test/target/test_target_user_deps.cpp @@ -1,9 +1,8 @@ -#include - #include "constants.h" #include "expect_command.h" #include "expect_target.h" +#include "test_target_util.h" #include "target/target.h" @@ -28,8 +27,9 @@ TEST_GROUP(TargetTestUserDepsGroup) }; // clang-format on -static const buildcc::Toolchain gcc(buildcc::Toolchain::Id::Gcc, "gcc", "as", - "gcc", "g++", "ar", "ld"); +static buildcc::Toolchain gcc(buildcc::ToolchainId::Gcc, "gcc", + buildcc::ToolchainExecutables("as", "gcc", "g++", + "ar", "ld")); static const fs::path target_source_intermediate_path = fs::path(BUILD_TARGET_USER_DEPS_INTERMEDIATE_DIR) / gcc.GetName(); @@ -80,9 +80,9 @@ TEST(TargetTestUserDepsGroup, Target_Build_CompileDeps_Rebuild) { { // * To make sure that save_file is newer - sleep(1); + buildcc::m::blocking_sleep(1); const fs::path new_source = - buildcc::env::get_project_root_dir() / "data" / "new_source.cpp"; + buildcc::Project::GetRootDir() / "data" / "new_source.cpp"; std::string buf{""}; buildcc::env::save_file(new_source.string().c_str(), buf, false); } @@ -119,9 +119,9 @@ TEST(TargetTestUserDepsGroup, Target_Build_LinkDeps_Rebuild) { { // * To make sure that save_file is newer - sleep(1); + buildcc::m::blocking_sleep(1); const fs::path new_source = - buildcc::env::get_project_root_dir() / "data" / "new_source.cpp"; + buildcc::Project::GetRootDir() / "data" / "new_source.cpp"; std::string buf{""}; buildcc::env::save_file(new_source.string().c_str(), buf, false); } @@ -143,7 +143,7 @@ TEST(TargetTestUserDepsGroup, Target_Build_LinkDeps_Rebuild) { int main(int ac, char **av) { fs::remove_all(target_source_intermediate_path); - buildcc::env::init(BUILD_SCRIPT_SOURCE, - BUILD_TARGET_USER_DEPS_INTERMEDIATE_DIR); + buildcc::Project::Init(BUILD_SCRIPT_SOURCE, + BUILD_TARGET_USER_DEPS_INTERMEDIATE_DIR); return CommandLineTestRunner::RunAllTests(ac, av); } diff --git a/buildcc/lib/target/test/target/test_template_generator.cpp b/buildcc/lib/target/test/target/test_template_generator.cpp new file mode 100644 index 00000000..451c76ae --- /dev/null +++ b/buildcc/lib/target/test/target/test_template_generator.cpp @@ -0,0 +1,97 @@ +#include "target/template_generator.h" + +#include "expect_command.h" +#include "expect_custom_generator.h" +#include "test_target_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(TemplateGeneratorTestGroup) +{ + void teardown() { + mock().checkExpectations(); + mock().clear(); + buildcc::env::set_task_state(buildcc::env::TaskState::SUCCESS); + } +}; +// clang-format on + +fs::path BUILD_DIR = fs::current_path() / "intermediate" / "template_generator"; + +TEST(TemplateGeneratorTestGroup, Basic) { + buildcc::TemplateGenerator generator("basic", ""); + generator.Build(); + + buildcc::m::CustomGeneratorRunner(generator); +} + +TEST(TemplateGeneratorTestGroup, Basic_Parse) { + buildcc::TemplateGenerator generator("basic_parse", ""); + generator.AddPatterns({ + {"hello", "Hello"}, + {"world", "World"}, + }); + std::string parsed = generator.Parse("{hello} {world}"); + STRCMP_EQUAL(parsed.c_str(), "Hello World"); +} + +TEST(TemplateGeneratorTestGroup, Basic_InputParse) { + buildcc::TemplateGenerator generator("basic_inputparse", ""); + generator.AddPatterns({ + {"hello", "Hello"}, + {"world", "World"}, + }); + generator.AddTemplate("{current_root_dir}/template/default_values.txt.in", + "{current_build_dir}/default_values.txt"); + generator.AddTemplate("{current_root_dir}/template/hello_world.txt.in", + "{current_build_dir}/hello_world.txt"); + generator.Build(); + + buildcc::m::CustomGeneratorRunner(generator); + + CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::SUCCESS); +} + +TEST(TemplateGeneratorTestGroup, Basic_SaveFailure) { + constexpr const char *const NAME = "basic_save_failure"; + { + buildcc::TemplateGenerator generator(NAME, ""); + + fs::create_directories(generator.GetBuildDir() / "default_values.txt"); + + generator.AddTemplate("{current_root_dir}/template/default_values.txt.in", + "{current_build_dir}/default_values.txt"); + generator.Build(); + + buildcc::m::CustomGeneratorRunner(generator); + CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::FAILURE); + } +} + +TEST(TemplateGeneratorTestGroup, Basic_LoadFailure) { + constexpr const char *const NAME = "basic_load_failure"; + { + buildcc::TemplateGenerator generator(NAME, ""); + + fs::create_directories(generator.GetBuildDir() / "default_values.txt.in"); + + generator.AddTemplate("{current_build_dir}/default_values.txt.in", + "{current_build_dir}/default_values.txt"); + generator.Build(); + + buildcc::m::CustomGeneratorRunner(generator); + CHECK(buildcc::env::get_task_state() == buildcc::env::TaskState::FAILURE); + } +} + +int main(int ac, char **av) { + fs::remove_all(BUILD_DIR); + buildcc::Project::Init(fs::current_path() / "data", BUILD_DIR); + return CommandLineTestRunner::RunAllTests(ac, av); +} diff --git a/buildcc/lib/target/test/target/test_toolchain_flag_api.cpp b/buildcc/lib/target/test/target/test_toolchain_flag_api.cpp new file mode 100644 index 00000000..e48becfb --- /dev/null +++ b/buildcc/lib/target/test/target/test_toolchain_flag_api.cpp @@ -0,0 +1,51 @@ +#include "toolchain/toolchain.h" + +#include "target/target_info.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" + +// clang-format off +TEST_GROUP(ToolchainFlagApiTestGroup) +{ +}; +// clang-format on + +TEST(ToolchainFlagApiTestGroup, BasicTargetTest) { + buildcc::Toolchain toolchain( + buildcc::ToolchainId::Gcc, "gcc", + buildcc::ToolchainExecutables("as", "gcc", "g++", "ar", "ld")); + + toolchain.AddPreprocessorFlag("-preprocessor"); + toolchain.AddAsmCompileFlag("-asm"); + toolchain.AddPchCompileFlag("-pchcompile"); + toolchain.AddPchObjectFlag("-pchobject"); + toolchain.AddCommonCompileFlag("-common"); + toolchain.AddCCompileFlag("-c"); + toolchain.AddCppCompileFlag("-cpp"); + toolchain.AddLinkFlag("-link"); + + // TODO, Add this in later + // * We should lock our toolchain before using it + // { CHECK_THROWS(std::exception, (buildcc::TargetInfo(toolchain, ""))); } + + { + buildcc::TargetInfo targetinfo(toolchain, ""); + CHECK_EQUAL(targetinfo.GetPreprocessorFlags().size(), 1); + CHECK_EQUAL(targetinfo.GetAsmCompileFlags().size(), 1); + CHECK_EQUAL(targetinfo.GetPchCompileFlags().size(), 1); + CHECK_EQUAL(targetinfo.GetPchObjectFlags().size(), 1); + CHECK_EQUAL(targetinfo.GetCommonCompileFlags().size(), 1); + CHECK_EQUAL(targetinfo.GetCCompileFlags().size(), 1); + CHECK_EQUAL(targetinfo.GetCppCompileFlags().size(), 1); + CHECK_EQUAL(targetinfo.GetLinkFlags().size(), 1); + } +} + +int main(int ac, char **av) { + MemoryLeakWarningPlugin::turnOffNewDeleteOverloads(); + return CommandLineTestRunner::RunAllTests(ac, av); +} diff --git a/buildcc/lib/toolchain/CMakeLists.txt b/buildcc/lib/toolchain/CMakeLists.txt index edb0cc09..ff1def9e 100644 --- a/buildcc/lib/toolchain/CMakeLists.txt +++ b/buildcc/lib/toolchain/CMakeLists.txt @@ -1,8 +1,18 @@ set(TOOLCHAIN_SRCS - include/toolchain/toolchain.h + # COMMON + src/common/toolchain_config.cpp + include/toolchain/common/toolchain_config.h + include/toolchain/common/file_ext.h + # API + src/api/toolchain_find.cpp src/api/toolchain_verify.cpp + include/toolchain/api/toolchain_find.h include/toolchain/api/toolchain_verify.h + include/toolchain/api/flag_api.h + + src/toolchain/toolchain.cpp + include/toolchain/toolchain.h ) if (${TESTING}) add_library(mock_toolchain @@ -14,11 +24,32 @@ if (${TESTING}) target_compile_options(mock_toolchain PUBLIC ${TEST_COMPILE_FLAGS} ${BUILD_COMPILE_FLAGS}) target_link_options(mock_toolchain PUBLIC ${TEST_LINK_FLAGS} ${BUILD_LINK_FLAGS}) target_link_libraries(mock_toolchain PUBLIC - mock_env + mock_schema CppUTest CppUTestExt - gcov + ${TEST_LINK_LIBS} + ) + + add_executable(test_toolchain_id + test/test_toolchain_id.cpp + ) + target_link_libraries(test_toolchain_id PRIVATE + mock_toolchain + ) + + add_executable(test_toolchain_config + test/test_toolchain_config.cpp + ) + target_link_libraries(test_toolchain_config PRIVATE + mock_toolchain + ) + + add_executable(test_toolchain_find + test/test_toolchain_find.cpp + ) + target_link_libraries(test_toolchain_find PRIVATE + mock_toolchain ) add_executable(test_toolchain_verify @@ -28,6 +59,11 @@ if (${TESTING}) mock_toolchain ) + add_test(NAME test_toolchain_id COMMAND test_toolchain_id) + add_test(NAME test_toolchain_config COMMAND test_toolchain_config) + add_test(NAME test_toolchain_find COMMAND test_toolchain_find + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test + ) add_test(NAME test_toolchain_verify COMMAND test_toolchain_verify WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test ) @@ -53,7 +89,7 @@ if(${BUILDCC_BUILD_AS_INTERFACE}) $ ) target_link_libraries(toolchain PUBLIC - env + schema ) endif() diff --git a/buildcc/lib/toolchain/include/toolchain/api/flag_api.h b/buildcc/lib/toolchain/include/toolchain/api/flag_api.h new file mode 100644 index 00000000..7d79388f --- /dev/null +++ b/buildcc/lib/toolchain/include/toolchain/api/flag_api.h @@ -0,0 +1,113 @@ +/* + * 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 TOOLCHAIN_API_FLAG_API_H_ +#define TOOLCHAIN_API_FLAG_API_H_ + +#include +#include + +namespace buildcc::internal { + +// Requires +// TargetSchema +template class FlagApi { +public: + void AddPreprocessorFlag(const std::string &flag) { + auto &t = static_cast(*this); + t.user_.preprocessor_flags.push_back(flag); + } + + void AddCommonCompileFlag(const std::string &flag) { + auto &t = static_cast(*this); + t.user_.common_compile_flags.push_back(flag); + } + + void AddPchCompileFlag(const std::string &flag) { + auto &t = static_cast(*this); + t.user_.pch_compile_flags.push_back(flag); + } + + void AddPchObjectFlag(const std::string &flag) { + auto &t = static_cast(*this); + t.user_.pch_object_flags.push_back(flag); + } + + void AddAsmCompileFlag(const std::string &flag) { + auto &t = static_cast(*this); + t.user_.asm_compile_flags.push_back(flag); + } + + void AddCCompileFlag(const std::string &flag) { + auto &t = static_cast(*this); + t.user_.c_compile_flags.push_back(flag); + } + + void AddCppCompileFlag(const std::string &flag) { + auto &t = static_cast(*this); + t.user_.cpp_compile_flags.push_back(flag); + } + + void AddLinkFlag(const std::string &flag) { + auto &t = static_cast(*this); + t.user_.link_flags.push_back(flag); + } + + // Getters + const std::vector &GetPreprocessorFlags() const { + const auto &t = static_cast(*this); + return t.user_.preprocessor_flags; + } + + const std::vector &GetCommonCompileFlags() const { + const auto &t = static_cast(*this); + return t.user_.common_compile_flags; + } + + const std::vector &GetPchCompileFlags() const { + const auto &t = static_cast(*this); + return t.user_.pch_compile_flags; + } + + const std::vector &GetPchObjectFlags() const { + const auto &t = static_cast(*this); + return t.user_.pch_object_flags; + } + + const std::vector &GetAsmCompileFlags() const { + const auto &t = static_cast(*this); + return t.user_.asm_compile_flags; + } + + const std::vector &GetCCompileFlags() const { + const auto &t = static_cast(*this); + return t.user_.c_compile_flags; + } + + const std::vector &GetCppCompileFlags() const { + const auto &t = static_cast(*this); + return t.user_.cpp_compile_flags; + } + + const std::vector &GetLinkFlags() const { + const auto &t = static_cast(*this); + return t.user_.link_flags; + } +}; + +} // namespace buildcc::internal + +#endif diff --git a/buildcc/lib/toolchain/include/toolchain/api/toolchain_find.h b/buildcc/lib/toolchain/include/toolchain/api/toolchain_find.h new file mode 100644 index 00000000..d71f82c5 --- /dev/null +++ b/buildcc/lib/toolchain/include/toolchain/api/toolchain_find.h @@ -0,0 +1,61 @@ +/* + * 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 TOOLCHAIN_TOOLCHAIN_FIND_H_ +#define TOOLCHAIN_TOOLCHAIN_FIND_H_ + +#include +#include +#include +#include + +#include "schema/path.h" + +namespace fs = std::filesystem; + +namespace buildcc { + +/** + * @brief Configure the behaviour of Toolchain::Find API. By default searches + * the directories mentioned in the ENV{PATH} variable to find the toolchain. + * @param absolute_search_paths absolute_search_paths expect directories that + * are iterated for exact toolchain matches + * @param env_vars env_vars contain paths that are seperated by OS delimiter. + * These are converted to paths and searched similarly to absolute_search_paths + *
+ * NOTE: env_vars must contain single absolute paths or multiple absolute + * paths seperated by OS delimiter
+ * Example: [Windows] "absolute_path_1;absolute_path_2;..."
+ * Example: [Linux] "absolute_path_1:absolute_path_2:..."
+ */ +struct ToolchainFindConfig { + ToolchainFindConfig(const std::vector &env_vars = {"PATH"}, + const std::vector &absolute_search_paths = {}) + : env_vars(env_vars), absolute_search_paths(absolute_search_paths) {} + + std::vector env_vars; + std::vector absolute_search_paths; +}; + +template class ToolchainFind { +public: + std::vector + Find(const ToolchainFindConfig &config = ToolchainFindConfig()) const; +}; + +} // namespace buildcc + +#endif diff --git a/buildcc/lib/toolchain/include/toolchain/api/toolchain_verify.h b/buildcc/lib/toolchain/include/toolchain/api/toolchain_verify.h index a22277da..389b54d1 100644 --- a/buildcc/lib/toolchain/include/toolchain/api/toolchain_verify.h +++ b/buildcc/lib/toolchain/include/toolchain/api/toolchain_verify.h @@ -18,55 +18,21 @@ #define TOOLCHAIN_TOOLCHAIN_VERIFY_H_ #include -#include #include -#include "env/logging.h" - #include "fmt/format.h" -namespace fs = std::filesystem; +#include "env/logging.h" +#include "env/optional.h" -namespace buildcc { +#include "toolchain/common/toolchain_executables.h" +#include "toolchain/common/toolchain_id.h" -/** - * @brief Configure the behaviour of Toolchain::Verify API. By default searches - * the directories mentioned in the ENV{PATH} variable to find the toolchain. - * @param absolute_search_paths absolute_search_paths expect directories that - * are iterated for exact toolchain matches - * @param env_vars env_vars contain paths that are seperated by OS delimiter. - * These are converted to paths and searched similarly to absolute_search_paths - *
- * NOTE: env_vars must contain single absolute paths or multiple absolute - * paths seperated by OS delimiter
- * Example: [Windows] "absolute_path_1;absolute_path_2;..."
- * Example: [Linux] "absolute_path_1:absolute_path_2:..."
- * @param compiler_version Optionally supply a compiler version if multiple - * toolchains of the same family/id are installed
- * Example: [GCC/MinGW/Clang] {compiler} -dumpversion
- * Example: [MSVC] getenv(VSCMD_VER)
- * For [MSVC] make sure to use `vcvarsall.bat {flavour}` to activate your - * toolchain - * @param target_arch Optionally supply a target architecture if multiple - * toolchains of the same family/id are installed but target different platforms - *
- * Example: [GCC/MinGW/Clang] {compiler} -dumpmachine
- * Example: [MSVC] getenv(VSCMD_ARG_HOST_ARCH) + - * getenv(VSCMD_ARG_TGT_ARCH)
- * For [MSVC] make sure to use `vcvarsall.bat {flavour}` to activate your - * toolchain - * @param update Updates the toolchain with absolute paths once verified
- * If multiple toolchains are found, uses the first in the list - */ -struct VerifyToolchainConfig { - std::vector absolute_search_paths; - std::vector env_vars{"PATH"}; +#include "toolchain/api/toolchain_find.h" - std::optional compiler_version; - std::optional target_arch; +namespace fs = std::filesystem; - bool update{true}; -}; +namespace buildcc { /** * @brief Verified Toolchain information @@ -76,16 +42,22 @@ struct VerifyToolchainConfig { * @param compiler_version Compiler version of the verified toolchain * @param target_arch Target architecture of the verified toolchain */ -struct VerifiedToolchain { +struct ToolchainCompilerInfo { + std::string ToString() const { return fmt::format("{}", *this); } + fs::path path; std::string compiler_version; std::string target_arch; - - std::string ToString() const { return fmt::format("{}", *this); } }; +// clang-format off +using ToolchainInfoCb = std::function(const ToolchainExecutables &)>; +// clang-format on + template class ToolchainVerify { public: + ToolchainVerify() = default; + /** * @brief Verify your toolchain executables by searching your operating system * paths @@ -96,11 +68,17 @@ template class ToolchainVerify { * multiple toolchains of similar names with different versions. Collect all * of them */ - std::vector - Verify(const VerifyToolchainConfig &config = VerifyToolchainConfig()); + ToolchainCompilerInfo + Verify(const ToolchainFindConfig &config = ToolchainFindConfig()); + + /** + * @brief Set ToolchainInfo callback for run time objects + */ + void SetToolchainInfoCb(const ToolchainInfoCb &cb); + const ToolchainInfoCb &GetToolchainInfoCb() const; -protected: - VerifiedToolchain verified_toolchain_; +private: + ToolchainInfoCb info_cb_; }; } // namespace buildcc @@ -112,9 +90,9 @@ constexpr const char *const kVerifiedToolchainFormat = R"({{ }})"; template <> -struct fmt::formatter : formatter { +struct fmt::formatter : formatter { template - auto format(const buildcc::VerifiedToolchain &vt, FormatContext &ctx) { + auto format(const buildcc::ToolchainCompilerInfo &vt, FormatContext &ctx) { std::string verified_toolchain_info = fmt::format(kVerifiedToolchainFormat, vt.path.string(), vt.compiler_version, vt.target_arch); diff --git a/buildcc/lib/target/include/target/common/target_file_ext.h b/buildcc/lib/toolchain/include/toolchain/common/file_ext.h similarity index 88% rename from buildcc/lib/target/include/target/common/target_file_ext.h rename to buildcc/lib/toolchain/include/toolchain/common/file_ext.h index bab96289..64a35660 100644 --- a/buildcc/lib/target/include/target/common/target_file_ext.h +++ b/buildcc/lib/toolchain/include/toolchain/common/file_ext.h @@ -14,12 +14,12 @@ * limitations under the License. */ -#ifndef TARGET_COMMON_TARGET_FILE_EXT_H_ -#define TARGET_COMMON_TARGET_FILE_EXT_H_ +#ifndef TOOLCHAIN_COMMON_FILE_EXT_H_ +#define TOOLCHAIN_COMMON_FILE_EXT_H_ namespace buildcc { -enum class TargetFileExt { +enum class FileExt { Asm, ///< Valid Assembly source extension C, ///< Valid C source extension Cpp, ///< Valid Cpp source extension diff --git a/buildcc/lib/toolchain/include/toolchain/common/toolchain_config.h b/buildcc/lib/toolchain/include/toolchain/common/toolchain_config.h new file mode 100644 index 00000000..8cdcce3b --- /dev/null +++ b/buildcc/lib/toolchain/include/toolchain/common/toolchain_config.h @@ -0,0 +1,101 @@ +/* + * 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 TOOLCHAIN_COMMON_TOOLCHAIN_CONFIG_H_ +#define TOOLCHAIN_COMMON_TOOLCHAIN_CONFIG_H_ + +#include +#include +#include + +#include "toolchain/common/file_ext.h" + +namespace fs = std::filesystem; + +namespace buildcc { + +struct ToolchainConfig { + ToolchainConfig() = default; + + /** + * @brief Get the valid file extension from a path + * + * See ToolchainConfig::valid_c_ext, ToolchainConfig::valid_cpp_ext, + * ToolchainConfig::valid_asm_ext, ToolchainConfig::valid_header_ext + * + * @param filepath Absolute / Relative path of the file + * @return FileExt File path detected as per Toolchain::valid_* + * variables + */ + FileExt GetFileExt(const fs::path &filepath) const; + + /** + * @brief Checks for C/C++ source file validity. + * + * See ToolchainConfig::valid_c_ext, ToolchainConfig::valid_cpp_ext, + * ToolchainConfig::valid_asm_ext + * + * @param filepath Absolute / Relative path of file + * @return true If file extension belongs to the above valid_* list + * @return false If file extension does not belong to the above valid_* list + */ + bool IsValidSource(const fs::path &filepath) const; + + /** + * @brief Checks for Header file validity + * + * See ToolchainConfig::valid_header_ext + * + * @param filepath Absolute / Relative path of file + * @return true If file extension belongs to above valid_* list + * @return false If file extension does not belong to above valid_* list + */ + bool IsValidHeader(const fs::path &filepath) const; + + /** + * @brief Expects Source file validity + * + * env::assert_fatal if not a valid source + * + * @param filepath Absolute / Relative path of file + */ + void ExpectsValidSource(const fs::path &filepath) const; + + /** + * @brief Expects header file validity + * + * env::assert_fatal if not a valid header + * + * @param filepath Absolute / Relative path of file + */ + void ExpectsValidHeader(const fs::path &filepath) const; + + std::string obj_ext{".o"}; + std::string pch_header_ext{".h"}; + std::string pch_compile_ext{".gch"}; + + std::string prefix_include_dir{"-I"}; + std::string prefix_lib_dir{"-L"}; + + std::unordered_set valid_c_ext{".c"}; + std::unordered_set valid_cpp_ext{".cpp", ".cxx", ".cc"}; + std::unordered_set valid_asm_ext{".s", ".S", ".asm"}; + std::unordered_set valid_header_ext{".h", ".hpp"}; +}; + +} // namespace buildcc + +#endif diff --git a/buildcc/lib/toolchain/include/toolchain/common/toolchain_executables.h b/buildcc/lib/toolchain/include/toolchain/common/toolchain_executables.h new file mode 100644 index 00000000..b8862200 --- /dev/null +++ b/buildcc/lib/toolchain/include/toolchain/common/toolchain_executables.h @@ -0,0 +1,41 @@ +/* + * 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 TOOLCHAIN_COMMON_TOOLCHAIN_EXECUTABLES_H_ +#define TOOLCHAIN_COMMON_TOOLCHAIN_EXECUTABLES_H_ + +#include +#include + +namespace buildcc { + +struct ToolchainExecutables { + explicit ToolchainExecutables() = default; + explicit ToolchainExecutables(std::string_view as, std::string_view c, + std::string_view cpp, std::string_view ar, + std::string_view link) + : assembler(as), c_compiler(c), cpp_compiler(cpp), archiver(ar), + linker(link) {} + std::string assembler; + std::string c_compiler; + std::string cpp_compiler; + std::string archiver; + std::string linker; +}; + +} // namespace buildcc + +#endif diff --git a/buildcc/lib/toolchain/include/toolchain/common/toolchain_id.h b/buildcc/lib/toolchain/include/toolchain/common/toolchain_id.h new file mode 100644 index 00000000..d19ea6d6 --- /dev/null +++ b/buildcc/lib/toolchain/include/toolchain/common/toolchain_id.h @@ -0,0 +1,65 @@ +/* + * 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 TOOLCHAIN_COMMON_TOOLCHAIN_ID_H_ +#define TOOLCHAIN_COMMON_TOOLCHAIN_ID_H_ + +#include "fmt/format.h" + +namespace buildcc { + +enum class ToolchainId { + Gcc = 0, ///< GCC Toolchain + Msvc, ///< MSVC Toolchain + Clang, ///< Clang Toolchain + MinGW, ///< MinGW Toolchain (Similar to GCC, but for Windows) + Custom, ///< Custom Toolchain not defined in this list + Undefined, ///< Default value when unknown +}; + +} // namespace buildcc + +template <> +struct fmt::formatter : formatter { + template + auto format(buildcc::ToolchainId id, FormatContext &ctx) { + std::string id_name; + switch (id) { + case buildcc::ToolchainId::Gcc: + id_name = "Gcc"; + break; + case buildcc::ToolchainId::Msvc: + id_name = "Msvc"; + break; + case buildcc::ToolchainId::Clang: + id_name = "Clang"; + break; + case buildcc::ToolchainId::MinGW: + id_name = "MinGW"; + break; + case buildcc::ToolchainId::Custom: + id_name = "Custom"; + break; + case buildcc::ToolchainId::Undefined: + default: + id_name = "Undefined"; + break; + } + return formatter::format(id_name, ctx); + } +}; + +#endif diff --git a/buildcc/lib/toolchain/include/toolchain/toolchain.h b/buildcc/lib/toolchain/include/toolchain/toolchain.h index f46c3d41..71e38e0e 100644 --- a/buildcc/lib/toolchain/include/toolchain/toolchain.h +++ b/buildcc/lib/toolchain/include/toolchain/toolchain.h @@ -21,57 +21,81 @@ #include #include -#include "api/toolchain_verify.h" +#include "toolchain/common/toolchain_config.h" +#include "toolchain/common/toolchain_executables.h" +#include "toolchain/common/toolchain_id.h" + +#include "toolchain/api/flag_api.h" +#include "toolchain/api/toolchain_find.h" +#include "toolchain/api/toolchain_verify.h" namespace buildcc { // Base toolchain class -class Toolchain : public ToolchainVerify { +class Toolchain : public internal::FlagApi, + public ToolchainFind, + public ToolchainVerify { public: - enum class Id { - Gcc = 0, ///< GCC Toolchain - Msvc, ///< MSVC Toolchain - Clang, ///< Clang Toolchain - MinGW, ///< MinGW Toolchain (Similar to GCC, but for Windows) - Custom, ///< Custom Toolchain not defined in this list - Undefined, ///< Default value when unknown - }; + // TODO, Remove ToolchainId from here + Toolchain(ToolchainId id, std::string_view name, + const ToolchainExecutables &executables, + const ToolchainConfig &config = ToolchainConfig()) + : id_(id), name_(name), executables_(executables), config_(config) {} -public: - explicit Toolchain(Id id, std::string_view name, - std::string_view asm_compiler, std::string_view c_compiler, - std::string_view cpp_compiler, std::string_view archiver, - std::string_view linker) - : id_(id), name_(name), asm_compiler_(asm_compiler), - c_compiler_(c_compiler), cpp_compiler_(cpp_compiler), - archiver_(archiver), linker_(linker) {} - - Toolchain(Toolchain &&toolchain) = default; - Toolchain(const Toolchain &toolchain) = delete; + virtual ~Toolchain() = default; + Toolchain(Toolchain &&) = default; + Toolchain &operator=(Toolchain &&) = default; + Toolchain(const Toolchain &) = delete; + Toolchain &operator=(const Toolchain &) = delete; // Getters - Id GetId() const { return id_; } + ToolchainId GetId() const { return id_; } const std::string &GetName() const { return name_; } - const std::string &GetAsmCompiler() const { return asm_compiler_; } - const std::string &GetCCompiler() const { return c_compiler_; } - const std::string &GetCppCompiler() const { return cpp_compiler_; } - const std::string &GetArchiver() const { return archiver_; } - const std::string &GetLinker() const { return linker_; } + const std::string &GetAssembler() const { return executables_.assembler; } + const std::string &GetCCompiler() const { return executables_.c_compiler; } + const std::string &GetCppCompiler() const { + return executables_.cpp_compiler; + } + const std::string &GetArchiver() const { return executables_.archiver; } + const std::string &GetLinker() const { return executables_.linker; } + const ToolchainExecutables &GetToolchainExecutables() const { + return executables_; + } + + const ToolchainConfig &GetConfig() const { return config_; } + +protected: + ToolchainId &RefId() { return id_; } + std::string &RefName() { return name_; } + ToolchainExecutables &RefExecutables() { return executables_; } + ToolchainConfig &RefConfig() { return config_; } private: + struct UserSchema { + std::vector preprocessor_flags; + std::vector common_compile_flags; + std::vector pch_compile_flags; + std::vector pch_object_flags; + std::vector asm_compile_flags; + std::vector c_compile_flags; + std::vector cpp_compile_flags; + std::vector link_flags; + }; + +private: + friend class internal::FlagApi; friend class ToolchainVerify; private: - Id id_; + ToolchainId id_; std::string name_; - std::string asm_compiler_; - std::string c_compiler_; - std::string cpp_compiler_; - std::string archiver_; - std::string linker_; + ToolchainExecutables executables_; + ToolchainConfig config_; + + // + UserSchema user_; }; -typedef Toolchain::Id ToolchainId; typedef Toolchain BaseToolchain; } // namespace buildcc diff --git a/buildcc/lib/toolchain/src/api/toolchain_find.cpp b/buildcc/lib/toolchain/src/api/toolchain_find.cpp new file mode 100644 index 00000000..9c1e621e --- /dev/null +++ b/buildcc/lib/toolchain/src/api/toolchain_find.cpp @@ -0,0 +1,108 @@ +/* + * 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. + */ + +#include "toolchain/api/toolchain_find.h" + +#include "env/assert_fatal.h" +#include "env/host_os.h" +#include "env/host_os_util.h" +#include "env/util.h" + +#include "toolchain/toolchain.h" + +namespace { + +std::vector ParseEnvVarToPaths(const std::string &env_var) { + const char *path_env = getenv(env_var.c_str()); + buildcc::env::assert_fatal( + path_env != nullptr, + fmt::format("Environment variable '{}' not present", env_var)); + + constexpr const char *os_env_delim = buildcc::env::get_os_envvar_delim(); + buildcc::env::assert_fatal("OS not supported"); + std::vector paths = + buildcc::env::split(path_env, os_env_delim[0]); + + return paths; +} + +bool ContainsToolchainExecutables( + const fs::directory_iterator &directory_iterator, + const buildcc::ToolchainExecutables &executables) { + std::unordered_set exes( + {executables.assembler, executables.c_compiler, executables.cpp_compiler, + executables.archiver, executables.linker}); + std::error_code ec; + for (const auto &dir_iter : directory_iterator) { + bool is_regular_file = dir_iter.is_regular_file(ec); + if (!is_regular_file || ec) { + continue; + } + const auto &filename_without_ext = dir_iter.path().stem().string(); + // NOTE, Must match the entire filename + exes.erase(filename_without_ext); + } + return exes.empty(); +} + +} // namespace + +namespace buildcc { + +template +std::vector +ToolchainFind::Find(const ToolchainFindConfig &config) const { + // Initialization + const T &t = static_cast(*this); + std::vector found_toolchains; + std::vector absolute_search_paths(config.absolute_search_paths); + + // Parse config envs and add it to absolute search paths + for (const auto &env_var : config.env_vars) { + std::vector paths = ParseEnvVarToPaths(env_var); + absolute_search_paths.insert(absolute_search_paths.end(), paths.begin(), + paths.end()); + } + + // Over the absolute search paths + // - Check if directory exists + // - Iterate over directory + // - Find ALL Toolchain binaries in ONE directory + // - If matched, store that path + for (const auto &search_path : absolute_search_paths) { + if (!fs::exists(search_path)) { + continue; + } + + std::error_code ec; + auto directory_iterator = fs::directory_iterator(search_path, ec); + if (ec) { + continue; + } + + bool toolchains_matched = ContainsToolchainExecutables( + directory_iterator, t.GetToolchainExecutables()); + if (toolchains_matched) { + found_toolchains.push_back(search_path); + } + } + + return found_toolchains; +} + +template class ToolchainFind; + +} // namespace buildcc diff --git a/buildcc/lib/toolchain/src/api/toolchain_verify.cpp b/buildcc/lib/toolchain/src/api/toolchain_verify.cpp index 44ce6021..7189ecb7 100644 --- a/buildcc/lib/toolchain/src/api/toolchain_verify.cpp +++ b/buildcc/lib/toolchain/src/api/toolchain_verify.cpp @@ -16,13 +16,12 @@ #include "toolchain/api/toolchain_verify.h" -#include #include #include #include -#include "toolchain/toolchain.h" +#include "schema/path.h" #include "env/assert_fatal.h" #include "env/host_os.h" @@ -31,256 +30,81 @@ #include "env/command.h" -namespace { -// Constants - -// Functions -std::vector ParseEnvVarToPaths(const std::string &env_var) { - const char *path_env = getenv(env_var.c_str()); - buildcc::env::assert_fatal( - path_env != nullptr, - fmt::format("Environment variable '{}' not present", env_var)); - - constexpr const char *os_env_delim = buildcc::env::get_os_envvar_delim(); - buildcc::env::assert_fatal("OS not supported"); - std::vector paths = - buildcc::env::split(path_env, os_env_delim[0]); - - return paths; -} - -std::string GetGccCompilerVersion(const buildcc::env::Command &command) { - std::vector stdout_data; - bool executed = buildcc::env::Command::Execute( - command.Construct("{compiler} -dumpversion"), {}, &stdout_data); - buildcc::env::assert_fatal( - executed, "GetCompilerVersion command not executed successfully"); - return stdout_data.at(0); -} - -std::string GetMsvcCompilerVersion() { - // Done VSCMD_VER - const char *vscmd_version = getenv("VSCMD_VER"); - buildcc::env::assert_fatal( - vscmd_version != nullptr, - "Setup Visual Studio build tools. Call `vcvarsall.bat {platform}` to " - "setup your target and host"); - return vscmd_version; -} - -std::optional -GetCompilerVersion(const fs::path &absolute_path, - const buildcc::Toolchain &toolchain) { - buildcc::env::Command command; - command.AddDefaultArgument( - "compiler", - (absolute_path / toolchain.GetCppCompiler()).make_preferred().string()); - - std::optional compiler_version; - switch (toolchain.GetId()) { - case buildcc::Toolchain::Id::Gcc: - case buildcc::Toolchain::Id::MinGW: - case buildcc::Toolchain::Id::Clang: - compiler_version = GetGccCompilerVersion(command); - break; - case buildcc::Toolchain::Id::Msvc: - compiler_version = GetMsvcCompilerVersion(); - break; - default: - buildcc::env::log_warning(__FUNCTION__, - "Operation not supported on this compiler"); - compiler_version = {}; - break; - } - return compiler_version; -} - -std::string GetGccTargetArchitecture(const buildcc::env::Command &command) { - std::vector stdout_data; - bool executed = buildcc::env::Command::Execute( - command.Construct("{compiler} -dumpmachine"), {}, &stdout_data); - buildcc::env::assert_fatal( - executed, "GetCompilerArchitecture command not executed successfully"); - return stdout_data.at(0); -} - -std::string GetMsvcTargetArchitecture() { - // DONE, Read `VSCMD_ARG_HOST_ARCH` from env path - // DONE, Read `VSCMD_ARG_TGT_ARCH` from env path - const char *vs_host_arch = getenv("VSCMD_ARG_HOST_ARCH"); - const char *vs_target_arch = getenv("VSCMD_ARG_TGT_ARCH"); - buildcc::env::assert_fatal( - (vs_host_arch != nullptr) && (vs_target_arch != nullptr), - "Setup Visual Studio build tools. Call `vcvarsall.bat {platform}` to " - "setup your target and host"); +#include "toolchain/toolchain.h" - // DONE, Concat them! - return fmt::format("{}_{}", vs_host_arch, vs_target_arch); -} +namespace { -std::optional -GetCompilerArchitecture(const fs::path &absolute_path, - const buildcc::Toolchain &toolchain) { - buildcc::env::Command command; - command.AddDefaultArgument( - "compiler", - (absolute_path / toolchain.GetCppCompiler()).make_preferred().string()); - std::optional target_arch; - switch (toolchain.GetId()) { - case buildcc::Toolchain::Id::Gcc: - case buildcc::Toolchain::Id::MinGW: - case buildcc::Toolchain::Id::Clang: - target_arch = GetGccTargetArchitecture(command); - break; - case buildcc::Toolchain::Id::Msvc: - target_arch = GetMsvcTargetArchitecture(); - break; - default: - buildcc::env::log_warning(__FUNCTION__, - "Operation not supported on this compiler"); - target_arch = {}; - break; - } - return target_arch; +buildcc::ToolchainExecutables CreateToolchainExecutables( + const fs::path &absolute_path, + const buildcc::ToolchainExecutables ¤t_executables) { + constexpr const char *const executable_ext = + buildcc::env::get_os_executable_extension(); + buildcc::env::assert_fatal( + "Host executable extension not supported"); + + std::string assembler_path = + (absolute_path / + fmt::format("{}{}", current_executables.assembler, executable_ext)) + .string(); + std::string c_compiler_path = + (absolute_path / + fmt::format("{}{}", current_executables.c_compiler, executable_ext)) + .string(); + std::string cpp_compiler_path = + (absolute_path / + fmt::format("{}{}", current_executables.cpp_compiler, executable_ext)) + .string(); + std::string archiver_path = + (absolute_path / + fmt::format("{}{}", current_executables.archiver, executable_ext)) + .string(); + std::string linker_path = + (absolute_path / + fmt::format("{}{}", current_executables.linker, executable_ext)) + .string(); + + return buildcc::ToolchainExecutables(assembler_path, c_compiler_path, + cpp_compiler_path, archiver_path, + linker_path); } -class ToolchainMatcher { -public: - explicit ToolchainMatcher(const buildcc::Toolchain &toolchain) - : toolchain_(toolchain) {} - - void FillWithToolchainFilenames() { - constexpr const char *os_executable_ext = - buildcc::env::get_os_executable_extension(); - buildcc::env::assert_fatal( - "OS not supported"); - - matcher_.clear(); - matcher_.insert( - fmt::format("{}{}", toolchain_.GetAsmCompiler(), os_executable_ext)); - matcher_.insert( - fmt::format("{}{}", toolchain_.GetCCompiler(), os_executable_ext)); - matcher_.insert( - fmt::format("{}{}", toolchain_.GetCppCompiler(), os_executable_ext)); - matcher_.insert( - fmt::format("{}{}", toolchain_.GetArchiver(), os_executable_ext)); - matcher_.insert( - fmt::format("{}{}", toolchain_.GetLinker(), os_executable_ext)); - } - - void Check(const std::string &filename) { matcher_.erase(filename); } - bool Found() { return matcher_.empty(); } - -private: - const buildcc::Toolchain &toolchain_; - - std::unordered_set matcher_; -}; - } // namespace namespace buildcc { template -std::vector -ToolchainVerify::Verify(const VerifyToolchainConfig &config) { - // TODO, Convert this to fs::path eventually - std::unordered_set absolute_search_paths{ - config.absolute_search_paths.begin(), config.absolute_search_paths.end()}; - - // Parse config envs - for (const std::string &env_var : config.env_vars) { - std::vector paths = ParseEnvVarToPaths(env_var); - for (const auto &p : paths) { - absolute_search_paths.insert(p); - } - } - - std::vector verified_toolchains; +ToolchainCompilerInfo +ToolchainVerify::Verify(const ToolchainFindConfig &config) { T &t = static_cast(*this); - - ToolchainMatcher matcher(t); - matcher.FillWithToolchainFilenames(); - // Iterate over absolute search paths - // [Verification] Match ALL toolchains PER directory - for (const std::string &pstr : absolute_search_paths) { - fs::path p{pstr}; - if (!fs::exists(p)) { - continue; - } - - std::error_code ec; - auto directory_iterator = fs::directory_iterator(p, ec); - if (ec) { - continue; - } - - // For each directory, Check if ALL toolchain filenames are found - for (const auto &dir_iter : directory_iterator) { - bool is_regular_file = dir_iter.is_regular_file(ec); - if (!is_regular_file || ec) { - continue; - } - const auto &filename = dir_iter.path().filename().string(); - matcher.Check(filename); - } - - // Store verified toolchain path if found - if (matcher.Found()) { - env::log_info(__FUNCTION__, fmt::format("Found: {}", p.string())); - - VerifiedToolchain vt; - vt.path = p; - vt.compiler_version = env::trim(GetCompilerVersion(p, t).value_or("")); - vt.target_arch = env::trim(GetCompilerArchitecture(p, t).value_or("")); - - // Check add - bool add{true}; - if (config.compiler_version.has_value()) { - add = add && (config.compiler_version.value() == vt.compiler_version); - } - if (config.target_arch.has_value()) { - add = add && (config.target_arch.value() == vt.target_arch); - } - if (add) { - verified_toolchains.push_back(vt); - } - } - - // Reset - matcher.FillWithToolchainFilenames(); + std::vector toolchain_paths = t.Find(config); + env::assert_fatal(!toolchain_paths.empty(), "No toolchains found"); + + ToolchainExecutables exes = + CreateToolchainExecutables(toolchain_paths[0], t.executables_); + env::optional op_toolchain_compiler_info{}; + if (GetToolchainInfoCb()) { + op_toolchain_compiler_info = GetToolchainInfoCb()(exes); } + env::assert_fatal(op_toolchain_compiler_info.has_value(), + "Could not verify toolchain"); - if (config.update && !verified_toolchains.empty()) { - constexpr const char *os_executable_ext = - buildcc::env::get_os_executable_extension(); - buildcc::env::assert_fatal( - "OS not supported"); + ToolchainCompilerInfo toolchain_compiler_info = + op_toolchain_compiler_info.value(); + toolchain_compiler_info.path = toolchain_paths[0]; - verified_toolchain_ = verified_toolchains[0]; - t.asm_compiler_ = (verified_toolchain_.path / - fmt::format("{}{}", t.asm_compiler_, os_executable_ext)) - .make_preferred() - .string(); - t.c_compiler_ = (verified_toolchain_.path / - fmt::format("{}{}", t.c_compiler_, os_executable_ext)) - .make_preferred() - .string(); - t.cpp_compiler_ = (verified_toolchain_.path / - fmt::format("{}{}", t.cpp_compiler_, os_executable_ext)) - .make_preferred() - .string(); - t.archiver_ = (verified_toolchain_.path / - fmt::format("{}{}", t.archiver_, os_executable_ext)) - .make_preferred() - .string(); - t.linker_ = (verified_toolchain_.path / - fmt::format("{}{}", t.linker_, os_executable_ext)) - .make_preferred() - .string(); - } + // Update the compilers + t.executables_ = exes; + return toolchain_compiler_info; +} - return verified_toolchains; +template +void ToolchainVerify::SetToolchainInfoCb(const ToolchainInfoCb &cb) { + info_cb_ = cb; +} + +template +const ToolchainInfoCb &ToolchainVerify::GetToolchainInfoCb() const { + return info_cb_; } template class ToolchainVerify; diff --git a/buildcc/lib/toolchain/src/common/toolchain_config.cpp b/buildcc/lib/toolchain/src/common/toolchain_config.cpp new file mode 100644 index 00000000..55125dd4 --- /dev/null +++ b/buildcc/lib/toolchain/src/common/toolchain_config.cpp @@ -0,0 +1,88 @@ +/* + * 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. + */ + +#include "toolchain/common/toolchain_config.h" + +#include "env/assert_fatal.h" + +#include "schema/path.h" + +#include "fmt/format.h" + +namespace buildcc { + +FileExt ToolchainConfig::GetFileExt(const fs::path &filepath) const { + if (!filepath.has_extension()) { + return FileExt::Invalid; + } + + FileExt type = FileExt::Invalid; + const std::string ext = filepath.extension().string(); + + if (valid_c_ext.count(ext) == 1) { + type = FileExt::C; + } else if (valid_cpp_ext.count(ext) == 1) { + type = FileExt::Cpp; + } else if (valid_asm_ext.count(ext) == 1) { + type = FileExt::Asm; + } else if (valid_header_ext.count(ext) == 1) { + type = FileExt::Header; + } + + return type; +} + +bool ToolchainConfig::IsValidSource(const fs::path &filepath) const { + if (!filepath.has_extension()) { + return false; + } + + const std::string ext = filepath.extension().string(); + bool valid = false; + if ((valid_c_ext.find(ext) != valid_c_ext.end()) || + (valid_cpp_ext.find(ext) != valid_cpp_ext.end()) || + (valid_asm_ext.find(ext) != valid_asm_ext.end())) { + valid = true; + } + return valid; +} + +void ToolchainConfig::ExpectsValidSource(const fs::path &filepath) const { + env::assert_fatal( + IsValidSource(filepath), + fmt::format("{} does not have a valid source extension", filepath)); +} + +bool ToolchainConfig::IsValidHeader(const fs::path &filepath) const { + if (!filepath.has_extension()) { + return {}; + } + + const std::string ext = filepath.extension().string(); + bool valid = false; + if ((valid_header_ext.find(ext) != valid_header_ext.end())) { + valid = true; + } + return valid; +} + +void ToolchainConfig::ExpectsValidHeader(const fs::path &filepath) const { + env::assert_fatal( + IsValidHeader(filepath), + fmt::format("{} does not have a valid header extension", filepath)); +} + +} // namespace buildcc diff --git a/buildcc/lib/toolchain/src/toolchain/toolchain.cpp b/buildcc/lib/toolchain/src/toolchain/toolchain.cpp new file mode 100644 index 00000000..3c5a3141 --- /dev/null +++ b/buildcc/lib/toolchain/src/toolchain/toolchain.cpp @@ -0,0 +1,21 @@ +/* + * 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. + */ + +#include "toolchain/toolchain.h" + +#include + +namespace buildcc {} // namespace buildcc diff --git a/buildcc/lib/toolchain/test/test_toolchain_config.cpp b/buildcc/lib/toolchain/test/test_toolchain_config.cpp new file mode 100644 index 00000000..a0cfce24 --- /dev/null +++ b/buildcc/lib/toolchain/test/test_toolchain_config.cpp @@ -0,0 +1,41 @@ +#include "toolchain/common/toolchain_config.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" + +// clang-format off +TEST_GROUP(ToolchainConfigTestGroup) +{ +}; +// clang-format on + +TEST(ToolchainConfigTestGroup, GetFileExt) { + buildcc::ToolchainConfig toolchain_config; + + buildcc::FileExt ext; + + ext = toolchain_config.GetFileExt("file.asm"); + CHECK(ext == buildcc::FileExt::Asm); + + ext = toolchain_config.GetFileExt("file.c"); + CHECK(ext == buildcc::FileExt::C); + + ext = toolchain_config.GetFileExt("file.cpp"); + CHECK(ext == buildcc::FileExt::Cpp); + + ext = toolchain_config.GetFileExt("file.h"); + CHECK(ext == buildcc::FileExt::Header); + + ext = toolchain_config.GetFileExt("file.invalid"); + CHECK(ext == buildcc::FileExt::Invalid); + + ext = toolchain_config.GetFileExt("random/directory"); + CHECK(ext == buildcc::FileExt::Invalid); +} + +int main(int ac, char **av) { + return CommandLineTestRunner::RunAllTests(ac, av); +} diff --git a/buildcc/lib/toolchain/test/test_toolchain_find.cpp b/buildcc/lib/toolchain/test/test_toolchain_find.cpp new file mode 100644 index 00000000..9657dfb9 --- /dev/null +++ b/buildcc/lib/toolchain/test/test_toolchain_find.cpp @@ -0,0 +1,107 @@ +#include + +#include "toolchain/toolchain.h" + +#include "env/host_os.h" + +#include "expect_command.h" + +#include "mock_command_copier.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(ToolchainFindTestGroup) +{ + void teardown() { + mock().checkExpectations(); + mock().clear(); + } +}; +// clang-format on + +TEST(ToolchainFindTestGroup, FindToolchain_ThroughEnvVar) { + buildcc::Toolchain gcc( + buildcc::ToolchainId::Gcc, "gcc", + buildcc::ToolchainExecutables("as", "gcc", "g++", "ar", "ld")); + + std::string putenv_str = fmt::format("CUSTOM_BUILDCC_PATH={}/toolchains/gcc", + fs::current_path().string()); + int put = putenv(putenv_str.data()); + CHECK_TRUE(put == 0); + const char *custom_buildcc_path = getenv("CUSTOM_BUILDCC_PATH"); + CHECK_TRUE(custom_buildcc_path != nullptr); + UT_PRINT(custom_buildcc_path); + + buildcc::ToolchainFindConfig config; + config.env_vars.clear(); + config.env_vars.push_back("CUSTOM_BUILDCC_PATH"); + + std::vector found_toolchains = gcc.Find(config); + CHECK_TRUE(!found_toolchains.empty()); +} + +TEST(ToolchainFindTestGroup, FindToolchain_ThroughAbsolutePath) { + buildcc::Toolchain gcc( + buildcc::ToolchainId::Gcc, "gcc", + buildcc::ToolchainExecutables("as", "gcc", "g++", "ar", "ld")); + + buildcc::ToolchainFindConfig config; + config.absolute_search_paths.push_back(fs::current_path() / "toolchains" / + "gcc"); + config.env_vars.clear(); + + std::vector found_toolchains = gcc.Find(config); + CHECK_TRUE(!found_toolchains.empty()); +} + +TEST(ToolchainFindTestGroup, FindToolchain_DirectoryDoesntExist) { + buildcc::Toolchain gcc( + buildcc::ToolchainId::Gcc, "gcc", + buildcc::ToolchainExecutables("as", "gcc", "g++", "ar", "ld")); + + buildcc::ToolchainFindConfig config; + config.absolute_search_paths.push_back(fs::current_path() / "toolchains" / + "directory_doesnt_exist"); + config.env_vars.clear(); + + std::vector found_toolchains = gcc.Find(config); + CHECK_TRUE(found_toolchains.empty()); +} + +TEST(ToolchainFindTestGroup, FindToolchain_NoDirectoryFound) { + buildcc::Toolchain gcc( + buildcc::ToolchainId::Gcc, "gcc", + buildcc::ToolchainExecutables("as", "gcc", "g++", "ar", "ld")); + + buildcc::ToolchainFindConfig config; + config.absolute_search_paths.push_back(fs::current_path() / "toolchains" / + "gcc" / "ar"); + config.env_vars.clear(); + + std::vector found_toolchains = gcc.Find(config); + CHECK_TRUE(found_toolchains.empty()); +} + +TEST(ToolchainFindTestGroup, FindToolchain_NoToolchainFound) { + buildcc::Toolchain gcc( + buildcc::ToolchainId::Gcc, "gcc", + buildcc::ToolchainExecutables("as", "gcc", "g++", "ar", "ld")); + + buildcc::ToolchainFindConfig config; + config.absolute_search_paths.push_back(fs::current_path() / "toolchains"); + config.env_vars.clear(); + + std::vector found_toolchains = gcc.Find(config); + CHECK_TRUE(found_toolchains.empty()); +} + +int main(int ac, char **av) { + MemoryLeakWarningPlugin::turnOffNewDeleteOverloads(); + return CommandLineTestRunner::RunAllTests(ac, av); +} diff --git a/buildcc/lib/toolchain/test/test_toolchain_id.cpp b/buildcc/lib/toolchain/test/test_toolchain_id.cpp new file mode 100644 index 00000000..6cb77e6b --- /dev/null +++ b/buildcc/lib/toolchain/test/test_toolchain_id.cpp @@ -0,0 +1,50 @@ +#include + +#include "toolchain/common/toolchain_id.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(ToolchainIdTestGroup) +{ + void teardown() { + mock().checkExpectations(); + mock().clear(); + } +}; +// clang-format on + +TEST(ToolchainIdTestGroup, ToolchainIdAsString) { + std::string compiler; + + compiler = fmt::format("{}", buildcc::ToolchainId::Gcc); + STRCMP_EQUAL(compiler.c_str(), "Gcc"); + + compiler = fmt::format("{}", buildcc::ToolchainId::Msvc); + STRCMP_EQUAL(compiler.c_str(), "Msvc"); + + compiler = fmt::format("{}", buildcc::ToolchainId::Clang); + STRCMP_EQUAL(compiler.c_str(), "Clang"); + + compiler = fmt::format("{}", buildcc::ToolchainId::MinGW); + STRCMP_EQUAL(compiler.c_str(), "MinGW"); + + compiler = fmt::format("{}", buildcc::ToolchainId::Custom); + STRCMP_EQUAL(compiler.c_str(), "Custom"); + + compiler = fmt::format("{}", buildcc::ToolchainId::Undefined); + STRCMP_EQUAL(compiler.c_str(), "Undefined"); + + compiler = fmt::format("{}", (buildcc::ToolchainId)65535); + STRCMP_EQUAL(compiler.c_str(), "Undefined"); +} + +int main(int ac, char **av) { + MemoryLeakWarningPlugin::turnOffNewDeleteOverloads(); + return CommandLineTestRunner::RunAllTests(ac, av); +} diff --git a/buildcc/lib/toolchain/test/test_toolchain_verify.cpp b/buildcc/lib/toolchain/test/test_toolchain_verify.cpp index ad9a15f1..697fb45c 100644 --- a/buildcc/lib/toolchain/test/test_toolchain_verify.cpp +++ b/buildcc/lib/toolchain/test/test_toolchain_verify.cpp @@ -2,6 +2,7 @@ #include "toolchain/toolchain.h" +#include "env/command.h" #include "env/host_os.h" #include "expect_command.h" @@ -16,7 +17,7 @@ #include "CppUTestExt/MockSupport.h" // clang-format off -TEST_GROUP(ToolchainTestGroup) +TEST_GROUP(ToolchainVerifyTestGroup) { void teardown() { mock().checkExpectations(); @@ -25,157 +26,60 @@ TEST_GROUP(ToolchainTestGroup) }; // clang-format on -// NOTE, We are mocking the environment instead of actually querying it -TEST(ToolchainTestGroup, VerifyToolchain_Gcc) { - buildcc::Toolchain gcc(buildcc::Toolchain::Id::Gcc, "gcc", "as", "gcc", "g++", - "ar", "ld"); - - std::vector version_stdout_data{"version"}; - std::vector arch_stdout_data{"arch"}; - buildcc::env::m::CommandExpect_Execute(1, true, &version_stdout_data); - buildcc::env::m::CommandExpect_Execute(1, true, &arch_stdout_data); - - std::string putenv_str = fmt::format("CUSTOM_BUILDCC_PATH={}/toolchains/gcc", - fs::current_path().string()); - int put = putenv(putenv_str.data()); - CHECK_TRUE(put == 0); - const char *custom_buildcc_path = getenv("CUSTOM_BUILDCC_PATH"); - CHECK_TRUE(custom_buildcc_path != nullptr); - UT_PRINT(custom_buildcc_path); - - buildcc::VerifyToolchainConfig config; - config.env_vars.clear(); - config.env_vars.push_back("CUSTOM_BUILDCC_PATH"); - - std::vector verified_toolchains = - gcc.Verify(config); - UT_PRINT(std::to_string(verified_toolchains.size()).c_str()); - CHECK_TRUE(!verified_toolchains.empty()); - STRCMP_EQUAL(verified_toolchains[0].compiler_version.c_str(), "version"); - STRCMP_EQUAL(verified_toolchains[0].target_arch.c_str(), "arch"); -} - -TEST(ToolchainTestGroup, VerifyToolchain_Clang) { - buildcc::Toolchain clang(buildcc::Toolchain::Id::Clang, "clang", "llvm-as", - "clang", "clang++", "llvm-ar", "lld"); - - std::vector version_stdout_data{"version"}; - std::vector arch_stdout_data{"arch"}; - buildcc::env::m::CommandExpect_Execute(1, true, &version_stdout_data); - buildcc::env::m::CommandExpect_Execute(1, true, &arch_stdout_data); - - std::string putenv_str = fmt::format( - "CUSTOM_BUILDCC_PATH={}/toolchains/clang", fs::current_path().string()); - int put = putenv(putenv_str.data()); - CHECK_TRUE(put == 0); - const char *custom_buildcc_path = getenv("CUSTOM_BUILDCC_PATH"); - CHECK_TRUE(custom_buildcc_path != nullptr); - UT_PRINT(custom_buildcc_path); - - buildcc::VerifyToolchainConfig config; - config.env_vars.clear(); - config.env_vars.push_back("CUSTOM_BUILDCC_PATH"); - - std::vector verified_toolchains = - clang.Verify(config); - UT_PRINT(std::to_string(verified_toolchains.size()).c_str()); - CHECK_TRUE(!verified_toolchains.empty()); - STRCMP_EQUAL(verified_toolchains[0].compiler_version.c_str(), "version"); - STRCMP_EQUAL(verified_toolchains[0].target_arch.c_str(), "arch"); -} - -TEST(ToolchainTestGroup, VerifyToolchain_Msvc) { - buildcc::Toolchain msvc(buildcc::Toolchain::Id::Msvc, "msvc", "cl", "cl", - "cl", "lib", "link"); - // Setup ENV - // VSCMD_VER - std::string vscmd_ver = std::string("VSCMD_VER=version"); - // VSCMD_ARG_HOST_ARCH - std::string host_arch = std::string("VSCMD_ARG_HOST_ARCH=host_arch"); - // VSCMD_ARG_TGT_ARCH - std::string tgt_arch = std::string("VSCMD_ARG_TGT_ARCH=tgt_arch"); - - CHECK_TRUE(putenv(vscmd_ver.data()) == 0); - CHECK_TRUE(putenv(host_arch.data()) == 0); - CHECK_TRUE(putenv(tgt_arch.data()) == 0); - - // MSVC Compiler - std::string putenv_str = fmt::format("CUSTOM_BUILDCC_PATH={}/toolchains/msvc", - fs::current_path().string()); - int put = putenv(putenv_str.data()); - CHECK_TRUE(put == 0); - const char *custom_buildcc_path = getenv("CUSTOM_BUILDCC_PATH"); - CHECK_TRUE(custom_buildcc_path != nullptr); - UT_PRINT(custom_buildcc_path); - - buildcc::VerifyToolchainConfig config; - config.env_vars.clear(); - config.env_vars.push_back("CUSTOM_BUILDCC_PATH"); - - std::vector verified_toolchains = - msvc.Verify(config); - UT_PRINT(std::to_string(verified_toolchains.size()).c_str()); - CHECK_TRUE(!verified_toolchains.empty()); - STRCMP_EQUAL(verified_toolchains[0].compiler_version.c_str(), "version"); - STRCMP_EQUAL(verified_toolchains[0].target_arch.c_str(), - "host_arch_tgt_arch"); -} +class MockToolchain : public buildcc::Toolchain { +public: + MockToolchain(buildcc::ToolchainId id, const std::string &name, + const buildcc::ToolchainExecutables &executables = + buildcc::ToolchainExecutables("as", "gcc", "g++", "ar", + "ld")) + : buildcc::Toolchain(id, name, executables) {} +}; -TEST(ToolchainTestGroup, VerifyToolchain_BadCompilerId) { - buildcc::Toolchain gcc((buildcc::Toolchain::Id)65535, "gcc", "as", "gcc", - "g++", "ar", "ld"); +// NOTE, We are mocking the environment instead of actually querying it +TEST(ToolchainVerifyTestGroup, VerifyToolchain_BaseToolchain_Failure) { + MockToolchain gcc( + buildcc::ToolchainId::Gcc, "gcc", + buildcc::ToolchainExecutables("as", "gcc", "g++", "ar", "ld")); - std::string putenv_str = fmt::format("CUSTOM_BUILDCC_PATH={}/toolchains/gcc", - fs::current_path().string()); + std::string putenv_str = + fmt::format("CUSTOM_BUILDCC_PATH={}/toolchains/gcc", fs::current_path()); int put = putenv(putenv_str.data()); CHECK_TRUE(put == 0); const char *custom_buildcc_path = getenv("CUSTOM_BUILDCC_PATH"); CHECK_TRUE(custom_buildcc_path != nullptr); UT_PRINT(custom_buildcc_path); - buildcc::VerifyToolchainConfig config; + buildcc::ToolchainFindConfig config; config.env_vars.clear(); config.env_vars.push_back("CUSTOM_BUILDCC_PATH"); - std::vector verified_toolchains = - gcc.Verify(config); - UT_PRINT(std::to_string(verified_toolchains.size()).c_str()); - CHECK_TRUE(!verified_toolchains.empty()); - STRCMP_EQUAL(verified_toolchains[0].compiler_version.c_str(), ""); - STRCMP_EQUAL(verified_toolchains[0].target_arch.c_str(), ""); + CHECK_THROWS(std::exception, gcc.Verify(config)); } -TEST(ToolchainTestGroup, VerifyToolchain_BadAbsolutePath) { - buildcc::Toolchain gcc(buildcc::Toolchain::Id::Gcc, "gcc", "as", "gcc", "g++", - "ar", "ld"); +TEST(ToolchainVerifyTestGroup, VerifyToolchain_BadAbsolutePath) { + MockToolchain gcc(buildcc::ToolchainId::Gcc, "gcc"); - buildcc::VerifyToolchainConfig config; + buildcc::ToolchainFindConfig config; config.env_vars.clear(); config.absolute_search_paths.push_back( - (fs::current_path() / "does_not_exist").string()); + (fs::current_path() / "does_not_exist")); - std::vector verified_toolchains = - gcc.Verify(config); - UT_PRINT(std::to_string(verified_toolchains.size()).c_str()); - CHECK_TRUE(verified_toolchains.empty()); + CHECK_THROWS(std::exception, gcc.Verify(config)); } -TEST(ToolchainTestGroup, VerifyToolchain_PathContainsDir) { - buildcc::Toolchain gcc(buildcc::Toolchain::Id::Gcc, "gcc", "as", "gcc", "g++", - "ar", "ld"); +TEST(ToolchainVerifyTestGroup, VerifyToolchain_PathContainsDir) { + MockToolchain gcc(buildcc::ToolchainId::Gcc, "gcc"); - buildcc::VerifyToolchainConfig config; + buildcc::ToolchainFindConfig config; config.env_vars.clear(); - config.absolute_search_paths.push_back( - (fs::current_path() / "toolchains").string()); + config.absolute_search_paths.push_back((fs::current_path() / "toolchains")); - std::vector verified_toolchains = - gcc.Verify(config); - UT_PRINT(std::to_string(verified_toolchains.size()).c_str()); - CHECK_TRUE(verified_toolchains.empty()); + CHECK_THROWS(std::exception, gcc.Verify(config)); } -TEST(ToolchainTestGroup, VerifyToolchain_LockedFolder) { +#if defined(__GNUC__) && !defined(__MINGW32__) && !defined(__MINGW64__) + +TEST(ToolchainVerifyTestGroup, VerifyToolchain_LockedFolder) { std::error_code err; fs::permissions(fs::current_path() / "toolchains" / "gcc", fs::perms::none, err); @@ -183,18 +87,14 @@ TEST(ToolchainTestGroup, VerifyToolchain_LockedFolder) { FAIL_TEST("Could not set file permissions"); } - buildcc::Toolchain gcc(buildcc::Toolchain::Id::Gcc, "gcc", "as", "gcc", "g++", - "ar", "ld"); + MockToolchain gcc(buildcc::ToolchainId::Gcc, "gcc"); - buildcc::VerifyToolchainConfig config; + buildcc::ToolchainFindConfig config; config.env_vars.clear(); config.absolute_search_paths.push_back( - (fs::current_path() / "toolchains" / "gcc").string()); + (fs::current_path() / "toolchains" / "gcc")); - std::vector verified_toolchains = - gcc.Verify(config); - UT_PRINT(std::to_string(verified_toolchains.size()).c_str()); - CHECK_TRUE(verified_toolchains.empty()); + CHECK_THROWS(std::exception, gcc.Verify(config)); fs::permissions(fs::current_path() / "toolchains" / "gcc", fs::perms::all, err); @@ -203,134 +103,39 @@ TEST(ToolchainTestGroup, VerifyToolchain_LockedFolder) { } } -TEST(ToolchainTestGroup, VerifyToolchain_ConditionalAdd_CompilerVersion) { - buildcc::Toolchain gcc(buildcc::Toolchain::Id::Gcc, "gcc", "as", "gcc", "g++", - "ar", "ld"); - - buildcc::VerifyToolchainConfig config; - config.env_vars.clear(); - config.absolute_search_paths.push_back( - (fs::current_path() / "toolchains" / "gcc").string()); - config.compiler_version = "10.2.1"; - - std::vector compiler_version{"10.2.1"}; - std::vector arch{"none"}; - buildcc::env::m::CommandExpect_Execute(1, true, &compiler_version, nullptr); - buildcc::env::m::CommandExpect_Execute(1, true, &arch, nullptr); - - std::vector verified_toolchains = - gcc.Verify(config); - UT_PRINT(std::to_string(verified_toolchains.size()).c_str()); - CHECK_EQUAL(verified_toolchains.size(), 1); -} - -TEST(ToolchainTestGroup, - VerifyToolchain_ConditionalAdd_CompilerVersionFailure) { - buildcc::Toolchain gcc(buildcc::Toolchain::Id::Gcc, "gcc", "as", "gcc", "g++", - "ar", "ld"); - - buildcc::VerifyToolchainConfig config; - config.env_vars.clear(); - config.absolute_search_paths.push_back( - (fs::current_path() / "toolchains" / "gcc").string()); - config.compiler_version = "11.0.0"; - - std::vector compiler_version{"10.2.1"}; - std::vector arch{"none"}; - buildcc::env::m::CommandExpect_Execute(1, true, &compiler_version, nullptr); - buildcc::env::m::CommandExpect_Execute(1, true, &arch, nullptr); - - std::vector verified_toolchains = - gcc.Verify(config); - UT_PRINT(std::to_string(verified_toolchains.size()).c_str()); - CHECK_EQUAL(verified_toolchains.size(), 0); -} - -TEST(ToolchainTestGroup, VerifyToolchain_ConditionalAdd_TargetArch) { - buildcc::Toolchain gcc(buildcc::Toolchain::Id::Gcc, "gcc", "as", "gcc", "g++", - "ar", "ld"); - - buildcc::VerifyToolchainConfig config; - config.env_vars.clear(); - config.absolute_search_paths.push_back( - (fs::current_path() / "toolchains" / "gcc").string()); - config.target_arch = "arm-none-eabi"; - - std::vector compiler_version{"10.2.1"}; - std::vector arch{"arm-none-eabi"}; - buildcc::env::m::CommandExpect_Execute(1, true, &compiler_version, nullptr); - buildcc::env::m::CommandExpect_Execute(1, true, &arch, nullptr); - - std::vector verified_toolchains = - gcc.Verify(config); - UT_PRINT(std::to_string(verified_toolchains.size()).c_str()); - CHECK_EQUAL(verified_toolchains.size(), 1); -} - -TEST(ToolchainTestGroup, VerifyToolchain_ConditionalAdd_TargetArchFailure) { - buildcc::Toolchain gcc(buildcc::Toolchain::Id::Gcc, "gcc", "as", "gcc", "g++", - "ar", "ld"); - - buildcc::VerifyToolchainConfig config; - config.env_vars.clear(); - config.absolute_search_paths.push_back( - (fs::current_path() / "toolchains" / "gcc").string()); - config.target_arch = "none"; - - std::vector compiler_version{"10.2.1"}; - std::vector arch{"arm-none-eabi"}; - buildcc::env::m::CommandExpect_Execute(1, true, &compiler_version, nullptr); - buildcc::env::m::CommandExpect_Execute(1, true, &arch, nullptr); - - std::vector verified_toolchains = - gcc.Verify(config); - UT_PRINT(std::to_string(verified_toolchains.size()).c_str()); - CHECK_EQUAL(verified_toolchains.size(), 0); -} - -TEST(ToolchainTestGroup, VerifyToolchain_ConditionalAdd_BothFailure) { - buildcc::Toolchain gcc(buildcc::Toolchain::Id::Gcc, "gcc", "as", "gcc", "g++", - "ar", "ld"); - - buildcc::VerifyToolchainConfig config; - config.env_vars.clear(); - config.absolute_search_paths.push_back( - (fs::current_path() / "toolchains" / "gcc").string()); - config.compiler_version = "none"; - config.target_arch = "none"; - - std::vector compiler_version{"10.2.1"}; - std::vector arch{"arm-none-eabi"}; - buildcc::env::m::CommandExpect_Execute(1, true, &compiler_version, nullptr); - buildcc::env::m::CommandExpect_Execute(1, true, &arch, nullptr); - - std::vector verified_toolchains = - gcc.Verify(config); - UT_PRINT(std::to_string(verified_toolchains.size()).c_str()); - CHECK_EQUAL(verified_toolchains.size(), 0); -} - -TEST(ToolchainTestGroup, VerifyToolchain_UpdateFalse) { - buildcc::Toolchain gcc(buildcc::Toolchain::Id::Gcc, "gcc", "as", "gcc", "g++", - "ar", "ld"); +#endif + +TEST(ToolchainVerifyTestGroup, CustomToolchainInfo) { + buildcc::Toolchain toolchain( + buildcc::ToolchainId::Gcc, "gcc", + buildcc::ToolchainExecutables("as", "gcc", "g++", "ar", "ld")); + toolchain.SetToolchainInfoCb( + [](const buildcc::ToolchainExecutables &executables) + -> buildcc::env::optional { + (void)executables; + mock().actualCall("SetToolchainInfoCb"); + buildcc::ToolchainCompilerInfo info; + info.compiler_version = "version"; + info.target_arch = "arch"; + return info; + }); + + std::string putenv_str = + fmt::format("CUSTOM_BUILDCC_PATH={}/toolchains/gcc", fs::current_path()); + int put = putenv(putenv_str.data()); + CHECK_TRUE(put == 0); + const char *custom_buildcc_path = getenv("CUSTOM_BUILDCC_PATH"); + CHECK_TRUE(custom_buildcc_path != nullptr); + UT_PRINT(custom_buildcc_path); - buildcc::VerifyToolchainConfig config; + buildcc::ToolchainFindConfig config; config.env_vars.clear(); - config.absolute_search_paths.push_back( - (fs::current_path() / "toolchains" / "gcc").string()); - // config.compiler_version = "none"; - // config.target_arch = "none"; - config.update = false; - - std::vector compiler_version{"10.2.1"}; - std::vector arch{"arm-none-eabi"}; - buildcc::env::m::CommandExpect_Execute(1, true, &compiler_version, nullptr); - buildcc::env::m::CommandExpect_Execute(1, true, &arch, nullptr); + config.env_vars.push_back("CUSTOM_BUILDCC_PATH"); - std::vector verified_toolchains = - gcc.Verify(config); - UT_PRINT(std::to_string(verified_toolchains.size()).c_str()); - CHECK_EQUAL(verified_toolchains.size(), 1); + mock().expectOneCall("SetToolchainInfoCb"); + auto info = toolchain.Verify(config); + STRCMP_EQUAL(info.compiler_version.c_str(), "version"); + STRCMP_EQUAL(info.target_arch.c_str(), "arch"); } int main(int ac, char **av) { @@ -347,6 +152,6 @@ int main(int ac, char **av) { // toolchains/msvc // toolchains/mingw // TODO, Check executables used in clang - + MemoryLeakWarningPlugin::turnOffNewDeleteOverloads(); return CommandLineTestRunner::RunAllTests(ac, av); } diff --git a/buildcc/lib/toolchain/test/toolchains/custom/archiver b/buildcc/lib/toolchain/test/toolchains/custom/archiver new file mode 100644 index 00000000..e69de29b diff --git a/buildcc/lib/toolchain/test/toolchains/custom/archiver.exe b/buildcc/lib/toolchain/test/toolchains/custom/archiver.exe new file mode 100644 index 00000000..e69de29b diff --git a/buildcc/lib/toolchain/test/toolchains/custom/assembler b/buildcc/lib/toolchain/test/toolchains/custom/assembler new file mode 100644 index 00000000..e69de29b diff --git a/buildcc/lib/toolchain/test/toolchains/custom/assembler.exe b/buildcc/lib/toolchain/test/toolchains/custom/assembler.exe new file mode 100644 index 00000000..e69de29b diff --git a/buildcc/lib/toolchain/test/toolchains/custom/c_compiler b/buildcc/lib/toolchain/test/toolchains/custom/c_compiler new file mode 100644 index 00000000..e69de29b diff --git a/buildcc/lib/toolchain/test/toolchains/custom/c_compiler.exe b/buildcc/lib/toolchain/test/toolchains/custom/c_compiler.exe new file mode 100644 index 00000000..e69de29b diff --git a/buildcc/lib/toolchain/test/toolchains/custom/cpp_compiler b/buildcc/lib/toolchain/test/toolchains/custom/cpp_compiler new file mode 100644 index 00000000..e69de29b diff --git a/buildcc/lib/toolchain/test/toolchains/custom/cpp_compiler.exe b/buildcc/lib/toolchain/test/toolchains/custom/cpp_compiler.exe new file mode 100644 index 00000000..e69de29b diff --git a/buildcc/lib/toolchain/test/toolchains/custom/linker b/buildcc/lib/toolchain/test/toolchains/custom/linker new file mode 100644 index 00000000..e69de29b diff --git a/buildcc/lib/toolchain/test/toolchains/custom/linker.exe b/buildcc/lib/toolchain/test/toolchains/custom/linker.exe new file mode 100644 index 00000000..e69de29b diff --git a/buildcc/plugins/CMakeLists.txt b/buildcc/plugins/CMakeLists.txt index 42ae927b..42e4700d 100644 --- a/buildcc/plugins/CMakeLists.txt +++ b/buildcc/plugins/CMakeLists.txt @@ -17,20 +17,21 @@ target_link_libraries(mock_plugins PUBLIC CppUTest CppUTestExt - gcov + ${TEST_LINK_LIBS} ) # Tests -add_executable(test_buildcc_find - test/test_buildcc_find.cpp -) -target_link_libraries(test_buildcc_find PRIVATE - mock_plugins -) +# Removed test_buildcc_find till it is not complete +# add_executable(test_buildcc_find +# test/test_buildcc_find.cpp +# ) +# target_link_libraries(test_buildcc_find PRIVATE +# mock_plugins +# ) -add_test(NAME test_buildcc_find COMMAND test_buildcc_find - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test -) +# add_test(NAME test_buildcc_find COMMAND test_buildcc_find +# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test +# ) endif() diff --git a/buildcc/plugins/include/plugins/buildcc_find.h b/buildcc/plugins/include/plugins/buildcc_find.h index 1c8f3661..50eab64e 100644 --- a/buildcc/plugins/include/plugins/buildcc_find.h +++ b/buildcc/plugins/include/plugins/buildcc_find.h @@ -20,6 +20,7 @@ #include #include #include +#include #include "env/host_os.h" diff --git a/buildcc/plugins/include/plugins/clang_compile_commands.h b/buildcc/plugins/include/plugins/clang_compile_commands.h index 9bfb32ee..a228e8cb 100644 --- a/buildcc/plugins/include/plugins/clang_compile_commands.h +++ b/buildcc/plugins/include/plugins/clang_compile_commands.h @@ -33,7 +33,7 @@ class ClangCompileCommands { void AddTarget(const BaseTarget *target); /** - * @brief Generate clang compile commands file in `env::get_project_build_dir` + * @brief Generate clang compile commands file in `Project::GetBuildDir` * folder */ void Generate(); diff --git a/buildcc/plugins/src/buildcc_find.cpp b/buildcc/plugins/src/buildcc_find.cpp index 94e6aac9..d60eaa34 100644 --- a/buildcc/plugins/src/buildcc_find.cpp +++ b/buildcc/plugins/src/buildcc_find.cpp @@ -22,8 +22,9 @@ #include "env/host_os_util.h" #include "env/logging.h" +#include "env/util.h" -#include "target/common/path.h" +#include "schema/path.h" namespace { constexpr const char *const kEnvVarNotFound = @@ -33,20 +34,6 @@ constexpr const char *const kOsNotSupported = "issue at https://github.com/coder137/build_in_cpp outlining your OS and " "usecase"; -std::vector SplitEnv(const char *env_ptr, const char *delim) { - std::vector env_paths; - - std::string temp{env_ptr}; - char *path = std::strtok(temp.data(), delim); - while (path != nullptr) { - env_paths.push_back( - buildcc::internal::Path::CreateNewPath(path).GetPathname()); - path = std::strtok(nullptr, delim); - } - - return env_paths; -} - std::vector SearchEnv(const std::string &host_env_var, const std::string ®ex) { char *path_ptr = std::getenv(host_env_var.c_str()); @@ -56,13 +43,13 @@ std::vector SearchEnv(const std::string &host_env_var, return {}; } - std::vector env_paths; constexpr const char *const kDelim = buildcc::env::get_os_envvar_delim(); if constexpr (kDelim == nullptr) { buildcc::env::log_critical(__FUNCTION__, kOsNotSupported); return {}; } - env_paths = SplitEnv(path_ptr, kDelim); + + std::vector env_paths = buildcc::env::split(path_ptr, kDelim[0]); // DONE, Construct a directory iterator // Only take the files @@ -71,12 +58,12 @@ std::vector SearchEnv(const std::string &host_env_var, std::error_code errcode; const auto dir_iter = fs::directory_iterator(env_p, errcode); if (errcode) { - buildcc::env::log_critical(env_p.string(), errcode.message()); + buildcc::env::log_critical(env_p, errcode.message()); continue; } for (const auto &dir_entry : dir_iter) { - if (!dir_entry.is_regular_file()) { + if (!dir_entry.path().has_filename()) { continue; } diff --git a/buildcc/plugins/src/clang_compile_commands.cpp b/buildcc/plugins/src/clang_compile_commands.cpp index 6596843d..d3cd92e6 100644 --- a/buildcc/plugins/src/clang_compile_commands.cpp +++ b/buildcc/plugins/src/clang_compile_commands.cpp @@ -66,7 +66,7 @@ void ClangCompileCommands::Generate() { // DONE, Get intermediate directory from env std::string sf = fmt::format("{}", source); const std::string &command = t->GetCompileCommand(source); - std::string directory = fmt::format("{}", env::get_project_build_dir()); + std::string directory = fmt::format("{}", Project::GetBuildDir()); std::string temp = fmt::format( clang_compile_command_format, fmt::arg("directory", directory), @@ -81,7 +81,7 @@ void ClangCompileCommands::Generate() { // DONE, Convert to json // DONE, Save file std::filesystem::path file = - std::filesystem::path(buildcc::env::get_project_build_dir()) / + std::filesystem::path(buildcc::Project::GetBuildDir()) / "compile_commands.json"; bool saved = env::save_file(path_as_string(file).c_str(), compile_commands, false); diff --git a/buildcc/schema/CMakeLists.txt b/buildcc/schema/CMakeLists.txt index cbbe4b23..2f953e53 100644 --- a/buildcc/schema/CMakeLists.txt +++ b/buildcc/schema/CMakeLists.txt @@ -1,26 +1 @@ -# Generate files -set(SCHEMA_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated CACHE PATH "Generate path of flatbuffer schema") - -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 - -I ${CMAKE_CURRENT_SOURCE_DIR} - --gen-object-api -) - -add_custom_command(OUTPUT ${FBS_GEN_FILES} - COMMAND flatc -o ${SCHEMA_BUILD_DIR} ${FBS_GEN_OPTIONS} --cpp ${FBS_FILES} - DEPENDS flatc ${FBS_FILES} -) - -add_custom_target(fbs_to_header - DEPENDS ${FBS_GEN_FILES} -) +include(cmake/schema.cmake) diff --git a/buildcc/schema/cmake/schema.cmake b/buildcc/schema/cmake/schema.cmake new file mode 100644 index 00000000..cfe1e0e0 --- /dev/null +++ b/buildcc/schema/cmake/schema.cmake @@ -0,0 +1,113 @@ +# schema test +if (${TESTING}) + add_library(mock_schema STATIC + include/schema/interface/serialization_interface.h + + include/schema/path.h + + src/custom_generator_serialization.cpp + include/schema/custom_generator_schema.h + include/schema/custom_generator_serialization.h + + src/target_serialization.cpp + include/schema/target_schema.h + include/schema/target_serialization.h + ) + target_include_directories(mock_schema PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/mock/include + ${SCHEMA_BUILD_DIR} + ) + target_link_libraries(mock_schema PUBLIC + mock_env + nlohmann_json::nlohmann_json + + CppUTest + CppUTestExt + ${TEST_LINK_LIBS} + ) + + target_compile_options(mock_schema PUBLIC ${TEST_COMPILE_FLAGS} ${BUILD_COMPILE_FLAGS}) + target_link_options(mock_schema PUBLIC ${TEST_LINK_FLAGS} ${BUILD_LINK_FLAGS}) + + # Tests + add_executable(test_path_schema + test/test_path_schema.cpp + ) + target_link_libraries(test_path_schema PRIVATE mock_schema) + + add_executable(test_custom_generator_serialization + test/test_custom_generator_serialization.cpp + ) + target_link_libraries(test_custom_generator_serialization PRIVATE mock_schema) + + add_executable(test_target_serialization + test/test_target_serialization.cpp + ) + target_link_libraries(test_target_serialization PRIVATE mock_schema) + + add_test(NAME test_path_schema COMMAND test_path_schema + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test + ) + add_test(NAME test_custom_generator_serialization COMMAND test_custom_generator_serialization + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test + ) + add_test(NAME test_target_serialization COMMAND test_target_serialization + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test + ) +endif() + +set(SCHEMA_SRCS + include/schema/interface/serialization_interface.h + + include/schema/path.h + + src/custom_generator_serialization.cpp + include/schema/custom_generator_schema.h + include/schema/custom_generator_serialization.h + + src/target_serialization.cpp + include/schema/target_schema.h + include/schema/target_serialization.h +) + +if(${BUILDCC_BUILD_AS_SINGLE_LIB}) + target_sources(buildcc PRIVATE + ${SCHEMA_SRCS} + ) + target_include_directories(buildcc PUBLIC + $ + $ + ) + target_include_directories(buildcc PRIVATE + ${SCHEMA_BUILD_DIR} + ) +endif() + +if(${BUILDCC_BUILD_AS_INTERFACE}) + m_clangtidy("schema") + add_library(schema + ${SCHEMA_SRCS} + ) + target_include_directories(schema PUBLIC + $ + $ + ) + target_link_libraries(schema PUBLIC + env + nlohmann_json::nlohmann_json + ) + target_include_directories(schema PRIVATE + ${SCHEMA_BUILD_DIR} + ) + target_compile_options(schema PRIVATE ${BUILD_COMPILE_FLAGS}) + target_link_options(schema PRIVATE ${BUILD_LINK_FLAGS}) +endif() + +if (${BUILDCC_INSTALL}) + if (${BUILDCC_BUILD_AS_INTERFACE}) + install(TARGETS schema DESTINATION lib EXPORT schemaConfig) + install(EXPORT schemaConfig DESTINATION "${BUILDCC_INSTALL_LIB_PREFIX}/schema") + endif() + install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ DESTINATION "${BUILDCC_INSTALL_HEADER_PREFIX}") +endif() diff --git a/buildcc/schema/generator.fbs b/buildcc/schema/generator.fbs deleted file mode 100644 index 08166702..00000000 --- a/buildcc/schema/generator.fbs +++ /dev/null @@ -1,27 +0,0 @@ -// 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. - -include "path.fbs"; - -namespace schema.internal; - -// Each generator consists of many relational files of [input] - [output] - [commands] -table Generator { - name:string (key); - inputs:[Path]; - outputs:[string]; - commands:[string]; -} - -root_type Generator; diff --git a/buildcc/schema/include/schema/custom_generator_schema.h b/buildcc/schema/include/schema/custom_generator_schema.h new file mode 100644 index 00000000..a4088a66 --- /dev/null +++ b/buildcc/schema/include/schema/custom_generator_schema.h @@ -0,0 +1,75 @@ +/* + * 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 SCHEMA_CUSTOM_GENERATOR_SCHEMA_H_ +#define SCHEMA_CUSTOM_GENERATOR_SCHEMA_H_ + +#include + +#include "schema/path.h" + +namespace buildcc::internal { + +struct CustomGeneratorSchema { +private: + static constexpr const char *const kName = "name"; + static constexpr const char *const kIds = "ids"; + +public: + using IdKey = std::string; + struct IdInfo { + private: + static constexpr const char *const kInputs = "inputs"; + static constexpr const char *const kOutputs = "outputs"; + static constexpr const char *const kUserblob = "userblob"; + + public: + PathInfoList inputs; + PathList outputs; + std::vector userblob; + + friend void to_json(json &j, const IdInfo &info) { + j[kInputs] = info.inputs; + j[kOutputs] = info.outputs; + j[kUserblob] = info.userblob; + } + + friend void from_json(const json &j, IdInfo &info) { + j.at(kInputs).get_to(info.inputs); + j.at(kOutputs).get_to(info.outputs); + j.at(kUserblob).get_to(info.userblob); + } + }; + + using IdPair = std::pair; + + std::string name; + std::unordered_map internal_ids; + + friend void to_json(json &j, const CustomGeneratorSchema &schema) { + j[kName] = schema.name; + j[kIds] = schema.internal_ids; + } + + friend void from_json(const json &j, CustomGeneratorSchema &schema) { + j.at(kName).get_to(schema.name); + j.at(kIds).get_to(schema.internal_ids); + } +}; + +} // namespace buildcc::internal + +#endif diff --git a/buildcc/schema/include/schema/custom_generator_serialization.h b/buildcc/schema/include/schema/custom_generator_serialization.h new file mode 100644 index 00000000..45b03a1f --- /dev/null +++ b/buildcc/schema/include/schema/custom_generator_serialization.h @@ -0,0 +1,52 @@ +/* + * 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 SCHEMA_CUSTOM_GENERATOR_SERIALIZATION_H_ +#define SCHEMA_CUSTOM_GENERATOR_SERIALIZATION_H_ + +#include +#include +#include + +#include "schema/interface/serialization_interface.h" + +#include "schema/custom_generator_schema.h" +#include "schema/path.h" + +namespace buildcc::internal { + +class CustomGeneratorSerialization : public SerializationInterface { +public: + explicit CustomGeneratorSerialization(const fs::path &serialized_file) + : SerializationInterface(serialized_file) {} + + void UpdateStore(const CustomGeneratorSchema &store) { store_ = store; } + const CustomGeneratorSchema &GetLoad() const { return load_; } + const CustomGeneratorSchema &GetStore() const { return store_; } + +private: + bool Verify(const std::string &serialized_data) override; + bool Load(const std::string &serialized_data) override; + bool Store(const fs::path &absolute_serialized_file) override; + +private: + CustomGeneratorSchema load_; + CustomGeneratorSchema store_; +}; + +} // namespace buildcc::internal + +#endif diff --git a/buildcc/schema/include/schema/interface/serialization_interface.h b/buildcc/schema/include/schema/interface/serialization_interface.h new file mode 100644 index 00000000..c09bdde2 --- /dev/null +++ b/buildcc/schema/include/schema/interface/serialization_interface.h @@ -0,0 +1,76 @@ +/* + * 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 SCHEMA_INTERFACE_SERIALIZATION_INTERFACE_H_ +#define SCHEMA_INTERFACE_SERIALIZATION_INTERFACE_H_ + +#include + +#include "env/assert_fatal.h" +#include "env/util.h" + +#include "schema/path.h" + +namespace fs = std::filesystem; + +namespace buildcc::internal { + +class SerializationInterface { +public: + explicit SerializationInterface(const fs::path &serialized_file) + : serialized_file_(serialized_file) {} + virtual ~SerializationInterface() = default; + + bool LoadFromFile() { + std::string buffer; + + // Read from serialized file + bool is_loaded = + env::load_file(path_as_string(serialized_file_).c_str(), true, &buffer); + if (!is_loaded) { + return false; + } + + // Verify serialized data as per schema + if (!Verify(buffer)) { + return false; + } + + // Load serialized data as C++ data + loaded_ = Load(buffer); + return loaded_; + } + + bool StoreToFile() { return Store(serialized_file_); } + + const fs::path &GetSerializedFile() const noexcept { + return serialized_file_; + } + bool IsLoaded() const noexcept { return loaded_; } + +private: + virtual bool Verify(const std::string &serialized_data) = 0; + virtual bool Load(const std::string &serialized_data) = 0; + virtual bool Store(const fs::path &absolute_serialized_file) = 0; + +private: + fs::path serialized_file_; + bool loaded_{false}; +}; + +} // namespace buildcc::internal + +#endif diff --git a/buildcc/schema/include/schema/path.h b/buildcc/schema/include/schema/path.h new file mode 100644 index 00000000..0c07863f --- /dev/null +++ b/buildcc/schema/include/schema/path.h @@ -0,0 +1,260 @@ +/* + * 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 SCHEMA_PATH_H_ +#define SCHEMA_PATH_H_ + +#include +#include +#include +#include + +// Env +#include "env/assert_fatal.h" + +// Third party +#include "fmt/format.h" +#include "nlohmann/json.hpp" + +namespace fs = std::filesystem; +using json = nlohmann::ordered_json; + +namespace buildcc::internal { + +struct PathInfo { +private: + static constexpr const char *const kPath = "path"; + static constexpr const char *const kHash = "hash"; + +public: + PathInfo() = default; + PathInfo(const std::string &p, const std::string &h) : path(p), hash(h) {} + + bool operator==(const PathInfo &other) const { + return ((path == other.path) && (hash == other.hash)); + } + + /** + * @brief Sanitizes a fs::path or std::string to a standard path string + * - Converts backslash (\) to forward slash (/) + * - Makes fs::lexically_normal (see std::filesystem library impl) + * + * @param str User provided fs::path/std::string + * @return std::string Sanitized path as std::string + */ + static std::string ToPathString(const fs::path &p) { + return fs::path(p).make_preferred().lexically_normal().string(); + } + + /** + * @brief Formats a fs::path or std::string for display + * - All the sanitization as done in `ToPathString` + * Additionally + * - Adds quotation marks ("") when a space is detected + * For example: test/hello world/ -> "test/hello world/" + * + * NOTE: Use this API only in places where you would like to output to + * console/run or create command through subprocess + * + * @param str User provided fs::path/std::string + * @return std::string Sanitized path as std::string for display + */ + static std::string ToPathDisplayString(const fs::path &p) { + auto path_str = ToPathString(p); + // if spaces are present in the path string, surround this with brackets + if (path_str.find(' ') != std::string::npos) { + path_str = fmt::format("\"{}\"", path_str); + } + return path_str; + } + + friend void to_json(json &j, const PathInfo &info) { + j[kPath] = info.path; + j[kHash] = info.hash; + } + + friend void from_json(const json &j, PathInfo &info) { + j.at(kPath).get_to(info.path); + j.at(kHash).get_to(info.hash); + } + + std::string path; + std::string hash; +}; + +/** + * @brief Stores path + */ +class PathList { +public: + PathList() = default; + PathList(std::initializer_list paths) { + for (const auto &path : paths) { + Emplace(path); + } + } + + void Emplace(const fs::path &p) { + auto path_str = PathInfo::ToPathString(p); + paths_.emplace_back(std::move(path_str)); + } + + // TODO, Create a move version of Emplace(std::string &&pstr) + + void Insert(const PathList &other) { + paths_.insert(paths_.end(), other.paths_.begin(), other.paths_.end()); + } + + // TODO, Create a move version of Insert (PathList &&) + + // TODO, Remove this (redundant, use operator == overload instead) + bool IsEqual(const PathList &other) const { return paths_ == other.paths_; } + + const std::vector &GetPaths() const { return paths_; } + + std::unordered_set GetUnorderedPaths() const { + std::unordered_set unordered_paths(paths_.begin(), + paths_.end()); + return unordered_paths; + } + + bool operator==(const PathList &other) const { return IsEqual(other); } + + friend void to_json(json &j, const PathList &plist) { j = plist.paths_; } + + friend void from_json(const json &j, PathList &plist) { + j.get_to(plist.paths_); + } + +private: + std::vector paths_; +}; + +/** + * @brief Stores path + path hash in a hashmap + * + */ +class PathInfoList { +public: + PathInfoList() = default; + explicit PathInfoList( + std::initializer_list> + path_infos) { + for (const auto &pinfo : path_infos) { + Emplace(pinfo.first, pinfo.second); + } + } + + void Emplace(const fs::path &p, const std::string &hash) { + auto path_str = PathInfo::ToPathString(p); + infos_.emplace_back(PathInfo(path_str, hash)); + } + + // TODO, Create a move version of Emplace(std::string &&pstr, std::string + // &&hash) + + void Insert(const PathInfoList &other) { + infos_.insert(infos_.end(), other.infos_.begin(), other.infos_.end()); + } + + // TODO, Create a move version of Insert(PathInfoList &&other) + + void ComputeHashForAll() { + for (auto &info : infos_) { + info.hash = ComputeHash(info.path); + } + } + + // TODO, Remove redundant function (use operator == overload) + bool IsEqual(const PathInfoList &other) const { + return infos_ == other.infos_; + } + + const std::vector &GetPathInfos() const { return infos_; } + + std::unordered_map GetUnorderedPathInfos() const { + std::unordered_map unordered_path_infos; + for (const auto &info : infos_) { + unordered_path_infos.try_emplace(info.path, info.hash); + } + return unordered_path_infos; + } + + std::vector GetPaths() const { + std::vector paths; + for (const auto &info : infos_) { + paths.emplace_back(info.path); + } + return paths; + } + + // TODO, Add Compute Strategy enum + static std::string ComputeHash(const std::string &pstr) { + auto path_str = PathInfo::ToPathString(pstr); + + // TODO, There might be a file checksum hash compute strategy + // This is the timestamp hash compute strategy + std::error_code errcode; + const std::uint64_t last_write_timestamp = + std::filesystem::last_write_time(path_str, errcode) + .time_since_epoch() + .count(); + env::assert_fatal(errcode.value() == 0, + fmt::format("{} not found", path_str)); + return std::to_string(last_write_timestamp); + } + + bool operator==(const PathInfoList &other) const { return IsEqual(other); } + + friend void to_json(json &j, const PathInfoList &plist) { j = plist.infos_; } + + friend void from_json(const json &j, PathInfoList &plist) { + j.get_to(plist.infos_); + } + +private: + std::vector infos_; +}; + +} // namespace buildcc::internal + +namespace buildcc { + +inline std::string path_as_string(const fs::path &p) { + return internal::PathInfo::ToPathString(p); +} + +inline std::string path_as_display_string(const fs::path &p) { + return internal::PathInfo::ToPathDisplayString(p); +} + +inline fs::path string_as_path(const std::string &p) { + return path_as_string(p); +} + +} // namespace buildcc + +// FMT specialization + +template <> struct fmt::formatter : formatter { + template + auto format(const fs::path &p, FormatContext &ctx) { + return formatter::format(buildcc::path_as_display_string(p), + ctx); + } +}; + +#endif diff --git a/buildcc/schema/include/schema/target_schema.h b/buildcc/schema/include/schema/target_schema.h new file mode 100644 index 00000000..474bcb86 --- /dev/null +++ b/buildcc/schema/include/schema/target_schema.h @@ -0,0 +1,144 @@ +/* + * 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 SCHEMA_TARGET_SCHEMA_H_ +#define SCHEMA_TARGET_SCHEMA_H_ + +#include +#include + +#include "schema/path.h" +#include "schema/target_type.h" + +namespace buildcc::internal { + +struct TargetSchema { + std::string name; + TargetType type{TargetType::Undefined}; + + PathInfoList sources; + PathInfoList headers; + PathInfoList pchs; + + PathInfoList libs; + std::vector external_libs; + + PathList include_dirs; + PathList lib_dirs; + + std::vector preprocessor_flags; + std::vector common_compile_flags; + std::vector pch_compile_flags; + std::vector pch_object_flags; + std::vector asm_compile_flags; + std::vector c_compile_flags; + std::vector cpp_compile_flags; + std::vector link_flags; + + PathInfoList compile_dependencies; + PathInfoList link_dependencies; + + // TODO, Verify this using fs::exists + bool pch_compiled{false}; + bool target_linked{false}; + +private: + static constexpr const char *const kName = "name"; + static constexpr const char *const kType = "type"; + + static constexpr const char *const kSources = "sources"; + static constexpr const char *const kHeaders = "headers"; + static constexpr const char *const kPchs = "pchs"; + static constexpr const char *const kLibs = "libs"; + static constexpr const char *const kExternalLibs = "external_libs"; + + static constexpr const char *const kIncludeDirs = "include_dirs"; + static constexpr const char *const kLibDirs = "lib_dirs"; + + static constexpr const char *const kPreprocessorFlags = "preprocessor_flags"; + static constexpr const char *const kCommonCompileFlags = + "common_compile_flags"; + static constexpr const char *const kPchCompileFlags = "pch_compile_flags"; + static constexpr const char *const kPchObjectFlags = "pch_object_flags"; + static constexpr const char *const kAsmCompileFlags = "asm_compile_flags"; + static constexpr const char *const kCCompileFlags = "c_compile_flags"; + static constexpr const char *const kCppCompileFlags = "cpp_compile_flags"; + static constexpr const char *const kLinkFlags = "link_flags"; + + static constexpr const char *const kCompileDependencies = + "compile_dependencies"; + static constexpr const char *const kLinkDependencies = "link_dependencies"; + + static constexpr const char *const kPchCompiled = "pch_compiled"; + static constexpr const char *const kTargetLinked = "target_linked"; + +public: + friend void to_json(json &j, const TargetSchema &schema) { + j[kName] = schema.name; + j[kType] = schema.type; + j[kSources] = schema.sources; + j[kHeaders] = schema.headers; + j[kPchs] = schema.pchs; + j[kLibs] = schema.libs; + j[kExternalLibs] = schema.external_libs; + j[kIncludeDirs] = schema.include_dirs; + j[kLibDirs] = schema.lib_dirs; + + j[kPreprocessorFlags] = schema.preprocessor_flags; + j[kCommonCompileFlags] = schema.common_compile_flags; + j[kPchCompileFlags] = schema.pch_compile_flags; + j[kPchObjectFlags] = schema.pch_object_flags; + j[kAsmCompileFlags] = schema.asm_compile_flags; + j[kCCompileFlags] = schema.c_compile_flags; + j[kCppCompileFlags] = schema.cpp_compile_flags; + j[kLinkFlags] = schema.link_flags; + + j[kCompileDependencies] = schema.compile_dependencies; + j[kLinkDependencies] = schema.link_dependencies; + j[kPchCompiled] = schema.pch_compiled; + j[kTargetLinked] = schema.target_linked; + } + + friend void from_json(const json &j, TargetSchema &schema) { + j.at(kName).get_to(schema.name); + j.at(kType).get_to(schema.type); + j.at(kSources).get_to(schema.sources); + j.at(kHeaders).get_to(schema.headers); + j.at(kPchs).get_to(schema.pchs); + j.at(kLibs).get_to(schema.libs); + j.at(kExternalLibs).get_to(schema.external_libs); + j.at(kIncludeDirs).get_to(schema.include_dirs); + j.at(kLibDirs).get_to(schema.lib_dirs); + + j.at(kPreprocessorFlags).get_to(schema.preprocessor_flags); + j.at(kCommonCompileFlags).get_to(schema.common_compile_flags); + j.at(kPchCompileFlags).get_to(schema.pch_compile_flags); + j.at(kPchObjectFlags).get_to(schema.pch_object_flags); + j.at(kAsmCompileFlags).get_to(schema.asm_compile_flags); + j.at(kCCompileFlags).get_to(schema.c_compile_flags); + j.at(kCppCompileFlags).get_to(schema.cpp_compile_flags); + j.at(kLinkFlags).get_to(schema.link_flags); + + j.at(kCompileDependencies).get_to(schema.compile_dependencies); + j.at(kLinkDependencies).get_to(schema.link_dependencies); + j.at(kPchCompiled).get_to(schema.pch_compiled); + j.at(kTargetLinked).get_to(schema.target_linked); + } +}; + +} // namespace buildcc::internal + +#endif diff --git a/buildcc/schema/include/schema/target_serialization.h b/buildcc/schema/include/schema/target_serialization.h new file mode 100644 index 00000000..f8cc532b --- /dev/null +++ b/buildcc/schema/include/schema/target_serialization.h @@ -0,0 +1,56 @@ +/* + * 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 SCHEMA_TARGET_SERIALIZATION_H_ +#define SCHEMA_TARGET_SERIALIZATION_H_ + +#include + +#include "schema/path.h" +#include "schema/target_schema.h" + +#include "schema/interface/serialization_interface.h" + +namespace buildcc::internal { + +class TargetSerialization : public SerializationInterface { +public: + TargetSerialization(const fs::path &serialized_file) + : SerializationInterface(serialized_file) {} + + void UpdatePchCompiled(const TargetSchema &store); + void UpdateTargetCompiled(); + void AddSource(const std::string &source, const std::string &hash); + void UpdateStore(const TargetSchema &store); + + const TargetSchema &GetLoad() const { return load_; } + const TargetSchema &GetStore() const { return store_; } + +private: + bool Verify(const std::string &serialized_data) override; + bool Load(const std::string &serialized_data) override; + bool Store(const fs::path &absolute_serialized_file) override; + +private: + TargetSchema load_; + TargetSchema store_; + + std::mutex add_source_mutex; +}; + +} // namespace buildcc::internal + +#endif diff --git a/buildcc/schema/include/schema/target_type.h b/buildcc/schema/include/schema/target_type.h new file mode 100644 index 00000000..2871551a --- /dev/null +++ b/buildcc/schema/include/schema/target_type.h @@ -0,0 +1,66 @@ +/* + * 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 SCHEMA_TARGET_TYPE_H_ +#define SCHEMA_TARGET_TYPE_H_ + +#include +#include +#include + +namespace buildcc { + +enum class TargetType { + Executable, ///< Executable Target type + StaticLibrary, ///< Static library target type + DynamicLibrary, ///< Dynamic library target type + Undefined, ///< Undefined target type +}; + +constexpr std::array, 3> + kTargetTypeInfo{ + std::make_pair("executable", TargetType::Executable), + std::make_pair("static_library", TargetType::StaticLibrary), + std::make_pair("dynamic_library", TargetType::DynamicLibrary), + }; + +template void to_json(JsonType &j, TargetType type) { + j = nullptr; + auto iter = std::find_if(kTargetTypeInfo.cbegin(), kTargetTypeInfo.cend(), + [type](const auto &p) { return p.second == type; }); + if (iter != kTargetTypeInfo.cend()) { + j = iter->first; + } +} + +template +void from_json(const JsonType &j, TargetType &type) { + type = TargetType::Undefined; + if (j.is_string()) { + std::string name; + j.get_to(name); + auto iter = + std::find_if(kTargetTypeInfo.cbegin(), kTargetTypeInfo.cend(), + [&name](const auto &p) { return p.first == name; }); + if (iter != kTargetTypeInfo.cend()) { + type = iter->second; + } + } +} + +} // namespace buildcc + +#endif diff --git a/buildcc/schema/path.fbs b/buildcc/schema/path.fbs deleted file mode 100644 index 3407649e..00000000 --- a/buildcc/schema/path.fbs +++ /dev/null @@ -1,20 +0,0 @@ -// 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. - -namespace schema.internal; - -table Path { - pathname:string (key); - last_write_timestamp:uint64; -} diff --git a/buildcc/schema/src/custom_generator_serialization.cpp b/buildcc/schema/src/custom_generator_serialization.cpp new file mode 100644 index 00000000..e71d0097 --- /dev/null +++ b/buildcc/schema/src/custom_generator_serialization.cpp @@ -0,0 +1,54 @@ +/* + * 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. + */ + +#include "schema/custom_generator_serialization.h" + +// Third party +#include "nlohmann/json.hpp" + +namespace buildcc::internal { + +// PRIVATE + +bool CustomGeneratorSerialization::Verify(const std::string &serialized_data) { + (void)serialized_data; + return true; +} + +bool CustomGeneratorSerialization::Load(const std::string &serialized_data) { + json j = json::parse(serialized_data, nullptr, false); + bool loaded = !j.is_discarded(); + + if (loaded) { + try { + load_ = j.get(); + } catch (const std::exception &e) { + env::log_critical(__FUNCTION__, e.what()); + loaded = false; + } + } + return loaded; +} + +bool CustomGeneratorSerialization::Store( + const fs::path &absolute_serialized_file) { + json j = store_; + auto data = j.dump(4); + return env::save_file(path_as_string(absolute_serialized_file).c_str(), data, + false); +} + +} // namespace buildcc::internal diff --git a/buildcc/schema/src/target_serialization.cpp b/buildcc/schema/src/target_serialization.cpp new file mode 100644 index 00000000..55826069 --- /dev/null +++ b/buildcc/schema/src/target_serialization.cpp @@ -0,0 +1,75 @@ +/* + * 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. + */ + +#include "schema/target_serialization.h" + +namespace buildcc::internal { + +// PUBLIC +void TargetSerialization::UpdatePchCompiled(const TargetSchema &store) { + store_.pchs = store.pchs; + store_.pch_compiled = true; +} + +// TODO, When you add source here you need to add it with the hash +void TargetSerialization::AddSource(const std::string &source, + const std::string &hash) { + std::scoped_lock guard(add_source_mutex); + store_.sources.Emplace(source, hash); +} + +void TargetSerialization::UpdateTargetCompiled() { + store_.target_linked = true; +} + +void TargetSerialization::UpdateStore(const TargetSchema &store) { + TargetSchema temp = store; + temp.pchs = store_.pchs; + temp.pch_compiled = store_.pch_compiled; + temp.sources = store_.sources; + temp.target_linked = store_.target_linked; + store_ = std::move(temp); +} + +// PRIVATE +bool TargetSerialization::Verify(const std::string &serialized_data) { + (void)serialized_data; + return true; +} + +bool TargetSerialization::Load(const std::string &serialized_data) { + json j = json::parse(serialized_data, nullptr, false); + bool loaded = !j.is_discarded(); + + if (loaded) { + try { + load_ = j.get(); + } catch (const std::exception &e) { + env::log_critical(__FUNCTION__, e.what()); + loaded = false; + } + } + return loaded; +} + +bool TargetSerialization::Store(const fs::path &absolute_serialized_file) { + json j = store_; + auto data = j.dump(4); + return env::save_file(path_as_string(absolute_serialized_file).c_str(), data, + false); +} + +} // namespace buildcc::internal diff --git a/buildcc/schema/target.fbs b/buildcc/schema/target.fbs deleted file mode 100644 index 0eff9a0e..00000000 --- a/buildcc/schema/target.fbs +++ /dev/null @@ -1,67 +0,0 @@ -// 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. - -include "path.fbs"; - -namespace schema.internal; - -enum TargetType : byte { - Executable, - StaticLibrary, - DynamicLibrary -} - -// TODO, Check if Toolchain needs to be added to Target -table Target { - // Metadata - name:string (key); - type:TargetType; - - // Input - // Files - source_files:[Path]; - header_files:[Path]; - pch_files:[Path]; - lib_deps:[Path]; - - // Links - external_lib_deps:[string]; - - // Directories - include_dirs:[string]; - lib_dirs:[string]; - - // Flags - preprocessor_flags:[string]; - common_compile_flags:[string]; - pch_compile_flags:[string]; - pch_object_flags:[string]; - asm_compile_flags:[string]; - c_compile_flags:[string]; - cpp_compile_flags:[string]; - link_flags:[string]; - - // Additional dependencies - compile_dependencies:[Path]; - link_dependencies:[Path]; - - // Output - // Does not need to be stored - - // State - pch_compiled:bool; - target_linked:bool; -} - -root_type Target; diff --git a/buildcc/schema/test/.gitignore b/buildcc/schema/test/.gitignore new file mode 100644 index 00000000..7628df5e --- /dev/null +++ b/buildcc/schema/test/.gitignore @@ -0,0 +1,2 @@ +*.bin +*.json diff --git a/buildcc/schema/test/dump/.gitkeep b/buildcc/schema/test/dump/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/buildcc/schema/test/test_custom_generator_serialization.cpp b/buildcc/schema/test/test_custom_generator_serialization.cpp new file mode 100644 index 00000000..e7411470 --- /dev/null +++ b/buildcc/schema/test/test_custom_generator_serialization.cpp @@ -0,0 +1,70 @@ +#include "schema/custom_generator_serialization.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(CustomGeneratorSerializationTestGroup) +{ + void teardown() { + mock().clear(); + } +}; +// clang-format on + +TEST(CustomGeneratorSerializationTestGroup, JsonParse_Failure) { + { + // JSON Parse fails + buildcc::internal::CustomGeneratorSerialization serialization( + "dump/CustomGeneratorJsonParseFailure.json"); + + buildcc::env::save_file(serialization.GetSerializedFile().string().c_str(), + std::string(""), false); + bool loaded = serialization.LoadFromFile(); + CHECK_FALSE(loaded); + } + + { + // Custom Generator Schema conversion fails + buildcc::internal::CustomGeneratorSerialization serialization( + "dump/CustomGeneratorJsonParseFailure.json"); + + auto data = R"({"name": ""})"; + buildcc::env::save_file(serialization.GetSerializedFile().string().c_str(), + data, false); + bool loaded = serialization.LoadFromFile(); + CHECK_FALSE(loaded); + } +} + +TEST(CustomGeneratorSerializationTestGroup, FormatEmptyCheck) { + buildcc::internal::CustomGeneratorSerialization serialization( + "dump/CustomGeneratorFormatEmptyCheck.json"); + + bool stored = serialization.StoreToFile(); + CHECK_TRUE(stored); +} + +TEST(CustomGeneratorSerializationTestGroup, EmptyFile_Failure) { + { + buildcc::internal::CustomGeneratorSerialization serialization( + "dump/CustomGeneratorEmptyFile.json"); + CHECK_FALSE(serialization.LoadFromFile()); + } + + { + buildcc::internal::CustomGeneratorSerialization serialization( + "dump/CustomGeneratorEmptyFile.json"); + buildcc::env::save_file(serialization.GetSerializedFile().string().c_str(), + "", false); + CHECK_FALSE(serialization.LoadFromFile()); + } +} + +int main(int ac, char **av) { + return CommandLineTestRunner::RunAllTests(ac, av); +} diff --git a/buildcc/schema/test/test_path_schema.cpp b/buildcc/schema/test/test_path_schema.cpp new file mode 100644 index 00000000..fe5543b2 --- /dev/null +++ b/buildcc/schema/test/test_path_schema.cpp @@ -0,0 +1,147 @@ +// Internal +#include "schema/path.h" + +#include "env/host_os.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" + +// clang-format off +TEST_GROUP(PathSchemaTestGroup) +{ +}; +// clang-format on + +TEST(PathSchemaTestGroup, PathList) { buildcc::internal::PathList paths; } + +TEST(PathSchemaTestGroup, Path_ToPathString) { + auto path_str = + buildcc::internal::PathInfo::ToPathString("hello/\\first.txt"); + + if constexpr (buildcc::env::is_win()) { + STRCMP_EQUAL("hello\\first.txt", path_str.c_str()); + } else if constexpr (buildcc::env::is_linux() || buildcc::env::is_mac() || + buildcc::env::is_unix()) { + STRCMP_EQUAL("hello/\\first.txt", path_str.c_str()); + } else { + FAIL("Operating system not supported"); + } +} + +TEST(PathSchemaTestGroup, Path_ToPathDisplayString) { + auto path_str = buildcc::internal::PathInfo::ToPathDisplayString( + "hello/\\first/hello world.txt"); + + if constexpr (buildcc::env::is_win()) { + STRCMP_EQUAL("\"hello\\first\\hello world.txt\"", path_str.c_str()); + } else if constexpr (buildcc::env::is_linux() || buildcc::env::is_mac() || + buildcc::env::is_unix()) { + STRCMP_EQUAL("\"hello/\\first/hello world.txt\"", path_str.c_str()); + } else { + FAIL("Operating system not supported"); + } +} + +TEST(PathSchemaTestGroup, PathInfoList_OrderedEmplace) { + buildcc::internal::PathInfoList path_infos; + + path_infos.Emplace("hello/world/first_file.txt", ""); + path_infos.Emplace("hello/world/second_file.txt", ""); + path_infos.Emplace("hello/world/third_file.txt", ""); + path_infos.Emplace("hello/world/fourth_file.txt", ""); + + std::vector paths; + if constexpr (buildcc::env::is_win()) { + paths = { + "hello\\world\\first_file.txt", + "hello\\world\\second_file.txt", + "hello\\world\\third_file.txt", + "hello\\world\\fourth_file.txt", + }; + } else if constexpr (buildcc::env::is_linux() || buildcc::env::is_unix() || + buildcc::env::is_mac()) { + paths = { + "hello/world/first_file.txt", + "hello/world/second_file.txt", + "hello/world/third_file.txt", + "hello/world/fourth_file.txt", + }; + } else { + FAIL("Operating system not supported"); + } + + auto inserted_paths = path_infos.GetPaths(); + for (std::size_t i = 0; i < inserted_paths.size(); i++) { + STRCMP_EQUAL(paths[i].c_str(), inserted_paths[i].c_str()); + } + + json j = path_infos; + UT_PRINT(j.dump().c_str()); +} + +TEST(PathSchemaTestGroup, PathInfoList_GetChanged) { + { + buildcc::internal::PathInfoList pinfolist1{ + {"first.txt", "1"}, + {"second.txt", "2"}, + {"third.txt", "3"}, + }; + buildcc::internal::PathInfoList pinfolist2{ + {"first.txt", "1"}, + {"second.txt", "2"}, + {"third.txt", "3"}, + }; + CHECK_TRUE(pinfolist1.IsEqual(pinfolist2)); + } + + // Missing path info + { + buildcc::internal::PathInfoList pinfolist1{ + {"first.txt", "1"}, + {"second.txt", "2"}, + {"third.txt", "3"}, + }; + buildcc::internal::PathInfoList pinfolist2{ + {"first.txt", "1"}, {"second.txt", "2"}, + // {"third.txt", "3"}, + }; + CHECK_FALSE(pinfolist1.IsEqual(pinfolist2)); + } + + // Wrong hash + { + buildcc::internal::PathInfoList pinfolist1{ + {"first.txt", "1"}, + {"second.txt", "2"}, + {"third.txt", "3"}, + }; + buildcc::internal::PathInfoList pinfolist2{ + {"first.txt", "1"}, + {"second.txt", "2"}, + {"third.txt", "4"}, + }; + CHECK_FALSE(pinfolist1.IsEqual(pinfolist2)); + } + + // Wrong order + { + buildcc::internal::PathInfoList pinfolist1{ + {"first.txt", "1"}, + {"second.txt", "2"}, + {"third.txt", "3"}, + }; + buildcc::internal::PathInfoList pinfolist2{ + {"first.txt", "1"}, + {"third.txt", "3"}, + {"second.txt", "2"}, + }; + CHECK_FALSE(pinfolist1.IsEqual(pinfolist2)); + } +} + +int main(int ac, char **av) { + return CommandLineTestRunner::RunAllTests(ac, av); +} diff --git a/buildcc/schema/test/test_target_serialization.cpp b/buildcc/schema/test/test_target_serialization.cpp new file mode 100644 index 00000000..522a6b64 --- /dev/null +++ b/buildcc/schema/test/test_target_serialization.cpp @@ -0,0 +1,196 @@ +#include "schema/target_serialization.h" + +#include "nlohmann/json.hpp" + +using json = nlohmann::ordered_json; + +// 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(TargetSerializationTestGroup) +{ + void teardown() { + mock().clear(); + } +}; +// clang-format on + +TEST(TargetSerializationTestGroup, TargetType) { + buildcc::TargetType type; + json j; + + { + j = buildcc::kTargetTypeInfo[0].first; + from_json(j, type); + CHECK_TRUE(type == buildcc::kTargetTypeInfo[0].second); + } + + { + j = buildcc::kTargetTypeInfo[1].first; + from_json(j, type); + CHECK_TRUE(type == buildcc::kTargetTypeInfo[1].second); + } + + { + j = buildcc::kTargetTypeInfo[2].first; + from_json(j, type); + CHECK_TRUE(type == buildcc::kTargetTypeInfo[2].second); + } + + { + j = "should_not_exist"; + from_json(j, type); + CHECK_TRUE(type == buildcc::TargetType::Undefined); + } + + { + j = nullptr; + from_json(j, type); + CHECK_TRUE(type == buildcc::TargetType::Undefined); + } +} + +TEST(TargetSerializationTestGroup, TargetSerialization_TargetType) { + { + // Target Type executable + buildcc::internal::TargetSerialization serialization( + "dump/TargetTypeTest.json"); + + buildcc::internal::TargetSchema schema; + schema.type = buildcc::TargetType::Executable; + serialization.UpdateStore(schema); + bool store = serialization.StoreToFile(); + CHECK_TRUE(store); + + bool load = serialization.LoadFromFile(); + CHECK_TRUE(load); + CHECK_TRUE(serialization.GetLoad().type == buildcc::TargetType::Executable); + } + + { + // Target Type static library + buildcc::internal::TargetSerialization serialization( + "dump/TargetTypeTest.json"); + + buildcc::internal::TargetSchema schema; + schema.type = buildcc::TargetType::StaticLibrary; + serialization.UpdateStore(schema); + bool store = serialization.StoreToFile(); + CHECK_TRUE(store); + + bool load = serialization.LoadFromFile(); + CHECK_TRUE(load); + CHECK_TRUE(serialization.GetLoad().type == + buildcc::TargetType::StaticLibrary); + } + + { + // Target Type dynamic library + buildcc::internal::TargetSerialization serialization( + "dump/TargetTypeTest.json"); + + buildcc::internal::TargetSchema schema; + schema.type = buildcc::TargetType::DynamicLibrary; + serialization.UpdateStore(schema); + bool store = serialization.StoreToFile(); + CHECK_TRUE(store); + + bool load = serialization.LoadFromFile(); + CHECK_TRUE(load); + CHECK_TRUE(serialization.GetLoad().type == + buildcc::TargetType::DynamicLibrary); + } + + { + // Target Type undefined + buildcc::internal::TargetSerialization serialization( + "dump/TargetTypeTest.json"); + + buildcc::internal::TargetSchema schema; + schema.type = buildcc::TargetType::Undefined; + serialization.UpdateStore(schema); + bool store = serialization.StoreToFile(); + CHECK_TRUE(store); + + bool load = serialization.LoadFromFile(); + CHECK_TRUE(load); + CHECK_TRUE(serialization.GetLoad().type == buildcc::TargetType::Undefined); + } + + { + // Target Type random value + buildcc::internal::TargetSerialization serialization( + "dump/TargetTypeTest.json"); + + buildcc::internal::TargetSchema schema; + schema.type = (buildcc::TargetType)65535; + serialization.UpdateStore(schema); + bool store = serialization.StoreToFile(); + CHECK_TRUE(store); + + bool load = serialization.LoadFromFile(); + CHECK_TRUE(load); + CHECK_TRUE(serialization.GetLoad().type == buildcc::TargetType::Undefined); + } +} + +TEST(TargetSerializationTestGroup, JsonParse_Failure) { + { + // JSON Parse fails + buildcc::internal::TargetSerialization serialization( + "dump/TargetJsonParseFailure.json"); + + buildcc::env::save_file(serialization.GetSerializedFile().string().c_str(), + std::string(""), false); + bool loaded = serialization.LoadFromFile(); + CHECK_FALSE(loaded); + } + + { + // Custom Generator Schema conversion fails + buildcc::internal::TargetSerialization serialization( + "dump/TargetJsonParseFailure.json"); + + auto data = R"({"name": ""})"; + buildcc::env::save_file(serialization.GetSerializedFile().string().c_str(), + data, false); + bool loaded = serialization.LoadFromFile(); + CHECK_FALSE(loaded); + } +} + +TEST(TargetSerializationTestGroup, FormatEmptyCheck) { + buildcc::internal::TargetSerialization serialization( + "dump/TargetFormatEmptyCheck.json"); + + bool stored = serialization.StoreToFile(); + CHECK_TRUE(stored); + + bool loaded = serialization.LoadFromFile(); + CHECK_TRUE(loaded); +} + +TEST(TargetSerializationTestGroup, EmptyFile_Failure) { + { + buildcc::internal::TargetSerialization serialization( + "dump/TargetEmptyFile.json"); + CHECK_FALSE(serialization.LoadFromFile()); + } + + { + buildcc::internal::TargetSerialization serialization( + "dump/TargetEmptyFile.json"); + buildcc::env::save_file(serialization.GetSerializedFile().string().c_str(), + "", false); + CHECK_FALSE(serialization.LoadFromFile()); + } +} + +int main(int ac, char **av) { + return CommandLineTestRunner::RunAllTests(ac, av); +} diff --git a/buildcc/targets/include/targets/target_gcc.h b/buildcc/targets/include/targets/target_gcc.h index 60df007f..2e2dfb32 100644 --- a/buildcc/targets/include/targets/target_gcc.h +++ b/buildcc/targets/include/targets/target_gcc.h @@ -29,14 +29,6 @@ constexpr const char *const kGccExecutableExt = ""; constexpr const char *const kGccStaticLibExt = ".a"; constexpr const char *const kGccDynamicLibExt = ".so"; -constexpr const char *const kGccObjExt = ".o"; -constexpr const char *const kGccPchHeaderExt = ".h"; -constexpr const char *const kGccPchCompileExt = ".gch"; - -// GCC -constexpr const char *const kGccPrefixIncludeDir = "-I"; -constexpr const char *const kGccPrefixLibDir = "-L"; - constexpr const char *const kGccGenericPchCompileCommand = "{compiler} {preprocessor_flags} {include_dirs} {common_compile_flags} " "{pch_compile_flags} {compile_flags} -o {output} -c {input}"; @@ -72,11 +64,6 @@ class GccConfig : ConfigInterface { const std::string &link_command) { TargetConfig config; config.target_ext = target_ext; - config.obj_ext = kGccObjExt; - config.pch_header_ext = kGccPchHeaderExt; - config.pch_compile_ext = kGccPchCompileExt; - std::string prefix_include_dir = kGccPrefixIncludeDir; - std::string prefix_lib_dir = kGccPrefixLibDir; config.pch_command = kGccGenericPchCompileCommand; config.compile_command = compile_command; config.link_command = link_command; diff --git a/buildcc/targets/include/targets/target_generic.h b/buildcc/targets/include/targets/target_generic.h index 0d5d5ed0..0246c1ac 100644 --- a/buildcc/targets/include/targets/target_generic.h +++ b/buildcc/targets/include/targets/target_generic.h @@ -18,7 +18,6 @@ #define TARGETS_TARGET_GENERIC_H_ #include -#include #include "target/target.h" #include "toolchain/toolchain.h" @@ -137,7 +136,7 @@ class ExecutableTarget_generic : public BaseTarget { public: ExecutableTarget_generic(const std::string &name, const BaseToolchain &toolchain, const TargetEnv &env, - const std::optional &config = {}) + const env::optional &config = {}) : Target(name, TargetType::Executable, toolchain, env, config.value_or(GenericConfig::Executable(toolchain.GetId()))) { switch (toolchain.GetId()) { @@ -163,7 +162,7 @@ class StaticTarget_generic : public BaseTarget { public: StaticTarget_generic(const std::string &name, const BaseToolchain &toolchain, const TargetEnv &env, - const std::optional &config = {}) + const env::optional &config = {}) : Target(name, TargetType::StaticLibrary, toolchain, env, config.value_or(GenericConfig::StaticLib(toolchain.GetId()))) { switch (toolchain.GetId()) { @@ -188,7 +187,7 @@ class DynamicTarget_generic : public BaseTarget { public: DynamicTarget_generic(const std::string &name, const BaseToolchain &toolchain, const TargetEnv &env, - const std::optional &config = {}) + const env::optional &config = {}) : Target(name, TargetType::DynamicLibrary, toolchain, env, config.value_or(GenericConfig::DynamicLib(toolchain.GetId()))) { switch (toolchain.GetId()) { @@ -213,7 +212,7 @@ class Target_generic : public BaseTarget { public: Target_generic(const std::string &name, TargetType type, const BaseToolchain &toolchain, const TargetEnv &env, - const std::optional &config = {}) + const env::optional &config = {}) : Target( name, type, toolchain, env, config.value_or(GenericConfig::Generic(type, toolchain.GetId()))) { diff --git a/buildcc/targets/include/targets/target_mingw.h b/buildcc/targets/include/targets/target_mingw.h index 44f93720..abaeeb38 100644 --- a/buildcc/targets/include/targets/target_mingw.h +++ b/buildcc/targets/include/targets/target_mingw.h @@ -54,11 +54,6 @@ class MingwConfig : ConfigInterface { const std::string &link_command) { TargetConfig config; config.target_ext = target_ext; - config.obj_ext = kGccObjExt; - config.pch_header_ext = kGccPchHeaderExt; - config.pch_compile_ext = kGccPchCompileExt; - std::string prefix_include_dir = kGccPrefixIncludeDir; - std::string prefix_lib_dir = kGccPrefixLibDir; config.pch_command = kGccGenericPchCompileCommand; config.compile_command = compile_command; config.link_command = link_command; diff --git a/buildcc/targets/include/targets/target_msvc.h b/buildcc/targets/include/targets/target_msvc.h index 0d8e91ae..7baaae88 100644 --- a/buildcc/targets/include/targets/target_msvc.h +++ b/buildcc/targets/include/targets/target_msvc.h @@ -33,13 +33,6 @@ constexpr const char *const kMsvcStaticLibExt = ".lib"; // OUT .dll needs to be present in the executable folder during runtime constexpr const char *const kMsvcDynamicLibExt = ".lib"; -constexpr const char *const kMsvcObjExt = ".obj"; -constexpr const char *const kMsvcPchHeaderExt = ".h"; -constexpr const char *const kMsvcPchCompileExt = ".pch"; - -constexpr const char *const kMsvcPrefixIncludeDir = "/I"; -constexpr const char *const kMsvcPrefixLibDir = "/LIBPATH:"; - constexpr const char *const kMsvcPchCompileCommand = "{compiler} {preprocessor_flags} {include_dirs} {common_compile_flags} " "/Yc{input} /FI{input} /Fp{output} {pch_compile_flags} {compile_flags} " @@ -79,11 +72,6 @@ class MsvcConfig : ConfigInterface { const std::string &link_command) { TargetConfig config; config.target_ext = target_ext; - config.obj_ext = kMsvcObjExt; - config.pch_header_ext = kMsvcPchHeaderExt; - config.pch_compile_ext = kMsvcPchCompileExt; - config.prefix_include_dir = kMsvcPrefixIncludeDir; - config.prefix_lib_dir = kMsvcPrefixLibDir; config.pch_command = kMsvcPchCompileCommand; config.compile_command = compile_command; config.link_command = link_command; diff --git a/buildcc/toolchains/CMakeLists.txt b/buildcc/toolchains/CMakeLists.txt index d7e8b6c3..b1380fcd 100644 --- a/buildcc/toolchains/CMakeLists.txt +++ b/buildcc/toolchains/CMakeLists.txt @@ -1,36 +1,77 @@ set(TOOLCHAIN_SPECIALIZED_SRCS + src/toolchain_infos.cpp + include/toolchains/toolchain_infos.h + + src/toolchain_gcc.cpp include/toolchains/toolchain_gcc.h - include/toolchains/toolchain_msvc.h include/toolchains/toolchain_mingw.h + + src/toolchain_msvc.cpp + include/toolchains/toolchain_msvc.h + + src/toolchain_aggregate.cpp + include/toolchains/toolchain_generic.h + include/toolchains/toolchain_custom.h + + include/toolchains/toolchain_specialized.h ) +if (${TESTING}) + add_library(mock_toolchain_specialized + ${TOOLCHAIN_SPECIALIZED_SRCS} + ) + target_include_directories(mock_toolchain_specialized PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ) + target_compile_options(mock_toolchain_specialized PUBLIC + ${TEST_COMPILE_FLAGS} ${BUILD_COMPILE_FLAGS} + ) + target_link_options(mock_toolchain_specialized PUBLIC + ${TEST_LINK_FLAGS} ${BUILD_LINK_FLAGS} + ) + target_link_libraries(mock_toolchain_specialized PUBLIC + mock_toolchain + + CppUTest + CppUTestExt + ${TEST_LINK_LIBS} + ) + + add_executable(test_toolchain_specialized test/test_toolchain_specialized.cpp) + target_link_libraries(test_toolchain_specialized PRIVATE mock_toolchain_specialized) + + add_test(NAME test_toolchain_specialized COMMAND test_toolchain_specialized) +endif() + if(${BUILDCC_BUILD_AS_SINGLE_LIB}) target_sources(buildcc PRIVATE ${TOOLCHAIN_SPECIALIZED_SRCS} ) - target_include_directories(buildcc INTERFACE + target_include_directories(buildcc PUBLIC $ $ ) endif() -m_clangtidy("toolchain_specialized") -add_library(toolchain_specialized INTERFACE - ${TOOLCHAIN_SPECIALIZED_SRCS} -) -target_include_directories(toolchain_specialized INTERFACE - $ - $ -) -target_link_libraries(toolchain_specialized INTERFACE - toolchain -) +if(${BUILDCC_BUILD_AS_INTERFACE}) + m_clangtidy("toolchain_specialized") + add_library(toolchain_specialized STATIC + ${TOOLCHAIN_SPECIALIZED_SRCS} + ) + target_include_directories(toolchain_specialized PUBLIC + $ + $ + ) + target_link_libraries(toolchain_specialized PUBLIC + toolchain + ) +endif() if (${BUILDCC_INSTALL}) - install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ DESTINATION "${BUILDCC_INSTALL_HEADER_PREFIX}") if (${BUILDCC_BUILD_AS_INTERFACE}) # toolchain_specialized Install install(TARGETS toolchain_specialized DESTINATION lib EXPORT toolchain_specializedConfig) install(EXPORT toolchain_specializedConfig DESTINATION "${BUILDCC_INSTALL_LIB_PREFIX}/toolchain_specialized") endif() + install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ DESTINATION "${BUILDCC_INSTALL_HEADER_PREFIX}") endif() diff --git a/buildcc/toolchains/include/toolchains/toolchain_custom.h b/buildcc/toolchains/include/toolchains/toolchain_custom.h new file mode 100644 index 00000000..dd43dfc9 --- /dev/null +++ b/buildcc/toolchains/include/toolchains/toolchain_custom.h @@ -0,0 +1,44 @@ +/* + * 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 TOOLCHAINS_TOOLCHAIN_CUSTOM_H_ +#define TOOLCHAINS_TOOLCHAIN_CUSTOM_H_ + +#include "toolchain/toolchain.h" + +namespace buildcc { + +class Toolchain_custom : public Toolchain { +public: + // Run time basic constructor + Toolchain_custom(ToolchainId id, const std::string &name, + const ToolchainExecutables &executables, + const env::optional &op_config = {}) + : Toolchain(id, name, executables, + op_config.value_or(ToolchainConfig())) { + Initialize(); + } + + virtual ~Toolchain_custom() = default; + Toolchain_custom(const Toolchain_custom &) = delete; + +private: + void Initialize(); +}; + +} // namespace buildcc + +#endif diff --git a/buildcc/toolchains/include/toolchains/toolchain_gcc.h b/buildcc/toolchains/include/toolchains/toolchain_gcc.h index b7c490fb..60b15058 100644 --- a/buildcc/toolchains/include/toolchains/toolchain_gcc.h +++ b/buildcc/toolchains/include/toolchains/toolchain_gcc.h @@ -33,9 +33,22 @@ namespace buildcc { */ class Toolchain_gcc : public Toolchain { public: - Toolchain_gcc() - : Toolchain(Toolchain::Id::Gcc, "gcc", "as", "gcc", "g++", "ar", "ld") {} - Toolchain_gcc(const Toolchain_gcc &gcc) = delete; + // Run time basic constructor + Toolchain_gcc(const std::string &name = "gcc", + const env::optional &op_executables = {}, + const env::optional &op_config = {}) + : Toolchain(ToolchainId::Gcc, name, + op_executables.value_or( + ToolchainExecutables("as", "gcc", "g++", "ar", "ld")), + op_config.value_or(ToolchainConfig())) { + Initialize(); + } + + virtual ~Toolchain_gcc() = default; + Toolchain_gcc(const Toolchain_gcc &) = delete; + +private: + void Initialize(); }; } // namespace buildcc diff --git a/buildcc/toolchains/include/toolchains/toolchain_generic.h b/buildcc/toolchains/include/toolchains/toolchain_generic.h new file mode 100644 index 00000000..76adfb38 --- /dev/null +++ b/buildcc/toolchains/include/toolchains/toolchain_generic.h @@ -0,0 +1,41 @@ +/* + * 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 TOOLCHAINS_TOOLCHAIN_GENERIC_H_ +#define TOOLCHAINS_TOOLCHAIN_GENERIC_H_ + +#include "toolchain/toolchain.h" + +namespace buildcc { + +class Toolchain_generic { +public: + /** + * @brief Create a generic toolchain instance + * + * @return Toolchain& Returns the BaseToolchain with necessary virtual + * function overrides + * Asserts fatal if ToolchainId is not supported + */ + static Toolchain & + New(ToolchainId id, const std::string &identifier, + const env::optional &op_executables = {}, + const env::optional &op_config = {}); +}; + +} // namespace buildcc + +#endif diff --git a/buildcc/toolchains/include/toolchains/toolchain_infos.h b/buildcc/toolchains/include/toolchains/toolchain_infos.h new file mode 100644 index 00000000..111ef2b7 --- /dev/null +++ b/buildcc/toolchains/include/toolchains/toolchain_infos.h @@ -0,0 +1,51 @@ +/* + * 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 TOOLCHAINS_TOOLCHAIN_INFOS_H_ +#define TOOLCHAINS_TOOLCHAIN_INFOS_H_ + +#include + +#include "toolchain/toolchain.h" + +namespace buildcc { + +class GlobalToolchainMetadata { +public: + static const ToolchainConfig &GetConfig(ToolchainId id); + static const ToolchainInfoCb &GetInfoCb(ToolchainId id); + +private: + struct ToolchainMetadata { + ToolchainMetadata(const ToolchainConfig &config, const ToolchainInfoCb &cb) + : config_(config), cb_(cb) {} + + ToolchainConfig config_; + ToolchainInfoCb cb_; + }; + +private: + static void Expect(ToolchainId id); + static const ToolchainMetadata &Get(ToolchainId id); + +private: + static std::unordered_map + global_toolchain_metadata_; +}; + +} // namespace buildcc + +#endif diff --git a/buildcc/toolchains/include/toolchains/toolchain_mingw.h b/buildcc/toolchains/include/toolchains/toolchain_mingw.h index 44d4b09b..4b019169 100644 --- a/buildcc/toolchains/include/toolchains/toolchain_mingw.h +++ b/buildcc/toolchains/include/toolchains/toolchain_mingw.h @@ -19,6 +19,8 @@ #include "toolchain/toolchain.h" +#include "toolchain_gcc.h" + namespace buildcc { /** @@ -31,11 +33,25 @@ namespace buildcc { * archiver = "ar"
* linker = "ld"
*/ -class Toolchain_mingw : public BaseToolchain { +class Toolchain_mingw : public Toolchain { public: - Toolchain_mingw() - : Toolchain(ToolchainId::MinGW, "gcc", "as", "gcc", "g++", "ar", "ld") {} + // Run time basic constructor + Toolchain_mingw( + const std::string &name = "gcc", + const env::optional &op_executables = {}, + const env::optional &op_config = {}) + : Toolchain(ToolchainId::MinGW, name, + op_executables.value_or( + ToolchainExecutables("as", "gcc", "g++", "ar", "ld")), + op_config.value_or(ToolchainConfig())) { + Initialize(); + } + + virtual ~Toolchain_mingw() = default; Toolchain_mingw(const Toolchain_mingw &) = delete; + +private: + void Initialize(); }; } // namespace buildcc diff --git a/buildcc/toolchains/include/toolchains/toolchain_msvc.h b/buildcc/toolchains/include/toolchains/toolchain_msvc.h index f74a479d..8fbb8fdf 100644 --- a/buildcc/toolchains/include/toolchains/toolchain_msvc.h +++ b/buildcc/toolchains/include/toolchains/toolchain_msvc.h @@ -33,10 +33,22 @@ namespace buildcc { */ class Toolchain_msvc : public Toolchain { public: - Toolchain_msvc() - : Toolchain(Toolchain::Id::Msvc, "msvc", "cl", "cl", "cl", "lib", - "link") {} - Toolchain_msvc(const Toolchain_msvc &gcc) = delete; + // Run time basic constructor + Toolchain_msvc(const std::string &name = "msvc", + const env::optional &op_executables = {}, + const env::optional &op_config = {}) + : Toolchain(ToolchainId::Msvc, name, + op_executables.value_or( + ToolchainExecutables("cl", "cl", "cl", "lib", "link")), + op_config.value_or(ToolchainConfig())) { + Initialize(); + } + + virtual ~Toolchain_msvc() = default; + Toolchain_msvc(const Toolchain_msvc &) = delete; + +private: + void Initialize(); }; } // namespace buildcc diff --git a/buildcc/toolchains/include/toolchains/toolchain_specialized.h b/buildcc/toolchains/include/toolchains/toolchain_specialized.h new file mode 100644 index 00000000..5e316e09 --- /dev/null +++ b/buildcc/toolchains/include/toolchains/toolchain_specialized.h @@ -0,0 +1,32 @@ +/* + * 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 TOOLCHAINS_TOOLCHAIN_SPECIALIZED_H_ +#define TOOLCHAINS_TOOLCHAIN_SPECIALIZED_H_ + +// Common +#include "toolchain_infos.h" + +// Toolchain specialized implementation +#include "toolchain_gcc.h" +#include "toolchain_mingw.h" +#include "toolchain_msvc.h" + +// Aggregation +#include "toolchain_custom.h" +#include "toolchain_generic.h" + +#endif diff --git a/buildcc/toolchains/src/toolchain_aggregate.cpp b/buildcc/toolchains/src/toolchain_aggregate.cpp new file mode 100644 index 00000000..6a9526b0 --- /dev/null +++ b/buildcc/toolchains/src/toolchain_aggregate.cpp @@ -0,0 +1,84 @@ +/* + * 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. + */ + +#include "toolchains/toolchain_infos.h" + +#include "toolchains/toolchain_generic.h" + +#include "toolchains/toolchain_custom.h" +#include "toolchains/toolchain_gcc.h" +#include "toolchains/toolchain_mingw.h" +#include "toolchains/toolchain_msvc.h" + +#include "env/assert_fatal.h" +#include "env/storage.h" + +namespace { + +template +buildcc::Toolchain *AddIf(const std::string &identifier, Params &&...params) { + buildcc::Toolchain *toolchain{nullptr}; + if (!buildcc::Storage::Contains(identifier)) { + toolchain = &buildcc::Storage::Add(identifier, + std::forward(params)...); + } + return toolchain; +} + +} // namespace + +namespace buildcc { + +void Toolchain_custom::Initialize() { + auto id = GetId(); + RefConfig() = GlobalToolchainMetadata::GetConfig(id); + SetToolchainInfoCb(GlobalToolchainMetadata::GetInfoCb(id)); +} + +Toolchain &Toolchain_generic::New( + ToolchainId id, const std::string &identifier, + const env::optional &op_executables, + const env::optional &op_config) { + Toolchain *toolchain{nullptr}; + switch (id) { + case ToolchainId::Gcc: + toolchain = + AddIf(identifier, identifier, op_executables, op_config); + break; + case ToolchainId::Msvc: + toolchain = AddIf(identifier, identifier, op_executables, + op_config); + break; + case ToolchainId::MinGW: + toolchain = AddIf(identifier, identifier, op_executables, + op_config); + break; + case ToolchainId::Clang: + case ToolchainId::Custom: + env::assert_fatal(op_executables.has_value(), + "ToolchainId::Custom and ToolchainId::Clang require " + "executables to be provided"); + toolchain = AddIf(identifier, id, identifier, + op_executables.value(), op_config); + break; + default: + break; + } + env::assert_fatal(toolchain != nullptr, "Toolchain could not be created"); + return *toolchain; +} + +} // namespace buildcc diff --git a/buildcc/toolchains/src/toolchain_gcc.cpp b/buildcc/toolchains/src/toolchain_gcc.cpp new file mode 100644 index 00000000..19b72402 --- /dev/null +++ b/buildcc/toolchains/src/toolchain_gcc.cpp @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#include "toolchains/toolchain_gcc.h" +#include "toolchains/toolchain_mingw.h" + +#include "toolchains/toolchain_infos.h" + +#include "env/command.h" + +namespace buildcc { + +void Toolchain_gcc::Initialize() { + RefConfig() = GlobalToolchainMetadata::GetConfig(ToolchainId::Gcc); + SetToolchainInfoCb(GlobalToolchainMetadata::GetInfoCb(ToolchainId::Gcc)); +} + +void Toolchain_mingw::Initialize() { + RefConfig() = GlobalToolchainMetadata::GetConfig(ToolchainId::MinGW); + SetToolchainInfoCb(GlobalToolchainMetadata::GetInfoCb(ToolchainId::MinGW)); +} + +} // namespace buildcc diff --git a/buildcc/toolchains/src/toolchain_infos.cpp b/buildcc/toolchains/src/toolchain_infos.cpp new file mode 100644 index 00000000..1b7a1fc4 --- /dev/null +++ b/buildcc/toolchains/src/toolchain_infos.cpp @@ -0,0 +1,185 @@ +/* + * 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. + */ + +#include "toolchains/toolchain_infos.h" + +#include "env/assert_fatal.h" +#include "env/command.h" +#include "env/logging.h" + +namespace { + +// GCC +constexpr const char *const kGccObjExt = ".o"; +constexpr const char *const kGccPchHeaderExt = ".h"; +constexpr const char *const kGccPchCompileExt = ".gch"; +constexpr const char *const kGccPrefixIncludeDir = "-I"; +constexpr const char *const kGccPrefixLibDir = "-L"; +buildcc::ToolchainConfig GetGccToolchainConfig() { + buildcc::ToolchainConfig config; + config.obj_ext = kGccObjExt; + config.pch_header_ext = kGccPchHeaderExt; + config.pch_compile_ext = kGccPchCompileExt; + config.prefix_include_dir = kGccPrefixIncludeDir; + config.prefix_lib_dir = kGccPrefixLibDir; + return config; +} + +buildcc::env::optional +GetGccCompilerVersion(const buildcc::env::Command &command) { + std::vector stdout_data; + bool executed = buildcc::env::Command::Execute( + command.Construct("{compiler} -dumpversion"), {}, &stdout_data); + if (!executed || stdout_data.empty()) { + return {}; + } + return stdout_data[0]; +} + +buildcc::env::optional +GetGccTargetArchitecture(const buildcc::env::Command &command) { + std::vector stdout_data; + bool executed = buildcc::env::Command::Execute( + command.Construct("{compiler} -dumpmachine"), {}, &stdout_data); + if (!executed || stdout_data.empty()) { + return {}; + } + return stdout_data[0]; +} + +buildcc::env::optional +GetGccToolchainInfo(const buildcc::ToolchainExecutables &executables) { + buildcc::env::Command command; + command.AddDefaultArgument("compiler", executables.cpp_compiler); + + auto op_compiler_version = GetGccCompilerVersion(command); + auto op_target_arch = GetGccTargetArchitecture(command); + if (!op_compiler_version.has_value() || !op_target_arch.has_value()) { + return {}; + } + + buildcc::ToolchainCompilerInfo compiler_info; + compiler_info.compiler_version = op_compiler_version.value(); + compiler_info.target_arch = op_target_arch.value(); + return compiler_info; +} + +// MSVC + +constexpr const char *const kMsvcObjExt = ".obj"; +constexpr const char *const kMsvcPchHeaderExt = ".h"; +constexpr const char *const kMsvcPchCompileExt = ".pch"; +constexpr const char *const kMsvcPrefixIncludeDir = "/I"; +constexpr const char *const kMsvcPrefixLibDir = "/LIBPATH:"; +buildcc::ToolchainConfig GetMsvcToolchainConfig() { + buildcc::ToolchainConfig config; + config.obj_ext = kMsvcObjExt; + config.pch_header_ext = kMsvcPchHeaderExt; + config.pch_compile_ext = kMsvcPchCompileExt; + config.prefix_include_dir = kMsvcPrefixIncludeDir; + config.prefix_lib_dir = kMsvcPrefixLibDir; + return config; +} + +buildcc::env::optional GetMsvcCompilerVersion() { + const char *vscmd_version = getenv("VSCMD_VER"); + if (vscmd_version == nullptr) { + return {}; + } + return vscmd_version; +} + +buildcc::env::optional GetMsvcTargetArchitecture() { + // DONE, Read `VSCMD_ARG_HOST_ARCH` from env path + // DONE, Read `VSCMD_ARG_TGT_ARCH` from env path + const char *vs_host_arch = getenv("VSCMD_ARG_HOST_ARCH"); + const char *vs_target_arch = getenv("VSCMD_ARG_TGT_ARCH"); + if (vs_host_arch == nullptr || vs_target_arch == nullptr) { + return {}; + } + + // DONE, Concat them + return fmt::format("{}_{}", vs_host_arch, vs_target_arch); +} + +buildcc::env::optional +GetMsvcToolchainInfo(const buildcc::ToolchainExecutables &executables) { + (void)executables; + auto op_compiler_version = GetMsvcCompilerVersion(); + auto op_target_arch = GetMsvcTargetArchitecture(); + if (!op_compiler_version.has_value() || !op_target_arch.has_value()) { + return {}; + } + + buildcc::ToolchainCompilerInfo compiler_info; + compiler_info.compiler_version = op_compiler_version.value(); + compiler_info.target_arch = op_target_arch.value(); + return compiler_info; +} + +// + +buildcc::env::optional +GetErrorToolchainInfo(const buildcc::ToolchainExecutables &executables) { + (void)executables; + buildcc::env::log_critical(__FUNCTION__, + "ToolchainInfo does not exist for particular " + "ToolchainId. Supply your own through " + "Toolchain::SetToolchainInfoCb method."); + return {}; +} + +} // namespace + +namespace buildcc { + +std::unordered_map + GlobalToolchainMetadata::global_toolchain_metadata_{ + {ToolchainId::Gcc, + ToolchainMetadata(GetGccToolchainConfig(), GetGccToolchainInfo)}, + {ToolchainId::MinGW, + ToolchainMetadata(GetGccToolchainConfig(), GetGccToolchainInfo)}, + {ToolchainId::Clang, + ToolchainMetadata(GetGccToolchainConfig(), GetGccToolchainInfo)}, + {ToolchainId::Msvc, + ToolchainMetadata(GetMsvcToolchainConfig(), GetMsvcToolchainInfo)}, + {ToolchainId::Custom, + ToolchainMetadata(ToolchainConfig(), GetErrorToolchainInfo)}, + {ToolchainId::Undefined, + ToolchainMetadata(ToolchainConfig(), GetErrorToolchainInfo)}, + }; + +const ToolchainConfig &GlobalToolchainMetadata::GetConfig(ToolchainId id) { + Expect(id); + return Get(id).config_; +} +const ToolchainInfoCb &GlobalToolchainMetadata::GetInfoCb(ToolchainId id) { + Expect(id); + return Get(id).cb_; +} + +// PRIVATE +void GlobalToolchainMetadata::Expect(ToolchainId id) { + env::assert_fatal(global_toolchain_metadata_.find(id) != + global_toolchain_metadata_.end(), + "Invalid ToolchainId"); +} +const GlobalToolchainMetadata::ToolchainMetadata & +GlobalToolchainMetadata::Get(ToolchainId id) { + return global_toolchain_metadata_.at(id); +} + +} // namespace buildcc diff --git a/buildcc/toolchains/src/toolchain_msvc.cpp b/buildcc/toolchains/src/toolchain_msvc.cpp new file mode 100644 index 00000000..ddb047e6 --- /dev/null +++ b/buildcc/toolchains/src/toolchain_msvc.cpp @@ -0,0 +1,28 @@ +/* + * 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. + */ + +#include "toolchains/toolchain_msvc.h" + +#include "toolchains/toolchain_infos.h" + +namespace buildcc { + +void Toolchain_msvc::Initialize() { + RefConfig() = GlobalToolchainMetadata::GetConfig(ToolchainId::Msvc); + SetToolchainInfoCb(GlobalToolchainMetadata::GetInfoCb(ToolchainId::Msvc)); +} + +} // namespace buildcc diff --git a/buildcc/toolchains/test/test_toolchain_specialized.cpp b/buildcc/toolchains/test/test_toolchain_specialized.cpp new file mode 100644 index 00000000..0678875e --- /dev/null +++ b/buildcc/toolchains/test/test_toolchain_specialized.cpp @@ -0,0 +1,383 @@ +#include "env/host_os_util.h" +#include "env/storage.h" +#include "env/util.h" + +#include "toolchains/toolchain_specialized.h" + +#include "expect_command.h" +#include "mock_command_copier.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(ToolchainSpecializedTestGroup) +{ + void setup() { + } + void teardown() { + mock().clear(); + } +}; +// clang-format on + +TEST(ToolchainSpecializedTestGroup, GCC) { + buildcc::Toolchain_gcc gcc; + STRCMP_EQUAL(gcc.GetName().c_str(), "gcc"); + STRCMP_EQUAL(gcc.GetAssembler().c_str(), "as"); + STRCMP_EQUAL(gcc.GetCCompiler().c_str(), "gcc"); + STRCMP_EQUAL(gcc.GetCppCompiler().c_str(), "g++"); + STRCMP_EQUAL(gcc.GetArchiver().c_str(), "ar"); + STRCMP_EQUAL(gcc.GetLinker().c_str(), "ld"); + + fs::path current_directory = fs::current_path(); + buildcc::ToolchainFindConfig find_config; + find_config.env_vars.clear(); + find_config.absolute_search_paths.push_back(current_directory); + + std::vector version_stdout{"version"}; + std::vector arch_stdout{"arch"}; + buildcc::env::m::CommandExpect_Execute(1, true, &version_stdout); + buildcc::env::m::CommandExpect_Execute(1, true, &arch_stdout); + auto info = gcc.Verify(find_config); + STRCMP_EQUAL(info.compiler_version.c_str(), "version"); + STRCMP_EQUAL(info.target_arch.c_str(), "arch"); +} + +TEST(ToolchainSpecializedTestGroup, GCC_Fail) { + buildcc::Toolchain_gcc gcc; + STRCMP_EQUAL(gcc.GetName().c_str(), "gcc"); + STRCMP_EQUAL(gcc.GetAssembler().c_str(), "as"); + STRCMP_EQUAL(gcc.GetCCompiler().c_str(), "gcc"); + STRCMP_EQUAL(gcc.GetCppCompiler().c_str(), "g++"); + STRCMP_EQUAL(gcc.GetArchiver().c_str(), "ar"); + STRCMP_EQUAL(gcc.GetLinker().c_str(), "ld"); + + fs::path current_directory = fs::current_path(); + buildcc::ToolchainFindConfig find_config; + find_config.env_vars.clear(); + find_config.absolute_search_paths.push_back(current_directory); + + { + std::vector version_stdout{"version"}; + std::vector arch_stdout{"arch"}; + buildcc::env::m::CommandExpect_Execute(1, false, &version_stdout); + buildcc::env::m::CommandExpect_Execute(1, true, &arch_stdout); + CHECK_THROWS(std::exception, gcc.Verify(find_config)); + } + + { + std::vector version_stdout; + std::vector arch_stdout{"arch"}; + buildcc::env::m::CommandExpect_Execute(1, true, &version_stdout); + buildcc::env::m::CommandExpect_Execute(1, true, &arch_stdout); + CHECK_THROWS(std::exception, gcc.Verify(find_config)); + } + + { + std::vector version_stdout{"version"}; + std::vector arch_stdout{"arch"}; + buildcc::env::m::CommandExpect_Execute(1, true, &version_stdout); + buildcc::env::m::CommandExpect_Execute(1, false, &arch_stdout); + CHECK_THROWS(std::exception, gcc.Verify(find_config)); + } + + { + std::vector version_stdout{"version"}; + std::vector arch_stdout{}; + buildcc::env::m::CommandExpect_Execute(1, true, &version_stdout); + buildcc::env::m::CommandExpect_Execute(1, true, &arch_stdout); + CHECK_THROWS(std::exception, gcc.Verify(find_config)); + } +} + +TEST(ToolchainSpecializedTestGroup, MINGW) { + buildcc::Toolchain_mingw gcc; + STRCMP_EQUAL(gcc.GetName().c_str(), "gcc"); + STRCMP_EQUAL(gcc.GetAssembler().c_str(), "as"); + STRCMP_EQUAL(gcc.GetCCompiler().c_str(), "gcc"); + STRCMP_EQUAL(gcc.GetCppCompiler().c_str(), "g++"); + STRCMP_EQUAL(gcc.GetArchiver().c_str(), "ar"); + STRCMP_EQUAL(gcc.GetLinker().c_str(), "ld"); + + fs::path current_directory = fs::current_path(); + buildcc::ToolchainFindConfig find_config; + find_config.env_vars.clear(); + find_config.absolute_search_paths.push_back(current_directory); + + std::vector version_stdout{"version"}; + std::vector arch_stdout{"arch"}; + buildcc::env::m::CommandExpect_Execute(1, true, &version_stdout); + buildcc::env::m::CommandExpect_Execute(1, true, &arch_stdout); + auto info = gcc.Verify(find_config); + STRCMP_EQUAL(info.compiler_version.c_str(), "version"); + STRCMP_EQUAL(info.target_arch.c_str(), "arch"); +} + +#if defined(__GNUC__) && !defined(__MINGW32__) && !defined(__MINGW64__) + +TEST(ToolchainSpecializedTestGroup, MSVC) { + buildcc::Toolchain_msvc msvc; + STRCMP_EQUAL(msvc.GetName().c_str(), "msvc"); + STRCMP_EQUAL(msvc.GetAssembler().c_str(), "cl"); + STRCMP_EQUAL(msvc.GetCCompiler().c_str(), "cl"); + STRCMP_EQUAL(msvc.GetCppCompiler().c_str(), "cl"); + STRCMP_EQUAL(msvc.GetArchiver().c_str(), "lib"); + STRCMP_EQUAL(msvc.GetLinker().c_str(), "link"); + + const auto &toolchain_config = msvc.GetConfig(); + STRCMP_EQUAL(toolchain_config.obj_ext.c_str(), ".obj"); + STRCMP_EQUAL(toolchain_config.pch_header_ext.c_str(), ".h"); + STRCMP_EQUAL(toolchain_config.pch_compile_ext.c_str(), ".pch"); + STRCMP_EQUAL(toolchain_config.prefix_include_dir.c_str(), "/I"); + STRCMP_EQUAL(toolchain_config.prefix_lib_dir.c_str(), "/LIBPATH:"); + + fs::path current_directory = fs::current_path(); + buildcc::ToolchainFindConfig find_config; + find_config.env_vars.clear(); + find_config.absolute_search_paths.push_back(current_directory); + + char vscmd_ver[] = "VSCMD_VER=version"; + char vscmd_arg_host_arch[] = "VSCMD_ARG_HOST_ARCH=host"; + char vscmd_arg_tgt_arch[] = "VSCMD_ARG_TGT_ARCH=target"; + CHECK_EQUAL(putenv(vscmd_ver), 0); + CHECK_EQUAL(putenv(vscmd_arg_host_arch), 0); + CHECK_EQUAL(putenv(vscmd_arg_tgt_arch), 0); + auto info = msvc.Verify(find_config); + STRCMP_EQUAL(info.compiler_version.c_str(), "version"); + STRCMP_EQUAL(info.target_arch.c_str(), "host_target"); +} + +TEST(ToolchainSpecializedTestGroup, MSVC_Fail) { + buildcc::Toolchain_msvc msvc; + STRCMP_EQUAL(msvc.GetName().c_str(), "msvc"); + STRCMP_EQUAL(msvc.GetAssembler().c_str(), "cl"); + STRCMP_EQUAL(msvc.GetCCompiler().c_str(), "cl"); + STRCMP_EQUAL(msvc.GetCppCompiler().c_str(), "cl"); + STRCMP_EQUAL(msvc.GetArchiver().c_str(), "lib"); + STRCMP_EQUAL(msvc.GetLinker().c_str(), "link"); + + const auto &toolchain_config = msvc.GetConfig(); + STRCMP_EQUAL(toolchain_config.obj_ext.c_str(), ".obj"); + STRCMP_EQUAL(toolchain_config.pch_header_ext.c_str(), ".h"); + STRCMP_EQUAL(toolchain_config.pch_compile_ext.c_str(), ".pch"); + STRCMP_EQUAL(toolchain_config.prefix_include_dir.c_str(), "/I"); + STRCMP_EQUAL(toolchain_config.prefix_lib_dir.c_str(), "/LIBPATH:"); + + fs::path current_directory = fs::current_path(); + buildcc::ToolchainFindConfig find_config; + find_config.env_vars.clear(); + find_config.absolute_search_paths.push_back(current_directory); + + { + char vscmd_ver[] = "VSCMD_VER"; + char vscmd_arg_host_arch[] = "VSCMD_ARG_HOST_ARCH"; + char vscmd_arg_tgt_arch[] = "VSCMD_ARG_TGT_ARCH"; + CHECK_EQUAL(putenv(vscmd_ver), 0); + CHECK_EQUAL(putenv(vscmd_arg_host_arch), 0); + CHECK_EQUAL(putenv(vscmd_arg_tgt_arch), 0); + CHECK_THROWS(std::exception, msvc.Verify(find_config)); + } + + { + char vscmd_ver[] = "VSCMD_VER"; + char vscmd_arg_host_arch[] = "VSCMD_ARG_HOST_ARCH=host"; + char vscmd_arg_tgt_arch[] = "VSCMD_ARG_TGT_ARCH"; + CHECK_EQUAL(putenv(vscmd_ver), 0); + CHECK_EQUAL(putenv(vscmd_arg_host_arch), 0); + CHECK_EQUAL(putenv(vscmd_arg_tgt_arch), 0); + CHECK_THROWS(std::exception, msvc.Verify(find_config)); + } + + { + char vscmd_ver[] = "VSCMD_VER"; + char vscmd_arg_host_arch[] = "VSCMD_ARG_HOST_ARCH"; + char vscmd_arg_tgt_arch[] = "VSCMD_ARG_TGT_ARCH=target"; + CHECK_EQUAL(putenv(vscmd_ver), 0); + CHECK_EQUAL(putenv(vscmd_arg_host_arch), 0); + CHECK_EQUAL(putenv(vscmd_arg_tgt_arch), 0); + CHECK_THROWS(std::exception, msvc.Verify(find_config)); + } + + { + char vscmd_ver[] = "VSCMD_VER=version"; + char vscmd_arg_host_arch[] = "VSCMD_ARG_HOST_ARCH"; + char vscmd_arg_tgt_arch[] = "VSCMD_ARG_TGT_ARCH"; + CHECK_EQUAL(putenv(vscmd_ver), 0); + CHECK_EQUAL(putenv(vscmd_arg_host_arch), 0); + CHECK_EQUAL(putenv(vscmd_arg_tgt_arch), 0); + CHECK_THROWS(std::exception, msvc.Verify(find_config)); + } +} + +#endif + +TEST(ToolchainSpecializedTestGroup, Clang) { + buildcc::Toolchain_custom clang( + buildcc::ToolchainId::Clang, "clang", + buildcc::ToolchainExecutables("llvm-as", "clang", "clang++", "llvm-ar", + "ld")); + STRCMP_EQUAL(clang.GetName().c_str(), "clang"); + STRCMP_EQUAL(clang.GetAssembler().c_str(), "llvm-as"); + STRCMP_EQUAL(clang.GetCCompiler().c_str(), "clang"); + STRCMP_EQUAL(clang.GetCppCompiler().c_str(), "clang++"); + STRCMP_EQUAL(clang.GetArchiver().c_str(), "llvm-ar"); + STRCMP_EQUAL(clang.GetLinker().c_str(), "ld"); + + fs::path current_directory = fs::current_path(); + buildcc::ToolchainFindConfig find_config; + find_config.env_vars.clear(); + find_config.absolute_search_paths.push_back(current_directory); + + std::vector version_stdout{"version"}; + std::vector arch_stdout{"arch"}; + buildcc::env::m::CommandExpect_Execute(1, true, &version_stdout); + buildcc::env::m::CommandExpect_Execute(1, true, &arch_stdout); + auto info = clang.Verify(find_config); + STRCMP_EQUAL(info.compiler_version.c_str(), "version"); + STRCMP_EQUAL(info.target_arch.c_str(), "arch"); +} + +TEST(ToolchainSpecializedTestGroup, Global) { + CHECK_THROWS(std::exception, buildcc::GlobalToolchainMetadata::GetConfig( + (buildcc::ToolchainId)65535)); + + CHECK_THROWS(std::exception, buildcc::GlobalToolchainMetadata::GetInfoCb( + (buildcc::ToolchainId)65535)); + + CHECK_FALSE(buildcc::GlobalToolchainMetadata::GetInfoCb( + buildcc::ToolchainId::Custom)(buildcc::ToolchainExecutables()) + .has_value()); + CHECK_FALSE( + buildcc::GlobalToolchainMetadata::GetInfoCb( + buildcc::ToolchainId::Undefined)(buildcc::ToolchainExecutables()) + .has_value()); +} + +TEST(ToolchainSpecializedTestGroup, Generic) { + MemoryLeakWarningPlugin::saveAndDisableNewDeleteOverloads(); + + { + auto &gcc = + buildcc::Toolchain_generic::New(buildcc::ToolchainId::Gcc, "gcc"); + STRCMP_EQUAL(gcc.GetName().c_str(), "gcc"); + STRCMP_EQUAL(gcc.GetAssembler().c_str(), "as"); + STRCMP_EQUAL(gcc.GetCCompiler().c_str(), "gcc"); + STRCMP_EQUAL(gcc.GetCppCompiler().c_str(), "g++"); + STRCMP_EQUAL(gcc.GetArchiver().c_str(), "ar"); + STRCMP_EQUAL(gcc.GetLinker().c_str(), "ld"); + + // Already defined with same identifier + CHECK_THROWS(std::exception, buildcc::Toolchain_generic::New( + buildcc::ToolchainId::Gcc, "gcc")); + } + + { + auto &mingw = + buildcc::Toolchain_generic::New(buildcc::ToolchainId::MinGW, "mingw"); + STRCMP_EQUAL(mingw.GetName().c_str(), "mingw"); + STRCMP_EQUAL(mingw.GetAssembler().c_str(), "as"); + STRCMP_EQUAL(mingw.GetCCompiler().c_str(), "gcc"); + STRCMP_EQUAL(mingw.GetCppCompiler().c_str(), "g++"); + STRCMP_EQUAL(mingw.GetArchiver().c_str(), "ar"); + STRCMP_EQUAL(mingw.GetLinker().c_str(), "ld"); + } + + { + auto &msvc = + buildcc::Toolchain_generic::New(buildcc::ToolchainId::Msvc, "msvc"); + STRCMP_EQUAL(msvc.GetName().c_str(), "msvc"); + STRCMP_EQUAL(msvc.GetAssembler().c_str(), "cl"); + STRCMP_EQUAL(msvc.GetCCompiler().c_str(), "cl"); + STRCMP_EQUAL(msvc.GetCppCompiler().c_str(), "cl"); + STRCMP_EQUAL(msvc.GetArchiver().c_str(), "lib"); + STRCMP_EQUAL(msvc.GetLinker().c_str(), "link"); + + const auto &toolchain_config = msvc.GetConfig(); + STRCMP_EQUAL(toolchain_config.obj_ext.c_str(), ".obj"); + STRCMP_EQUAL(toolchain_config.pch_header_ext.c_str(), ".h"); + STRCMP_EQUAL(toolchain_config.pch_compile_ext.c_str(), ".pch"); + STRCMP_EQUAL(toolchain_config.prefix_include_dir.c_str(), "/I"); + STRCMP_EQUAL(toolchain_config.prefix_lib_dir.c_str(), "/LIBPATH:"); + } + + { + auto &clang = buildcc::Toolchain_generic::New( + buildcc::ToolchainId::Clang, "clang", + buildcc::ToolchainExecutables("llvm-as", "clang", "clang++", "llvm-ar", + "ld")); + STRCMP_EQUAL(clang.GetName().c_str(), "clang"); + STRCMP_EQUAL(clang.GetAssembler().c_str(), "llvm-as"); + STRCMP_EQUAL(clang.GetCCompiler().c_str(), "clang"); + STRCMP_EQUAL(clang.GetCppCompiler().c_str(), "clang++"); + STRCMP_EQUAL(clang.GetArchiver().c_str(), "llvm-ar"); + STRCMP_EQUAL(clang.GetLinker().c_str(), "ld"); + } + + { + CHECK_THROWS(std::exception, buildcc::Toolchain_generic::New( + buildcc::ToolchainId::Custom, "custom")); + } + + { + CHECK_THROWS(std::exception, + buildcc::Toolchain_generic::New( + buildcc::ToolchainId::Undefined, "undefined")); + } + + buildcc::Storage::Clear(); + MemoryLeakWarningPlugin::restoreNewDeleteOverloads(); +} + +// + +void convert_executables_to_full_path(buildcc::ToolchainExecutables &exes, + const std::string &ext) { + fs::path current_path = fs::current_path().make_preferred(); + exes.assembler = + (current_path / fmt::format("{}{}", exes.assembler, ext)).string(); + exes.c_compiler = + (current_path / fmt::format("{}{}", exes.c_compiler, ext)).string(); + exes.cpp_compiler = + (current_path / fmt::format("{}{}", exes.cpp_compiler, ext)).string(); + exes.archiver = + (current_path / fmt::format("{}{}", exes.archiver, ext)).string(); + exes.linker = (current_path / fmt::format("{}{}", exes.linker, ext)).string(); +} + +void create_dummy_executables(const buildcc::ToolchainExecutables &exes) { + buildcc::env::save_file(exes.assembler.c_str(), "", false); + buildcc::env::save_file(exes.c_compiler.c_str(), "", false); + buildcc::env::save_file(exes.cpp_compiler.c_str(), "", false); + buildcc::env::save_file(exes.archiver.c_str(), "", false); + buildcc::env::save_file(exes.linker.c_str(), "", false); +} + +int main(int ac, char **av) { + buildcc::env::m::VectorStringCopier copier; + mock().installCopier(TEST_VECTOR_STRING_TYPE, copier); + + constexpr const char *const exe_ext = + buildcc::env::get_os_executable_extension(); + std::string ext = ""; + if (exe_ext) { + ext = exe_ext; + } + buildcc::ToolchainExecutables gcc_exes("as", "gcc", "g++", "ar", "ld"); + convert_executables_to_full_path(gcc_exes, ext); + create_dummy_executables(gcc_exes); + + buildcc::ToolchainExecutables msvc_exes("cl", "cl", "cl", "lib", "link"); + convert_executables_to_full_path(msvc_exes, ext); + create_dummy_executables(msvc_exes); + + buildcc::ToolchainExecutables clang_exes("llvm-as", "clang", "clang++", + "llvm-ar", "ld"); + convert_executables_to_full_path(clang_exes, ext); + create_dummy_executables(clang_exes); + return CommandLineTestRunner::RunAllTests(ac, av); +} diff --git a/buildexe/CMakeLists.txt b/buildexe/CMakeLists.txt index b05801bb..b78f1030 100644 --- a/buildexe/CMakeLists.txt +++ b/buildexe/CMakeLists.txt @@ -16,11 +16,12 @@ add_executable(buildexe target_sources(buildexe PRIVATE ../bootstrap/src/build_buildcc.cpp ../bootstrap/src/build_cli11.cpp - ../bootstrap/src/build_flatbuffers.cpp + ../bootstrap/src/build_nlohmann_json.cpp ../bootstrap/src/build_fmtlib.cpp ../bootstrap/src/build_spdlog.cpp ../bootstrap/src/build_taskflow.cpp ../bootstrap/src/build_tpl.cpp + ../bootstrap/src/build_tl_optional.cpp ) target_include_directories(buildexe PRIVATE include diff --git a/buildexe/buildexe.cpp b/buildexe/buildexe.cpp index faa7ede4..a93d554f 100644 --- a/buildexe/buildexe.cpp +++ b/buildexe/buildexe.cpp @@ -22,12 +22,6 @@ #include "buildexe/toolchain_setup.h" #include "bootstrap/build_buildcc.h" -#include "bootstrap/build_cli11.h" -#include "bootstrap/build_flatbuffers.h" -#include "bootstrap/build_fmtlib.h" -#include "bootstrap/build_spdlog.h" -#include "bootstrap/build_taskflow.h" -#include "bootstrap/build_tpl.h" using namespace buildcc; @@ -42,8 +36,7 @@ int main(int argc, char **argv) { // BuildExeArgs buildexe_args; - buildexe_args.Setup(); - buildexe_args.Parse(argc, argv); + buildexe_args.Setup(argc, argv); // TODO, Add Verification subcommand here for OS, Compiler etc! // os win, linux considerations @@ -54,20 +47,21 @@ int main(int argc, char **argv) { // TODO, Add libraries (git cloned) // TODO, Add extension (git cloned) - Register reg(buildexe_args.GetArgs()); - reg.Clean(clean_cb); + Reg::Init(); + Reg::Call(Args::Clean()).Func(clean_cb); // Host Toolchain - BaseToolchain toolchain = - buildexe_args.GetHostToolchainArg().ConstructToolchain(); - find_toolchain_verify(toolchain); + auto &toolchain = buildexe_args.GetHostToolchainArg().ConstructToolchain(); + toolchain.Verify(); + if (buildexe_args.GetBuildMode() == BuildExeMode::Script) { host_toolchain_verify(toolchain); } // Build Target - BuildEnvSetup build_setup(reg, toolchain, buildexe_args); - build_setup.ConstructTarget(); + BuildEnvSetup build_setup(toolchain, buildexe_args); + Reg::Toolchain(ArgToolchainState(true)).BuildPackage(build_setup); + Reg::Run(); // Run Target if script mode if (buildexe_args.GetBuildMode() == BuildExeMode::Script) { @@ -81,6 +75,6 @@ int main(int argc, char **argv) { } static void clean_cb() { - env::log_info(kTag, fmt::format("Cleaning {}", env::get_project_build_dir())); - fs::remove_all(env::get_project_build_dir()); + env::log_info(kTag, fmt::format("Cleaning {}", Project::GetBuildDir())); + fs::remove_all(Project::GetBuildDir()); } diff --git a/buildexe/include/buildexe/args_setup.h b/buildexe/include/buildexe/args_setup.h index 5fc9faff..980ee696 100644 --- a/buildexe/include/buildexe/args_setup.h +++ b/buildexe/include/buildexe/args_setup.h @@ -26,13 +26,17 @@ enum class BuildExeMode { Script, }; -struct ArgTargetInfo { +struct ArgTargetInfo : public ArgCustom { + void Add(CLI::App &app) override; + std::string name; TargetType type; fs::path relative_to_root; }; -struct ArgTargetInputs { +struct ArgTargetInputs : public ArgCustom { + void Add(CLI::App &app) override; + // Sources std::vector source_files; std::vector include_dirs; @@ -50,7 +54,9 @@ struct ArgTargetInputs { std::vector link_flags; }; -struct ArgScriptInfo { +struct ArgScriptInfo : public ArgCustom { + void Add(CLI::App &app) override; + std::vector configs; }; @@ -59,44 +65,41 @@ struct LibInfo { std::string absolute_lib_path; }; +struct ArgLibsInfo : public ArgCustom { + void Add(CLI::App &app) override; + + std::vector libs_info; + std::vector lib_build_files; +}; + class BuildExeArgs { public: - void Setup(); - void Parse(int argc, char **argv) { args_.Parse(argc, argv); } + void Setup(int argc, char **argv); // Getters - const Args &GetArgs() const { return args_; } - const ArgToolchain &GetHostToolchainArg() const { - return host_toolchain_arg_; - } + ArgToolchain &GetHostToolchainArg() { return host_toolchain_arg_; } const ArgTargetInfo &GetTargetInfo() const { return out_targetinfo_; } const ArgTargetInputs &GetTargetInputs() const { return out_targetinputs_; } const ArgScriptInfo &GetScriptInfo() const { return out_scriptinfo_; } BuildExeMode GetBuildMode() const { return out_mode_; } - const std::vector &GetLibsInfo() const { return libs_info_; } + const std::vector &GetLibsInfo() const { + return out_libsinfo_.libs_info; + } const std::vector &GetLibBuildFiles() const { - return lib_build_files_; + return out_libsinfo_.lib_build_files; } private: - void SetupBuildMode(); - void SetupTargetInfo(); - void SetupTargetInputs(); - void SetupScriptMode(); - void SetupLibs(); + void SetupBuildMode(CLI::App &app); private: - Args args_; ArgToolchain host_toolchain_arg_; ArgTargetInfo out_targetinfo_; ArgTargetInputs out_targetinputs_; ArgScriptInfo out_scriptinfo_; - + ArgLibsInfo out_libsinfo_; BuildExeMode out_mode_; - - std::vector libs_info_; - std::vector lib_build_files_; }; } // namespace buildcc diff --git a/buildexe/include/buildexe/build_env_setup.h b/buildexe/include/buildexe/build_env_setup.h index d73c9e9c..afbbe321 100644 --- a/buildexe/include/buildexe/build_env_setup.h +++ b/buildexe/include/buildexe/build_env_setup.h @@ -31,13 +31,11 @@ class BuildEnvSetup { static constexpr const char *const kUserTargetName = "UserTarget"; public: - BuildEnvSetup(Register ®, const BaseToolchain &toolchain, + BuildEnvSetup(const BaseToolchain &toolchain, const BuildExeArgs &buildexe_args) - : reg_(reg), toolchain_(toolchain), buildexe_args_(buildexe_args) { - state_.build = true; - } + : toolchain_(toolchain), buildexe_args_(buildexe_args) {} - void ConstructTarget(); + void Setup(const ArgToolchainState &state); void RunUserTarget(const ArgScriptInfo &arg_script_info); @@ -62,12 +60,11 @@ class BuildEnvSetup { void DepUserTargetOnBuildcc(); private: - Register ®_; const BaseToolchain &toolchain_; const BuildExeArgs &buildexe_args_; ArgToolchainState state_; - PersistentStorage storage_; + ScopedStorage storage_; }; } // namespace buildcc diff --git a/buildexe/include/buildexe/toolchain_setup.h b/buildexe/include/buildexe/toolchain_setup.h index 423bbbf4..2f87b9f3 100644 --- a/buildexe/include/buildexe/toolchain_setup.h +++ b/buildexe/include/buildexe/toolchain_setup.h @@ -21,7 +21,6 @@ namespace buildcc { -void find_toolchain_verify(BaseToolchain &toolchain); void host_toolchain_verify(const BaseToolchain &toolchain); } // namespace buildcc diff --git a/buildexe/src/args_setup.cpp b/buildexe/src/args_setup.cpp index a797bf95..edee4ada 100644 --- a/buildexe/src/args_setup.cpp +++ b/buildexe/src/args_setup.cpp @@ -32,90 +32,73 @@ static const std::unordered_map kTargetTypeMap{ {"dynamicLibrary", TargetType::DynamicLibrary}, }; -void BuildExeArgs::Setup() { - args_.AddToolchain("host", "Host Toolchain", host_toolchain_arg_); - SetupBuildMode(); - SetupTargetInfo(); - SetupTargetInputs(); - SetupScriptMode(); - SetupLibs(); +void BuildExeArgs::Setup(int argc, char **argv) { + Args::Init() + .AddToolchain("host", "Host Toolchain", host_toolchain_arg_) + .AddCustomData(out_targetinfo_) + .AddCustomData(out_targetinputs_) + .AddCustomData(out_scriptinfo_) + .AddCustomData(out_libsinfo_) + .AddCustomCallback([&](CLI::App &app) { SetupBuildMode(app); }) + .Parse(argc, argv); } -void BuildExeArgs::SetupBuildMode() { - args_.Ref() - .add_option("--mode", out_mode_, "Provide BuildExe run mode") +void BuildExeArgs::SetupBuildMode(CLI::App &app) { + app.add_option("--mode", out_mode_, "Provide BuildExe run mode") ->transform(CLI::CheckedTransformer(kBuildExeModeMap, CLI::ignore_case)) ->required(); } -// TODO, Add subcommand [build.info] -void BuildExeArgs::SetupTargetInfo() { +void ArgTargetInfo::Add(CLI::App &app) { constexpr const char *const kProjectInfo = "Project Info"; - auto &app = args_.Ref(); - auto *project_info_app = app.add_option_group(kProjectInfo); - project_info_app - ->add_option("--name", out_targetinfo_.name, "Provide Target name") + project_info_app->add_option("--name", name, "Provide Target name") ->required(); - project_info_app - ->add_option("--type", out_targetinfo_.type, "Provide Target Type") + project_info_app->add_option("--type", type, "Provide Target Type") ->transform(CLI::CheckedTransformer(kTargetTypeMap, CLI::ignore_case)) ->required(); project_info_app - ->add_option("--relative_to_root", out_targetinfo_.relative_to_root, + ->add_option("--relative_to_root", relative_to_root, "Provide Target relative to root") ->required(); } -// TODO, Add subcommand [build.inputs] -// TODO, Add group, group by sources, headers, inncludes on CLI -void BuildExeArgs::SetupTargetInputs() { +void ArgTargetInputs::Add(CLI::App &app) { constexpr const char *const kTargetInputs = "Target Inputs"; - auto &app = args_.Ref(); - auto *target_inputs_app = app.add_option_group(kTargetInputs); - target_inputs_app->add_option("--srcs", out_targetinputs_.source_files, - "Provide source files"); - target_inputs_app->add_option("--includes", out_targetinputs_.include_dirs, + target_inputs_app->add_option("--srcs", source_files, "Provide source files"); + target_inputs_app->add_option("--includes", include_dirs, "Provide include dirs"); - target_inputs_app->add_option("--lib_dirs", out_targetinputs_.lib_dirs, - "Provide lib dirs"); - target_inputs_app->add_option("--external_libs", - out_targetinputs_.external_lib_deps, + target_inputs_app->add_option("--lib_dirs", lib_dirs, "Provide lib dirs"); + target_inputs_app->add_option("--external_libs", external_lib_deps, "Provide external libs"); - target_inputs_app->add_option("--preprocessor_flags", - out_targetinputs_.preprocessor_flags, + target_inputs_app->add_option("--preprocessor_flags", preprocessor_flags, "Provide Preprocessor flags"); - target_inputs_app->add_option("--common_compile_flags", - out_targetinputs_.common_compile_flags, + target_inputs_app->add_option("--common_compile_flags", common_compile_flags, "Provide CommonCompile Flags"); - target_inputs_app->add_option("--asm_compile_flags", - out_targetinputs_.asm_compile_flags, + target_inputs_app->add_option("--asm_compile_flags", asm_compile_flags, "Provide AsmCompile Flags"); - target_inputs_app->add_option("--c_compile_flags", - out_targetinputs_.c_compile_flags, + target_inputs_app->add_option("--c_compile_flags", c_compile_flags, "Provide CCompile Flags"); - target_inputs_app->add_option("--cpp_compile_flags", - out_targetinputs_.cpp_compile_flags, + target_inputs_app->add_option("--cpp_compile_flags", cpp_compile_flags, "Provide CppCompile Flags"); - target_inputs_app->add_option("--link_flags", out_targetinputs_.link_flags, + target_inputs_app->add_option("--link_flags", link_flags, "Provide Link Flags"); -} +}; -void BuildExeArgs::SetupScriptMode() { - auto *script_args = args_.Ref().add_subcommand("script"); - script_args->add_option("--configs", out_scriptinfo_.configs, - "Config files for script mode"); +void ArgScriptInfo::Add(CLI::App &app) { + auto *script_args = app.add_subcommand("script"); + script_args->add_option("--configs", configs, "Config files for script mode"); } -void BuildExeArgs::SetupLibs() { - auto *libs_app = args_.Ref().add_subcommand("libs", "Libraries"); +void ArgLibsInfo::Add(CLI::App &app) { + auto *libs_app = app.add_subcommand("libs", "Libraries"); std::error_code ec; fs::directory_iterator dir_iter = fs::directory_iterator(BuildccHome::GetBuildccLibsDir(), ec); @@ -132,13 +115,13 @@ void BuildExeArgs::SetupLibs() { LibInfo lib_info; lib_info.lib_name = lib_name; lib_info.absolute_lib_path = fmt::format("{}", lib_path); - libs_info_.push_back(lib_info); + libs_info.push_back(lib_info); auto add_lib_files_cb_func = [lib_path, this](const std::vector &paths) { for (const auto &p : paths) { - fs::path absolute_file_path = lib_path / p; - lib_build_files_.push_back(absolute_file_path); + fs::path absolute_file_path = (lib_path / p).make_preferred(); + lib_build_files.push_back(absolute_file_path); } }; diff --git a/buildexe/src/build_env_setup.cpp b/buildexe/src/build_env_setup.cpp index 6884a1de..d36360eb 100644 --- a/buildexe/src/build_env_setup.cpp +++ b/buildexe/src/build_env_setup.cpp @@ -21,7 +21,8 @@ namespace buildcc { constexpr const char *const kTag = "BuildExe"; -void BuildEnvSetup::ConstructTarget() { +void BuildEnvSetup::Setup(const ArgToolchainState &state) { + state_ = state; if (buildexe_args_.GetBuildMode() == BuildExeMode::Script) { // buildcc and user target ConstructUserTargetWithBuildcc(); @@ -29,7 +30,6 @@ void BuildEnvSetup::ConstructTarget() { // user target ConstructUserTarget(); } - reg_.RunBuild(); } void BuildEnvSetup::RunUserTarget(const ArgScriptInfo &arg_script_info) { @@ -76,7 +76,7 @@ void BuildEnvSetup::ConstructUserTargetWithBuildcc() { void BuildEnvSetup::BuildccTargetSetup() { const fs::path &buildcc_base = BuildccHome::GetBuildccBaseDir(); auto &buildcc_package = storage_.Add( - kBuildccPackageName, reg_, toolchain_, + kBuildccPackageName, toolchain_, TargetEnv(buildcc_base, buildcc_base / "_build_exe")); buildcc_package.Setup(state_); } @@ -194,40 +194,24 @@ struct BuildExeLibDir {{ } // Segregate valid lib files into sources and include dirs - internal::fs_unordered_set sources; - internal::fs_unordered_set include_dirs; - internal::fs_unordered_set headers; for (const auto &lib_build_file : buildexe_args_.GetLibBuildFiles()) { - if (user_target.GetConfig().IsValidSource(lib_build_file)) { - sources.insert(lib_build_file); + if (user_target.GetToolchain().GetConfig().IsValidSource(lib_build_file)) { + user_target.AddSourceAbsolute(lib_build_file); } - if (user_target.GetConfig().IsValidHeader(lib_build_file)) { - include_dirs.insert(lib_build_file.parent_path()); - headers.insert(lib_build_file); + if (user_target.GetToolchain().GetConfig().IsValidHeader(lib_build_file)) { + user_target.AddIncludeDirAbsolute(lib_build_file.parent_path(), false); + user_target.AddHeaderAbsolute(lib_build_file); } } - - // Add sources to user_target - for (const auto &s : sources) { - user_target.AddSourceAbsolute(s); - } - // Add include dirs to user_target - for (const auto &idir : include_dirs) { - user_target.AddIncludeDir(idir, false); - } - // Add headers to user_target - for (const auto &h : headers) { - user_target.AddHeaderAbsolute(h); - } } void BuildEnvSetup::UserTargetBuild() { - reg_.Build( - state_, [](BaseTarget &target) { target.Build(); }, GetUserTarget()); + Reg::Toolchain(state_).Build([](BaseTarget &target) { target.Build(); }, + GetUserTarget()); } void BuildEnvSetup::DepUserTargetOnBuildcc() { - reg_.Dep(GetUserTarget(), GetBuildcc()); + Reg::Toolchain(state_).Dep(GetUserTarget(), GetBuildcc()); } } // namespace buildcc diff --git a/buildexe/src/toolchain_setup.cpp b/buildexe/src/toolchain_setup.cpp index e9bfe0e1..4d483daf 100644 --- a/buildexe/src/toolchain_setup.cpp +++ b/buildexe/src/toolchain_setup.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "buildexe/toolchain_setup.h" + namespace { constexpr const char *const kTag = "BuildExe"; @@ -22,35 +24,10 @@ constexpr const char *const kTag = "BuildExe"; namespace buildcc { -void find_toolchain_verify(BaseToolchain &toolchain) { - auto verified_toolchains = toolchain.Verify(); - env::assert_fatal(!verified_toolchains.empty(), - "Toolchain could not be verified. Please input correct " - "Gcc, Msvc, Clang or MinGW toolchain executable names"); - if (verified_toolchains.size() > 1) { - env::log_info( - kTag, - fmt::format( - "Found {} toolchains. By default using the first added" - "toolchain. Modify your environment `PATH` information if you " - "would like compiler precedence when similar compilers are " - "detected in different folders", - verified_toolchains.size())); - } - - // Print - int counter = 1; - for (const auto &vt : verified_toolchains) { - std::string info = fmt::format("{}. : {}", counter, vt.ToString()); - env::log_info("Host Toolchain", info); - counter++; - } -} - void host_toolchain_verify(const BaseToolchain &toolchain) { env::log_info(kTag, "*** Starting Toolchain verification ***"); - fs::path file = env::get_project_build_dir() / "verify_host_toolchain" / + fs::path file = Project::GetBuildDir() / "verify_host_toolchain" / "verify_host_toolchain.cpp"; fs::create_directories(file.parent_path()); std::string file_data = R"(// Generated by BuildExe diff --git a/cmake/coverage/gcovr.cmake b/cmake/coverage/gcovr.cmake index 9bba8891..4d97ebbe 100644 --- a/cmake/coverage/gcovr.cmake +++ b/cmake/coverage/gcovr.cmake @@ -9,7 +9,7 @@ else() message("GCOVR at ${gcovr_program}") set(GCOVR_REMOVE_OPTIONS - --exclude "(.+/)?flatbuffers(.+/)?" + --exclude "(.+/)?third_party(.+/)?" --exclude "(.+/)?spdlog(.+/)?" --exclude "(.+/)?fmt(.+/)?" --exclude "(.+/)?taskflow(.+/)?" diff --git a/cmake/coverage/lcov.cmake b/cmake/coverage/lcov.cmake index e658e4bb..8860f459 100644 --- a/cmake/coverage/lcov.cmake +++ b/cmake/coverage/lcov.cmake @@ -16,11 +16,11 @@ else() ) set(LCOV_REMOVE_OPTIONS "/usr*" + "*third_party*" "*/CppUTestExt*" "*/CppUTest*" "*/fmt*" "*/spdlog*" - "*/flatbuffers*" "*/taskflow*" "*/CLI11*" "*/generated*" diff --git a/cmake/flags/test_flags.cmake b/cmake/flags/test_flags.cmake index bb8d8758..5846fab7 100644 --- a/cmake/flags/test_flags.cmake +++ b/cmake/flags/test_flags.cmake @@ -1,4 +1,7 @@ -set(TEST_COMPILE_FLAGS -g -Og -fprofile-arcs -ftest-coverage -fno-omit-frame-pointer -fno-optimize-sibling-calls -fno-inline -fprofile-abs-path -) -set(TEST_LINK_FLAGS -g -Og -fprofile-arcs -ftest-coverage -fprofile-abs-path -) +if(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") + set(TEST_COMPILE_FLAGS -g -Og -fprofile-arcs -ftest-coverage -fno-omit-frame-pointer -fno-optimize-sibling-calls -fno-inline -fprofile-abs-path + ) + set(TEST_LINK_FLAGS -g -Og -fprofile-arcs -ftest-coverage -fprofile-abs-path + ) + set(TEST_LINK_LIBS gcov) +endif() diff --git a/cmake/target/flatbuffers.cmake b/cmake/target/flatbuffers.cmake deleted file mode 100644 index 0c59684b..00000000 --- a/cmake/target/flatbuffers.cmake +++ /dev/null @@ -1,34 +0,0 @@ - -# TODO, Update FLATBUFFERS option to conditionally compile FLATC - -# Set flatbuffer specific options here -set(FLATBUFFERS_BUILD_CPP17 ON CACHE BOOL "Flatbuffers C++17 support") -set(FLATBUFFERS_BUILD_TESTS OFF CACHE BOOL "Flatbuffers build tests") -set(FLATBUFFERS_BUILD_FLATC ${BUILDCC_FLATBUFFERS_FLATC} CACHE BOOL "Flatbuffers build flatc") -set(FLATBUFFERS_BUILD_FLATHASH OFF CACHE BOOL "Flatbuffers build flathash") -set(FLATBUFFERS_BUILD_FLATLIB OFF CACHE BOOL "Flatbuffers build flatlib") -set(FLATBUFFERS_LIBCXX_WITH_CLANG OFF CACHE BOOL "Flatbuffers LIBCXX") -set(FLATBUFFERS_INSTALL ON CACHE BOOL "Enable the installation of targets.") -set(FLATBUFFERS_ENABLE_PCH ${BUILDCC_PRECOMPILE_HEADERS} CACHE BOOL "Flatbuffers PCH") -add_subdirectory(third_party/flatbuffers) - -set(FBS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/flatbuffers/include/flatbuffers") -set(FBS_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/flatbuffers/include") -file(GLOB FLATBUFFERS_INCLUDE_HEADERS "${FBS_DIR}/*.h") -if(${BUILDCC_PRECOMPILE_HEADERS}) - list(APPEND FLATBUFFERS_INCLUDE_HEADERS ${FBS_DIR}/pch/pch.h) -endif() - -add_library(flatbuffers_header_only INTERFACE - ${FLATBUFFERS_INCLUDE_HEADERS} -) -target_include_directories(flatbuffers_header_only INTERFACE - $ - $ -) - -if (${BUILDCC_INSTALL}) - install(TARGETS flatbuffers_header_only DESTINATION lib EXPORT flatbuffers_header_onlyConfig) - install(DIRECTORY ${FBS_INCLUDE_DIR} DESTINATION "include") - install(EXPORT flatbuffers_header_onlyConfig DESTINATION "lib/cmake/flatbuffers_header_only") -endif() diff --git a/cmake/target/json.cmake b/cmake/target/json.cmake new file mode 100644 index 00000000..bc524c55 --- /dev/null +++ b/cmake/target/json.cmake @@ -0,0 +1,3 @@ +set(JSON_BuildTests OFF CACHE BOOL "JSON Unit tests") +set(JSON_Install ON CACHE BOOL "JSON Install") +add_subdirectory(third_party/json) diff --git a/cmake/target/tl_optional.cmake b/cmake/target/tl_optional.cmake new file mode 100644 index 00000000..f7afb525 --- /dev/null +++ b/cmake/target/tl_optional.cmake @@ -0,0 +1,2 @@ +set(OPTIONAL_ENABLE_TESTS OFF CACHE BOOL "TL_OPTIONAL Tests") +add_subdirectory(third_party/tl_optional) 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 diff --git a/doc/faq/why_this_lib.md b/doc/faq/why_this_lib.md index f14544cd..a44368fb 100644 --- a/doc/faq/why_this_lib.md +++ b/doc/faq/why_this_lib.md @@ -31,6 +31,10 @@ Also see [Target::BuildRecompile() in build.cpp](../../buildcc/lib/target/src/ta - For example: an array cannot be a root when writing your schema. - `flexbuffer` can be used to generate flexibile + efficient JSON. +# JSON + +- See https://github.com/coder137/build_in_cpp/issues/222 + # Fmtlib and Spdlog - Fmtlib and Spdlog are extremely popular libraries for formatting and logging. diff --git a/doc/software_architecture/buildcc_interface_lib.PNG b/doc/software_architecture/buildcc_interface_lib.PNG index e2cc97aa..58a0d56b 100644 Binary files a/doc/software_architecture/buildcc_interface_lib.PNG and b/doc/software_architecture/buildcc_interface_lib.PNG differ diff --git a/doc/software_architecture/buildcc_single_lib.PNG b/doc/software_architecture/buildcc_single_lib.PNG index 50e6f27b..767441d2 100644 Binary files a/doc/software_architecture/buildcc_single_lib.PNG and b/doc/software_architecture/buildcc_single_lib.PNG differ diff --git a/doc/user/installation_using_cmake.md b/doc/user/installation_using_cmake.md index 8789ba00..9e90bfbd 100644 --- a/doc/user/installation_using_cmake.md +++ b/doc/user/installation_using_cmake.md @@ -4,7 +4,6 @@ ## BuildCC User options - BUILDCC_INSTALL: ON -- BUILDCC_FLATBUFFERS_FLATC: ON - BUILDCC_BUILD_AS_SINGLE_LIB: ON - Generates `libbuildcc` - BUILDCC_BUILD_AS_INTERFACE_LIB: OFF diff --git a/docs/source/arch/cmake_boilerplate.rst b/docs/source/arch/cmake_boilerplate.rst index 3c418790..9a882cea 100644 --- a/docs/source/arch/cmake_boilerplate.rst +++ b/docs/source/arch/cmake_boilerplate.rst @@ -57,7 +57,7 @@ We can then ``add_subdirectory`` that particular folder. This helps us keep our ${CMAKE_CURRENT_SOURCE_DIR}/mock/include ) target_link_libraries(mock_env PUBLIC - fmt::fmt-header-only + fmt::fmt Taskflow CppUTest @@ -87,7 +87,6 @@ We can then ``add_subdirectory`` that particular folder. This helps us keep our src/assert_fatal.cpp src/logging.cpp include/env/assert_fatal.h - include/env/assert_throw.h include/env/env.h include/env/logging.h include/env/util.h @@ -123,11 +122,11 @@ We can then ``add_subdirectory`` that particular folder. This helps us keep our $ $ ) - target_link_libraries(env PUBLIC fmt::fmt-header-only) + target_link_libraries(env PUBLIC fmt::fmt) target_compile_options(env PRIVATE ${BUILD_COMPILE_FLAGS}) target_link_options(env PRIVATE ${BUILD_LINK_FLAGS}) target_link_libraries(env PRIVATE - spdlog::spdlog_header_only + spdlog::spdlog tiny-process-library::tiny-process-library ) endif() diff --git a/docs/source/arch/project_layout.rst b/docs/source/arch/project_layout.rst index b009c523..a62579f6 100644 --- a/docs/source/arch/project_layout.rst +++ b/docs/source/arch/project_layout.rst @@ -14,11 +14,15 @@ Project Layout ** README.md ** TODO.md * buildcc - ** env - ** toolchain - ** target - ** args - ** register + ** lib + *** env + *** toolchain + *** target + *** args + *** register + ** schema + ** toolchains + ** targets * bootstrap * buildexe * cmake @@ -34,7 +38,7 @@ Project Layout * third_party ** CLI11 ** cpputest - ** flatbuffers + ** json ** fmt ** spdlog ** taskflow @@ -105,7 +109,7 @@ Global cmake variables and custom_targets * build_flags.cmake * test_flags.cmake * target - * flatbuffers.cmake + * json.cmake * fmt.cmake * spdlog.cmake * tpl.cmake @@ -140,7 +144,7 @@ example third_party ----------- -* Flatbuffers +* JSON * Fmtlib * Spdlog * CLI11 diff --git a/docs/source/arch/software_heirarchy.rst b/docs/source/arch/software_heirarchy.rst index 76538f31..feb4540e 100644 --- a/docs/source/arch/software_heirarchy.rst +++ b/docs/source/arch/software_heirarchy.rst @@ -11,7 +11,7 @@ BuildCC single lib .. uml:: - rectangle Flatbuffers as flatbuffers + rectangle JSON as json rectangle fmt as fmt rectangle spdlog as spdlog rectangle Taskflow as taskflow @@ -20,7 +20,7 @@ BuildCC single lib rectangle BuildCC as buildcc - flatbuffers -up-> buildcc + json -up-> buildcc fmt -up-> buildcc spdlog -up-> buildcc taskflow -up-> buildcc @@ -39,7 +39,7 @@ BuildCC interface lib .. uml:: - rectangle Flatbuffers as flatbuffers #palegreen + rectangle JSON as json #palegreen rectangle fmt as fmt #palegreen rectangle spdlog as spdlog #palegreen rectangle Taskflow as taskflow #palegreen @@ -47,6 +47,7 @@ BuildCC interface lib rectangle "tiny-process-library" as tpl #palegreen rectangle Environment as env #aliceblue + rectangle Schema as schema #aliceblue rectangle Toolchain as toolchain #aliceblue rectangle Target as target #aliceblue rectangle "Toolchain specialized" as toolchain_specialized #aliceblue @@ -61,17 +62,18 @@ BuildCC interface lib spdlog .up.> env tpl .up.> env - flatbuffers .up.> target - taskflow -up-> target - cli11 -up-> args taskflow -up-> register - env -up-> toolchain + json .up.> schema + env -up-> schema + + schema -up-> toolchain toolchain -up-> target - toolchain -up-> toolchain_specialized + taskflow -up-> target + toolchain -up-> toolchain_specialized target -up-> target_specialized target -up-> args diff --git a/docs/source/arch/testing.rst b/docs/source/arch/testing.rst index 58c0b5ef..31423bef 100644 --- a/docs/source/arch/testing.rst +++ b/docs/source/arch/testing.rst @@ -37,17 +37,13 @@ mock_toolchain mock_target ^^^^^^^^^^^^ -.. doxygenfunction:: GeneratorRunner +.. doxygenfunction:: CustomGeneratorRunner -.. doxygenfunction:: GeneratorExpect_InputRemoved +.. doxygenfunction:: CustomGeneratorExpect_IdRemoved -.. doxygenfunction:: GeneratorExpect_InputAdded +.. doxygenfunction:: CustomGeneratorExpect_IdAdded -.. doxygenfunction:: GeneratorExpect_InputUpdated - -.. doxygenfunction:: GeneratorExpect_OutputChanged - -.. doxygenfunction:: GeneratorExpect_CommandChanged +.. doxygenfunction:: CustomGeneratorExpect_IdUpdated From the :doc:`serialization_schema` **generator** we can see that our generator has 3 rebuild conditions diff --git a/docs/source/examples/gcc.rst b/docs/source/examples/gcc.rst index ee5e0246..fb19a1a4 100644 --- a/docs/source/examples/gcc.rst +++ b/docs/source/examples/gcc.rst @@ -218,7 +218,7 @@ Use BuildCC with CMake # fmt is imported by spdlog by default find_package(fmt_package NAMES "fmt" REQUIRED) find_package(spdlog_package NAMES "spdlog" REQUIRED) - find_package(flatbuffers_header_only_package NAMES "flatbuffers_header_only" REQUIRED) + find_package(nlohmann_json_package NAMES "nlohmann_json" REQUIRED) find_package(taskflow_package NAMES "Taskflow" "taskflow" REQUIRED) find_package(CLI11_package NAMES "CLI11" REQUIRED) find_package(tiny_process_library_package NAMES "tiny-process-library" REQUIRED) @@ -227,7 +227,7 @@ Use BuildCC with CMake message("Find package: ${fmt_package_DIR}") message("Find package: ${spdlog_package_DIR}") - message("Find package: ${flatbuffers_header_only_package_DIR}") + message("Find package: ${nlohmann_json_package_DIR}") message("Find package: ${taskflow_package_DIR}") message("Find package: ${CLI11_package_DIR}") message("Find package: ${tiny_process_library_package_DIR}") # diff --git a/docs/source/examples/hybrid.rst b/docs/source/examples/hybrid.rst index abc73c73..b79b802d 100644 --- a/docs/source/examples/hybrid.rst +++ b/docs/source/examples/hybrid.rst @@ -14,20 +14,18 @@ We are only using a single Toolchain - Target pair :linenos: int main(int argc, char ** argv) { - // Args module to get data from the command line or .toml file passed in through --config - Args args; - // Register your [toolchain.{name}] // In this case it will be [toolchain.gcc] ArgToolchain arg_gcc; - args.AddToolchain("gcc", "Generic gcc toolchain", arg_gcc); - // Parse the arguments from the command line - args.Parse(argc, argv); + // Args module to get data from the command line or .toml file passed in through --config + Args::Init() + .AddToolchain("gcc", "Generic gcc toolchain", arg_gcc) + .Parse(argc, argv); // Register module - Register reg(args); - reg.Clean(clean_cb); + Reg::Init(); + Reg::Call(Args::Clean()).Func(clean_cb); // TODO, Write your target builds here // See examples below @@ -75,14 +73,13 @@ Compile a single source with a single GCC compiler. ExecutableTarget_gcc hello_world("hello_world", gcc, ""); // Select your builds and tests using the .toml files - reg.Build(arg_gcc.state, hello_world_build_cb, hello_world); - reg.Test(arg_gcc.state, "{executable}", hello_world); - - // Build Target - reg.RunBuild(); + Reg::Toolchain(arg_gcc.state) + .Func([&]() { gcc.Verify(); }) + .Build(arg_gcc.state, hello_world_build_cb, hello_world) + .Test(arg_gcc.state, "{executable}", hello_world); - // Test Target - reg.RunTest(); + // Build and Test Target + Reg::Run(); } static void hello_world_build_cb(BaseTarget &target) { @@ -100,23 +97,21 @@ We are using multiple Toolchain - Target pairs :linenos: int main(int argc, char ** argv) { - // Args module to get data from the command line or .toml file passed in through --config - Args args; - // Register your [toolchain.{name}] // In this case it will be [toolchain.gcc] and [toolchain.msvc] ArgToolchain arg_gcc; ArgToolchain arg_msvc; - args.AddToolchain("gcc", "Generic gcc toolchain", arg_gcc); - args.AddToolchain("msvc", "Generic msvc toolchain", arg_msvc); - // NOTE, You can add more toolchains here as per your project requirement - // Parse the arguments from the command line - args.Parse(argc, argv); + // Args module to get data from the command line or .toml file passed in through --config + Args::Init() + .AddToolchain("gcc", "Generic gcc toolchain", arg_gcc) + .AddToolchain("msvc", "Generic msvc toolchain", arg_msvc) + .Parse(argc, argv); + // NOTE, You can add more toolchains here as per your project requirement // Register module - Register reg(args); - reg.Clean(clean_cb); + Reg::Init(); + Reg::Call(Args::Clean()).Func(clean_cb); // TODO, Write your target builds here // See examples below @@ -167,30 +162,30 @@ Similar to lowlevel GCC Flags example for both the GCC and MSVC compiler // See Multiple Boilerplate Toolchain_gcc gcc; - Toolchain_msvc msvc; - ExecutableTarget_gcc g_cppflags("cppflags", gcc, "files"); - ExecutableTarget_msvc m_cppflags("cppflags", msvc, "files"); ExecutableTarget_gcc g_cflags("cflags", gcc, "files"); + // Select your builds and tests using the .toml files + Reg::Toolchain(arg_gcc.state) + .Func([&](){ gcc.Verify(); }) + .Build(cppflags_build_cb, g_cppflags) + .Build(cflags_build_cb, g_cflags) + .Test("{executable}", g_cppflags) + .Test("{executable}", g_cflags); + + Toolchain_msvc msvc; + ExecutableTarget_msvc m_cppflags("cppflags", msvc, "files"); ExecutableTarget_msvc m_cflags("cflags", msvc, "files"); + Reg::Toolchain(arg_msvc.state) + .Func([&](){ msvc.Verify(); }) + .Build(cppflags_build_cb, m_cppflags) + .Build(cflags_build_cb, m_cflags) + .Test("{executable}", m_cppflags) + .Test("{executable}", m_cflags); - // Select your builds and tests using the .toml files - reg.Build(arg_gcc.state, cppflags_build_cb, g_cppflags); - reg.Build(arg_msvc.state, cppflags_build_cb, m_cppflags); - reg.Build(arg_gcc.state, cflags_build_cb, g_cflags); - reg.Build(arg_msvc.state, cflags_build_cb, m_cflags); - - // Test steps - reg.Test(arg_gcc.state, "{executable}", g_cppflags); - reg.Test(arg_msvc.state, "{executable}", m_cppflags); - reg.Test(arg_gcc.state, "{executable}", g_cflags); - reg.Test(arg_msvc.state, "{executable}", m_cflags); - - // Build Target - reg.RunBuild(); - - // Test Target - reg.RunTest(); + // Build and Test target + Reg::Run(); + + return 0; } static void cppflags_build_cb(BaseTarget &cppflags) { @@ -301,7 +296,6 @@ For end users consuming third party libraries .. code-block:: cpp :linenos: - :emphasize-lines: 18 #include "build.foo.h" @@ -312,13 +306,16 @@ For end users consuming third party libraries Toolchain_gcc gcc; Toolchain_msvc msvc; - ExecutableTarget_gcc g_foolib("foolib", gcc, ""); - ExecutableTarget_msvc m_foolib("foolib", msvc, ""); + ExecutableTarget_gcc g_foolib("foolib", gcc, TargetEnv("...")); + ExecutableTarget_msvc m_foolib("foolib", msvc, TargetEnv("..."); - reg.Build(arg_gcc.state, foolib_build_cb, g_foolib); - reg.Build(arg_msvc.state, foolib_build_cb, m_foolib); + Reg::Toolchain(arg_gcc.state) + .Build(foolib_build_cb, g_foolib); + + Reg::Toolchain(arg_msvc.state) + .Build(foolib_build_cb, m_foolib); - reg.RunBuild(); + Reg::Run(); } static void foolib_build_cb(BaseTarget &target) { @@ -334,48 +331,44 @@ For super customized targets and toolchains .. code-block:: cpp :linenos: - :emphasize-lines: 12,13,30,34,35,36 int main(int argc, char ** argv) { - Args args; ArgToolchain arg_gcc; ArgToolchain arg_msvc; ArgToolchain toolchain_clang_gnu; ArgTarget target_clang_gnu; - args.AddToolchain("gcc", "Generic gcc toolchain", arg_gcc); - args.AddToolchain("msvc", "Generic msvc toolchain", arg_msvc); - - // Add your custom toolchain - target to the command line - args.AddToolchain("clang_gnu", "Clang GNU toolchain", toolchain_clang_gnu); - args.AddTarget("clang_gnu", "Clang GNU target", target_clang_gnu); - args.Parse(argc, argv); + Args::Init() + .AddToolchain("gcc", "Generic gcc toolchain", arg_gcc) + .AddToolchain("msvc", "Generic msvc toolchain", arg_msvc) + .AddToolchain("clang_gnu", "Clang GNU toolchain", toolchain_clang_gnu) + .AddTarget("clang_gnu", "Clang GNU target", target_clang_gnu) + .Parse(argc, argv); // Additional boilerplate // Supplied at compile time Toolchain_gcc gcc; Toolchain_msvc msvc; + // Get custom toolchain from the command line, supplied at run time + auto &clang = toolchain_clang_gnu; + clang.SetToolchainInfoFunc(GlobalToolchainInfo::Get(clang.id)); ExecutableTarget_gcc g_foolib("foolib", gcc, ""); ExecutableTarget_msvc m_foolib("foolib", msvc, ""); - - reg.Build(arg_gcc.state, foolib_build_cb, g_foolib); - reg.Build(arg_msvc.state, foolib_build_cb, m_foolib); - - // Get custom toolchain from the command line, supplied at run time - BaseToolchain clang = toolchain_clang_gnu.ConstructToolchain(); - // Get compile_command and link_command from the command line - TargetConfig config; - config.compile_command = target_clang_gnu.compile_command; - config.link_command = target_clang_gnu.link_command; - Target_custom c_foolib("CFoolib.exe", TargetType::Executable, clang, "", config); - reg.Build(toolchain_clang_gnu.state, foolib_build_cb, c_foolib); + Target_custom c_foolib("CFoolib.exe", TargetType::Executable, clang, "", target_clang_gnu.GetTargetConfig()); + + Reg::Toolchain(arg_gcc.state) + .Build(foolib_build_cb, g_foolib); + Reg::Toolchain(arg_msvc.state) + .Build(foolib_build_cb, m_foolib); + Reg::Toolchain(toolchain_clang_gnu.state) + .Build( foolib_build_cb, c_foolib); // Build targets - reg.RunBuild(); + Reg::Run(); } static void foolib_build_cb(BaseTarget &target) { @@ -387,7 +380,6 @@ For super customized targets and toolchains .. code-block:: toml :linenos: - :emphasize-lines: 4,18 # See Multiple boilerplate .toml file @@ -418,7 +410,6 @@ Precompile header usage with GCC and MSVC compilers .. code-block:: cpp :linenos: - :emphasize-lines: 8,9,10,36,37,38 // Modified Lowlevel GCC Flags example for PCH @@ -487,7 +478,6 @@ Chain **Generators** and **Targets** using the ``Register`` module .. code-block:: cpp :linenos: - :emphasize-lines: 7,8,17,18 int main(int argc, char ** argv) { // See Multiple Boilerplate @@ -496,17 +486,19 @@ Chain **Generators** and **Targets** using the ``Register`` module Toolchain_msvc msvc; BaseGenerator cpp_generator("cpp_generator", ""); - reg.Build(cpp_generator_cb, cpp_generator); + Reg::Call().Build(cpp_generator_cb, cpp_generator); ExecutableTarget_gcc g_cpptarget("cpptarget", gcc, ""); - reg.Build(arg_gcc.state, cpp_target_cb, g_cpptarget, cpp_generator); + Reg::Toolchain(arg_gcc.state) + .Func([&](){ gcc.Verify(); }) + .Build(cpp_target_cb, g_cpptarget, cpp_generator) + .Dep(g_cpptarget, cpp_generator); ExecutableTarget_msvc m_cpptarget("cpptarget", msvc, ""); - reg.Build(arg_msvc.state, cpp_target_cb, m_cpptarget, cpp_generator); - - // cpp_generator runs before g_cpptarget and m_cpptarget - reg.Dep(g_cpptarget, cpp_generator); - reg.Dep(m_cpptarget, cpp_generator); + Reg::Toolchain(arg_msvc.state) + .Func([&](){ msvc.Verify(); }) + .Build(cpp_target_cb, m_cpptarget, cpp_generator) + .Dep(m_cpptarget, cpp_generator); } // Use a python generator to create main.cpp @@ -537,7 +529,6 @@ Target Info .. code-block:: cpp :linenos: - :emphasize-lines: 8,9 int main(int argc, char ** argv) { // See Multiple boilerplate @@ -546,14 +537,17 @@ Target Info Toolchain_msvc msvc; // TargetInfo - TargetInfo genericadd_ho("files"); - reg.Callback(genericadd_ho_cb, genericadd_ho); - + TargetInfo g_genericadd_ho(gcc, "files"); ExecutableTarget_gcc g_genericadd1("generic_add_1", gcc, "files"); - ExecutableTarget_msvc m_genericadd1("generic_add_1", msvc, "files"); + Reg::Toolchain(arg_gcc.state) + .Func(genericadd_ho_cb, g_genericadd_ho) + .Build(genericadd1_build_cb, g_genericadd1, g_genericadd_ho); - reg.Build(arg_gcc.state, genericadd1_build_cb, g_genericadd1, genericadd_ho); - reg.Build(arg_msvc.state, genericadd1_build_cb, m_genericadd1, genericadd_ho); + TargetInfo m_genericadd_ho(msvc, "files"); + ExecutableTarget_msvc m_genericadd1("generic_add_1", msvc, "files"); + Reg::Toolchain(arg_msvc.state) + .Func(genericadd_ho_cb, m_genericadd_ho) + .Build(genericadd1_build_cb, m_genericadd1, m_genericadd_ho); } // HO library contains include dirs and header files which are copied into executable target diff --git a/docs/source/getting_started/buildexe_package_manager.rst b/docs/source/getting_started/buildexe_package_manager.rst index 161e09ad..5e9493c8 100644 --- a/docs/source/getting_started/buildexe_package_manager.rst +++ b/docs/source/getting_started/buildexe_package_manager.rst @@ -192,7 +192,6 @@ Write your C++ "script" .. code-block:: cpp :linenos: - :emphasize-lines: 4,8,38,39,40,43,44,47,48,52,79 #include "buildcc.h" @@ -210,23 +209,22 @@ Write your C++ "script" static void hello_world_build_cb(BaseTarget &target, BaseTarget &fmt_lib); int main(int argc, char **argv) { - // 1. Get arguments - Args args; + // Get arguments ArgToolchain arg_gcc; - args.AddToolchain("gcc", "Generic gcc toolchain", arg_gcc); - args.Parse(argc, argv); - // 2. Initialize your environment - Register reg(args); + Args::Init() + .AddToolchain("gcc", "Generic gcc toolchain", arg_gcc) + .Parse(argc, argv); - // 3. Pre-build steps - reg.Clean(clean_cb); + // Initialize your environment + Reg::Init(); - // 4. Build steps + // Pre-build steps + Reg::Call(Args::Clean().Func(clean_cb); + + // Build steps // Explicit toolchain - target pairs Toolchain_gcc gcc; - auto verified_toolchains = gcc.Verify(); - env::assert_fatal(!verified_toolchains.empty(), "GCC Toolchain not found"); // Setup your [Library]Target_[toolchain] fmtlib instance // Update your TargetEnv to point to `BuildExeLibDir::fmt` folder @@ -237,29 +235,24 @@ Write your C++ "script" // We use the build.fmt.h and build.fmt.cpp APIs to define how we build our fmtlib FmtConfig fmt_config; - reg.Build(arg_gcc.state, build_fmt_cb, fmt_lib, fmt_config); // Define our hello world executable ExecutableTarget_gcc hello_world("hello_world", gcc, ""); - reg.Build(arg_gcc.state, hello_world_build_cb, hello_world, fmt_lib); - + // Fmt lib is a dependency to the Hello world executable // This means that fmt lib is guaranteed to be built before the hello world executable - reg.Dep(hello_world, fmt_lib); - - // 5. Test steps i.e Hello world is automatically run - reg.Test(arg_gcc.state, "{executable}", hello_world); + Reg::Toolchain(arg_gcc.state) + .Build(arg_gcc.state, build_fmt_cb, fmt_lib, fmt_config) + .Build(hello_world_build_cb, hello_world, fmt_lib) + .Dep(hello_world, fmt_lib) + .Test("{executable}", hello_world);; + - // 6. Build Target + // Build and Test Target // Builds libfmt.a and ./hello_world - reg.RunBuild(); - - // 7. Test Target - // Executes ./hello_world - // Output -> Hello World - reg.RunTest(); + Reg::Run(); - // 8. Post Build steps + // Post Build steps // - Clang Compile Commands plugin::ClangCompileCommands({&hello_world}).Generate(); // - Graphviz dump diff --git a/docs/source/getting_started/buildexe_script_example.rst b/docs/source/getting_started/buildexe_script_example.rst index 23a3f211..ca229500 100644 --- a/docs/source/getting_started/buildexe_script_example.rst +++ b/docs/source/getting_started/buildexe_script_example.rst @@ -84,7 +84,6 @@ We then setup our main **toolchain**-**target** pairs. Highlighted below .. code-block:: cpp :linenos: - :emphasize-lines: 25,26,27,29,30 :caption: build.helloworld.cpp #include "buildcc.h" @@ -96,32 +95,33 @@ We then setup our main **toolchain**-**target** pairs. Highlighted below void hello_world_build_cb(BaseTarget & target); int main(int argc, char ** argv) { - // Step 1. Setup your args - Args args; + // Setup your args ArgToolchain arg_gcc; - args.AddToolchain("gcc", "GCC toolchain", arg_gcc); - args.Parse(argc, argv); - // Step 2. Register - Register reg(args); + Args::Init() + .AddToolchain("gcc", "GCC toolchain", arg_gcc) + .Parse(argc, argv); - // Step 3. Pre build steps + // Register + Reg::Init(); + + // Pre build steps // for example. clean your environment - reg.Clean(clean_cb); + Reg::Call(Args::Clean()).Func(clean_cb); - // Step 4. Build steps + // Build steps // Main setup Toolchain_gcc gcc; - auto verified_gcc_toolchains = gcc.Verify(); - env::assert_fatal(!verified_gcc_toolchains.empty(), "GCC toolchain not found"); - ExecutableTarget_gcc hello_world("hello_world", gcc, ""); - reg.Build(arg_gcc.state, hello_world_build_cb, hello_world); - // Step 5. Build your targets - reg.RunBuild(); + Reg::Toolchain(arg_gcc.state) + .Func([&](){ gcc.Verify() }) + .Build(hello_world_build_cb, hello_world); + + // Build your targets + Reg::Run(); - // Step 6. Post build steps + // Post build steps // for example. clang compile commands database plugin::ClangCompileCommands({&hello_world}).Generate(); diff --git a/docs/source/getting_started/buildexe_setup.rst b/docs/source/getting_started/buildexe_setup.rst index 8d2b5918..7c554971 100644 --- a/docs/source/getting_started/buildexe_setup.rst +++ b/docs/source/getting_started/buildexe_setup.rst @@ -144,7 +144,7 @@ For example we download the **fmt library** to our libs folder **** [git cloned] *** spdlog **** [git cloned] - *** flatbuffers + *** json **** [git cloned] ** host *** gcc_x86_64-linux-gnu_9.3.0.toml diff --git a/docs/source/intro/toc.rst b/docs/source/intro/toc.rst index 3e2b01f5..46b05085 100644 --- a/docs/source/intro/toc.rst +++ b/docs/source/intro/toc.rst @@ -25,7 +25,7 @@ Pre-requisites * C++17 filesystem library support * C++11 thread library support * Third Party Libraries (See License below) - * Flatbuffers v2.0.0 + * JSON v3.11.2 * Taskflow v3.1.0 * CLI11 v2.1.0 * Tiny Process Library v2.0.4 @@ -68,6 +68,6 @@ Licenses * `Tiny Process Library `_ (Process handling) [MIT License] * `Taskflow `_ (Parallel Programming) [MIT License] [Header Only] * See also `3rd-Party `_ used by Taskflow -* `Flatbuffers `_ (Serialization) [Apache-2.0 License] [Header Only] +* `Nlohmann::Json `_ (Serialization) [MIT License] [Header Only] * `CLI11 `_ (Argument Parsing) [BSD-3-Clause License] [Header Only] * `CppUTest `_ (Unit Testing/Mocking) [BSD-3-Clause License] diff --git a/docs/source/user_api/args.rst b/docs/source/user_api/args.rst index 383268c7..5c7bbd72 100644 --- a/docs/source/user_api/args.rst +++ b/docs/source/user_api/args.rst @@ -5,11 +5,14 @@ args.h ------- .. doxygenclass:: buildcc::Args - :members: Args, Parse, Ref, ConstRef, AddToolchain, Clean, GetLogLevel, GetProjectRootDir, GetProjectBuildDir + +.. doxygenstruct:: buildcc::ArgCustom .. doxygenstruct:: buildcc::ArgToolchainState -.. doxygenstruct:: buildcc::ArgToolchain +.. doxygenclass:: buildcc::ArgToolchain + +.. doxygenstruct:: buildcc::ArgTarget Example --------- @@ -17,30 +20,42 @@ Example .. code-block:: cpp :linenos: + using namespace buildcc; + + struct CustomData : ArgCustom { + void Add(CLI::App & app) override { + // setup your app from data1, data2, data3, data... + // NOTE: The Add method should not be invoked by the user + // NOTE: The Add method is only expected to be invoked once, not multiple times. + } + + std::string data1; + int data2; + float data3; + // etc + }; + int main(int argc, char ** argv) { - Args args; ArgToolchain arg_gcc_toolchain; - args.AddToolchain("gcc", "Generic GCC toolchain", arg_gcc_toolchain); - - // TODO, Add ArgTarget example (Currently incomplete) - args.Parse(argc, argv); + ArgCustomData custom_data; + Args::Init() + .AddToolchain("gcc", "Generic GCC toolchain", arg_gcc_toolchain) + .AddCustomCallback([](CLI::App &app) {}); + .AddCustomData(custom_data); + .Parse(argc, argv); // Root - args.GetProjectRootDir(); // Contains ``root_dir`` value - args.GetProjectBuildDir(); // Contains ``build_dir`` value - args.GetLogLevel(); // Contains ``loglevel`` enum - args.Clean(); // Contains ``clean`` value + Args::GetProjectRootDir(); // Contains ``root_dir`` value + Args::GetProjectBuildDir(); // Contains ``build_dir`` value + Args::GetLogLevel(); // Contains ``loglevel`` enum + Args::Clean(); // Contains ``clean`` value // Toolchain // .build, .test arg_gcc_toolchain.state; // .id, .name, .asm_compiler, .c_compiler, .cpp_compiler, .archiver, .linker -> BaseToolchain - BaseToolchain gcc_toolchain = arg_gcc_toolchain.ConstructToolchain(); - - // Underlying CLI11 library - auto & app = args.Ref(); - const auto & app = args.ConstRef(); - + auto &gcc_toolchain = arg_gcc_toolchain; + gcc_toolchain.SetToolchainInfoFunc(GlobalToolchainInfo::Get(gcc_toolchain.id)); return 0; } diff --git a/docs/source/user_api/environment.rst b/docs/source/user_api/environment.rst index e353b601..07a869af 100644 --- a/docs/source/user_api/environment.rst +++ b/docs/source/user_api/environment.rst @@ -5,11 +5,7 @@ Environment env.h ----- -.. doxygenfunction:: is_init - -.. doxygenfunction:: get_project_root_dir - -.. doxygenfunction:: get_project_build_dir +.. doxygenclass:: buildcc::Project logging.h --------- @@ -37,19 +33,6 @@ assert_fatal.h .. doxygendefine:: ASSERT_FATAL -assert_throw.h --------------- - -.. doxygenfunction:: assert_throw([[maybe_unused]] const char *) - -.. doxygenfunction:: assert_throw(const std::string &) - -.. doxygenfunction:: assert_throw(bool, const char *) - -.. doxygenfunction:: assert_throw(bool, const std::string &) - -.. doxygendefine:: ASSERT_THROW - command.h --------- diff --git a/docs/source/user_api/generator.rst b/docs/source/user_api/generator.rst index 9301e6a3..364a1cf0 100644 --- a/docs/source/user_api/generator.rst +++ b/docs/source/user_api/generator.rst @@ -1,16 +1,26 @@ -Generator -========= +Template Generator +=================== -generator.h ------------ +> TODO, -.. doxygenclass:: buildcc::Generator +File Generator +=============== -.. doxygentypedef:: BaseGenerator +> TODO + +Custom Generator +================ + +custom_generator.h +------------------- + +.. doxygenclass:: buildcc::CustomGenerator Example -------- +> TODO, Update example + .. code-block:: cpp :linenos: diff --git a/docs/source/user_api/register.rst b/docs/source/user_api/register.rst index 77033451..4d8d5795 100644 --- a/docs/source/user_api/register.rst +++ b/docs/source/user_api/register.rst @@ -4,9 +4,7 @@ Register register.h ----------- -.. doxygenclass:: buildcc::Register - :members: Register, Clean, Callback, CallbackIf, Build, Dep, Test, RunBuild, RunTest - +.. doxygenclass:: buildcc::Reg test_info.h ------------- @@ -26,19 +24,20 @@ Example static void callback_usage_func(const BigObj & cobj, BigObj & obj); int main(int argc, char ** argv) { - Args args; - args.Parse(argc, argv); + Args::Init() + .Parse(argc, argv); + + Reg::Init(); + Reg::Call(Args::Clean()).Func([](){ + fs::remove_all(Project::GetBuildDir()); + }) - Register reg(args); - reg.Clean([](){ - fs::remove_all(env::get_project_build_dir()); - }); BigObj obj; - reg.Callback(callback_usage_func, BigObj(), obj); + Reg::Call().Func(callback_usage_func, BigObj(), obj) bool expression = true; // false - reg.CallbackIf(expression, callback_usage_func, BigObj(), obj); + Reg::Call(expression).Func(callback_usage_func, BigObj(), obj) // Example snippets of these given in Target API // Build diff --git a/docs/source/user_api/target.rst b/docs/source/user_api/target.rst index 30931368..5d7fa001 100644 --- a/docs/source/user_api/target.rst +++ b/docs/source/user_api/target.rst @@ -36,11 +36,6 @@ sync_api.h .. doxygenclass:: buildcc::internal::SyncApi -target_getter.h ------------------ - -.. doxygenclass:: buildcc::internal::TargetInfoGetter - TargetInfo =========== @@ -56,10 +51,10 @@ Target APIs .. important:: Target APIs can also use TargetInfo APIs -target_info_getter.h +target_env.h --------------------- -.. doxygenclass:: buildcc::internal::TargetGetter +.. doxygenclass:: buildcc::internal::TargetEnvApi Target ======= diff --git a/docs/source/user_api/target_utils.rst b/docs/source/user_api/target_utils.rst index b85fc251..4940883f 100644 --- a/docs/source/user_api/target_utils.rst +++ b/docs/source/user_api/target_utils.rst @@ -3,11 +3,6 @@ Target Utils Commonly used Utils (Classes / Structs) for **Generator**, **TargetInfo** and **Target** classes. -target_file_ext.h ------------------- - -.. doxygenenum:: buildcc::TargetFileExt - target_type.h -------------- diff --git a/docs/source/user_api/toc.rst b/docs/source/user_api/toc.rst index c149c8cd..c7c858d8 100644 --- a/docs/source/user_api/toc.rst +++ b/docs/source/user_api/toc.rst @@ -6,6 +6,7 @@ User API args register + toolchain_utils toolchain target_utils generator diff --git a/docs/source/user_api/toolchain.rst b/docs/source/user_api/toolchain.rst index a3710106..debbe45d 100644 --- a/docs/source/user_api/toolchain.rst +++ b/docs/source/user_api/toolchain.rst @@ -6,29 +6,79 @@ toolchain.h .. doxygenclass:: buildcc::Toolchain -.. doxygentypedef:: ToolchainId - .. doxygentypedef:: BaseToolchain +toolchain_find.h +----------------- + +.. doxygenclass:: buildcc::ToolchainFind + +.. doxygenstruct:: buildcc::ToolchainFindConfig + toolchain_verify.h ------------------ .. doxygenclass:: buildcc::ToolchainVerify -.. doxygenstruct:: buildcc::VerifyToolchainConfig +.. doxygenstruct:: buildcc::ToolchainCompilerInfo -.. doxygenstruct:: buildcc::VerifiedToolchain - -Example --------- +Example for Default Toolchain +------------------------------ .. code-block:: cpp :linenos: - BaseToolchain custom_toolchain(ToolchainId::Custom, "custom_new_toolchain", "asm_compiler", "c_compiler", "cpp_compiler", "archiver", "linker"); + BaseToolchain arm_gcc(ToolchainId::Gcc, "arm-none-eabi-gcc", "arm-none-eabi-as", "arm-none-eabi-gcc", "arm-none-eabi-g++", "arm-none-eabi-ar", "arm-none-eabi-ld"); + + // Toolchain::Find is only used to return a list of paths where the ToolchainExecutables are found + // NOTE: All ToolchainExecutables must be found in a single directory for it to be present in the list + { + ToolchainFindConfig find_config; + // Modify it here if needed + auto found_toolchains = arm_gcc.Find(find_config); + } + + // Runs Toolchain::Find + // Selects first found toolchain (update ToolchainVerifyConfig if you want to select a different toolchain for verification) + // Runs a pre-added ToolchainId::GCC verification function + // If Verification Fails: Terminates the program + // Else: Updates the arm_gcc ToolchainExecutables to the full path + // i.e `arm-none-eabi-gcc` becomes `{host_absolute_path}/arm-none-eabi-gcc{host_executable_extension}` + { + ToolchainVerifyConfig verify_config; + // Modify it here if needed + arm_gcc.Verify(verify_config); + } + +Example for Custom Toolchain +---------------------------- + +.. code-block:: cpp + :linenos: - std::vector verified_toolchains = custom_toolchain.Verify(); - env::assert_fatal(!verified_toolchains.empty(), "Toolchain not found"); + BaseToolchain custom_toolchain(ToolchainId::Custom, "custom_new_toolchain", "assembler", "c_compiler", "cpp_compiler", "archiver", "linker"); + + // Toolchain::Find similar to previous example + + // Find all the relevant toolchains on your host system + // Selects the first found toolchain + // Runs a verification function on the selected toolchain depending on the `ToolchainId` + Toolchain::AddVerificationFunc(ToolchainId::Custom, + [](const ToolchainExecutables & executables) -> buildcc::env::optional { + // Use executables to get compiler_version and target_arch + if (success) { + ToolchainCompilerInfo info; + info.compiler_version = "compiler_version"; + info.target_arch = "target_arch"; + return info; + } else { + return {}; + } + }, "custom_verification_func") + + ToolchainVerifyConfig verify_config; + verify_config.verification_identifier = "custom_verification_func"; + custom_toolchain.Verify(verify_config); Specialized Toolchain ===================== diff --git a/docs/source/user_api/toolchain_utils.rst b/docs/source/user_api/toolchain_utils.rst new file mode 100644 index 00000000..b3161529 --- /dev/null +++ b/docs/source/user_api/toolchain_utils.rst @@ -0,0 +1,24 @@ +Toolchain Utils +================ + +Commonly used Utils (Classes / Structs) for **Toolchain** + +file_ext.h +------------ + +.. doxygenenum:: buildcc::FileExt + +toolchain_config.h +-------------------- + +.. doxygenstruct:: buildcc::ToolchainConfig + +toolchain_id.h +--------------- + +.. doxygenenum:: buildcc::ToolchainId + +toolchain_executables.h +----------------------- + +.. doxygenstruct:: buildcc::ToolchainExecutables diff --git a/example/buildexe/libs/build.main.cpp b/example/buildexe/libs/build.main.cpp index 0159796c..64301e32 100644 --- a/example/buildexe/libs/build.main.cpp +++ b/example/buildexe/libs/build.main.cpp @@ -16,56 +16,49 @@ static void clean_cb(); static void hello_world_build_cb(BaseTarget &target, BaseTarget &fmt_lib); int main(int argc, char **argv) { - // 1. Get arguments - Args args; + // Get arguments ArgToolchain arg_gcc; - args.AddToolchain("gcc", "Generic gcc toolchain", arg_gcc); - args.Parse(argc, argv); + Args::Init() + .AddToolchain("gcc", "Generic gcc toolchain", arg_gcc) + .Parse(argc, argv); - // 2. Initialize your environment - Register reg(args); + // Initialize your environment + Reg::Init(); - // 3. Pre-build steps - reg.Clean(clean_cb); + // Pre-build steps + Reg::Call(Args::Clean()).Func(clean_cb); - // 4. Build steps + // Build steps // Explicit toolchain - target pairs Toolchain_gcc gcc; - auto verified_toolchains = gcc.Verify(); - env::assert_fatal(!verified_toolchains.empty(), "GCC Toolchain not found"); StaticTarget_gcc fmt_lib( "libfmt", gcc, - TargetEnv(BuildExeLibDir::fmt, env::get_project_build_dir() / "fmt")); + TargetEnv(BuildExeLibDir::fmt, Project::GetBuildDir() / "fmt")); FmtConfig fmt_config; - reg.Build(arg_gcc.state, build_fmt_cb, fmt_lib, fmt_config); - ExecutableTarget_gcc hello_world("hello_world", gcc, ""); - reg.Build(arg_gcc.state, hello_world_build_cb, hello_world, fmt_lib); - - reg.Dep(hello_world, fmt_lib); - - // 5. Test steps - reg.Test(arg_gcc.state, "{executable}", hello_world); - - // 6. Build Target - reg.RunBuild(); + Reg::Toolchain(arg_gcc.state) + .Func([&]() { gcc.Verify(); }) + .Build(build_fmt_cb, fmt_lib, fmt_config) + .Build(hello_world_build_cb, hello_world, fmt_lib) + .Dep(hello_world, fmt_lib) + .Test("{executable}", hello_world); - // 7. Test Target - reg.RunTest(); + // Build Target + Reg::Run(); - // 8. Post Build steps + // Post Build steps // - Clang Compile Commands plugin::ClangCompileCommands({&hello_world}).Generate(); // - Graphviz dump - std::cout << reg.GetTaskflow().dump() << std::endl; + std::cout << Reg::GetTaskflow().dump() << std::endl; return 0; } static void clean_cb() { - env::log_info(EXE, fmt::format("Cleaning {}", env::get_project_build_dir())); - fs::remove_all(env::get_project_build_dir()); + env::log_info(EXE, fmt::format("Cleaning {}", Project::GetBuildDir())); + fs::remove_all(Project::GetBuildDir()); } static void hello_world_build_cb(BaseTarget &target, BaseTarget &fmt_lib) { diff --git a/example/gcc/AfterInstall/CMakeLists.txt b/example/gcc/AfterInstall/CMakeLists.txt index 6848d43a..55b3aca4 100644 --- a/example/gcc/AfterInstall/CMakeLists.txt +++ b/example/gcc/AfterInstall/CMakeLists.txt @@ -12,8 +12,9 @@ project(simple) # fmt is imported by spdlog by default find_package(fmt_package NAMES "fmt" REQUIRED) find_package(spdlog_package NAMES "spdlog" REQUIRED) -find_package(flatbuffers_header_only_package NAMES "flatbuffers_header_only" REQUIRED) +find_package(nlohmann_json_package NAMES "nlohmann_json" REQUIRED) find_package(taskflow_package NAMES "Taskflow" "taskflow" REQUIRED) +find_package(tl_optional_package NAMES "tl-optional" REQUIRED) find_package(CLI11_package NAMES "CLI11" REQUIRED) find_package(tiny_process_library_package NAMES "tiny-process-library" REQUIRED) @@ -21,7 +22,8 @@ find_package(buildcc_package NAMES "buildcc" REQUIRED) message("Find package: ${fmt_package_DIR}") message("Find package: ${spdlog_package_DIR}") -message("Find package: ${flatbuffers_header_only_package_DIR}") +message("Find package: ${nlohmann_json_package_DIR}") +message("Find package: ${tl_optional_package_DIR}") message("Find package: ${taskflow_package_DIR}") message("Find package: ${CLI11_package_DIR}") message("Find package: ${tiny_process_library_package_DIR}") # diff --git a/example/gcc/AfterInstall/build.cpp b/example/gcc/AfterInstall/build.cpp index 0b1cc61b..1c7a08ca 100644 --- a/example/gcc/AfterInstall/build.cpp +++ b/example/gcc/AfterInstall/build.cpp @@ -12,7 +12,7 @@ int main(void) { // this path // 2. Intermediate build folder i.e all intermediate generated files should go // here - env::init(BUILD_ROOT, BUILD_INTERMEDIATE_DIR); + Project::Init(BUILD_ROOT, BUILD_INTERMEDIATE_DIR); env::set_log_level(env::LogLevel::Trace); Toolchain_gcc gcc; diff --git a/example/gcc/DynamicLib/build.cpp b/example/gcc/DynamicLib/build.cpp index 56af0976..7f7e4056 100644 --- a/example/gcc/DynamicLib/build.cpp +++ b/example/gcc/DynamicLib/build.cpp @@ -13,7 +13,7 @@ int main(void) { // this path // 2. Intermediate build folder i.e all intermediate generated files should go // here - env::init(BUILD_ROOT, BUILD_INTERMEDIATE_DIR); + Project::Init(BUILD_ROOT, BUILD_INTERMEDIATE_DIR); env::set_log_level(env::LogLevel::Trace); Toolchain_gcc gcc; diff --git a/example/gcc/Flags/build.cpp b/example/gcc/Flags/build.cpp index 39401c58..01a412b6 100644 --- a/example/gcc/Flags/build.cpp +++ b/example/gcc/Flags/build.cpp @@ -12,7 +12,7 @@ int main(void) { // this path // 2. Intermediate build folder i.e all intermediate generated files should go // here - env::init(BUILD_ROOT, BUILD_INTERMEDIATE_DIR); + Project::Init(BUILD_ROOT, BUILD_INTERMEDIATE_DIR); env::set_log_level(env::LogLevel::Trace); Toolchain_gcc gcc; diff --git a/example/gcc/IncludeDir/build.cpp b/example/gcc/IncludeDir/build.cpp index 4444a8d1..1a64262a 100644 --- a/example/gcc/IncludeDir/build.cpp +++ b/example/gcc/IncludeDir/build.cpp @@ -12,7 +12,7 @@ int main(void) { // this path // 2. Intermediate build folder i.e all intermediate generated files should go // here - env::init(BUILD_ROOT, BUILD_INTERMEDIATE_DIR); + Project::Init(BUILD_ROOT, BUILD_INTERMEDIATE_DIR); env::set_log_level(env::LogLevel::Trace); Toolchain_gcc gcc; diff --git a/example/gcc/Plugins/build.cpp b/example/gcc/Plugins/build.cpp index 29b8da0d..1c324a56 100644 --- a/example/gcc/Plugins/build.cpp +++ b/example/gcc/Plugins/build.cpp @@ -12,7 +12,7 @@ int main(void) { // this path // 2. Intermediate build folder i.e all intermediate generated files should go // here - env::init(BUILD_ROOT, BUILD_INTERMEDIATE_DIR); + Project::Init(BUILD_ROOT, BUILD_INTERMEDIATE_DIR); env::set_log_level(env::LogLevel::Trace); Toolchain_gcc gcc; diff --git a/example/gcc/Simple/build.cpp b/example/gcc/Simple/build.cpp index 8c5125f3..04686bd3 100644 --- a/example/gcc/Simple/build.cpp +++ b/example/gcc/Simple/build.cpp @@ -12,7 +12,7 @@ int main(void) { // this path // 2. Intermediate build folder i.e all intermediate generated files should go // here - env::init(BUILD_SCRIPT_SOURCE, BUILD_SCRIPT_FOLDER); + Project::Init(BUILD_SCRIPT_SOURCE, BUILD_SCRIPT_FOLDER); env::set_log_level(env::LogLevel::Trace); Toolchain_gcc gcc; diff --git a/example/gcc/StaticLib/build.cpp b/example/gcc/StaticLib/build.cpp index 81c971f2..163264f8 100644 --- a/example/gcc/StaticLib/build.cpp +++ b/example/gcc/StaticLib/build.cpp @@ -12,7 +12,7 @@ int main(void) { // this path // 2. Intermediate build folder i.e all intermediate generated files should go // here - env::init(BUILD_ROOT, BUILD_INTERMEDIATE_DIR); + Project::Init(BUILD_ROOT, BUILD_INTERMEDIATE_DIR); env::set_log_level(env::LogLevel::Trace); Toolchain_gcc gcc; diff --git a/example/hybrid/custom_target/build.main.cpp b/example/hybrid/custom_target/build.main.cpp index c54c8cbc..88ece332 100644 --- a/example/hybrid/custom_target/build.main.cpp +++ b/example/hybrid/custom_target/build.main.cpp @@ -8,57 +8,52 @@ static void foolib_build_cb(BaseTarget &target); static constexpr std::string_view EXE = "build"; int main(int argc, char **argv) { - // 1. Get arguments - Args args; + // Get arguments ArgToolchain arg_gcc; ArgToolchain arg_msvc; - ArgToolchain toolchain_clang_gnu; - ArgTarget target_clang_gnu; - args.AddToolchain("gcc", "Generic gcc toolchain", arg_gcc); - args.AddToolchain("msvc", "Generic msvc toolchain", arg_msvc); - args.AddToolchain("clang_gnu", "Clang GNU toolchain", toolchain_clang_gnu); - args.AddTarget("clang_gnu", "Clang GNU target", target_clang_gnu); - args.Parse(argc, argv); - - // 2. Initialize your environment - Register reg(args); - - // 3. Pre-build steps - reg.Clean(clean_cb); - - // 4. Build steps + ArgToolchain arg_toolchain_clang_gnu; + ArgTarget arg_target_clang_gnu; + Args::Init() + .AddToolchain("gcc", "Generic gcc toolchain", arg_gcc) + .AddToolchain("msvc", "Generic msvc toolchain", arg_msvc) + .AddToolchain("clang_gnu", "Clang GNU toolchain", arg_toolchain_clang_gnu) + .AddTarget("clang_gnu", "Clang GNU target", arg_target_clang_gnu) + .Parse(argc, argv); + + // Initialize your environment + Reg::Init(); + + // Pre-build steps + Reg::Call(Args::Clean()).Func(clean_cb); + + // Build steps Toolchain_gcc gcc; - Toolchain_msvc msvc; - ExecutableTarget_gcc g_foolib("foolib", gcc, ""); - ExecutableTarget_msvc m_foolib("foolib", msvc, ""); + Reg::Toolchain(arg_gcc.state).Build(foolib_build_cb, g_foolib); - reg.Build(arg_gcc.state, foolib_build_cb, g_foolib); - reg.Build(arg_msvc.state, foolib_build_cb, m_foolib); + Toolchain_msvc msvc; + ExecutableTarget_msvc m_foolib("foolib", msvc, ""); + Reg::Toolchain(arg_msvc.state).Build(foolib_build_cb, m_foolib); // * NOTE, This is how we add our custom toolchain - BaseToolchain clang = toolchain_clang_gnu.ConstructToolchain(); - - // * M2, Get from Args (see build_linux.toml or build_win.toml files) - TargetConfig config; - config.compile_command = target_clang_gnu.compile_command; - config.link_command = target_clang_gnu.link_command; + auto &clang = arg_toolchain_clang_gnu.ConstructToolchain(); Target_custom c_foolib("CFoolib.exe", TargetType::Executable, clang, "", - config); - reg.Build(toolchain_clang_gnu.state, foolib_build_cb, c_foolib); + arg_target_clang_gnu.GetTargetConfig()); + Reg::Toolchain(arg_toolchain_clang_gnu.state) + .Build(foolib_build_cb, c_foolib); - // 5. - reg.RunBuild(); + // + Reg::Run(); - // 6. + // plugin::ClangCompileCommands({&g_foolib, &m_foolib, &c_foolib}).Generate(); return 0; } static void clean_cb() { - env::log_info(EXE, fmt::format("Cleaning {}", env::get_project_build_dir())); - fs::remove_all(env::get_project_build_dir()); + env::log_info(EXE, fmt::format("Cleaning {}", Project::GetBuildDir())); + fs::remove_all(Project::GetBuildDir()); } static void foolib_build_cb(BaseTarget &target) { diff --git a/example/hybrid/dep_chaining/build.cpp b/example/hybrid/dep_chaining/build.cpp index 4c2e007e..1195f989 100644 --- a/example/hybrid/dep_chaining/build.cpp +++ b/example/hybrid/dep_chaining/build.cpp @@ -9,68 +9,59 @@ constexpr const char *const EXE = "build"; // Function Prototypes static void clean_cb(); static void cpp_target_cb(BaseTarget &cpptarget, - const BaseGenerator &cpp_generator); -static void c_target_cb(BaseTarget &ctarget, const BaseGenerator &c_generator); -static void cpp_generator_cb(BaseGenerator &generator); -static void c_generator_cb(BaseGenerator &generator); + const FileGenerator &cpp_generator); +static void c_target_cb(BaseTarget &ctarget, const FileGenerator &c_generator); +static void cpp_generator_cb(FileGenerator &generator); +static void c_generator_cb(FileGenerator &generator); int main(int argc, char **argv) { - // 1. Get arguments - Args args; + // Get arguments ArgToolchain arg_gcc; ArgToolchain arg_msvc; - args.AddToolchain("gcc", "Generic gcc toolchain", arg_gcc); - args.AddToolchain("msvc", "Generic msvc toolchain", arg_msvc); - args.Parse(argc, argv); + Args::Init() + .AddToolchain("gcc", "Generic gcc toolchain", arg_gcc) + .AddToolchain("msvc", "Generic msvc toolchain", arg_msvc) + .Parse(argc, argv); - // 2. Initialize your environment - Register reg(args); + // Initialize your environment + Reg::Init(); - // 3. Pre-build steps - reg.Clean(clean_cb); + // Pre-build steps + Reg::Call(Args::Clean()).Func(clean_cb); - // 4. Build steps + // Generator + FileGenerator cpp_generator("cpp_generator", ""); + FileGenerator c_generator("c_generator", ""); + Reg::Call() + .Build(cpp_generator_cb, cpp_generator) + .Build(c_generator_cb, c_generator); + + // Build steps // Explicit toolchain - target pairs Toolchain_gcc gcc; - Toolchain_msvc msvc; - - // CPP - BaseGenerator cpp_generator("cpp_generator", ""); - reg.Build(cpp_generator_cb, cpp_generator); - ExecutableTarget_gcc g_cpptarget("cpptarget", gcc, ""); - reg.Build(arg_gcc.state, cpp_target_cb, g_cpptarget, cpp_generator); - - ExecutableTarget_msvc m_cpptarget("cpptarget", msvc, ""); - reg.Build(arg_msvc.state, cpp_target_cb, m_cpptarget, cpp_generator); - - reg.Dep(g_cpptarget, cpp_generator); - reg.Dep(m_cpptarget, cpp_generator); - - // C - BaseGenerator c_generator("c_generator", ""); - reg.Build(c_generator_cb, c_generator); - ExecutableTarget_gcc g_ctarget("ctarget", gcc, ""); - reg.Build(arg_gcc.state, c_target_cb, g_ctarget, c_generator); + Reg::Toolchain(arg_gcc.state) + .Build(cpp_target_cb, g_cpptarget, cpp_generator) + .Build(c_target_cb, g_ctarget, c_generator) + .Dep(g_cpptarget, cpp_generator) + .Dep(g_ctarget, c_generator) + .Test("{executable}", g_cpptarget) + .Test("{executable}", g_ctarget); + Toolchain_msvc msvc; + ExecutableTarget_msvc m_cpptarget("cpptarget", msvc, ""); ExecutableTarget_msvc m_ctarget("ctarget", msvc, ""); - reg.Build(arg_msvc.state, c_target_cb, m_ctarget, c_generator); - - reg.Dep(g_ctarget, c_generator); - reg.Dep(m_ctarget, c_generator); - - // Tests - reg.Test(arg_gcc.state, "{executable}", g_cpptarget); - reg.Test(arg_gcc.state, "{executable}", g_ctarget); - reg.Test(arg_msvc.state, "{executable}", m_cpptarget); - reg.Test(arg_msvc.state, "{executable}", m_ctarget); - - // 6. Build Target - reg.RunBuild(); + Reg::Toolchain(arg_msvc.state) + .Build(cpp_target_cb, m_cpptarget, cpp_generator) + .Build(c_target_cb, m_ctarget, c_generator) + .Dep(m_cpptarget, cpp_generator) + .Dep(m_ctarget, c_generator) + .Test("{executable}", m_cpptarget) + .Test("{executable}", m_ctarget); - // 7. Test Target - reg.RunTest(); + // Build and Test + Reg::Run(); // - Clang Compile Commands plugin::ClangCompileCommands( @@ -78,7 +69,7 @@ int main(int argc, char **argv) { .Generate(); // - Plugin Graph - std::string output = reg.GetTaskflow().dump(); + std::string output = Reg::GetTaskflow().dump(); const bool saved = env::save_file("graph.dot", output, false); env::assert_fatal(saved, "Could not save graph.dot file"); @@ -86,36 +77,39 @@ int main(int argc, char **argv) { } static void clean_cb() { - env::log_info(EXE, fmt::format("Cleaning {}", env::get_project_build_dir())); - fs::remove_all(env::get_project_build_dir()); + env::log_info(EXE, fmt::format("Cleaning {}", Project::GetBuildDir())); + fs::remove_all(Project::GetBuildDir()); } static void cpp_target_cb(BaseTarget &cpptarget, - const BaseGenerator &cpp_generator) { - const fs::path main_cpp = - fs::path(cpp_generator.GetValueByIdentifier("main_cpp")) - .lexically_relative(env::get_project_root_dir()); + const FileGenerator &cpp_generator) { + const fs::path main_cpp = fs::path(cpp_generator.Get("main_cpp")) + .lexically_relative(Project::GetRootDir()); cpptarget.AddSource(main_cpp); cpptarget.Build(); } -static void c_target_cb(BaseTarget &ctarget, const BaseGenerator &c_generator) { - const fs::path main_c = fs::path(c_generator.GetValueByIdentifier("main_c")) - .lexically_relative(env::get_project_root_dir()); +static void c_target_cb(BaseTarget &ctarget, const FileGenerator &c_generator) { + const fs::path main_c = fs::path(c_generator.Get("main_c")) + .lexically_relative(Project::GetRootDir()); ctarget.AddSource(main_c); ctarget.Build(); } -static void cpp_generator_cb(BaseGenerator &generator) { - generator.AddOutput("{gen_build_dir}/main.cpp", "main_cpp"); - generator.AddCommand("python3 {gen_root_dir}/python/gen.py --source_type cpp " - "--destination {main_cpp}"); +static void cpp_generator_cb(FileGenerator &generator) { + generator.AddPattern("main_cpp", "{current_build_dir}/main.cpp"); + generator.AddOutput("{main_cpp}"); + generator.AddCommand( + "python3 {current_root_dir}/python/gen.py --source_type cpp " + "--destination {main_cpp}"); generator.Build(); } -static void c_generator_cb(BaseGenerator &generator) { - generator.AddOutput("{gen_build_dir}/main.c", "main_c"); - generator.AddCommand("python3 {gen_root_dir}/python/gen.py --source_type c " - "--destination {main_c}"); +static void c_generator_cb(FileGenerator &generator) { + generator.AddPattern("main_c", "{current_build_dir}/main.c"); + generator.AddOutput("{main_c}"); + generator.AddCommand( + "python3 {current_root_dir}/python/gen.py --source_type c " + "--destination {main_c}"); generator.Build(); } diff --git a/example/hybrid/external_lib/build.main.cpp b/example/hybrid/external_lib/build.main.cpp index f2e1cfa7..c2361b9e 100644 --- a/example/hybrid/external_lib/build.main.cpp +++ b/example/hybrid/external_lib/build.main.cpp @@ -5,51 +5,60 @@ using namespace buildcc; static void clean_cb(); -static void foolib_build_cb(BaseTarget &target); +static void foolib_build_cb(BaseTarget &target, const TargetInfo &foolib); constexpr const char *const EXE = "build"; int main(int argc, char **argv) { - // 1. Get arguments - Args args; + // Get arguments ArgToolchain arg_gcc; ArgToolchain arg_msvc; - args.AddToolchain("gcc", "Generic gcc toolchain", arg_gcc); - args.AddToolchain("msvc", "Generic msvc toolchain", arg_msvc); - args.Parse(argc, argv); + Args::Init() + .AddToolchain("gcc", "Generic gcc toolchain", arg_gcc) + .AddToolchain("msvc", "Generic msvc toolchain", arg_msvc) + .Parse(argc, argv); - // 2. Initialize your environment - Register reg(args); + // Initialize your environment + Reg::Init(); - // 3. Pre-build steps - reg.Clean(clean_cb); + // Pre-build steps + Reg::Call(Args::Clean()).Func(clean_cb); - // 4. Build steps + // Build steps Toolchain_gcc gcc; - Toolchain_msvc msvc; - - ExecutableTarget_gcc g_foolib("cppflags", gcc, ""); - ExecutableTarget_msvc m_foolib("cppflags", msvc, ""); + TargetInfo g_foo(gcc, "../foolib"); + ExecutableTarget_gcc g_external("cppflags", gcc, ""); + Reg::Toolchain(arg_gcc.state) + .Func(fooTarget, g_foo) + .Build(foolib_build_cb, g_external, g_foo); - reg.Build(arg_gcc.state, foolib_build_cb, g_foolib); - reg.Build(arg_msvc.state, foolib_build_cb, m_foolib); + Toolchain_msvc msvc; + ExecutableTarget_msvc m_external("cppflags", msvc, ""); + TargetInfo m_foo(gcc, "../foolib"); + Reg::Toolchain(arg_msvc.state) + .Func(fooTarget, m_foo) + .Build(foolib_build_cb, m_external, m_foo); - // 5. - reg.RunBuild(); + // + Reg::Run(); - // 6. - plugin::ClangCompileCommands({&g_foolib, &m_foolib}).Generate(); + // + plugin::ClangCompileCommands({&g_external, &m_external}).Generate(); return 0; } static void clean_cb() { - env::log_info(EXE, fmt::format("Cleaning {}", env::get_project_build_dir())); - fs::remove_all(env::get_project_build_dir()); + env::log_info(EXE, fmt::format("Cleaning {}", Project::GetBuildDir())); + fs::remove_all(Project::GetBuildDir()); } -static void foolib_build_cb(BaseTarget &target) { - fooTarget(target, "../foolib"); +static void foolib_build_cb(BaseTarget &target, const TargetInfo &foolib) { target.AddSource("main.cpp"); + target.Insert(foolib, { + SyncOption::SourceFiles, + SyncOption::HeaderFiles, + SyncOption::IncludeDirs, + }); target.Build(); } diff --git a/example/hybrid/foolib/build.foo.cpp b/example/hybrid/foolib/build.foo.cpp index 4941c2f2..56142aeb 100644 --- a/example/hybrid/foolib/build.foo.cpp +++ b/example/hybrid/foolib/build.foo.cpp @@ -1,6 +1,6 @@ #include "build.foo.h" -void fooTarget(buildcc::BaseTarget &target, const fs::path &relative_path) { - target.AddSource(relative_path / "src/foo.cpp"); - target.AddIncludeDir(relative_path / "src", true); +void fooTarget(buildcc::TargetInfo &target) { + target.AddSource("src/foo.cpp"); + target.AddIncludeDir("src", true); } diff --git a/example/hybrid/foolib/build.foo.h b/example/hybrid/foolib/build.foo.h index d53d08b5..4a741df6 100644 --- a/example/hybrid/foolib/build.foo.h +++ b/example/hybrid/foolib/build.foo.h @@ -2,4 +2,4 @@ #include "buildcc.h" -void fooTarget(buildcc::BaseTarget &target, const fs::path &relative_path); +void fooTarget(buildcc::TargetInfo &target); diff --git a/example/hybrid/foolib/build.main.cpp b/example/hybrid/foolib/build.main.cpp index 66e618be..58e104e9 100644 --- a/example/hybrid/foolib/build.main.cpp +++ b/example/hybrid/foolib/build.main.cpp @@ -5,51 +5,60 @@ using namespace buildcc; static void clean_cb(); -static void foolib_build_cb(BaseTarget &target); +static void main_build_cb(BaseTarget &target, const TargetInfo &foolib); constexpr std::string_view EXE = "build"; int main(int argc, char **argv) { - // 1. Get arguments - Args args; + // Get arguments ArgToolchain arg_gcc; ArgToolchain arg_msvc; - args.AddToolchain("gcc", "Generic gcc toolchain", arg_gcc); - args.AddToolchain("msvc", "Generic msvc toolchain", arg_msvc); - args.Parse(argc, argv); + Args::Init() + .AddToolchain("gcc", "Generic gcc toolchain", arg_gcc) + .AddToolchain("msvc", "Generic msvc toolchain", arg_msvc) + .Parse(argc, argv); - // 2. Initialize your environment - Register reg(args); + // Initialize your environment + Reg::Init(); - // 3. Pre-build steps - reg.Clean(clean_cb); + // Pre-build steps + Reg::Call(Args::Clean()).Func(clean_cb); - // 4. Build steps + // Build steps Toolchain_gcc gcc; - Toolchain_msvc msvc; - - ExecutableTarget_gcc g_foolib("foolib", gcc, ""); - ExecutableTarget_msvc m_foolib("foolib", msvc, ""); + TargetInfo g_foo(gcc, ""); + ExecutableTarget_gcc g_main("one_executable", gcc, ""); + Reg::Toolchain(arg_gcc.state) + .Func(fooTarget, g_foo) + .Build(main_build_cb, g_main, g_foo); - reg.Build(arg_gcc.state, foolib_build_cb, g_foolib); - reg.Build(arg_msvc.state, foolib_build_cb, m_foolib); + Toolchain_msvc msvc; + TargetInfo m_foo(msvc, ""); + ExecutableTarget_msvc m_main("one_executable", msvc, ""); + Reg::Toolchain(arg_msvc.state) + .Func(fooTarget, m_foo) + .Build(main_build_cb, m_main, m_foo); - // 5. - reg.RunBuild(); + // + Reg::Run(); - // 6. - plugin::ClangCompileCommands({&g_foolib, &m_foolib}).Generate(); + // + plugin::ClangCompileCommands({&g_main, &m_main}).Generate(); return 0; } static void clean_cb() { - env::log_info(EXE, fmt::format("Cleaning {}", env::get_project_build_dir())); - fs::remove_all(env::get_project_build_dir()); + env::log_info(EXE, fmt::format("Cleaning {}", Project::GetBuildDir())); + fs::remove_all(Project::GetBuildDir()); } -static void foolib_build_cb(BaseTarget &target) { - fooTarget(target, ""); +static void main_build_cb(BaseTarget &target, const TargetInfo &foolib) { target.AddSource("main.cpp"); + target.Insert(foolib, { + SyncOption::SourceFiles, + SyncOption::HeaderFiles, + SyncOption::IncludeDirs, + }); target.Build(); } diff --git a/example/hybrid/generic/CMakeLists.txt b/example/hybrid/generic/CMakeLists.txt index 6d0520a0..8695177f 100644 --- a/example/hybrid/generic/CMakeLists.txt +++ b/example/hybrid/generic/CMakeLists.txt @@ -23,8 +23,6 @@ endif() # Run your build file add_custom_target(run_hybrid_generic_example - COMMAND hybrid_generic_example --help-all - COMMAND hybrid_generic_example toolchain --help-all COMMAND hybrid_generic_example --config ${CMAKE_CURRENT_SOURCE_DIR}/build_generic.toml # COMMAND dot -Tpng graph.dot -o graph.PNG WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/example/hybrid/generic/build.cpp b/example/hybrid/generic/build.cpp index bcf89acf..fd388c36 100644 --- a/example/hybrid/generic/build.cpp +++ b/example/hybrid/generic/build.cpp @@ -15,71 +15,99 @@ constexpr std::string_view EXE = "build"; // Function Prototypes static void clean_cb(); +static void args_lib_type_cb(CLI::App &app, TargetType &lib_type); static void foolib_build_cb(BaseTarget &foolib_target); static void generic_build_cb(BaseTarget &generic_target, BaseTarget &foolib_target); +static void post_build_cb(BaseTarget &generic_target, + BaseTarget &foolib_target); + int main(int argc, char **argv) { - // 1. Get arguments + // Get arguments ArgToolchain custom_toolchain; TargetType default_lib_type{TargetType::StaticLibrary}; - Args args; - - try { - const std::map lib_type_map_{ - {"StaticLib", TargetType::StaticLibrary}, - {"DynamicLib", TargetType::DynamicLibrary}, - }; - - args.Ref() - .add_option("--default_lib_type", default_lib_type, "Default Lib Type") - ->transform(CLI::CheckedTransformer(lib_type_map_, CLI::ignore_case)) - ->group("Custom"); - - // NOTE, You can add more custom toolchains as per your requirement - args.AddToolchain("user", "User defined toolchain", custom_toolchain); - } catch (const std::exception &e) { - std::cout << "EXCEPTION " << e.what() << std::endl; - } + Args::Init() + .AddToolchain("user", "User defined toolchain", custom_toolchain) + .AddCustomCallback( + [&](CLI::App &app) { args_lib_type_cb(app, default_lib_type); }) + .Parse(argc, argv); - args.Parse(argc, argv); + // Initialize your environment + Reg::Init(); - // 2. Initialize your environment - Register reg(args); + // Pre-build steps + Reg::Call(Args::Clean()).Func(clean_cb); - // 3. Pre-build steps - reg.Clean(clean_cb); - - // 4. Build steps + // Build steps // Toolchain + Generic Target - BaseToolchain toolchain = custom_toolchain.ConstructToolchain(); - Target_generic foolib_target("libfoo", default_lib_type, toolchain, ""); - reg.Build(custom_toolchain.state, foolib_build_cb, foolib_target); - - // Target specific settings + auto &toolchain = custom_toolchain.ConstructToolchain(); + Target_generic foolib_target("libfoo", default_lib_type, toolchain, + "../foolib"); Target_generic generic_target("generic", TargetType::Executable, toolchain, "src"); - reg.Build(custom_toolchain.state, generic_build_cb, generic_target, - foolib_target); - reg.Dep(generic_target, foolib_target); + Reg::Toolchain(custom_toolchain.state) + .Func([&]() { toolchain.Verify(); }) + .Build(foolib_build_cb, foolib_target) + .Build(generic_build_cb, generic_target, foolib_target) + .Dep(generic_target, foolib_target) + .Test("{executable}", generic_target); + + // Build Target + Reg::Run([&]() { post_build_cb(generic_target, foolib_target); }); + + // - Clang Compile Commands + plugin::ClangCompileCommands({&foolib_target, &generic_target}).Generate(); + + // - Plugin Graph + std::string output = Reg::GetTaskflow().dump(); + const bool saved = env::save_file("graph.dot", output, false); + env::assert_fatal(saved, "Could not save graph.dot file"); + + return 0; +} + +static void clean_cb() { + env::log_info(EXE, fmt::format("Cleaning {}", Project::GetBuildDir())); + fs::remove_all(Project::GetBuildDir()); +} - // 5. Test steps - reg.Test(custom_toolchain.state, "{executable}", generic_target); +void args_lib_type_cb(CLI::App &app, TargetType &lib_type) { + const std::map lib_type_map_{ + {"StaticLib", TargetType::StaticLibrary}, + {"DynamicLib", TargetType::DynamicLibrary}, + }; - // 6. Build Target - reg.RunBuild(); + app.add_option("--default_lib_type", lib_type, "Default Lib Type") + ->transform(CLI::CheckedTransformer(lib_type_map_, CLI::ignore_case)) + ->group("Custom"); +} + +static void foolib_build_cb(BaseTarget &foolib_target) { + fooTarget(foolib_target); + foolib_target.Build(); +} - // 7. Post Build steps +static void generic_build_cb(BaseTarget &generic_target, + BaseTarget &foolib_target) { + generic_target.AddSource("main.cpp"); + generic_target.AddLibDep(foolib_target); + generic_target.Insert(foolib_target, { + SyncOption::IncludeDirs, + }); + generic_target.Build(); +} + +void post_build_cb(BaseTarget &generic_target, BaseTarget &foolib_target) { // For Static Lib do nothing // For Dynamic Lib we need to handle special cases // - MSVC behaviour // - Copy to executable location - if (default_lib_type == TargetType::DynamicLibrary) { - + if (foolib_target.GetType() == TargetType::DynamicLibrary) { // MSVC special case fs::path copy_from_path; fs::path copy_to_path; - if (toolchain.GetId() == ToolchainId::Msvc) { + if (foolib_target.GetToolchain().GetId() == ToolchainId::Msvc) { copy_from_path = fmt::format("{}.dll", path_as_string(foolib_target.GetTargetPath())); copy_to_path = @@ -94,43 +122,10 @@ int main(int argc, char **argv) { } // Copy case + // TODO, This should be baked into the `Target` API if (generic_target.IsBuilt()) { fs::remove(copy_to_path); fs::copy(copy_from_path, copy_to_path); } } - - // 8. Test Target - reg.RunTest(); - - // - Clang Compile Commands - plugin::ClangCompileCommands({&foolib_target, &generic_target}).Generate(); - - // - Plugin Graph - std::string output = reg.GetTaskflow().dump(); - const bool saved = env::save_file("graph.dot", output, false); - env::assert_fatal(saved, "Could not save graph.dot file"); - - return 0; -} - -static void clean_cb() { - env::log_info(EXE, fmt::format("Cleaning {}", env::get_project_build_dir())); - fs::remove_all(env::get_project_build_dir()); -} - -static void foolib_build_cb(BaseTarget &foolib_target) { - fooTarget(foolib_target, env::get_project_root_dir() / ".." / "foolib"); - foolib_target.Build(); -} - -static void generic_build_cb(BaseTarget &generic_target, - BaseTarget &foolib_target) { - const auto &foolib_include_dirs = foolib_target.GetIncludeDirs(); - std::for_each( - foolib_include_dirs.cbegin(), foolib_include_dirs.cend(), - [&](const fs::path &p) { generic_target.AddIncludeDir(p, true); }); - generic_target.AddLibDep(foolib_target); - generic_target.AddSource("main.cpp"); - generic_target.Build(); } diff --git a/example/hybrid/pch/build.cpp b/example/hybrid/pch/build.cpp index 1c12a502..28f3d410 100644 --- a/example/hybrid/pch/build.cpp +++ b/example/hybrid/pch/build.cpp @@ -12,56 +12,50 @@ static void cppflags_build_cb(BaseTarget &cppflags); static void cflags_build_cb(BaseTarget &cflags); int main(int argc, char **argv) { - // 1. Get arguments - Args args; + // Get arguments ArgToolchain arg_gcc; ArgToolchain arg_msvc; - args.AddToolchain("gcc", "Generic gcc toolchain", arg_gcc); - args.AddToolchain("msvc", "Generic msvc toolchain", arg_msvc); - args.Parse(argc, argv); + Args::Init() + .AddToolchain("gcc", "Generic gcc toolchain", arg_gcc) + .AddToolchain("msvc", "Generic msvc toolchain", arg_msvc) + .Parse(argc, argv); - // 2. Initialize your environment - Register reg(args); + // Initialize your environment + Reg::Init(); - // 3. Pre-build steps - reg.Clean(clean_cb); + // Pre-build steps + Reg::Call(Args::Clean()).Func(clean_cb); - // 4. Build steps + // Build steps // Explicit toolchain - target pairs Toolchain_gcc gcc; - Toolchain_msvc msvc; - ExecutableTarget_gcc g_cppflags("cppflags", gcc, "files"); ExecutableTarget_gcc g_cflags("cflags", gcc, "files"); - ExecutableTarget_msvc m_cppflags("cppflags", msvc, "files"); - ExecutableTarget_msvc m_cflags("cflags", msvc, "files"); - // Select your builds and tests using the .toml files - reg.Build(arg_gcc.state, cppflags_build_cb, g_cppflags); - reg.Build(arg_msvc.state, cppflags_build_cb, m_cppflags); - reg.Build(arg_gcc.state, cflags_build_cb, g_cflags); - reg.Build(arg_msvc.state, cflags_build_cb, m_cflags); + Reg::Toolchain(arg_gcc.state) + .Build(cppflags_build_cb, g_cppflags) + .Build(cflags_build_cb, g_cflags) + .Test("{executable}", g_cppflags) + .Test("{executable}", g_cflags); - // 5. Test steps - // NOTE, For now they are just dummy callbacks - reg.Test(arg_gcc.state, "{executable}", g_cppflags); - reg.Test(arg_msvc.state, "{executable}", m_cppflags); - reg.Test(arg_gcc.state, "{executable}", g_cflags); - reg.Test(arg_msvc.state, "{executable}", m_cflags); - - // 6. Build Target - reg.RunBuild(); + Toolchain_msvc msvc; + ExecutableTarget_msvc m_cppflags("cppflags", msvc, "files"); + ExecutableTarget_msvc m_cflags("cflags", msvc, "files"); + Reg::Toolchain(arg_msvc.state) + .Build(cppflags_build_cb, m_cppflags) + .Build(cflags_build_cb, m_cflags) + .Test("{executable}", m_cppflags) + .Test("{executable}", m_cflags); - // 7. Test Target - reg.RunTest(); + Reg::Run(); - // 8. Post Build steps + // Post Build steps // - Clang Compile Commands plugin::ClangCompileCommands({&g_cflags, &g_cppflags}).Generate(); // - Plugin Graph - std::string output = reg.GetTaskflow().dump(); + std::string output = Reg::GetTaskflow().dump(); const bool saved = env::save_file("graph.dot", output, false); env::assert_fatal(saved, "Could not save graph.dot file"); @@ -69,8 +63,8 @@ int main(int argc, char **argv) { } static void clean_cb() { - env::log_info(EXE, fmt::format("Cleaning {}", env::get_project_build_dir())); - fs::remove_all(env::get_project_build_dir()); + env::log_info(EXE, fmt::format("Cleaning {}", Project::GetBuildDir())); + fs::remove_all(Project::GetBuildDir()); } static void cppflags_build_cb(BaseTarget &cppflags) { diff --git a/example/hybrid/simple/build.cpp b/example/hybrid/simple/build.cpp index 963d3333..7dbacb3b 100644 --- a/example/hybrid/simple/build.cpp +++ b/example/hybrid/simple/build.cpp @@ -12,56 +12,52 @@ static void cppflags_build_cb(BaseTarget &cppflags); static void cflags_build_cb(BaseTarget &cflags); int main(int argc, char **argv) { - // 1. Get arguments - Args args; + // Get arguments ArgToolchain arg_gcc; ArgToolchain arg_msvc; - args.AddToolchain("gcc", "Generic gcc toolchain", arg_gcc); - args.AddToolchain("msvc", "Generic msvc toolchain", arg_msvc); - args.Parse(argc, argv); + Args::Init() + .AddToolchain("gcc", "Generic gcc toolchain", arg_gcc) + .AddToolchain("msvc", "Generic msvc toolchain", arg_msvc) + .Parse(argc, argv); - // 2. Initialize your environment - Register reg(args); + // Initialize your environment + Reg::Init(); - // 3. Pre-build steps - reg.Clean(clean_cb); + // Pre-build steps + Reg::Call(Args::Clean()).Func(clean_cb); - // 4. Build steps + // Build steps // Explicit toolchain - target pairs Toolchain_gcc gcc; - Toolchain_msvc msvc; - ExecutableTarget_gcc g_cppflags("cppflags", gcc, "files"); - ExecutableTarget_msvc m_cppflags("cppflags", msvc, "files"); ExecutableTarget_gcc g_cflags("cflags", gcc, "files"); - ExecutableTarget_msvc m_cflags("cflags", msvc, "files"); - - // Select your builds and tests using the .toml files - reg.Build(arg_gcc.state, cppflags_build_cb, g_cppflags); - reg.Build(arg_msvc.state, cppflags_build_cb, m_cppflags); - reg.Build(arg_gcc.state, cflags_build_cb, g_cflags); - reg.Build(arg_msvc.state, cflags_build_cb, m_cflags); + Reg::Toolchain(arg_gcc.state) + .Func([&]() { gcc.Verify(); }) + .Build(cppflags_build_cb, g_cppflags) + .Build(cflags_build_cb, g_cflags) + .Test("{executable}", g_cppflags) + .Test("{executable}", g_cflags); - // 5. Test steps - reg.Test(arg_gcc.state, "{executable}", g_cppflags); - reg.Test(arg_msvc.state, "{executable}", m_cppflags); - reg.Test(arg_gcc.state, "{executable}", g_cflags); - reg.Test(arg_msvc.state, "{executable}", m_cflags); - - // 6. Build Target - reg.RunBuild(); + Toolchain_msvc msvc; + ExecutableTarget_msvc m_cppflags("cppflags", msvc, "files"); + ExecutableTarget_msvc m_cflags("cflags", msvc, "files"); + Reg::Toolchain(arg_msvc.state) + .Func([&]() { msvc.Verify(); }) + .Build(cppflags_build_cb, m_cppflags) + .Build(cflags_build_cb, m_cflags) + .Test("{executable}", m_cppflags) + .Test("{executable}", m_cflags); - // 7. Test Target - reg.RunTest(); + Reg::Run(); - // 8. Post Build steps + // Post Build steps // - Clang Compile Commands plugin::ClangCompileCommands({&g_cflags, &g_cppflags, &m_cflags, &m_cppflags}) .Generate(); // - Plugin Graph - std::string output = reg.GetTaskflow().dump(); + std::string output = Reg::GetTaskflow().dump(); const bool saved = env::save_file("graph.dot", output, false); env::assert_fatal(saved, "Could not save graph.dot file"); @@ -69,8 +65,8 @@ int main(int argc, char **argv) { } static void clean_cb() { - env::log_info(EXE, fmt::format("Cleaning {}", env::get_project_build_dir())); - fs::remove_all(env::get_project_build_dir()); + env::log_info(EXE, fmt::format("Cleaning {}", Project::GetBuildDir())); + fs::remove_all(Project::GetBuildDir()); } static void cppflags_build_cb(BaseTarget &cppflags) { diff --git a/example/hybrid/single/build.cpp b/example/hybrid/single/build.cpp index 3abace55..b85a774f 100644 --- a/example/hybrid/single/build.cpp +++ b/example/hybrid/single/build.cpp @@ -9,39 +9,33 @@ static void clean_cb(); static void hello_world_build_cb(BaseTarget &target); int main(int argc, char **argv) { - // 1. Get arguments - Args args; + // Get arguments ArgToolchain arg_gcc; - args.AddToolchain("gcc", "Generic gcc toolchain", arg_gcc); - args.Parse(argc, argv); + Args::Init() + .AddToolchain("gcc", "Generic gcc toolchain", arg_gcc) + .Parse(argc, argv); - // 2. Initialize your environment - Register reg(args); + // Initialize your environment + Reg::Init(); - // 3. Pre-build steps - reg.Clean(clean_cb); + // Pre-build steps + Reg::Call(Args::Clean()).Func(clean_cb); - // 4. Build steps + // Build steps // Explicit toolchain - target pairs Toolchain_gcc gcc; - auto verified_toolchains = gcc.Verify(); - env::assert_fatal(!verified_toolchains.empty(), "GCC Toolchain not found"); - ExecutableTarget_gcc hello_world("hello_world", gcc, ""); // Select your builds and tests using the .toml files - reg.Build(arg_gcc.state, hello_world_build_cb, hello_world); - - // 5. Test steps - reg.Test(arg_gcc.state, "{executable}", hello_world); - - // 6. Build Target - reg.RunBuild(); + Reg::Toolchain(arg_gcc.state) + .Func([&]() { gcc.Verify(); }) + .Build(hello_world_build_cb, hello_world) + .Test("{executable}", hello_world); - // 7. Test Target - reg.RunTest(); + // Build and Test Target + Reg::Run(); - // 8. Post Build steps + // Post Build steps // - Clang Compile Commands plugin::ClangCompileCommands({&hello_world}).Generate(); @@ -49,8 +43,8 @@ int main(int argc, char **argv) { } static void clean_cb() { - env::log_info(EXE, fmt::format("Cleaning {}", env::get_project_build_dir())); - fs::remove_all(env::get_project_build_dir()); + env::log_info(EXE, fmt::format("Cleaning {}", Project::GetBuildDir())); + fs::remove_all(Project::GetBuildDir()); } static void hello_world_build_cb(BaseTarget &target) { diff --git a/example/hybrid/target_info/build.cpp b/example/hybrid/target_info/build.cpp index 25aa1a4a..e2684d0f 100644 --- a/example/hybrid/target_info/build.cpp +++ b/example/hybrid/target_info/build.cpp @@ -13,63 +13,55 @@ static void genericadd2_build_cb(BaseTarget &genericadd, const TargetInfo &genericadd_ho); int main(int argc, char **argv) { - // 1. Get arguments - Args args; + // Get arguments ArgToolchain arg_gcc; ArgToolchain arg_msvc; - args.AddToolchain("gcc", "Generic gcc toolchain", arg_gcc); - args.AddToolchain("msvc", "Generic msvc toolchain", arg_msvc); - args.Parse(argc, argv); + Args::Init() + .AddToolchain("gcc", "Generic gcc toolchain", arg_gcc) + .AddToolchain("msvc", "Generic msvc toolchain", arg_msvc) + .Parse(argc, argv); - // 2. Initialize your environment - Register reg(args); + // Initialize your environment + Reg::Init(); - // 3. Pre-build steps - reg.Clean(clean_cb); + // Pre-build steps + Reg::Call(Args::Clean()).Func(clean_cb); - // 4. Build steps + // Build steps // Explicit toolchain - target pairs Toolchain_gcc gcc; - Toolchain_msvc msvc; - - // TargetInfo - TargetInfo genericadd_ho("files"); - reg.Callback(genericadd_ho_cb, genericadd_ho); - + TargetInfo gcc_genericadd_ho(gcc, "files"); ExecutableTarget_gcc g_genericadd1("generic_add_1", gcc, "files"); - ExecutableTarget_msvc m_genericadd1("generic_add_1", msvc, "files"); - ExecutableTarget_gcc g_genericadd2("generic_add_2", gcc, "files"); - ExecutableTarget_msvc m_genericadd2("generic_add_2", msvc, "files"); - - // Select your builds and tests using the .toml files - reg.Build(arg_gcc.state, genericadd1_build_cb, g_genericadd1, genericadd_ho); - reg.Build(arg_msvc.state, genericadd1_build_cb, m_genericadd1, genericadd_ho); - - reg.Build(arg_gcc.state, genericadd2_build_cb, g_genericadd2, genericadd_ho); - reg.Build(arg_msvc.state, genericadd2_build_cb, m_genericadd2, genericadd_ho); - - // 5. Test steps - // NOTE, For now they are just dummy callbacks - reg.Test(arg_gcc.state, "{executable}", g_genericadd1); - reg.Test(arg_msvc.state, "{executable}", m_genericadd1); - - reg.Test(arg_gcc.state, "{executable}", g_genericadd2); - reg.Test(arg_msvc.state, "{executable}", m_genericadd2); - - // 6. Build Target - reg.RunBuild(); - - // 7. Test Target - reg.RunTest(); - - // 8. Post Build steps + Reg::Toolchain(arg_gcc.state) + .Func([&]() { gcc.Verify(); }) + .Func(genericadd_ho_cb, gcc_genericadd_ho) + .Build(genericadd1_build_cb, g_genericadd1, gcc_genericadd_ho) + .Build(genericadd2_build_cb, g_genericadd2, gcc_genericadd_ho) + .Test("{executable}", g_genericadd1) + .Test("{executable}", g_genericadd2); + Toolchain_msvc msvc; + TargetInfo msvc_genericadd_ho(msvc, "files"); + ExecutableTarget_msvc m_genericadd1("generic_add_1", msvc, "files"); + ExecutableTarget_msvc m_genericadd2("generic_add_2", msvc, "files"); + Reg::Toolchain(arg_msvc.state) + .Func([&]() { msvc.Verify(); }) + .Func(genericadd_ho_cb, msvc_genericadd_ho) + .Build(genericadd1_build_cb, m_genericadd1, msvc_genericadd_ho) + .Build(genericadd2_build_cb, m_genericadd2, msvc_genericadd_ho) + .Test("{executable}", m_genericadd1) + .Test("{executable}", m_genericadd2); + + // Run + Reg::Run(); + + // Post Build steps // - Clang Compile Commands plugin::ClangCompileCommands({&g_genericadd1, &m_genericadd1}).Generate(); // - Plugin Graph - std::string output = reg.GetTaskflow().dump(); + std::string output = Reg::GetTaskflow().dump(); const bool saved = env::save_file("graph.dot", output, false); env::assert_fatal(saved, "Could not save graph.dot file"); @@ -77,8 +69,8 @@ int main(int argc, char **argv) { } static void clean_cb() { - env::log_info(EXE, fmt::format("Cleaning {}", env::get_project_build_dir())); - fs::remove_all(env::get_project_build_dir()); + env::log_info(EXE, fmt::format("Cleaning {}", Project::GetBuildDir())); + fs::remove_all(Project::GetBuildDir()); } static void genericadd_ho_cb(TargetInfo &genericadd_ho) { diff --git a/example/mingw/DynamicLib/build.cpp b/example/mingw/DynamicLib/build.cpp index 5a166e31..1de9c662 100644 --- a/example/mingw/DynamicLib/build.cpp +++ b/example/mingw/DynamicLib/build.cpp @@ -13,7 +13,7 @@ int main(void) { // this path // 2. Intermediate build folder i.e all intermediate generated files should go // here - env::init(BUILD_ROOT, BUILD_INTERMEDIATE_DIR); + Project::Init(BUILD_ROOT, BUILD_INTERMEDIATE_DIR); env::set_log_level(env::LogLevel::Trace); Toolchain_mingw mingw; diff --git a/example/mingw/Executable/build.cpp b/example/mingw/Executable/build.cpp index 07ac8279..41550319 100644 --- a/example/mingw/Executable/build.cpp +++ b/example/mingw/Executable/build.cpp @@ -12,7 +12,7 @@ int main(void) { // this path // 2. Intermediate build folder i.e all intermediate generated files should go // here - env::init(BUILD_ROOT, BUILD_INTERMEDIATE_DIR); + Project::Init(BUILD_ROOT, BUILD_INTERMEDIATE_DIR); env::set_log_level(env::LogLevel::Trace); Toolchain_mingw mingw; diff --git a/example/mingw/Pch/build.cpp b/example/mingw/Pch/build.cpp index c09973fc..c4747467 100644 --- a/example/mingw/Pch/build.cpp +++ b/example/mingw/Pch/build.cpp @@ -15,7 +15,7 @@ int main() { // this path // 2. Intermediate build folder i.e all intermediate generated files should go // here - env::init(BUILD_ROOT, BUILD_INTERMEDIATE_DIR); + Project::Init(BUILD_ROOT, BUILD_INTERMEDIATE_DIR); env::set_log_level(env::LogLevel::Trace); Toolchain_mingw mingw; diff --git a/example/mingw/StaticLib/build.cpp b/example/mingw/StaticLib/build.cpp index ce21b845..628d5081 100644 --- a/example/mingw/StaticLib/build.cpp +++ b/example/mingw/StaticLib/build.cpp @@ -12,7 +12,7 @@ int main(void) { // this path // 2. Intermediate build folder i.e all intermediate generated files should go // here - env::init(BUILD_ROOT, BUILD_INTERMEDIATE_DIR); + Project::Init(BUILD_ROOT, BUILD_INTERMEDIATE_DIR); env::set_log_level(env::LogLevel::Trace); Toolchain_mingw mingw; diff --git a/example/msvc/DynamicLib/build.cpp b/example/msvc/DynamicLib/build.cpp index 65d5b9d6..7c20a2f9 100644 --- a/example/msvc/DynamicLib/build.cpp +++ b/example/msvc/DynamicLib/build.cpp @@ -12,7 +12,7 @@ int main(void) { // this path // 2. Intermediate build folder i.e all intermediate generated files should go // here - env::init(BUILD_SCRIPT_SOURCE, BUILD_SCRIPT_FOLDER); + Project::Init(BUILD_SCRIPT_SOURCE, BUILD_SCRIPT_FOLDER); env::set_log_level(env::LogLevel::Debug); Toolchain_msvc msvc; diff --git a/example/msvc/Executable/build.cpp b/example/msvc/Executable/build.cpp index 9d83d8bb..8ea7d4c9 100644 --- a/example/msvc/Executable/build.cpp +++ b/example/msvc/Executable/build.cpp @@ -12,7 +12,7 @@ int main(void) { // this path // 2. Intermediate build folder i.e all intermediate generated files should go // here - env::init(BUILD_SCRIPT_SOURCE, BUILD_SCRIPT_FOLDER); + Project::Init(BUILD_SCRIPT_SOURCE, BUILD_SCRIPT_FOLDER); env::set_log_level(env::LogLevel::Trace); Toolchain_msvc msvc; diff --git a/example/msvc/StaticLib/build.cpp b/example/msvc/StaticLib/build.cpp index 7622c2b7..5410df18 100644 --- a/example/msvc/StaticLib/build.cpp +++ b/example/msvc/StaticLib/build.cpp @@ -12,7 +12,7 @@ int main(void) { // this path // 2. Intermediate build folder i.e all intermediate generated files should go // here - env::init(BUILD_SCRIPT_SOURCE, BUILD_SCRIPT_FOLDER); + Project::Init(BUILD_SCRIPT_SOURCE, BUILD_SCRIPT_FOLDER); env::set_log_level(env::LogLevel::Trace); Toolchain_msvc msvc; diff --git a/third_party/flatbuffers b/third_party/flatbuffers deleted file mode 160000 index a9a295fe..00000000 --- a/third_party/flatbuffers +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a9a295fecf3fbd5a4f571f53b01f63202a3e2113 diff --git a/third_party/json b/third_party/json new file mode 160000 index 00000000..bc889afb --- /dev/null +++ b/third_party/json @@ -0,0 +1 @@ +Subproject commit bc889afb4c5bf1c0d8ee29ef35eaaf4c8bef8a5d diff --git a/third_party/tl_optional b/third_party/tl_optional new file mode 160000 index 00000000..c28fcf74 --- /dev/null +++ b/third_party/tl_optional @@ -0,0 +1 @@ +Subproject commit c28fcf74d207fc667c4ed3dbae4c251ea551c8c1