|
|
|
@ -2,6 +2,7 @@
|
|
|
|
|
|
|
|
|
|
#include "enum_helper.hpp"
|
|
|
|
|
#include "fs.hpp"
|
|
|
|
|
#include <deque>
|
|
|
|
|
#include <stdexcept>
|
|
|
|
|
#include <toml.hpp>
|
|
|
|
|
#include <tsl/ordered_map.h>
|
|
|
|
@ -34,35 +35,32 @@ static EnumType to_enum(const std::string &str, const std::string &help_name) {
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
static void get_optional(const TomlBasicValue &v, const toml::key &ky, T &destination);
|
|
|
|
|
static std::string format_key_error(const std::string &error, 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();
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
static void get_optional(const TomlBasicValue &v, const toml::key &ky, Condition<T> &destination) {
|
|
|
|
|
// TODO: this algorithm in O(n) over the amount of keys, kinda bad
|
|
|
|
|
const auto &table = 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>(v, ky);
|
|
|
|
|
}
|
|
|
|
|
std::ostringstream oss;
|
|
|
|
|
oss << "[error] " << error << '\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.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';
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
static void get_optional(const TomlBasicValue &v, const toml::key &ky, T &destination) {
|
|
|
|
|
// TODO: this currently doesn't allow you to get an optional map<string, X>
|
|
|
|
|
if (v.contains(ky)) {
|
|
|
|
|
destination = toml::find<T>(v, ky);
|
|
|
|
|
}
|
|
|
|
|
return oss.str();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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<toml::key> m_visited;
|
|
|
|
@ -72,13 +70,25 @@ class TomlChecker {
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
~TomlChecker() noexcept(false) {}
|
|
|
|
|
|
|
|
|
|
// TOOD: check if the condition is valid during the parsing stage to print better errors!
|
|
|
|
|
template <typename T>
|
|
|
|
|
void optional(const toml::key &ky, Condition<T> &destination) {
|
|
|
|
|
get_optional(m_v, ky, 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);
|
|
|
|
@ -102,77 +112,116 @@ class TomlChecker {
|
|
|
|
|
visit(ky);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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(), '~');
|
|
|
|
|
bool contains(const toml::key &ky) {
|
|
|
|
|
visit(ky);
|
|
|
|
|
return m_v.contains(ky);
|
|
|
|
|
}
|
|
|
|
|
oss << '\n';
|
|
|
|
|
|
|
|
|
|
return oss.str();
|
|
|
|
|
const TomlBasicValue &find(const toml::key &ky) {
|
|
|
|
|
visit(ky);
|
|
|
|
|
return toml::find(m_v, ky);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void check() {
|
|
|
|
|
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.count(ky)) {
|
|
|
|
|
// TODO: check if condition (ky) exists
|
|
|
|
|
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.count(jtr.first) == 0) {
|
|
|
|
|
throw std::runtime_error(format_unknown_key(jtr.first, jtr.second));
|
|
|
|
|
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.count(ky) == 0) {
|
|
|
|
|
} else if (!m_visited.contains(ky)) {
|
|
|
|
|
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));
|
|
|
|
|
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_unknown_key(ky, itr.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) {
|
|
|
|
|
const auto toml_path = fs::path(path) / "cmake.toml";
|
|
|
|
|
if (!fs::exists(toml_path)) {
|
|
|
|
|
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());
|
|
|
|
|
// TODO: TomlChecker for the "cmake" section
|
|
|
|
|
if (build) {
|
|
|
|
|
|
|
|
|
|
TomlCheckerRoot checker;
|
|
|
|
|
|
|
|
|
|
if (toml.contains("cmake")) {
|
|
|
|
|
const auto &cmake = toml::find(toml, "cmake");
|
|
|
|
|
auto &cmake = checker.create(toml, "cmake");
|
|
|
|
|
|
|
|
|
|
cmake.required("version", cmake_version);
|
|
|
|
|
|
|
|
|
|
if (cmake.contains("bin-dir")) {
|
|
|
|
|
throw std::runtime_error("bin-dir has been renamed to build-dir");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get_optional(cmake, "build-dir", build_dir);
|
|
|
|
|
get_optional(cmake, "generator", generator);
|
|
|
|
|
get_optional(cmake, "config", config);
|
|
|
|
|
get_optional(cmake, "arguments", gen_args);
|
|
|
|
|
get_optional(cmake, "allow-in-tree", allow_in_tree);
|
|
|
|
|
}
|
|
|
|
|
cmake.optional("build-dir", build_dir);
|
|
|
|
|
cmake.optional("generator", generator);
|
|
|
|
|
cmake.optional("config", config);
|
|
|
|
|
cmake.optional("arguments", gen_args);
|
|
|
|
|
cmake.optional("allow-in-tree", allow_in_tree);
|
|
|
|
|
|
|
|
|
|
if (cmake.contains("cmkr-include")) {
|
|
|
|
|
const auto &cmkr_include_kv = cmake.find("cmkr-include");
|
|
|
|
|
if (cmkr_include_kv.is_string()) {
|
|
|
|
|
cmkr_include = cmkr_include_kv.as_string();
|
|
|
|
|
} else {
|
|
|
|
|
// Allow disabling this feature with cmkr-include = false
|
|
|
|
|
cmkr_include = "";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cmake.optional("cpp-flags", cppflags);
|
|
|
|
|
cmake.optional("c-flags", cflags);
|
|
|
|
|
cmake.optional("link-flags", linkflags);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Skip the rest of the parsing when building
|
|
|
|
|
if (build) {
|
|
|
|
|
checker.check(conditions);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Reasonable default conditions (you can override these if you desire)
|
|
|
|
|
if (parent == nullptr) {
|
|
|
|
|
conditions["windows"] = R"cmake(WIN32)cmake";
|
|
|
|
@ -194,28 +243,8 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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();
|
|
|
|
|
if (cmake.contains("cmkr-include")) {
|
|
|
|
|
const auto &cmkr_include_kv = toml::find(cmake, "cmkr-include");
|
|
|
|
|
if (cmkr_include_kv.is_string()) {
|
|
|
|
|
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")) {
|
|
|
|
|
TomlChecker project(toml, "project");
|
|
|
|
|
auto &project = checker.create(toml, "project");
|
|
|
|
|
project.required("name", project_name);
|
|
|
|
|
project.optional("version", project_version);
|
|
|
|
|
project.optional("description", project_description);
|
|
|
|
@ -225,7 +254,6 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
|
|
|
|
|
project.optional("include-before", include_before);
|
|
|
|
|
project.optional("include-after", include_after);
|
|
|
|
|
project.optional("subdirs", project_subdirs);
|
|
|
|
|
project.check();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (toml.contains("subdir")) {
|
|
|
|
@ -234,13 +262,12 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
|
|
|
|
|
Subdir subdir;
|
|
|
|
|
subdir.name = itr.first;
|
|
|
|
|
|
|
|
|
|
TomlChecker sub(itr.second);
|
|
|
|
|
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);
|
|
|
|
|
sub.check();
|
|
|
|
|
|
|
|
|
|
subdirs.push_back(subdir);
|
|
|
|
|
}
|
|
|
|
@ -258,11 +285,10 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
|
|
|
|
|
} else if (value.is_string()) {
|
|
|
|
|
s.val = value.as_string();
|
|
|
|
|
} else {
|
|
|
|
|
TomlChecker setting(value);
|
|
|
|
|
auto &setting = checker.create(value);
|
|
|
|
|
setting.optional("comment", s.comment);
|
|
|
|
|
setting.visit("value");
|
|
|
|
|
if (value.contains("value")) {
|
|
|
|
|
auto v = toml::find(value, "value");
|
|
|
|
|
if (setting.contains("value")) {
|
|
|
|
|
const auto &v = setting.find("value");
|
|
|
|
|
if (v.is_boolean()) {
|
|
|
|
|
s.val = v.as_boolean();
|
|
|
|
|
} else {
|
|
|
|
@ -271,7 +297,6 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
|
|
|
|
|
}
|
|
|
|
|
setting.optional("cache", s.cache);
|
|
|
|
|
setting.optional("force", s.force);
|
|
|
|
|
setting.check();
|
|
|
|
|
}
|
|
|
|
|
settings.push_back(s);
|
|
|
|
|
}
|
|
|
|
@ -287,10 +312,9 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
|
|
|
|
|
if (value.is_boolean()) {
|
|
|
|
|
o.val = value.as_boolean();
|
|
|
|
|
} else {
|
|
|
|
|
TomlChecker option(value);
|
|
|
|
|
auto &option = checker.create(value);
|
|
|
|
|
option.optional("comment", o.comment);
|
|
|
|
|
option.optional("value", o.val);
|
|
|
|
|
option.check();
|
|
|
|
|
}
|
|
|
|
|
options.push_back(o);
|
|
|
|
|
}
|
|
|
|
@ -306,17 +330,17 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
|
|
|
|
|
if (itr.second.is_string()) {
|
|
|
|
|
p.version = itr.second.as_string();
|
|
|
|
|
} else {
|
|
|
|
|
TomlChecker pkg(value);
|
|
|
|
|
auto &pkg = checker.create(value);
|
|
|
|
|
pkg.optional("version", p.version);
|
|
|
|
|
pkg.optional("required", p.required);
|
|
|
|
|
pkg.optional("config", p.config);
|
|
|
|
|
pkg.optional("components", p.components);
|
|
|
|
|
pkg.check();
|
|
|
|
|
}
|
|
|
|
|
packages.push_back(p);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: perform checking here
|
|
|
|
|
if (toml.contains("fetch-content")) {
|
|
|
|
|
const auto &fc = toml::find(toml, "fetch-content").as_table();
|
|
|
|
|
for (const auto &itr : fc) {
|
|
|
|
@ -358,7 +382,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
|
|
|
|
|
Target target;
|
|
|
|
|
target.name = itr.first;
|
|
|
|
|
|
|
|
|
|
TomlChecker t(value);
|
|
|
|
|
auto &t = checker.create(value);
|
|
|
|
|
std::string type;
|
|
|
|
|
t.required("type", type);
|
|
|
|
|
target.type = to_enum<TargetType>(type, "target type");
|
|
|
|
@ -399,8 +423,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
|
|
|
|
|
t.optional("condition", target.condition);
|
|
|
|
|
t.optional("alias", target.alias);
|
|
|
|
|
|
|
|
|
|
t.visit("properties");
|
|
|
|
|
if (value.contains("properties")) {
|
|
|
|
|
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;
|
|
|
|
@ -418,7 +441,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const auto &props = toml::find(value, "properties").as_table();
|
|
|
|
|
const auto &props = t.find("properties").as_table();
|
|
|
|
|
for (const auto &propKv : props) {
|
|
|
|
|
const auto &k = propKv.first;
|
|
|
|
|
const auto &v = propKv.second;
|
|
|
|
@ -436,7 +459,6 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
|
|
|
|
|
t.optional("cmake-after", target.cmake_after);
|
|
|
|
|
t.optional("include-before", target.include_before);
|
|
|
|
|
t.optional("include-after", target.include_after);
|
|
|
|
|
t.check();
|
|
|
|
|
|
|
|
|
|
targets.push_back(target);
|
|
|
|
|
}
|
|
|
|
@ -445,14 +467,13 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
|
|
|
|
|
if (toml.contains("test")) {
|
|
|
|
|
const auto &ts = toml::find(toml, "test").as_array();
|
|
|
|
|
for (const auto &value : ts) {
|
|
|
|
|
TomlChecker t(value);
|
|
|
|
|
auto &t = checker.create(value);
|
|
|
|
|
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);
|
|
|
|
|
t.check();
|
|
|
|
|
tests.push_back(test);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -460,26 +481,25 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
|
|
|
|
|
if (toml.contains("install")) {
|
|
|
|
|
const auto &is = toml::find(toml, "install").as_array();
|
|
|
|
|
for (const auto &value : is) {
|
|
|
|
|
TomlChecker i(value);
|
|
|
|
|
auto &i = checker.create(value);
|
|
|
|
|
Install inst;
|
|
|
|
|
i.optional("targets", inst.targets);
|
|
|
|
|
i.optional("files", inst.files);
|
|
|
|
|
i.optional("dirs", inst.dirs);
|
|
|
|
|
i.optional("configs", inst.configs);
|
|
|
|
|
i.required("destination", inst.destination);
|
|
|
|
|
i.check();
|
|
|
|
|
installs.push_back(inst);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (toml.contains("vcpkg")) {
|
|
|
|
|
TomlChecker v(toml, "vcpkg");
|
|
|
|
|
auto &v = checker.create(toml, "vcpkg");
|
|
|
|
|
v.optional("url", vcpkg.url);
|
|
|
|
|
v.optional("version", vcpkg.version);
|
|
|
|
|
v.required("packages", vcpkg.packages);
|
|
|
|
|
v.check();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
checker.check(conditions);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool is_root_path(const std::string &path) {
|
|
|
|
|