mirror of
https://github.com/build-cpp/cmkr
synced 2026-06-08 13:22:55 +00:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ef537ce084 | |||
| 9cdd0f7344 | |||
| e69cf4d2b9 | |||
| e98a906231 | |||
| 9a82f8c796 | |||
| 13255c68cf | |||
| 5768460827 | |||
| 50d4a905b6 | |||
| f957cec2dc | |||
| 232e49e087 | |||
| 7408d42160 | |||
| 1596a8143d | |||
| 9b0f18ee94 | |||
| 887086dc05 | |||
| edc8e1e02e | |||
| 1f6e31e0ef | |||
| 4de1500bb3 | |||
| 85370968f8 | |||
| 06a4c04df6 | |||
| fef2a0215b | |||
| fc74a5e802 | |||
| e48d402e21 | |||
| f6a245618e | |||
| c713606fd4 | |||
| fe9b6587a9 | |||
| a2f8d9e5c9 | |||
| c7925e7110 | |||
| c778a5fe2b | |||
| 16b2cbbb57 | |||
| 2f5c7823be | |||
| a29b6a8aaa |
Generated
+1
-1
@@ -25,7 +25,7 @@ project(cmkr
|
||||
LANGUAGES
|
||||
CXX
|
||||
VERSION
|
||||
0.2.8
|
||||
0.2.13
|
||||
DESCRIPTION
|
||||
"CMakeLists generator from TOML"
|
||||
)
|
||||
|
||||
@@ -69,3 +69,5 @@ arguments:
|
||||
- https://github.com/ToruNiina/toml11
|
||||
- https://github.com/mpark/variant
|
||||
- https://www.svgrepo.com/svg/192268/hammer
|
||||
- https://github.com/can1357 for buying `cmkr.build` ❤️
|
||||
- https://github.com/JustasMasiulis for fixing the dark theme ❤️
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ cmkr-include = false
|
||||
|
||||
[project]
|
||||
name = "cmkr"
|
||||
version = "0.2.8"
|
||||
version = "0.2.13"
|
||||
description = "CMakeLists generator from TOML"
|
||||
languages = ["CXX"]
|
||||
subdirs = ["third_party", "tests"]
|
||||
|
||||
@@ -12,6 +12,14 @@ if(NOT EXISTS "${CMAKE_SOURCE_DIR}/cmake/cmkr.cmake")
|
||||
message(FATAL_ERROR "Cannot find cmkr.cmake")
|
||||
endif()
|
||||
|
||||
# Validate branch
|
||||
find_package(Git REQUIRED)
|
||||
execute_process(COMMAND "${GIT_EXECUTABLE}" branch --show-current OUTPUT_VARIABLE GIT_BRANCH)
|
||||
string(STRIP "${GIT_BRANCH}" GIT_BRANCH)
|
||||
if(NOT GIT_BRANCH STREQUAL "main")
|
||||
message(FATAL_ERROR "You need to be on the main branch, you are on: ${GIT_BRANCH}")
|
||||
endif()
|
||||
|
||||
file(READ "${CMAKE_SOURCE_DIR}/cmake.toml" CMAKE_TOML)
|
||||
string(FIND "${CMAKE_TOML}" "[project]" PROJECT_INDEX)
|
||||
string(SUBSTRING "${CMAKE_TOML}" ${PROJECT_INDEX} -1 CMAKE_TOML_PROJECT)
|
||||
@@ -38,7 +46,7 @@ endif()
|
||||
|
||||
message(STATUS "Version ${OLDVERSION} -> ${NEWVERSION}")
|
||||
|
||||
find_program(CMKR_EXECUTABLE "cmkr" PATHS "${CMAKE_SOURCE_DIR}/build" NO_CACHE REQUIRED)
|
||||
find_program(CMKR_EXECUTABLE "cmkr" PATHS "${CMAKE_SOURCE_DIR}/build" PATH_SUFFIXES Debug Release RelWithDebInfo MinSizeRel NO_CACHE REQUIRED)
|
||||
message(STATUS "Found cmkr: ${CMKR_EXECUTABLE}")
|
||||
|
||||
# Replace version in cmake.toml
|
||||
|
||||
+13
-2
@@ -2,7 +2,8 @@ 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.8" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE)
|
||||
set(CMKR_TAG "v0.2.13" 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
|
||||
if(CMAKE_SCRIPT_MODE_FILE)
|
||||
@@ -15,7 +16,7 @@ endif()
|
||||
set(CMKR_EXECUTABLE "" CACHE FILEPATH "cmkr executable")
|
||||
set(CMKR_SKIP_GENERATION OFF CACHE BOOL "skip automatic cmkr generation")
|
||||
set(CMKR_BUILD_TYPE "Debug" CACHE STRING "cmkr build configuration")
|
||||
mark_as_advanced(CMKR_REPO CMKR_TAG CMKR_EXECUTABLE CMKR_SKIP_GENERATION CMKR_BUILD_TYPE)
|
||||
mark_as_advanced(CMKR_REPO CMKR_TAG CMKR_COMMIT_HASH CMKR_EXECUTABLE CMKR_SKIP_GENERATION CMKR_BUILD_TYPE)
|
||||
|
||||
# Disable cmkr if generation is disabled
|
||||
if(DEFINED ENV{CI} OR CMKR_SKIP_GENERATION OR CMKR_BUILD_SKIP_GENERATION)
|
||||
@@ -115,6 +116,16 @@ else()
|
||||
${CMKR_REPO}
|
||||
"${CMKR_DIRECTORY}"
|
||||
)
|
||||
if(CMKR_COMMIT_HASH)
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" checkout -q "${CMKR_COMMIT_HASH}"
|
||||
RESULT_VARIABLE CMKR_EXEC_RESULT
|
||||
WORKING_DIRECTORY "${CMKR_DIRECTORY}"
|
||||
)
|
||||
if(NOT CMKR_EXEC_RESULT EQUAL 0)
|
||||
message(FATAL_ERROR "Tag '${CMKR_TAG}' hash is not '${CMKR_COMMIT_HASH}'")
|
||||
endif()
|
||||
endif()
|
||||
message(STATUS "[cmkr] Building cmkr (using system compiler)...")
|
||||
cmkr_exec("${CMAKE_COMMAND}"
|
||||
--no-warn-unused-cli
|
||||
|
||||
@@ -3,6 +3,7 @@ gem "minima", "~> 2.5"
|
||||
gem "github-pages", group: :jekyll_plugins
|
||||
|
||||
gem "jekyll-remote-theme"
|
||||
gem "jekyll-sitemap", "~> 1.4.0"
|
||||
|
||||
platforms :mingw, :x64_mingw, :mswin, :jruby do
|
||||
gem "tzinfo", "~> 1.2"
|
||||
|
||||
@@ -271,6 +271,7 @@ PLATFORMS
|
||||
DEPENDENCIES
|
||||
github-pages
|
||||
jekyll-remote-theme
|
||||
jekyll-sitemap (~> 1.4.0)
|
||||
just-the-docs
|
||||
minima (~> 2.5)
|
||||
tzinfo (~> 1.2)
|
||||
|
||||
@@ -2,6 +2,7 @@ title: cmkr
|
||||
|
||||
plugins:
|
||||
- jekyll-remote-theme
|
||||
- jekyll-sitemap
|
||||
|
||||
# Automatically deduce dark theme preference https://github.com/pmarsceill/just-the-docs/pull/464
|
||||
remote_theme: build-cpp/just-the-docs@light-switch
|
||||
@@ -19,3 +20,5 @@ gh_edit_repository: "https://github.com/build-cpp/cmkr"
|
||||
gh_edit_branch: "main"
|
||||
gh_edit_view_mode: "edit"
|
||||
gh_edit_source: docs
|
||||
|
||||
production_url : https://cmkr.build
|
||||
+4
-2
@@ -200,7 +200,9 @@ working-directory = "mytest-dir"
|
||||
condition = "mycondition"
|
||||
targets = ["mytarget", "mytest"]
|
||||
destination = ["bin"]
|
||||
component = "mycomponent"
|
||||
files = ["content/my.png"]
|
||||
dirs = [""]
|
||||
configs = [""]
|
||||
dirs = ["include"]
|
||||
configs = ["Release", "Debug"]
|
||||
optional = false
|
||||
```
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
layout: null
|
||||
permalink: /getting-started/
|
||||
---
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="0;url=https://cmkr.build">
|
||||
</head>
|
||||
</html>
|
||||
@@ -128,11 +128,13 @@ struct Install {
|
||||
std::vector<std::string> configs;
|
||||
std::string destination;
|
||||
std::string component;
|
||||
bool optional = false;
|
||||
};
|
||||
|
||||
struct Subdir {
|
||||
std::string name;
|
||||
std::string condition;
|
||||
|
||||
Condition<std::string> cmake_before;
|
||||
Condition<std::string> cmake_after;
|
||||
ConditionVector include_before;
|
||||
@@ -143,6 +145,11 @@ struct Content {
|
||||
std::string name;
|
||||
std::string condition;
|
||||
tsl::ordered_map<std::string, std::string> arguments;
|
||||
|
||||
Condition<std::string> cmake_before;
|
||||
Condition<std::string> cmake_after;
|
||||
ConditionVector include_before;
|
||||
ConditionVector include_after;
|
||||
};
|
||||
|
||||
struct Project {
|
||||
|
||||
+64
-53
@@ -13,15 +13,6 @@
|
||||
namespace cmkr {
|
||||
namespace gen {
|
||||
|
||||
static std::string to_upper(const std::string &str) {
|
||||
std::string temp;
|
||||
temp.reserve(str.size());
|
||||
for (auto c : str) {
|
||||
temp.push_back(::toupper(c));
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
static std::string format(const char *format, tsl::ordered_map<std::string, std::string> variables) {
|
||||
std::string s = format;
|
||||
for (const auto &itr : variables) {
|
||||
@@ -34,7 +25,7 @@ static std::string format(const char *format, tsl::ordered_map<std::string, std:
|
||||
return s;
|
||||
}
|
||||
|
||||
static std::vector<std::string> expand_cmake_path(const fs::path &name, const fs::path &toml_dir) {
|
||||
static std::vector<std::string> expand_cmake_path(const fs::path &name, const fs::path &toml_dir, bool root_project) {
|
||||
std::vector<std::string> temp;
|
||||
|
||||
auto extract_suffix = [](const fs::path &base, const fs::path &full) {
|
||||
@@ -47,6 +38,10 @@ static std::vector<std::string> expand_cmake_path(const fs::path &name, const fs
|
||||
auto stem = name.filename().stem().string();
|
||||
auto ext = name.extension();
|
||||
|
||||
if (root_project && stem == "**" && name == name.filename()) {
|
||||
throw std::runtime_error("Recursive globbing not allowed in project root: " + name.string());
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -71,11 +66,11 @@ static std::vector<std::string> expand_cmake_path(const fs::path &name, const fs
|
||||
return temp;
|
||||
}
|
||||
|
||||
static std::vector<std::string> expand_cmake_paths(const std::vector<std::string> &sources, const fs::path &toml_dir) {
|
||||
static std::vector<std::string> expand_cmake_paths(const std::vector<std::string> &sources, const fs::path &toml_dir, bool root_project) {
|
||||
// TODO: add duplicate checking
|
||||
std::vector<std::string> result;
|
||||
for (const auto &src : sources) {
|
||||
auto expanded = expand_cmake_path(src, toml_dir);
|
||||
auto expanded = expand_cmake_path(src, toml_dir, root_project);
|
||||
for (const auto &f : expanded) {
|
||||
result.push_back(f);
|
||||
}
|
||||
@@ -162,11 +157,16 @@ struct Command {
|
||||
}
|
||||
|
||||
static std::string quote(const std::string &str) {
|
||||
// Don't quote arguments that don't need quoting
|
||||
if (str.find(' ') == std::string::npos && str.find('\"') == std::string::npos && str.find('/') == std::string::npos &&
|
||||
str.find(';') == std::string::npos) {
|
||||
return str;
|
||||
// Quote an empty string
|
||||
if (str.empty()) {
|
||||
return "\"\"";
|
||||
}
|
||||
// Don't quote arguments that don't need quoting
|
||||
// https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#unquoted-argument
|
||||
// NOTE: Normally '/' does not require quoting according to the documentation but this has been the case here
|
||||
// previously, so for backwards compatibility its still here.
|
||||
if (str.find_first_of("()#\"\\'> |/;") == str.npos)
|
||||
return str;
|
||||
std::string result;
|
||||
result += "\"";
|
||||
for (char ch : str) {
|
||||
@@ -398,6 +398,14 @@ struct Generator {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void conditional_includes(const parser::ConditionVector &include) {
|
||||
handle_condition(include, [this](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
|
||||
}
|
||||
|
||||
void conditional_cmake(const parser::Condition<std::string> &cmake) {
|
||||
handle_condition(cmake, [this](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
|
||||
}
|
||||
};
|
||||
|
||||
struct ConditionScope {
|
||||
@@ -472,6 +480,9 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
throw std::runtime_error("No cmake.toml found!");
|
||||
}
|
||||
|
||||
// Root project doesn't have a parent
|
||||
auto root_project = parent_project == nullptr;
|
||||
|
||||
parser::Project project(parent_project, path, false);
|
||||
Generator gen(project);
|
||||
|
||||
@@ -480,16 +491,13 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
auto cmd = [&gen](const std::string &command) { return gen.cmd(command); };
|
||||
auto comment = [&gen](const std::string &comment) { return gen.comment(comment); };
|
||||
auto endl = [&gen]() { gen.endl(); };
|
||||
auto inject_includes = [&gen](const std::vector<std::string> &includes) { gen.inject_includes(includes); };
|
||||
auto inject_cmake = [&gen](const std::string &cmake) { gen.inject_cmake(cmake); };
|
||||
|
||||
std::string cmkr_url = "https://github.com/build-cpp/cmkr";
|
||||
comment("This file is automatically generated from cmake.toml - DO NOT EDIT");
|
||||
comment("See " + cmkr_url + " for more information");
|
||||
endl();
|
||||
|
||||
// Root project doesn't have a parent
|
||||
if (parent_project == nullptr) {
|
||||
if (root_project) {
|
||||
cmd("cmake_minimum_required")("VERSION", project.cmake_version).endl();
|
||||
|
||||
if (!project.allow_in_tree) {
|
||||
@@ -562,7 +570,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
if (!project.options.empty()) {
|
||||
comment("Options");
|
||||
for (const auto &opt : project.options) {
|
||||
cmd("option")(opt.name, opt.comment, opt.val ? "ON" : "OFF");
|
||||
cmd("option")(opt.name, RawArg(Command::quote(opt.comment)), opt.val ? "ON" : "OFF");
|
||||
}
|
||||
endl();
|
||||
}
|
||||
@@ -588,8 +596,8 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
endl();
|
||||
}
|
||||
|
||||
gen.handle_condition(project.include_before, [&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
|
||||
gen.handle_condition(project.cmake_before, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
|
||||
gen.conditional_includes(project.include_before);
|
||||
gen.conditional_cmake(project.cmake_before);
|
||||
|
||||
if (!project.project_name.empty()) {
|
||||
auto languages = std::make_pair("LANGUAGES", project.project_languages);
|
||||
@@ -598,8 +606,8 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
cmd("project")(project.project_name, languages, version, description).endl();
|
||||
}
|
||||
|
||||
gen.handle_condition(project.include_after, [&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
|
||||
gen.handle_condition(project.cmake_after, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
|
||||
gen.conditional_includes(project.include_after);
|
||||
gen.conditional_cmake(project.cmake_after);
|
||||
|
||||
if (!project.vcpkg.packages.empty()) {
|
||||
// Allow the user to specify a url or derive it from the version
|
||||
@@ -626,8 +634,12 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
cmd("include")("FetchContent");
|
||||
cmd("message")("STATUS", "Fetching vcpkg (" + version_name + ")...");
|
||||
cmd("FetchContent_Declare")("vcpkg", "URL", url);
|
||||
cmd("FetchContent_MakeAvailable")("vcpkg");
|
||||
cmd("include")("${vcpkg_SOURCE_DIR}/scripts/buildsystems/vcpkg.cmake");
|
||||
// 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("endif")();
|
||||
endl();
|
||||
// clang-format on
|
||||
@@ -700,6 +712,10 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
cmd("include")("FetchContent").endl();
|
||||
for (const auto &content : project.contents) {
|
||||
ConditionScope cs(gen, content.condition);
|
||||
|
||||
gen.conditional_includes(content.include_before);
|
||||
gen.conditional_cmake(content.cmake_before);
|
||||
|
||||
std::string version_info = "";
|
||||
if (content.arguments.contains("GIT_TAG")) {
|
||||
version_info = " (" + content.arguments.at("GIT_TAG") + ")";
|
||||
@@ -707,12 +723,11 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
version_info = " (" + content.arguments.at("SVN_REVISION") + ")";
|
||||
}
|
||||
cmd("message")("STATUS", "Fetching " + content.name + version_info + "...");
|
||||
ss << "FetchContent_Declare(\n\t" << content.name << "\n";
|
||||
for (const auto &arg : content.arguments) {
|
||||
ss << "\t" << arg.first << "\n\t\t" << arg.second << "\n";
|
||||
}
|
||||
ss << ")\n";
|
||||
cmd("FetchContent_Declare")(content.name, content.arguments);
|
||||
cmd("FetchContent_MakeAvailable")(content.name).endl();
|
||||
|
||||
gen.conditional_includes(content.include_after);
|
||||
gen.conditional_cmake(content.cmake_after);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -757,14 +772,13 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
for (const auto &subdir : project.subdirs) {
|
||||
ConditionScope cs(gen, subdir.condition);
|
||||
|
||||
gen.handle_condition(subdir.include_before,
|
||||
[&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
|
||||
gen.handle_condition(subdir.cmake_before, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
|
||||
gen.conditional_includes(subdir.include_before);
|
||||
gen.conditional_cmake(subdir.cmake_before);
|
||||
|
||||
add_subdir(subdir.name);
|
||||
|
||||
gen.handle_condition(subdir.include_after, [&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
|
||||
gen.handle_condition(subdir.cmake_after, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
|
||||
gen.conditional_includes(subdir.include_after);
|
||||
gen.conditional_cmake(subdir.cmake_after);
|
||||
}
|
||||
|
||||
if (!project.targets.empty()) {
|
||||
@@ -794,14 +808,12 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
cmd("set")("CMKR_TARGET", target.name);
|
||||
|
||||
if (tmplate != nullptr) {
|
||||
gen.handle_condition(tmplate->outline.include_before,
|
||||
[&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
|
||||
gen.handle_condition(tmplate->outline.cmake_before, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
|
||||
gen.conditional_includes(tmplate->outline.include_before);
|
||||
gen.conditional_cmake(tmplate->outline.cmake_before);
|
||||
}
|
||||
|
||||
gen.handle_condition(target.include_before,
|
||||
[&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
|
||||
gen.handle_condition(target.cmake_before, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
|
||||
gen.conditional_includes(target.include_before);
|
||||
gen.conditional_cmake(target.cmake_before);
|
||||
|
||||
auto sources_var = target.name + "_SOURCES";
|
||||
|
||||
@@ -810,7 +822,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
|
||||
if (tmplate != nullptr) {
|
||||
gen.handle_condition(tmplate->outline.sources, [&](const std::string &condition, const std::vector<std::string> &condition_sources) {
|
||||
auto sources = expand_cmake_paths(condition_sources, path);
|
||||
auto sources = expand_cmake_paths(condition_sources, path, root_project);
|
||||
if (sources.empty()) {
|
||||
auto source_key = condition.empty() ? "sources" : (condition + ".sources");
|
||||
throw std::runtime_error(target.name + " " + source_key + " wildcard found 0 files");
|
||||
@@ -820,7 +832,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
}
|
||||
|
||||
gen.handle_condition(target.sources, [&](const std::string &condition, const std::vector<std::string> &condition_sources) {
|
||||
auto sources = expand_cmake_paths(condition_sources, path);
|
||||
auto sources = expand_cmake_paths(condition_sources, path, root_project);
|
||||
if (sources.empty()) {
|
||||
auto source_key = condition.empty() ? "sources" : (condition + ".sources");
|
||||
throw std::runtime_error(target.name + " " + source_key + " wildcard found 0 files");
|
||||
@@ -982,14 +994,12 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
});
|
||||
}
|
||||
|
||||
gen.handle_condition(target.include_after,
|
||||
[&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
|
||||
gen.handle_condition(target.cmake_after, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
|
||||
gen.conditional_includes(target.include_after);
|
||||
gen.conditional_cmake(target.cmake_after);
|
||||
|
||||
if (tmplate != nullptr) {
|
||||
gen.handle_condition(tmplate->outline.include_after,
|
||||
[&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
|
||||
gen.handle_condition(tmplate->outline.cmake_after, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
|
||||
gen.conditional_includes(tmplate->outline.include_after);
|
||||
gen.conditional_cmake(tmplate->outline.cmake_after);
|
||||
}
|
||||
|
||||
cmd("unset")("CMKR_TARGET");
|
||||
@@ -1022,7 +1032,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
auto dirs = std::make_pair("DIRS", inst.dirs);
|
||||
std::vector<std::string> files_data;
|
||||
if (!inst.files.empty()) {
|
||||
files_data = expand_cmake_paths(inst.files, path);
|
||||
files_data = expand_cmake_paths(inst.files, path, root_project);
|
||||
if (files_data.empty()) {
|
||||
throw std::runtime_error("[[install]] files wildcard did not resolve to any files");
|
||||
}
|
||||
@@ -1035,8 +1045,9 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
component_name = inst.targets.front();
|
||||
}
|
||||
auto component = std::make_pair("COMPONENT", component_name);
|
||||
auto optional = inst.optional ? "OPTIONAL" : "";
|
||||
ConditionScope cs(gen, inst.condition);
|
||||
cmd("install")(targets, dirs, files, configs, destination, component);
|
||||
cmd("install")(targets, dirs, files, configs, destination, component, optional);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+101
-32
@@ -5,6 +5,7 @@
|
||||
#include <stdexcept>
|
||||
#include <toml.hpp>
|
||||
#include <tsl/ordered_map.h>
|
||||
#include <tsl/ordered_set.h>
|
||||
|
||||
namespace cmkr {
|
||||
namespace parser {
|
||||
@@ -49,8 +50,8 @@ static std::string format_key_error(const std::string &error, const toml::key &k
|
||||
|
||||
class TomlChecker {
|
||||
const TomlBasicValue &m_v;
|
||||
tsl::ordered_map<toml::key, bool> m_visited;
|
||||
tsl::ordered_map<toml::key, bool> m_conditionVisited;
|
||||
tsl::ordered_set<toml::key> m_visited;
|
||||
tsl::ordered_set<toml::key> m_conditionVisited;
|
||||
|
||||
public:
|
||||
TomlChecker(const TomlBasicValue &v, const toml::key &ky) : m_v(toml::find(v, ky)) {}
|
||||
@@ -77,7 +78,7 @@ class TomlChecker {
|
||||
// Handle visiting logic
|
||||
for (const auto &itr : destination) {
|
||||
if (!itr.first.empty()) {
|
||||
m_conditionVisited.emplace(itr.first, true);
|
||||
m_conditionVisited.emplace(itr.first);
|
||||
}
|
||||
}
|
||||
visit(ky);
|
||||
@@ -108,7 +109,9 @@ class TomlChecker {
|
||||
return toml::find(m_v, ky);
|
||||
}
|
||||
|
||||
void visit(const toml::key &ky) { m_visited.emplace(ky, true); }
|
||||
void visit(const toml::key &ky) { m_visited.emplace(ky); }
|
||||
|
||||
bool visisted(const toml::key &ky) const { return m_visited.contains(ky); }
|
||||
|
||||
void check(const tsl::ordered_map<std::string, std::string> &conditions) const {
|
||||
for (const auto &itr : m_v.as_table()) {
|
||||
@@ -143,14 +146,21 @@ class TomlChecker {
|
||||
};
|
||||
|
||||
class TomlCheckerRoot {
|
||||
const TomlBasicValue &m_root;
|
||||
std::deque<TomlChecker> m_checkers;
|
||||
tsl::ordered_set<toml::key> m_visisted;
|
||||
bool m_checked = false;
|
||||
|
||||
public:
|
||||
TomlCheckerRoot() = default;
|
||||
TomlCheckerRoot(const TomlBasicValue &root) : m_root(root) {}
|
||||
TomlCheckerRoot(const TomlCheckerRoot &) = delete;
|
||||
TomlCheckerRoot(TomlCheckerRoot &&) = delete;
|
||||
|
||||
bool contains(const toml::key &ky) {
|
||||
m_visisted.emplace(ky);
|
||||
return m_root.contains(ky);
|
||||
}
|
||||
|
||||
TomlChecker &create(const TomlBasicValue &v) {
|
||||
m_checkers.emplace_back(v);
|
||||
return m_checkers.back();
|
||||
@@ -161,7 +171,14 @@ class TomlCheckerRoot {
|
||||
return m_checkers.back();
|
||||
}
|
||||
|
||||
void check(const tsl::ordered_map<std::string, std::string> &conditions) {
|
||||
void check(const tsl::ordered_map<std::string, std::string> &conditions, bool check_root) {
|
||||
if (check_root) {
|
||||
for (const auto &itr : m_root.as_table()) {
|
||||
if (!m_visisted.contains(itr.first)) {
|
||||
throw std::runtime_error(format_key_error("Unknown key '" + itr.first + "'", itr.first, itr.second));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto &checker : m_checkers) {
|
||||
checker.check(conditions);
|
||||
}
|
||||
@@ -171,13 +188,16 @@ class TomlCheckerRoot {
|
||||
Project::Project(const Project *parent, const std::string &path, bool build) {
|
||||
const auto toml_path = fs::path(path) / "cmake.toml";
|
||||
if (!fs::exists(toml_path)) {
|
||||
throw std::runtime_error("No cmake.toml was found!");
|
||||
throw std::runtime_error("File not found '" + toml_path.string() + "'");
|
||||
}
|
||||
const auto toml = toml::parse<toml::discard_comments, tsl::ordered_map, std::vector>(toml_path.string());
|
||||
if (toml.size() == 0) {
|
||||
throw std::runtime_error("Empty TOML '" + toml_path.string() + "'");
|
||||
}
|
||||
|
||||
TomlCheckerRoot checker;
|
||||
TomlCheckerRoot checker(toml);
|
||||
|
||||
if (toml.contains("cmake")) {
|
||||
if (checker.contains("cmake")) {
|
||||
auto &cmake = checker.create(toml, "cmake");
|
||||
|
||||
cmake.required("version", cmake_version);
|
||||
@@ -209,7 +229,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
|
||||
|
||||
// Skip the rest of the parsing when building
|
||||
if (build) {
|
||||
checker.check(conditions);
|
||||
checker.check(conditions, false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -231,14 +251,14 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
|
||||
templates = parent->templates;
|
||||
}
|
||||
|
||||
if (toml.contains("conditions")) {
|
||||
if (checker.contains("conditions")) {
|
||||
auto conds = toml::find<decltype(conditions)>(toml, "conditions");
|
||||
for (const auto &cond : conds) {
|
||||
conditions[cond.first] = cond.second;
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("project")) {
|
||||
if (checker.contains("project")) {
|
||||
auto &project = checker.create(toml, "project");
|
||||
project.required("name", project_name);
|
||||
project.optional("version", project_version);
|
||||
@@ -251,7 +271,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
|
||||
project.optional("subdirs", project_subdirs);
|
||||
}
|
||||
|
||||
if (toml.contains("subdir")) {
|
||||
if (checker.contains("subdir")) {
|
||||
const auto &subs = toml::find(toml, "subdir").as_table();
|
||||
for (const auto &itr : subs) {
|
||||
Subdir subdir;
|
||||
@@ -268,7 +288,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("settings")) {
|
||||
if (checker.contains("settings")) {
|
||||
using set_map = tsl::ordered_map<std::string, TomlBasicValue>;
|
||||
const auto &sets = toml::find<set_map>(toml, "settings");
|
||||
for (const auto &itr : sets) {
|
||||
@@ -297,7 +317,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("options")) {
|
||||
if (checker.contains("options")) {
|
||||
using opts_map = tsl::ordered_map<std::string, TomlBasicValue>;
|
||||
const auto &opts = toml::find<opts_map>(toml, "options");
|
||||
for (const auto &itr : opts) {
|
||||
@@ -315,7 +335,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("find-package")) {
|
||||
if (checker.contains("find-package")) {
|
||||
using pkg_map = tsl::ordered_map<std::string, TomlBasicValue>;
|
||||
const auto &pkgs = toml::find<pkg_map>(toml, "find-package");
|
||||
for (const auto &itr : pkgs) {
|
||||
@@ -336,41 +356,88 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: perform checking here
|
||||
if (toml.contains("fetch-content")) {
|
||||
if (checker.contains("fetch-content")) {
|
||||
const auto &fc = toml::find(toml, "fetch-content").as_table();
|
||||
for (const auto &itr : fc) {
|
||||
Content content;
|
||||
content.name = itr.first;
|
||||
|
||||
auto &c = checker.create(itr.second);
|
||||
c.optional("condition", content.condition);
|
||||
c.optional("cmake-before", content.cmake_before);
|
||||
c.optional("cmake-after", content.cmake_after);
|
||||
c.optional("include-before", content.include_before);
|
||||
c.optional("include-after", content.include_after);
|
||||
|
||||
for (const auto &argItr : itr.second.as_table()) {
|
||||
auto key = argItr.first;
|
||||
if (key == "condition") {
|
||||
content.condition = argItr.second.as_string();
|
||||
continue;
|
||||
std::string value;
|
||||
if (argItr.second.is_array()) {
|
||||
for (const auto &list_val : argItr.second.as_array()) {
|
||||
if (!value.empty()) {
|
||||
value += ';';
|
||||
}
|
||||
value += list_val.as_string();
|
||||
}
|
||||
} else if (argItr.second.is_boolean()) {
|
||||
value = argItr.second.as_boolean() ? "ON" : "OFF";
|
||||
} else {
|
||||
value = argItr.second.as_string();
|
||||
}
|
||||
|
||||
auto is_cmake_arg = [](const std::string &s) {
|
||||
for (auto c : s) {
|
||||
if (!(std::isdigit(c) || std::isupper(c) || c == '_')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// https://cmake.org/cmake/help/latest/command/string.html#supported-hash-algorithms
|
||||
tsl::ordered_set<std::string> hash_algorithms = {
|
||||
"md5", "sha1", "sha224", "sha256", "sha384", "sha512", "sha3_224", "sha3_256", "sha3_384", "sha3_512",
|
||||
};
|
||||
|
||||
auto key = argItr.first;
|
||||
if (key == "git") {
|
||||
key = "GIT_REPOSITORY";
|
||||
} else if (key == "tag") {
|
||||
key = "GIT_TAG";
|
||||
} else if (key == "shallow") {
|
||||
key = "GIT_SHALLOW";
|
||||
} else if (key == "svn") {
|
||||
key = "SVN_REPOSITORY";
|
||||
} else if (key == "rev") {
|
||||
key = "SVN_REVISION";
|
||||
} else if (key == "url") {
|
||||
key = "URL";
|
||||
} else if (hash_algorithms.contains(key)) {
|
||||
std::string algo;
|
||||
for (auto c : key) {
|
||||
algo.push_back(std::toupper(c));
|
||||
}
|
||||
key = "URL_HASH";
|
||||
value = algo + "=" + value;
|
||||
} else if (key == "hash") {
|
||||
key = "URL_HASH";
|
||||
} else {
|
||||
// don't change arg
|
||||
} else if (is_cmake_arg(key)) {
|
||||
// allow passthrough of ExternalProject options
|
||||
} else if (!c.visisted(key)) {
|
||||
throw std::runtime_error(format_key_error("Unknown key '" + argItr.first + "'", argItr.first, argItr.second));
|
||||
}
|
||||
content.arguments.emplace(key, argItr.second.as_string());
|
||||
|
||||
// Make sure not to emit keys like "condition" in the FetchContent call
|
||||
if (!c.visisted(key)) {
|
||||
content.arguments.emplace(key, value);
|
||||
}
|
||||
|
||||
c.visit(argItr.first);
|
||||
}
|
||||
contents.emplace_back(std::move(content));
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("bin")) {
|
||||
if (checker.contains("bin")) {
|
||||
throw std::runtime_error("[[bin]] has been renamed to [[target]]");
|
||||
}
|
||||
|
||||
@@ -489,7 +556,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
|
||||
return target;
|
||||
};
|
||||
|
||||
if (toml.contains("template")) {
|
||||
if (checker.contains("template")) {
|
||||
const auto &ts = toml::find(toml, "template").as_table();
|
||||
for (const auto &itr : ts) {
|
||||
auto &t = checker.create(itr.second);
|
||||
@@ -517,7 +584,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("target")) {
|
||||
if (checker.contains("target")) {
|
||||
const auto &ts = toml::find(toml, "target").as_table();
|
||||
for (const auto &itr : ts) {
|
||||
auto &t = checker.create(itr.second);
|
||||
@@ -525,7 +592,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("test")) {
|
||||
if (checker.contains("test")) {
|
||||
const auto &ts = toml::find(toml, "test").as_array();
|
||||
for (const auto &value : ts) {
|
||||
auto &t = checker.create(value);
|
||||
@@ -540,7 +607,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("install")) {
|
||||
if (checker.contains("install")) {
|
||||
const auto &is = toml::find(toml, "install").as_array();
|
||||
for (const auto &value : is) {
|
||||
auto &i = checker.create(value);
|
||||
@@ -552,14 +619,16 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
|
||||
i.optional("configs", inst.configs);
|
||||
i.required("destination", inst.destination);
|
||||
i.optional("component", inst.component);
|
||||
i.optional("optional", inst.optional);
|
||||
installs.push_back(inst);
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("vcpkg")) {
|
||||
if (checker.contains("vcpkg")) {
|
||||
auto &v = checker.create(toml, "vcpkg");
|
||||
v.optional("url", vcpkg.url);
|
||||
v.optional("version", vcpkg.version);
|
||||
|
||||
for (const auto &p : v.find("packages").as_array()) {
|
||||
Vcpkg::Package package;
|
||||
const auto &package_str = p.as_string().str;
|
||||
@@ -582,7 +651,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
|
||||
}
|
||||
}
|
||||
|
||||
checker.check(conditions);
|
||||
checker.check(conditions, true);
|
||||
}
|
||||
|
||||
bool is_root_path(const std::string &path) {
|
||||
|
||||
Generated
+8
-8
@@ -14,7 +14,7 @@ add_test(
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/basic"
|
||||
COMMAND
|
||||
$<TARGET_FILE:cmkr>
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
build
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@ add_test(
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/interface"
|
||||
COMMAND
|
||||
$<TARGET_FILE:cmkr>
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
build
|
||||
)
|
||||
|
||||
@@ -34,7 +34,7 @@ add_test(
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/fetch-content"
|
||||
COMMAND
|
||||
$<TARGET_FILE:cmkr>
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
build
|
||||
)
|
||||
|
||||
@@ -44,7 +44,7 @@ add_test(
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/conditions"
|
||||
COMMAND
|
||||
$<TARGET_FILE:cmkr>
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
build
|
||||
)
|
||||
|
||||
@@ -54,7 +54,7 @@ add_test(
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/vcpkg"
|
||||
COMMAND
|
||||
$<TARGET_FILE:cmkr>
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
build
|
||||
)
|
||||
|
||||
@@ -64,7 +64,7 @@ add_test(
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/cxx-standard"
|
||||
COMMAND
|
||||
$<TARGET_FILE:cmkr>
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
build
|
||||
)
|
||||
|
||||
@@ -74,7 +74,7 @@ add_test(
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/globbing"
|
||||
COMMAND
|
||||
$<TARGET_FILE:cmkr>
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
build
|
||||
)
|
||||
|
||||
@@ -84,7 +84,7 @@ add_test(
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/templates"
|
||||
COMMAND
|
||||
$<TARGET_FILE:cmkr>
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
build
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user