diff --git a/include/vcpkg/base/delayed_init.h b/include/vcpkg/base/delayed_init.h new file mode 100644 index 00000000000000..a3257f54eafe70 --- /dev/null +++ b/include/vcpkg/base/delayed_init.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include +#include + +namespace vcpkg +{ + // implements the equivalent of function static initialization for an object + template + struct DelayedInit + { + template + const T& get(F&& f) const + { + std::call_once(underlying_->flag_, [&f, this]() { underlying_->storage_ = std::forward(f)(); }); + return *underlying_->storage_.get(); + } + + private: + struct Storage + { + std::once_flag flag_; + Optional storage_; + }; + std::unique_ptr underlying_ = std::make_unique(); + }; +} diff --git a/include/vcpkg/base/files.h b/include/vcpkg/base/files.h index 1c5e8393f31a40..3e56101c9ee3de 100644 --- a/include/vcpkg/base/files.h +++ b/include/vcpkg/base/files.h @@ -38,6 +38,7 @@ namespace fs path u8path(vcpkg::StringView s); inline path u8path(const char* first, const char* last) { return u8path(vcpkg::StringView{first, last}); } + inline path u8path(std::initializer_list il) { return u8path(vcpkg::StringView{il.begin(), il.end()}); } inline path u8path(const char* s) { return u8path(vcpkg::StringView{s, s + ::strlen(s)}); } #if defined(_MSC_VER) @@ -58,6 +59,9 @@ namespace fs std::string u8string(const path& p); std::string generic_u8string(const path& p); + // equivalent to p.lexically_normal() + path lexically_normal(const path& p); + #if defined(_WIN32) enum class file_type { @@ -251,9 +255,6 @@ namespace vcpkg::Files constexpr char preferred_separator = '/'; #endif // _WIN32 - // Adds file as a new path element to the end of base, with an additional slash if necessary - std::string add_filename(StringView base, StringView file); - #if defined(_WIN32) fs::path win32_fix_path_case(const fs::path& source); #endif // _WIN32 diff --git a/include/vcpkg/base/jsonreader.h b/include/vcpkg/base/jsonreader.h index 6518501a240e73..cdd0299d2ff6ad 100644 --- a/include/vcpkg/base/jsonreader.h +++ b/include/vcpkg/base/jsonreader.h @@ -112,6 +112,24 @@ namespace vcpkg::Json m_path.pop_back(); } + // value should be the value at key of the currently visited object + template + void visit_at_index(const Value& value, int64_t index, Type& place, IDeserializer& visitor) + { + m_path.push_back(index); + auto opt = visitor.visit(*this, value); + + if (auto p_opt = opt.get()) + { + place = std::move(*p_opt); + } + else + { + add_expected_type_error(visitor.type_name()); + } + m_path.pop_back(); + } + // returns whether key \in obj template bool optional_object_field(const Object& obj, StringView key, Type& place, IDeserializer& visitor) @@ -167,8 +185,8 @@ namespace vcpkg::Json } }; - VCPKG_MSVC_WARNING(push); - VCPKG_MSVC_WARNING(disable : 4505); + VCPKG_MSVC_WARNING(push) + VCPKG_MSVC_WARNING(disable : 4505) template Optional IDeserializer::visit(Reader& r, const Value& value) @@ -235,7 +253,7 @@ namespace vcpkg::Json return nullopt; } - VCPKG_MSVC_WARNING(pop); + VCPKG_MSVC_WARNING(pop) struct StringDeserializer final : IDeserializer { diff --git a/include/vcpkg/base/optional.h b/include/vcpkg/base/optional.h index 606d60ce529f7a..c46c2e6f8608f3 100644 --- a/include/vcpkg/base/optional.h +++ b/include/vcpkg/base/optional.h @@ -30,7 +30,7 @@ namespace vcpkg constexpr OptionalStorage(const T& t) : m_is_present(true), m_t(t) { } constexpr OptionalStorage(T&& t) : m_is_present(true), m_t(std::move(t)) { } template::value>> - constexpr explicit OptionalStorage(Optional&& t) : m_is_present(false), m_inactive() + explicit OptionalStorage(Optional&& t) : m_is_present(false), m_inactive() { if (auto p = t.get()) { @@ -39,7 +39,7 @@ namespace vcpkg } } template - constexpr explicit OptionalStorage(const Optional& t) : m_is_present(false), m_inactive() + explicit OptionalStorage(const Optional& t) : m_is_present(false), m_inactive() { if (auto p = t.get()) { diff --git a/include/vcpkg/base/pragmas.h b/include/vcpkg/base/pragmas.h index 73f14c4b246637..06efd5105a25ee 100644 --- a/include/vcpkg/base/pragmas.h +++ b/include/vcpkg/base/pragmas.h @@ -25,6 +25,7 @@ #define ASSUME(expr) #endif +// the static_assert(true, "")s are to avoid the extra ';' warning #ifdef _MSC_VER #define VCPKG_MSVC_WARNING(...) __pragma(warning(__VA_ARGS__)) #define GCC_DIAGNOSTIC(...) diff --git a/include/vcpkg/configurationdeserializer.h b/include/vcpkg/configurationdeserializer.h index 83d3d0869f8ce8..d6b2393567a432 100644 --- a/include/vcpkg/configurationdeserializer.h +++ b/include/vcpkg/configurationdeserializer.h @@ -21,7 +21,7 @@ namespace vcpkg constexpr static StringLiteral PATH = "path"; constexpr static StringLiteral KIND_BUILTIN = "builtin"; - constexpr static StringLiteral KIND_DIRECTORY = "directory"; + constexpr static StringLiteral KIND_FILESYSTEM = "filesystem"; virtual StringView type_name() const override; virtual View valid_fields() const override; diff --git a/include/vcpkg/dependencies.h b/include/vcpkg/dependencies.h index db135aa28d4f7b..5e46a61948486a 100644 --- a/include/vcpkg/dependencies.h +++ b/include/vcpkg/dependencies.h @@ -172,7 +172,5 @@ namespace vcpkg::Dependencies std::vector features, CMakeVars::CMakeVarProvider& var_provider); - void print_plan(const ActionPlan& action_plan, - const bool is_recursive = true, - const fs::path& default_ports_dir = {}); + void print_plan(const ActionPlan& action_plan, const bool is_recursive = true, const fs::path& vcpkg_root_dir = {}); } diff --git a/include/vcpkg/registries.h b/include/vcpkg/registries.h index 812bce86b71f2d..1f8c0f393b13a8 100644 --- a/include/vcpkg/registries.h +++ b/include/vcpkg/registries.h @@ -6,6 +6,8 @@ #include #include +#include + #include #include #include @@ -13,9 +15,22 @@ namespace vcpkg { + struct RegistryEntry + { + // returns fs::path() if version doesn't exist + virtual fs::path get_port_directory(const VcpkgPaths& paths, const VersionT& version) const = 0; + + virtual ~RegistryEntry() = default; + }; + struct RegistryImpl { - virtual fs::path get_registry_root(const VcpkgPaths& paths) const = 0; + // returns nullptr if the port doesn't exist + virtual std::unique_ptr get_port_entry(const VcpkgPaths& paths, StringView port_name) const = 0; + // appends the names of the ports to the out parameter + virtual void get_all_port_names(std::vector& port_names, const VcpkgPaths& paths) const = 0; + + virtual Optional get_baseline_version(const VcpkgPaths& paths, StringView port_name) const = 0; virtual ~RegistryImpl() = default; }; diff --git a/include/vcpkg/vcpkgpaths.h b/include/vcpkg/vcpkgpaths.h index aa1ba92d79b772..def874e7c33ea0 100644 --- a/include/vcpkg/vcpkgpaths.h +++ b/include/vcpkg/vcpkgpaths.h @@ -86,7 +86,6 @@ namespace vcpkg fs::path buildtrees; fs::path downloads; fs::path packages; - fs::path ports; fs::path installed; fs::path triplets; fs::path community_triplets; @@ -128,6 +127,10 @@ namespace vcpkg const FeatureFlagSettings& get_feature_flags() const; void track_feature_flag_metrics() const; + // the directory of the builtin ports + // this should be used only for helper commands, not core commands like `install`. + fs::path builtin_ports_directory() const { return root / fs::u8path("ports"); } + private: std::unique_ptr m_pimpl; }; diff --git a/include/vcpkg/versiont.h b/include/vcpkg/versiont.h index 768ca0c47d51aa..506d7090a5b4a3 100644 --- a/include/vcpkg/versiont.h +++ b/include/vcpkg/versiont.h @@ -1,4 +1,5 @@ #pragma once + #include namespace vcpkg @@ -14,6 +15,8 @@ namespace vcpkg friend bool operator==(const VersionT& left, const VersionT& right); friend bool operator!=(const VersionT& left, const VersionT& right); + friend struct VersionTMapLess; + private: std::string value; int port_version; @@ -29,4 +32,9 @@ namespace vcpkg std::string to_string() const; }; + + struct VersionTMapLess + { + bool operator()(const VersionT& left, const VersionT& right) const; + }; } diff --git a/src/vcpkg-test/commands.create.cpp b/src/vcpkg-test/commands.create.cpp index 0cc93bd57652fb..8b8dfa2307662b 100644 --- a/src/vcpkg-test/commands.create.cpp +++ b/src/vcpkg-test/commands.create.cpp @@ -19,7 +19,7 @@ TEST_CASE ("create smoke test", "[commands-create]") VcpkgPaths paths(fsWrapper, args); const auto exit_code = Commands::Create::perform(args, paths); REQUIRE(exit_code == 0); - const auto expected_port = paths.ports / fs::u8path("zlib2"); + const auto expected_port = paths.builtin_ports_directory() / fs::u8path("zlib2"); const auto expected_portfile_cmake = expected_port / fs::u8path("portfile.cmake"); const auto lines = fsWrapper.read_lines(expected_portfile_cmake); REQUIRE(lines.has_value()); diff --git a/src/vcpkg-test/files.cpp b/src/vcpkg-test/files.cpp index 94cf7e1ddb5ff5..ffa72d0fb5ab2c 100644 --- a/src/vcpkg-test/files.cpp +++ b/src/vcpkg-test/files.cpp @@ -194,6 +194,96 @@ TEST_CASE ("remove all", "[files]") CHECK_EC_ON_FILE(temp_dir, ec); } +TEST_CASE ("lexically_normal", "[files]") +{ + const auto lexically_normal = [](const char* s) { return fs::lexically_normal(fs::u8path(s)); }; + const auto native = [](const char* s) { return std::move(fs::u8path(s).make_preferred()); }; + CHECK(fs::lexically_normal(fs::path()).native() == fs::path().native()); + + // these test cases are taken from the MS STL tests + CHECK(lexically_normal("cat/./dog/..").native() == native("cat/").native()); + CHECK(lexically_normal("cat/.///dog/../").native() == native("cat/").native()); + + CHECK(lexically_normal("cat/./dog/..").native() == native("cat/").native()); + CHECK(lexically_normal("cat/.///dog/../").native() == native("cat/").native()); + + CHECK(lexically_normal(".").native() == native(".").native()); + CHECK(lexically_normal("./").native() == native(".").native()); + CHECK(lexically_normal("./.").native() == native(".").native()); + CHECK(lexically_normal("././").native() == native(".").native()); + + CHECK(lexically_normal("../../..").native() == native("../../..").native()); + CHECK(lexically_normal("../../../").native() == native("../../..").native()); + + CHECK(lexically_normal("../../../a/b/c").native() == native("../../../a/b/c").native()); + + CHECK(lexically_normal("/../../..").native() == native("/").native()); + CHECK(lexically_normal("/../../../").native() == native("/").native()); + + CHECK(lexically_normal("/../../../a/b/c").native() == native("/a/b/c").native()); + + CHECK(lexically_normal("a/..").native() == native(".").native()); + CHECK(lexically_normal("a/../").native() == native(".").native()); + +#if defined(_WIN32) + CHECK(lexically_normal(R"(X:)").native() == LR"(X:)"); + + CHECK(lexically_normal(R"(X:DriveRelative)").native() == LR"(X:DriveRelative)"); + + CHECK(lexically_normal(R"(X:\)").native() == LR"(X:\)"); + CHECK(lexically_normal(R"(X:/)").native() == LR"(X:\)"); + CHECK(lexically_normal(R"(X:\\\)").native() == LR"(X:\)"); + CHECK(lexically_normal(R"(X:///)").native() == LR"(X:\)"); + + CHECK(lexically_normal(R"(X:\DosAbsolute)").native() == LR"(X:\DosAbsolute)"); + CHECK(lexically_normal(R"(X:/DosAbsolute)").native() == LR"(X:\DosAbsolute)"); + CHECK(lexically_normal(R"(X:\\\DosAbsolute)").native() == LR"(X:\DosAbsolute)"); + CHECK(lexically_normal(R"(X:///DosAbsolute)").native() == LR"(X:\DosAbsolute)"); + + CHECK(lexically_normal(R"(\RootRelative)").native() == LR"(\RootRelative)"); + CHECK(lexically_normal(R"(/RootRelative)").native() == LR"(\RootRelative)"); + CHECK(lexically_normal(R"(\\\RootRelative)").native() == LR"(\RootRelative)"); + CHECK(lexically_normal(R"(///RootRelative)").native() == LR"(\RootRelative)"); + + CHECK(lexically_normal(R"(\\server\share)").native() == LR"(\\server\share)"); + CHECK(lexically_normal(R"(//server/share)").native() == LR"(\\server\share)"); + CHECK(lexically_normal(R"(\\server\\\share)").native() == LR"(\\server\share)"); + CHECK(lexically_normal(R"(//server///share)").native() == LR"(\\server\share)"); + + CHECK(lexically_normal(R"(\\?\device)").native() == LR"(\\?\device)"); + CHECK(lexically_normal(R"(//?/device)").native() == LR"(\\?\device)"); + + CHECK(lexically_normal(R"(\??\device)").native() == LR"(\??\device)"); + CHECK(lexically_normal(R"(/??/device)").native() == LR"(\??\device)"); + + CHECK(lexically_normal(R"(\\.\device)").native() == LR"(\\.\device)"); + CHECK(lexically_normal(R"(//./device)").native() == LR"(\\.\device)"); + + CHECK(lexically_normal(R"(\\?\UNC\server\share)").native() == LR"(\\?\UNC\server\share)"); + CHECK(lexically_normal(R"(//?/UNC/server/share)").native() == LR"(\\?\UNC\server\share)"); + + CHECK(lexically_normal(R"(C:\a/b\\c\/d/\e//f)").native() == LR"(C:\a\b\c\d\e\f)"); + + CHECK(lexically_normal(R"(C:\meow\)").native() == LR"(C:\meow\)"); + CHECK(lexically_normal(R"(C:\meow/)").native() == LR"(C:\meow\)"); + CHECK(lexically_normal(R"(C:\meow\\)").native() == LR"(C:\meow\)"); + CHECK(lexically_normal(R"(C:\meow\/)").native() == LR"(C:\meow\)"); + CHECK(lexically_normal(R"(C:\meow/\)").native() == LR"(C:\meow\)"); + CHECK(lexically_normal(R"(C:\meow//)").native() == LR"(C:\meow\)"); + + CHECK(lexically_normal(R"(C:\a\.\b\.\.\c\.\.\.)").native() == LR"(C:\a\b\c\)"); + CHECK(lexically_normal(R"(C:\a\.\b\.\.\c\.\.\.\)").native() == LR"(C:\a\b\c\)"); + + CHECK(lexically_normal(R"(C:\a\b\c\d\e\..\f\..\..\..\g\h)").native() == LR"(C:\a\b\g\h)"); + + CHECK(lexically_normal(R"(C:\a\b\c\d\e\..\f\..\..\..\g\h\..)").native() == LR"(C:\a\b\g\)"); + CHECK(lexically_normal(R"(C:\a\b\c\d\e\..\f\..\..\..\g\h\..\)").native() == LR"(C:\a\b\g\)"); + CHECK(lexically_normal( + R"(/\server/\share/\a/\b/\c/\./\./\d/\../\../\../\../\../\../\../\other/x/y/z/.././..\meow.txt)") + .native() == LR"(\\server\other\x\meow.txt)"); +#endif +} + #if defined(_WIN32) TEST_CASE ("win32_fix_path_case", "[files]") { @@ -250,21 +340,6 @@ TEST_CASE ("win32_fix_path_case", "[files]") } #endif // _WIN32 -TEST_CASE ("add_filename", "[files]") -{ - using vcpkg::Files::add_filename; - using vcpkg::Files::preferred_separator; - - CHECK(add_filename("a/b", "c") == std::string("a/b") + preferred_separator + "c"); - CHECK(add_filename("a/b/", "c") == "a/b/c"); - CHECK(add_filename("a/b\\", "c") == "a/b\\c"); - CHECK(add_filename("", "c") == "c"); - - // note that we don't special case slashes in the second argument; the caller shouldn't do that - CHECK(add_filename("a/b/", "\\c") == "a/b/\\c"); - CHECK(add_filename("a/b\\", "/c") == "a/b\\/c"); -} - #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) TEST_CASE ("remove all -- benchmarks", "[files][!benchmark]") { diff --git a/src/vcpkg-test/manifests.cpp b/src/vcpkg-test/manifests.cpp index 5ec3f160a360c4..ee6b15e8da9722 100644 --- a/src/vcpkg-test/manifests.cpp +++ b/src/vcpkg-test/manifests.cpp @@ -321,7 +321,7 @@ TEST_CASE ("Serialize all the ports", "[manifests]") std::vector scfs; - for (auto dir : fs::directory_iterator(paths.ports)) + for (auto dir : fs::directory_iterator(paths.builtin_ports_directory())) { const auto control = dir / fs::u8path("CONTROL"); const auto manifest = dir / fs::u8path("vcpkg.json"); diff --git a/src/vcpkg/base/files.cpp b/src/vcpkg/base/files.cpp index ee3a339e016198..4cccebd4593ef8 100644 --- a/src/vcpkg/base/files.cpp +++ b/src/vcpkg/base/files.cpp @@ -21,8 +21,39 @@ #endif // ^^^ defined(__APPLE__) #include +#include #include +namespace +{ +#if defined(_WIN32) + struct IsSlash + { + bool operator()(const wchar_t c) const noexcept { return c == L'/' || c == L'\\'; } + }; +#else + struct IsSlash + { + bool operator()(const char c) const noexcept { return c == '/'; } + }; +#endif + + constexpr IsSlash is_slash; + + struct NativeStringView + { + const fs::path::value_type* first; + const fs::path::value_type* last; + NativeStringView() = default; + NativeStringView(const fs::path::value_type* first, const fs::path::value_type* last) : first(first), last(last) + { + } + bool empty() const { return first == last; } + bool is_dot() const { return (last - first) == 1 && *first == '.'; } + bool is_dot_dot() const { return (last - first) == 2 && *first == '.' && *(first + 1) == '.'; } + }; +} + #if defined(_WIN32) namespace { @@ -111,6 +142,148 @@ std::string fs::generic_u8string(const fs::path& p) #endif } +fs::path fs::lexically_normal(const fs::path& p) +{ + // copied from microsoft/STL, stl/inc/filesystem:lexically_normal() + // relicensed under MIT for the vcpkg repository. + + // N4810 29.11.7.1 [fs.path.generic]/6: + // "Normalization of a generic format pathname means:" + + // "1. If the path is empty, stop." + if (p.empty()) + { + return {}; + } + + // "2. Replace each slash character in the root-name with a preferred-separator." + const auto first = p.native().data(); + const auto last = first + p.native().size(); + const auto root_name_end = first + p.root_name().native().size(); + + fs::path::string_type normalized(first, root_name_end); + +#if defined(_WIN32) + std::replace(normalized.begin(), normalized.end(), L'/', L'\\'); +#endif + + // "3. Replace each directory-separator with a preferred-separator. + // [ Note: The generic pathname grammar (29.11.7.1) defines directory-separator + // as one or more slashes and preferred-separators. -end note ]" + std::list lst; // Empty string_view means directory-separator + // that will be normalized to a preferred-separator. + // Non-empty string_view means filename. + for (auto next = root_name_end; next != last;) + { + if (is_slash(*next)) + { + if (lst.empty() || !lst.back().empty()) + { + // collapse one or more slashes and preferred-separators to one empty wstring_view + lst.emplace_back(); + } + + ++next; + } + else + { + const auto filename_end = std::find_if(next + 1, last, is_slash); + lst.emplace_back(next, filename_end); + next = filename_end; + } + } + + // "4. Remove each dot filename and any immediately following directory-separator." + for (auto next = lst.begin(); next != lst.end();) + { + if (next->is_dot()) + { + next = lst.erase(next); // erase dot filename + + if (next != lst.end()) + { + next = lst.erase(next); // erase immediately following directory-separator + } + } + else + { + ++next; + } + } + + // "5. As long as any appear, remove a non-dot-dot filename immediately followed by a + // directory-separator and a dot-dot filename, along with any immediately following directory-separator." + for (auto next = lst.begin(); next != lst.end();) + { + auto prev = next; + + // If we aren't going to erase, keep advancing. + // If we're going to erase, next now points past the dot-dot filename. + ++next; + + if (prev->is_dot_dot() && prev != lst.begin() && --prev != lst.begin() && !(--prev)->is_dot_dot()) + { + if (next != lst.end()) + { // dot-dot filename has an immediately following directory-separator + ++next; + } + + lst.erase(prev, next); // next remains valid + } + } + + // "6. If there is a root-directory, remove all dot-dot filenames + // and any directory-separators immediately following them. + // [ Note: These dot-dot filenames attempt to refer to nonexistent parent directories. -end note ]" + if (!lst.empty() && lst.front().empty()) + { // we have a root-directory + for (auto next = lst.begin(); next != lst.end();) + { + if (next->is_dot_dot()) + { + next = lst.erase(next); // erase dot-dot filename + + if (next != lst.end()) + { + next = lst.erase(next); // erase immediately following directory-separator + } + } + else + { + ++next; + } + } + } + + // "7. If the last filename is dot-dot, remove any trailing directory-separator." + if (lst.size() >= 2 && lst.back().empty() && std::prev(lst.end(), 2)->is_dot_dot()) + { + lst.pop_back(); + } + + // Build up normalized by flattening lst. + for (const auto& elem : lst) + { + if (elem.empty()) + { + normalized += fs::path::preferred_separator; + } + else + { + normalized.append(elem.first, elem.last); + } + } + + // "8. If the path is empty, add a dot." + if (normalized.empty()) + { + normalized.push_back('.'); + } + + // "The result of normalization is a path in normal form, which is said to be normalized." + return std::move(normalized); +} + namespace vcpkg::Files { static const std::regex FILESYSTEM_INVALID_CHARACTERS_REGEX = std::regex(R"([\/:*?"<>|])"); @@ -1168,20 +1341,21 @@ namespace vcpkg::Files virtual std::vector find_from_PATH(const std::string& name) const override { #if defined(_WIN32) - static constexpr StringLiteral EXTS[] = {".cmd", ".exe", ".bat"}; + static constexpr wchar_t const* EXTS[] = {L".cmd", L".exe", L".bat"}; #else // ^^^ defined(_WIN32) // !defined(_WIN32) vvv - static constexpr StringLiteral EXTS[] = {""}; + static constexpr char const* EXTS[] = {""}; #endif // ^^^!defined(_WIN32) auto paths = Strings::split_paths(System::get_environment_variable("PATH").value_or_exit(VCPKG_LINE_INFO)); std::vector ret; for (auto&& path : paths) { - auto base = add_filename(path, name); + auto base = fs::u8path(path); + base /= fs::u8path(name); for (auto&& ext : EXTS) { - auto p = fs::u8path(base + ext.c_str()); + auto p = fs::path(base.native() + ext); if (Util::find(ret, p) == ret.end() && this->exists(p, ignore_errors)) { ret.push_back(p); @@ -1367,25 +1541,4 @@ namespace vcpkg::Files } #endif // _WIN32 - std::string add_filename(StringView base, StringView file) - { - std::string result; - const auto base_size = base.size(); - const auto file_size = file.size(); - if (base_size != 0 && !fs::is_slash(base.data()[base_size - 1])) - { - result.reserve(base_size + file_size + 1); - result.append(base.data(), base_size); - result.push_back(preferred_separator); - result.append(file.data(), file_size); - } - else - { - result.reserve(base_size + file_size); - result.append(base.data(), base_size); - result.append(file.data(), file_size); - } - - return result; - } } diff --git a/src/vcpkg/build.cpp b/src/vcpkg/build.cpp index c517750d897bf1..c20f48b3610a59 100644 --- a/src/vcpkg/build.cpp +++ b/src/vcpkg/build.cpp @@ -723,7 +723,7 @@ namespace vcpkg::Build } auto u8portdir = fs::u8string(scfl.source_location); - if (!Strings::case_insensitive_ascii_starts_with(u8portdir, fs::u8string(paths.ports))) + if (!Strings::case_insensitive_ascii_starts_with(u8portdir, fs::u8string(paths.builtin_ports_directory()))) { System::printf("-- Installing port from location: %s\n", u8portdir); } diff --git a/src/vcpkg/commands.autocomplete.cpp b/src/vcpkg/commands.autocomplete.cpp index 84a1f5ed5301c8..7e9039dcb65910 100644 --- a/src/vcpkg/commands.autocomplete.cpp +++ b/src/vcpkg/commands.autocomplete.cpp @@ -94,7 +94,8 @@ namespace vcpkg::Commands::Autocomplete const auto triplet_prefix = match[3].str(); // TODO: Support autocomplete for ports in --overlay-ports - auto maybe_port = Paragraphs::try_load_port(paths.get_filesystem(), paths.ports / port_name); + auto maybe_port = + Paragraphs::try_load_port(paths.get_filesystem(), paths.builtin_ports_directory() / port_name); if (maybe_port.error()) { Checks::exit_success(VCPKG_LINE_INFO); diff --git a/src/vcpkg/commands.ci.cpp b/src/vcpkg/commands.ci.cpp index 7741b55e84ee27..1d83f471b9a293 100644 --- a/src/vcpkg/commands.ci.cpp +++ b/src/vcpkg/commands.ci.cpp @@ -518,7 +518,7 @@ namespace vcpkg::Commands::CI if (is_dry_run) { - Dependencies::print_plan(action_plan, true, paths.ports); + Dependencies::print_plan(action_plan, true, paths.builtin_ports_directory()); } else { diff --git a/src/vcpkg/commands.edit.cpp b/src/vcpkg/commands.edit.cpp index 8d6401df1f9b99..e23b35d0cf1829 100644 --- a/src/vcpkg/commands.edit.cpp +++ b/src/vcpkg/commands.edit.cpp @@ -115,7 +115,7 @@ namespace vcpkg::Commands::Edit // TODO: Support edit for --overlay-ports return Util::fmap(ports, [&](const std::string& port_name) -> std::string { - const auto portpath = paths.ports / port_name; + const auto portpath = paths.builtin_ports_directory() / port_name; const auto portfile = portpath / "portfile.cmake"; const auto buildtrees_current_dir = paths.build_dir(port_name); const auto pattern = port_name + "_"; @@ -145,7 +145,7 @@ namespace vcpkg::Commands::Edit } return Util::fmap(ports, [&](const std::string& port_name) -> std::string { - const auto portpath = paths.ports / port_name; + const auto portpath = paths.builtin_ports_directory() / port_name; const auto portfile = portpath / "portfile.cmake"; return Strings::format(R"###("%s" "%s")###", fs::u8string(portpath), fs::u8string(portfile)); }); @@ -160,7 +160,7 @@ namespace vcpkg::Commands::Edit const std::vector& ports = args.command_arguments; for (auto&& port_name : ports) { - const fs::path portpath = paths.ports / port_name; + const fs::path portpath = paths.builtin_ports_directory() / port_name; Checks::check_exit( VCPKG_LINE_INFO, fs.is_directory(portpath), R"(Could not find port named "%s")", port_name); } diff --git a/src/vcpkg/commands.format-manifest.cpp b/src/vcpkg/commands.format-manifest.cpp index 8d4be1ce982336..64d491a84f0b4c 100644 --- a/src/vcpkg/commands.format-manifest.cpp +++ b/src/vcpkg/commands.format-manifest.cpp @@ -245,7 +245,7 @@ namespace vcpkg::Commands::FormatManifest if (format_all) { - for (const auto& dir : fs::directory_iterator(paths.ports)) + for (const auto& dir : fs::directory_iterator(paths.builtin_ports_directory())) { auto control_path = dir.path() / fs::u8path("CONTROL"); auto manifest_path = dir.path() / fs::u8path("vcpkg.json"); diff --git a/src/vcpkg/commands.portsdiff.cpp b/src/vcpkg/commands.portsdiff.cpp index 63e0e0e24e308b..d93d4d98a84981 100644 --- a/src/vcpkg/commands.portsdiff.cpp +++ b/src/vcpkg/commands.portsdiff.cpp @@ -84,7 +84,7 @@ namespace vcpkg::Commands::PortsDiff auto& fs = paths.get_filesystem(); const fs::path& git_exe = paths.get_tool_exe(Tools::GIT); const fs::path dot_git_dir = paths.root / ".git"; - const std::string ports_dir_name_as_string = fs::u8string(paths.ports.filename()); + const std::string ports_dir_name_as_string = fs::u8string(paths.builtin_ports_directory().filename()); const fs::path temp_checkout_path = paths.root / Strings::format("%s-%s", ports_dir_name_as_string, git_commit_id); fs.create_directory(temp_checkout_path, ec); diff --git a/src/vcpkg/commands.setinstalled.cpp b/src/vcpkg/commands.setinstalled.cpp index 1c04694c891b23..99c41a5b47dadc 100644 --- a/src/vcpkg/commands.setinstalled.cpp +++ b/src/vcpkg/commands.setinstalled.cpp @@ -86,7 +86,7 @@ namespace vcpkg::Commands::SetInstalled return Util::Sets::contains(specs_installed, ipa.spec); }); - Dependencies::print_plan(action_plan, true, paths.ports); + Dependencies::print_plan(action_plan, true, paths.builtin_ports_directory()); if (auto p_pkgsconfig = maybe_pkgsconfig.get()) { diff --git a/src/vcpkg/commands.upgrade.cpp b/src/vcpkg/commands.upgrade.cpp index 7abf1865d96956..0d8a6c4aff6188 100644 --- a/src/vcpkg/commands.upgrade.cpp +++ b/src/vcpkg/commands.upgrade.cpp @@ -169,7 +169,7 @@ namespace vcpkg::Commands::Upgrade action.build_options = vcpkg::Build::default_build_package_options; } - Dependencies::print_plan(action_plan, true, paths.ports); + Dependencies::print_plan(action_plan, true, paths.builtin_ports_directory()); if (!no_dry_run) { diff --git a/src/vcpkg/dependencies.cpp b/src/vcpkg/dependencies.cpp index 21af23c0b4ca1a..69db32e27c5e58 100644 --- a/src/vcpkg/dependencies.cpp +++ b/src/vcpkg/dependencies.cpp @@ -312,10 +312,10 @@ namespace vcpkg::Dependencies const CStringView s, const Build::BuildPackageOptions& options, const fs::path& install_port_path, - const fs::path& default_port_path) + const fs::path& builtin_ports_dir) { - if (!default_port_path.empty() && !Strings::case_insensitive_ascii_starts_with(fs::u8string(install_port_path), - fs::u8string(default_port_path))) + if (!builtin_ports_dir.empty() && !Strings::case_insensitive_ascii_starts_with(fs::u8string(install_port_path), + fs::u8string(builtin_ports_dir))) { const char* const from_head = options.use_head_version == Build::UseHeadVersion::YES ? " (from HEAD)" : ""; switch (request_type) @@ -1033,7 +1033,7 @@ namespace vcpkg::Dependencies PackageGraph::~PackageGraph() = default; - void print_plan(const ActionPlan& action_plan, const bool is_recursive, const fs::path& default_ports_dir) + void print_plan(const ActionPlan& action_plan, const bool is_recursive, const fs::path& builtin_ports_dir) { if (action_plan.remove_actions.empty() && action_plan.already_installed.empty() && action_plan.install_actions.empty()) @@ -1093,7 +1093,7 @@ namespace vcpkg::Dependencies if (auto* pscfl = p->source_control_file_location.get()) { return to_output_string( - p->request_type, p->displayname(), p->build_options, pscfl->source_location, default_ports_dir); + p->request_type, p->displayname(), p->build_options, pscfl->source_location, builtin_ports_dir); } return to_output_string(p->request_type, p->displayname(), p->build_options); diff --git a/src/vcpkg/install.cpp b/src/vcpkg/install.cpp index f916100fac98a8..0bae37e5a4233e 100644 --- a/src/vcpkg/install.cpp +++ b/src/vcpkg/install.cpp @@ -927,7 +927,7 @@ namespace vcpkg::Install Metrics::g_metrics.lock()->track_property("installplan_1", specs_string); - Dependencies::print_plan(action_plan, is_recursive, paths.ports); + Dependencies::print_plan(action_plan, is_recursive, paths.builtin_ports_directory()); auto it_pkgsconfig = options.settings.find(OPTION_WRITE_PACKAGES_CONFIG); if (it_pkgsconfig != options.settings.end()) diff --git a/src/vcpkg/paragraphs.cpp b/src/vcpkg/paragraphs.cpp index ba28acccfdf58a..421f28b002df17 100644 --- a/src/vcpkg/paragraphs.cpp +++ b/src/vcpkg/paragraphs.cpp @@ -388,24 +388,6 @@ namespace vcpkg::Paragraphs return pghs.error(); } - static void load_port_names_from_root(std::vector& ports, - const VcpkgPaths& paths, - const fs::path& registry_root) - { - 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 port_dir_entry.filename() == ".DS_Store"; }); - - for (auto&& path : port_dirs) - { - ports.push_back(fs::u8string(path.filename())); - } - } - LoadResults try_load_all_registry_ports(const VcpkgPaths& paths) { LoadResults ret; @@ -417,11 +399,11 @@ namespace vcpkg::Paragraphs for (const auto& registry : registries.registries()) { - load_port_names_from_root(ports, paths, registry.implementation().get_registry_root(paths)); + registry.implementation().get_all_port_names(ports, paths); } if (auto registry = registries.default_registry()) { - load_port_names_from_root(ports, paths, registry->get_registry_root(paths)); + registry->get_all_port_names(ports, paths); } Util::sort_unique_erase(ports); @@ -437,27 +419,36 @@ namespace vcpkg::Paragraphs continue; } - auto root = impl->get_registry_root(paths); - - auto port_path = root / fs::u8path(port_name); - - if (!fs.exists(port_path)) + auto port_entry = impl->get_port_entry(paths, port_name); + auto baseline_version = impl->get_baseline_version(paths, port_name); + if (port_entry && baseline_version) + { + auto port_path = port_entry->get_port_directory(paths, *baseline_version.get()); + if (port_path.empty()) + { + Debug::print("Registry for port `", + port_name, + "` is incorrect - baseline port version `", + baseline_version.get()->to_string(), + "` not found."); + } + auto maybe_spgh = try_load_port(fs, port_path); + if (const auto spgh = maybe_spgh.get()) + { + ret.paragraphs.emplace_back(std::move(*spgh), std::move(port_path)); + } + else + { + ret.errors.emplace_back(std::move(maybe_spgh).error()); + } + } + else { // 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 , but doesn't // declare it owns . continue; } - - auto maybe_spgh = try_load_port(fs, port_path); - if (const auto spgh = maybe_spgh.get()) - { - ret.paragraphs.emplace_back(std::move(*spgh), std::move(port_path)); - } - else - { - ret.errors.emplace_back(std::move(maybe_spgh).error()); - } } return ret; @@ -496,13 +487,16 @@ namespace vcpkg::Paragraphs LoadResults ret; std::vector port_names; - load_port_names_from_root(port_names, paths, directory); const auto& fs = paths.get_filesystem(); + auto port_dirs = fs.get_files_non_recursive(directory); + Util::sort(port_dirs); - for (const auto& name : port_names) + Util::erase_remove_if(port_dirs, + [&](auto&& port_dir_entry) { return port_dir_entry.filename() == ".DS_Store"; }); + + for (auto&& path : port_dirs) { - auto path = directory / fs::u8path(name); auto maybe_spgh = try_load_port(fs, path); if (const auto spgh = maybe_spgh.get()) { diff --git a/src/vcpkg/portfileprovider.cpp b/src/vcpkg/portfileprovider.cpp index 085219d11d5c58..3919731873ff86 100644 --- a/src/vcpkg/portfileprovider.cpp +++ b/src/vcpkg/portfileprovider.cpp @@ -120,11 +120,19 @@ namespace vcpkg::PortFileProvider const auto& fs = paths.get_filesystem(); 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 baseline_version = registry->get_baseline_version(paths, spec); + auto entry = registry->get_port_entry(paths, spec); + if (entry && baseline_version) { + auto port_directory = entry->get_port_directory(paths, *baseline_version.get()); + if (port_directory.empty()) + { + Checks::exit_with_message(VCPKG_LINE_INFO, + "Error: registry is incorrect. Baseline version for port `%s` is `%s`, " + "but that version is not in the registry.\n", + spec, + baseline_version.get()->to_string()); + } auto found_scf = Paragraphs::try_load_port(fs, port_directory); if (auto scf = found_scf.get()) { @@ -145,6 +153,18 @@ namespace vcpkg::PortFileProvider VCPKG_LINE_INFO, "Error: Failed to load port %s from %s", spec, fs::u8string(port_directory)); } } + else + { + Debug::print("Failed to find port `", + spec, + "` in registry:", + entry ? " entry found;" : " no entry found;", + baseline_version ? " baseline version found\n" : " no baseline version found\n"); + } + } + else + { + Debug::print("Failed to find registry for port: `", spec, "`.\n"); } return nullopt; } diff --git a/src/vcpkg/registries.cpp b/src/vcpkg/registries.cpp index f668630683139a..e481685d932252 100644 --- a/src/vcpkg/registries.cpp +++ b/src/vcpkg/registries.cpp @@ -1,27 +1,340 @@ +#include #include #include +#include #include #include #include +#include + +#include namespace { - struct BuiltinRegistry final : vcpkg::RegistryImpl + using namespace vcpkg; + + struct BuiltinEntry final : RegistryEntry + { + fs::path port_directory; + + BuiltinEntry(fs::path&& p) : port_directory(std::move(p)) { } + + fs::path get_port_directory(const VcpkgPaths&, const VersionT&) const override { return port_directory; } + }; + + struct BuiltinRegistry final : RegistryImpl + { + std::unique_ptr get_port_entry(const VcpkgPaths& paths, StringView port_name) const override + { + auto p = paths.builtin_ports_directory() / fs::u8path(port_name); + if (paths.get_filesystem().exists(p)) + { + return std::make_unique(std::move(p)); + } + else + { + return nullptr; + } + } + + void get_all_port_names(std::vector& names, const VcpkgPaths& paths) const override + { + const auto& fs = paths.get_filesystem(); + auto port_dirs = fs.get_files_non_recursive(paths.builtin_ports_directory()); + Util::sort(port_dirs); + + Util::erase_remove_if(port_dirs, + [&](auto&& port_dir_entry) { return port_dir_entry.filename() == ".DS_Store"; }); + + std::transform(port_dirs.begin(), port_dirs.end(), std::back_inserter(names), [](const fs::path& p) { + return fs::u8string(p.filename()); + }); + } + + Optional get_baseline_version(const VcpkgPaths&, StringView) const override { return VersionT{}; } + }; + + struct FilesystemEntry final : RegistryEntry + { + std::map versions; + + fs::path get_port_directory(const VcpkgPaths&, const VersionT& version) const override + { + auto it = versions.find(version); + if (it != versions.end()) + { + return it->second; + } + return {}; + } + }; + + struct VersionTDeserializer final : Json::IDeserializer + { + StringView type_name() const override { return "a version object"; } + View valid_fields() const override + { + static const StringView t[] = {"version-string", "port-version"}; + return t; + } + + Optional visit_object(Json::Reader& r, const Json::Object& obj) override + { + std::string version; + int port_version = 0; + + r.required_object_field(type_name(), obj, "version-string", version, version_deserializer); + r.optional_object_field(obj, "port-version", port_version, Json::NaturalNumberDeserializer::instance); + + return VersionT{std::move(version), port_version}; + } + + static Json::StringDeserializer version_deserializer; + static VersionTDeserializer instance; + }; + Json::StringDeserializer VersionTDeserializer::version_deserializer{"version"}; + VersionTDeserializer VersionTDeserializer::instance; + + struct FilesystemVersionEntryDeserializer final : Json::IDeserializer> + { + StringView type_name() const override { return "a version entry object"; } + View valid_fields() const override + { + static const StringView t[] = {"version-string", "port-version", "registry-path"}; + return t; + } + + Optional> visit_object(Json::Reader& r, const Json::Object& obj) override + { + fs::path registry_path; + + auto version = VersionTDeserializer::instance.visit_object(r, obj); + + r.required_object_field( + "version entry", obj, "registry-path", registry_path, Json::PathDeserializer::instance); + // registry_path should look like `/blah/foo` + if (registry_path.has_root_name() || !registry_path.has_root_directory()) + { + r.add_generic_error(type_name(), "must be an absolute path without a drive name"); + registry_path.clear(); + } + + return std::pair{std::move(version).value_or(VersionT{}), std::move(registry_path)}; + } + + static FilesystemVersionEntryDeserializer instance; + }; + FilesystemVersionEntryDeserializer FilesystemVersionEntryDeserializer::instance; + + struct FilesystemEntryDeserializer final : Json::IDeserializer + { + StringView type_name() const override { return "a registry entry object"; } + + Optional visit_array(Json::Reader& r, const Json::Array& arr) override + { + FilesystemEntry res; + + std::pair buffer; + for (std::size_t idx = 0; idx < arr.size(); ++idx) + { + r.visit_at_index( + arr[idx], static_cast(idx), buffer, FilesystemVersionEntryDeserializer::instance); + + auto it = res.versions.lower_bound(buffer.first); + if (it == res.versions.end() || it->first != buffer.first) + { + buffer.second = registry_root / fs::lexically_normal(buffer.second).relative_path(); + res.versions.insert(it, std::move(buffer)); + } + else if (buffer.first != VersionT{}) + { + r.add_generic_error( + type_name(), "Gave multiple definitions for version: ", buffer.first.to_string()); + } + } + + return res; + } + + FilesystemEntryDeserializer(const fs::path& p) : registry_root(p) { } + + const fs::path& registry_root; + }; + + struct BaselineDeserializer final : Json::IDeserializer>> { - virtual fs::path get_registry_root(const vcpkg::VcpkgPaths& paths) const override { return paths.ports; } + StringView type_name() const override { return "a baseline object"; } + + Optional visit_object(Json::Reader& r, const Json::Object& obj) override + { + std::map> result; + + for (auto pr : obj) + { + const auto& version_value = pr.second; + VersionT version; + r.visit_in_key(version_value, pr.first, version, VersionTDeserializer::instance); + + result.emplace(pr.first.to_string(), std::move(version)); + } + + return std::move(result); + } + + static BaselineDeserializer instance; }; + BaselineDeserializer BaselineDeserializer::instance; - struct DirectoryRegistry final : vcpkg::RegistryImpl + struct FilesystemRegistry final : RegistryImpl { - virtual fs::path get_registry_root(const vcpkg::VcpkgPaths& paths) const override + std::unique_ptr get_port_entry(const VcpkgPaths& paths, StringView port_name) const override { - return vcpkg::Files::combine(paths.config_root_dir, path); + const auto& fs = paths.get_filesystem(); + auto entry_path = this->path_to_port_entry(paths, port_name); + if (!fs.exists(entry_path)) + { + Debug::print( + "Failed to find entry for port `", port_name, "` in file: ", fs::u8string(entry_path), "\n"); + return nullptr; + } + std::error_code ec; + auto json_document = Json::parse_file(fs, entry_path, ec); + + if (auto p = json_document.get()) + { + Json::Reader r; + auto real_path = paths.config_root_dir / path; + FilesystemEntryDeserializer deserializer{real_path}; + auto entry = r.visit(p->first, deserializer); + auto pentry = entry.get(); + if (pentry && r.errors().empty()) + { + return std::make_unique(std::move(*pentry)); + } + else + { + vcpkg::Checks::exit_with_message(VCPKG_LINE_INFO, + "Failed to parse the port entry for port `%s` at `%s`.\n%s", + port_name, + fs::u8string(entry_path), + Strings::join("\n", r.errors())); + } + } + else + { + Debug::print("Failed to parse json document: ", json_document.error()->format(), "\n"); + } + + return nullptr; } - DirectoryRegistry(fs::path&& path_) : path(path_) { } + void get_all_port_names(std::vector& port_names, const VcpkgPaths& paths) const override + { + std::error_code ec; + for (const auto& super_dir : fs::directory_iterator(path)) + { + if (!fs::is_directory(paths.get_filesystem().status(super_dir, ec))) + { + continue; + } + + auto super_dir_filename = fs::u8string(super_dir.path().filename()); + if (!Strings::ends_with(super_dir_filename, "-")) + { + continue; + } + + super_dir_filename.pop_back(); + for (const auto& database_entry : fs::directory_iterator(super_dir)) + { + auto database_entry_filename = database_entry.path().filename(); + auto database_entry_filename_str = fs::u8string(database_entry_filename); + + if (!Strings::starts_with(database_entry_filename_str, super_dir_filename) || + !Strings::ends_with(database_entry_filename_str, ".json")) + { + Debug::print("Unexpected file in database (this is not an error): ", + fs::u8string(database_entry.path()), + "\n"); + continue; + } + + port_names.push_back(fs::u8string(database_entry_filename.replace_extension())); + } + } + } + + Optional get_baseline_version(const VcpkgPaths& paths, StringView port_name) const override + { + const auto& baseline_cache = baseline.get([this, &paths] { return load_baseline_versions(paths); }); + auto it = baseline_cache.find(port_name); + if (it != baseline_cache.end()) + { + return it->second; + } + else + { + return nullopt; + } + } + + FilesystemRegistry(fs::path&& path_) : path(path_) { } + + private: + fs::path path_to_registry_database(const VcpkgPaths& paths) const + { + fs::path path_to_db = paths.config_root_dir / path; + path_to_db /= fs::u8path({'\xF0', '\x9F', '\x98', '\x87'}); // utf-8 for 😇 + return path_to_db; + } + + fs::path path_to_port_entry(const VcpkgPaths& paths, StringView port_name) const + { + Checks::check_exit(VCPKG_LINE_INFO, port_name.size() != 0); + fs::path path_to_entry = path_to_registry_database(paths); + path_to_entry /= fs::u8path({port_name.byte_at_index(0), '-'}); + path_to_entry /= fs::u8path(port_name); + path_to_entry += fs::u8path(".json"); + + return path_to_entry; + } + + std::map> load_baseline_versions(const VcpkgPaths& paths) const + { + auto baseline_file = path_to_registry_database(paths) / fs::u8path("baseline.json"); + + auto value = Json::parse_file(VCPKG_LINE_INFO, paths.get_filesystem(), baseline_file); + if (!value.first.is_object()) + { + Checks::exit_with_message(VCPKG_LINE_INFO, "Error: `baseline.json` does not have a top-level object."); + } + + const auto& obj = value.first.object(); + auto baseline_value = obj.get("default"); + if (!baseline_value) + { + Checks::exit_with_message( + VCPKG_LINE_INFO, "Error: `baseline.json` does not contain the baseline \"%s\"", "default"); + } + + Json::Reader r; + std::map> result; + r.visit_in_key(*baseline_value, "default", result, BaselineDeserializer::instance); + + if (r.errors().empty()) + { + return result; + } + else + { + Checks::exit_with_message( + VCPKG_LINE_INFO, "Error: failed to parse `baseline.json`:\n%s", Strings::join("\n", r.errors())); + } + } fs::path path; + DelayedInit>> baseline; }; } @@ -35,12 +348,14 @@ namespace vcpkg Checks::check_exit(VCPKG_LINE_INFO, implementation_ != nullptr); } + RegistryImplDeserializer RegistryImplDeserializer::instance; + StringView RegistryImplDeserializer::type_name() const { return "a registry"; } constexpr StringLiteral RegistryImplDeserializer::KIND; constexpr StringLiteral RegistryImplDeserializer::PATH; constexpr StringLiteral RegistryImplDeserializer::KIND_BUILTIN; - constexpr StringLiteral RegistryImplDeserializer::KIND_DIRECTORY; + constexpr StringLiteral RegistryImplDeserializer::KIND_FILESYSTEM; View RegistryImplDeserializer::valid_fields() const { @@ -65,19 +380,18 @@ namespace vcpkg } return static_cast>(std::make_unique()); } - else if (kind == KIND_DIRECTORY) + else if (kind == KIND_FILESYSTEM) { fs::path path; - r.required_object_field("a directory registry", obj, PATH, path, Json::PathDeserializer::instance); + r.required_object_field("a filesystem registry", obj, PATH, path, Json::PathDeserializer::instance); - return static_cast>(std::make_unique(std::move(path))); + return static_cast>(std::make_unique(std::move(path))); } else { return nullopt; } } - RegistryImplDeserializer RegistryImplDeserializer::instance; StringView RegistryDeserializer::type_name() const { return "a registry"; } @@ -95,7 +409,7 @@ namespace vcpkg Optional RegistryDeserializer::visit_object(Json::Reader& r, const Json::Object& obj) { - auto impl = RegistryImplDeserializer{}.visit_object(r, obj); + auto impl = RegistryImplDeserializer::instance.visit_object(r, obj); if (!impl.has_value()) { diff --git a/src/vcpkg/vcpkgpaths.cpp b/src/vcpkg/vcpkgpaths.cpp index 1d474605694de9..cd438fef3beef4 100644 --- a/src/vcpkg/vcpkgpaths.cpp +++ b/src/vcpkg/vcpkgpaths.cpp @@ -325,7 +325,6 @@ If you wish to silence this error and use classic mode, you can: process_output_directory(filesystem, root, args.downloads_root_dir.get(), "downloads", VCPKG_LINE_INFO); packages = process_output_directory(filesystem, root, args.packages_root_dir.get(), "packages", VCPKG_LINE_INFO); - ports = filesystem.canonical(VCPKG_LINE_INFO, root / fs::u8path("ports")); scripts = process_input_directory(filesystem, root, args.scripts_root_dir.get(), "scripts", VCPKG_LINE_INFO); prefab = root / fs::u8path("prefab"); diff --git a/src/vcpkg/versiont.cpp b/src/vcpkg/versiont.cpp index c419745c2837f1..8853123079da24 100644 --- a/src/vcpkg/versiont.cpp +++ b/src/vcpkg/versiont.cpp @@ -19,6 +19,21 @@ namespace vcpkg } bool operator!=(const VersionT& left, const VersionT& right) { return !(left == right); } + bool VersionTMapLess::operator()(const VersionT& left, const VersionT& right) const + { + auto cmp = left.value.compare(right.value); + if (cmp < 0) + { + return true; + } + else if (cmp > 0) + { + return false; + } + + return left.port_version < right.port_version; + } + VersionDiff::VersionDiff() noexcept : left(), right() { } VersionDiff::VersionDiff(const VersionT& left, const VersionT& right) : left(left), right(right) { }