diff --git a/include/project_parser.hpp b/include/project_parser.hpp index e77b957..c8f8186 100644 --- a/include/project_parser.hpp +++ b/include/project_parser.hpp @@ -133,6 +133,7 @@ struct Install { struct Subdir { std::string name; std::string condition; + Condition cmake_before; Condition cmake_after; ConditionVector include_before; @@ -143,6 +144,11 @@ struct Content { std::string name; std::string condition; tsl::ordered_map arguments; + + Condition cmake_before; + Condition cmake_after; + ConditionVector include_before; + ConditionVector include_after; }; struct Project { diff --git a/src/cmake_generator.cpp b/src/cmake_generator.cpp index 68efd5a..389b3d5 100644 --- a/src/cmake_generator.cpp +++ b/src/cmake_generator.cpp @@ -13,15 +13,6 @@ namespace cmkr { namespace gen { -static std::string to_upper(const std::string &str) { - std::string temp; - temp.reserve(str.size()); - for (auto c : str) { - temp.push_back(::toupper(c)); - } - return temp; -} - static std::string format(const char *format, tsl::ordered_map variables) { std::string s = format; for (const auto &itr : variables) { @@ -710,6 +701,11 @@ void generate_cmake(const char *path, const parser::Project *parent_project) { cmd("include")("FetchContent").endl(); for (const auto &content : project.contents) { ConditionScope cs(gen, content.condition); + + gen.handle_condition(content.include_before, + [&](const std::string &, const std::vector &includes) { inject_includes(includes); }); + gen.handle_condition(content.cmake_before, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); }); + std::string version_info = ""; if (content.arguments.contains("GIT_TAG")) { version_info = " (" + content.arguments.at("GIT_TAG") + ")"; @@ -723,6 +719,10 @@ void generate_cmake(const char *path, const parser::Project *parent_project) { } ss << ")\n"; cmd("FetchContent_MakeAvailable")(content.name).endl(); + + gen.handle_condition(content.include_after, + [&](const std::string &, const std::vector &includes) { inject_includes(includes); }); + gen.handle_condition(content.cmake_after, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); }); } } diff --git a/src/project_parser.cpp b/src/project_parser.cpp index 05bc4aa..d466c0c 100644 --- a/src/project_parser.cpp +++ b/src/project_parser.cpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace cmkr { namespace parser { @@ -49,8 +50,8 @@ static std::string format_key_error(const std::string &error, const toml::key &k class TomlChecker { const TomlBasicValue &m_v; - tsl::ordered_map m_visited; - tsl::ordered_map m_conditionVisited; + tsl::ordered_set m_visited; + tsl::ordered_set m_conditionVisited; public: TomlChecker(const TomlBasicValue &v, const toml::key &ky) : m_v(toml::find(v, ky)) {} @@ -77,7 +78,7 @@ class TomlChecker { // Handle visiting logic for (const auto &itr : destination) { if (!itr.first.empty()) { - m_conditionVisited.emplace(itr.first, true); + m_conditionVisited.emplace(itr.first); } } visit(ky); @@ -108,7 +109,9 @@ class TomlChecker { return toml::find(m_v, ky); } - void visit(const toml::key &ky) { m_visited.emplace(ky, true); } + void visit(const toml::key &ky) { m_visited.emplace(ky); } + + bool visisted(const toml::key &ky) const { return m_visited.contains(ky); } void check(const tsl::ordered_map &conditions) const { for (const auto &itr : m_v.as_table()) { @@ -145,7 +148,7 @@ class TomlChecker { class TomlCheckerRoot { const TomlBasicValue &m_root; std::deque m_checkers; - tsl::ordered_map m_visisted; + tsl::ordered_set m_visisted; bool m_checked = false; public: @@ -154,7 +157,7 @@ class TomlCheckerRoot { TomlCheckerRoot(TomlCheckerRoot &&) = delete; bool contains(const toml::key &ky) { - m_visisted[ky] = true; + m_visisted.emplace(ky); return m_root.contains(ky); } @@ -353,19 +356,49 @@ Project::Project(const Project *parent, const std::string &path, bool build) { } } - // TODO: perform checking here if (checker.contains("fetch-content")) { const auto &fc = toml::find(toml, "fetch-content").as_table(); for (const auto &itr : fc) { Content content; content.name = itr.first; + + auto &c = checker.create(itr.second); + c.optional("condition", content.condition); + c.optional("cmake-before", content.cmake_before); + c.optional("cmake-after", content.cmake_after); + c.optional("include-before", content.include_before); + c.optional("include-after", content.include_after); + for (const auto &argItr : itr.second.as_table()) { - auto key = argItr.first; - if (key == "condition") { - content.condition = argItr.second.as_string(); - continue; + std::string value; + if (argItr.second.is_array()) { + for (const auto &list_val : argItr.second.as_array()) { + if (!value.empty()) { + value += ';'; + } + value += list_val.as_string(); + } + } else if (argItr.second.is_boolean()) { + value = argItr.second.as_boolean() ? "ON" : "OFF"; + } else { + value = argItr.second.as_string(); } + auto is_cmake_arg = [](const std::string &s) { + for (auto c : s) { + if (!(std::isdigit(c) || std::isupper(c) || c == '_')) { + return false; + } + } + return true; + }; + + // https://cmake.org/cmake/help/latest/command/string.html#supported-hash-algorithms + tsl::ordered_set hash_algorithms = { + "md5", "sha1", "sha224", "sha256", "sha384", "sha512", "sha3_224", "sha3_256", "sha3_384", "sha3_512", + }; + + auto key = argItr.first; if (key == "git") { key = "GIT_REPOSITORY"; } else if (key == "tag") { @@ -378,18 +411,23 @@ Project::Project(const Project *parent, const std::string &path, bool build) { key = "SVN_REVISION"; } else if (key == "url") { key = "URL"; + } else if (hash_algorithms.contains(key)) { + std::string algo; + for (auto c : key) { + algo.push_back(std::toupper(c)); + } + key = "URL_HASH"; + value = algo + "=" + value; } else if (key == "hash") { key = "URL_HASH"; - } else { - // don't change arg + } else if (is_cmake_arg(key)) { + // allow passthrough of ExternalProject options + } else if (!c.visisted(key)) { + throw std::runtime_error(format_key_error("Unknown key '" + argItr.first + "'", argItr.first, argItr.second)); } - std::string value; - if (argItr.second.is_boolean()) { - value = argItr.second.as_boolean() ? "ON" : "OFF"; - } else { - value = argItr.second.as_string(); - } + c.visit(argItr.first); + content.arguments.emplace(key, value); } contents.emplace_back(std::move(content));