[vcpkg] Registries MVP (#13038)
This commit is contained in:
Родитель
4d68c16770
Коммит
65640a2960
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 ®istry.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" />
|
||||
|
|
Загрузка…
Ссылка в новой задаче