From eed1e38407e0e5f6d8398602084f300bdb95b964 Mon Sep 17 00:00:00 2001 From: cursey Date: Wed, 5 Jan 2022 12:04:08 -0800 Subject: [PATCH 1/8] Add support for target templates --- include/project_parser.hpp | 1 + src/project_parser.cpp | 203 ++++++++++++++++++++---------- tests/templates/cmake.toml | 18 +++ tests/templates/src/templates.cpp | 9 ++ 4 files changed, 165 insertions(+), 66 deletions(-) create mode 100644 tests/templates/cmake.toml create mode 100644 tests/templates/src/templates.cpp diff --git a/include/project_parser.hpp b/include/project_parser.hpp index bebab1a..8c26b50 100644 --- a/include/project_parser.hpp +++ b/include/project_parser.hpp @@ -165,6 +165,7 @@ struct Project { std::vector installs; tsl::ordered_map conditions; std::vector subdirs; + std::vector templates; Project(const Project *parent, const std::string &path, bool build); }; diff --git a/src/project_parser.cpp b/src/project_parser.cpp index 0a693b2..9cef19d 100644 --- a/src/project_parser.cpp +++ b/src/project_parser.cpp @@ -96,6 +96,36 @@ class TomlChecker { visit(ky); } + template + void optional_append(const toml::key &ky, Condition &destination) { + // TODO: this algorithm in O(n) over the amount of keys, kinda bad + const auto &table = m_v.as_table(); + for (const auto &itr : table) { + const auto &key = itr.first; + const auto &value = itr.second; + T *dest = nullptr; + if (value.is_table()) { + if (value.contains(ky)) { + dest = &destination[key]; + } + } else if (key == ky) { + dest = &destination[""]; + } + if (dest != nullptr) { + const auto &items = toml::find(m_v, ky); + dest->insert(dest->end(), items.begin(), items.end()); + } + } + + // Handle visiting logic + for (const auto &itr : destination) { + if (!itr.first.empty()) { + m_conditionVisited.emplace(itr.first, true); + } + } + visit(ky); + } + template void optional(const toml::key &ky, T &destination) { // TODO: this currently doesn't allow you to get an optional map @@ -105,6 +135,17 @@ class TomlChecker { visit(ky); } + template + void optional_append(const toml::key &ky, T &destination) { + // TODO: this currently doesn't allow you to get an optional map + if (m_v.contains(ky)) { + const auto &items = toml::find(m_v, ky); + destination.insert(destination.end(), items.begin(), items.end()); + } + visit(ky); + } + + template void required(const toml::key &ky, T &destination) { destination = toml::find(m_v, ky); @@ -386,94 +427,124 @@ Project::Project(const Project *parent, const std::string &path, bool build) { throw std::runtime_error("[[bin]] has been renamed to [[target]]"); } - if (toml.contains("target")) { - const auto &ts = toml::find(toml, "target").as_table(); + if (toml.contains("target") || toml.contains("template")) { + auto is_template = true; - for (const auto &itr : ts) { - const auto &value = itr.second; + for (const auto &ts : {&toml::find(toml, "template"), &toml::find(toml, "target")}) { + for (const auto &itr : ts->as_table()) { + const auto &value = itr.second; + auto &t = checker.create(value); + std::string template_name; - Target target; - target.name = itr.first; + if (!is_template) { + t.optional("template", template_name); + } - auto &t = checker.create(value); - std::string type; - t.required("type", type); - target.type = to_enum(type, "target type"); + Target target; + auto from_template = false; - t.optional("headers", target.headers); - t.optional("sources", target.sources); + if (!template_name.empty()) { + for (const auto & template_ : templates) { + if (template_name == template_.name) { + from_template = true; + target = template_; + } + } - t.optional("compile-definitions", target.compile_definitions); - t.optional("private-compile-definitions", target.private_compile_definitions); + if (!from_template) { + throw std::runtime_error("Could not find template named " + template_name); + } + } - t.optional("compile-features", target.compile_features); - t.optional("private-compile-features", target.private_compile_features); + target.name = itr.first; - t.optional("compile-options", target.compile_options); - t.optional("private-compile-options", target.private_compile_options); + if (!from_template) { + std::string type; + t.required("type", type); + target.type = to_enum(type, "target type"); + } - t.optional("include-directories", target.include_directories); - t.optional("private-include-directories", target.private_include_directories); + t.optional_append("headers", target.headers); + t.optional_append("sources", target.sources); - t.optional("link-directories", target.link_directories); - t.optional("private-link-directories", target.private_link_directories); + t.optional_append("compile-definitions", target.compile_definitions); + t.optional_append("private-compile-definitions", target.private_compile_definitions); - t.optional("link-libraries", target.link_libraries); - t.optional("private-link-libraries", target.private_link_libraries); + t.optional_append("compile-features", target.compile_features); + t.optional_append("private-compile-features", target.private_compile_features); - t.optional("link-options", target.link_options); - t.optional("private-link-options", target.private_link_options); + t.optional_append("compile-options", target.compile_options); + t.optional_append("private-compile-options", target.private_compile_options); - t.optional("precompile-headers", target.precompile_headers); - t.optional("private-precompile-headers", target.private_precompile_headers); + t.optional_append("include-directories", target.include_directories); + t.optional_append("private-include-directories", target.private_include_directories); - if (!target.headers.empty()) { - auto &sources = target.sources.nth(0).value(); - const auto &headers = target.headers.nth(0)->second; - sources.insert(sources.end(), headers.begin(), headers.end()); - } + t.optional_append("link-directories", target.link_directories); + t.optional_append("private-link-directories", target.private_link_directories); - t.optional("condition", target.condition); - t.optional("alias", target.alias); + t.optional_append("link-libraries", target.link_libraries); + t.optional_append("private-link-libraries", target.private_link_libraries); - if (t.contains("properties")) { - auto store_property = [&target](const toml::key &k, const TomlBasicValue &v, const std::string &condition) { - if (v.is_array()) { - std::string property_list; - for (const auto &list_val : v.as_array()) { - if (!property_list.empty()) { - property_list += ';'; + t.optional_append("link-options", target.link_options); + t.optional_append("private-link-options", target.private_link_options); + + t.optional_append("precompile-headers", target.precompile_headers); + t.optional_append("private-precompile-headers", target.private_precompile_headers); + + if (!target.headers.empty()) { + auto &sources = target.sources.nth(0).value(); + const auto &headers = target.headers.nth(0)->second; + sources.insert(sources.end(), headers.begin(), headers.end()); + } + + t.optional("condition", target.condition); + t.optional("alias", target.alias); + + if (t.contains("properties")) { + auto store_property = [&target](const toml::key &k, const TomlBasicValue &v, const std::string &condition) { + if (v.is_array()) { + std::string property_list; + for (const auto &list_val : v.as_array()) { + if (!property_list.empty()) { + property_list += ';'; + } + property_list += list_val.as_string(); } - property_list += list_val.as_string(); + target.properties[condition][k] = property_list; + } else if (v.is_boolean()) { + target.properties[condition][k] = v.as_boolean() ? "ON" : "OFF"; + } else { + target.properties[condition][k] = v.as_string(); } - target.properties[condition][k] = property_list; - } else if (v.is_boolean()) { - target.properties[condition][k] = v.as_boolean() ? "ON" : "OFF"; - } else { - target.properties[condition][k] = v.as_string(); - } - }; - - const auto &props = t.find("properties").as_table(); - for (const auto &propKv : props) { - const auto &k = propKv.first; - const auto &v = propKv.second; - if (v.is_table()) { - for (const auto &condKv : v.as_table()) { - store_property(condKv.first, condKv.second, k); + }; + + const auto &props = t.find("properties").as_table(); + for (const auto &propKv : props) { + const auto &k = propKv.first; + const auto &v = propKv.second; + if (v.is_table()) { + for (const auto &condKv : v.as_table()) { + store_property(condKv.first, condKv.second, k); + } + } else { + store_property(k, v, ""); } - } else { - store_property(k, v, ""); } } - } - t.optional("cmake-before", target.cmake_before); - t.optional("cmake-after", target.cmake_after); - t.optional("include-before", target.include_before); - t.optional("include-after", target.include_after); + t.optional("cmake-before", target.cmake_before); + t.optional("cmake-after", target.cmake_after); + t.optional_append("include-before", target.include_before); + t.optional_append("include-after", target.include_after); + + if (is_template) { + templates.push_back(target); + } else { + targets.push_back(target); + } + } - targets.push_back(target); + is_template = false; } } diff --git a/tests/templates/cmake.toml b/tests/templates/cmake.toml new file mode 100644 index 0000000..e54731e --- /dev/null +++ b/tests/templates/cmake.toml @@ -0,0 +1,18 @@ +# A project using templates. + +[project] +name = "templates" +description = "Template example" + +[template.app] +type = "executable" +sources = ["src/templates.cpp"] +compile-definitions = ["IS_APP=true"] + +[target.app-a] +template = "app" +compile-definitions = ["APP_A"] + +[target.app-b] +template = "app" +compile-definitions = ["APP_B"] diff --git a/tests/templates/src/templates.cpp b/tests/templates/src/templates.cpp new file mode 100644 index 0000000..57d0797 --- /dev/null +++ b/tests/templates/src/templates.cpp @@ -0,0 +1,9 @@ +#include + +int main() { +#if defined(APP_A) + puts("Hello from app A!"); +#elif defined(APP_B) + puts("Hello from app B!"); +#endif +} From 61d5e64d87b8d1e0f1b21c6843cdbe404e8c5dec Mon Sep 17 00:00:00 2001 From: cursey Date: Wed, 5 Jan 2022 12:48:43 -0800 Subject: [PATCH 2/8] Fix parser expecting to always find templates --- src/project_parser.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/project_parser.cpp b/src/project_parser.cpp index 9cef19d..abe8ae5 100644 --- a/src/project_parser.cpp +++ b/src/project_parser.cpp @@ -428,9 +428,14 @@ Project::Project(const Project *parent, const std::string &path, bool build) { } if (toml.contains("target") || toml.contains("template")) { + const toml::basic_value empty_templates_table = toml::table(); + auto tables = { + toml.contains("template") ? &toml::find(toml, "template") : &empty_templates_table, + &toml::find(toml, "target") + }; auto is_template = true; - for (const auto &ts : {&toml::find(toml, "template"), &toml::find(toml, "target")}) { + for (const auto &ts : tables) { for (const auto &itr : ts->as_table()) { const auto &value = itr.second; auto &t = checker.create(value); From 2b7ee72e8642e25f1d0ba4bb15a8747ab4f5a02c Mon Sep 17 00:00:00 2001 From: cursey Date: Mon, 10 Jan 2022 23:27:48 -0800 Subject: [PATCH 3/8] Separate template parsing and generation * Reuse type for specifying template to use * Add support for template add-function * Add support for template pass-sources-to-add-function --- include/enum_helper.hpp | 7 +- include/project_parser.hpp | 11 +- src/cmake_generator.cpp | 133 +++++++++++++++--- src/project_parser.cpp | 233 ++++++++++++------------------- tests/driver/cmake.toml | 19 +++ tests/driver/cmake/FindWdk.cmake | 179 ++++++++++++++++++++++++ tests/driver/src/DriverEntry.cpp | 5 + tests/templates/cmake.toml | 4 +- 8 files changed, 423 insertions(+), 168 deletions(-) create mode 100644 tests/driver/cmake.toml create mode 100644 tests/driver/cmake/FindWdk.cmake create mode 100644 tests/driver/src/DriverEntry.cpp diff --git a/include/enum_helper.hpp b/include/enum_helper.hpp index 7aafd78..93d88d9 100644 --- a/include/enum_helper.hpp +++ b/include/enum_helper.hpp @@ -4,6 +4,7 @@ #include #include #include +#include // This is the type that will hold all the strings. // Each enumeration type will declare its own specialization. @@ -12,7 +13,7 @@ // be no definition of a generic version). template struct enumStrings { - static char const *data[]; + static std::vector data; }; // This is a utility type. @@ -43,8 +44,8 @@ std::istream &operator>>(std::istream &str, enumRefHolder const &data) { // These two can be made easier to read in C++11 // using std::begin() and std::end() // - static auto begin = std::begin(enumStrings::data); - static auto end = std::end(enumStrings::data); + auto begin = std::begin(enumStrings::data); + auto end = std::end(enumStrings::data); auto find = std::find(begin, end, value); if (find != end) { diff --git a/include/project_parser.hpp b/include/project_parser.hpp index 8c26b50..bd2bafa 100644 --- a/include/project_parser.hpp +++ b/include/project_parser.hpp @@ -56,11 +56,14 @@ enum TargetType { target_interface, target_custom, target_object, + target_template, + target_COUNT, }; struct Target { std::string name; TargetType type = {}; + std::string type_string; ConditionVector headers; ConditionVector sources; @@ -100,6 +103,12 @@ struct Target { ConditionVector include_after; }; +struct Template { + Target outline; + std::string add_function; + bool pass_sources_to_add_function = false; +}; + struct Test { std::string name; std::string condition; @@ -160,12 +169,12 @@ struct Project { std::vector packages; Vcpkg vcpkg; std::vector contents; + std::vector