aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornicole mazzuca <mazzucan@outlook.com>2020-09-02 09:13:44 -0700
committerGitHub <noreply@github.com>2020-09-02 09:13:44 -0700
commit9740611cab8faa9d9ada57265471e8e4786d37bc (patch)
tree71ca07c2bdd77a89f9596e759e618f64077821d9
parentf4a2dce829fb7cbf7e988edc5eceb40e35cbf2a6 (diff)
downloadvcpkg-9740611cab8faa9d9ada57265471e8e4786d37bc.tar.gz
vcpkg-9740611cab8faa9d9ada57265471e8e4786d37bc.zip
[vcpkg] Registries MVP (#13038)
-rw-r--r--toolsrc/.clang-format12
-rw-r--r--toolsrc/CMakeLists.txt35
-rw-r--r--toolsrc/include/vcpkg/base/files.h2
-rw-r--r--toolsrc/include/vcpkg/base/fwd/json.h44
-rw-r--r--toolsrc/include/vcpkg/base/fwd/optional.h7
-rw-r--r--toolsrc/include/vcpkg/base/fwd/span.h7
-rw-r--r--toolsrc/include/vcpkg/base/fwd/stringview.h6
-rw-r--r--toolsrc/include/vcpkg/base/json.h380
-rw-r--r--toolsrc/include/vcpkg/base/optional.h47
-rw-r--r--toolsrc/include/vcpkg/base/stringview.h2
-rw-r--r--toolsrc/include/vcpkg/base/util.h29
-rw-r--r--toolsrc/include/vcpkg/configuration.h41
-rw-r--r--toolsrc/include/vcpkg/fwd/configuration.h6
-rw-r--r--toolsrc/include/vcpkg/fwd/registries.h8
-rw-r--r--toolsrc/include/vcpkg/paragraphs.h30
-rw-r--r--toolsrc/include/vcpkg/portfileprovider.h7
-rw-r--r--toolsrc/include/vcpkg/registries.h97
-rw-r--r--toolsrc/include/vcpkg/sourceparagraph.h3
-rw-r--r--toolsrc/include/vcpkg/vcpkgcmdarguments.h3
-rw-r--r--toolsrc/include/vcpkg/vcpkgpaths.h13
-rw-r--r--toolsrc/src/vcpkg-test/files.cpp19
-rw-r--r--toolsrc/src/vcpkg-test/optional.cpp33
-rw-r--r--toolsrc/src/vcpkg/base/files.cpp39
-rw-r--r--toolsrc/src/vcpkg/base/json.cpp49
-rw-r--r--toolsrc/src/vcpkg/commands.ci.cpp5
-rw-r--r--toolsrc/src/vcpkg/commands.edit.cpp5
-rw-r--r--toolsrc/src/vcpkg/commands.format-manifest.cpp5
-rw-r--r--toolsrc/src/vcpkg/commands.portsdiff.cpp10
-rw-r--r--toolsrc/src/vcpkg/configuration.cpp66
-rw-r--r--toolsrc/src/vcpkg/install.cpp21
-rw-r--r--toolsrc/src/vcpkg/paragraphs.cpp109
-rw-r--r--toolsrc/src/vcpkg/portfileprovider.cpp100
-rw-r--r--toolsrc/src/vcpkg/registries.cpp128
-rw-r--r--toolsrc/src/vcpkg/sourceparagraph.cpp678
-rw-r--r--toolsrc/src/vcpkg/vcpkgcmdarguments.cpp3
-rw-r--r--toolsrc/src/vcpkg/vcpkgpaths.cpp164
-rw-r--r--toolsrc/windows-bootstrap/vcpkglib/vcpkglib.vcxproj3
37 files changed, 1605 insertions, 611 deletions
diff --git a/toolsrc/.clang-format b/toolsrc/.clang-format
index cdaabb8c1..374a4233e 100644
--- a/toolsrc/.clang-format
+++ b/toolsrc/.clang-format
@@ -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
diff --git a/toolsrc/CMakeLists.txt b/toolsrc/CMakeLists.txt
index 8b525c248..aa54a791b 100644
--- a/toolsrc/CMakeLists.txt
+++ b/toolsrc/CMakeLists.txt
@@ -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}
-
- ${VCPKG_SOURCES}
- ${VCPKGMETRICSUPLOADER_SOURCES}
-
- ${VCPKG_TEST_SOURCES}
- ${VCPKG_TEST_INCLUDES}
-
- ${VCPKG_FUZZ_SOURCES})
+ # 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}
+
+ COMMAND ${CLANG_FORMAT} -i -verbose ${VCPKG_SOURCES}
+ COMMAND ${CLANG_FORMAT} -i -verbose ${VCPKGMETRICSUPLOADER_SOURCES}
+
+ COMMAND ${CLANG_FORMAT} -i -verbose ${VCPKG_TEST_SOURCES}
+ COMMAND ${CLANG_FORMAT} -i -verbose ${VCPKG_TEST_INCLUDES}
+
+ COMMAND ${CLANG_FORMAT} -i -verbose ${VCPKG_FUZZ_SOURCES})
endif()
diff --git a/toolsrc/include/vcpkg/base/files.h b/toolsrc/include/vcpkg/base/files.h
index e0a555024..08bbe7906 100644
--- a/toolsrc/include/vcpkg/base/files.h
+++ b/toolsrc/include/vcpkg/base/files.h
@@ -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);
diff --git a/toolsrc/include/vcpkg/base/fwd/json.h b/toolsrc/include/vcpkg/base/fwd/json.h
new file mode 100644
index 000000000..7071103c1
--- /dev/null
+++ b/toolsrc/include/vcpkg/base/fwd/json.h
@@ -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;
+ };
+}
diff --git a/toolsrc/include/vcpkg/base/fwd/optional.h b/toolsrc/include/vcpkg/base/fwd/optional.h
new file mode 100644
index 000000000..2c3a7ea6c
--- /dev/null
+++ b/toolsrc/include/vcpkg/base/fwd/optional.h
@@ -0,0 +1,7 @@
+#pragma once
+
+namespace vcpkg
+{
+ template<class T>
+ struct Optional;
+}
diff --git a/toolsrc/include/vcpkg/base/fwd/span.h b/toolsrc/include/vcpkg/base/fwd/span.h
new file mode 100644
index 000000000..af78aa642
--- /dev/null
+++ b/toolsrc/include/vcpkg/base/fwd/span.h
@@ -0,0 +1,7 @@
+#pragma once
+
+namespace vcpkg
+{
+ template<class T>
+ struct Span;
+}
diff --git a/toolsrc/include/vcpkg/base/fwd/stringview.h b/toolsrc/include/vcpkg/base/fwd/stringview.h
new file mode 100644
index 000000000..e297fd43b
--- /dev/null
+++ b/toolsrc/include/vcpkg/base/fwd/stringview.h
@@ -0,0 +1,6 @@
+#pragma once
+
+namespace vcpkg
+{
+ struct StringView;
+}
diff --git a/toolsrc/include/vcpkg/base/json.h b/toolsrc/include/vcpkg/base/json.h
index a4ad44ebd..3539f2df4 100644
--- a/toolsrc/include/vcpkg/base/json.h
+++ b/toolsrc/include/vcpkg/base/json.h
@@ -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 Type>
+ void required_object_field(
+ StringView type, const Object& obj, StringView key, Type& place, IDeserializer<Type>&& visitor)
+ {
+ required_object_field(type, obj, key, place, visitor);
+ }
- template<class Visitor>
- void optional_object_field(const Object& obj, StringView key, VisitorType<Visitor>& place, Visitor&& visitor)
+ // returns whether key \in obj
+ template<class Type>
+ bool optional_object_field(const Object& obj, StringView key, Type& place, IDeserializer<Type>& visitor)
+ {
+ return internal_field(obj, key, place, visitor);
+ }
+ template<class Type>
+ bool optional_object_field(const Object& obj, StringView key, Type& place, IDeserializer<Type>&& visitor)
{
- internal_field(obj, key, place, visitor);
+ return optional_object_field(obj, key, place, visitor);
}
- template<class Visitor>
- Optional<std::vector<VisitorType<Visitor>>> array_elements(const Array& arr, StringView key, Visitor&& 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)
{
- std::vector<VisitorType<Visitor>> result;
+ 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);
+ }
+ };
+
+ struct StringDeserializer final : IDeserializer<std::string>
+ {
+ virtual StringView type_name() const override { return type_name_; }
+ virtual Optional<std::string> visit_string(Reader&, StringView, StringView sv) override
+ {
+ return sv.to_string();
+ }
+
+ 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
+ {
+ if (value > std::numeric_limits<int>::max() || value < 0)
+ {
+ return nullopt;
+ }
+ return static_cast<int>(value);
+ }
+ };
+
+ 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,
};
- // Warning: NEVER use this type except as a CRTP base
template<class Underlying>
- struct VisitorCrtpBase
+ 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)
+ {
+ }
+
+ virtual Optional<type> visit_array(Reader& r, StringView key, const Array& arr) override
+ {
+ 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>>
{
- // the following function must be defined by the Underlying class
- // const char* type_name();
+ virtual StringView type_name() const override { return "a string or array of strings"; }
- // 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)
+ virtual Optional<std::vector<std::string>> visit_string(Reader&, StringView, StringView sv) override
{
- return static_cast<Underlying&>(*this).visit_number(r, field_name, static_cast<double>(i));
+ 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;
+ }
}
- 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 PackageNameDeserializer final : Json::IDeserializer<std::string>
+ {
+ virtual StringView type_name() const override { return "a package name"; }
+
+ static bool is_package_name(StringView sv)
{
- return Optional<typename Underlying::type>(nullopt);
+ if (sv.size() == 0)
+ {
+ return false;
+ }
+
+ for (const auto& ident : Strings::split(sv, '.'))
+ {
+ if (!IdentifierDeserializer::is_ident(ident))
+ {
+ return false;
+ }
+ }
+
+ return true;
}
- auto visit_object(Reader&, StringView, const Json::Object&)
+
+ virtual Optional<std::string> visit_string(Json::Reader&, StringView, StringView sv) override
{
- return Optional<typename Underlying::type>(nullopt);
+ 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);
diff --git a/toolsrc/include/vcpkg/base/optional.h b/toolsrc/include/vcpkg/base/optional.h
index 091cd498e..54e370dff 100644
--- a/toolsrc/include/vcpkg/base/optional.h
+++ b/toolsrc/include/vcpkg/base/optional.h
@@ -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;
- }
}
diff --git a/toolsrc/include/vcpkg/base/stringview.h b/toolsrc/include/vcpkg/base/stringview.h
index 405142c36..9238990fe 100644
--- a/toolsrc/include/vcpkg/base/stringview.h
+++ b/toolsrc/include/vcpkg/base/stringview.h
@@ -1,5 +1,7 @@
#pragma once
+#include <vcpkg/base/fwd/stringview.h>
+
#include <vcpkg/base/optional.h>
#include <limits>
diff --git a/toolsrc/include/vcpkg/base/util.h b/toolsrc/include/vcpkg/base/util.h
index 11807b249..0453ac20a 100644
--- a/toolsrc/include/vcpkg/base/util.h
+++ b/toolsrc/include/vcpkg/base/util.h
@@ -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>()))))>;
diff --git a/toolsrc/include/vcpkg/configuration.h b/toolsrc/include/vcpkg/configuration.h
new file mode 100644
index 000000000..44440fa04
--- /dev/null
+++ b/toolsrc/include/vcpkg/configuration.h
@@ -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;
+ };
+}
diff --git a/toolsrc/include/vcpkg/fwd/configuration.h b/toolsrc/include/vcpkg/fwd/configuration.h
new file mode 100644
index 000000000..5fa9cee34
--- /dev/null
+++ b/toolsrc/include/vcpkg/fwd/configuration.h
@@ -0,0 +1,6 @@
+#pragma once
+
+namespace vcpkg
+{
+ struct Configuration;
+}
diff --git a/toolsrc/include/vcpkg/fwd/registries.h b/toolsrc/include/vcpkg/fwd/registries.h
new file mode 100644
index 000000000..7352c429d
--- /dev/null
+++ b/toolsrc/include/vcpkg/fwd/registries.h
@@ -0,0 +1,8 @@
+#pragma once
+
+namespace vcpkg
+{
+ struct RegistryImpl;
+ struct Registry;
+ struct RegistrySet;
+}
diff --git a/toolsrc/include/vcpkg/paragraphs.h b/toolsrc/include/vcpkg/paragraphs.h
index 25d037591..95726f913 100644
--- a/toolsrc/include/vcpkg/paragraphs.h
+++ b/toolsrc/include/vcpkg/paragraphs.h
@@ -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);
-
- std::vector<std::unique_ptr<SourceControlFile>> 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;
+
+ 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);
}
diff --git a/toolsrc/include/vcpkg/portfileprovider.h b/toolsrc/include/vcpkg/portfileprovider.h
index e4b5d9cb5..d3fa0b51f 100644
--- a/toolsrc/include/vcpkg/portfileprovider.h
+++ b/toolsrc/include/vcpkg/portfileprovider.h
@@ -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;
};
}
diff --git a/toolsrc/include/vcpkg/registries.h b/toolsrc/include/vcpkg/registries.h
new file mode 100644
index 000000000..78c055dae
--- /dev/null
+++ b/toolsrc/include/vcpkg/registries.h
@@ -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_;
+ };
+
+}
diff --git a/toolsrc/include/vcpkg/sourceparagraph.h b/toolsrc/include/vcpkg/sourceparagraph.h
index ac3699ca9..d5069adde 100644
--- a/toolsrc/include/vcpkg/sourceparagraph.h
+++ b/toolsrc/include/vcpkg/sourceparagraph.h
@@ -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>
diff --git a/toolsrc/include/vcpkg/vcpkgcmdarguments.h b/toolsrc/include/vcpkg/vcpkgcmdarguments.h
index 3e956c83e..626e598be 100644
--- a/toolsrc/include/vcpkg/vcpkgcmdarguments.h
+++ b/toolsrc/include/vcpkg/vcpkgcmdarguments.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;
diff --git a/toolsrc/include/vcpkg/vcpkgpaths.h b/toolsrc/include/vcpkg/vcpkgpaths.h
index 2262543d9..ae2ee3823 100644
--- a/toolsrc/include/vcpkg/vcpkgpaths.h
+++ b/toolsrc/include/vcpkg/vcpkgpaths.h
@@ -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;
diff --git a/toolsrc/src/vcpkg-test/files.cpp b/toolsrc/src/vcpkg-test/files.cpp
index 255c87d69..a8c7c2ba2 100644
--- a/toolsrc/src/vcpkg-test/files.cpp
+++ b/toolsrc/src/vcpkg-test/files.cpp
@@ -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);
diff --git a/toolsrc/src/vcpkg-test/optional.cpp b/toolsrc/src/vcpkg-test/optional.cpp
index 929410eb3..f3c61387f 100644
--- a/toolsrc/src/vcpkg-test/optional.cpp
+++ b/toolsrc/src/vcpkg-test/optional.cpp
@@ -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);
diff --git a/toolsrc/src/vcpkg/base/files.cpp b/toolsrc/src/vcpkg/base/files.cpp
index 91f22bd2f..42efd473f 100644
--- a/toolsrc/src/vcpkg/base/files.cpp
+++ b/toolsrc/src/vcpkg/base/files.cpp
@@ -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
}
}
diff --git a/toolsrc/src/vcpkg/base/json.cpp b/toolsrc/src/vcpkg/base/json.cpp
index 62f50f9d0..58121ac8e 100644
--- a/toolsrc/src/vcpkg/base/json.cpp
+++ b/toolsrc/src/vcpkg/base/json.cpp
@@ -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
{
diff --git a/toolsrc/src/vcpkg/commands.ci.cpp b/toolsrc/src/vcpkg/commands.ci.cpp
index e9577e4ad..ee7b4d4a0 100644
--- a/toolsrc/src/vcpkg/commands.ci.cpp
+++ b/toolsrc/src/vcpkg/commands.ci.cpp
@@ -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)
diff --git a/toolsrc/src/vcpkg/commands.edit.cpp b/toolsrc/src/vcpkg/commands.edit.cpp
index 13091352e..7ee74eae0 100644
--- a/toolsrc/src/vcpkg/commands.edit.cpp
+++ b/toolsrc/src/vcpkg/commands.edit.cpp
@@ -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 = {
diff --git a/toolsrc/src/vcpkg/commands.format-manifest.cpp b/toolsrc/src/vcpkg/commands.format-manifest.cpp
index 72cac6958..24beae0d8 100644
--- a/toolsrc/src/vcpkg/commands.format-manifest.cpp
+++ b/toolsrc/src/vcpkg/commands.format-manifest.cpp
@@ -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);
diff --git a/toolsrc/src/vcpkg/commands.portsdiff.cpp b/toolsrc/src/vcpkg/commands.portsdiff.cpp
index a25d441c8..392ca70dd 100644
--- a/toolsrc/src/vcpkg/commands.portsdiff.cpp
+++ b/toolsrc/src/vcpkg/commands.portsdiff.cpp
@@ -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;
diff --git a/toolsrc/src/vcpkg/configuration.cpp b/toolsrc/src/vcpkg/configuration.cpp
new file mode 100644
index 000000000..adc1f814d
--- /dev/null
+++ b/toolsrc/src/vcpkg/configuration.cpp
@@ -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();
+ }
+
+}
diff --git a/toolsrc/src/vcpkg/install.cpp b/toolsrc/src/vcpkg/install.cpp
index 08c160f89..cc0b6b931 100644
--- a/toolsrc/src/vcpkg/install.cpp
+++ b/toolsrc/src/vcpkg/install.cpp
@@ -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);
diff --git a/toolsrc/src/vcpkg/paragraphs.cpp b/toolsrc/src/vcpkg/paragraphs.cpp
index 37f919b63..19c1d266d 100644
--- a/toolsrc/src/vcpkg/paragraphs.cpp
+++ b/toolsrc/src/vcpkg/paragraphs.cpp
@@ -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);
+ }
}
diff --git a/toolsrc/src/vcpkg/portfileprovider.cpp b/toolsrc/src/vcpkg/portfileprovider.cpp
index 0e9b355de..0e065eff7 100644
--- a/toolsrc/src/vcpkg/portfileprovider.cpp
+++ b/toolsrc/src/vcpkg/portfileprovider.cpp
@@ -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,54 @@ 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(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_spec),
+ 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(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), ports_dir / 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(ports_dir / spec),
+ fs::u8string(port_directory),
spec,
scf->get()->core_paragraph->name);
}
@@ -115,7 +150,7 @@ 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(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;
}
}
diff --git a/toolsrc/src/vcpkg/registries.cpp b/toolsrc/src/vcpkg/registries.cpp
new file mode 100644
index 000000000..5df38e6ef
--- /dev/null
+++ b/toolsrc/src/vcpkg/registries.cpp
@@ -0,0 +1,128 @@
+#include <vcpkg/base/json.h>
+
+#include <vcpkg/registries.h>
+
+namespace
+{
+ struct BuiltinRegistry final : vcpkg::RegistryImpl
+ {
+ virtual fs::path get_registry_root(const vcpkg::VcpkgPaths& paths) const override { return paths.ports; }
+ };
+
+ struct DirectoryRegistry final : vcpkg::RegistryImpl
+ {
+ virtual fs::path get_registry_root(const vcpkg::VcpkgPaths& paths) const override
+ {
+ return vcpkg::Files::combine(paths.config_root_dir, path);
+ }
+
+ DirectoryRegistry(fs::path&& path_) : path(path_) { }
+
+ fs::path path;
+ };
+}
+
+namespace vcpkg
+{
+ std::unique_ptr<RegistryImpl> Registry::builtin_registry() { return std::make_unique<BuiltinRegistry>(); }
+
+ Registry::Registry(std::vector<std::string>&& packages, std::unique_ptr<RegistryImpl>&& impl)
+ : packages_(std::move(packages)), implementation_(std::move(impl))
+ {
+ Checks::check_exit(VCPKG_LINE_INFO, implementation_ != nullptr);
+ }
+
+ StringView RegistryImplDeserializer::type_name() const { return "a registry"; }
+
+ Span<const StringView> RegistryImplDeserializer::valid_fields() const
+ {
+ static const StringView t[] = {KIND, PATH};
+ return t;
+ }
+
+ Optional<std::unique_ptr<RegistryImpl>> RegistryImplDeserializer::visit_null(Json::Reader&, StringView)
+ {
+ return nullptr;
+ }
+
+ Optional<std::unique_ptr<RegistryImpl>> RegistryImplDeserializer::visit_object(Json::Reader& r,
+ StringView,
+ const Json::Object& obj)
+ {
+ std::string kind;
+ r.required_object_field(
+ type_name(), obj, KIND, kind, Json::StringDeserializer{"a registry implementation kind"});
+
+ if (kind == KIND_BUILTIN)
+ {
+ if (obj.contains(PATH))
+ {
+ r.error().add_extra_fields(type_name().to_string(), {PATH});
+ }
+ return static_cast<std::unique_ptr<RegistryImpl>>(std::make_unique<BuiltinRegistry>());
+ }
+ else if (kind == KIND_DIRECTORY)
+ {
+ fs::path path;
+ r.required_object_field(type_name(), obj, PATH, path, Json::PathDeserializer{});
+
+ return static_cast<std::unique_ptr<RegistryImpl>>(std::make_unique<DirectoryRegistry>(std::move(path)));
+ }
+ else
+ {
+ return nullopt;
+ }
+ }
+
+ StringView RegistryDeserializer::type_name() const { return "a registry"; }
+
+ Span<const StringView> RegistryDeserializer::valid_fields() const
+ {
+ static const StringView t[] = {
+ RegistryImplDeserializer::KIND,
+ RegistryImplDeserializer::PATH,
+ PACKAGES,
+ };
+ return t;
+ }
+
+ Optional<Registry> RegistryDeserializer::visit_object(Json::Reader& r, StringView key, const Json::Object& obj)
+ {
+ auto impl = RegistryImplDeserializer{}.visit_object(r, key, obj);
+
+ if (!impl.has_value())
+ {
+ return nullopt;
+ }
+
+ std::vector<std::string> packages;
+ r.required_object_field(
+ type_name(),
+ obj,
+ PACKAGES,
+ packages,
+ Json::ArrayDeserializer<Json::PackageNameDeserializer>{"an array of registries", Json::AllowEmpty::Yes});
+
+ return Registry{std::move(packages), std::move(impl).value_or_exit(VCPKG_LINE_INFO)};
+ }
+
+ RegistrySet::RegistrySet() : default_registry_(Registry::builtin_registry()), registries_() { }
+
+ const RegistryImpl* RegistrySet::registry_for_port(StringView name) const
+ {
+ for (const auto& registry : registries())
+ {
+ const auto& packages = registry.packages();
+ if (std::find(packages.begin(), packages.end(), name) != packages.end())
+ {
+ return &registry.implementation();
+ }
+ }
+ return default_registry();
+ }
+
+ void RegistrySet::add_registry(Registry&& r) { registries_.push_back(std::move(r)); }
+
+ void RegistrySet::set_default_registry(std::unique_ptr<RegistryImpl>&& r) { default_registry_ = std::move(r); }
+ void RegistrySet::set_default_registry(std::nullptr_t) { default_registry_.reset(); }
+}
diff --git a/toolsrc/src/vcpkg/sourceparagraph.cpp b/toolsrc/src/vcpkg/sourceparagraph.cpp
index 84d9eec78..a00fd7579 100644
--- a/toolsrc/src/vcpkg/sourceparagraph.cpp
+++ b/toolsrc/src/vcpkg/sourceparagraph.cpp
@@ -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 std::move(*res);
}
- }
-
- 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)
+ else
{
+ Debug::print("Failed to parse platform expression: ", opt.error(), "\n");
return nullopt;
}
- return static_cast<int>(value);
}
};
- struct BooleanField : Json::VisitorCrtpBase<BooleanField>
+ struct DependencyDeserializer : Json::IDeserializer<Dependency>
{
- using type = bool;
- StringView type_name() { return "a boolean"; }
+ virtual StringView type_name() const override { return "a dependency"; }
- 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_; }
+ constexpr static StringLiteral NAME = "name";
+ constexpr static StringLiteral FEATURES = "features";
+ constexpr static StringLiteral DEFAULT_FEATURES = "default-features";
+ constexpr static StringLiteral PLATFORM = "platform";
- ArrayField(StringView type_name_, AllowEmpty allow_empty, T&& t = {})
- : type_name_(type_name_), underlying_visitor_(static_cast<T&&>(t)), allow_empty_(allow_empty)
+ virtual Span<const StringView> valid_fields() const override
{
+ static const StringView t[] = {
+ NAME,
+ FEATURES,
+ DEFAULT_FEATURES,
+ PLATFORM,
+ };
+
+ return t;
}
- Optional<type> visit_array(Json::Reader& r, StringView key, const Json::Array& arr)
+ virtual Optional<Dependency> visit_string(Json::Reader&, StringView, StringView sv) override
{
- if (allow_empty_ == AllowEmpty::No && arr.size() == 0)
+ if (!Json::PackageNameDeserializer::is_package_name(sv))
{
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"});
+ Dependency dep;
+ dep.name = sv.to_string();
+ return dep;
}
- };
- 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)
+ virtual Optional<Dependency> visit_object(Json::Reader& r, StringView, const Json::Object& obj) override
{
- 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)");
+ Dependency dep;
- // back-compat
- if (sv == "all_modules")
+ for (const auto& el : obj)
{
- return true;
+ if (Strings::starts_with(el.first, "$"))
+ {
+ dep.extra_info.insert_or_replace(el.first.to_string(), el.second);
+ }
}
- if (!std::regex_match(sv.begin(), sv.end(), BASIC_IDENTIFIER))
- {
- return false; // we're not even in the shape of an identifier
- }
+ 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});
- if (std::regex_match(sv.begin(), sv.end(), RESERVED))
+ bool default_features = true;
+ r.optional_object_field(obj, DEFAULT_FEATURES, default_features, Json::BooleanDeserializer{});
+ if (!default_features)
{
- return false; // we're a reserved identifier
+ dep.features.push_back("core");
}
- return true;
- }
+ r.optional_object_field(obj, PLATFORM, dep.platform, PlatformExprDeserializer{});
- Optional<std::string> visit_string(Json::Reader&, StringView, StringView sv)
- {
- if (is_ident(sv))
- {
- return sv.to_string();
- }
- else
- {
- return nullopt;
- }
+ return dep;
}
};
- struct PackageNameField : Json::VisitorCrtpBase<PackageNameField>
+ struct FeatureDeserializer : Json::IDeserializer<std::unique_ptr<FeatureParagraph>>
{
- using type = std::string;
- StringView type_name() { return "a package name"; }
+ virtual StringView type_name() const override { return "a feature"; }
+
+ constexpr static StringLiteral NAME = "name";
+ constexpr static StringLiteral DESCRIPTION = "description";
+ constexpr static StringLiteral DEPENDENCIES = "dependencies";
- static bool is_package_name(StringView sv)
+ virtual Span<const StringView> valid_fields() const override
{
- if (sv.size() == 0)
- {
- return false;
- }
+ 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& ident : Strings::split(sv, '.'))
+ for (const auto& el : obj)
{
- if (!IdentifierField::is_ident(ident))
+ if (Strings::starts_with(el.first, "$"))
{
- return false;
+ feature->extra_info.insert_or_replace(el.first.to_string(), el.second);
}
}
- return true;
- }
+ 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});
- Optional<std::string> visit_string(Json::Reader&, StringView, StringView sv)
- {
- if (!is_package_name(sv))
- {
- return nullopt;
- }
- return sv.to_string();
+ 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>
- {
- 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>
+ struct ManifestDeserializer : Json::IDeserializer<std::unique_ptr<SourceControlFile>>
{
- 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,
+
+ PORT_VERSION,
+ MAINTAINERS,
+ DESCRIPTION,
+ HOMEPAGE,
+ DOCUMENTATION,
+ LICENSE,
+ DEPENDENCIES,
+ DEV_DEPENDENCIES,
+ FEATURES,
+ DEFAULT_FEATURES,
+ SUPPORTS,
+ };
- Dependency dep;
- dep.name = sv.to_string();
- return dep;
+ 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 res = visit.visit_value(manifest, "$", ManifestDeserializer{});
+
+ if (err.pcei.has_error())
{
- 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);
- }
+ 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);
+ }
+ }
- 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};
+ 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 (const auto& el : manifest)
+ for (auto&& error_info : error_info_list)
{
- if (Strings::starts_with(el.first, "$"))
+ Checks::check_exit(VCPKG_LINE_INFO, error_info != nullptr);
+ if (!error_info->error.empty())
{
- spgh->extra_info.insert_or_replace(el.first.to_string(), el.second);
+ System::print2(
+ System::Color::error, "Error: while loading ", error_info->name, ":\n", error_info->error, '\n');
}
}
- 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))
+ bool have_remaining_fields = false;
+ for (auto&& error_info : error_info_list)
{
- 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{});
+ 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");
- visit.optional_object_field(manifest,
- ManifestFields::DEFAULT_FEATURES,
- spgh->default_features,
- ArrayField<IdentifierField>{"an array of identifiers", AllowEmpty::Yes});
+ for (const auto& pr : error_info->extra_fields)
+ {
+ System::print2(" In ", pr.first, ": ", Strings::join(", ", pr.second), "\n");
+ }
+ have_remaining_fields = true;
+ }
+ }
- visit.optional_object_field(manifest,
- ManifestFields::FEATURES,
- control_file->feature_paragraphs,
- ArrayField<FeatureField>{"an array of feature definitions", AllowEmpty::Yes});
+ 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);
+ }
- if (err.pcei.has_error())
+ for (auto&& error_info : error_info_list)
{
- return std::make_unique<ParseControlErrorInfo>(std::move(err.pcei));
+ 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");
+ }
+ }
}
- canonicalize(*control_file);
- return std::move(control_file);
+ 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);
diff --git a/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp b/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp
index 7377baafb..18288104f 100644
--- a/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp
+++ b/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp
@@ -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)
diff --git a/toolsrc/src/vcpkg/vcpkgpaths.cpp b/toolsrc/src/vcpkg/vcpkgpaths.cpp
index 660c180e2..677ac1a09 100644
--- a/toolsrc/src/vcpkg/vcpkgpaths.cpp
+++ b/toolsrc/src/vcpkg/vcpkgpaths.cpp
@@ -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())
diff --git a/toolsrc/windows-bootstrap/vcpkglib/vcpkglib.vcxproj b/toolsrc/windows-bootstrap/vcpkglib/vcpkglib.vcxproj
index 334532a36..240b0115b 100644
--- a/toolsrc/windows-bootstrap/vcpkglib/vcpkglib.vcxproj
+++ b/toolsrc/windows-bootstrap/vcpkglib/vcpkglib.vcxproj
@@ -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" />