Merge pull request #65 from gmh5225/feature-msvc-static

[feature] msvc-static
main
Duncan Ogilvie 2 years ago committed by GitHub
commit e6ded077cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,20 +1,20 @@
option(CMKR_GENERATE_DOCUMENTATION "Generate cmkr documentation" ${CMKR_ROOT_PROJECT}) 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) function(generate_documentation)
if(CMKR_GENERATE_DOCUMENTATION) if(CMKR_GENERATE_DOCUMENTATION)
message(STATUS "[cmkr] Generating 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 # Delete previously generated examples
set(example_folder "${PROJECT_SOURCE_DIR}/docs/examples") set(example_folder "${PROJECT_SOURCE_DIR}/docs/examples")

@ -27,6 +27,7 @@ name = "myproject"
version = "1.0.0" version = "1.0.0"
description = "Description of the project" description = "Description of the project"
languages = ["C", "CXX"] languages = ["C", "CXX"]
msvc-runtime = "" # dynamic (implicit default), static
cmake-before = """ cmake-before = """
message(STATUS "CMake injected before the project() call") message(STATUS "CMake injected before the project() call")
""" """
@ -58,6 +59,8 @@ sources = ["src/main.cpp"]
windows.sources = ["src/windows_specific.cpp"] windows.sources = ["src/windows_specific.cpp"]
``` ```
### Predefined conditions
The following conditions are predefined (you can override them if you desire): The following conditions are predefined (you can override them if you desire):
```toml ```toml
@ -144,6 +147,7 @@ alias = "mytarget::mytarget"
type = "static" # executable, shared (DLL), static, interface, object, library, custom type = "static" # executable, shared (DLL), static, interface, object, library, custom
headers = ["src/mytarget.h"] headers = ["src/mytarget.h"]
sources = ["src/mytarget.cpp"] sources = ["src/mytarget.cpp"]
msvc-runtime = "" # dynamic (implicit default), static
# The keys below match the target_xxx CMake commands # The keys below match the target_xxx CMake commands
# Keys prefixed with private- will get PRIVATE visibility # Keys prefixed with private- will get PRIVATE visibility

@ -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"
```
<sup><sub>This page was automatically generated from [tests/msvc-runtime/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/msvc-runtime/cmake.toml).</sub></sup>

@ -151,7 +151,17 @@ struct Content {
ConditionVector include_after; ConditionVector include_after;
}; };
enum MsvcRuntimeType {
msvc_dynamic,
msvc_static,
msvc_last,
};
extern const char *msvcRuntimeTypeNames[msvc_last];
struct Project { struct Project {
const Project *parent;
// This is the CMake version required to use all cmkr versions. // This is the CMake version required to use all cmkr versions.
std::string cmake_version = "3.15"; std::string cmake_version = "3.15";
std::string cmkr_include = "cmkr.cmake"; std::string cmkr_include = "cmkr.cmake";
@ -169,6 +179,7 @@ struct Project {
std::string project_version; std::string project_version;
std::string project_description; std::string project_description;
std::vector<std::string> project_languages; std::vector<std::string> project_languages;
MsvcRuntimeType project_msvc_runtime = msvc_last;
Condition<std::string> cmake_before; Condition<std::string> cmake_before;
Condition<std::string> cmake_after; Condition<std::string> cmake_after;
ConditionVector include_before; ConditionVector include_before;
@ -186,6 +197,7 @@ struct Project {
std::vector<Subdir> subdirs; std::vector<Subdir> subdirs;
Project(const Project *parent, const std::string &path, bool build); Project(const Project *parent, const std::string &path, bool build);
const Project *root() const;
}; };
bool is_root_path(const std::string &path); bool is_root_path(const std::string &path);

@ -524,6 +524,12 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
if (root_project) { if (root_project) {
cmd("cmake_minimum_required")("VERSION", project.cmake_version).endl(); cmd("cmake_minimum_required")("VERSION", project.cmake_version).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) { if (!project.allow_in_tree) {
// clang-format off // clang-format off
cmd("if")("CMAKE_SOURCE_DIR", "STREQUAL", "CMAKE_BINARY_DIR"); cmd("if")("CMAKE_SOURCE_DIR", "STREQUAL", "CMAKE_BINARY_DIR");
@ -806,6 +812,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
} }
if (!project.targets.empty()) { if (!project.targets.empty()) {
auto root_project = project.root();
for (size_t i = 0; i < project.targets.size(); i++) { for (size_t i = 0; i < project.targets.size(); i++) {
if (i > 0) { if (i > 0) {
endl(); endl();
@ -1014,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<std::string, std::string> &properties) { gen.handle_condition(props, [&](const std::string &, const tsl::ordered_map<std::string, std::string> &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); cmd("set_target_properties")(target.name, "PROPERTIES", properties);
}); });
} }

@ -21,6 +21,17 @@ static TargetType parse_targetType(const std::string &name) {
return target_last; 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<MsvcRuntimeType>(i);
}
}
return msvc_last;
}
using TomlBasicValue = toml::basic_value<toml::discard_comments, tsl::ordered_map, std::vector>; using TomlBasicValue = toml::basic_value<toml::discard_comments, tsl::ordered_map, std::vector>;
static std::string format_key_error(const std::string &error, const toml::key &ky, const TomlBasicValue &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"; const auto toml_path = fs::path(path) / "cmake.toml";
if (!fs::exists(toml_path)) { if (!fs::exists(toml_path)) {
throw std::runtime_error("File not found '" + toml_path.string() + "'"); 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-before", include_before);
project.optional("include-after", include_after); project.optional("include-after", include_after);
project.optional("subdirs", project_subdirs); 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")) { if (checker.contains("subdir")) {
@ -523,6 +549,34 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
t.optional("precompile-headers", target.precompile_headers); t.optional("precompile-headers", target.precompile_headers);
t.optional("private-precompile-headers", target.private_precompile_headers); t.optional("private-precompile-headers", target.private_precompile_headers);
Condition<std::string> 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$<$<CONFIG:Debug>:Debug>DLL";
break;
case msvc_static:
target.properties[condItr.first]["MSVC_RUNTIME_LIBRARY"] = "MultiThreaded$<$<CONFIG:Debug>: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("condition", target.condition);
t.optional("alias", target.alias); t.optional("alias", target.alias);
@ -664,6 +718,13 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
checker.check(conditions, true); 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) { bool is_root_path(const std::string &path) {
const auto toml_path = fs::path(path) / "cmake.toml"; const auto toml_path = fs::path(path) / "cmake.toml";
if (!fs::exists(toml_path)) { if (!fs::exists(toml_path)) {

12
tests/CMakeLists.txt generated

@ -88,3 +88,15 @@ add_test(
build build
) )
if(MSVC) # msvc
add_test(
NAME
msvc-runtime
WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/msvc-runtime"
COMMAND
"$<TARGET_FILE:cmkr>"
build
)
endif()

@ -45,3 +45,10 @@ name = "templates"
working-directory = "templates" working-directory = "templates"
command = "$<TARGET_FILE:cmkr>" command = "$<TARGET_FILE:cmkr>"
arguments = ["build"] arguments = ["build"]
[[test]]
condition = "msvc"
name = "msvc-runtime"
working-directory = "msvc-runtime"
command = "$<TARGET_FILE:cmkr>"
arguments = ["build"]

@ -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"

@ -0,0 +1,5 @@
#include <cstdio>
int main() {
puts("Hello from cmkr(msvc-static)!");
}
Loading…
Cancel
Save