This commit is contained in:
nicole mazzuca 2020-09-02 09:13:44 -07:00 коммит произвёл GitHub
Родитель 4d68c16770
Коммит 65640a2960
37 изменённых файлов: 1638 добавлений и 644 удалений

Просмотреть файл

@ -40,13 +40,15 @@ IncludeCategories:
Priority: -1
- Regex: '^<catch2/catch\.hpp>$'
Priority: 1
- Regex: '^<vcpkg/fwd/.*\.h>$'
- Regex: '^<vcpkg/base/fwd/.*\.h>$'
Priority: 2
- Regex: '^<vcpkg/base/.*\.h>$'
- Regex: '^<vcpkg/fwd/.*\.h>$'
Priority: 3
- Regex: '^<vcpkg/.*\.h>$'
- Regex: '^<vcpkg/base/.*\.h>$'
Priority: 4
- Regex: '^<[a-z0-9_]*\.h>$'
- Regex: '^<vcpkg/.*\.h>$'
Priority: 5
- Regex: '^<[a-z0-9_]*>$' # C++ standard library
- Regex: '^<[a-z0-9_]*\.h>$'
Priority: 6
- Regex: '^<[a-z0-9_]*>$' # C++ standard library
Priority: 7

Просмотреть файл

@ -33,8 +33,8 @@ endif()
file(GLOB VCPKGLIB_SOURCES CONFIGURE_DEPENDS src/vcpkg/*.cpp)
file(GLOB VCPKGLIB_BASE_SOURCES CONFIGURE_DEPENDS src/vcpkg/base/*.cpp)
file(GLOB VCPKGLIB_INCLUDES CONFIGURE_DEPENDS include/vcpkg/*.h)
file(GLOB VCPKGLIB_BASE_INCLUDES CONFIGURE_DEPENDS include/vcpkg/base/*.h)
file(GLOB VCPKGLIB_INCLUDES CONFIGURE_DEPENDS include/vcpkg/*.h include/vcpkg/fwd/*.h)
file(GLOB VCPKGLIB_BASE_INCLUDES CONFIGURE_DEPENDS include/vcpkg/base/*.h include/vcpkg/base/fwd/*.h)
set(VCPKG_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/vcpkg.cpp)
@ -154,19 +154,20 @@ endif()
find_program(CLANG_FORMAT clang-format)
if(CLANG_FORMAT)
add_custom_target(format COMMAND ${CLANG_FORMAT} -i -verbose
${CMAKE_CURRENT_SOURCE_DIR}/src/pch.cpp
${VCPKGLIB_BASE_SOURCES}
${VCPKGLIB_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/include/pch.h
${VCPKGLIB_BASE_INCLUDES}
${VCPKGLIB_INCLUDES}
# doing all of these formats in one line has a tendency to overflow the command line length
add_custom_target(format
COMMAND ${CLANG_FORMAT} -i -verbose ${CMAKE_CURRENT_SOURCE_DIR}/src/pch.cpp
COMMAND ${CLANG_FORMAT} -i -verbose ${VCPKGLIB_BASE_SOURCES}
COMMAND ${CLANG_FORMAT} -i -verbose ${VCPKGLIB_SOURCES}
COMMAND ${CLANG_FORMAT} -i -verbose ${CMAKE_CURRENT_SOURCE_DIR}/include/pch.h
COMMAND ${CLANG_FORMAT} -i -verbose ${VCPKGLIB_BASE_INCLUDES}
COMMAND ${CLANG_FORMAT} -i -verbose ${VCPKGLIB_INCLUDES}
${VCPKG_SOURCES}
${VCPKGMETRICSUPLOADER_SOURCES}
COMMAND ${CLANG_FORMAT} -i -verbose ${VCPKG_SOURCES}
COMMAND ${CLANG_FORMAT} -i -verbose ${VCPKGMETRICSUPLOADER_SOURCES}
${VCPKG_TEST_SOURCES}
${VCPKG_TEST_INCLUDES}
COMMAND ${CLANG_FORMAT} -i -verbose ${VCPKG_TEST_SOURCES}
COMMAND ${CLANG_FORMAT} -i -verbose ${VCPKG_TEST_INCLUDES}
${VCPKG_FUZZ_SOURCES})
COMMAND ${CLANG_FORMAT} -i -verbose ${VCPKG_FUZZ_SOURCES})
endif()

Просмотреть файл

@ -157,7 +157,7 @@ namespace vcpkg::Files
/// <summary>Read text lines from a file</summary>
/// <remarks>Lines will have up to one trailing carriage-return character stripped (CRLF)</remarks>
virtual Expected<std::vector<std::string>> read_lines(const fs::path& file_path) const = 0;
virtual fs::path find_file_recursively_up(const fs::path& starting_dir, const std::string& filename) const = 0;
virtual fs::path find_file_recursively_up(const fs::path& starting_dir, const fs::path& filename) const = 0;
virtual std::vector<fs::path> get_files_recursive(const fs::path& dir) const = 0;
virtual std::vector<fs::path> get_files_non_recursive(const fs::path& dir) const = 0;
void write_lines(const fs::path& file_path, const std::vector<std::string>& lines, LineInfo linfo);

Просмотреть файл

@ -0,0 +1,44 @@
#pragma once
#include <vcpkg/base/fwd/optional.h>
#include <vcpkg/base/fwd/span.h>
#include <vcpkg/base/fwd/stringview.h>
namespace vcpkg::Json
{
struct JsonStyle;
enum class ValueKind : int;
struct Value;
struct Object;
struct Array;
struct ReaderError;
struct BasicReaderError;
struct Reader;
// This is written all the way out so that one can include a subclass in a header
template<class Type>
struct IDeserializer
{
using type = Type;
virtual StringView type_name() const = 0;
virtual Span<const StringView> valid_fields() const;
virtual Optional<Type> visit_null(Reader&, StringView);
virtual Optional<Type> visit_boolean(Reader&, StringView, bool);
virtual Optional<Type> visit_integer(Reader& r, StringView field_name, int64_t i);
virtual Optional<Type> visit_number(Reader&, StringView, double);
virtual Optional<Type> visit_string(Reader&, StringView, StringView);
virtual Optional<Type> visit_array(Reader&, StringView, const Array&);
virtual Optional<Type> visit_object(Reader&, StringView, const Object&);
protected:
IDeserializer() = default;
IDeserializer(const IDeserializer&) = default;
IDeserializer& operator=(const IDeserializer&) = default;
IDeserializer(IDeserializer&&) = default;
IDeserializer& operator=(IDeserializer&&) = default;
virtual ~IDeserializer() = default;
};
}

Просмотреть файл

@ -0,0 +1,7 @@
#pragma once
namespace vcpkg
{
template<class T>
struct Optional;
}

Просмотреть файл

@ -0,0 +1,7 @@
#pragma once
namespace vcpkg
{
template<class T>
struct Span;
}

Просмотреть файл

@ -0,0 +1,6 @@
#pragma once
namespace vcpkg
{
struct StringView;
}

Просмотреть файл

@ -1,5 +1,7 @@
#pragma once
#include <vcpkg/base/fwd/json.h>
#include <vcpkg/base/expected.h>
#include <vcpkg/base/files.h>
#include <vcpkg/base/parse.h>
@ -64,10 +66,7 @@ namespace vcpkg::Json
int indent = 2;
};
struct Array;
struct Object;
enum class ValueKind
enum class ValueKind : int
{
Null,
Boolean,
@ -81,7 +80,6 @@ namespace vcpkg::Json
namespace impl
{
struct ValueImpl;
struct SyntaxErrorImpl;
}
struct Value
@ -289,6 +287,53 @@ namespace vcpkg::Json
underlying_t underlying_;
};
VCPKG_MSVC_WARNING(push);
VCPKG_MSVC_WARNING(disable : 4505);
template<class Type>
Span<const StringView> IDeserializer<Type>::valid_fields() const
{
return {};
}
template<class Type>
Optional<Type> IDeserializer<Type>::visit_null(Reader&, StringView)
{
return nullopt;
}
template<class Type>
Optional<Type> IDeserializer<Type>::visit_boolean(Reader&, StringView, bool)
{
return nullopt;
}
template<class Type>
Optional<Type> IDeserializer<Type>::visit_integer(Reader& r, StringView field_name, int64_t i)
{
return this->visit_number(r, field_name, static_cast<double>(i));
}
template<class Type>
Optional<Type> IDeserializer<Type>::visit_number(Reader&, StringView, double)
{
return nullopt;
}
template<class Type>
Optional<Type> IDeserializer<Type>::visit_string(Reader&, StringView, StringView)
{
return nullopt;
}
template<class Type>
Optional<Type> IDeserializer<Type>::visit_array(Reader&, StringView, const Array&)
{
return nullopt;
}
template<class Type>
Optional<Type> IDeserializer<Type>::visit_object(Reader&, StringView, const Object&)
{
return nullopt;
}
VCPKG_MSVC_WARNING(pop);
struct ReaderError
{
virtual void add_missing_field(std::string&& type, std::string&& key) = 0;
@ -299,6 +344,37 @@ namespace vcpkg::Json
virtual ~ReaderError() = default;
};
struct BasicReaderError : ReaderError
{
virtual void add_missing_field(std::string&& type, std::string&& key) override
{
missing_fields.emplace_back(std::move(type), std::move(key));
}
virtual void add_expected_type(std::string&& key, std::string&& expected_type) override
{
expected_types.emplace_back(std::move(key), std::move(expected_type));
}
virtual void add_extra_fields(std::string&& type, std::vector<std::string>&& fields) override
{
extra_fields.emplace_back(std::move(type), std::move(fields));
}
virtual void add_mutually_exclusive_fields(std::string&& type, std::vector<std::string>&& fields) override
{
mutually_exclusive_fields.emplace_back(std::move(type), std::move(fields));
}
bool has_error() const
{
return !missing_fields.empty() || !expected_types.empty() || !extra_fields.empty() ||
!mutually_exclusive_fields.empty();
}
std::vector<std::pair<std::string, std::string>> missing_fields;
std::vector<std::pair<std::string, std::string>> expected_types;
std::vector<std::pair<std::string, std::vector<std::string>>> extra_fields;
std::vector<std::pair<std::string, std::vector<std::string>>> mutually_exclusive_fields;
};
struct Reader
{
explicit Reader(ReaderError* err) : err(err) { }
@ -308,30 +384,31 @@ namespace vcpkg::Json
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)
template<class Type>
Optional<Type> internal_visit(const Value& value, StringView key, IDeserializer<Type>& 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());
case ValueKind::Null: return visitor.visit_null(*this, key);
case ValueKind::Boolean: return visitor.visit_boolean(*this, key, value.boolean());
case ValueKind::Integer: return visitor.visit_integer(*this, key, value.integer());
case ValueKind::Number: return visitor.visit_number(*this, key, value.number());
case ValueKind::String: return visitor.visit_string(*this, key, value.string());
case ValueKind::Array: return visitor.visit_array(*this, key, value.array());
case ValueKind::Object:
{
const auto& obj = value.object();
check_for_unexpected_fields(obj, visitor.valid_fields(), visitor.type_name());
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)
template<class Type>
bool internal_field(const Object& obj, StringView key, Type& place, IDeserializer<Type>& visitor)
{
auto value = obj.get(key);
if (!value)
@ -339,7 +416,7 @@ namespace vcpkg::Json
return false;
}
Optional<VisitorType<Visitor>> opt = internal_visit(*value, key, visitor);
Optional<Type> opt = internal_visit(*value, key, visitor);
if (auto val = opt.get())
{
@ -353,27 +430,115 @@ namespace vcpkg::Json
return true;
}
static std::vector<std::string> invalid_json_fields(const Json::Object& obj,
Span<const StringView> known_fields) noexcept
{
const auto field_is_unknown = [known_fields](StringView sv) {
// allow directives
if (sv.size() != 0 && *sv.begin() == '$')
{
return false;
}
return std::find(known_fields.begin(), known_fields.end(), sv) == known_fields.end();
};
std::vector<std::string> res;
for (const auto& kv : obj)
{
if (field_is_unknown(kv.first))
{
res.push_back(kv.first.to_string());
}
}
return res;
}
// checks that an object doesn't contain any fields which both:
// * don't start with a `$`
// * are not in `valid_fields`
// if known_fields.empty(), then it's treated as if all field names are valid
void check_for_unexpected_fields(const Object& obj, Span<const StringView> valid_fields, StringView type_name)
{
if (valid_fields.size() == 0)
{
return;
}
auto extra_fields = invalid_json_fields(obj, valid_fields);
if (!extra_fields.empty())
{
error().add_extra_fields(type_name.to_string(), std::move(extra_fields));
}
}
public:
template<class Visitor>
template<class Type>
void required_object_field(
StringView type, const Object& obj, StringView key, VisitorType<Visitor>& place, Visitor&& visitor)
StringView type, const Object& obj, StringView key, Type& place, IDeserializer<Type>& 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)
template<class Type>
void required_object_field(
StringView type, const Object& obj, StringView key, Type& place, IDeserializer<Type>&& visitor)
{
internal_field(obj, key, place, visitor);
required_object_field(type, obj, key, place, visitor);
}
template<class Visitor>
Optional<std::vector<VisitorType<Visitor>>> array_elements(const Array& arr, StringView key, Visitor&& visitor)
// returns whether key \in obj
template<class Type>
bool optional_object_field(const Object& obj, StringView key, Type& place, IDeserializer<Type>& visitor)
{
std::vector<VisitorType<Visitor>> result;
return internal_field(obj, key, place, visitor);
}
template<class Type>
bool optional_object_field(const Object& obj, StringView key, Type& place, IDeserializer<Type>&& visitor)
{
return optional_object_field(obj, key, place, visitor);
}
template<class Type>
Optional<Type> visit_value(const Value& value, StringView key, IDeserializer<Type>& visitor)
{
return internal_visit(value, key, visitor);
}
template<class Type>
Optional<Type> visit_value(const Value& value, StringView key, IDeserializer<Type>&& visitor)
{
return visit_value(value, key, visitor);
}
template<class Type>
Optional<Type> visit_value(const Array& value, StringView key, IDeserializer<Type>& visitor)
{
return visitor.visit_array(*this, key, value);
}
template<class Type>
Optional<Type> visit_value(const Array& value, StringView key, IDeserializer<Type>&& visitor)
{
return visit_value(value, key, visitor);
}
template<class Type>
Optional<Type> visit_value(const Object& value, StringView key, IDeserializer<Type>& visitor)
{
check_for_unexpected_fields(value, visitor.valid_fields(), visitor.type_name());
return visitor.visit_object(*this, key, value);
}
template<class Type>
Optional<Type> visit_value(const Object& value, StringView key, IDeserializer<Type>&& visitor)
{
return visit_value(value, key, visitor);
}
template<class Type>
Optional<std::vector<Type>> array_elements(const Array& arr, StringView key, IDeserializer<Type>& visitor)
{
std::vector<Type> result;
for (const auto& el : arr)
{
auto opt = internal_visit(el, key, visitor);
@ -388,42 +553,161 @@ namespace vcpkg::Json
}
return std::move(result);
}
template<class Type>
Optional<std::vector<Type>> array_elements(const Array& arr, StringView key, IDeserializer<Type>&& visitor)
{
return array_elements(arr, key, visitor);
}
};
// Warning: NEVER use this type except as a CRTP base
template<class Underlying>
struct VisitorCrtpBase
struct StringDeserializer final : IDeserializer<std::string>
{
// the following function must be defined by the Underlying class
// const char* type_name();
virtual StringView type_name() const override { return type_name_; }
virtual Optional<std::string> visit_string(Reader&, StringView, StringView sv) override
{
return sv.to_string();
}
// 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)
explicit StringDeserializer(StringView type_name_) : type_name_(type_name_) { }
private:
StringView type_name_;
};
struct PathDeserializer final : IDeserializer<fs::path>
{
virtual StringView type_name() const override { return "a path"; }
virtual Optional<fs::path> visit_string(Reader&, StringView, StringView sv) override { return fs::u8path(sv); }
};
struct NaturalNumberDeserializer final : IDeserializer<int>
{
virtual StringView type_name() const override { return "a natural number"; }
virtual Optional<int> visit_integer(Reader&, StringView, int64_t value) override
{
return static_cast<Underlying&>(*this).visit_number(r, field_name, static_cast<double>(i));
if (value > std::numeric_limits<int>::max() || value < 0)
{
return nullopt;
}
return static_cast<int>(value);
}
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&)
};
struct BooleanDeserializer final : IDeserializer<bool>
{
virtual StringView type_name() const override { return "a boolean"; }
virtual Optional<bool> visit_boolean(Reader&, StringView, bool b) override { return b; }
};
enum class AllowEmpty : bool
{
No,
Yes,
};
template<class Underlying>
struct ArrayDeserializer final : IDeserializer<std::vector<typename Underlying::type>>
{
using typename IDeserializer<std::vector<typename Underlying::type>>::type;
virtual StringView type_name() const override { return type_name_; }
ArrayDeserializer(StringView type_name_, AllowEmpty allow_empty, Underlying&& t = {})
: type_name_(type_name_), underlying_visitor_(static_cast<Underlying&&>(t)), allow_empty_(allow_empty)
{
return Optional<typename Underlying::type>(nullopt);
}
auto visit_object(Reader&, StringView, const Json::Object&)
virtual Optional<type> visit_array(Reader& r, StringView key, const Array& arr) override
{
return Optional<typename Underlying::type>(nullopt);
if (allow_empty_ == AllowEmpty::No && arr.size() == 0)
{
return nullopt;
}
return r.array_elements(arr, key, underlying_visitor_);
}
private:
StringView type_name_;
Underlying underlying_visitor_;
AllowEmpty allow_empty_;
};
struct ParagraphDeserializer final : IDeserializer<std::vector<std::string>>
{
virtual StringView type_name() const override { return "a string or array of strings"; }
virtual Optional<std::vector<std::string>> visit_string(Reader&, StringView, StringView sv) override
{
std::vector<std::string> out;
out.push_back(sv.to_string());
return out;
}
virtual Optional<std::vector<std::string>> visit_array(Reader& r, StringView key, const Array& arr) override
{
return r.array_elements(arr, key, StringDeserializer{"a string"});
}
};
struct IdentifierDeserializer final : Json::IDeserializer<std::string>
{
virtual StringView type_name() const override { return "an identifier"; }
// [a-z0-9]+(-[a-z0-9]+)*, plus not any of {prn, aux, nul, con, lpt[1-9], com[1-9], core, default}
static bool is_ident(StringView sv);
virtual Optional<std::string> visit_string(Json::Reader&, StringView, StringView sv) override
{
if (is_ident(sv))
{
return sv.to_string();
}
else
{
return nullopt;
}
}
};
struct PackageNameDeserializer final : Json::IDeserializer<std::string>
{
virtual StringView type_name() const override { return "a package name"; }
static bool is_package_name(StringView sv)
{
if (sv.size() == 0)
{
return false;
}
for (const auto& ident : Strings::split(sv, '.'))
{
if (!IdentifierDeserializer::is_ident(ident))
{
return false;
}
}
return true;
}
virtual Optional<std::string> visit_string(Json::Reader&, StringView, StringView sv) override
{
if (!is_package_name(sv))
{
return nullopt;
}
return sv.to_string();
}
// 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::pair<Value, JsonStyle> parse_file(vcpkg::LineInfo linfo, const Files::Filesystem&, const fs::path&) noexcept;
std::string stringify(const Value&, JsonStyle style);
std::string stringify(const Object&, JsonStyle style);

Просмотреть файл

@ -1,5 +1,7 @@
#pragma once
#include <vcpkg/base/fwd/optional.h>
#include <vcpkg/base/lineinfo.h>
#include <vcpkg/base/pragmas.h>
@ -15,6 +17,9 @@ namespace vcpkg
const static constexpr NullOpt nullopt{0};
template<class T>
struct Optional;
namespace details
{
template<class T, bool B = std::is_copy_constructible<T>::value>
@ -167,6 +172,7 @@ namespace vcpkg
{
constexpr OptionalStorage() noexcept : m_t(nullptr) { }
constexpr OptionalStorage(T& t) : m_t(&t) { }
constexpr OptionalStorage(Optional<T>& t) : m_t(t.get()) { }
constexpr bool has_value() const { return m_t != nullptr; }
@ -176,6 +182,24 @@ namespace vcpkg
T* m_t;
};
template<class T, bool B>
struct OptionalStorage<const T&, B>
{
constexpr OptionalStorage() noexcept : m_t(nullptr) { }
constexpr OptionalStorage(const T& t) : m_t(&t) { }
constexpr OptionalStorage(const Optional<T>& t) : m_t(t.get()) { }
constexpr OptionalStorage(const Optional<const T>& t) : m_t(t.get()) { }
constexpr OptionalStorage(Optional<T>&& t) = delete;
constexpr OptionalStorage(Optional<const T>&& t) = delete;
constexpr bool has_value() const { return m_t != nullptr; }
const T& value() const { return *this->m_t; }
private:
const T* m_t;
};
// Note: implemented in checks.cpp to cut the header dependency
void exit_if_null(bool b, const LineInfo& line_info);
}
@ -325,27 +349,4 @@ namespace vcpkg
if (auto p = o.get()) return t != *p;
return true;
}
template<class Container, class Projection>
auto common_projection(const Container& input, Projection proj)
-> Optional<std::decay_t<decltype(proj(*(input.begin())))>>
{
const auto last = input.end();
auto first = input.begin();
if (first == last)
{
return nullopt;
}
const auto& prototype = proj(*first);
while (++first != last)
{
if (prototype != proj(*first))
{
return nullopt;
}
}
return prototype;
}
}

Просмотреть файл

@ -1,5 +1,7 @@
#pragma once
#include <vcpkg/base/fwd/stringview.h>
#include <vcpkg/base/optional.h>
#include <limits>

Просмотреть файл

@ -1,5 +1,7 @@
#pragma once
#include <vcpkg/base/optional.h>
#include <algorithm>
#include <functional>
#include <map>
@ -63,7 +65,10 @@ namespace vcpkg::Util
}
template<class Range, class Func>
using FmapOut = std::remove_reference_t<decltype(std::declval<Func&>()(*std::declval<Range>().begin()))>;
using FmapRefOut = decltype(std::declval<Func&>()(*std::declval<Range>().begin()));
template<class Range, class Func>
using FmapOut = std::decay_t<FmapRefOut<Range, Func>>;
template<class Range, class Func, class Out = FmapOut<Range, Func>>
std::vector<Out> fmap(Range&& xs, Func&& f)
@ -77,6 +82,28 @@ namespace vcpkg::Util
return ret;
}
template<class Range, class Proj, class Out = FmapRefOut<Range, Proj>>
Optional<Out> common_projection(Range&& input, Proj&& proj)
{
const auto last = input.end();
auto first = input.begin();
if (first == last)
{
return nullopt;
}
Out prototype = proj(*first);
while (++first != last)
{
if (prototype != proj(*first))
{
return nullopt;
}
}
return prototype;
}
template<class Cont, class Func>
using FmapFlattenOut = std::decay_t<decltype(*begin(std::declval<Func>()(*begin(std::declval<Cont>()))))>;

Просмотреть файл

@ -0,0 +1,41 @@
#pragma once
#include <vcpkg/fwd/configuration.h>
#include <vcpkg/fwd/vcpkgcmdarguments.h>
#include <vcpkg/base/json.h>
#include <vcpkg/registries.h>
namespace vcpkg
{
struct Configuration
{
// This member is set up via two different configuration options,
// `registries` and `default_registry`. The fall back logic is
// taken care of in RegistrySet.
RegistrySet registry_set;
};
struct ConfigurationDeserializer final : Json::IDeserializer<Configuration>
{
virtual StringView type_name() const override { return "a configuration object"; }
constexpr static StringLiteral DEFAULT_REGISTRY = "default-registry";
constexpr static StringLiteral REGISTRIES = "registries";
virtual Span<const StringView> valid_fields() const override
{
constexpr static StringView t[] = {DEFAULT_REGISTRY, REGISTRIES};
return t;
}
virtual Optional<Configuration> visit_object(Json::Reader& r, StringView, const Json::Object& obj) override;
ConfigurationDeserializer(const VcpkgCmdArguments& args);
private:
bool print_json;
bool registries_enabled;
};
}

Просмотреть файл

@ -0,0 +1,6 @@
#pragma once
namespace vcpkg
{
struct Configuration;
}

Просмотреть файл

@ -0,0 +1,8 @@
#pragma once
namespace vcpkg
{
struct RegistryImpl;
struct Registry;
struct RegistrySet;
}

Просмотреть файл

@ -17,23 +17,33 @@ namespace vcpkg::Paragraphs
bool is_port_directory(const Files::Filesystem& fs, const fs::path& path);
Parse::ParseExpected<SourceControlFile> try_load_manifest(const Files::Filesystem& fs,
const std::string& port_name,
const fs::path& path_to_manifest,
std::error_code& ec);
Parse::ParseExpected<SourceControlFile> try_load_port(const Files::Filesystem& fs, const fs::path& path);
ExpectedS<BinaryControlFile> try_load_cached_package(const VcpkgPaths& paths, const PackageSpec& spec);
struct LoadResults
{
std::vector<std::unique_ptr<SourceControlFile>> paragraphs;
std::vector<SourceControlFileLocation> paragraphs;
std::vector<std::unique_ptr<Parse::ParseControlErrorInfo>> errors;
};
LoadResults try_load_all_ports(const Files::Filesystem& fs, const fs::path& ports_dir);
// this allows one to pass this around as an overload set to stuff like `Util::fmap`,
// as opposed to making it a function
constexpr struct
{
const std::string& operator()(const SourceControlFileLocation* loc) const
{
return (*this)(*loc->source_control_file);
}
const std::string& operator()(const SourceControlFileLocation& loc) const
{
return (*this)(*loc.source_control_file);
}
const std::string& operator()(const SourceControlFile& scf) const { return scf.core_paragraph->name; }
} get_name_of_control_file;
std::vector<std::unique_ptr<SourceControlFile>> load_all_ports(const Files::Filesystem& fs,
const fs::path& ports_dir);
LoadResults try_load_all_registry_ports(const VcpkgPaths& paths);
std::vector<SourceControlFileLocation> load_all_registry_ports(const VcpkgPaths& paths);
std::vector<SourceControlFileLocation> load_overlay_ports(const VcpkgPaths& paths, const fs::path& dir);
}

Просмотреть файл

@ -26,14 +26,13 @@ namespace vcpkg::PortFileProvider
struct PathsPortFileProvider : Util::ResourceBase, PortFileProvider
{
explicit PathsPortFileProvider(const vcpkg::VcpkgPaths& paths,
const std::vector<std::string>& ports_dirs_paths);
explicit PathsPortFileProvider(const vcpkg::VcpkgPaths& paths, const std::vector<std::string>& overlay_ports);
ExpectedS<const SourceControlFileLocation&> get_control_file(const std::string& src_name) const override;
std::vector<const SourceControlFileLocation*> load_all_control_files() const override;
private:
Files::Filesystem& filesystem;
std::vector<fs::path> ports_dirs;
const VcpkgPaths& paths;
std::vector<fs::path> overlay_ports;
mutable std::unordered_map<std::string, SourceControlFileLocation> cache;
};
}

Просмотреть файл

@ -0,0 +1,97 @@
#pragma once
#include <vcpkg/base/fwd/json.h>
#include <vcpkg/base/files.h>
#include <vcpkg/base/optional.h>
#include <vcpkg/vcpkgpaths.h>
#include <memory>
#include <string>
#include <system_error>
#include <vector>
namespace vcpkg
{
struct RegistryImpl
{
virtual fs::path get_registry_root(const VcpkgPaths& paths) const = 0;
virtual ~RegistryImpl() = default;
};
struct Registry
{
// requires: static_cast<bool>(implementation)
Registry(std::vector<std::string>&& packages, std::unique_ptr<RegistryImpl>&& implementation);
Registry(std::vector<std::string>&&, std::nullptr_t) = delete;
// always ordered lexicographically
Span<const std::string> packages() const { return packages_; }
const RegistryImpl& implementation() const { return *implementation_; }
static std::unique_ptr<RegistryImpl> builtin_registry();
private:
std::vector<std::string> packages_;
std::unique_ptr<RegistryImpl> implementation_;
};
struct RegistryImplDeserializer : Json::IDeserializer<std::unique_ptr<RegistryImpl>>
{
constexpr static StringLiteral KIND = "kind";
constexpr static StringLiteral PATH = "path";
constexpr static StringLiteral KIND_BUILTIN = "builtin";
constexpr static StringLiteral KIND_DIRECTORY = "directory";
virtual StringView type_name() const override;
virtual Span<const StringView> valid_fields() const override;
virtual Optional<std::unique_ptr<RegistryImpl>> visit_null(Json::Reader&, StringView) override;
virtual Optional<std::unique_ptr<RegistryImpl>> visit_object(Json::Reader&,
StringView,
const Json::Object&) override;
};
struct RegistryDeserializer final : Json::IDeserializer<Registry>
{
constexpr static StringLiteral PACKAGES = "packages";
virtual StringView type_name() const override;
virtual Span<const StringView> valid_fields() const override;
virtual Optional<Registry> visit_object(Json::Reader&, StringView, const Json::Object&) override;
};
// this type implements the registry fall back logic from the registries RFC:
// A port name maps to one of the non-default registries if that registry declares
// that it is the registry for that port name, else it maps to the default registry
// if that registry exists; else, there is no registry for a port.
// The way one sets this up is via the `"registries"` and `"default_registry"`
// configuration fields.
struct RegistrySet
{
RegistrySet();
// finds the correct registry for the port name
// Returns the null pointer if there is no registry set up for that name
const RegistryImpl* registry_for_port(StringView port_name) const;
Span<const Registry> registries() const { return registries_; }
const RegistryImpl* default_registry() const { return default_registry_.get(); }
// TODO: figure out how to get this to return an error (or maybe it should be a warning?)
void add_registry(Registry&& r);
void set_default_registry(std::unique_ptr<RegistryImpl>&& r);
void set_default_registry(std::nullptr_t r);
private:
std::unique_ptr<RegistryImpl> default_registry_;
std::vector<Registry> registries_;
};
}

Просмотреть файл

@ -1,7 +1,8 @@
#pragma once
#include <vcpkg/base/fwd/json.h>
#include <vcpkg/base/expected.h>
#include <vcpkg/base/json.h>
#include <vcpkg/base/span.h>
#include <vcpkg/base/system.h>
#include <vcpkg/base/system.print.h>

Просмотреть файл

@ -170,9 +170,12 @@ namespace vcpkg
Optional<bool> compiler_tracking = nullopt;
constexpr static StringLiteral MANIFEST_MODE_FEATURE = "manifests";
Optional<bool> manifest_mode = nullopt;
constexpr static StringLiteral REGISTRIES_FEATURE = "registries";
Optional<bool> registries_feature = nullopt;
bool binary_caching_enabled() const { return binary_caching.value_or(true); }
bool compiler_tracking_enabled() const { return compiler_tracking.value_or(true); }
bool registries_enabled() const { return registries_feature.value_or(false); }
bool output_json() const { return json.value_or(false); }
std::string command;

Просмотреть файл

@ -1,5 +1,10 @@
#pragma once
#include <vcpkg/base/fwd/json.h>
#include <vcpkg/fwd/configuration.h>
#include <vcpkg/fwd/registries.h>
#include <vcpkg/fwd/vcpkgcmdarguments.h>
#include <vcpkg/fwd/vcpkgpaths.h>
#include <vcpkg/base/cache.h>
@ -45,7 +50,6 @@ namespace vcpkg
}
struct BinaryParagraph;
struct VcpkgCmdArguments;
struct PackageSpec;
struct Triplet;
@ -77,6 +81,7 @@ namespace vcpkg
fs::path original_cwd;
fs::path root;
fs::path manifest_root_dir;
fs::path config_root_dir;
fs::path buildtrees;
fs::path downloads;
fs::path packages;
@ -102,6 +107,10 @@ namespace vcpkg
const fs::path& get_tool_exe(const std::string& tool) const;
const std::string& get_tool_version(const std::string& tool) const;
Optional<const Json::Object&> get_manifest() const;
Optional<const Json::JsonStyle&> get_manifest_style() const;
const Configuration& get_configuration() const;
/// <summary>Retrieve a toolset matching a VS version</summary>
/// <remarks>
/// Valid version strings are "v120", "v140", "v141", and "". Empty string gets the latest.
@ -112,7 +121,7 @@ namespace vcpkg
const System::Environment& get_action_env(const Build::AbiInfo& abi_info) const;
const std::string& get_triplet_info(const Build::AbiInfo& abi_info) const;
bool manifest_mode_enabled() const { return !manifest_root_dir.empty(); }
bool manifest_mode_enabled() const { return get_manifest().has_value(); }
void track_feature_flag_metrics() const;

Просмотреть файл

@ -155,6 +155,25 @@ namespace
}
}
TEST_CASE ("fs::combine works correctly", "[filesystem][files]")
{
using namespace fs;
using namespace vcpkg::Files;
CHECK(combine(u8path("/a/b"), u8path("c/d")) == u8path("/a/b/c/d"));
CHECK(combine(u8path("a/b"), u8path("c/d")) == u8path("a/b/c/d"));
CHECK(combine(u8path("/a/b"), u8path("/c/d")) == u8path("/c/d"));
#if defined(_WIN32)
CHECK(combine(u8path("C:/a/b"), u8path("c/d")) == u8path("C:/a/b/c/d"));
CHECK(combine(u8path("C:a/b"), u8path("c/d")) == u8path("C:a/b/c/d"));
CHECK(combine(u8path("C:a/b"), u8path("/c/d")) == u8path("C:/c/d"));
CHECK(combine(u8path("C:/a/b"), u8path("/c/d")) == u8path("C:/c/d"));
CHECK(combine(u8path("C:/a/b"), u8path("D:/c/d")) == u8path("D:/c/d"));
CHECK(combine(u8path("C:/a/b"), u8path("D:c/d")) == u8path("D:c/d"));
CHECK(combine(u8path("C:/a/b"), u8path("C:c/d")) == u8path("C:/a/b/c/d"));
#endif
}
TEST_CASE ("remove all", "[files]")
{
auto urbg = get_urbg(0);

Просмотреть файл

@ -1,6 +1,7 @@
#include <catch2/catch.hpp>
#include <vcpkg/base/optional.h>
#include <vcpkg/base/util.h>
#include <vector>
@ -27,9 +28,39 @@ TEST_CASE ("equal", "[optional]")
CHECK(Optional<int>{42} == Optional<int>{42});
}
TEST_CASE ("ref conversion", "[optional]")
{
using vcpkg::Optional;
Optional<int> i_empty;
Optional<int> i_1 = 1;
const Optional<int> ci_1 = 1;
Optional<int&> ref_empty = i_empty;
Optional<const int&> cref_empty = i_empty;
Optional<int&> ref_1 = i_1;
Optional<const int&> cref_1 = ci_1;
REQUIRE(ref_empty.has_value() == false);
REQUIRE(cref_empty.has_value() == false);
REQUIRE(ref_1.get() == i_1.get());
REQUIRE(cref_1.get() == ci_1.get());
ref_empty = i_1;
cref_empty = ci_1;
REQUIRE(ref_empty.get() == i_1.get());
REQUIRE(cref_empty.get() == ci_1.get());
const int x = 5;
cref_1 = x;
REQUIRE(cref_1.get() == &x);
}
TEST_CASE ("common_projection", "[optional]")
{
using vcpkg::common_projection;
using vcpkg::Util::common_projection;
std::vector<int> input;
CHECK(!common_projection(input, identity_projection{}).has_value());
input.push_back(42);

Просмотреть файл

@ -498,8 +498,7 @@ namespace vcpkg::Files
return output;
}
virtual fs::path find_file_recursively_up(const fs::path& starting_dir,
const std::string& filename) const override
virtual fs::path find_file_recursively_up(const fs::path& starting_dir, const fs::path& filename) const override
{
fs::path current_dir = starting_dir;
if (exists(VCPKG_LINE_INFO, current_dir / filename))
@ -1154,7 +1153,8 @@ namespace vcpkg::Files
{
#if VCPKG_USE_STD_FILESYSTEM
return lhs / rhs;
#else // ^^^ VCPKG_USE_STD_FILESYSTEM // !VCPKG_USE_STD_FILESYSTEM vvv
#else // ^^^ std::filesystem // std::experimental::filesystem vvv
#if !defined(_WIN32)
if (rhs.is_absolute())
{
return rhs;
@ -1163,6 +1163,37 @@ namespace vcpkg::Files
{
return lhs / rhs;
}
#endif
#else // ^^^ unix // windows vvv
auto rhs_root_directory = rhs.root_directory();
auto rhs_root_name = rhs.root_name();
if (rhs_root_directory.empty() && rhs_root_name.empty())
{
return lhs / rhs;
}
else if (rhs_root_directory.empty())
{
// !rhs_root_name.empty()
if (rhs_root_name == lhs.root_name())
{
return lhs / rhs.relative_path();
}
else
{
return rhs;
}
}
else if (rhs_root_name.empty())
{
// !rhs_root_directory.empty()
return lhs.root_name() / rhs;
}
else
{
// rhs.absolute()
return rhs;
}
#endif // ^^^ windows
#endif // ^^^ std::experimental::filesystem
}
}

Просмотреть файл

@ -5,6 +5,8 @@
#include <inttypes.h>
#include <regex>
namespace vcpkg::Json
{
using VK = ValueKind;
@ -986,6 +988,33 @@ namespace vcpkg::Json
};
}
bool IdentifierDeserializer::is_ident(StringView sv)
{
static const std::regex BASIC_IDENTIFIER = std::regex(R"([a-z0-9]+(-[a-z0-9]+)*)");
// we only check for lowercase in RESERVED since we already remove all
// strings with uppercase letters from the basic check
static const std::regex RESERVED = std::regex(R"(prn|aux|nul|con|(lpt|com)[1-9]|core|default)");
// back-compat
if (sv == "all_modules")
{
return true;
}
if (!std::regex_match(sv.begin(), sv.end(), BASIC_IDENTIFIER))
{
return false; // we're not even in the shape of an identifier
}
if (std::regex_match(sv.begin(), sv.end(), RESERVED))
{
return false; // we're a reserved identifier
}
return true;
}
ExpectedT<std::pair<Value, JsonStyle>, std::unique_ptr<Parse::IParseError>> parse_file(const Files::Filesystem& fs,
const fs::path& path,
std::error_code& ec) noexcept
@ -1002,6 +1031,26 @@ namespace vcpkg::Json
}
}
std::pair<Value, JsonStyle> parse_file(vcpkg::LineInfo linfo,
const Files::Filesystem& fs,
const fs::path& path) noexcept
{
std::error_code ec;
auto ret = parse_file(fs, path, ec);
if (ec)
{
System::print2(System::Color::error, "Failed to read ", fs::u8string(path), ": ", ec.message(), "\n");
Checks::exit_fail(linfo);
}
else if (!ret)
{
System::print2(System::Color::error, "Failed to parse ", fs::u8string(path), ":\n");
System::print2(ret.error()->format());
Checks::exit_fail(linfo);
}
return ret.value_or_exit(linfo);
}
ExpectedT<std::pair<Value, JsonStyle>, std::unique_ptr<Parse::IParseError>> parse(StringView json,
const fs::path& filepath) noexcept
{

Просмотреть файл

@ -14,6 +14,7 @@
#include <vcpkg/input.h>
#include <vcpkg/install.h>
#include <vcpkg/packagespec.h>
#include <vcpkg/paragraphs.h>
#include <vcpkg/platform-expression.h>
#include <vcpkg/vcpkgcmdarguments.h>
#include <vcpkg/vcpkglib.h>
@ -457,9 +458,7 @@ namespace vcpkg::Commands::CI
XunitTestResults xunitTestResults;
std::vector<std::string> all_ports =
Util::fmap(provider.load_all_control_files(), [](const SourceControlFileLocation*& scfl) -> std::string {
return scfl->source_control_file.get()->core_paragraph->name;
});
Util::fmap(provider.load_all_control_files(), Paragraphs::get_name_of_control_file);
std::vector<TripletAndSummary> results;
auto timer = Chrono::ElapsedTimer::create_started();
for (Triplet triplet : triplets)

Просмотреть файл

@ -85,10 +85,9 @@ namespace vcpkg::Commands::Edit
static std::vector<std::string> valid_arguments(const VcpkgPaths& paths)
{
auto sources_and_errors = Paragraphs::try_load_all_ports(paths.get_filesystem(), paths.ports);
auto sources_and_errors = Paragraphs::try_load_all_registry_ports(paths);
return Util::fmap(sources_and_errors.paragraphs,
[](auto&& pgh) -> std::string { return pgh->core_paragraph->name; });
return Util::fmap(sources_and_errors.paragraphs, Paragraphs::get_name_of_control_file);
}
static constexpr std::array<CommandSwitch, 2> EDIT_SWITCHES = {

Просмотреть файл

@ -8,6 +8,7 @@
#include <vcpkg/portfileprovider.h>
#include <vcpkg/sourceparagraph.h>
#include <vcpkg/vcpkgcmdarguments.h>
#include <vcpkg/vcpkgpaths.h>
namespace
{
@ -41,7 +42,9 @@ namespace
return nullopt;
}
auto scf = SourceControlFile::parse_manifest_file(manifest_path, parsed_json.object());
auto parsed_json_obj = parsed_json.object();
auto scf = SourceControlFile::parse_manifest_file(manifest_path, parsed_json_obj);
if (!scf.has_value())
{
System::printf(System::Color::error, "Failed to parse manifest file: %s\n", path_string);

Просмотреть файл

@ -100,13 +100,13 @@ namespace vcpkg::Commands::PortsDiff
System::cmd_execute_and_capture_output(cmd, System::get_clean_environment());
System::cmd_execute_and_capture_output(Strings::format(R"("%s" reset)", fs::u8string(git_exe)),
System::get_clean_environment());
const auto all_ports =
Paragraphs::load_all_ports(paths.get_filesystem(), temp_checkout_path / ports_dir_name_as_string);
const auto ports_at_commit =
Paragraphs::load_overlay_ports(paths, temp_checkout_path / ports_dir_name_as_string);
std::map<std::string, VersionT> names_and_versions;
for (auto&& port : all_ports)
for (auto&& port : ports_at_commit)
{
const auto& core_pgh = *port->core_paragraph;
names_and_versions.emplace(port->core_paragraph->name, VersionT(core_pgh.version, core_pgh.port_version));
const auto& core_pgh = *port.source_control_file->core_paragraph;
names_and_versions.emplace(core_pgh.name, VersionT(core_pgh.version, core_pgh.port_version));
}
fs.remove_all(temp_checkout_path, VCPKG_LINE_INFO);
return names_and_versions;

Просмотреть файл

@ -0,0 +1,66 @@
#include <vcpkg/base/system.print.h>
#include <vcpkg/configuration.h>
#include <vcpkg/vcpkgcmdarguments.h>
namespace vcpkg
{
Optional<Configuration> ConfigurationDeserializer::visit_object(Json::Reader& r,
StringView,
const Json::Object& obj)
{
RegistrySet registries;
bool registries_feature_flags_warning = false;
{
std::unique_ptr<RegistryImpl> default_registry;
if (r.optional_object_field(obj, DEFAULT_REGISTRY, default_registry, RegistryImplDeserializer{}))
{
if (!registries_enabled)
{
registries_feature_flags_warning = true;
}
else
{
registries.set_default_registry(std::move(default_registry));
}
}
}
std::vector<Registry> regs;
r.optional_object_field(
obj,
REGISTRIES,
regs,
Json::ArrayDeserializer<RegistryDeserializer>{"an array of registries", Json::AllowEmpty::Yes});
if (!regs.empty() && !registries_enabled)
{
registries_feature_flags_warning = true;
regs.clear();
}
for (Registry& reg : regs)
{
registries.add_registry(std::move(reg));
}
if (registries_feature_flags_warning && !print_json)
{
System::printf(System::Color::warning,
"Warning: configuration specified the \"registries\" or \"default-registries\" field, but "
"the %s feature flag was not enabled.\n",
VcpkgCmdArguments::REGISTRIES_FEATURE);
}
return Configuration{std::move(registries)};
}
ConfigurationDeserializer::ConfigurationDeserializer(const VcpkgCmdArguments& args)
{
registries_enabled = args.registries_enabled();
print_json = args.output_json();
}
}

Просмотреть файл

@ -551,10 +551,9 @@ namespace vcpkg::Install
std::vector<std::string> get_all_port_names(const VcpkgPaths& paths)
{
auto sources_and_errors = Paragraphs::try_load_all_ports(paths.get_filesystem(), paths.ports);
auto sources_and_errors = Paragraphs::try_load_all_registry_ports(paths);
return Util::fmap(sources_and_errors.paragraphs,
[](auto&& pgh) -> std::string { return pgh->core_paragraph->name; });
return Util::fmap(sources_and_errors.paragraphs, Paragraphs::get_name_of_control_file);
}
const CommandStructure COMMAND_STRUCTURE = {
@ -781,7 +780,7 @@ namespace vcpkg::Install
auto var_provider_storage = CMakeVars::make_triplet_cmake_var_provider(paths);
auto& var_provider = *var_provider_storage;
if (paths.manifest_mode_enabled())
if (auto manifest = paths.get_manifest().get())
{
Optional<fs::path> pkgsconfig;
auto it_pkgsconfig = options.settings.find(OPTION_WRITE_PACKAGES_CONFIG);
@ -790,18 +789,12 @@ namespace vcpkg::Install
pkgsconfig = fs::u8path(it_pkgsconfig->second);
}
std::error_code ec;
auto manifest_path = paths.manifest_root_dir / fs::u8path("vcpkg.json");
auto maybe_manifest_scf = Paragraphs::try_load_manifest(fs, "manifest", manifest_path, ec);
if (ec)
{
Checks::exit_with_message(
VCPKG_LINE_INFO, "Failed to read manifest %s: %s", fs::u8string(manifest_path), ec.message());
}
else if (!maybe_manifest_scf)
auto maybe_manifest_scf = SourceControlFile::parse_manifest_file("manifest", *manifest);
if (!maybe_manifest_scf)
{
print_error_message(maybe_manifest_scf.error());
Checks::exit_with_message(VCPKG_LINE_INFO, "Failed to read manifest %s.", fs::u8string(manifest_path));
Checks::exit_with_message(
VCPKG_LINE_INFO, "Failed to parse manifest %s/vcpkg.json.", fs::u8string(paths.manifest_root_dir));
}
auto& manifest_scf = *maybe_manifest_scf.value_or_exit(VCPKG_LINE_INFO);

Просмотреть файл

@ -5,8 +5,10 @@
#include <vcpkg/base/util.h>
#include <vcpkg/binaryparagraph.h>
#include <vcpkg/configuration.h>
#include <vcpkg/paragraphparser.h>
#include <vcpkg/paragraphs.h>
#include <vcpkg/registries.h>
using namespace vcpkg::Parse;
using namespace vcpkg;
@ -259,10 +261,10 @@ namespace vcpkg::Paragraphs
return fs.exists(path / fs::u8path("CONTROL")) || fs.exists(path / fs::u8path("vcpkg.json"));
}
ParseExpected<SourceControlFile> try_load_manifest(const Files::Filesystem& fs,
const std::string& port_name,
const fs::path& path_to_manifest,
std::error_code& ec)
static ParseExpected<SourceControlFile> try_load_manifest(const Files::Filesystem& fs,
const std::string& port_name,
const fs::path& path_to_manifest,
std::error_code& ec)
{
auto error_info = std::make_unique<ParseControlErrorInfo>();
auto res = Json::parse_file(fs, path_to_manifest, ec);
@ -343,34 +345,84 @@ namespace vcpkg::Paragraphs
return pghs.error();
}
LoadResults try_load_all_ports(const Files::Filesystem& fs, const fs::path& ports_dir)
static void load_port_names_from_root(std::vector<std::string>& ports,
const VcpkgPaths& paths,
const fs::path& registry_root)
{
LoadResults ret;
auto port_dirs = fs.get_files_non_recursive(ports_dir);
const auto& fs = paths.get_filesystem();
auto port_dirs = fs.get_files_non_recursive(registry_root);
Util::sort(port_dirs);
// TODO: search in `b-` for ports starting with `b`
Util::erase_remove_if(port_dirs, [&](auto&& port_dir_entry) {
return fs.is_regular_file(port_dir_entry) && port_dir_entry.filename() == ".DS_Store";
});
for (auto&& path : port_dirs)
{
auto maybe_spgh = try_load_port(fs, path);
ports.push_back(fs::u8string(path.filename()));
}
}
LoadResults try_load_all_registry_ports(const VcpkgPaths& paths)
{
LoadResults ret;
const auto& fs = paths.get_filesystem();
std::vector<std::string> ports;
const auto& registries = paths.get_configuration().registry_set;
for (const auto& registry : registries.registries())
{
load_port_names_from_root(ports, paths, registry.implementation().get_registry_root(paths));
}
if (auto registry = registries.default_registry())
{
load_port_names_from_root(ports, paths, registry->get_registry_root(paths));
}
Util::sort_unique_erase(ports);
for (const auto& port_name : ports)
{
auto impl = registries.registry_for_port(port_name);
if (!impl)
{
// this is a port for which no registry is set
// this can happen when there's no default registry,
// and a registry has a port definition which it doesn't own the name of.
continue;
}
auto root = impl->get_registry_root(paths);
auto port_path = root / fs::u8path(port_name);
if (!fs.exists(port_path))
{
// the registry that owns the name of this port does not actually contain the port
// this can happen if R1 contains the port definition for <abc>, but doesn't
// declare it owns <abc>.
continue;
}
auto maybe_spgh = try_load_port(fs, port_path);
if (const auto spgh = maybe_spgh.get())
{
ret.paragraphs.emplace_back(std::move(*spgh));
ret.paragraphs.emplace_back(std::move(*spgh), std::move(port_path));
}
else
{
ret.errors.emplace_back(std::move(maybe_spgh).error());
}
}
return ret;
}
std::vector<std::unique_ptr<SourceControlFile>> load_all_ports(const Files::Filesystem& fs,
const fs::path& ports_dir)
static void load_results_print_error(const LoadResults& results)
{
auto results = try_load_all_ports(fs, ports_dir);
if (!results.errors.empty())
{
if (Debug::g_debugging)
@ -388,6 +440,39 @@ namespace vcpkg::Paragraphs
"Use '--debug' to get more information about the parse failures.\n\n");
}
}
}
std::vector<SourceControlFileLocation> load_all_registry_ports(const VcpkgPaths& paths)
{
auto results = try_load_all_registry_ports(paths);
load_results_print_error(results);
return std::move(results.paragraphs);
}
std::vector<SourceControlFileLocation> load_overlay_ports(const VcpkgPaths& paths, const fs::path& directory)
{
LoadResults ret;
std::vector<std::string> port_names;
load_port_names_from_root(port_names, paths, directory);
const auto& fs = paths.get_filesystem();
for (const auto& name : port_names)
{
auto path = directory / fs::u8path(name);
auto maybe_spgh = try_load_port(fs, path);
if (const auto spgh = maybe_spgh.get())
{
ret.paragraphs.emplace_back(std::move(*spgh), std::move(path));
}
else
{
ret.errors.emplace_back(std::move(maybe_spgh).error());
}
}
load_results_print_error(ret);
return std::move(ret.paragraphs);
}
}

Просмотреть файл

@ -1,7 +1,9 @@
#include <vcpkg/base/system.debug.h>
#include <vcpkg/configuration.h>
#include <vcpkg/paragraphs.h>
#include <vcpkg/portfileprovider.h>
#include <vcpkg/registries.h>
#include <vcpkg/sourceparagraph.h>
namespace vcpkg::PortFileProvider
@ -23,12 +25,12 @@ namespace vcpkg::PortFileProvider
return Util::fmap(ports, [](auto&& kvpair) -> const SourceControlFileLocation* { return &kvpair.second; });
}
PathsPortFileProvider::PathsPortFileProvider(const vcpkg::VcpkgPaths& paths,
const std::vector<std::string>& ports_dirs_paths)
: filesystem(paths.get_filesystem())
PathsPortFileProvider::PathsPortFileProvider(const vcpkg::VcpkgPaths& paths_,
const std::vector<std::string>& overlay_ports_)
: paths(paths_)
{
auto& fs = paths.get_filesystem();
for (auto&& overlay_path : ports_dirs_paths)
for (auto&& overlay_path : overlay_ports_)
{
if (!overlay_path.empty())
{
@ -45,17 +47,16 @@ namespace vcpkg::PortFileProvider
Debug::print("Using overlay: ", fs::u8string(overlay), "\n");
Checks::check_exit(
VCPKG_LINE_INFO, filesystem.exists(overlay), "Error: Path \"%s\" does not exist", overlay.string());
VCPKG_LINE_INFO, fs.exists(overlay), "Error: Path \"%s\" does not exist", fs::u8string(overlay));
Checks::check_exit(VCPKG_LINE_INFO,
fs::is_directory(fs.status(VCPKG_LINE_INFO, overlay)),
"Error: Path \"%s\" must be a directory",
overlay.string());
ports_dirs.emplace_back(overlay);
overlay_ports.emplace_back(overlay);
}
}
ports_dirs.emplace_back(paths.ports);
}
ExpectedS<const SourceControlFileLocation&> PathsPortFileProvider::get_control_file(const std::string& spec) const
@ -66,12 +67,14 @@ namespace vcpkg::PortFileProvider
return cache_it->second;
}
for (auto&& ports_dir : ports_dirs)
const auto& fs = paths.get_filesystem();
for (auto&& ports_dir : overlay_ports)
{
// Try loading individual port
if (Paragraphs::is_port_directory(filesystem, ports_dir))
if (Paragraphs::is_port_directory(fs, ports_dir))
{
auto maybe_scf = Paragraphs::try_load_port(filesystem, ports_dir);
auto maybe_scf = Paragraphs::try_load_port(fs, ports_dir);
if (auto scf = maybe_scf.get())
{
if (scf->get()->core_paragraph->name == spec)
@ -92,22 +95,22 @@ namespace vcpkg::PortFileProvider
continue;
}
auto ports_spec = ports_dir / spec;
if (Paragraphs::is_port_directory(filesystem, ports_spec))
auto ports_spec = ports_dir / fs::u8path(spec);
if (Paragraphs::is_port_directory(fs, ports_spec))
{
auto found_scf = Paragraphs::try_load_port(filesystem, ports_spec);
auto found_scf = Paragraphs::try_load_port(fs, ports_spec);
if (auto scf = found_scf.get())
{
if (scf->get()->core_paragraph->name == spec)
{
auto it = cache.emplace(std::piecewise_construct,
std::forward_as_tuple(spec),
std::forward_as_tuple(std::move(*scf), ports_dir / spec));
std::forward_as_tuple(std::move(spec)),
std::forward_as_tuple(std::move(*scf), std::move(ports_spec)));
return it.first->second;
}
Checks::exit_with_message(VCPKG_LINE_INFO,
"Error: Failed to load port from %s: names did not match: '%s' != '%s'",
fs::u8string(ports_dir / spec),
fs::u8string(ports_spec),
spec,
scf->get()->core_paragraph->name);
}
@ -115,7 +118,39 @@ namespace vcpkg::PortFileProvider
{
vcpkg::print_error_message(found_scf.error());
Checks::exit_with_message(
VCPKG_LINE_INFO, "Error: Failed to load port from %s", spec, fs::u8string(ports_dir));
VCPKG_LINE_INFO, "Error: Failed to load port %s from %s", spec, fs::u8string(ports_dir));
}
}
}
if (auto registry = paths.get_configuration().registry_set.registry_for_port(spec))
{
auto registry_root = registry->get_registry_root(paths);
auto port_directory = registry_root / fs::u8path(spec);
if (fs.exists(port_directory))
{
auto found_scf = Paragraphs::try_load_port(fs, port_directory);
if (auto scf = found_scf.get())
{
if (scf->get()->core_paragraph->name == spec)
{
auto it = cache.emplace(std::piecewise_construct,
std::forward_as_tuple(spec),
std::forward_as_tuple(std::move(*scf), std::move(port_directory)));
return it.first->second;
}
Checks::exit_with_message(VCPKG_LINE_INFO,
"Error: Failed to load port from %s: names did not match: '%s' != '%s'",
fs::u8string(port_directory),
spec,
scf->get()->core_paragraph->name);
}
else
{
vcpkg::print_error_message(found_scf.error());
Checks::exit_with_message(
VCPKG_LINE_INFO, "Error: Failed to load port %s from %s", spec, fs::u8string(port_directory));
}
}
}
@ -129,20 +164,19 @@ namespace vcpkg::PortFileProvider
cache.clear();
std::vector<const SourceControlFileLocation*> ret;
for (auto&& ports_dir : ports_dirs)
for (const fs::path& ports_dir : overlay_ports)
{
// Try loading individual port
if (Paragraphs::is_port_directory(filesystem, ports_dir))
if (Paragraphs::is_port_directory(paths.get_filesystem(), ports_dir))
{
auto maybe_scf = Paragraphs::try_load_port(filesystem, ports_dir);
auto maybe_scf = Paragraphs::try_load_port(paths.get_filesystem(), ports_dir);
if (auto scf = maybe_scf.get())
{
auto port_name = scf->get()->core_paragraph->name;
if (cache.find(port_name) == cache.end())
{
auto it = cache.emplace(std::piecewise_construct,
std::forward_as_tuple(port_name),
std::forward_as_tuple(std::move(*scf), ports_dir));
auto scfl = SourceControlFileLocation{std::move(*scf), ports_dir};
auto it = cache.emplace(std::move(port_name), std::move(scfl));
ret.emplace_back(&it.first->second);
}
}
@ -156,19 +190,29 @@ namespace vcpkg::PortFileProvider
}
// Try loading all ports inside ports_dir
auto found_scf = Paragraphs::load_all_ports(filesystem, ports_dir);
for (auto&& scf : found_scf)
auto found_scfls = Paragraphs::load_overlay_ports(paths, ports_dir);
for (auto&& scfl : found_scfls)
{
auto port_name = scf->core_paragraph->name;
auto port_name = scfl.source_control_file->core_paragraph->name;
if (cache.find(port_name) == cache.end())
{
auto it = cache.emplace(std::piecewise_construct,
std::forward_as_tuple(port_name),
std::forward_as_tuple(std::move(scf), ports_dir / port_name));
auto it = cache.emplace(std::move(port_name), std::move(scfl));
ret.emplace_back(&it.first->second);
}
}
}
auto all_ports = Paragraphs::load_all_registry_ports(paths);
for (auto&& scfl : all_ports)
{
auto port_name = scfl.source_control_file->core_paragraph->name;
if (cache.find(port_name) == cache.end())
{
auto it = cache.emplace(port_name, std::move(scfl));
ret.emplace_back(&it.first->second);
}
}
return ret;
}
}

128
src/vcpkg/registries.cpp Normal file
Просмотреть файл

@ -0,0 +1,128 @@
#include <vcpkg/base/json.h>
#include <vcpkg/registries.h>
namespace
{
struct BuiltinRegistry final : vcpkg::RegistryImpl
{
virtual fs::path get_registry_root(const vcpkg::VcpkgPaths& paths) const override { return paths.ports; }
};
struct DirectoryRegistry final : vcpkg::RegistryImpl
{
virtual fs::path get_registry_root(const vcpkg::VcpkgPaths& paths) const override
{
return vcpkg::Files::combine(paths.config_root_dir, path);
}
DirectoryRegistry(fs::path&& path_) : path(path_) { }
fs::path path;
};
}
namespace vcpkg
{
std::unique_ptr<RegistryImpl> Registry::builtin_registry() { return std::make_unique<BuiltinRegistry>(); }
Registry::Registry(std::vector<std::string>&& packages, std::unique_ptr<RegistryImpl>&& impl)
: packages_(std::move(packages)), implementation_(std::move(impl))
{
Checks::check_exit(VCPKG_LINE_INFO, implementation_ != nullptr);
}
StringView RegistryImplDeserializer::type_name() const { return "a registry"; }
Span<const StringView> RegistryImplDeserializer::valid_fields() const
{
static const StringView t[] = {KIND, PATH};
return t;
}
Optional<std::unique_ptr<RegistryImpl>> RegistryImplDeserializer::visit_null(Json::Reader&, StringView)
{
return nullptr;
}
Optional<std::unique_ptr<RegistryImpl>> RegistryImplDeserializer::visit_object(Json::Reader& r,
StringView,
const Json::Object& obj)
{
std::string kind;
r.required_object_field(
type_name(), obj, KIND, kind, Json::StringDeserializer{"a registry implementation kind"});
if (kind == KIND_BUILTIN)
{
if (obj.contains(PATH))
{
r.error().add_extra_fields(type_name().to_string(), {PATH});
}
return static_cast<std::unique_ptr<RegistryImpl>>(std::make_unique<BuiltinRegistry>());
}
else if (kind == KIND_DIRECTORY)
{
fs::path path;
r.required_object_field(type_name(), obj, PATH, path, Json::PathDeserializer{});
return static_cast<std::unique_ptr<RegistryImpl>>(std::make_unique<DirectoryRegistry>(std::move(path)));
}
else
{
return nullopt;
}
}
StringView RegistryDeserializer::type_name() const { return "a registry"; }
Span<const StringView> RegistryDeserializer::valid_fields() const
{
static const StringView t[] = {
RegistryImplDeserializer::KIND,
RegistryImplDeserializer::PATH,
PACKAGES,
};
return t;
}
Optional<Registry> RegistryDeserializer::visit_object(Json::Reader& r, StringView key, const Json::Object& obj)
{
auto impl = RegistryImplDeserializer{}.visit_object(r, key, obj);
if (!impl.has_value())
{
return nullopt;
}
std::vector<std::string> packages;
r.required_object_field(
type_name(),
obj,
PACKAGES,
packages,
Json::ArrayDeserializer<Json::PackageNameDeserializer>{"an array of registries", Json::AllowEmpty::Yes});
return Registry{std::move(packages), std::move(impl).value_or_exit(VCPKG_LINE_INFO)};
}
RegistrySet::RegistrySet() : default_registry_(Registry::builtin_registry()), registries_() { }
const RegistryImpl* RegistrySet::registry_for_port(StringView name) const
{
for (const auto& registry : registries())
{
const auto& packages = registry.packages();
if (std::find(packages.begin(), packages.end(), name) != packages.end())
{
return &registry.implementation();
}
}
return default_registry();
}
void RegistrySet::add_registry(Registry&& r) { registries_.push_back(std::move(r)); }
void RegistrySet::set_default_registry(std::unique_ptr<RegistryImpl>&& r) { default_registry_ = std::move(r); }
void RegistrySet::set_default_registry(std::nullptr_t) { default_registry_.reset(); }
}

Просмотреть файл

@ -80,24 +80,6 @@ namespace vcpkg
static const std::string SUPPORTS = "Supports";
}
namespace ManifestFields
{
constexpr static StringLiteral NAME = "name";
constexpr static StringLiteral VERSION = "version-string";
constexpr static StringLiteral PORT_VERSION = "port-version";
constexpr static StringLiteral MAINTAINERS = "maintainers";
constexpr static StringLiteral DESCRIPTION = "description";
constexpr static StringLiteral HOMEPAGE = "homepage";
constexpr static StringLiteral DOCUMENTATION = "documentation";
constexpr static StringLiteral LICENSE = "license";
constexpr static StringLiteral DEPENDENCIES = "dependencies";
constexpr static StringLiteral DEV_DEPENDENCIES = "dev-dependencies";
constexpr static StringLiteral FEATURES = "features";
constexpr static StringLiteral DEFAULT_FEATURES = "default-features";
constexpr static StringLiteral SUPPORTS = "supports";
}
static Span<const StringView> get_list_of_valid_fields()
{
static const StringView valid_fields[] = {
@ -116,111 +98,7 @@ namespace vcpkg
return valid_fields;
}
static Span<const StringView> get_list_of_manifest_fields()
{
constexpr static StringView valid_fields[] = {
ManifestFields::NAME,
ManifestFields::VERSION,
ManifestFields::PORT_VERSION,
ManifestFields::MAINTAINERS,
ManifestFields::DESCRIPTION,
ManifestFields::HOMEPAGE,
ManifestFields::DOCUMENTATION,
ManifestFields::LICENSE,
ManifestFields::DEPENDENCIES,
ManifestFields::DEV_DEPENDENCIES,
ManifestFields::FEATURES,
ManifestFields::DEFAULT_FEATURES,
ManifestFields::SUPPORTS,
};
return valid_fields;
}
void print_error_message(Span<const std::unique_ptr<Parse::ParseControlErrorInfo>> error_info_list)
{
Checks::check_exit(VCPKG_LINE_INFO, error_info_list.size() > 0);
for (auto&& error_info : error_info_list)
{
Checks::check_exit(VCPKG_LINE_INFO, error_info != nullptr);
if (!error_info->error.empty())
{
System::print2(
System::Color::error, "Error: while loading ", error_info->name, ":\n", error_info->error, '\n');
}
}
bool have_remaining_fields = false;
for (auto&& error_info : error_info_list)
{
if (!error_info->extra_fields.empty())
{
System::print2(System::Color::error,
"Error: There are invalid fields in the control or manifest file of ",
error_info->name,
'\n');
System::print2("The following fields were not expected:\n");
for (const auto& pr : error_info->extra_fields)
{
System::print2(" In ", pr.first, ": ", Strings::join(", ", pr.second), "\n");
}
have_remaining_fields = true;
}
}
if (have_remaining_fields)
{
System::print2("This is the list of valid fields for CONTROL files (case-sensitive): \n\n ",
Strings::join("\n ", get_list_of_valid_fields()),
"\n\n");
System::print2("And this is the list of valid fields for manifest files: \n\n ",
Strings::join("\n ", get_list_of_manifest_fields()),
"\n\n");
#if defined(_WIN32)
auto bootstrap = ".\\bootstrap-vcpkg.bat";
#else
auto bootstrap = "./bootstrap-vcpkg.sh";
#endif
System::print2("You may need to update the vcpkg binary; try running %s to update.\n\n", bootstrap);
}
for (auto&& error_info : error_info_list)
{
if (!error_info->missing_fields.empty())
{
System::print2(System::Color::error,
"Error: There are missing fields in the control file of ",
error_info->name,
'\n');
System::print2("The following fields were missing:\n");
for (const auto& pr : error_info->missing_fields)
{
System::print2(" In ", pr.first, ": ", Strings::join(", ", pr.second), "\n");
}
}
}
for (auto&& error_info : error_info_list)
{
if (!error_info->expected_types.empty())
{
System::print2(System::Color::error,
"Error: There are invalid field types in the CONTROL or manifest file of ",
error_info->name,
'\n');
System::print2("The following fields had the wrong types:\n\n");
for (const auto& pr : error_info->expected_types)
{
System::printf(" %s was expected to be %s\n", pr.first, pr.second);
}
System::print2("\n");
}
}
}
void print_error_message(Span<const std::unique_ptr<Parse::ParseControlErrorInfo>> error_info_list);
std::string Type::to_string(const Type& t)
{
@ -479,202 +357,136 @@ namespace vcpkg
return control_file;
}
static std::vector<std::string> invalid_json_fields(const Json::Object& obj,
Span<const StringView> known_fields) noexcept
struct PlatformExprDeserializer : Json::IDeserializer<PlatformExpression::Expr>
{
const auto field_is_unknown = [known_fields](StringView sv) {
// allow directives
if (sv.size() != 0 && *sv.begin() == '$')
{
return false;
}
return std::find(known_fields.begin(), known_fields.end(), sv) == known_fields.end();
};
virtual StringView type_name() const override { return "a platform expression"; }
std::vector<std::string> res;
for (const auto& kv : obj)
virtual Optional<PlatformExpression::Expr> visit_string(Json::Reader&, StringView, StringView sv) override
{
if (field_is_unknown(kv.first))
auto opt =
PlatformExpression::parse_platform_expression(sv, PlatformExpression::MultipleBinaryOperators::Deny);
if (auto res = opt.get())
{
res.push_back(kv.first.to_string());
}
}
return res;
}
struct StringField : Json::VisitorCrtpBase<StringField>
{
using type = std::string;
StringView type_name() { return type_name_; }
Optional<std::string> visit_string(Json::Reader&, StringView, StringView sv) { return sv.to_string(); }
explicit StringField(StringView type_name_) : type_name_(type_name_) { }
private:
StringView type_name_;
};
struct NaturalNumberField : Json::VisitorCrtpBase<NaturalNumberField>
{
using type = int;
StringView type_name() { return "a natural number"; }
Optional<int> visit_integer(Json::Reader&, StringView, int64_t value)
{
if (value > std::numeric_limits<int>::max() || value < 0)
{
return nullopt;
}
return static_cast<int>(value);
}
};
struct BooleanField : Json::VisitorCrtpBase<BooleanField>
{
using type = bool;
StringView type_name() { return "a boolean"; }
Optional<bool> visit_boolean(Json::Reader&, StringView, bool b) { return b; }
};
enum class AllowEmpty : bool
{
No,
Yes,
};
template<class T>
struct ArrayField : Json::VisitorCrtpBase<ArrayField<T>>
{
using type = std::vector<typename T::type>;
StringView type_name() { return type_name_; }
ArrayField(StringView type_name_, AllowEmpty allow_empty, T&& t = {})
: type_name_(type_name_), underlying_visitor_(static_cast<T&&>(t)), allow_empty_(allow_empty)
{
}
Optional<type> visit_array(Json::Reader& r, StringView key, const Json::Array& arr)
{
if (allow_empty_ == AllowEmpty::No && arr.size() == 0)
{
return nullopt;
}
return r.array_elements(arr, key, underlying_visitor_);
}
private:
StringView type_name_;
T underlying_visitor_;
AllowEmpty allow_empty_;
};
struct ParagraphField : Json::VisitorCrtpBase<ParagraphField>
{
using type = std::vector<std::string>;
StringView type_name() { return "a string or array of strings"; }
Optional<std::vector<std::string>> visit_string(Json::Reader&, StringView, StringView sv)
{
std::vector<std::string> out;
out.push_back(sv.to_string());
return out;
}
Optional<std::vector<std::string>> visit_array(Json::Reader& r, StringView key, const Json::Array& arr)
{
return r.array_elements(arr, key, StringField{"a string"});
}
};
struct IdentifierField : Json::VisitorCrtpBase<IdentifierField>
{
using type = std::string;
StringView type_name() { return "an identifier"; }
// [a-z0-9]+(-[a-z0-9]+)*, plus not any of {prn, aux, nul, con, lpt[1-9], com[1-9], core, default}
static bool is_ident(StringView sv)
{
static const std::regex BASIC_IDENTIFIER = std::regex(R"([a-z0-9]+(-[a-z0-9]+)*)");
// we only check for lowercase in RESERVED since we already remove all
// strings with uppercase letters from the basic check
static const std::regex RESERVED = std::regex(R"(prn|aux|nul|con|(lpt|com)[1-9]|core|default)");
// back-compat
if (sv == "all_modules")
{
return true;
}
if (!std::regex_match(sv.begin(), sv.end(), BASIC_IDENTIFIER))
{
return false; // we're not even in the shape of an identifier
}
if (std::regex_match(sv.begin(), sv.end(), RESERVED))
{
return false; // we're a reserved identifier
}
return true;
}
Optional<std::string> visit_string(Json::Reader&, StringView, StringView sv)
{
if (is_ident(sv))
{
return sv.to_string();
return std::move(*res);
}
else
{
Debug::print("Failed to parse platform expression: ", opt.error(), "\n");
return nullopt;
}
}
};
struct PackageNameField : Json::VisitorCrtpBase<PackageNameField>
struct DependencyDeserializer : Json::IDeserializer<Dependency>
{
using type = std::string;
StringView type_name() { return "a package name"; }
virtual StringView type_name() const override { return "a dependency"; }
static bool is_package_name(StringView sv)
constexpr static StringLiteral NAME = "name";
constexpr static StringLiteral FEATURES = "features";
constexpr static StringLiteral DEFAULT_FEATURES = "default-features";
constexpr static StringLiteral PLATFORM = "platform";
virtual Span<const StringView> valid_fields() const override
{
if (sv.size() == 0)
{
return false;
}
static const StringView t[] = {
NAME,
FEATURES,
DEFAULT_FEATURES,
PLATFORM,
};
for (const auto& ident : Strings::split(sv, '.'))
{
if (!IdentifierField::is_ident(ident))
{
return false;
}
}
return true;
return t;
}
Optional<std::string> visit_string(Json::Reader&, StringView, StringView sv)
virtual Optional<Dependency> visit_string(Json::Reader&, StringView, StringView sv) override
{
if (!is_package_name(sv))
if (!Json::PackageNameDeserializer::is_package_name(sv))
{
return nullopt;
}
return sv.to_string();
Dependency dep;
dep.name = sv.to_string();
return dep;
}
virtual Optional<Dependency> visit_object(Json::Reader& r, StringView, const Json::Object& obj) override
{
Dependency dep;
for (const auto& el : obj)
{
if (Strings::starts_with(el.first, "$"))
{
dep.extra_info.insert_or_replace(el.first.to_string(), el.second);
}
}
r.required_object_field(type_name(), obj, NAME, dep.name, Json::PackageNameDeserializer{});
r.optional_object_field(obj,
FEATURES,
dep.features,
Json::ArrayDeserializer<Json::IdentifierDeserializer>{"an array of identifiers",
Json::AllowEmpty::Yes});
bool default_features = true;
r.optional_object_field(obj, DEFAULT_FEATURES, default_features, Json::BooleanDeserializer{});
if (!default_features)
{
dep.features.push_back("core");
}
r.optional_object_field(obj, PLATFORM, dep.platform, PlatformExprDeserializer{});
return dep;
}
};
struct FeatureDeserializer : Json::IDeserializer<std::unique_ptr<FeatureParagraph>>
{
virtual StringView type_name() const override { return "a feature"; }
constexpr static StringLiteral NAME = "name";
constexpr static StringLiteral DESCRIPTION = "description";
constexpr static StringLiteral DEPENDENCIES = "dependencies";
virtual Span<const StringView> valid_fields() const override
{
static const StringView t[] = {NAME, DESCRIPTION, DEPENDENCIES};
return t;
}
virtual Optional<std::unique_ptr<FeatureParagraph>> visit_object(Json::Reader& r,
StringView,
const Json::Object& obj) override
{
auto feature = std::make_unique<FeatureParagraph>();
for (const auto& el : obj)
{
if (Strings::starts_with(el.first, "$"))
{
feature->extra_info.insert_or_replace(el.first.to_string(), el.second);
}
}
r.required_object_field(type_name(), obj, NAME, feature->name, Json::IdentifierDeserializer{});
r.required_object_field(type_name(), obj, DESCRIPTION, feature->description, Json::ParagraphDeserializer{});
r.optional_object_field(
obj,
DEPENDENCIES,
feature->dependencies,
Json::ArrayDeserializer<DependencyDeserializer>{"an array of dependencies", Json::AllowEmpty::Yes});
return std::move(feature);
}
};
// We "parse" this so that we can add actual license parsing at some point in the future
// without breaking anyone
struct LicenseExpressionField : Json::VisitorCrtpBase<LicenseExpressionField>
struct LicenseExpressionDeserializer : Json::IDeserializer<std::string>
{
using type = std::string;
StringView type_name() { return "an SPDX license expression"; }
virtual StringView type_name() const override { return "an SPDX license expression"; }
enum class Mode
{
@ -691,12 +503,11 @@ namespace vcpkg
constexpr static StringView VALID_LICENSES[] =
#include "spdx-licenses.inc"
;
constexpr static StringView VALID_EXCEPTIONS[] =
#include "spdx-exceptions.inc"
#include "spdx-licenses.inc"
;
Optional<std::string> visit_string(Json::Reader&, StringView, StringView sv)
virtual Optional<std::string> visit_string(Json::Reader&, StringView, StringView sv) override
{
Mode mode = Mode::ExpectExpression;
size_t open_parens = 0;
@ -811,129 +622,104 @@ namespace vcpkg
}
};
struct PlatformExprField : Json::VisitorCrtpBase<PlatformExprField>
struct ManifestDeserializer : Json::IDeserializer<std::unique_ptr<SourceControlFile>>
{
using type = PlatformExpression::Expr;
StringView type_name() { return "a platform expression"; }
Optional<PlatformExpression::Expr> visit_string(Json::Reader&, StringView, StringView sv)
{
auto opt =
PlatformExpression::parse_platform_expression(sv, PlatformExpression::MultipleBinaryOperators::Deny);
if (auto res = opt.get())
{
return std::move(*res);
}
else
{
Debug::print("Failed to parse platform expression: ", opt.error(), "\n");
return nullopt;
}
}
};
struct DependencyField : Json::VisitorCrtpBase<DependencyField>
{
using type = Dependency;
StringView type_name() { return "a dependency"; }
virtual StringView type_name() const override { return "a manifest"; }
constexpr static StringLiteral NAME = "name";
constexpr static StringLiteral VERSION = "version-string";
constexpr static StringLiteral PORT_VERSION = "port-version";
constexpr static StringLiteral MAINTAINERS = "maintainers";
constexpr static StringLiteral DESCRIPTION = "description";
constexpr static StringLiteral HOMEPAGE = "homepage";
constexpr static StringLiteral DOCUMENTATION = "documentation";
constexpr static StringLiteral LICENSE = "license";
constexpr static StringLiteral DEPENDENCIES = "dependencies";
constexpr static StringLiteral DEV_DEPENDENCIES = "dev-dependencies";
constexpr static StringLiteral FEATURES = "features";
constexpr static StringLiteral DEFAULT_FEATURES = "default-features";
constexpr static StringLiteral PLATFORM = "platform";
const static StringView KNOWN_FIELDS[4]; // not constexpr in MSVC 2015
constexpr static StringLiteral SUPPORTS = "supports";
Optional<Dependency> visit_string(Json::Reader&, StringView, StringView sv)
virtual Span<const StringView> valid_fields() const override
{
if (!PackageNameField::is_package_name(sv))
{
return nullopt;
}
static const StringView t[] = {
NAME,
VERSION,
Dependency dep;
dep.name = sv.to_string();
return dep;
PORT_VERSION,
MAINTAINERS,
DESCRIPTION,
HOMEPAGE,
DOCUMENTATION,
LICENSE,
DEPENDENCIES,
DEV_DEPENDENCIES,
FEATURES,
DEFAULT_FEATURES,
SUPPORTS,
};
return t;
}
Optional<Dependency> visit_object(Json::Reader& r, StringView, const Json::Object& obj)
virtual Optional<std::unique_ptr<SourceControlFile>> visit_object(Json::Reader& r,
StringView,
const Json::Object& obj) override
{
{
auto extra_fields = invalid_json_fields(obj, KNOWN_FIELDS);
if (!extra_fields.empty())
{
r.error().add_extra_fields(type_name().to_string(), std::move(extra_fields));
}
}
auto control_file = std::make_unique<SourceControlFile>();
control_file->core_paragraph = std::make_unique<SourceParagraph>();
Dependency dep;
auto& spgh = control_file->core_paragraph;
spgh->type = Type{Type::PORT};
for (const auto& el : obj)
{
if (Strings::starts_with(el.first, "$"))
{
dep.extra_info.insert_or_replace(el.first.to_string(), el.second);
spgh->extra_info.insert_or_replace(el.first.to_string(), el.second);
}
}
r.required_object_field(type_name(), obj, NAME, dep.name, PackageNameField{});
constexpr static StringView type_name = "vcpkg.json";
r.required_object_field(type_name, obj, NAME, spgh->name, Json::IdentifierDeserializer{});
r.required_object_field(type_name, obj, VERSION, spgh->version, Json::StringDeserializer{"a version"});
r.optional_object_field(obj, PORT_VERSION, spgh->port_version, Json::NaturalNumberDeserializer{});
r.optional_object_field(obj, MAINTAINERS, spgh->maintainers, Json::ParagraphDeserializer{});
r.optional_object_field(obj, DESCRIPTION, spgh->description, Json::ParagraphDeserializer{});
r.optional_object_field(obj, HOMEPAGE, spgh->homepage, Json::StringDeserializer{"a url"});
r.optional_object_field(obj, DOCUMENTATION, spgh->documentation, Json::StringDeserializer{"a url"});
r.optional_object_field(obj, LICENSE, spgh->license, LicenseExpressionDeserializer{});
r.optional_object_field(
obj, FEATURES, dep.features, ArrayField<IdentifierField>{"an array of identifiers", AllowEmpty::Yes});
obj,
DEPENDENCIES,
spgh->dependencies,
Json::ArrayDeserializer<DependencyDeserializer>{"an array of dependencies", Json::AllowEmpty::Yes});
bool default_features = true;
r.optional_object_field(obj, DEFAULT_FEATURES, default_features, BooleanField{});
if (!default_features)
if (obj.contains(DEV_DEPENDENCIES))
{
dep.features.push_back("core");
System::print2(System::Color::error, "dev_dependencies are not yet supported");
Checks::exit_fail(VCPKG_LINE_INFO);
}
r.optional_object_field(obj, PLATFORM, dep.platform, PlatformExprField{});
r.optional_object_field(obj, SUPPORTS, spgh->supports_expression, PlatformExprDeserializer{});
return dep;
}
};
const StringView DependencyField::KNOWN_FIELDS[] = {NAME, FEATURES, DEFAULT_FEATURES, PLATFORM};
struct FeatureField : Json::VisitorCrtpBase<FeatureField>
{
using type = std::unique_ptr<FeatureParagraph>;
StringView type_name() { return "a feature"; }
constexpr static StringLiteral NAME = "name";
constexpr static StringLiteral DESCRIPTION = "description";
constexpr static StringLiteral DEPENDENCIES = "dependencies";
const static StringView KNOWN_FIELDS[3]; // Not constexpr in MSVC 2015
Optional<std::unique_ptr<FeatureParagraph>> visit_object(Json::Reader& r, StringView, const Json::Object& obj)
{
{
auto extra_fields = invalid_json_fields(obj, KNOWN_FIELDS);
if (!extra_fields.empty())
{
r.error().add_extra_fields(type_name().to_string(), std::move(extra_fields));
}
}
auto feature = std::make_unique<FeatureParagraph>();
for (const auto& el : obj)
{
if (Strings::starts_with(el.first, "$"))
{
feature->extra_info.insert_or_replace(el.first.to_string(), el.second);
}
}
r.required_object_field(type_name(), obj, NAME, feature->name, IdentifierField{});
r.required_object_field(type_name(), obj, DESCRIPTION, feature->description, ParagraphField{});
r.optional_object_field(obj,
DEPENDENCIES,
feature->dependencies,
ArrayField<DependencyField>{"an array of dependencies", AllowEmpty::Yes});
DEFAULT_FEATURES,
spgh->default_features,
Json::ArrayDeserializer<Json::IdentifierDeserializer>{"an array of identifiers",
Json::AllowEmpty::Yes});
return std::move(feature);
r.optional_object_field(
obj,
FEATURES,
control_file->feature_paragraphs,
Json::ArrayDeserializer<FeatureDeserializer>{"an array of feature definitions", Json::AllowEmpty::Yes});
canonicalize(*control_file);
return std::move(control_file);
}
};
const StringView FeatureField::KNOWN_FIELDS[] = {NAME, DESCRIPTION, DEPENDENCIES};
Parse::ParseExpected<SourceControlFile> SourceControlFile::parse_manifest_file(const fs::path& path_to_manifest,
const Json::Object& manifest)
@ -970,68 +756,105 @@ namespace vcpkg
auto visit = Json::Reader{&err};
err.pcei.name = fs::u8string(path_to_manifest);
{
auto extra_fields = invalid_json_fields(manifest, get_list_of_manifest_fields());
if (!extra_fields.empty())
{
err.pcei.extra_fields["manifest"] = std::move(extra_fields);
}
}
auto control_file = std::make_unique<SourceControlFile>();
control_file->core_paragraph = std::make_unique<SourceParagraph>();
auto& spgh = control_file->core_paragraph;
spgh->type = Type{Type::PORT};
for (const auto& el : manifest)
{
if (Strings::starts_with(el.first, "$"))
{
spgh->extra_info.insert_or_replace(el.first.to_string(), el.second);
}
}
constexpr static StringView type_name = "vcpkg.json";
visit.required_object_field(type_name, manifest, ManifestFields::NAME, spgh->name, IdentifierField{});
visit.required_object_field(
type_name, manifest, ManifestFields::VERSION, spgh->version, StringField{"a version"});
visit.optional_object_field(manifest, ManifestFields::PORT_VERSION, spgh->port_version, NaturalNumberField{});
visit.optional_object_field(manifest, ManifestFields::MAINTAINERS, spgh->maintainers, ParagraphField{});
visit.optional_object_field(manifest, ManifestFields::DESCRIPTION, spgh->description, ParagraphField{});
visit.optional_object_field(manifest, ManifestFields::HOMEPAGE, spgh->homepage, StringField{"a url"});
visit.optional_object_field(manifest, ManifestFields::DOCUMENTATION, spgh->documentation, StringField{"a url"});
visit.optional_object_field(manifest, ManifestFields::LICENSE, spgh->license, LicenseExpressionField{});
visit.optional_object_field(manifest,
ManifestFields::DEPENDENCIES,
spgh->dependencies,
ArrayField<DependencyField>{"an array of dependencies", AllowEmpty::Yes});
if (manifest.contains(ManifestFields::DEV_DEPENDENCIES))
{
System::print2(System::Color::error, "dev_dependencies are not yet supported");
Checks::exit_fail(VCPKG_LINE_INFO);
}
visit.optional_object_field(manifest, ManifestFields::SUPPORTS, spgh->supports_expression, PlatformExprField{});
visit.optional_object_field(manifest,
ManifestFields::DEFAULT_FEATURES,
spgh->default_features,
ArrayField<IdentifierField>{"an array of identifiers", AllowEmpty::Yes});
visit.optional_object_field(manifest,
ManifestFields::FEATURES,
control_file->feature_paragraphs,
ArrayField<FeatureField>{"an array of feature definitions", AllowEmpty::Yes});
auto res = visit.visit_value(manifest, "$", ManifestDeserializer{});
if (err.pcei.has_error())
{
return std::make_unique<ParseControlErrorInfo>(std::move(err.pcei));
}
else if (auto p = res.get())
{
return std::move(*p);
}
else
{
Checks::unreachable(VCPKG_LINE_INFO);
}
}
canonicalize(*control_file);
return std::move(control_file);
void print_error_message(Span<const std::unique_ptr<Parse::ParseControlErrorInfo>> error_info_list)
{
Checks::check_exit(VCPKG_LINE_INFO, error_info_list.size() > 0);
for (auto&& error_info : error_info_list)
{
Checks::check_exit(VCPKG_LINE_INFO, error_info != nullptr);
if (!error_info->error.empty())
{
System::print2(
System::Color::error, "Error: while loading ", error_info->name, ":\n", error_info->error, '\n');
}
}
bool have_remaining_fields = false;
for (auto&& error_info : error_info_list)
{
if (!error_info->extra_fields.empty())
{
System::print2(System::Color::error,
"Error: There are invalid fields in the control or manifest file of ",
error_info->name,
'\n');
System::print2("The following fields were not expected:\n");
for (const auto& pr : error_info->extra_fields)
{
System::print2(" In ", pr.first, ": ", Strings::join(", ", pr.second), "\n");
}
have_remaining_fields = true;
}
}
if (have_remaining_fields)
{
System::print2("This is the list of valid fields for CONTROL files (case-sensitive): \n\n ",
Strings::join("\n ", get_list_of_valid_fields()),
"\n\n");
System::print2("And this is the list of valid fields for manifest files: \n\n ",
Strings::join("\n ", ManifestDeserializer{}.valid_fields()),
"\n\n");
#if defined(_WIN32)
auto bootstrap = ".\\bootstrap-vcpkg.bat";
#else
auto bootstrap = "./bootstrap-vcpkg.sh";
#endif
System::printf("You may need to update the vcpkg binary; try running %s to update.\n\n", bootstrap);
}
for (auto&& error_info : error_info_list)
{
if (!error_info->missing_fields.empty())
{
System::print2(System::Color::error,
"Error: There are missing fields in the control file of ",
error_info->name,
'\n');
System::print2("The following fields were missing:\n");
for (const auto& pr : error_info->missing_fields)
{
System::print2(" In ", pr.first, ": ", Strings::join(", ", pr.second), "\n");
}
}
}
for (auto&& error_info : error_info_list)
{
if (!error_info->expected_types.empty())
{
System::print2(System::Color::error,
"Error: There are invalid field types in the CONTROL or manifest file of ",
error_info->name,
'\n');
System::print2("The following fields had the wrong types:\n\n");
for (const auto& pr : error_info->expected_types)
{
System::printf(" %s was expected to be %s\n", pr.first, pr.second);
}
System::print2("\n");
}
}
}
Optional<const FeatureParagraph&> SourceControlFile::find_feature(const std::string& featurename) const
@ -1127,18 +950,18 @@ namespace vcpkg
dep_obj.insert(el.first.to_string(), el.second);
}
dep_obj.insert(DependencyField::NAME, Json::Value::string(dep.name));
dep_obj.insert(DependencyDeserializer::NAME, Json::Value::string(dep.name));
auto features_copy = dep.features;
auto core_it = std::find(features_copy.begin(), features_copy.end(), "core");
if (core_it != features_copy.end())
{
dep_obj.insert(DependencyField::DEFAULT_FEATURES, Json::Value::boolean(false));
dep_obj.insert(DependencyDeserializer::DEFAULT_FEATURES, Json::Value::boolean(false));
features_copy.erase(core_it);
}
serialize_optional_array(dep_obj, DependencyField::FEATURES, features_copy);
serialize_optional_string(dep_obj, DependencyField::PLATFORM, to_string(dep.platform));
serialize_optional_array(dep_obj, DependencyDeserializer::FEATURES, features_copy);
serialize_optional_string(dep_obj, DependencyDeserializer::PLATFORM, to_string(dep.platform));
}
};
@ -1149,25 +972,26 @@ namespace vcpkg
obj.insert(el.first.to_string(), el.second);
}
obj.insert(ManifestFields::NAME, Json::Value::string(scf.core_paragraph->name));
obj.insert(ManifestFields::VERSION, Json::Value::string(scf.core_paragraph->version));
obj.insert(ManifestDeserializer::NAME, Json::Value::string(scf.core_paragraph->name));
obj.insert(ManifestDeserializer::VERSION, Json::Value::string(scf.core_paragraph->version));
if (scf.core_paragraph->port_version != 0 || debug)
{
obj.insert(ManifestFields::PORT_VERSION, Json::Value::integer(scf.core_paragraph->port_version));
obj.insert(ManifestDeserializer::PORT_VERSION, Json::Value::integer(scf.core_paragraph->port_version));
}
serialize_paragraph(obj, ManifestFields::MAINTAINERS, scf.core_paragraph->maintainers);
serialize_paragraph(obj, ManifestFields::DESCRIPTION, scf.core_paragraph->description);
serialize_paragraph(obj, ManifestDeserializer::MAINTAINERS, scf.core_paragraph->maintainers);
serialize_paragraph(obj, ManifestDeserializer::DESCRIPTION, scf.core_paragraph->description);
serialize_optional_string(obj, ManifestFields::HOMEPAGE, scf.core_paragraph->homepage);
serialize_optional_string(obj, ManifestFields::DOCUMENTATION, scf.core_paragraph->documentation);
serialize_optional_string(obj, ManifestFields::LICENSE, scf.core_paragraph->license);
serialize_optional_string(obj, ManifestFields::SUPPORTS, to_string(scf.core_paragraph->supports_expression));
serialize_optional_string(obj, ManifestDeserializer::HOMEPAGE, scf.core_paragraph->homepage);
serialize_optional_string(obj, ManifestDeserializer::DOCUMENTATION, scf.core_paragraph->documentation);
serialize_optional_string(obj, ManifestDeserializer::LICENSE, scf.core_paragraph->license);
serialize_optional_string(
obj, ManifestDeserializer::SUPPORTS, to_string(scf.core_paragraph->supports_expression));
if (!scf.core_paragraph->dependencies.empty() || debug)
{
auto& deps = obj.insert(ManifestFields::DEPENDENCIES, Json::Array());
auto& deps = obj.insert(ManifestDeserializer::DEPENDENCIES, Json::Array());
for (const auto& dep : scf.core_paragraph->dependencies)
{
@ -1175,11 +999,11 @@ namespace vcpkg
}
}
serialize_optional_array(obj, ManifestFields::DEFAULT_FEATURES, scf.core_paragraph->default_features);
serialize_optional_array(obj, ManifestDeserializer::DEFAULT_FEATURES, scf.core_paragraph->default_features);
if (!scf.feature_paragraphs.empty() || debug)
{
auto& arr = obj.insert(ManifestFields::FEATURES, Json::Array());
auto& arr = obj.insert(ManifestDeserializer::FEATURES, Json::Array());
for (const auto& feature : scf.feature_paragraphs)
{
auto& feature_obj = arr.push_back(Json::Object());
@ -1188,12 +1012,12 @@ namespace vcpkg
feature_obj.insert(el.first.to_string(), el.second);
}
feature_obj.insert(FeatureField::NAME, Json::Value::string(feature->name));
serialize_paragraph(feature_obj, FeatureField::DESCRIPTION, feature->description, true);
feature_obj.insert(FeatureDeserializer::NAME, Json::Value::string(feature->name));
serialize_paragraph(feature_obj, FeatureDeserializer::DESCRIPTION, feature->description, true);
if (!feature->dependencies.empty() || debug)
{
auto& deps = feature_obj.insert(FeatureField::DEPENDENCIES, Json::Array());
auto& deps = feature_obj.insert(FeatureDeserializer::DEPENDENCIES, Json::Array());
for (const auto& dep : feature->dependencies)
{
serialize_dependency(deps, dep);

Просмотреть файл

@ -49,6 +49,7 @@ namespace vcpkg
{VcpkgCmdArguments::BINARY_CACHING_FEATURE, args.binary_caching},
{VcpkgCmdArguments::MANIFEST_MODE_FEATURE, args.manifest_mode},
{VcpkgCmdArguments::COMPILER_TRACKING_FEATURE, args.compiler_tracking},
{VcpkgCmdArguments::REGISTRIES_FEATURE, args.registries_feature},
};
for (const auto& desc : flag_descriptions)
@ -730,6 +731,7 @@ namespace vcpkg
{BINARY_CACHING_FEATURE, binary_caching},
{MANIFEST_MODE_FEATURE, manifest_mode},
{COMPILER_TRACKING_FEATURE, compiler_tracking},
{REGISTRIES_FEATURE, registries_feature},
};
for (const auto& flag : flags)
@ -754,6 +756,7 @@ namespace vcpkg
} flags[] = {
{BINARY_CACHING_FEATURE, binary_caching_enabled()},
{COMPILER_TRACKING_FEATURE, compiler_tracking_enabled()},
{REGISTRIES_FEATURE, registries_enabled()},
};
for (const auto& flag : flags)

Просмотреть файл

@ -8,9 +8,12 @@
#include <vcpkg/binaryparagraph.h>
#include <vcpkg/build.h>
#include <vcpkg/commands.h>
#include <vcpkg/configuration.h>
#include <vcpkg/globalstate.h>
#include <vcpkg/metrics.h>
#include <vcpkg/packagespec.h>
#include <vcpkg/registries.h>
#include <vcpkg/sourceparagraph.h>
#include <vcpkg/tools.h>
#include <vcpkg/vcpkgcmdarguments.h>
#include <vcpkg/vcpkgpaths.h>
@ -81,6 +84,141 @@ namespace
namespace vcpkg
{
static Configuration deserialize_configuration(const Json::Object& obj, const VcpkgCmdArguments& args)
{
Json::BasicReaderError err;
Json::Reader reader{&err};
auto deserializer = ConfigurationDeserializer(args);
auto parsed_config_opt = reader.visit_value(obj, "$", deserializer);
if (err.has_error())
{
if (!err.missing_fields.empty())
{
System::print2(System::Color::error, "Error: missing fields in configuration:\n");
for (const auto& missing : err.missing_fields)
{
System::printf(
System::Color::error, " %s was expected to have: %s\n", missing.first, missing.second);
}
}
if (!err.expected_types.empty())
{
System::print2(System::Color::error, "Error: Invalid types in configuration:\n");
for (const auto& expected : err.expected_types)
{
System::printf(
System::Color::error, " %s was expected to be %s\n", expected.first, expected.second);
}
}
if (!err.extra_fields.empty())
{
System::print2(System::Color::error, "Error: Invalid fields in configuration:\n");
for (const auto& extra : err.extra_fields)
{
System::printf(System::Color::error,
" %s had invalid fields: %s\n",
extra.first,
Strings::join(", ", extra.second));
}
}
if (!err.mutually_exclusive_fields.empty())
{
// this should never happen
Checks::unreachable(VCPKG_LINE_INFO);
}
Checks::exit_fail(VCPKG_LINE_INFO);
}
return std::move(parsed_config_opt).value_or_exit(VCPKG_LINE_INFO);
}
struct ManifestAndConfig
{
fs::path config_directory;
Configuration config;
};
static std::pair<Json::Object, Json::JsonStyle> load_manifest(const Files::Filesystem& fs,
const fs::path& manifest_dir)
{
std::error_code ec;
auto manifest_path = manifest_dir / fs::u8path("vcpkg.json");
auto manifest_opt = Json::parse_file(fs, manifest_path, ec);
if (ec)
{
Checks::exit_with_message(VCPKG_LINE_INFO,
"Failed to load manifest from directory %s: %s",
fs::u8string(manifest_dir),
ec.message());
}
if (!manifest_opt.has_value())
{
Checks::exit_with_message(VCPKG_LINE_INFO,
"Failed to parse manifest at %s: %s",
fs::u8string(manifest_path),
manifest_opt.error()->format());
}
auto manifest_value = std::move(manifest_opt).value_or_exit(VCPKG_LINE_INFO);
if (!manifest_value.first.is_object())
{
System::print2(System::Color::error,
"Failed to parse manifest at ",
fs::u8string(manifest_path),
": Manifest files must have a top-level object\n");
Checks::exit_fail(VCPKG_LINE_INFO);
}
return {std::move(manifest_value.first.object()), std::move(manifest_value.second)};
}
struct ConfigAndPath
{
fs::path config_directory;
Configuration config;
};
// doesn't yet implement searching upwards for configurations, nor inheritance of configurations
static ConfigAndPath load_configuration(const Files::Filesystem& fs,
const VcpkgCmdArguments& args,
const fs::path& vcpkg_root,
const fs::path& manifest_dir)
{
fs::path config_dir;
if (!manifest_dir.empty())
{
// manifest mode
config_dir = manifest_dir;
}
else
{
// classic mode
config_dir = vcpkg_root;
}
auto path_to_config = config_dir / fs::u8path("vcpkg-configuration.json");
if (!fs.exists(path_to_config))
{
return {};
}
auto parsed_config = Json::parse_file(VCPKG_LINE_INFO, fs, path_to_config);
if (!parsed_config.first.is_object())
{
System::print2(System::Color::error,
"Failed to parse ",
fs::u8string(path_to_config),
": configuration files must have a top-level object\n");
Checks::exit_fail(VCPKG_LINE_INFO);
}
auto config_obj = std::move(parsed_config.first.object());
return {std::move(config_dir), deserialize_configuration(config_obj, args)};
}
namespace details
{
struct VcpkgPathsImpl
@ -104,6 +242,9 @@ namespace vcpkg
Build::EnvCache m_env_cache;
fs::SystemHandle file_lock_handle;
Optional<std::pair<Json::Object, Json::JsonStyle>> m_manifest_doc;
Configuration m_config;
};
}
@ -136,7 +277,7 @@ namespace vcpkg
}
else
{
manifest_root_dir = filesystem.find_file_recursively_up(original_cwd, "vcpkg.json");
manifest_root_dir = filesystem.find_file_recursively_up(original_cwd, fs::u8path("vcpkg.json"));
}
uppercase_win32_drive_letter(manifest_root_dir);
@ -162,6 +303,8 @@ namespace vcpkg
System::printf(System::Color::error, " %s\n", ec.message());
Checks::exit_fail(VCPKG_LINE_INFO);
}
m_pimpl->m_manifest_doc = load_manifest(filesystem, manifest_root_dir);
}
else
{
@ -198,6 +341,11 @@ If you wish to silence this error and use classic mode, you can:
process_output_directory(filesystem, root, args.install_root_dir.get(), "installed", VCPKG_LINE_INFO);
}
auto config_file = load_configuration(filesystem, args, root, manifest_root_dir);
config_root_dir = std::move(config_file.config_directory);
m_pimpl->m_config = std::move(config_file.config);
buildtrees =
process_output_directory(filesystem, root, args.buildtrees_root_dir.get(), "buildtrees", VCPKG_LINE_INFO);
downloads =
@ -336,6 +484,20 @@ If you wish to silence this error and use classic mode, you can:
return m_pimpl->m_tool_cache->get_tool_version(*this, tool);
}
Optional<const Json::Object&> VcpkgPaths::get_manifest() const
{
if (auto p = m_pimpl->m_manifest_doc.get())
{
return p->first;
}
else
{
return nullopt;
}
}
const Configuration& VcpkgPaths::get_configuration() const { return m_pimpl->m_config; }
const Toolset& VcpkgPaths::get_toolset(const Build::PreBuildInfo& prebuildinfo) const
{
if (!prebuildinfo.using_vcvars())

Просмотреть файл

@ -225,6 +225,7 @@
<ClInclude Include="..\..\include\vcpkg\portfileprovider.h" />
<ClInclude Include="..\..\include\vcpkg\postbuildlint.h" />
<ClInclude Include="..\..\include\vcpkg\postbuildlint.buildtype.h" />
<ClInclude Include="..\..\include\vcpkg\registries.h" />
<ClInclude Include="..\..\include\vcpkg\remove.h" />
<ClInclude Include="..\..\include\vcpkg\sourceparagraph.h" />
<ClInclude Include="..\..\include\vcpkg\statusparagraph.h" />
@ -294,6 +295,7 @@
<ClCompile Include="..\..\src\vcpkg\commands.upgrade.cpp" />
<ClCompile Include="..\..\src\vcpkg\commands.version.cpp" />
<ClCompile Include="..\..\src\vcpkg\commands.xvsinstances.cpp" />
<ClCompile Include="..\..\src\vcpkg\configuration.cpp" />
<ClCompile Include="..\..\src\vcpkg\dependencies.cpp" />
<ClCompile Include="..\..\src\vcpkg\export.cpp" />
<ClCompile Include="..\..\src\vcpkg\export.chocolatey.cpp" />
@ -310,6 +312,7 @@
<ClCompile Include="..\..\src\vcpkg\portfileprovider.cpp" />
<ClCompile Include="..\..\src\vcpkg\postbuildlint.buildtype.cpp" />
<ClCompile Include="..\..\src\vcpkg\postbuildlint.cpp" />
<ClCompile Include="..\..\src\vcpkg\registries.cpp" />
<ClCompile Include="..\..\src\vcpkg\remove.cpp" />
<ClCompile Include="..\..\src\vcpkg\sourceparagraph.cpp" />
<ClCompile Include="..\..\src\vcpkg\statusparagraph.cpp" />