From 9f9934e9a526b71e267f6fa433735e0abe457851 Mon Sep 17 00:00:00 2001 From: Duncan Ogilvie Date: Wed, 22 Dec 2021 13:31:52 +0100 Subject: [PATCH] Implemented checking of keys in conditions --- src/project_parser.cpp | 111 +++++++++++++++++++++++++++++------------ 1 file changed, 80 insertions(+), 31 deletions(-) diff --git a/src/project_parser.cpp b/src/project_parser.cpp index 2941010..828dade 100644 --- a/src/project_parser.cpp +++ b/src/project_parser.cpp @@ -62,25 +62,37 @@ static void get_optional(const TomlBasicValue &v, const toml::key &ky, T &destin } } +// TODO: construct this from a helper class with state so all the checking can be done implicitly class TomlChecker { const TomlBasicValue &m_v; tsl::ordered_set m_visited; - bool m_checked = false; + tsl::ordered_set m_conditionVisited; public: TomlChecker(const TomlBasicValue &v, const toml::key &ky) : m_v(toml::find(v, ky)) {} TomlChecker(const TomlBasicValue &v) : m_v(v) {} TomlChecker(const TomlChecker &) = delete; - ~TomlChecker() noexcept(false) { - if (!m_checked) { - throw std::runtime_error("TomlChecker::check() not called"); + ~TomlChecker() noexcept(false) {} + + // TOOD: check if the condition is valid during the parsing stage to print better errors! + template + void optional(const toml::key &ky, Condition &destination) { + get_optional(m_v, ky, destination); + for (const auto &itr : destination) { + if (!itr.first.empty()) { + m_conditionVisited.emplace(itr.first); + } } + visit(ky); } template void optional(const toml::key &ky, T &destination) { - get_optional(m_v, ky, destination); + // TODO: this currently doesn't allow you to get an optional map + if (m_v.contains(ky)) { + destination = toml::find(m_v, ky); + } visit(ky); } @@ -92,13 +104,48 @@ class TomlChecker { void visit(const toml::key &ky) { m_visited.insert(ky); } + std::string format_unknown_key(const toml::key &ky, const TomlBasicValue &value) { + auto loc = value.location(); + auto line_number_str = std::to_string(loc.line()); + auto line_width = line_number_str.length(); + auto line_str = loc.line_str(); + + std::ostringstream oss; + oss << "[error] Unknown key: " << ky << '\n'; + oss << " --> " << loc.file_name() << '\n'; + + oss << std::string(line_width + 2, ' ') << "|\n"; + oss << ' ' << line_number_str << " | " << line_str << '\n'; + + oss << std::string(line_width + 2, ' ') << '|'; + auto key_start = line_str.find_last_of(ky, loc.column()); + if (key_start != std::string::npos) { + oss << std::string(key_start - ky.length() + 2, ' ') << std::string(ky.length(), '~'); + } + oss << '\n'; + + return oss.str(); + } + void check() { - m_checked = true; for (const auto &itr : m_v.as_table()) { const auto &ky = itr.first; - if (m_visited.count(ky) == 0) { - // TODO: nice error messages - throw std::runtime_error("Unknown key '" + ky + "'"); + if (m_conditionVisited.count(ky)) { + // TODO: check if condition (ky) exists + for (const auto &jtr : itr.second.as_table()) { + if (m_visited.count(jtr.first) == 0) { + throw std::runtime_error(format_unknown_key(jtr.first, jtr.second)); + } + } + } else if (m_visited.count(ky) == 0) { + if (itr.second.is_table()) { + for (const auto &jtr : itr.second.as_table()) { + if (m_visited.count(jtr.first) == 0) { + throw std::runtime_error(format_unknown_key(jtr.first, jtr.second)); + } + } + } + throw std::runtime_error(format_unknown_key(ky, itr.second)); } } } @@ -126,6 +173,30 @@ Project::Project(const Project *parent, const std::string &path, bool build) { get_optional(cmake, "allow-in-tree", allow_in_tree); } } else { + // Reasonable default conditions (you can override these if you desire) + if (parent == nullptr) { + conditions["windows"] = R"cmake(WIN32)cmake"; + conditions["macos"] = R"cmake(CMAKE_SYSTEM_NAME MATCHES "Darwin")cmake"; + conditions["unix"] = R"cmake(UNIX)cmake"; + conditions["bsd"] = R"cmake(CMAKE_SYSTEM_NAME MATCHES "BSD")cmake"; + conditions["linux"] = conditions["lunix"] = R"cmake(CMAKE_SYSTEM_NAME MATCHES "Linux")cmake"; + conditions["gcc"] = R"cmake(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "GNU")cmake"; + conditions["clang"] = R"cmake(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_C_COMPILER_ID MATCHES "Clang")cmake"; + conditions["msvc"] = R"cmake(MSVC)cmake"; + } else { + conditions = parent->conditions; + } + + if (toml.contains("conditions")) { + auto conds = toml::find(toml, "conditions"); + for (const auto &cond : conds) { + conditions[cond.first] = cond.second; + } + } + + // TODO: make TomlCheckerFactory + // .check() only once (at the end) + if (toml.contains("cmake")) { const auto &cmake = toml::find(toml, "cmake"); cmake_version = toml::find(cmake, "version").as_string(); @@ -165,7 +236,6 @@ Project::Project(const Project *parent, const std::string &path, bool build) { TomlChecker sub(itr.second); sub.optional("condition", subdir.condition); - sub.optional("condition", subdir.condition); sub.optional("cmake-before", subdir.cmake_before); sub.optional("cmake-after", subdir.cmake_after); sub.optional("include-before", subdir.include_before); @@ -409,27 +479,6 @@ Project::Project(const Project *parent, const std::string &path, bool build) { v.required("packages", vcpkg.packages); v.check(); } - - // Reasonable default conditions (you can override these if you desire) - if (parent == nullptr) { - conditions["windows"] = R"cmake(WIN32)cmake"; - conditions["macos"] = R"cmake(CMAKE_SYSTEM_NAME MATCHES "Darwin")cmake"; - conditions["unix"] = R"cmake(UNIX)cmake"; - conditions["bsd"] = R"cmake(CMAKE_SYSTEM_NAME MATCHES "BSD")cmake"; - conditions["linux"] = conditions["lunix"] = R"cmake(CMAKE_SYSTEM_NAME MATCHES "Linux")cmake"; - conditions["gcc"] = R"cmake(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "GNU")cmake"; - conditions["clang"] = R"cmake(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_C_COMPILER_ID MATCHES "Clang")cmake"; - conditions["msvc"] = R"cmake(MSVC)cmake"; - } else { - conditions = parent->conditions; - } - - if (toml.contains("conditions")) { - auto conds = toml::find(toml, "conditions"); - for (const auto &cond : conds) { - conditions[cond.first] = cond.second; - } - } } }