From 459ebb14b56b07a9486040ee9348b96efdada846 Mon Sep 17 00:00:00 2001 From: Duncan Ogilvie Date: Thu, 30 Dec 2021 02:31:12 +0100 Subject: [PATCH 1/2] 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); From 9db82dfd057e293c79a21b502d2bdf46a66dca32 Mon Sep 17 00:00:00 2001 From: Duncan Ogilvie Date: Thu, 30 Dec 2021 02:43:17 +0100 Subject: [PATCH 2/2] Update toml reference --- docs/cmake-toml.md | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/docs/cmake-toml.md b/docs/cmake-toml.md index f5e826e..d00522b 100644 --- a/docs/cmake-toml.md +++ b/docs/cmake-toml.md @@ -47,6 +47,8 @@ arch64 = "CMAKE_SIZEOF_VOID_P EQUALS 8" 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: ```toml @@ -56,7 +58,7 @@ sources = ["src/main.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 [conditions] @@ -74,7 +76,7 @@ msvc = "MSVC" ```toml [subdir.mysubdir] -condition = "linux" +condition = "mycondition" cmake-before = """ 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 ```toml -[find-package] -mypackage = { version = "1.0", required = true, config = true, components = ["mycomponent"] } - -# Alternative syntax [find-package.mypackage] +condition = "mycondition" version = "1.0" required = true config = true @@ -117,22 +116,27 @@ components = ["mycomponent"] **Note**: The `[fetch-content]` feature is unpolished and will likely change in a future release. ```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] +condition = "mycondition" git = "https://github.com/myuser/gitcontent" 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 ```toml [target.mytarget] -condition = "linux" +condition = "mycondition" alias = "mytarget::mytarget" type = "static" # executable, shared (DLL), static, interface, object, library, custom headers = ["src/mytarget.h"] @@ -180,6 +184,7 @@ FOLDER = "MyFolder" ```toml # You can declare as many as you want like this, but the name has to be unique [[test]] +condition = "mycondition" name = "mytest" command = "$" arguments = ["arg1", "arg2"] @@ -189,6 +194,7 @@ working-directory = "mytest-dir" ```toml [[install]] +condition = "mycondition" targets = ["mytarget", "mytest"] destination = ["bin"] files = ["content/my.png"]