Compare commits

..

25 Commits

Author SHA1 Message Date
Duncan Ogilvie d2e376080a Bump to 0.2.22 2023-03-09 21:36:11 +01:00
Duncan Ogilvie 634908c651 Merge pull request #97 from build-cpp/cmkr-init-regression
Fix a regression where cmkr init wouldn't create any source files
2023-03-09 21:35:25 +01:00
Duncan Ogilvie 33911f3f1b Fix a regression where cmkr init wouldn't create any source files 2023-03-09 21:35:11 +01:00
Duncan Ogilvie e650073bef Bump to 0.2.21 2023-03-08 09:48:40 +01:00
Duncan Ogilvie 96e44f7771 Merge pull request #96 from build-cpp/check-sources
Improve error messages and check if specified sources exist
2023-03-08 09:47:43 +01:00
Duncan Ogilvie cb9ad5cdb3 Improve error messages and check if specified sources exist 2023-03-08 09:41:40 +01:00
Duncan Ogilvie c785b540d3 Document target templates 2023-03-08 09:01:39 +01:00
Duncan Ogilvie 275b73eefa Remove debug print 2023-03-02 08:43:45 +01:00
Duncan Ogilvie ea49a3acb7 Bump to 0.2.20 2023-02-28 21:31:18 +01:00
Duncan Ogilvie 257e14c869 Merge pull request #93 from build-cpp/gitattributes
Automatically generate .gitattributes and .gitignore with cmkr init
2023-02-28 21:30:34 +01:00
Duncan Ogilvie 7508816fe7 Merge pull request #94 from build-cpp/user-experience
Add a special "root" value for `[options]`
2023-02-28 21:27:11 +01:00
Duncan Ogilvie 45678bc5bc Automatically generate .gitattributes and .gitignore with cmkr init 2023-02-28 21:26:07 +01:00
Duncan Ogilvie b869e5646f Add a special "root" value for [options] 2023-02-28 19:34:31 +01:00
Duncan Ogilvie a499df84b1 Bump to 0.2.19 2023-02-28 17:11:55 +01:00
Duncan Ogilvie 49440f99e0 Merge pull request #92 from build-cpp/improved-options
Improve and document the `[options]` feature
2023-02-20 02:13:50 +01:00
Duncan Ogilvie 0c19c3da0c Improve the code related to [options] 2023-02-20 02:09:36 +01:00
Duncan Ogilvie 233cadadd0 Merge pull request #90 from mike1k/sanity
Error on unknown language & unacceptable source file.
2023-02-20 01:31:47 +01:00
Duncan Ogilvie 58a5a935e5 Add a bunch of missing languages and extensions and refactor 2023-02-20 01:18:48 +01:00
Duncan Ogilvie e69427bf94 Merge pull request #91 from build-cpp/clang-condition
Fix the `clang` condition and CI improvements
2023-02-19 22:30:15 +01:00
Duncan Ogilvie bc6359805d Change the clang condition to not detect clang-cl
References:
- https://github.com/cursey/safetyhook/issues/18
- https://discourse.cmake.org/t/clang-on-windows-detected-with-msvc-interface/3470
2023-02-19 22:23:34 +01:00
Duncan Ogilvie 8dcc11e349 Add helper function Project::cmake_minimum_version for version-dependent features 2023-02-19 22:19:12 +01:00
Duncan Ogilvie 51dc49e6f1 Improve CI performance for non-tagged builds 2023-02-19 20:06:58 +01:00
mike 7d27c28b59 Fixed failing tests due to implicit project languages (internal list empty) 2023-02-09 10:53:48 -06:00
Duncan Ogilvie ae6bea3b58 Improve reference documentation 2023-01-28 11:57:22 +01:00
Duncan Ogilvie 5a0eda7abc Automatically generate release notes 2023-01-15 13:34:36 +01:00
8 changed files with 313 additions and 36 deletions
+8 -5
View File
@@ -15,7 +15,7 @@ jobs:
BUILD_TYPE: Release
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Tag cmkr.cmake
if: ${{ startsWith(github.ref, 'refs/tags/') }}
@@ -38,28 +38,31 @@ jobs:
ctest -C ${{ env.BUILD_TYPE }} --verbose
- name: Upload artifacts
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: ${{ github.event.repository.name }}-${{ matrix.os }}
path: install/bin/*
- name: Get lowercase OS name
id: osname
uses: ASzc/change-string-case-action@v1
uses: ASzc/change-string-case-action@07c1e24a97f0951e13f88870b99c058fcf0b14cf # v5
if: ${{ startsWith(github.ref, 'refs/tags/') }}
with:
string: ${{ runner.os }}
- name: Compress artifacts
uses: vimtor/action-zip@v1
uses: vimtor/action-zip@26a249fb00d43ca98dad77a4b3838025fc226aa1 # v1.1
if: ${{ startsWith(github.ref, 'refs/tags/') }}
with:
files: install/bin/
dest: ${{ github.event.repository.name }}-${{ steps.osname.outputs.lowercase }}.zip
- name: Release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15
if: ${{ startsWith(github.ref, 'refs/tags/') }}
with:
prerelease: ${{ !startsWith(github.ref, 'refs/tags/v') || contains(github.ref, '-pre') }}
files: ${{ github.event.repository.name }}-${{ steps.osname.outputs.lowercase }}.zip
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+1 -1
View File
@@ -22,7 +22,7 @@ project(cmkr
LANGUAGES
CXX
VERSION
0.2.18
0.2.22
DESCRIPTION
"CMakeLists generator from TOML"
)
+1 -1
View File
@@ -4,7 +4,7 @@ cmkr-include = false
[project]
name = "cmkr"
version = "0.2.18"
version = "0.2.22"
description = "CMakeLists generator from TOML"
languages = ["CXX"]
include-after = [
+1 -1
View File
@@ -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.18" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE)
set(CMKR_TAG "v0.2.22" 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
+42 -2
View File
@@ -72,7 +72,7 @@ unix = "UNIX"
bsd = "CMAKE_SYSTEM_NAME MATCHES \"BSD\""
linux = "CMAKE_SYSTEM_NAME MATCHES \"Linux\""
gcc = "CMAKE_CXX_COMPILER_ID STREQUAL \"GNU\" OR CMAKE_C_COMPILER_ID STREQUAL \"GNU\""
clang = "CMAKE_CXX_COMPILER_ID MATCHES \"Clang\" OR CMAKE_C_COMPILER_ID MATCHES \"Clang\""
clang = "(CMAKE_CXX_COMPILER_ID MATCHES \"Clang\" AND NOT CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES \"^MSVC$\") OR (CMAKE_C_COMPILER_ID MATCHES \"Clang\" AND NOT CMAKE_C_COMPILER_FRONTEND_VARIANT MATCHES \"^MSVC$\")"
msvc = "MSVC"
root = "CMKR_ROOT_PROJECT"
x64 = "CMAKE_SIZEOF_VOID_P EQUAL 8"
@@ -94,6 +94,19 @@ include-before = ["cmake/before-subdir.cmake"]
include-after = ["cmake/after-subdir.cmake"]
```
## Options
```toml
[options]
MYPROJECT_BUILD_TESTS = false
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).
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]`).
## Variables
```toml
@@ -137,6 +150,7 @@ components = ["mycomponent"]
condition = "mycondition"
git = "https://github.com/myuser/gitcontent"
tag = "v0.1"
shallow = false
[fetch-content.svncontent]
condition = "mycondition"
@@ -146,7 +160,10 @@ rev = "svn_rev"
[fetch-content.urlcontent]
condition = "mycondition"
url = "https://content-host.com/urlcontent.zip"
hash = "123123123123"
# These are equivalent, supported algorithms:
# md5, sha1, sha224, sha256, sha384, sha512, sha3_224, sha3_256, sha3_384, sha3_512
hash = "SHA1 502a4e25b8b209889c99c7fa0732102682c2e4ff"
sha1 = "502a4e25b8b209889c99c7fa0732102682c2e4ff"
```
## Targets
@@ -195,6 +212,29 @@ CXX_STANDARD_REQUIRED = true
FOLDER = "MyFolder"
```
## Templates
To avoid repeating yourself you can create your own target type and use it in your targets:
```toml
[template.example]
condition = "MYPROJECT_BUILD_EXAMPLES"
type = "executable"
link-libraries = ["myproject::mylib"]
add-function = ""
pass-sources = false
# Properties from the template are merged with the ones here
[target.myexample]
type = "example"
sources = ["src/myexample.cpp"]
```
The properties declared on a `template` are the same as the ones you use for targets. The only exceptions are:
- `add-function`: Specifies a custom add function. Projects like [pybind11](https://pybind11.readthedocs.io/en/stable/cmake/index.html#new-findpython-mode) have their own `add_xxx` function, which you can specify here.
- `pass-sources`: Pass sources directly to the add function instead of using `target_sources`.
## Tests and installation (unfinished)
**Note**: The `[[test]]` and `[[install]]` are unfinished features and will likely change in a future release.
+6 -4
View File
@@ -16,16 +16,16 @@ using ConditionVector = Condition<std::vector<std::string>>;
struct Variable {
std::string name;
std::string comment;
mpark::variant<bool, std::string> val;
std::string help;
mpark::variant<bool, std::string> value;
bool cache = false;
bool force = false;
};
struct Option {
std::string name;
std::string comment;
bool val = false;
std::string help;
mpark::variant<bool, std::string> value;
};
struct Package {
@@ -180,6 +180,7 @@ struct Project {
std::string project_version;
std::string project_description;
std::vector<std::string> project_languages;
bool project_allow_unknown_languages = false;
MsvcRuntimeType project_msvc_runtime = msvc_last;
Condition<std::string> cmake_before;
Condition<std::string> cmake_after;
@@ -199,6 +200,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;
};
bool is_root_path(const std::string &path);
+197 -12
View File
@@ -14,6 +14,35 @@
namespace cmkr {
namespace gen {
/*
Location: CMake/share/cmake-3.26/Modules
rg "set\(CMAKE_(.+)_SOURCE_FILE_EXTENSIONS"
Links:
- https://gitlab.kitware.com/cmake/cmake/-/issues/24340
- https://cmake.org/cmake/help/latest/command/enable_language.html
*/
static tsl::ordered_map<std::string, std::vector<std::string>> known_languages = {
{"ASM", {".s", ".S", ".asm", ".abs", ".msa", ".s90", ".s43", ".s85", ".s51"}},
{"ASM-ATT", {".s", ".asm"}},
{"ASM_MARMASM", {".asm"}},
{"ASM_MASM", {".asm"}},
{"ASM_NASM", {".nasm", ".asm"}},
{"C", {".c", ".m"}},
{"CSharp", {".cs"}},
{"CUDA", {".cu"}},
{"CXX", {".C", ".M", ".c++", ".cc", ".cpp", ".cxx", ".m", ".mm", ".mpp", ".CPP", ".ixx", ".cppm"}},
{"Fortran", {".f", ".F", ".fpp", ".FPP", ".f77", ".F77", ".f90", ".F90", ".for", ".For", ".FOR", ".f95", ".F95", ".cuf", ".CUF"}},
{"HIP", {".hip"}},
{"ISPC", {".ispc"}},
{"Java", {".java"}},
{"OBJC", {".m"}},
{"OBJCXX", {".M", ".m", ".mm"}},
{"RC", {".rc", ".RC"}},
{"Swift", {".swift"}},
};
static std::string format(const char *format, tsl::ordered_map<std::string, std::string> variables) {
std::string s = format;
for (const auto &itr : variables) {
@@ -90,6 +119,19 @@ static void create_file(const fs::path &path, const std::string &contents) {
ofs << contents;
}
static std::string read_file(const fs::path &path) {
std::ifstream ifs(path, std::ios::binary);
if (!ifs) {
throw std::runtime_error("Failed to read " + path.string());
}
std::string contents;
ifs.seekg(0, std::ios::end);
contents.resize(ifs.tellg());
ifs.seekg(0, std::ios::beg);
ifs.read(&contents[0], contents.size());
return contents;
}
// CMake target name rules: https://cmake.org/cmake/help/latest/policy/CMP0037.html [A-Za-z0-9_.+\-]
// TOML bare keys: non-empty strings composed only of [A-Za-z0-9_-]
// We replace all non-TOML bare key characters with _
@@ -106,18 +148,82 @@ static std::string escape_project_name(const std::string &name) {
return escaped;
}
static void generate_gitfile(const char *gitfile, const std::vector<std::string> &desired_lines) {
// Reference: https://github.com/github/linguist/blob/master/docs/overrides.md#summary
auto lines = desired_lines;
auto generate = [&lines](const char *newline) {
std::string generated;
generated += "# cmkr";
generated += newline;
for (const auto &line : lines) {
generated += line;
generated += newline;
}
return generated;
};
if (!fs::exists(gitfile)) {
create_file(gitfile, generate("\n"));
} else {
auto contents = read_file(gitfile);
std::string line;
auto cr = 0, lf = 0;
auto flush_line = [&line, &lines]() {
auto itr = std::find(lines.begin(), lines.end(), line);
if (itr != lines.end()) {
lines.erase(itr);
}
line.clear();
};
for (size_t i = 0; i < contents.length(); i++) {
if (contents[i] == '\r') {
cr++;
continue;
}
if (contents[i] == '\n') {
lf++;
flush_line();
} else {
line += contents[i];
}
}
if (!line.empty()) {
flush_line();
}
if (!lines.empty()) {
// Append the cmkr .gitattributes using the detected newline
auto newline = cr == lf ? "\r\n" : "\n";
if (!contents.empty() && contents.back() != '\n') {
contents += newline;
}
contents += newline;
contents += generate(newline);
create_file(gitfile, contents);
}
}
}
void generate_project(const std::string &type) {
const auto name = escape_project_name(fs::current_path().stem().string());
if (fs::exists(fs::current_path() / "cmake.toml")) {
throw std::runtime_error("Cannot initialize a project when cmake.toml already exists!");
}
// Check if the folder is empty before creating any files
auto is_empty = fs::is_empty(fs::current_path());
// Automatically generate .gitattributes to not skew the statistics of the repo
generate_gitfile(".gitattributes", {"/**/CMakeLists.txt linguist-generated", "/**/cmkr.cmake linguist-vendored"});
// Generate .gitignore with reasonable defaults for CMake
generate_gitfile(".gitignore", {"build*/", "cmake-build*/", ".idea/", ".vscode/"});
tsl::ordered_map<std::string, std::string> variables = {
{"@name", name},
{"@type", type},
};
if (!fs::is_empty(fs::current_path())) {
if (!is_empty) {
// Make a backup of an existing CMakeLists.txt if it exists
std::error_code ec;
fs::rename("CMakeLists.txt", "CMakeLists.txt.bak", ec);
@@ -510,6 +616,17 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
auto is_root_project = parent_project == nullptr;
parser::Project project(parent_project, path, false);
for (auto const &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());
} else {
throw std::runtime_error("Unknown language '" + lang + "' specified");
}
}
}
Generator gen(project);
// Helper lambdas for more convenient CMake generation
@@ -616,7 +733,13 @@ 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, RawArg(Command::quote(opt.comment)), opt.val ? "ON" : "OFF");
std::string default_val;
if (opt.value.index() == 0) {
default_val = mpark::get<0>(opt.value) ? "ON" : "OFF";
} else {
default_val = mpark::get<1>(opt.value);
}
cmd("option")(opt.name, RawArg(Command::quote(opt.help)), default_val);
}
endl();
}
@@ -625,16 +748,16 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
comment("Variables");
for (const auto &set : project.variables) {
std::string set_val;
if (set.val.index() == 1) {
set_val = mpark::get<1>(set.val);
if (set.value.index() == 1) {
set_val = mpark::get<1>(set.value);
} else {
set_val = mpark::get<0>(set.val) ? "ON" : "OFF";
set_val = mpark::get<0>(set.value) ? "ON" : "OFF";
}
if (set.cache) {
auto typ = set.val.index() == 1 ? "STRING" : "BOOL";
auto typ = set.value.index() == 1 ? "STRING" : "BOOL";
auto force = set.force ? "FORCE" : "";
cmd("set")(set.name, set_val, typ, set.comment, force);
cmd("set")(set.name, set_val, typ, set.help, force);
} else {
cmd("set")(set.name, set_val);
}
@@ -837,10 +960,38 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
gen.conditional_cmake(subdir.cmake_after);
}
// The implicit default is ["C", "CXX"], so make sure this list isn't
// empty or projects without languages explicitly defined will error.
auto project_languages = project.project_languages;
if (project_languages.empty())
project_languages = {"C", "CXX"};
// All acceptable extensions based off our given languages.
tsl::ordered_set<std::string> project_extensions;
for (const auto &language : project_languages) {
auto itr = known_languages.find(language);
if (itr != known_languages.end()) {
project_extensions.insert(itr->second.begin(), itr->second.end());
}
}
auto contains_language_source = [&project_extensions](const std::vector<std::string> &sources) {
for (const auto &source : sources) {
auto extension = fs::path(source).extension().string();
if (project_extensions.count(extension) > 0) {
return true;
}
}
return false;
};
if (!project.targets.empty()) {
auto project_root = project.root();
for (size_t i = 0; i < project.targets.size(); i++) {
const auto &target = project.targets[i];
auto throw_target_error = [&target](const std::string &message) { throw std::runtime_error("[target." + target.name + "] " + message); };
const parser::Template *tmplate = nullptr;
std::unique_ptr<ConditionScope> tmplate_cs{};
@@ -944,12 +1095,46 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
auto sources = expand_cmake_paths(condition_sources, path, is_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");
throw_target_error(source_key + " wildcard found 0 files");
}
// Make sure there are source files for the languages used by the project
switch (target.type) {
case parser::target_executable:
case parser::target_library:
case parser::target_shared:
case parser::target_static:
case parser::target_object:
if (!contains_language_source(sources)) {
std::string extensions;
for (const auto &language : project_extensions) {
if (!extensions.empty()) {
extensions += " ";
}
extensions += language;
}
throw_target_error("No sources found with valid extensions (" + extensions + ")");
}
// Make sure relative source files exist
for (const auto &source : sources) {
auto var_index = source.find("${");
if (var_index != std::string::npos)
continue;
const auto &source_path = fs::path(path) / source;
if (!fs::exists(source_path)) {
throw_target_error("Source file does not exist " + source);
}
}
break;
default:
break;
}
if (sources_with_set) {
// This is a sanity check to make sure the unconditional sources are first
if (!condition.empty()) {
throw std::runtime_error("Unreachable code, make sure unconditional sources are first");
throw_target_error("Unreachable code, make sure unconditional sources are first");
}
cmd("set")(sources_var, sources);
sources_with_set = false;
@@ -962,7 +1147,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
if (tmplate != nullptr) {
if (target_type != parser::target_template) {
throw std::runtime_error("Unreachable code, unexpected target type for template");
throw_target_error("Unreachable code, unexpected target type for template");
}
target_type = tmplate->outline.type;
}
@@ -1010,7 +1195,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
target_scope = "PUBLIC";
break;
default:
throw std::runtime_error("Unimplemented enum value");
throw_target_error("Unimplemented enum value");
}
// Handle custom add commands from templates.
@@ -1091,7 +1276,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
for (const auto &propItr : properties) {
if (propItr.first == "MSVC_RUNTIME_LIBRARY") {
if (project_root->project_msvc_runtime == parser::msvc_last) {
throw std::runtime_error("You cannot set [target].msvc-runtime without setting the root [project].msvc-runtime");
throw_target_error("You cannot set msvc-runtime without setting the root [project].msvc-runtime");
}
}
}
+57 -10
View File
@@ -265,7 +265,8 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
conditions["bsd"] = R"cmake(CMAKE_SYSTEM_NAME MATCHES "BSD")cmake";
conditions["linux"] = conditions["lunix"] = R"cmake(CMAKE_SYSTEM_NAME MATCHES "Linux")cmake";
conditions["gcc"] = R"cmake(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "GNU")cmake";
conditions["clang"] = R"cmake(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_C_COMPILER_ID MATCHES "Clang")cmake";
conditions["clang"] =
R"cmake((CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES "^MSVC$") OR (CMAKE_C_COMPILER_ID MATCHES "Clang" AND NOT CMAKE_C_COMPILER_FRONTEND_VARIANT MATCHES "^MSVC$"))cmake";
conditions["msvc"] = R"cmake(MSVC)cmake";
conditions["root"] = R"cmake(CMKR_ROOT_PROJECT)cmake";
conditions["x64"] = R"cmake(CMAKE_SIZEOF_VOID_P EQUAL 8)cmake";
@@ -288,6 +289,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
project.optional("version", project_version);
project.optional("description", project_description);
project.optional("languages", project_languages);
project.optional("allow-unknown-languages", project_allow_unknown_languages);
project.optional("cmake-before", cmake_before);
project.optional("cmake-after", cmake_after);
project.optional("include-before", include_before);
@@ -345,18 +347,18 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
s.name = itr.first;
const auto &value = itr.second;
if (value.is_boolean()) {
s.val = value.as_boolean();
s.value = value.as_boolean();
} else if (value.is_string()) {
s.val = value.as_string();
s.value = value.as_string();
} else {
auto &setting = checker.create(value);
setting.optional("comment", s.comment);
setting.optional("help", s.help);
if (setting.contains("value")) {
const auto &v = setting.find("value");
if (v.is_boolean()) {
s.val = v.as_boolean();
s.value = v.as_boolean();
} else {
s.val = v.as_string();
s.value = v.as_string();
}
}
setting.optional("cache", s.cache);
@@ -374,13 +376,37 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
o.name = itr.first;
const auto &value = itr.second;
if (value.is_boolean()) {
o.val = value.as_boolean();
} else {
o.value = value.as_boolean();
} else if (value.is_string()) {
auto str = std::string(value.as_string());
if (str == "root") {
o.value = std::string("${CMKR_ROOT_PROJECT}");
} else {
throw_key_error("Unsupported option value '" + str + "'", str, value);
}
} else if (value.is_table()) {
auto &option = checker.create(value);
option.optional("comment", o.comment);
option.optional("value", o.val);
option.optional("help", o.help);
if (option.contains("value")) {
const auto &ovalue = option.find("value");
if (ovalue.is_boolean()) {
o.value = ovalue.as_boolean();
} else if (ovalue.is_string()) {
auto str = std::string(ovalue.as_string());
if (str == "root") {
o.value = std::string("${CMKR_ROOT_PROJECT}");
} else {
throw_key_error("Unsupported option value '" + str + "'", str, value);
}
} else {
throw_key_error(toml::concat_to_string("Unsupported value type: ", ovalue.type()), "value", value);
}
}
} else {
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);
}
}
@@ -659,6 +685,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
t.optional("add-function", tmplate.add_function);
t.optional("pass-sources-to-add-function", tmplate.pass_sources_to_add_function);
t.optional("pass-sources", tmplate.pass_sources_to_add_function);
templates.push_back(tmplate);
}
@@ -741,6 +768,26 @@ const Project *Project::root() const {
return root;
}
bool Project::cmake_minimum_version(int major, int minor) const {
// NOTE: this code is like pulling teeth, sorry
auto root_version = root()->cmake_version;
puts(root_version.c_str());
auto range_index = root_version.find("...");
if (range_index != std::string::npos) {
root_version.resize(range_index);
}
auto period_index = root_version.find('.');
auto root_major = atoi(root_version.substr(0, period_index).c_str());
int root_minor = 0;
if (period_index != std::string::npos) {
auto end_index = root_version.find('.', period_index + 1);
root_minor = atoi(root_version.substr(period_index + 1, end_index).c_str());
}
return std::tie(root_major, root_minor) >= std::tie(major, minor);
}
bool is_root_path(const std::string &path) {
const auto toml_path = fs::path(path) / "cmake.toml";
if (!fs::exists(toml_path)) {