Merge pull request #46 from build-cpp/expanded-conditions

Support conditions everywhere
main
Duncan Ogilvie 3 years ago committed by GitHub
commit 5a6ac44fcf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -47,6 +47,8 @@ arch64 = "CMAKE_SIZEOF_VOID_P EQUALS 8"
arch32 = "CMAKE_SIZEOF_VOID_P EQUALS 4" arch32 = "CMAKE_SIZEOF_VOID_P EQUALS 4"
``` ```
This will make the `arch64` and `arch32` conditions available with their respective CMake expressions.
You can also prefix most keys with `condition.` to represent a conditional: You can also prefix most keys with `condition.` to represent a conditional:
```toml ```toml
@ -56,7 +58,7 @@ sources = ["src/main.cpp"]
windows.sources = ["src/windows_specific.cpp"] windows.sources = ["src/windows_specific.cpp"]
``` ```
This will make the `arch64` and `arch32` conditions available with their respective CMake expressions. The following conditions are predefined (you can override them if you desire): The following conditions are predefined (you can override them if you desire):
```toml ```toml
[conditions] [conditions]
@ -74,7 +76,7 @@ msvc = "MSVC"
```toml ```toml
[subdir.mysubdir] [subdir.mysubdir]
condition = "linux" condition = "mycondition"
cmake-before = """ cmake-before = """
message(STATUS "CMake injected before the add_subdirectory() call" message(STATUS "CMake injected before the add_subdirectory() call"
""" """
@ -101,11 +103,8 @@ To specify package features you can use the following syntax: `imgui[docking-exp
## Packages ## Packages
```toml ```toml
[find-package]
mypackage = { version = "1.0", required = true, config = true, components = ["mycomponent"] }
# Alternative syntax
[find-package.mypackage] [find-package.mypackage]
condition = "mycondition"
version = "1.0" version = "1.0"
required = true required = true
config = true config = true
@ -117,22 +116,27 @@ components = ["mycomponent"]
**Note**: The `[fetch-content]` feature is unpolished and will likely change in a future release. **Note**: The `[fetch-content]` feature is unpolished and will likely change in a future release.
```toml ```toml
[fetch-content]
gitcontent = { git = "https://github.com/myuser/gitcontent", tag = "v0.1" }
svncontent = { svn = "https://svn-host.com/url", rev = "svn_rev" }
urlcontent = { url = "https://content-host.com/urlcontent.zip", hash = "123123123123" }
# Alternative syntax
[fetch-content.gitcontent] [fetch-content.gitcontent]
condition = "mycondition"
git = "https://github.com/myuser/gitcontent" git = "https://github.com/myuser/gitcontent"
tag = "v0.1" tag = "v0.1"
[fetch-content.svncontent]
condition = "mycondition"
svn = "https://svn-host.com/url"
rev = "svn_rev"
[fetch-content.urlcontent]
condition = "mycondition"
url = "https://content-host.com/urlcontent.zip"
hash = "123123123123"
``` ```
## Targets ## Targets
```toml ```toml
[target.mytarget] [target.mytarget]
condition = "linux" condition = "mycondition"
alias = "mytarget::mytarget" alias = "mytarget::mytarget"
type = "static" # executable, shared (DLL), static, interface, object, library, custom type = "static" # executable, shared (DLL), static, interface, object, library, custom
headers = ["src/mytarget.h"] headers = ["src/mytarget.h"]
@ -180,6 +184,7 @@ FOLDER = "MyFolder"
```toml ```toml
# You can declare as many as you want like this, but the name has to be unique # You can declare as many as you want like this, but the name has to be unique
[[test]] [[test]]
condition = "mycondition"
name = "mytest" name = "mytest"
command = "$<TARGET_FILE:mytest>" command = "$<TARGET_FILE:mytest>"
arguments = ["arg1", "arg2"] arguments = ["arg1", "arg2"]
@ -189,6 +194,7 @@ working-directory = "mytest-dir"
```toml ```toml
[[install]] [[install]]
condition = "mycondition"
targets = ["mytarget", "mytest"] targets = ["mytarget", "mytest"]
destination = ["bin"] destination = ["bin"]
files = ["content/my.png"] files = ["content/my.png"]

@ -8,6 +8,11 @@
namespace cmkr { namespace cmkr {
namespace parser { namespace parser {
template <typename T>
using Condition = tsl::ordered_map<std::string, T>;
using ConditionVector = Condition<std::vector<std::string>>;
struct Setting { struct Setting {
std::string name; std::string name;
std::string comment; std::string comment;
@ -24,6 +29,7 @@ struct Option {
struct Package { struct Package {
std::string name; std::string name;
std::string condition;
std::string version; std::string version;
bool required = true; bool required = true;
bool config = false; bool config = false;
@ -52,11 +58,6 @@ enum TargetType {
target_object, target_object,
}; };
template <typename T>
using Condition = tsl::ordered_map<std::string, T>;
using ConditionVector = Condition<std::vector<std::string>>;
struct Target { struct Target {
std::string name; std::string name;
TargetType type = {}; TargetType type = {};
@ -101,6 +102,7 @@ struct Target {
struct Test { struct Test {
std::string name; std::string name;
std::string condition;
std::vector<std::string> configurations; std::vector<std::string> configurations;
std::string working_directory; std::string working_directory;
std::string command; std::string command;
@ -108,6 +110,7 @@ struct Test {
}; };
struct Install { struct Install {
std::string condition;
std::vector<std::string> targets; std::vector<std::string> targets;
std::vector<std::string> files; std::vector<std::string> files;
std::vector<std::string> dirs; std::vector<std::string> dirs;
@ -126,6 +129,7 @@ struct Subdir {
struct Content { struct Content {
std::string name; std::string name;
std::string condition;
tsl::ordered_map<std::string, std::string> arguments; tsl::ordered_map<std::string, std::string> arguments;
}; };

@ -316,10 +316,10 @@ static std::string tolf(const std::string &str) {
}; };
struct Generator { struct Generator {
Generator(parser::Project &project) : project(project) {} Generator(const parser::Project &project) : project(project) {}
Generator(const Generator &) = delete; Generator(const Generator &) = delete;
parser::Project &project; const parser::Project &project;
std::stringstream ss; std::stringstream ss;
int indent = 0; int indent = 0;
@ -383,11 +383,7 @@ struct Generator {
for (const auto &itr : value) { for (const auto &itr : value) {
const auto &condition = itr.first; const auto &condition = itr.first;
if (!condition.empty()) { if (!condition.empty()) {
if (project.conditions.count(condition) == 0) { cmd("if", condition)(RawArg(project.conditions.at(condition)));
// TODO: somehow print line number information here?
throw std::runtime_error("Unknown condition '" + condition + "'");
}
cmd("if", condition)(RawArg(project.conditions[condition]));
} }
if (!itr.second.empty()) { if (!itr.second.empty()) {
@ -403,6 +399,27 @@ struct Generator {
} }
}; };
struct ConditionScope {
Generator &gen;
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;
}
}
ConditionScope(const ConditionScope &) = delete;
ConditionScope(ConditionScope &&) = delete;
~ConditionScope() {
if (endif) {
gen.cmd("endif")();
}
}
};
static bool vcpkg_valid_identifier(const std::string &name) { static bool vcpkg_valid_identifier(const std::string &name) {
// prn|aux|nul|con|lpt[1-9]|com[1-9]|core|default // prn|aux|nul|con|lpt[1-9]|com[1-9]|core|default
auto is_reserved = [](const std::string &s) { auto is_reserved = [](const std::string &s) {
@ -679,7 +696,14 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
if (!project.contents.empty()) { if (!project.contents.empty()) {
cmd("include")("FetchContent").endl(); cmd("include")("FetchContent").endl();
for (const auto &content : project.contents) { for (const auto &content : project.contents) {
cmd("message")("STATUS", "Fetching " + content.name + "..."); ConditionScope cs(gen, content.condition);
std::string version_info = "";
if (content.arguments.contains("GIT_TAG")) {
version_info = " (" + content.arguments.at("GIT_TAG") + ")";
} else if (content.arguments.contains("SVN_REVISION")) {
version_info = " (" + content.arguments.at("SVN_REVISION") + ")";
}
cmd("message")("STATUS", "Fetching " + content.name + version_info + "...");
ss << "FetchContent_Declare(\n\t" << content.name << "\n"; ss << "FetchContent_Declare(\n\t" << content.name << "\n";
for (const auto &arg : content.arguments) { for (const auto &arg : content.arguments) {
ss << "\t" << arg.first << "\n\t\t" << arg.second << "\n"; ss << "\t" << arg.first << "\n\t\t" << arg.second << "\n";
@ -698,6 +722,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
auto required = dep.required ? "REQUIRED" : ""; auto required = dep.required ? "REQUIRED" : "";
auto config = dep.config ? "CONFIG" : ""; auto config = dep.config ? "CONFIG" : "";
auto components = std::make_pair("COMPONENTS", dep.components); auto components = std::make_pair("COMPONENTS", dep.components);
ConditionScope cs(gen, dep.condition);
cmd("find_package")(dep.name, version, required, config, components).endl(); cmd("find_package")(dep.name, version, required, config, components).endl();
} }
} }
@ -727,13 +752,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
endl(); endl();
} }
for (const auto &subdir : project.subdirs) { for (const auto &subdir : project.subdirs) {
if (!subdir.condition.empty()) { ConditionScope cs(gen, subdir.condition);
const auto &condition = subdir.condition;
if (project.conditions.count(condition) == 0) {
throw std::runtime_error("Unknown condition '" + condition + "' for [subdir." + subdir.name + "]");
}
gen.cmd("if", condition)(RawArg(project.conditions[condition]));
}
gen.handle_condition(subdir.include_before, gen.handle_condition(subdir.include_before,
[&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); }); [&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
@ -743,23 +762,18 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
gen.handle_condition(subdir.include_after, [&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); }); 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.handle_condition(subdir.cmake_after, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
if (!subdir.condition.empty()) {
cmd("endif")();
}
} }
if (!project.targets.empty()) { if (!project.targets.empty()) {
for (const auto &target : project.targets) { for (size_t i = 0; i < project.targets.size(); i++) {
if (i > 0) {
endl();
}
const auto &target = project.targets[i];
comment("Target " + target.name); comment("Target " + target.name);
if (!target.condition.empty()) { ConditionScope cs(gen, target.condition);
const auto &condition = target.condition;
if (project.conditions.count(condition) == 0) {
throw std::runtime_error("Unknown condition '" + condition + "' for [target." + target.name + "]");
}
gen.cmd("if", condition)(RawArg(project.conditions[condition]));
}
cmd("set")("CMKR_TARGET", target.name); cmd("set")("CMKR_TARGET", target.name);
@ -901,13 +915,9 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
cmd("unset")("CMKR_TARGET"); cmd("unset")("CMKR_TARGET");
cmd("unset")("CMKR_SOURCES"); cmd("unset")("CMKR_SOURCES");
if (!target.condition.empty()) {
cmd("endif")();
}
endl();
} }
endl();
} }
if (!project.tests.empty()) { if (!project.tests.empty()) {
@ -922,6 +932,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
auto working_directory = std::make_pair("WORKING_DIRECTORY", dir); auto working_directory = std::make_pair("WORKING_DIRECTORY", dir);
auto command = std::make_pair("COMMAND", test.command); auto command = std::make_pair("COMMAND", test.command);
auto arguments = std::make_pair("", test.arguments); auto arguments = std::make_pair("", test.arguments);
ConditionScope cs(gen, test.condition);
cmd("add_test")(name, configurations, working_directory, command, arguments).endl(); cmd("add_test")(name, configurations, working_directory, command, arguments).endl();
} }
} }
@ -941,6 +952,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
auto configs = std::make_pair("CONFIGURATIONS", inst.configs); auto configs = std::make_pair("CONFIGURATIONS", inst.configs);
auto destination = std::make_pair("DESTINATION", inst.destination); auto destination = std::make_pair("DESTINATION", inst.destination);
auto component = std::make_pair("COMPONENT", inst.targets.empty() ? "" : inst.targets.front()); auto component = std::make_pair("COMPONENT", inst.targets.empty() ? "" : inst.targets.front());
ConditionScope cs(gen, inst.condition);
cmd("install")(targets, dirs, files, configs, destination, component); cmd("install")(targets, dirs, files, configs, destination, component);
} }
} }

@ -145,6 +145,11 @@ class TomlChecker {
} }
} }
throw std::runtime_error(format_key_error("Unknown key '" + ky + "'", ky, itr.second)); throw std::runtime_error(format_key_error("Unknown key '" + ky + "'", ky, itr.second));
} else if (ky == "condition") {
std::string condition = itr.second.as_string();
if (!conditions.contains(condition)) {
throw std::runtime_error(format_key_error("Unknown condition '" + condition + "'", condition, itr.second));
}
} }
} }
} }
@ -330,6 +335,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
p.version = itr.second.as_string(); p.version = itr.second.as_string();
} else { } else {
auto &pkg = checker.create(value); auto &pkg = checker.create(value);
pkg.optional("condition", p.condition);
pkg.optional("version", p.version); pkg.optional("version", p.version);
pkg.optional("required", p.required); pkg.optional("required", p.required);
pkg.optional("config", p.config); pkg.optional("config", p.config);
@ -347,6 +353,11 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
content.name = itr.first; content.name = itr.first;
for (const auto &argItr : itr.second.as_table()) { for (const auto &argItr : itr.second.as_table()) {
auto key = argItr.first; auto key = argItr.first;
if (key == "condition") {
content.condition = argItr.second.as_string();
continue;
}
if (key == "git") { if (key == "git") {
key = "GIT_REPOSITORY"; key = "GIT_REPOSITORY";
} else if (key == "tag") { } else if (key == "tag") {
@ -469,6 +480,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
auto &t = checker.create(value); auto &t = checker.create(value);
Test test; Test test;
t.required("name", test.name); t.required("name", test.name);
t.optional("condition", test.condition);
t.optional("configurations", test.configurations); t.optional("configurations", test.configurations);
t.optional("working-directory", test.working_directory); t.optional("working-directory", test.working_directory);
t.required("command", test.command); t.required("command", test.command);
@ -482,6 +494,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
for (const auto &value : is) { for (const auto &value : is) {
auto &i = checker.create(value); auto &i = checker.create(value);
Install inst; Install inst;
i.optional("condition", inst.condition);
i.optional("targets", inst.targets); i.optional("targets", inst.targets);
i.optional("files", inst.files); i.optional("files", inst.files);
i.optional("dirs", inst.dirs); i.optional("dirs", inst.dirs);

Loading…
Cancel
Save