Merge pull request #35 from build-cpp/toml-checker

Proper error messages for invalid keys/conditions
main
Duncan Ogilvie 3 years ago committed by GitHub
commit 56a88c41e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -35,7 +35,7 @@ link-libraries = [
"ghc_filesystem", "ghc_filesystem",
"mpark_variant", "mpark_variant",
"ordered_map", "ordered_map",
"nlohmann_json" "nlohmann_json",
] ]
cmake-after = """ cmake-after = """
generate_resources(${CMKR_TARGET}) generate_resources(${CMKR_TARGET})

@ -4,10 +4,10 @@
#include "project_parser.hpp" #include "project_parser.hpp"
#include "fs.hpp" #include "fs.hpp"
#include <sstream>
#include <cstddef> #include <cstddef>
#include <stdexcept>
#include <cstdlib> #include <cstdlib>
#include <sstream>
#include <stdexcept>
#include <system_error> #include <system_error>
namespace cmkr { namespace cmkr {

@ -119,7 +119,7 @@ int generate_project(const char *str) {
const auto tomlbuf = format(cmake_toml, dir_name.c_str(), dir_name.c_str(), str, installed.c_str(), target.c_str(), dest.c_str()); const auto tomlbuf = format(cmake_toml, dir_name.c_str(), dir_name.c_str(), str, installed.c_str(), target.c_str(), dest.c_str());
if (strcmp(str, "interface")) { if (strcmp(str, "interface")) {
std::ofstream ofs("src/main.cpp"); std::ofstream ofs("src/main.cpp", std::ios::binary);
if (ofs.is_open()) { if (ofs.is_open()) {
ofs << mainbuf; ofs << mainbuf;
} }
@ -127,7 +127,7 @@ int generate_project(const char *str) {
ofs.close(); ofs.close();
} }
std::ofstream ofs2("cmake.toml"); std::ofstream ofs2("cmake.toml", std::ios::binary);
if (ofs2.is_open()) { if (ofs2.is_open()) {
ofs2 << tomlbuf; ofs2 << tomlbuf;
} }
@ -607,7 +607,7 @@ int generate_cmake(const char *path, const parser::Project *parent_project) {
j["description"] = project.project_description; j["description"] = project.project_description;
} }
std::ofstream ofs("vcpkg.json"); std::ofstream ofs("vcpkg.json", std::ios::binary);
if (!ofs) { if (!ofs) {
throw std::runtime_error("Failed to create a vcpkg.json manifest file!"); throw std::runtime_error("Failed to create a vcpkg.json manifest file!");
} }
@ -900,7 +900,7 @@ int generate_cmake(const char *path, const parser::Project *parent_project) {
if (!fs::exists(list_path)) if (!fs::exists(list_path))
return true; return true;
std::ifstream ifs(list_path, std::ios_base::binary); std::ifstream ifs(list_path, std::ios::binary);
if (!ifs.is_open()) { if (!ifs.is_open()) {
throw std::runtime_error("Failed to read " + list_path.string()); throw std::runtime_error("Failed to read " + list_path.string());
} }
@ -910,7 +910,7 @@ int generate_cmake(const char *path, const parser::Project *parent_project) {
}(); }();
if (should_regenerate) { if (should_regenerate) {
std::ofstream ofs(list_path, std::ios_base::binary); std::ofstream ofs(list_path, std::ios::binary);
if (ofs.is_open()) { if (ofs.is_open()) {
ofs << ss.str(); ofs << ss.str();
} else { } else {

@ -2,9 +2,11 @@
#include "enum_helper.hpp" #include "enum_helper.hpp"
#include "fs.hpp" #include "fs.hpp"
#include <deque>
#include <stdexcept> #include <stdexcept>
#include <toml.hpp> #include <toml.hpp>
#include <tsl/ordered_map.h> #include <tsl/ordered_map.h>
#include <tsl/ordered_set.h>
template <> template <>
const char *enumStrings<cmkr::parser::TargetType>::data[] = {"executable", "library", "shared", "static", "interface", "custom"}; const char *enumStrings<cmkr::parser::TargetType>::data[] = {"executable", "library", "shared", "static", "interface", "custom"};
@ -33,33 +35,147 @@ static EnumType to_enum(const std::string &str, const std::string &help_name) {
return value; return value;
} }
template <typename T> static std::string format_key_error(const std::string &error, const toml::key &ky, const TomlBasicValue &value) {
static void get_optional(const TomlBasicValue &v, const toml::key &ky, T &destination); auto loc = value.location();
auto line_number_str = std::to_string(loc.line());
template <typename T> auto line_width = line_number_str.length();
static void get_optional(const TomlBasicValue &v, const toml::key &ky, Condition<T> &destination) { auto line_str = loc.line_str();
// TODO: this algorithm in O(n) over the amount of keys, kinda bad
const auto &table = v.as_table(); std::ostringstream oss;
for (const auto &itr : table) { oss << "[error] " << error << '\n';
const auto &key = itr.first; oss << " --> " << loc.file_name() << '\n';
const auto &value = itr.second;
if (value.is_table()) { oss << std::string(line_width + 2, ' ') << "|\n";
if (value.contains(ky)) { oss << ' ' << line_number_str << " | " << line_str << '\n';
destination[key] = toml::find<T>(value, ky);
oss << std::string(line_width + 2, ' ') << '|';
auto key_start = line_str.substr(0, loc.column() - 1).rfind(ky);
if (key_start == std::string::npos) {
key_start = line_str.find(ky);
}
if (key_start != std::string::npos) {
oss << std::string(key_start + 1, ' ') << std::string(ky.length(), '~');
}
oss << '\n';
return oss.str();
}
class TomlChecker {
const TomlBasicValue &m_v;
tsl::ordered_set<toml::key> m_visited;
tsl::ordered_set<toml::key> 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(TomlChecker &&) = delete;
template <typename T>
void optional(const toml::key &ky, Condition<T> &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;
if (value.is_table()) {
if (value.contains(ky)) {
destination[key] = toml::find<T>(value, ky);
}
} else if (key == ky) {
destination[""] = toml::find<T>(m_v, ky);
}
}
// Handle visiting logic
for (const auto &itr : destination) {
if (!itr.first.empty()) {
m_conditionVisited.emplace(itr.first);
} }
} else if (key == ky) {
destination[""] = toml::find<T>(v, ky);
} }
visit(ky);
} }
}
template <typename T> template <typename T>
static void get_optional(const TomlBasicValue &v, const toml::key &ky, T &destination) { void optional(const toml::key &ky, T &destination) {
// TODO: this currently doesn't allow you to get an optional map<string, X> // TODO: this currently doesn't allow you to get an optional map<string, X>
if (v.contains(ky)) { if (m_v.contains(ky)) {
destination = toml::find<T>(v, ky); destination = toml::find<T>(m_v, ky);
}
visit(ky);
}
template <typename T>
void required(const toml::key &ky, T &destination) {
destination = toml::find<T>(m_v, ky);
visit(ky);
} }
}
bool contains(const toml::key &ky) {
visit(ky);
return m_v.contains(ky);
}
const TomlBasicValue &find(const toml::key &ky) {
visit(ky);
return toml::find(m_v, ky);
}
void visit(const toml::key &ky) { m_visited.insert(ky); }
void check(const tsl::ordered_map<std::string, std::string> &conditions) const {
for (const auto &itr : m_v.as_table()) {
const auto &ky = itr.first;
if (m_conditionVisited.contains(ky)) {
if (!conditions.contains(ky)) {
throw std::runtime_error(format_key_error("Unknown condition '" + ky + "'", ky, itr.second));
}
for (const auto &jtr : itr.second.as_table()) {
if (!m_visited.contains(jtr.first)) {
throw std::runtime_error(format_key_error("Unknown key '" + jtr.first + "'", jtr.first, jtr.second));
}
}
} else if (!m_visited.contains(ky)) {
if (itr.second.is_table()) {
for (const auto &jtr : itr.second.as_table()) {
if (!m_visited.contains(jtr.first)) {
throw std::runtime_error(format_key_error("Unknown key '" + jtr.first + "'", jtr.first, jtr.second));
}
}
}
throw std::runtime_error(format_key_error("Unknown key '" + ky + "'", ky, itr.second));
}
}
}
};
class TomlCheckerRoot {
std::deque<TomlChecker> m_checkers;
bool m_checked = false;
public:
TomlCheckerRoot() = default;
TomlCheckerRoot(const TomlCheckerRoot &) = delete;
TomlCheckerRoot(TomlCheckerRoot &&) = delete;
TomlChecker &create(const TomlBasicValue &v) {
m_checkers.emplace_back(v);
return m_checkers.back();
}
TomlChecker &create(const TomlBasicValue &v, const toml::key &ky) {
m_checkers.emplace_back(v, ky);
return m_checkers.back();
}
void check(const tsl::ordered_map<std::string, std::string> &conditions) {
for (const auto &checker : m_checkers) {
checker.check(conditions);
}
}
};
Project::Project(const Project *parent, const std::string &path, bool build) { Project::Project(const Project *parent, const std::string &path, bool build) {
const auto toml_path = fs::path(path) / "cmake.toml"; const auto toml_path = fs::path(path) / "cmake.toml";
@ -67,303 +183,323 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
throw std::runtime_error("No cmake.toml was found!"); throw std::runtime_error("No cmake.toml was found!");
} }
const auto toml = toml::parse<toml::preserve_comments, tsl::ordered_map, std::vector>(toml_path.string()); const auto toml = toml::parse<toml::preserve_comments, tsl::ordered_map, std::vector>(toml_path.string());
if (build) {
if (toml.contains("cmake")) {
const auto &cmake = toml::find(toml, "cmake");
if (cmake.contains("bin-dir")) { TomlCheckerRoot checker;
throw std::runtime_error("bin-dir has been renamed to build-dir");
} if (toml.contains("cmake")) {
auto &cmake = checker.create(toml, "cmake");
get_optional(cmake, "build-dir", build_dir); cmake.required("version", cmake_version);
get_optional(cmake, "generator", generator);
get_optional(cmake, "config", config); if (cmake.contains("bin-dir")) {
get_optional(cmake, "arguments", gen_args); throw std::runtime_error("bin-dir has been renamed to build-dir");
get_optional(cmake, "allow-in-tree", allow_in_tree);
} }
} else {
if (toml.contains("cmake")) { cmake.optional("build-dir", build_dir);
const auto &cmake = toml::find(toml, "cmake"); cmake.optional("generator", generator);
cmake_version = toml::find(cmake, "version").as_string(); cmake.optional("config", config);
if (cmake.contains("cmkr-include")) { cmake.optional("arguments", gen_args);
const auto &cmkr_include_kv = toml::find(cmake, "cmkr-include"); cmake.optional("allow-in-tree", allow_in_tree);
if (cmkr_include_kv.is_string()) {
cmkr_include = cmkr_include_kv.as_string(); if (cmake.contains("cmkr-include")) {
} else { const auto &cmkr_include_kv = cmake.find("cmkr-include");
// Allow disabling this feature with cmkr-include = false if (cmkr_include_kv.is_string()) {
cmkr_include = ""; cmkr_include = cmkr_include_kv.as_string();
} } else {
// Allow disabling this feature with cmkr-include = false
cmkr_include = "";
} }
get_optional(cmake, "cpp-flags", cppflags);
get_optional(cmake, "c-flags", cflags);
get_optional(cmake, "link-flags", linkflags);
} }
if (toml.contains("project")) { cmake.optional("cpp-flags", cppflags);
const auto &project = toml::find(toml, "project"); cmake.optional("c-flags", cflags);
project_name = toml::find(project, "name").as_string(); cmake.optional("link-flags", linkflags);
get_optional(project, "version", project_version); }
get_optional(project, "description", project_description);
get_optional(project, "languages", project_languages); // Skip the rest of the parsing when building
get_optional(project, "cmake-before", cmake_before); if (build) {
get_optional(project, "cmake-after", cmake_after); checker.check(conditions);
get_optional(project, "include-before", include_before); return;
get_optional(project, "include-after", include_after); }
get_optional(project, "subdirs", project_subdirs);
// 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<decltype(conditions)>(toml, "conditions");
for (const auto &cond : conds) {
conditions[cond.first] = cond.second;
} }
}
if (toml.contains("subdir")) { if (toml.contains("project")) {
const auto &subs = toml::find(toml, "subdir").as_table(); auto &project = checker.create(toml, "project");
for (const auto &sub : subs) { project.required("name", project_name);
Subdir subdir; project.optional("version", project_version);
subdir.name = sub.first; project.optional("description", project_description);
get_optional(sub.second, "condition", subdir.condition); project.optional("languages", project_languages);
get_optional(sub.second, "cmake-before", subdir.cmake_before); project.optional("cmake-before", cmake_before);
get_optional(sub.second, "cmake-after", subdir.cmake_after); project.optional("cmake-after", cmake_after);
get_optional(sub.second, "include-before", subdir.include_before); project.optional("include-before", include_before);
get_optional(sub.second, "include-after", subdir.include_after); project.optional("include-after", include_after);
subdirs.push_back(subdir); project.optional("subdirs", project_subdirs);
} }
if (toml.contains("subdir")) {
const auto &subs = toml::find(toml, "subdir").as_table();
for (const auto &itr : subs) {
Subdir subdir;
subdir.name = itr.first;
auto &sub = checker.create(itr.second);
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);
sub.optional("include-after", subdir.include_after);
subdirs.push_back(subdir);
} }
}
if (toml.contains("settings")) { if (toml.contains("settings")) {
using set_map = std::map<std::string, TomlBasicValue>; using set_map = std::map<std::string, TomlBasicValue>;
const auto &sets = toml::find<set_map>(toml, "settings"); const auto &sets = toml::find<set_map>(toml, "settings");
for (const auto &set : sets) { for (const auto &itr : sets) {
Setting s; Setting s;
s.name = set.first; s.name = itr.first;
if (set.second.is_boolean()) { const auto &value = itr.second;
s.val = set.second.as_boolean(); if (value.is_boolean()) {
} else if (set.second.is_string()) { s.val = value.as_boolean();
s.val = set.second.as_string(); } else if (value.is_string()) {
} else { s.val = value.as_string();
get_optional(set.second, "comment", s.comment); } else {
if (set.second.contains("value")) { auto &setting = checker.create(value);
auto v = toml::find(set.second, "value"); setting.optional("comment", s.comment);
if (v.is_boolean()) { if (setting.contains("value")) {
s.val = v.as_boolean(); const auto &v = setting.find("value");
} else { if (v.is_boolean()) {
s.val = v.as_string(); s.val = v.as_boolean();
} } else {
s.val = v.as_string();
} }
get_optional(set.second, "cache", s.cache);
get_optional(set.second, "force", s.force);
} }
settings.push_back(s); setting.optional("cache", s.cache);
setting.optional("force", s.force);
} }
settings.push_back(s);
} }
}
if (toml.contains("options")) { if (toml.contains("options")) {
using opts_map = tsl::ordered_map<std::string, TomlBasicValue>; using opts_map = tsl::ordered_map<std::string, TomlBasicValue>;
const auto &opts = toml::find<opts_map>(toml, "options"); const auto &opts = toml::find<opts_map>(toml, "options");
for (const auto &opt : opts) { for (const auto &itr : opts) {
Option o; Option o;
o.name = opt.first; o.name = itr.first;
if (opt.second.is_boolean()) { const auto &value = itr.second;
o.val = opt.second.as_boolean(); if (value.is_boolean()) {
} else { o.val = value.as_boolean();
get_optional(opt.second, "comment", o.comment); } else {
get_optional(opt.second, "value", o.val); auto &option = checker.create(value);
} option.optional("comment", o.comment);
options.push_back(o); option.optional("value", o.val);
} }
options.push_back(o);
} }
}
if (toml.contains("find-package")) { if (toml.contains("find-package")) {
using pkg_map = tsl::ordered_map<std::string, TomlBasicValue>; using pkg_map = tsl::ordered_map<std::string, TomlBasicValue>;
const auto &pkgs = toml::find<pkg_map>(toml, "find-package"); const auto &pkgs = toml::find<pkg_map>(toml, "find-package");
for (const auto &pkg : pkgs) { for (const auto &itr : pkgs) {
Package p; Package p;
p.name = pkg.first; p.name = itr.first;
if (pkg.second.is_string()) { const auto &value = itr.second;
p.version = pkg.second.as_string(); if (itr.second.is_string()) {
} else { p.version = itr.second.as_string();
get_optional(pkg.second, "version", p.version); } else {
get_optional(pkg.second, "required", p.required); auto &pkg = checker.create(value);
get_optional(pkg.second, "config", p.config); pkg.optional("version", p.version);
get_optional(pkg.second, "components", p.components); pkg.optional("required", p.required);
} pkg.optional("config", p.config);
packages.push_back(p); pkg.optional("components", p.components);
} }
packages.push_back(p);
} }
}
if (toml.contains("fetch-content")) { // TODO: perform checking here
const auto &fc = toml::find(toml, "fetch-content").as_table(); if (toml.contains("fetch-content")) {
for (const auto &itr : fc) { const auto &fc = toml::find(toml, "fetch-content").as_table();
Content content; for (const auto &itr : fc) {
content.name = itr.first; Content content;
for (const auto &argItr : itr.second.as_table()) { content.name = itr.first;
auto key = argItr.first; for (const auto &argItr : itr.second.as_table()) {
if (key == "git") { auto key = argItr.first;
key = "GIT_REPOSITORY"; if (key == "git") {
} else if (key == "tag") { key = "GIT_REPOSITORY";
key = "GIT_TAG"; } else if (key == "tag") {
} else if (key == "svn") { key = "GIT_TAG";
key = "SVN_REPOSITORY"; } else if (key == "svn") {
} else if (key == "rev") { key = "SVN_REPOSITORY";
key = "SVN_REVISION"; } else if (key == "rev") {
} else if (key == "url") { key = "SVN_REVISION";
key = "URL"; } else if (key == "url") {
} else if (key == "hash") { key = "URL";
key = "URL_HASH"; } else if (key == "hash") {
} else { key = "URL_HASH";
// don't change arg } else {
} // don't change arg
content.arguments.emplace(key, argItr.second.as_string());
} }
contents.emplace_back(std::move(content)); content.arguments.emplace(key, argItr.second.as_string());
} }
contents.emplace_back(std::move(content));
} }
}
if (toml.contains("bin")) { if (toml.contains("bin")) {
throw std::runtime_error("[[bin]] has been renamed to [[target]]"); throw std::runtime_error("[[bin]] has been renamed to [[target]]");
} }
if (toml.contains("target")) { if (toml.contains("target")) {
const auto &ts = toml::find(toml, "target").as_table(); const auto &ts = toml::find(toml, "target").as_table();
for (const auto &itr : ts) { for (const auto &itr : ts) {
const auto &t = itr.second; const auto &value = itr.second;
Target target;
target.name = itr.first;
target.type = to_enum<TargetType>(toml::find(t, "type").as_string(), "target type");
get_optional(t, "headers", target.headers); Target target;
get_optional(t, "sources", target.sources); target.name = itr.first;
get_optional(t, "compile-definitions", target.compile_definitions); auto &t = checker.create(value);
get_optional(t, "private-compile-definitions", target.private_compile_definitions); std::string type;
t.required("type", type);
target.type = to_enum<TargetType>(type, "target type");
get_optional(t, "compile-features", target.compile_features); t.optional("headers", target.headers);
get_optional(t, "private-compile-features", target.private_compile_features); t.optional("sources", target.sources);
get_optional(t, "compile-options", target.compile_options); t.optional("compile-definitions", target.compile_definitions);
get_optional(t, "private-compile-options", target.private_compile_options); t.optional("private-compile-definitions", target.private_compile_definitions);
get_optional(t, "include-directories", target.include_directories); t.optional("compile-features", target.compile_features);
get_optional(t, "private-include-directories", target.private_include_directories); t.optional("private-compile-features", target.private_compile_features);
get_optional(t, "link-directories", target.link_directories); t.optional("compile-options", target.compile_options);
get_optional(t, "private-link-directories", target.private_link_directories); t.optional("private-compile-options", target.private_compile_options);
get_optional(t, "link-libraries", target.link_libraries); t.optional("include-directories", target.include_directories);
get_optional(t, "private-link-libraries", target.private_link_libraries); t.optional("private-include-directories", target.private_include_directories);
get_optional(t, "link-options", target.link_options); t.optional("link-directories", target.link_directories);
get_optional(t, "private-link-options", target.private_link_options); t.optional("private-link-directories", target.private_link_directories);
get_optional(t, "precompile-headers", target.precompile_headers); t.optional("link-libraries", target.link_libraries);
get_optional(t, "private-precompile-headers", target.private_precompile_headers); t.optional("private-link-libraries", target.private_link_libraries);
if (!target.headers.empty()) { t.optional("link-options", target.link_options);
auto &sources = target.sources.nth(0).value(); t.optional("private-link-options", target.private_link_options);
const auto &headers = target.headers.nth(0)->second;
sources.insert(sources.end(), headers.begin(), headers.end());
}
if (t.contains("condition")) { t.optional("precompile-headers", target.precompile_headers);
target.condition = toml::find(t, "condition").as_string(); t.optional("private-precompile-headers", target.private_precompile_headers);
}
if (t.contains("alias")) { if (!target.headers.empty()) {
target.alias = toml::find(t, "alias").as_string(); 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")) { if (t.contains("properties")) {
auto store_property = [&target](const toml::key &k, const TomlBasicValue &v, const std::string &condition) { auto store_property = [&target](const toml::key &k, const TomlBasicValue &v, const std::string &condition) {
if (v.is_array()) { if (v.is_array()) {
std::string property_list; std::string property_list;
for (const auto &list_val : v.as_array()) { for (const auto &list_val : v.as_array()) {
if (!property_list.empty()) { if (!property_list.empty()) {
property_list += ';'; property_list += ';';
}
property_list += list_val.as_string();
} }
target.properties[condition][k] = property_list; property_list += list_val.as_string();
} 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()) {
const auto &props = toml::find(t, "properties").as_table(); target.properties[condition][k] = v.as_boolean() ? "ON" : "OFF";
for (const auto &propKv : props) { } else {
const auto &k = propKv.first; target.properties[condition][k] = v.as_string();
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) {
} else { const auto &k = propKv.first;
store_property(k, v, ""); 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, "");
} }
} }
get_optional(t, "cmake-before", target.cmake_before);
get_optional(t, "cmake-after", target.cmake_after);
get_optional(t, "include-before", target.include_before);
get_optional(t, "include-after", target.include_after);
targets.push_back(target);
} }
}
if (toml.contains("test")) { t.optional("cmake-before", target.cmake_before);
const auto &ts = toml::find(toml, "test").as_array(); t.optional("cmake-after", target.cmake_after);
for (const auto &t : ts) { t.optional("include-before", target.include_before);
Test test; t.optional("include-after", target.include_after);
test.name = toml::find(t, "name").as_string();
get_optional(t, "configurations", test.configurations);
get_optional(t, "working-directory", test.working_directory);
test.command = toml::find(t, "command").as_string();
get_optional(t, "arguments", test.arguments);
tests.push_back(test);
}
}
if (toml.contains("install")) { targets.push_back(target);
const auto &ts = toml::find(toml, "install").as_array();
for (const auto &t : ts) {
Install inst;
get_optional(t, "targets", inst.targets);
get_optional(t, "files", inst.files);
get_optional(t, "dirs", inst.dirs);
get_optional(t, "configs", inst.configs);
inst.destination = toml::find(t, "destination").as_string();
installs.push_back(inst);
}
} }
}
if (toml.contains("vcpkg")) { if (toml.contains("test")) {
const auto &v = toml::find(toml, "vcpkg"); const auto &ts = toml::find(toml, "test").as_array();
get_optional(v, "url", vcpkg.url); for (const auto &value : ts) {
get_optional(v, "version", vcpkg.version); auto &t = checker.create(value);
vcpkg.packages = toml::find<decltype(vcpkg.packages)>(v, "packages"); Test test;
t.required("name", test.name);
t.optional("configurations", test.configurations);
t.optional("working-directory", test.working_directory);
t.required("command", test.command);
t.optional("arguments", test.arguments);
tests.push_back(test);
} }
}
// Reasonable default conditions (you can override these if you desire) if (toml.contains("install")) {
if (parent == nullptr) { const auto &is = toml::find(toml, "install").as_array();
conditions["windows"] = R"cmake(WIN32)cmake"; for (const auto &value : is) {
conditions["macos"] = R"cmake(CMAKE_SYSTEM_NAME MATCHES "Darwin")cmake"; auto &i = checker.create(value);
conditions["unix"] = R"cmake(UNIX)cmake"; Install inst;
conditions["bsd"] = R"cmake(CMAKE_SYSTEM_NAME MATCHES "BSD")cmake"; i.optional("targets", inst.targets);
conditions["linux"] = conditions["lunix"] = R"cmake(CMAKE_SYSTEM_NAME MATCHES "Linux")cmake"; i.optional("files", inst.files);
conditions["gcc"] = R"cmake(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "GNU")cmake"; i.optional("dirs", inst.dirs);
conditions["clang"] = R"cmake(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_C_COMPILER_ID MATCHES "Clang")cmake"; i.optional("configs", inst.configs);
conditions["msvc"] = R"cmake(MSVC)cmake"; i.required("destination", inst.destination);
} else { installs.push_back(inst);
conditions = parent->conditions;
} }
}
if (toml.contains("conditions")) { if (toml.contains("vcpkg")) {
auto conds = toml::find<decltype(conditions)>(toml, "conditions"); auto &v = checker.create(toml, "vcpkg");
for (const auto &cond : conds) { v.optional("url", vcpkg.url);
conditions[cond.first] = cond.second; v.optional("version", vcpkg.version);
} v.required("packages", vcpkg.packages);
}
} }
checker.check(conditions);
} }
bool is_root_path(const std::string &path) { bool is_root_path(const std::string &path) {

Loading…
Cancel
Save