From 981c48dbc57c28f759d7e394b8e00578e87a6ea4 Mon Sep 17 00:00:00 2001 From: gmh5225 <2315157@qq.com> Date: Sun, 2 Oct 2022 10:43:32 +0800 Subject: [PATCH 1/6] [feature] msvc-static --- docs/examples/msvc-static.md | 27 +++++++++++++++++++++++++++ include/project_parser.hpp | 2 ++ src/cmake_generator.cpp | 4 ++++ src/project_parser.cpp | 2 ++ tests/CMakeLists.txt | 10 ++++++++++ tests/msvc-static/cmake.toml | 11 +++++++++++ tests/msvc-static/src/msvc-static.cpp | 5 +++++ 7 files changed, 61 insertions(+) create mode 100644 docs/examples/msvc-static.md create mode 100644 tests/msvc-static/cmake.toml create mode 100644 tests/msvc-static/src/msvc-static.cpp diff --git a/docs/examples/msvc-static.md b/docs/examples/msvc-static.md new file mode 100644 index 0000000..9436703 --- /dev/null +++ b/docs/examples/msvc-static.md @@ -0,0 +1,27 @@ +--- +# Automatically generated from tests/msvc-static/cmake.toml - DO NOT EDIT +layout: default +title: msvc static +permalink: /examples/msvc-static +parent: Examples +nav_order: 8 +--- + +# msvc static + +A msvc-static `cmake.toml` project: + +```toml +[project] +name = "msvc-static" +description = "msvc static" + +[target.basic] +type = "executable" +sources = ["src/msvc-static.cpp"] +msvc-static = true +``` + + + +This page was automatically generated from [tests/msvc-static/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/msvc-static/cmake.toml). diff --git a/include/project_parser.hpp b/include/project_parser.hpp index a4ce581..53cd214 100644 --- a/include/project_parser.hpp +++ b/include/project_parser.hpp @@ -103,6 +103,8 @@ struct Target { Condition cmake_after; ConditionVector include_before; ConditionVector include_after; + + bool allow_msvc_static = false; }; struct Template { diff --git a/src/cmake_generator.cpp b/src/cmake_generator.cpp index 76bd830..94a8557 100644 --- a/src/cmake_generator.cpp +++ b/src/cmake_generator.cpp @@ -1026,6 +1026,10 @@ void generate_cmake(const char *path, const parser::Project *parent_project) { gen.conditional_cmake(tmplate->outline.cmake_after); } + if (target.allow_msvc_static && !target.name.empty()) { + cmd("set_property")("TARGET", target.name, "PROPERTY", "MSVC_RUNTIME_LIBRARY", "MultiThreaded$<$:Debug>"); + } + cmd("unset")("CMKR_TARGET"); cmd("unset")("CMKR_SOURCES"); } diff --git a/src/project_parser.cpp b/src/project_parser.cpp index a117bf6..1f50f7c 100644 --- a/src/project_parser.cpp +++ b/src/project_parser.cpp @@ -514,6 +514,8 @@ Project::Project(const Project *parent, const std::string &path, bool build) { t.optional("precompile-headers", target.precompile_headers); t.optional("private-precompile-headers", target.private_precompile_headers); + t.optional("msvc-static", target.allow_msvc_static); + if (!target.headers.empty()) { auto &sources = target.sources.nth(0).value(); const auto &headers = target.headers.nth(0)->second; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index bc48fde..3693efe 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -88,3 +88,13 @@ add_test( build ) +add_test( + NAME + msvc-static + WORKING_DIRECTORY + "${CMAKE_CURRENT_LIST_DIR}/msvc-static" + COMMAND + "$" + build +) + diff --git a/tests/msvc-static/cmake.toml b/tests/msvc-static/cmake.toml new file mode 100644 index 0000000..25e7af5 --- /dev/null +++ b/tests/msvc-static/cmake.toml @@ -0,0 +1,11 @@ +# A msvc-static `cmake.toml` project: + +[project] +name = "msvc-static" +description = "msvc static" + +[target.basic] +type = "executable" +sources = ["src/msvc-static.cpp"] +msvc-static = true + diff --git a/tests/msvc-static/src/msvc-static.cpp b/tests/msvc-static/src/msvc-static.cpp new file mode 100644 index 0000000..684ef1d --- /dev/null +++ b/tests/msvc-static/src/msvc-static.cpp @@ -0,0 +1,5 @@ +#include + +int main() { + puts("Hello from cmkr(msvc-static)!"); +} From 28f541cd3c55a4b152b7ed15922be13c79714572 Mon Sep 17 00:00:00 2001 From: gmh5225 <2315157@qq.com> Date: Fri, 7 Oct 2022 06:41:11 +0800 Subject: [PATCH 2/6] [feature] add CMP0091 --- src/cmake_generator.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cmake_generator.cpp b/src/cmake_generator.cpp index 94a8557..6d97dec 100644 --- a/src/cmake_generator.cpp +++ b/src/cmake_generator.cpp @@ -523,7 +523,11 @@ void generate_cmake(const char *path, const parser::Project *parent_project) { if (root_project) { cmd("cmake_minimum_required")("VERSION", project.cmake_version).endl(); - + // clang-format off + cmd("if")("POLICY", "CMP0091"); + cmd("cmake_policy")("SET", "CMP0091", "NEW"); + cmd("endif")().endl(); + // clang-format on if (!project.allow_in_tree) { // clang-format off cmd("if")("CMAKE_SOURCE_DIR", "STREQUAL", "CMAKE_BINARY_DIR"); From af9a117f50ea31dcc017f3ac11c93bcfe13e762d Mon Sep 17 00:00:00 2001 From: gmh5225 <2315157@qq.com> Date: Fri, 7 Oct 2022 06:50:26 +0800 Subject: [PATCH 3/6] Fix merge --- src/project_parser.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/project_parser.cpp b/src/project_parser.cpp index 8041387..448b1bd 100644 --- a/src/project_parser.cpp +++ b/src/project_parser.cpp @@ -525,12 +525,6 @@ Project::Project(const Project *parent, const std::string &path, bool build) { t.optional("msvc-static", target.allow_msvc_static); - if (!target.headers.empty()) { - auto &sources = target.sources.nth(0).value(); - const auto &headers = target.headers.nth(0)->second; - sources.insert(sources.end(), headers.begin(), headers.end()); - } - t.optional("condition", target.condition); t.optional("alias", target.alias); From 69d844a1529f75cae03e15268500790ba4fbd4ca Mon Sep 17 00:00:00 2001 From: Duncan Ogilvie Date: Fri, 7 Oct 2022 17:46:36 +0200 Subject: [PATCH 4/6] Refactor msvc-static to msvc-runtime --- docs/examples/msvc-runtime.md | 34 ++++++++++ docs/examples/msvc-static.md | 27 -------- include/project_parser.hpp | 14 ++++- src/cmake_generator.cpp | 22 ++++--- src/project_parser.cpp | 63 ++++++++++++++++++- tests/CMakeLists.txt | 20 +++--- tests/cmake.toml | 7 +++ tests/msvc-runtime/cmake.toml | 15 +++++ .../src/main.cpp} | 0 tests/msvc-static/cmake.toml | 11 ---- 10 files changed, 154 insertions(+), 59 deletions(-) create mode 100644 docs/examples/msvc-runtime.md delete mode 100644 docs/examples/msvc-static.md create mode 100644 tests/msvc-runtime/cmake.toml rename tests/{msvc-static/src/msvc-static.cpp => msvc-runtime/src/main.cpp} (100%) delete mode 100644 tests/msvc-static/cmake.toml diff --git a/docs/examples/msvc-runtime.md b/docs/examples/msvc-runtime.md new file mode 100644 index 0000000..abeba94 --- /dev/null +++ b/docs/examples/msvc-runtime.md @@ -0,0 +1,34 @@ +--- +# Automatically generated from tests/msvc-runtime/cmake.toml - DO NOT EDIT +layout: default +title: Static MSVC runtime +permalink: /examples/msvc-runtime +parent: Examples +nav_order: 8 +--- + +# Static MSVC runtime + + + +```toml +[project] +name = "msvc-runtime" +description = "Static MSVC runtime" +msvc-runtime = "static" + +# This target will compile with a static runtime +[target.static-runtime] +type = "executable" +sources = ["src/main.cpp"] + +# This target overrides the [project].msvc-runtime +[target.dynamic-runtime] +type = "executable" +sources = ["src/main.cpp"] +msvc-runtime = "dynamic" +``` + + + +This page was automatically generated from [tests/msvc-runtime/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/msvc-runtime/cmake.toml). diff --git a/docs/examples/msvc-static.md b/docs/examples/msvc-static.md deleted file mode 100644 index 9436703..0000000 --- a/docs/examples/msvc-static.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -# Automatically generated from tests/msvc-static/cmake.toml - DO NOT EDIT -layout: default -title: msvc static -permalink: /examples/msvc-static -parent: Examples -nav_order: 8 ---- - -# msvc static - -A msvc-static `cmake.toml` project: - -```toml -[project] -name = "msvc-static" -description = "msvc static" - -[target.basic] -type = "executable" -sources = ["src/msvc-static.cpp"] -msvc-static = true -``` - - - -This page was automatically generated from [tests/msvc-static/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/msvc-static/cmake.toml). diff --git a/include/project_parser.hpp b/include/project_parser.hpp index be6b3c7..cbb1cd8 100644 --- a/include/project_parser.hpp +++ b/include/project_parser.hpp @@ -102,8 +102,6 @@ struct Target { Condition cmake_after; ConditionVector include_before; ConditionVector include_after; - - bool allow_msvc_static = false; }; struct Template { @@ -153,7 +151,17 @@ struct Content { ConditionVector include_after; }; +enum MsvcRuntimeType { + msvc_dynamic, + msvc_static, + msvc_last, +}; + +extern const char *msvcRuntimeTypeNames[msvc_last]; + struct Project { + const Project *parent; + // This is the CMake version required to use all cmkr versions. std::string cmake_version = "3.15"; std::string cmkr_include = "cmkr.cmake"; @@ -171,6 +179,7 @@ struct Project { std::string project_version; std::string project_description; std::vector project_languages; + MsvcRuntimeType project_msvc_runtime = msvc_last; Condition cmake_before; Condition cmake_after; ConditionVector include_before; @@ -188,6 +197,7 @@ struct Project { std::vector subdirs; Project(const Project *parent, const std::string &path, bool build); + const Project *root() const; }; bool is_root_path(const std::string &path); diff --git a/src/cmake_generator.cpp b/src/cmake_generator.cpp index 6d97dec..ff1893c 100644 --- a/src/cmake_generator.cpp +++ b/src/cmake_generator.cpp @@ -523,10 +523,12 @@ void generate_cmake(const char *path, const parser::Project *parent_project) { if (root_project) { cmd("cmake_minimum_required")("VERSION", project.cmake_version).endl(); - // clang-format off - cmd("if")("POLICY", "CMP0091"); - cmd("cmake_policy")("SET", "CMP0091", "NEW"); - cmd("endif")().endl(); + + if (project.project_msvc_runtime != parser::msvc_last) { + comment("Enable support for MSVC_RUNTIME_LIBRARY"); + cmd("cmake_policy")("SET", "CMP0091", "NEW").endl(); + } + // clang-format on if (!project.allow_in_tree) { // clang-format off @@ -810,6 +812,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) { } if (!project.targets.empty()) { + auto root_project = project.root(); for (size_t i = 0; i < project.targets.size(); i++) { if (i > 0) { endl(); @@ -1018,6 +1021,13 @@ void generate_cmake(const char *path, const parser::Project *parent_project) { } gen.handle_condition(props, [&](const std::string &, const tsl::ordered_map &properties) { + for (const auto &propItr : properties) { + if (propItr.first == "MSVC_RUNTIME_LIBRARY") { + if (root_project->project_msvc_runtime == parser::msvc_last) { + throw std::runtime_error("You cannot set [target].msvc-runtime without setting the root [project].msvc-runtime"); + } + } + } cmd("set_target_properties")(target.name, "PROPERTIES", properties); }); } @@ -1030,10 +1040,6 @@ void generate_cmake(const char *path, const parser::Project *parent_project) { gen.conditional_cmake(tmplate->outline.cmake_after); } - if (target.allow_msvc_static && !target.name.empty()) { - cmd("set_property")("TARGET", target.name, "PROPERTY", "MSVC_RUNTIME_LIBRARY", "MultiThreaded$<$:Debug>"); - } - cmd("unset")("CMKR_TARGET"); cmd("unset")("CMKR_SOURCES"); } diff --git a/src/project_parser.cpp b/src/project_parser.cpp index 448b1bd..bead08f 100644 --- a/src/project_parser.cpp +++ b/src/project_parser.cpp @@ -21,6 +21,17 @@ static TargetType parse_targetType(const std::string &name) { return target_last; } +const char *msvcRuntimeTypeNames[msvc_last] = {"dynamic", "static"}; + +static MsvcRuntimeType parse_msvcRuntimeType(const std::string &name) { + for (int i = 0; i < msvc_last; i++) { + if (name == msvcRuntimeTypeNames[i]) { + return static_cast(i); + } + } + return msvc_last; +} + using TomlBasicValue = toml::basic_value; static std::string format_key_error(const std::string &error, const toml::key &ky, const TomlBasicValue &value) { @@ -192,7 +203,7 @@ class TomlCheckerRoot { } }; -Project::Project(const Project *parent, const std::string &path, bool build) { +Project::Project(const Project *parent, const std::string &path, bool build) : parent(parent) { const auto toml_path = fs::path(path) / "cmake.toml"; if (!fs::exists(toml_path)) { throw std::runtime_error("File not found '" + toml_path.string() + "'"); @@ -276,6 +287,21 @@ Project::Project(const Project *parent, const std::string &path, bool build) { project.optional("include-before", include_before); project.optional("include-after", include_after); project.optional("subdirs", project_subdirs); + + std::string msvc_runtime; + project.optional("msvc-runtime", msvc_runtime); + if (!msvc_runtime.empty()) { + project_msvc_runtime = parse_msvcRuntimeType(msvc_runtime); + if (project_msvc_runtime == msvc_last) { + std::string error = "Unknown runtime '" + msvc_runtime + "'\n"; + error += "Available types:\n"; + for (std::string type_name : msvcRuntimeTypeNames) { + error += " - " + type_name + "\n"; + } + error.pop_back(); // Remove last newline + throw std::runtime_error(format_key_error(error, msvc_runtime, project.find("msvc-runtime"))); + } + } } if (checker.contains("subdir")) { @@ -523,7 +549,33 @@ Project::Project(const Project *parent, const std::string &path, bool build) { t.optional("precompile-headers", target.precompile_headers); t.optional("private-precompile-headers", target.private_precompile_headers); - t.optional("msvc-static", target.allow_msvc_static); + Condition msvc_runtime; + t.optional("msvc-runtime", msvc_runtime); + for (const auto &condItr : msvc_runtime) { + switch (parse_msvcRuntimeType(condItr.second)) { + case msvc_dynamic: + target.properties[condItr.first]["MSVC_RUNTIME_LIBRARY"] = "MultiThreaded$<$:Debug>DLL"; + break; + case msvc_static: + target.properties[condItr.first]["MSVC_RUNTIME_LIBRARY"] = "MultiThreaded$<$:Debug>"; + break; + default: { + std::string error = "Unknown runtime '" + condItr.second + "'\n"; + error += "Available types:\n"; + for (std::string type_name : msvcRuntimeTypeNames) { + error += " - " + type_name + "\n"; + } + error.pop_back(); // Remove last newline + const TomlBasicValue *report; + if (condItr.first.empty()) { + report = &t.find("msvc-runtime"); + } else { + report = &t.find(condItr.first).as_table().find("msvc-runtime").value(); + } + throw std::runtime_error(format_key_error(error, condItr.second, *report)); + } + } + } t.optional("condition", target.condition); t.optional("alias", target.alias); @@ -666,6 +718,13 @@ Project::Project(const Project *parent, const std::string &path, bool build) { checker.check(conditions, true); } +const Project *Project::root() const { + auto root = this; + while (root->parent != nullptr) + root = root->parent; + return root; +} + bool is_root_path(const std::string &path) { const auto toml_path = fs::path(path) / "cmake.toml"; if (!fs::exists(toml_path)) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3693efe..bc4bbf6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -88,13 +88,15 @@ add_test( build ) -add_test( - NAME - msvc-static - WORKING_DIRECTORY - "${CMAKE_CURRENT_LIST_DIR}/msvc-static" - COMMAND - "$" - build -) +if(MSVC) # msvc + add_test( + NAME + msvc-runtime + WORKING_DIRECTORY + "${CMAKE_CURRENT_LIST_DIR}/msvc-runtime" + COMMAND + "$" + build + ) +endif() diff --git a/tests/cmake.toml b/tests/cmake.toml index 10f46ed..6935a9a 100644 --- a/tests/cmake.toml +++ b/tests/cmake.toml @@ -45,3 +45,10 @@ name = "templates" working-directory = "templates" command = "$" arguments = ["build"] + +[[test]] +condition = "msvc" +name = "msvc-runtime" +working-directory = "msvc-runtime" +command = "$" +arguments = ["build"] \ No newline at end of file diff --git a/tests/msvc-runtime/cmake.toml b/tests/msvc-runtime/cmake.toml new file mode 100644 index 0000000..61c7c08 --- /dev/null +++ b/tests/msvc-runtime/cmake.toml @@ -0,0 +1,15 @@ +[project] +name = "msvc-runtime" +description = "Static MSVC runtime" +msvc-runtime = "static" + +# This target will compile with a static runtime +[target.static-runtime] +type = "executable" +sources = ["src/main.cpp"] + +# This target overrides the [project].msvc-runtime +[target.dynamic-runtime] +type = "executable" +sources = ["src/main.cpp"] +msvc-runtime = "dynamic" \ No newline at end of file diff --git a/tests/msvc-static/src/msvc-static.cpp b/tests/msvc-runtime/src/main.cpp similarity index 100% rename from tests/msvc-static/src/msvc-static.cpp rename to tests/msvc-runtime/src/main.cpp diff --git a/tests/msvc-static/cmake.toml b/tests/msvc-static/cmake.toml deleted file mode 100644 index 25e7af5..0000000 --- a/tests/msvc-static/cmake.toml +++ /dev/null @@ -1,11 +0,0 @@ -# A msvc-static `cmake.toml` project: - -[project] -name = "msvc-static" -description = "msvc static" - -[target.basic] -type = "executable" -sources = ["src/msvc-static.cpp"] -msvc-static = true - From 73622aa5ba960c2369ee70f40979e53bd023bb49 Mon Sep 17 00:00:00 2001 From: Duncan Ogilvie Date: Fri, 7 Oct 2022 22:09:54 +0200 Subject: [PATCH 5/6] Remove the add_test hook to generate tests the same on all platforms --- cmake/generate_documentation.cmake | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/cmake/generate_documentation.cmake b/cmake/generate_documentation.cmake index d2329c9..50895dc 100644 --- a/cmake/generate_documentation.cmake +++ b/cmake/generate_documentation.cmake @@ -1,20 +1,20 @@ option(CMKR_GENERATE_DOCUMENTATION "Generate cmkr documentation" ${CMKR_ROOT_PROJECT}) -set(CMKR_TESTS "" CACHE INTERNAL "List of test directories in the order declared in tests/cmake.toml") - -if(CMKR_GENERATE_DOCUMENTATION) - # Hook the add_test function to capture the tests in the order declared in tests/cmake.toml - function(add_test) - cmake_parse_arguments(TEST "" "WORKING_DIRECTORY" "" ${ARGN}) - get_filename_component(TEST_WORKING_DIRECTORY "${TEST_WORKING_DIRECTORY}" NAME) - list(APPEND CMKR_TESTS "${TEST_WORKING_DIRECTORY}") - set(CMKR_TESTS "${CMKR_TESTS}" CACHE INTERNAL "") - _add_test(${test} ${ARGN}) - endfunction() -endif() function(generate_documentation) if(CMKR_GENERATE_DOCUMENTATION) message(STATUS "[cmkr] Generating documentation...") + + # Extract the order of the tests + set(CMKR_TESTS "") + file(READ "${PROJECT_SOURCE_DIR}/tests/cmake.toml" tests_toml NO_HEX_CONVERSION) + string(REGEX MATCHALL "working-directory = \"([^\"]+)\"" tests_match "${tests_toml}") + foreach(match ${tests_match}) + if(match MATCHES "working-directory = \"([^\"]+)\"") + list(APPEND CMKR_TESTS "${CMAKE_MATCH_1}") + else() + message(FATAL_ERROR "This should not happen (wrong regex?)") + endif() + endforeach() # Delete previously generated examples set(example_folder "${PROJECT_SOURCE_DIR}/docs/examples") From 3615ccab94e973a6135e19f658f4a18de9d3b275 Mon Sep 17 00:00:00 2001 From: Duncan Ogilvie Date: Fri, 7 Oct 2022 22:15:32 +0200 Subject: [PATCH 6/6] Document msvc-runtime feature --- docs/cmake-toml.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/cmake-toml.md b/docs/cmake-toml.md index 82a832d..86a6b4a 100644 --- a/docs/cmake-toml.md +++ b/docs/cmake-toml.md @@ -27,6 +27,7 @@ name = "myproject" version = "1.0.0" description = "Description of the project" languages = ["C", "CXX"] +msvc-runtime = "" # dynamic (implicit default), static cmake-before = """ message(STATUS "CMake injected before the project() call") """ @@ -58,6 +59,8 @@ sources = ["src/main.cpp"] windows.sources = ["src/windows_specific.cpp"] ``` +### Predefined conditions + The following conditions are predefined (you can override them if you desire): ```toml @@ -144,6 +147,7 @@ alias = "mytarget::mytarget" type = "static" # executable, shared (DLL), static, interface, object, library, custom headers = ["src/mytarget.h"] sources = ["src/mytarget.cpp"] +msvc-runtime = "" # dynamic (implicit default), static # The keys below match the target_xxx CMake commands # Keys prefixed with private- will get PRIVATE visibility