Support conditions everywhere

Closes #36
main
Duncan Ogilvie 3 years ago
parent 490869b9a2
commit 459ebb14b5

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

@ -316,10 +316,10 @@ static std::string tolf(const std::string &str) {
};
struct Generator {
Generator(parser::Project &project) : project(project) {}
Generator(const parser::Project &project) : project(project) {}
Generator(const Generator &) = delete;
parser::Project &project;
const parser::Project &project;
std::stringstream ss;
int indent = 0;
@ -383,11 +383,7 @@ struct Generator {
for (const auto &itr : value) {
const auto &condition = itr.first;
if (!condition.empty()) {
if (project.conditions.count(condition) == 0) {
// TODO: somehow print line number information here?
throw std::runtime_error("Unknown condition '" + condition + "'");
}
cmd("if", condition)(RawArg(project.conditions[condition]));
cmd("if", condition)(RawArg(project.conditions.at(condition)));
}
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) {
// prn|aux|nul|con|lpt[1-9]|com[1-9]|core|default
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()) {
cmd("include")("FetchContent").endl();
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";
for (const auto &arg : content.arguments) {
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 config = dep.config ? "CONFIG" : "";
auto components = std::make_pair("COMPONENTS", dep.components);
ConditionScope cs(gen, dep.condition);
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();
}
for (const auto &subdir : project.subdirs) {
if (!subdir.condition.empty()) {
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]));
}
ConditionScope cs(gen, subdir.condition);
gen.handle_condition(subdir.include_before,
[&](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.cmake_after, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
if (!subdir.condition.empty()) {
cmd("endif")();
}
}
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);
if (!target.condition.empty()) {
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]));
}
ConditionScope cs(gen, target.condition);
cmd("set")("CMKR_TARGET", target.name);
@ -901,14 +915,10 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
cmd("unset")("CMKR_TARGET");
cmd("unset")("CMKR_SOURCES");
if (!target.condition.empty()) {
cmd("endif")();
}
endl();
}
}
if (!project.tests.empty()) {
cmd("enable_testing")().endl();
@ -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 command = std::make_pair("COMMAND", test.command);
auto arguments = std::make_pair("", test.arguments);
ConditionScope cs(gen, test.condition);
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 destination = std::make_pair("DESTINATION", inst.destination);
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);
}
}

@ -145,6 +145,11 @@ class TomlChecker {
}
}
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();
} else {
auto &pkg = checker.create(value);
pkg.optional("condition", p.condition);
pkg.optional("version", p.version);
pkg.optional("required", p.required);
pkg.optional("config", p.config);
@ -347,6 +353,11 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
content.name = itr.first;
for (const auto &argItr : itr.second.as_table()) {
auto key = argItr.first;
if (key == "condition") {
content.condition = argItr.second.as_string();
continue;
}
if (key == "git") {
key = "GIT_REPOSITORY";
} else if (key == "tag") {
@ -469,6 +480,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
auto &t = checker.create(value);
Test test;
t.required("name", test.name);
t.optional("condition", test.condition);
t.optional("configurations", test.configurations);
t.optional("working-directory", test.working_directory);
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) {
auto &i = checker.create(value);
Install inst;
i.optional("condition", inst.condition);
i.optional("targets", inst.targets);
i.optional("files", inst.files);
i.optional("dirs", inst.dirs);

Loading…
Cancel
Save