mirror of
https://github.com/build-cpp/cmkr
synced 2026-06-08 13:22:55 +00:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a362978f99 | |||
| a2f33297e4 | |||
| 674c976647 | |||
| 714a666e88 | |||
| d613e433d5 | |||
| 7c828d8740 | |||
| 559f750c89 | |||
| 4b6b72874e | |||
| 6bffa7cc71 | |||
| f415b432ed | |||
| 6c46664336 | |||
| f32aac63aa | |||
| 33d4cc4156 | |||
| 9288c8a87d | |||
| e36eee4420 | |||
| 53820c9f65 | |||
| ff7e4b8f23 | |||
| 1072cb44e2 | |||
| 926fc15fa2 | |||
| e2c929adb0 | |||
| 0dec7ca4d5 | |||
| 45e346b4a3 | |||
| 7165986190 | |||
| be0ef6d615 |
@@ -10,7 +10,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [windows-2022, macos-11, ubuntu-20.04]
|
||||
os: [windows-2022, macos-latest, ubuntu-20.04]
|
||||
env:
|
||||
BUILD_TYPE: 'Release'
|
||||
CMAKE_GENERATOR: 'Ninja'
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
name: lint
|
||||
|
||||
on: [push]
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
clang-format:
|
||||
# Skip building pull requests from the same repository
|
||||
if: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@@ -27,6 +29,8 @@ jobs:
|
||||
exit 1
|
||||
|
||||
editorconfig:
|
||||
# Skip building pull requests from the same repository
|
||||
if: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
||||
Generated
+11
-12
@@ -22,7 +22,7 @@ project(cmkr
|
||||
LANGUAGES
|
||||
CXX
|
||||
VERSION
|
||||
0.2.26
|
||||
0.2.31
|
||||
DESCRIPTION
|
||||
"CMakeLists generator from TOML"
|
||||
)
|
||||
@@ -58,12 +58,9 @@ generate_documentation()
|
||||
|
||||
# Target: cmkr
|
||||
set(cmkr_SOURCES
|
||||
"src/arguments.cpp"
|
||||
"src/build.cpp"
|
||||
"src/cmake_generator.cpp"
|
||||
"src/help.cpp"
|
||||
"src/main.cpp"
|
||||
"src/project_parser.cpp"
|
||||
cmake.toml
|
||||
"cmake/cmkr.cmake"
|
||||
"cmake/version.hpp.in"
|
||||
"include/arguments.hpp"
|
||||
"include/build.hpp"
|
||||
"include/cmake_generator.hpp"
|
||||
@@ -71,9 +68,12 @@ set(cmkr_SOURCES
|
||||
"include/help.hpp"
|
||||
"include/literals.hpp"
|
||||
"include/project_parser.hpp"
|
||||
"cmake/cmkr.cmake"
|
||||
"cmake/version.hpp.in"
|
||||
cmake.toml
|
||||
"src/arguments.cpp"
|
||||
"src/build.cpp"
|
||||
"src/cmake_generator.cpp"
|
||||
"src/help.cpp"
|
||||
"src/main.cpp"
|
||||
"src/project_parser.cpp"
|
||||
)
|
||||
|
||||
add_executable(cmkr)
|
||||
@@ -94,7 +94,6 @@ target_link_libraries(cmkr PRIVATE
|
||||
ghc_filesystem
|
||||
mpark_variant
|
||||
ordered_map
|
||||
nlohmann_json
|
||||
)
|
||||
|
||||
get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT)
|
||||
@@ -103,7 +102,7 @@ if(NOT CMKR_VS_STARTUP_PROJECT)
|
||||
endif()
|
||||
|
||||
set(CMKR_TARGET cmkr)
|
||||
generate_resources(${CMKR_TARGET})
|
||||
include("cmake/custom_targets.cmake")
|
||||
|
||||
install(
|
||||
TARGETS
|
||||
|
||||
+2
-5
@@ -4,7 +4,7 @@ cmkr-include = false
|
||||
|
||||
[project]
|
||||
name = "cmkr"
|
||||
version = "0.2.26"
|
||||
version = "0.2.31"
|
||||
description = "CMakeLists generator from TOML"
|
||||
languages = ["CXX"]
|
||||
include-after = [
|
||||
@@ -36,11 +36,8 @@ link-libraries = [
|
||||
"ghc_filesystem",
|
||||
"mpark_variant",
|
||||
"ordered_map",
|
||||
"nlohmann_json",
|
||||
]
|
||||
cmake-after = """
|
||||
generate_resources(${CMKR_TARGET})
|
||||
"""
|
||||
include-after = ["cmake/custom_targets.cmake"]
|
||||
|
||||
[[install]]
|
||||
targets = ["cmkr"]
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ include_guard()
|
||||
|
||||
# Change these defaults to point to your infrastructure if desired
|
||||
set(CMKR_REPO "https://github.com/build-cpp/cmkr" CACHE STRING "cmkr git repository" FORCE)
|
||||
set(CMKR_TAG "v0.2.26" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE)
|
||||
set(CMKR_TAG "v0.2.31" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE)
|
||||
set(CMKR_COMMIT_HASH "" CACHE STRING "cmkr git commit hash (optional)" FORCE)
|
||||
|
||||
# To bootstrap/generate a cmkr project: cmake -P cmkr.cmake
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
generate_resources(cmkr)
|
||||
|
||||
add_custom_target(regenerate-cmake
|
||||
COMMAND "$<TARGET_FILE:cmkr>" gen
|
||||
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
|
||||
)
|
||||
|
||||
if(CMAKE_CONFIGURATION_TYPES)
|
||||
add_custom_target(run-tests
|
||||
COMMAND "${CMAKE_CTEST_COMMAND}" -C $<CONFIG>
|
||||
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/tests"
|
||||
)
|
||||
else()
|
||||
add_custom_target(run-tests
|
||||
COMMAND "${CMAKE_CTEST_COMMAND}"
|
||||
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/tests"
|
||||
)
|
||||
endif()
|
||||
+12
-8
@@ -67,15 +67,17 @@ _Note_: It is generally discouraged to disable the `C` language, unless you are
|
||||
|
||||
## Conditions
|
||||
|
||||
You can specify your own conditions and use them in any `condition` field:
|
||||
You can specify your own named conditions and use them in any `condition` field:
|
||||
|
||||
```toml
|
||||
[conditions]
|
||||
arch64 = "CMAKE_SIZEOF_VOID_P EQUAL 8"
|
||||
arch32 = "CMAKE_SIZEOF_VOID_P EQUAL 4"
|
||||
ptr64 = "CMAKE_SIZEOF_VOID_P EQUAL 8"
|
||||
ptr32 = "CMAKE_SIZEOF_VOID_P EQUAL 4"
|
||||
```
|
||||
|
||||
This will make the `arch64` and `arch32` conditions available with their respective CMake expressions.
|
||||
This will make the `ptr64` and `ptr32` conditions available with their respective CMake expressions.
|
||||
|
||||
**Note**: condition names can only contain lower-case alphanumeric characters (`[0-9a-z]`) and dashes (`-`).
|
||||
|
||||
You can also prefix most keys with `condition.` to represent a conditional:
|
||||
|
||||
@@ -83,9 +85,11 @@ You can also prefix most keys with `condition.` to represent a conditional:
|
||||
[target]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
windows.sources = ["src/windows_specific.cpp"]
|
||||
ptr64.sources = ["src/ptr64_only.cpp"]
|
||||
```
|
||||
|
||||
Instead of a named condition you can also specify a [CMake expression](https://cmake.org/cmake/help/latest/command/if.html#condition-syntax) in quotes. Instances of `$<name>` are replaced with the corresponding condition. For example: `"CONDITIONS_BUILD_TESTS AND $<linux>"` becomes `CONDITIONS_BUILD_TESTS AND (CMAKE_SYSTEM_NAME MATCHES "Linux")` in the final `CMakeLists.txt` file.
|
||||
|
||||
### Predefined conditions
|
||||
|
||||
The following conditions are predefined (you can override them if you desire):
|
||||
@@ -131,7 +135,7 @@ MYPROJECT_SPECIAL_OPTION = { value = true, help = "Docstring for this option." }
|
||||
MYPROJECT_BUILD_EXAMPLES = "root"
|
||||
```
|
||||
|
||||
Options correspond to [CMake cache variables](https://cmake.org/cmake/help/book/mastering-cmake/chapter/CMake%20Cache.html) that can be used to customize your project at configure-time. You can configure with `cmake -DMYPROJECT_BUILD_TESTS=ON` to enable the option. Every option automatically gets a corresponding [condition](#conditions).
|
||||
Options correspond to [CMake cache variables](https://cmake.org/cmake/help/book/mastering-cmake/chapter/CMake%20Cache.html) that can be used to customize your project at configure-time. You can configure with `cmake -DMYPROJECT_BUILD_TESTS=ON` to enable the option. Every option automatically gets a corresponding [condition](#conditions). Additionally, a normalized condition is created based on the `[project].name` (i.e. `MYPROJECT_BUILD_TESTS` becomes `build-tests`).
|
||||
|
||||
The special value `root` can be used to set the option to `true` if the project is compiled as the root project (it will be `false` if someone is including your project via `[fetch-content]` or `[subdir]`).
|
||||
|
||||
@@ -149,8 +153,8 @@ Variables emit a [`set`](https://cmake.org/cmake/help/latest/command/set.html) a
|
||||
|
||||
```toml
|
||||
[vcpkg]
|
||||
version = "2021.05.12"
|
||||
url = "https://github.com/microsoft/vcpkg/archive/refs/tags/2021.05.12.tar.gz"
|
||||
version = "2024.03.25"
|
||||
url = "https://github.com/microsoft/vcpkg/archive/refs/tags/2024.03.25.tar.gz"
|
||||
packages = ["fmt", "zlib"]
|
||||
```
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ description = "Dependencies from vcpkg"
|
||||
# See https://github.com/microsoft/vcpkg/releases for vcpkg versions
|
||||
# See https://vcpkg.io/en/packages.html for available packages
|
||||
[vcpkg]
|
||||
version = "2022.11.14"
|
||||
version = "2024.03.25"
|
||||
packages = ["fmt"]
|
||||
|
||||
[find-package]
|
||||
|
||||
@@ -208,6 +208,7 @@ struct Project {
|
||||
Project(const Project *parent, const std::string &path, bool build);
|
||||
const Project *root() const;
|
||||
bool cmake_minimum_version(int major, int minor) const;
|
||||
static bool is_condition_name(const std::string &name);
|
||||
};
|
||||
|
||||
bool is_root_path(const std::string &path);
|
||||
|
||||
+175
-83
@@ -54,57 +54,85 @@ static std::string format(const char *format, const tsl::ordered_map<std::string
|
||||
return s;
|
||||
}
|
||||
|
||||
static std::vector<std::string> expand_cmake_path(const fs::path &name, const fs::path &toml_dir, bool is_root_project) {
|
||||
std::vector<std::string> temp;
|
||||
|
||||
auto extract_suffix = [](const fs::path &base, const fs::path &full) {
|
||||
auto fullpath = full.string();
|
||||
auto base_len = base.string().length();
|
||||
auto delet = fullpath.substr(base_len + 1, fullpath.length() - base_len);
|
||||
return delet;
|
||||
static std::vector<fs::path> expand_cmake_path(const fs::path &source_path, const fs::path &toml_dir, bool is_root_project) {
|
||||
auto is_subdir = [](fs::path p, const fs::path &root) {
|
||||
while (true) {
|
||||
if (p == root) {
|
||||
return true;
|
||||
}
|
||||
auto parent = p.parent_path();
|
||||
if (parent == p) {
|
||||
break;
|
||||
}
|
||||
p = parent;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
auto stem = name.filename().stem().string();
|
||||
auto ext = name.extension();
|
||||
|
||||
if (is_root_project && stem == "**" && name == name.filename()) {
|
||||
throw std::runtime_error("Recursive globbing not allowed in project root: " + name.string());
|
||||
if (!is_subdir(fs::absolute(toml_dir / source_path), toml_dir)) {
|
||||
throw std::runtime_error("Path traversal is not allowed: " + source_path.string());
|
||||
}
|
||||
|
||||
// Split the path at the first period (since fs::path::stem() and fs::path::extension() split at the last period)
|
||||
std::string stem, extension;
|
||||
auto filename = source_path.filename().string();
|
||||
auto dot_position = filename.find('.');
|
||||
if (dot_position != std::string::npos) {
|
||||
stem = filename.substr(0, dot_position);
|
||||
extension = filename.substr(dot_position);
|
||||
} else {
|
||||
stem = filename;
|
||||
}
|
||||
|
||||
if (is_root_project && stem == "**" && !source_path.has_parent_path()) {
|
||||
throw std::runtime_error("Recursive globbing not allowed in project root: " + source_path.string());
|
||||
}
|
||||
|
||||
auto has_extension = [](const fs::path &file_path, const std::string &extension) {
|
||||
auto path = file_path.string();
|
||||
return path.rfind(extension) == path.length() - extension.length();
|
||||
};
|
||||
|
||||
std::vector<fs::path> paths;
|
||||
if (stem == "*") {
|
||||
for (const auto &f : fs::directory_iterator(toml_dir / name.parent_path(), fs::directory_options::follow_directory_symlink)) {
|
||||
if (!f.is_directory() && f.path().extension() == ext) {
|
||||
temp.push_back(extract_suffix(toml_dir, f));
|
||||
for (const auto &f : fs::directory_iterator(toml_dir / source_path.parent_path(), fs::directory_options::follow_directory_symlink)) {
|
||||
if (!f.is_directory() && has_extension(f.path(), extension)) {
|
||||
paths.push_back(fs::relative(f, toml_dir));
|
||||
}
|
||||
}
|
||||
} else if (stem == "**") {
|
||||
for (const auto &f : fs::recursive_directory_iterator(toml_dir / name.parent_path(), fs::directory_options::follow_directory_symlink)) {
|
||||
if (!f.is_directory() && f.path().extension() == ext) {
|
||||
temp.push_back(extract_suffix(toml_dir, f.path()));
|
||||
for (const auto &f :
|
||||
fs::recursive_directory_iterator(toml_dir / source_path.parent_path(), fs::directory_options::follow_directory_symlink)) {
|
||||
if (!f.is_directory() && has_extension(f.path(), extension)) {
|
||||
paths.push_back(fs::relative(f, toml_dir));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
temp.push_back(name.string());
|
||||
paths.push_back(source_path);
|
||||
}
|
||||
// Normalize all paths to work with CMake (it needs a / on Windows as well)
|
||||
for (auto &path : temp) {
|
||||
std::replace(path.begin(), path.end(), '\\', '/');
|
||||
}
|
||||
// Sort paths alphabetically for consistent cross-OS generation
|
||||
std::sort(temp.begin(), temp.end());
|
||||
return temp;
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
static std::vector<std::string> expand_cmake_paths(const std::vector<std::string> &sources, const fs::path &toml_dir, bool is_root_project) {
|
||||
// TODO: add duplicate checking
|
||||
std::vector<std::string> result;
|
||||
std::vector<std::string> paths;
|
||||
for (const auto &src : sources) {
|
||||
auto expanded = expand_cmake_path(src, toml_dir, is_root_project);
|
||||
for (const auto &f : expanded) {
|
||||
result.push_back(f);
|
||||
paths.push_back(f.string());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
// Normalize all paths to work with CMake (it needs a / on Windows as well)
|
||||
for (auto &path : paths) {
|
||||
std::replace(path.begin(), path.end(), '\\', '/');
|
||||
}
|
||||
|
||||
// Sort paths alphabetically for consistent cross-OS generation
|
||||
std::sort(paths.begin(), paths.end());
|
||||
|
||||
// TODO: remove duplicates
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
static void create_file(const fs::path &path, const std::string &contents) {
|
||||
@@ -514,15 +542,13 @@ struct Generator {
|
||||
if (!value.empty()) {
|
||||
for (const auto &itr : value) {
|
||||
const auto &condition = itr.first;
|
||||
if (!condition.empty()) {
|
||||
cmd("if", condition)(RawArg(project.conditions.at(condition)));
|
||||
}
|
||||
auto endif = if_condition(condition);
|
||||
|
||||
if (!itr.second.empty()) {
|
||||
fn(condition, itr.second);
|
||||
}
|
||||
|
||||
if (!condition.empty()) {
|
||||
if (endif) {
|
||||
cmd("endif")().endl();
|
||||
} else if (!itr.second.empty()) {
|
||||
endl();
|
||||
@@ -538,6 +564,68 @@ struct Generator {
|
||||
void conditional_cmake(const parser::Condition<std::string> &cmake) {
|
||||
handle_condition(cmake, [this](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
|
||||
}
|
||||
|
||||
bool if_condition(const std::string &condition) {
|
||||
if (condition.empty()) {
|
||||
return false;
|
||||
}
|
||||
auto found = project.conditions.find(condition);
|
||||
if (found == project.conditions.end()) {
|
||||
if (cmkr::parser::Project::is_condition_name(condition)) {
|
||||
// NOTE: this should have been caught by the parser already
|
||||
throw std::runtime_error("Condition '" + condition + "' is not defined");
|
||||
}
|
||||
cmd("if", "NOTE: unnamed condition")(RawArg(cmake_condition(condition)));
|
||||
} else {
|
||||
cmd("if", condition)(RawArg(found->second));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string cmake_condition(const std::string &condition) {
|
||||
// HACK: this replaces '$<name>' with the value of the 'name' condition. We can safely
|
||||
// reuse the generator expression syntax, because it is not valid in CMake conditions.
|
||||
// TODO: properly handle quoted arguments (using a simple state machine):
|
||||
// https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#quoted-argument
|
||||
std::string result = "";
|
||||
bool in_replacement = false;
|
||||
std::string temp;
|
||||
for (size_t i = 0; i < condition.length(); i++) {
|
||||
if (in_replacement) {
|
||||
if (condition[i] == '>') {
|
||||
in_replacement = false;
|
||||
if (temp.empty()) {
|
||||
throw std::runtime_error("Empty replacement in condition '" + condition + "'");
|
||||
}
|
||||
auto found = project.conditions.find(temp);
|
||||
if (found == project.conditions.end()) {
|
||||
throw std::runtime_error("Unknown condition '" + temp + "' in replacement");
|
||||
}
|
||||
auto has_space = found->second.find(' ') != std::string::npos;
|
||||
if (has_space) {
|
||||
result += '(';
|
||||
}
|
||||
result += found->second;
|
||||
if (has_space) {
|
||||
result += ')';
|
||||
}
|
||||
temp.clear();
|
||||
} else {
|
||||
temp += condition[i];
|
||||
}
|
||||
} else if (condition[i] == '$' && i + 1 < condition.length() && condition[i + 1] == '<') {
|
||||
i++;
|
||||
in_replacement = true;
|
||||
} else {
|
||||
result += condition[i];
|
||||
}
|
||||
}
|
||||
if (!temp.empty()) {
|
||||
throw std::runtime_error("Unterminated replacement in condition '" + condition + "'");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
struct ConditionScope {
|
||||
@@ -545,10 +633,7 @@ struct ConditionScope {
|
||||
bool endif = false;
|
||||
|
||||
ConditionScope(Generator &gen, const std::string &condition) : gen(gen) {
|
||||
if (!condition.empty()) {
|
||||
gen.cmd("if", condition)(RawArg(gen.project.conditions.at(condition)));
|
||||
endif = true;
|
||||
}
|
||||
endif = gen.if_condition(condition);
|
||||
}
|
||||
|
||||
ConditionScope(const ConditionScope &) = delete;
|
||||
@@ -596,7 +681,10 @@ static std::string vcpkg_escape_identifier(const std::string &name) {
|
||||
ch = '-';
|
||||
}
|
||||
|
||||
escaped += std::tolower(ch);
|
||||
if (ch >= 'A' && ch <= 'Z') {
|
||||
ch += ('a' - 'A');
|
||||
}
|
||||
escaped += ch;
|
||||
}
|
||||
if (!vcpkg_valid_identifier(escaped)) {
|
||||
throw std::runtime_error("The escaped project name '" + escaped + "' is not usable with [vcpkg]");
|
||||
@@ -617,7 +705,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
|
||||
parser::Project project(parent_project, path, false);
|
||||
|
||||
for (auto const &lang : project.project_languages) {
|
||||
for (const auto &lang : project.project_languages) {
|
||||
if (known_languages.find(lang) == known_languages.end()) {
|
||||
if (project.project_allow_unknown_languages) {
|
||||
printf("[warning] Unknown language '%s' specified\n", lang.c_str());
|
||||
@@ -643,23 +731,6 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
if (is_root_project) {
|
||||
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");
|
||||
|
||||
switch (project.project_msvc_runtime) {
|
||||
case parser::msvc_dynamic:
|
||||
cmd("set")("CMAKE_MSVC_RUNTIME_LIBRARY", "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL");
|
||||
break;
|
||||
case parser::msvc_static:
|
||||
cmd("set")("CMAKE_MSVC_RUNTIME_LIBRARY", "MultiThreaded$<$<CONFIG:Debug>:Debug>");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
endl();
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
if (!project.allow_in_tree) {
|
||||
// clang-format off
|
||||
@@ -690,6 +761,26 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
cmd("endif")().endl();
|
||||
// clang-format on
|
||||
|
||||
if (project.project_msvc_runtime != parser::msvc_last) {
|
||||
comment("Enable support for MSVC_RUNTIME_LIBRARY");
|
||||
cmd("cmake_policy")("SET", "CMP0091", "NEW");
|
||||
|
||||
// clang-format off
|
||||
cmd("if")("NOT", "DEFINED", "CMAKE_MSVC_RUNTIME_LIBRARY");
|
||||
switch (project.project_msvc_runtime) {
|
||||
case parser::msvc_dynamic:
|
||||
cmd("set")("CMAKE_MSVC_RUNTIME_LIBRARY", "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL");
|
||||
break;
|
||||
case parser::msvc_static:
|
||||
cmd("set")("CMAKE_MSVC_RUNTIME_LIBRARY", "MultiThreaded$<$<CONFIG:Debug>:Debug>");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
cmd("endif")().endl();
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
fs::path cmkr_include(project.cmkr_include);
|
||||
if (!project.cmkr_include.empty() && !fs::exists(cmkr_include) && cmkr_include.is_relative()) {
|
||||
create_file(cmkr_include, resources::cmkr);
|
||||
@@ -814,19 +905,20 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
cmd("if")("CMKR_ROOT_PROJECT", "AND", "NOT", "CMKR_DISABLE_VCPKG");
|
||||
cmd("include")("FetchContent");
|
||||
comment("Fix warnings about DOWNLOAD_EXTRACT_TIMESTAMP");
|
||||
// clang-format off
|
||||
cmd("if")("POLICY", "CMP0135");
|
||||
cmd("cmake_policy")("SET", "CMP0135", "NEW");
|
||||
cmd("endif")();
|
||||
// clang-format on
|
||||
cmd("message")("STATUS", "Fetching vcpkg (" + version_name + ")...");
|
||||
cmd("FetchContent_Declare")("vcpkg", "URL", url);
|
||||
// Not using FetchContent_MakeAvailable here in case vcpkg adds CMakeLists.txt
|
||||
cmd("FetchContent_GetProperties")("vcpkg");
|
||||
cmd("if")("NOT", "vcpkg_POPULATED");
|
||||
cmd("FetchContent_Populate")("vcpkg");
|
||||
cmd("include")("${vcpkg_SOURCE_DIR}/scripts/buildsystems/vcpkg.cmake");
|
||||
cmd("endif")();
|
||||
cmd("message")("STATUS", "Fetching vcpkg (" + version_name + ")...");
|
||||
cmd("FetchContent_Declare")("vcpkg", "URL", url);
|
||||
// Not using FetchContent_MakeAvailable here in case vcpkg adds CMakeLists.txt
|
||||
cmd("FetchContent_GetProperties")("vcpkg");
|
||||
cmd("if")("NOT", "vcpkg_POPULATED");
|
||||
cmd("FetchContent_Populate")("vcpkg");
|
||||
cmd("if")("CMAKE_HOST_SYSTEM_NAME", "STREQUAL", "Darwin", "AND", "CMAKE_OSX_ARCHITECTURES", "STREQUAL", RawArg("\"\""));
|
||||
cmd("set")("CMAKE_OSX_ARCHITECTURES", "${CMAKE_HOST_SYSTEM_PROCESSOR}", "CACHE", "STRING", RawArg("\"\""), "FORCE");
|
||||
cmd("endif")();
|
||||
cmd("include")("${vcpkg_SOURCE_DIR}/scripts/buildsystems/vcpkg.cmake");
|
||||
cmd("endif")();
|
||||
cmd("endif")();
|
||||
endl();
|
||||
// clang-format on
|
||||
@@ -900,6 +992,20 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
ofs << "}\n";
|
||||
}
|
||||
|
||||
if (!project.packages.empty()) {
|
||||
comment("Packages");
|
||||
for (const auto &dep : project.packages) {
|
||||
auto version = dep.version;
|
||||
if (version == "*")
|
||||
version.clear();
|
||||
auto required = dep.required ? "REQUIRED" : "";
|
||||
auto config = dep.config ? "CONFIG" : "";
|
||||
auto components = std::make_pair("COMPONENTS", dep.components);
|
||||
ConditionScope cs(gen, dep.condition);
|
||||
cmd("find_package")(dep.name, version, required, config, components).endl();
|
||||
}
|
||||
}
|
||||
|
||||
if (!project.contents.empty()) {
|
||||
cmd("include")("FetchContent").endl();
|
||||
if (!project.root()->vcpkg.enabled()) {
|
||||
@@ -935,20 +1041,6 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!project.packages.empty()) {
|
||||
comment("Packages");
|
||||
for (const auto &dep : project.packages) {
|
||||
auto version = dep.version;
|
||||
if (version == "*")
|
||||
version.clear();
|
||||
auto required = dep.required ? "REQUIRED" : "";
|
||||
auto config = dep.config ? "CONFIG" : "";
|
||||
auto components = std::make_pair("COMPONENTS", dep.components);
|
||||
ConditionScope cs(gen, dep.condition);
|
||||
cmd("find_package")(dep.name, version, required, config, components).endl();
|
||||
}
|
||||
}
|
||||
|
||||
auto add_subdir = [&](const std::string &dir) {
|
||||
// clang-format off
|
||||
comment("Subdirectory: " + dir);
|
||||
|
||||
+59
-13
@@ -140,7 +140,7 @@ class TomlChecker {
|
||||
for (const auto &itr : m_v.as_table()) {
|
||||
const auto &ky = itr.first;
|
||||
if (m_conditionVisited.contains(ky)) {
|
||||
if (!conditions.contains(ky)) {
|
||||
if (!conditions.contains(ky) && Project::is_condition_name(ky)) {
|
||||
throw_key_error("Unknown condition '" + ky + "'", ky, itr.second);
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ class TomlChecker {
|
||||
throw_key_error("Unknown key '" + ky + "'", ky, itr.second);
|
||||
} else if (ky == "condition") {
|
||||
std::string condition = itr.second.as_string();
|
||||
if (!conditions.contains(condition)) {
|
||||
if (!conditions.contains(condition) && Project::is_condition_name(condition)) {
|
||||
throw_key_error("Unknown condition '" + condition + "'", condition, itr.second);
|
||||
}
|
||||
}
|
||||
@@ -282,6 +282,9 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
if (checker.contains("conditions")) {
|
||||
auto conds = toml::find<decltype(conditions)>(toml, "conditions");
|
||||
for (const auto &cond : conds) {
|
||||
if (!is_condition_name(cond.first)) {
|
||||
throw_key_error("Invalid condition name '" + cond.first + "'", cond.first, toml::find(toml::find(toml, "conditions"), cond.first));
|
||||
}
|
||||
conditions[cond.first] = cond.second;
|
||||
}
|
||||
}
|
||||
@@ -372,6 +375,25 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
}
|
||||
|
||||
if (checker.contains("options")) {
|
||||
auto normalize = [](const std::string &name) {
|
||||
std::string normalized;
|
||||
for (char ch : name) {
|
||||
if (ch == '_') {
|
||||
normalized += '-';
|
||||
} else if (ch >= 'A' && ch <= 'Z') {
|
||||
ch += ('a' - 'A');
|
||||
normalized += ch;
|
||||
} else if (ch == '-' || (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z')) {
|
||||
normalized += ch;
|
||||
} else {
|
||||
// Ignore all other characters
|
||||
}
|
||||
}
|
||||
return normalized;
|
||||
};
|
||||
auto nproject_prefix = normalize(project_name);
|
||||
nproject_prefix += '-';
|
||||
|
||||
using opts_map = tsl::ordered_map<std::string, TomlBasicValue>;
|
||||
const auto &opts = toml::find<opts_map>(toml, "options");
|
||||
for (const auto &itr : opts) {
|
||||
@@ -409,7 +431,18 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
throw_key_error(toml::concat_to_string("Unsupported value type: ", itr.second.type()), itr.first, itr.second);
|
||||
}
|
||||
options.push_back(o);
|
||||
conditions.emplace(o.name, o.name);
|
||||
|
||||
// Add an implicit condition for the option
|
||||
auto ncondition = normalize(o.name);
|
||||
if (ncondition.find(nproject_prefix) == 0) {
|
||||
ncondition = ncondition.substr(nproject_prefix.size());
|
||||
}
|
||||
if (!ncondition.empty()) {
|
||||
if (conditions.contains(ncondition)) {
|
||||
print_key_warning("Option '" + o.name + "' would create a condition '" + ncondition + "' that already exists", o.name, value);
|
||||
}
|
||||
conditions.emplace(ncondition, o.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -500,8 +533,11 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
key = "URL";
|
||||
} else if (hash_algorithms.contains(key)) {
|
||||
std::string algo;
|
||||
for (auto c : key) {
|
||||
algo.push_back(std::toupper(c));
|
||||
for (auto ch : key) {
|
||||
if (ch >= 'a' && ch <= 'z') {
|
||||
ch -= ('a' - 'A');
|
||||
}
|
||||
algo.push_back(ch);
|
||||
}
|
||||
key = "URL_HASH";
|
||||
value = algo + "=" + value;
|
||||
@@ -639,28 +675,28 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
|
||||
Condition<std::string> msvc_runtime;
|
||||
t.optional("msvc-runtime", msvc_runtime);
|
||||
for (const auto &condItr : msvc_runtime) {
|
||||
switch (parse_msvcRuntimeType(condItr.second)) {
|
||||
for (const auto &cond_itr : msvc_runtime) {
|
||||
switch (parse_msvcRuntimeType(cond_itr.second)) {
|
||||
case msvc_dynamic:
|
||||
target.properties[condItr.first]["MSVC_RUNTIME_LIBRARY"] = "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL";
|
||||
target.properties[cond_itr.first]["MSVC_RUNTIME_LIBRARY"] = "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL";
|
||||
break;
|
||||
case msvc_static:
|
||||
target.properties[condItr.first]["MSVC_RUNTIME_LIBRARY"] = "MultiThreaded$<$<CONFIG:Debug>:Debug>";
|
||||
target.properties[cond_itr.first]["MSVC_RUNTIME_LIBRARY"] = "MultiThreaded$<$<CONFIG:Debug>:Debug>";
|
||||
break;
|
||||
default: {
|
||||
std::string error = "Unknown runtime '" + condItr.second + "'\n";
|
||||
std::string error = "Unknown runtime '" + cond_itr.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()) {
|
||||
if (cond_itr.first.empty()) {
|
||||
report = &t.find("msvc-runtime");
|
||||
} else {
|
||||
report = &t.find(condItr.first).as_table().find("msvc-runtime").value();
|
||||
report = &t.find(cond_itr.first).as_table().find("msvc-runtime").value();
|
||||
}
|
||||
throw_key_error(error, condItr.second, *report);
|
||||
throw_key_error(error, cond_itr.second, *report);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -833,6 +869,16 @@ bool Project::cmake_minimum_version(int major, int minor) const {
|
||||
return std::tie(root_major, root_minor) >= std::tie(major, minor);
|
||||
}
|
||||
|
||||
bool Project::is_condition_name(const std::string &name) {
|
||||
auto is_named_condition = true;
|
||||
for (auto ch : name) {
|
||||
if (!(ch == '-' || (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z'))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_root_path(const std::string &path) {
|
||||
const auto toml_path = fs::path(path) / "cmake.toml";
|
||||
if (!fs::exists(toml_path)) {
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
name = "conditions"
|
||||
cmake-after = "set(CUSTOM ON)"
|
||||
|
||||
[options]
|
||||
CONDITIONS_BUILD_TESTS = "root"
|
||||
|
||||
[conditions]
|
||||
custom = "CUSTOM"
|
||||
|
||||
@@ -15,6 +18,8 @@ macos.cmake-after = "message(STATUS macos-after)"
|
||||
linux.cmake-after = "message(STATUS linux-after)"
|
||||
unix.cmake-after = "message(STATUS unix-after)"
|
||||
custom.cmake-after = "message(STATUS custom-after)"
|
||||
build-tests.cmake-after = "message(STATUS build-tests)"
|
||||
"CONDITIONS_BUILD_TESTS AND $<linux>".cmake-after = "message(STATUS linux-tests)"
|
||||
|
||||
[target.example.properties]
|
||||
AUTOMOC = false
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
|
||||
namespace mylib {
|
||||
std::string message();
|
||||
}
|
||||
} // namespace mylib
|
||||
|
||||
@@ -7,7 +7,7 @@ description = "Dependencies from vcpkg"
|
||||
# See https://github.com/microsoft/vcpkg/releases for vcpkg versions
|
||||
# See https://vcpkg.io/en/packages.html for available packages
|
||||
[vcpkg]
|
||||
version = "2022.11.14"
|
||||
version = "2024.03.25"
|
||||
packages = ["fmt"]
|
||||
|
||||
[find-package]
|
||||
|
||||
-4
@@ -16,7 +16,3 @@ target_include_directories(toml11 INTERFACE toml11-3.6.0)
|
||||
# https://github.com/mpark/variant (BSL-1.0)
|
||||
add_library(mpark_variant INTERFACE)
|
||||
target_include_directories(mpark_variant INTERFACE variant-1.4.0/include)
|
||||
|
||||
# https://github.com/nlohmann/json (MIT)
|
||||
add_library(nlohmann_json INTERFACE)
|
||||
target_include_directories(nlohmann_json INTERFACE nlohmann-3.9.1/include)
|
||||
Vendored
-21
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2013-2021 Niels Lohmann
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
-25447
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user