mirror of
https://github.com/build-cpp/cmkr
synced 2026-06-08 13:22:55 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a499df84b1 | |||
| 49440f99e0 | |||
| 0c19c3da0c | |||
| 233cadadd0 | |||
| 58a5a935e5 | |||
| e69427bf94 | |||
| bc6359805d | |||
| 8dcc11e349 | |||
| 51dc49e6f1 | |||
| 7d27c28b59 | |||
| ae6bea3b58 | |||
| 5a0eda7abc |
@@ -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 }}
|
||||
|
||||
Generated
+1
-1
@@ -22,7 +22,7 @@ project(cmkr
|
||||
LANGUAGES
|
||||
CXX
|
||||
VERSION
|
||||
0.2.18
|
||||
0.2.19
|
||||
DESCRIPTION
|
||||
"CMakeLists generator from TOML"
|
||||
)
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ cmkr-include = false
|
||||
|
||||
[project]
|
||||
name = "cmkr"
|
||||
version = "0.2.18"
|
||||
version = "0.2.19"
|
||||
description = "CMakeLists generator from TOML"
|
||||
languages = ["CXX"]
|
||||
include-after = [
|
||||
|
||||
+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.18" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE)
|
||||
set(CMKR_TAG "v0.2.19" 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
|
||||
|
||||
+16
-2
@@ -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,16 @@ 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." }
|
||||
```
|
||||
|
||||
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 options automatically gets a corresponding [condition](#conditions).
|
||||
|
||||
## Variables
|
||||
|
||||
```toml
|
||||
@@ -137,6 +147,7 @@ components = ["mycomponent"]
|
||||
condition = "mycondition"
|
||||
git = "https://github.com/myuser/gitcontent"
|
||||
tag = "v0.1"
|
||||
shallow = false
|
||||
|
||||
[fetch-content.svncontent]
|
||||
condition = "mycondition"
|
||||
@@ -146,7 +157,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
|
||||
|
||||
@@ -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;
|
||||
bool value = false;
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
+87
-6
@@ -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) {
|
||||
@@ -510,6 +539,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("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 +656,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, RawArg(Command::quote(opt.comment)), opt.val ? "ON" : "OFF");
|
||||
cmd("option")(opt.name, RawArg(Command::quote(opt.help)), opt.value ? "ON" : "OFF");
|
||||
}
|
||||
endl();
|
||||
}
|
||||
@@ -625,16 +665,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,6 +877,31 @@ 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++) {
|
||||
@@ -946,6 +1011,22 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
auto source_key = condition.empty() ? "sources" : (condition + ".sources");
|
||||
throw std::runtime_error(target.name + " " + 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)) {
|
||||
throw std::runtime_error("There were no source files linked within the target " + target.name);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (sources_with_set) {
|
||||
// This is a sanity check to make sure the unconditional sources are first
|
||||
if (!condition.empty()) {
|
||||
|
||||
+33
-9
@@ -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,14 @@ 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();
|
||||
o.value = value.as_boolean();
|
||||
} else {
|
||||
auto &option = checker.create(value);
|
||||
option.optional("comment", o.comment);
|
||||
option.optional("value", o.val);
|
||||
option.optional("help", o.help);
|
||||
option.optional("value", o.value);
|
||||
}
|
||||
options.push_back(o);
|
||||
conditions.emplace(o.name, o.name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -659,6 +662,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 +745,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)) {
|
||||
|
||||
Reference in New Issue
Block a user