mirror of
https://github.com/cemu-project/vcpkg.git
synced 2025-02-24 11:37:12 +01:00

==== Changes Related to manifests ==== * Add the `manifests` feature flag * This only says whether we look for a `vcpkg.json` in the cwd, not whether we support parsing manifests (for ports, for example) * Changes to the manifests RFC * `"authors"` -> `"maintainers"` * `--x-classic-mode` -> `-manifests` \in `vcpkg_feature_flags` * reserve `"core"` in addition to `"default"`, since that's already reserved for features * Add a small helper note about what identifiers must look like * `<license-string>`: SPDX v3.8 -> v3.9 * `"feature"."description"` is allowed to be an array of strings as well * `"version"` -> `"version-string"` for forward-compat with versions RFC * Add the `--feature-flags` option * Add the ability to turn off feature flags via passing `-<feature-flag>` to `VCPKG_FEATURE_FLAGS` or `--feature-flags` * Add CMake toolchain support for manifests * Requires either: * a feature flag of `manifests` in either `Env{VCPKG_FEATURE_FLAGS}` or `VCPKG_FEATURE_FLAGS` * Passing the `VCPKG_ENABLE_MANIFESTS` option * The toolchain will install your packages to `${VCPKG_MANIFEST_DIR}/vcpkg_installed`. * Add MSBuild `vcpkg integrate install` support for manifests * Requires `VcpkgEnableManifest` to be true * `vcpkg create` creates a port that has a `vcpkg.json` instead of a `CONTROL` * argparse, abseil, 3fd, and avisynthplus ports switched to manifest from CONTROL * Add support for `--x-manifest-root`, as well as code for finding it if not passed * Add support for parsing manifests! * Add a filesystem lock! ==== Important Changes which are somewhat unrelated to manifests ==== * Rename `logicexpression.{h,cpp}` to `platform-expression.{h,cpp}` * Add `PlatformExpression` type which takes the place of the old logic expression * Split the parsing of platform expressions from checking whether they're true or not * Eagerly parse PlatformExpressions as opposed to leaving them as strings * Add checking for feature flag consistency * i.e., if `-binarycaching` is passed, you shouldn't be passing `--binarysource` * Add the `Json::Reader` type which, with the help of user-defined visitors, converts JSON to your internal type * VcpkgArgParser: place the switch names into a constant as opposed to using magic constants * In general update the parsing code so that this ^ works * Add `Port-Version` fields to CONTROL files * This replaces the existing practice of `Version: <my-version>-<port-version>` ==== Smaller changes ==== * small drive-by cleanups to some CMake * `${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}` -> `${CURRENT_INSTALLED_DIR}` * Remove `-analyze` when compiling with clang-cl, since that's not a supported flag (vcpkg's build system) * Add a message about which compiler is detected by vcpkg's build system machinery * Fix `Expected::then` * Convert `""` to `{}` for `std::string` and `fs::path`, to avoid a `strlen` (additionally, `.empty()` instead of `== ""`, and `.clear()`) * Add `Strings::strto` which converts strings to numeric types * Support built-in arrays and `StringView` for `Strings::join` * Add `operator<` and friends to `StringView` * Add `substr` to `StringView` * SourceParagraphParser gets some new errors
414 lines
14 KiB
C++
414 lines
14 KiB
C++
#pragma once
|
|
|
|
#include <vcpkg/base/expected.h>
|
|
#include <vcpkg/base/files.h>
|
|
#include <vcpkg/base/parse.h>
|
|
#include <vcpkg/base/stringview.h>
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace vcpkg::Json
|
|
{
|
|
struct JsonStyle
|
|
{
|
|
enum class Newline
|
|
{
|
|
Lf,
|
|
CrLf
|
|
} newline_kind = Newline::Lf;
|
|
|
|
constexpr JsonStyle() noexcept = default;
|
|
|
|
static JsonStyle with_tabs() noexcept { return JsonStyle{-1}; }
|
|
static JsonStyle with_spaces(int indent) noexcept
|
|
{
|
|
vcpkg::Checks::check_exit(VCPKG_LINE_INFO, indent >= 0);
|
|
return JsonStyle{indent};
|
|
}
|
|
|
|
void set_tabs() noexcept { this->indent = -1; }
|
|
void set_spaces(int indent_) noexcept
|
|
{
|
|
vcpkg::Checks::check_exit(VCPKG_LINE_INFO, indent >= 0);
|
|
this->indent = indent_;
|
|
}
|
|
|
|
bool use_tabs() const noexcept { return indent == -1; }
|
|
bool use_spaces() const noexcept { return indent >= 0; }
|
|
|
|
int spaces() const noexcept
|
|
{
|
|
vcpkg::Checks::check_exit(VCPKG_LINE_INFO, indent >= 0);
|
|
return indent;
|
|
}
|
|
|
|
const char* newline() const noexcept
|
|
{
|
|
switch (this->newline_kind)
|
|
{
|
|
case Newline::Lf: return "\n";
|
|
case Newline::CrLf: return "\r\n";
|
|
default: Checks::exit_fail(VCPKG_LINE_INFO);
|
|
}
|
|
}
|
|
|
|
private:
|
|
constexpr explicit JsonStyle(int indent) : indent(indent) { }
|
|
// -1 for tab, >=0 gives # of spaces
|
|
int indent = 2;
|
|
};
|
|
|
|
struct Array;
|
|
struct Object;
|
|
|
|
enum class ValueKind
|
|
{
|
|
Null,
|
|
Boolean,
|
|
Integer,
|
|
Number,
|
|
String,
|
|
Array,
|
|
Object
|
|
};
|
|
|
|
namespace impl
|
|
{
|
|
struct ValueImpl;
|
|
struct SyntaxErrorImpl;
|
|
}
|
|
|
|
struct Value
|
|
{
|
|
Value() noexcept; // equivalent to Value::null()
|
|
Value(Value&&) noexcept;
|
|
Value& operator=(Value&&) noexcept;
|
|
~Value();
|
|
|
|
Value clone() const noexcept;
|
|
|
|
ValueKind kind() const noexcept;
|
|
|
|
bool is_null() const noexcept;
|
|
bool is_boolean() const noexcept;
|
|
bool is_integer() const noexcept;
|
|
// either integer _or_ number
|
|
bool is_number() const noexcept;
|
|
bool is_string() const noexcept;
|
|
bool is_array() const noexcept;
|
|
bool is_object() const noexcept;
|
|
|
|
// a.x() asserts when !a.is_x()
|
|
bool boolean() const noexcept;
|
|
int64_t integer() const noexcept;
|
|
double number() const noexcept;
|
|
StringView string() const noexcept;
|
|
|
|
const Array& array() const& noexcept;
|
|
Array& array() & noexcept;
|
|
Array&& array() && noexcept;
|
|
|
|
const Object& object() const& noexcept;
|
|
Object& object() & noexcept;
|
|
Object&& object() && noexcept;
|
|
|
|
static Value null(std::nullptr_t) noexcept;
|
|
static Value boolean(bool) noexcept;
|
|
static Value integer(int64_t i) noexcept;
|
|
static Value number(double d) noexcept;
|
|
static Value string(StringView) noexcept;
|
|
static Value array(Array&&) noexcept;
|
|
static Value object(Object&&) noexcept;
|
|
|
|
private:
|
|
friend struct impl::ValueImpl;
|
|
std::unique_ptr<impl::ValueImpl> underlying_;
|
|
};
|
|
|
|
struct Array
|
|
{
|
|
private:
|
|
using underlying_t = std::vector<Value>;
|
|
|
|
public:
|
|
Array() = default;
|
|
Array(Array const&) = delete;
|
|
Array(Array&&) = default;
|
|
Array& operator=(Array const&) = delete;
|
|
Array& operator=(Array&&) = default;
|
|
~Array() = default;
|
|
|
|
Array clone() const noexcept;
|
|
|
|
using iterator = underlying_t::iterator;
|
|
using const_iterator = underlying_t::const_iterator;
|
|
|
|
Value& push_back(Value&& value);
|
|
Object& push_back(Object&& value);
|
|
Array& push_back(Array&& value);
|
|
Value& insert_before(iterator it, Value&& value);
|
|
Object& insert_before(iterator it, Object&& value);
|
|
Array& insert_before(iterator it, Array&& value);
|
|
|
|
std::size_t size() const noexcept { return this->underlying_.size(); }
|
|
|
|
// asserts idx < size
|
|
Value& operator[](std::size_t idx) noexcept
|
|
{
|
|
vcpkg::Checks::check_exit(VCPKG_LINE_INFO, idx < this->size());
|
|
return this->underlying_[idx];
|
|
}
|
|
const Value& operator[](std::size_t idx) const noexcept
|
|
{
|
|
vcpkg::Checks::check_exit(VCPKG_LINE_INFO, idx < this->size());
|
|
return this->underlying_[idx];
|
|
}
|
|
|
|
iterator begin() { return underlying_.begin(); }
|
|
iterator end() { return underlying_.end(); }
|
|
const_iterator begin() const { return cbegin(); }
|
|
const_iterator end() const { return cend(); }
|
|
const_iterator cbegin() const { return underlying_.cbegin(); }
|
|
const_iterator cend() const { return underlying_.cend(); }
|
|
|
|
private:
|
|
underlying_t underlying_;
|
|
};
|
|
struct Object
|
|
{
|
|
private:
|
|
using underlying_t = std::vector<std::pair<std::string, Value>>;
|
|
|
|
underlying_t::const_iterator internal_find_key(StringView key) const noexcept;
|
|
|
|
public:
|
|
// these are here for better diagnostics
|
|
Object() = default;
|
|
Object(Object const&) = delete;
|
|
Object(Object&&) = default;
|
|
Object& operator=(Object const&) = delete;
|
|
Object& operator=(Object&&) = default;
|
|
~Object() = default;
|
|
|
|
Object clone() const noexcept;
|
|
|
|
// asserts if the key is found
|
|
Value& insert(std::string key, Value&& value);
|
|
Object& insert(std::string key, Object&& value);
|
|
Array& insert(std::string key, Array&& value);
|
|
|
|
// replaces the value if the key is found, otherwise inserts a new
|
|
// value.
|
|
Value& insert_or_replace(std::string key, Value&& value);
|
|
Object& insert_or_replace(std::string key, Object&& value);
|
|
Array& insert_or_replace(std::string key, Array&& value);
|
|
|
|
// returns whether the key existed
|
|
bool remove(StringView key) noexcept;
|
|
|
|
// asserts on lookup failure
|
|
Value& operator[](StringView key) noexcept
|
|
{
|
|
auto res = this->get(key);
|
|
vcpkg::Checks::check_exit(VCPKG_LINE_INFO, res);
|
|
return *res;
|
|
}
|
|
const Value& operator[](StringView key) const noexcept
|
|
{
|
|
auto res = this->get(key);
|
|
vcpkg::Checks::check_exit(VCPKG_LINE_INFO, res);
|
|
return *res;
|
|
}
|
|
|
|
Value* get(StringView key) noexcept;
|
|
const Value* get(StringView key) const noexcept;
|
|
|
|
bool contains(StringView key) const noexcept { return this->get(key); }
|
|
|
|
std::size_t size() const noexcept { return this->underlying_.size(); }
|
|
|
|
struct const_iterator
|
|
{
|
|
using value_type = std::pair<StringView, const Value&>;
|
|
using reference = value_type;
|
|
using iterator_category = std::forward_iterator_tag;
|
|
|
|
value_type operator*() const noexcept { return *underlying_; }
|
|
const_iterator& operator++() noexcept
|
|
{
|
|
++underlying_;
|
|
return *this;
|
|
}
|
|
const_iterator operator++(int) noexcept
|
|
{
|
|
auto res = *this;
|
|
++underlying_;
|
|
return res;
|
|
}
|
|
|
|
bool operator==(const_iterator other) const noexcept { return this->underlying_ == other.underlying_; }
|
|
bool operator!=(const_iterator other) const noexcept { return !(this->underlying_ == other.underlying_); }
|
|
|
|
private:
|
|
friend struct Object;
|
|
explicit const_iterator(const underlying_t::const_iterator& it) : underlying_(it) { }
|
|
underlying_t::const_iterator underlying_;
|
|
};
|
|
using iterator = const_iterator;
|
|
|
|
const_iterator begin() const noexcept { return this->cbegin(); }
|
|
const_iterator end() const noexcept { return this->cend(); }
|
|
const_iterator cbegin() const noexcept { return const_iterator{this->underlying_.begin()}; }
|
|
const_iterator cend() const noexcept { return const_iterator{this->underlying_.end()}; }
|
|
|
|
private:
|
|
underlying_t underlying_;
|
|
};
|
|
|
|
struct ReaderError
|
|
{
|
|
virtual void add_missing_field(std::string&& type, std::string&& key) = 0;
|
|
virtual void add_expected_type(std::string&& key, std::string&& expected_type) = 0;
|
|
virtual void add_extra_fields(std::string&& type, std::vector<std::string>&& fields) = 0;
|
|
virtual void add_mutually_exclusive_fields(std::string&& type, std::vector<std::string>&& fields) = 0;
|
|
|
|
virtual ~ReaderError() = default;
|
|
};
|
|
|
|
struct Reader
|
|
{
|
|
explicit Reader(ReaderError* err) : err(err) { }
|
|
|
|
ReaderError& error() const { return *err; }
|
|
|
|
private:
|
|
ReaderError* err;
|
|
|
|
template<class Visitor>
|
|
using VisitorType = typename std::remove_reference_t<Visitor>::type;
|
|
|
|
template<class Visitor>
|
|
Optional<VisitorType<Visitor>> internal_visit(const Value& value, StringView key, Visitor& visitor)
|
|
{
|
|
switch (value.kind())
|
|
{
|
|
using VK = Json::ValueKind;
|
|
case VK::Null: return visitor.visit_null(*this, key);
|
|
case VK::Boolean: return visitor.visit_boolean(*this, key, value.boolean());
|
|
case VK::Integer: return visitor.visit_integer(*this, key, value.integer());
|
|
case VK::Number: return visitor.visit_number(*this, key, value.number());
|
|
case VK::String: return visitor.visit_string(*this, key, value.string());
|
|
case VK::Array: return visitor.visit_array(*this, key, value.array());
|
|
case VK::Object: return visitor.visit_object(*this, key, value.object());
|
|
}
|
|
|
|
vcpkg::Checks::exit_fail(VCPKG_LINE_INFO);
|
|
}
|
|
|
|
// returns whether the field was found, not whether it was valid
|
|
template<class Visitor>
|
|
bool internal_field(const Object& obj, StringView key, VisitorType<Visitor>& place, Visitor& visitor)
|
|
{
|
|
auto value = obj.get(key);
|
|
if (!value)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Optional<VisitorType<Visitor>> opt = internal_visit(*value, key, visitor);
|
|
|
|
if (auto val = opt.get())
|
|
{
|
|
place = std::move(*val);
|
|
}
|
|
else
|
|
{
|
|
err->add_expected_type(key.to_string(), visitor.type_name().to_string());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public:
|
|
template<class Visitor>
|
|
void required_object_field(
|
|
StringView type, const Object& obj, StringView key, VisitorType<Visitor>& place, Visitor&& visitor)
|
|
{
|
|
if (!internal_field(obj, key, place, visitor))
|
|
{
|
|
err->add_missing_field(type.to_string(), key.to_string());
|
|
}
|
|
}
|
|
|
|
template<class Visitor>
|
|
void optional_object_field(const Object& obj, StringView key, VisitorType<Visitor>& place, Visitor&& visitor)
|
|
{
|
|
internal_field(obj, key, place, visitor);
|
|
}
|
|
|
|
template<class Visitor>
|
|
Optional<std::vector<VisitorType<Visitor>>> array_elements(const Array& arr, StringView key, Visitor&& visitor)
|
|
{
|
|
std::vector<VisitorType<Visitor>> result;
|
|
for (const auto& el : arr)
|
|
{
|
|
auto opt = internal_visit(el, key, visitor);
|
|
if (auto p = opt.get())
|
|
{
|
|
result.push_back(std::move(*p));
|
|
}
|
|
else
|
|
{
|
|
return nullopt;
|
|
}
|
|
}
|
|
return std::move(result);
|
|
}
|
|
};
|
|
|
|
// Warning: NEVER use this type except as a CRTP base
|
|
template<class Underlying>
|
|
struct VisitorCrtpBase
|
|
{
|
|
// the following function must be defined by the Underlying class
|
|
// const char* type_name();
|
|
|
|
// We do this auto dance since function bodies are checked _after_ typedefs in the superclass,
|
|
// but function declarations are checked beforehand. Therefore, we can get C++ to use this typedef
|
|
// only once the function bodies are checked by returning `auto` and specifying the
|
|
// return type in the function body.
|
|
auto visit_null(Reader&, StringView) { return Optional<typename Underlying::type>(nullopt); }
|
|
auto visit_boolean(Reader&, StringView, bool) { return Optional<typename Underlying::type>(nullopt); }
|
|
auto visit_integer(Reader& r, StringView field_name, int64_t i)
|
|
{
|
|
return static_cast<Underlying&>(*this).visit_number(r, field_name, static_cast<double>(i));
|
|
}
|
|
auto visit_number(Reader&, StringView, double) { return Optional<typename Underlying::type>(nullopt); }
|
|
auto visit_string(Reader&, StringView, StringView) { return Optional<typename Underlying::type>(nullopt); }
|
|
auto visit_array(Reader&, StringView, const Json::Array&)
|
|
{
|
|
return Optional<typename Underlying::type>(nullopt);
|
|
}
|
|
auto visit_object(Reader&, StringView, const Json::Object&)
|
|
{
|
|
return Optional<typename Underlying::type>(nullopt);
|
|
}
|
|
// we can't make the SMFs protected because of an issue with /permissive mode
|
|
};
|
|
|
|
ExpectedT<std::pair<Value, JsonStyle>, std::unique_ptr<Parse::IParseError>> parse_file(
|
|
const Files::Filesystem&, const fs::path&, std::error_code& ec) noexcept;
|
|
ExpectedT<std::pair<Value, JsonStyle>, std::unique_ptr<Parse::IParseError>> parse(
|
|
StringView text, const fs::path& filepath = {}) noexcept;
|
|
|
|
std::string stringify(const Value&, JsonStyle style);
|
|
std::string stringify(const Object&, JsonStyle style);
|
|
std::string stringify(const Array&, JsonStyle style);
|
|
|
|
}
|