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