mirror of
https://github.com/build-cpp/cmkr
synced 2026-06-08 13:22:55 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ea49a3acb7 | |||
| 257e14c869 | |||
| 7508816fe7 | |||
| 45678bc5bc | |||
| b869e5646f |
Generated
+1
-1
@@ -22,7 +22,7 @@ project(cmkr
|
||||
LANGUAGES
|
||||
CXX
|
||||
VERSION
|
||||
0.2.19
|
||||
0.2.20
|
||||
DESCRIPTION
|
||||
"CMakeLists generator from TOML"
|
||||
)
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ cmkr-include = false
|
||||
|
||||
[project]
|
||||
name = "cmkr"
|
||||
version = "0.2.19"
|
||||
version = "0.2.20"
|
||||
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.19" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE)
|
||||
set(CMKR_TAG "v0.2.20" 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
|
||||
|
||||
+4
-1
@@ -100,9 +100,12 @@ include-after = ["cmake/after-subdir.cmake"]
|
||||
[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 options 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).
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ struct Variable {
|
||||
struct Option {
|
||||
std::string name;
|
||||
std::string help;
|
||||
bool value = false;
|
||||
mpark::variant<bool, std::string> value;
|
||||
};
|
||||
|
||||
struct Package {
|
||||
|
||||
+83
-2
@@ -119,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 _
|
||||
@@ -135,12 +148,74 @@ 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()) {
|
||||
printf("erase:%s\n", line.c_str());
|
||||
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!");
|
||||
}
|
||||
|
||||
// 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},
|
||||
@@ -656,7 +731,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.help)), opt.value ? "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();
|
||||
}
|
||||
@@ -892,7 +973,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
|
||||
}
|
||||
}
|
||||
|
||||
auto contains_language_source = [&project_extensions](const std::vector<std::string>& sources) {
|
||||
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) {
|
||||
|
||||
+25
-2
@@ -377,10 +377,33 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
|
||||
const auto &value = itr.second;
|
||||
if (value.is_boolean()) {
|
||||
o.value = value.as_boolean();
|
||||
} else {
|
||||
} 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("help", o.help);
|
||||
option.optional("value", o.value);
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user