From 459ebb14b56b07a9486040ee9348b96efdada846 Mon Sep 17 00:00:00 2001 From: Duncan Ogilvie Date: Thu, 30 Dec 2021 02:31:12 +0100 Subject: [PATCH] Support conditions everywhere Closes #36 --- include/project_parser.hpp | 14 ++++--- src/cmake_generator.cpp | 78 ++++++++++++++++++++++---------------- src/project_parser.cpp | 13 +++++++ 3 files changed, 67 insertions(+), 38 deletions(-) diff --git a/include/project_parser.hpp b/include/project_parser.hpp index dc25a61..bebab1a 100644 --- a/include/project_parser.hpp +++ b/include/project_parser.hpp @@ -8,6 +8,11 @@ namespace cmkr { namespace parser { +template +using Condition = tsl::ordered_map; + +using ConditionVector = Condition>; + 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 -using Condition = tsl::ordered_map; - -using ConditionVector = Condition>; - struct Target { std::string name; TargetType type = {}; @@ -101,6 +102,7 @@ struct Target { struct Test { std::string name; + std::string condition; std::vector configurations; std::string working_directory; std::string command; @@ -108,6 +110,7 @@ struct Test { }; struct Install { + std::string condition; std::vector targets; std::vector files; std::vector dirs; @@ -126,6 +129,7 @@ struct Subdir { struct Content { std::string name; + std::string condition; tsl::ordered_map arguments; }; diff --git a/src/cmake_generator.cpp b/src/cmake_generator.cpp index 75242cb..5b314d4 100644 --- a/src/cmake_generator.cpp +++ b/src/cmake_generator.cpp @@ -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 &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 &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,13 +915,9 @@ 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(); } + + endl(); } 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 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); } } diff --git a/src/project_parser.cpp b/src/project_parser.cpp index 00c7735..9e71b2f 100644 --- a/src/project_parser.cpp +++ b/src/project_parser.cpp @@ -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);