aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornicole mazzuca <mazzucan@outlook.com>2020-11-09 11:15:31 -0800
committerGitHub <noreply@github.com>2020-11-09 11:15:31 -0800
commita94f261320398b859e5afb2b4d4c5a574f5d205a (patch)
treed8ae8ad5f740275f2c75e4981c5bd27a9b15a9ed
parent179bdd1697e03663869c2c3341343396c3ac6dfb (diff)
downloadvcpkg-a94f261320398b859e5afb2b4d4c5a574f5d205a.tar.gz
vcpkg-a94f261320398b859e5afb2b4d4c5a574f5d205a.zip
[vcpkg] Initial Registries: Part 2 MVP (#14153)
* [vcpkg wip] start implementation of registries pt. 2 * Remove the `ports` field of `VcpkgPaths` This is an implementation detail, and so we want to make sure that it's obvious that this is an internal detail; thus, we add a new function `builtin_ports_directory()`, which returns the directory where the builtin ports backing store is. * continue WIP * wip * It works! * format * fix some issues * switch from function static to DelayedInit * fix lexically_normal for experimental::filesystem * format * fix missing include * add STL notice * whee error handling * moar error handling! * ignore extra files in registries * formatting * Billy CRs * Update toolsrc/include/vcpkg/versiont.h * fix add_filename test * fix tests * remove unused function add_filename
-rw-r--r--toolsrc/include/vcpkg/base/delayed_init.h29
-rw-r--r--toolsrc/include/vcpkg/base/files.h7
-rw-r--r--toolsrc/include/vcpkg/base/jsonreader.h24
-rw-r--r--toolsrc/include/vcpkg/base/optional.h4
-rw-r--r--toolsrc/include/vcpkg/base/pragmas.h1
-rw-r--r--toolsrc/include/vcpkg/configurationdeserializer.h2
-rw-r--r--toolsrc/include/vcpkg/dependencies.h4
-rw-r--r--toolsrc/include/vcpkg/registries.h17
-rw-r--r--toolsrc/include/vcpkg/vcpkgpaths.h5
-rw-r--r--toolsrc/include/vcpkg/versiont.h8
-rw-r--r--toolsrc/src/vcpkg-test/commands.create.cpp2
-rw-r--r--toolsrc/src/vcpkg-test/files.cpp105
-rw-r--r--toolsrc/src/vcpkg-test/manifests.cpp2
-rw-r--r--toolsrc/src/vcpkg/base/files.cpp203
-rw-r--r--toolsrc/src/vcpkg/build.cpp2
-rw-r--r--toolsrc/src/vcpkg/commands.autocomplete.cpp3
-rw-r--r--toolsrc/src/vcpkg/commands.ci.cpp2
-rw-r--r--toolsrc/src/vcpkg/commands.edit.cpp6
-rw-r--r--toolsrc/src/vcpkg/commands.format-manifest.cpp2
-rw-r--r--toolsrc/src/vcpkg/commands.portsdiff.cpp2
-rw-r--r--toolsrc/src/vcpkg/commands.setinstalled.cpp2
-rw-r--r--toolsrc/src/vcpkg/commands.upgrade.cpp2
-rw-r--r--toolsrc/src/vcpkg/dependencies.cpp10
-rw-r--r--toolsrc/src/vcpkg/install.cpp2
-rw-r--r--toolsrc/src/vcpkg/paragraphs.cpp70
-rw-r--r--toolsrc/src/vcpkg/portfileprovider.cpp28
-rw-r--r--toolsrc/src/vcpkg/registries.cpp338
-rw-r--r--toolsrc/src/vcpkg/vcpkgpaths.cpp1
-rw-r--r--toolsrc/src/vcpkg/versiont.cpp15
29 files changed, 771 insertions, 127 deletions
diff --git a/toolsrc/include/vcpkg/base/delayed_init.h b/toolsrc/include/vcpkg/base/delayed_init.h
new file mode 100644
index 000000000..a3257f54e
--- /dev/null
+++ b/toolsrc/include/vcpkg/base/delayed_init.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <vcpkg/base/optional.h>
+
+#include <memory>
+#include <mutex>
+
+namespace vcpkg
+{
+ // implements the equivalent of function static initialization for an object
+ template<class T>
+ struct DelayedInit
+ {
+ template<class F>
+ const T& get(F&& f) const
+ {
+ std::call_once(underlying_->flag_, [&f, this]() { underlying_->storage_ = std::forward<F>(f)(); });
+ return *underlying_->storage_.get();
+ }
+
+ private:
+ struct Storage
+ {
+ std::once_flag flag_;
+ Optional<T> storage_;
+ };
+ std::unique_ptr<Storage> underlying_ = std::make_unique<Storage>();
+ };
+}
diff --git a/toolsrc/include/vcpkg/base/files.h b/toolsrc/include/vcpkg/base/files.h
index 1c5e8393f..3e56101c9 100644
--- a/toolsrc/include/vcpkg/base/files.h
+++ b/toolsrc/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<char> 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/toolsrc/include/vcpkg/base/jsonreader.h b/toolsrc/include/vcpkg/base/jsonreader.h
index 6518501a2..cdd0299d2 100644
--- a/toolsrc/include/vcpkg/base/jsonreader.h
+++ b/toolsrc/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<class Type>
+ void visit_at_index(const Value& value, int64_t index, Type& place, IDeserializer<Type>& 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<class Type>
bool optional_object_field(const Object& obj, StringView key, Type& place, IDeserializer<Type>& 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<class Type>
Optional<Type> IDeserializer<Type>::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<std::string>
{
diff --git a/toolsrc/include/vcpkg/base/optional.h b/toolsrc/include/vcpkg/base/optional.h
index 606d60ce5..c46c2e6f8 100644
--- a/toolsrc/include/vcpkg/base/optional.h
+++ b/toolsrc/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<class U, class = std::enable_if_t<!std::is_reference<U>::value>>
- constexpr explicit OptionalStorage(Optional<U>&& t) : m_is_present(false), m_inactive()
+ explicit OptionalStorage(Optional<U>&& t) : m_is_present(false), m_inactive()
{
if (auto p = t.get())
{
@@ -39,7 +39,7 @@ namespace vcpkg
}
}
template<class U>
- constexpr explicit OptionalStorage(const Optional<U>& t) : m_is_present(false), m_inactive()
+ explicit OptionalStorage(const Optional<U>& t) : m_is_present(false), m_inactive()
{
if (auto p = t.get())
{
diff --git a/toolsrc/include/vcpkg/base/pragmas.h b/toolsrc/include/vcpkg/base/pragmas.h
index 73f14c4b2..06efd5105 100644
--- a/toolsrc/include/vcpkg/base/pragmas.h
+++ b/toolsrc/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/toolsrc/include/vcpkg/configurationdeserializer.h b/toolsrc/include/vcpkg/configurationdeserializer.h
index 83d3d0869..d6b239356 100644
--- a/toolsrc/include/vcpkg/configurationdeserializer.h
+++ b/toolsrc/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<StringView> valid_fields() const override;
diff --git a/toolsrc/include/vcpkg/dependencies.h b/toolsrc/include/vcpkg/dependencies.h
index db135aa28..5e46a6194 100644
--- a/toolsrc/include/vcpkg/dependencies.h
+++ b/toolsrc/include/vcpkg/dependencies.h
@@ -172,7 +172,5 @@ namespace vcpkg::Dependencies
std::vector<std::string> 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/toolsrc/include/vcpkg/registries.h b/toolsrc/include/vcpkg/registries.h
index 812bce86b..1f8c0f393 100644
--- a/toolsrc/include/vcpkg/registries.h
+++ b/toolsrc/include/vcpkg/registries.h
@@ -6,6 +6,8 @@
#include <vcpkg/base/stringview.h>
#include <vcpkg/base/view.h>
+#include <vcpkg/versiont.h>
+
#include <memory>
#include <string>
#include <system_error>
@@ -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<RegistryEntry> 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<std::string>& port_names, const VcpkgPaths& paths) const = 0;
+
+ virtual Optional<VersionT> get_baseline_version(const VcpkgPaths& paths, StringView port_name) const = 0;
virtual ~RegistryImpl() = default;
};
diff --git a/toolsrc/include/vcpkg/vcpkgpaths.h b/toolsrc/include/vcpkg/vcpkgpaths.h
index aa1ba92d7..def874e7c 100644
--- a/toolsrc/include/vcpkg/vcpkgpaths.h
+++ b/toolsrc/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<details::VcpkgPathsImpl> m_pimpl;
};
diff --git a/toolsrc/include/vcpkg/versiont.h b/toolsrc/include/vcpkg/versiont.h
index 768ca0c47..506d7090a 100644
--- a/toolsrc/include/vcpkg/versiont.h
+++ b/toolsrc/include/vcpkg/versiont.h
@@ -1,4 +1,5 @@
#pragma once
+
#include <string>
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/toolsrc/src/vcpkg-test/commands.create.cpp b/toolsrc/src/vcpkg-test/commands.create.cpp
index 0cc93bd57..8b8dfa230 100644
--- a/toolsrc/src/vcpkg-test/commands.create.cpp
+++ b/toolsrc/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/toolsrc/src/vcpkg-test/files.cpp b/toolsrc/src/vcpkg-test/files.cpp
index 94cf7e1dd..ffa72d0fb 100644
--- a/toolsrc/src/vcpkg-test/files.cpp
+++ b/toolsrc/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/toolsrc/src/vcpkg-test/manifests.cpp b/toolsrc/src/vcpkg-test/manifests.cpp
index 5ec3f160a..ee6b15e8d 100644
--- a/toolsrc/src/vcpkg-test/manifests.cpp
+++ b/toolsrc/src/vcpkg-test/manifests.cpp
@@ -321,7 +321,7 @@ TEST_CASE ("Serialize all the ports", "[manifests]")
std::vector<SourceControlFile> 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/toolsrc/src/vcpkg/base/files.cpp b/toolsrc/src/vcpkg/base/files.cpp
index ee3a339e0..4cccebd45 100644
--- a/toolsrc/src/vcpkg/base/files.cpp
+++ b/toolsrc/src/vcpkg/base/files.cpp
@@ -21,8 +21,39 @@
#endif // ^^^ defined(__APPLE__)
#include <algorithm>
+#include <list>
#include <string>
+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<NativeStringView> 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<fs::path> 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<fs::path> 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/toolsrc/src/vcpkg/build.cpp b/toolsrc/src/vcpkg/build.cpp
index c517750d8..c20f48b36 100644
--- a/toolsrc/src/vcpkg/build.cpp
+++ b/toolsrc/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/toolsrc/src/vcpkg/commands.autocomplete.cpp b/toolsrc/src/vcpkg/commands.autocomplete.cpp
index 84a1f5ed5..7e9039dcb 100644
--- a/toolsrc/src/vcpkg/commands.autocomplete.cpp
+++ b/toolsrc/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/toolsrc/src/vcpkg/commands.ci.cpp b/toolsrc/src/vcpkg/commands.ci.cpp
index 7741b55e8..1d83f471b 100644
--- a/toolsrc/src/vcpkg/commands.ci.cpp
+++ b/toolsrc/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/toolsrc/src/vcpkg/commands.edit.cpp b/toolsrc/src/vcpkg/commands.edit.cpp
index 8d6401df1..e23b35d0c 100644
--- a/toolsrc/src/vcpkg/commands.edit.cpp
+++ b/toolsrc/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<std::string>& 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/toolsrc/src/vcpkg/commands.format-manifest.cpp b/toolsrc/src/vcpkg/commands.format-manifest.cpp
index 8d4be1ce9..64d491a84 100644
--- a/toolsrc/src/vcpkg/commands.format-manifest.cpp
+++ b/toolsrc/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/toolsrc/src/vcpkg/commands.portsdiff.cpp b/toolsrc/src/vcpkg/commands.portsdiff.cpp
index 63e0e0e24..d93d4d98a 100644
--- a/toolsrc/src/vcpkg/commands.portsdiff.cpp
+++ b/toolsrc/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/toolsrc/src/vcpkg/commands.setinstalled.cpp b/toolsrc/src/vcpkg/commands.setinstalled.cpp
index 1c04694c8..99c41a5b4 100644
--- a/toolsrc/src/vcpkg/commands.setinstalled.cpp
+++ b/toolsrc/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/toolsrc/src/vcpkg/commands.upgrade.cpp b/toolsrc/src/vcpkg/commands.upgrade.cpp
index 7abf1865d..0d8a6c4af 100644
--- a/toolsrc/src/vcpkg/commands.upgrade.cpp
+++ b/toolsrc/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/toolsrc/src/vcpkg/dependencies.cpp b/toolsrc/src/vcpkg/dependencies.cpp
index 21af23c0b..69db32e27 100644
--- a/toolsrc/src/vcpkg/dependencies.cpp
+++ b/toolsrc/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/toolsrc/src/vcpkg/install.cpp b/toolsrc/src/vcpkg/install.cpp
index f916100fa..0bae37e5a 100644
--- a/toolsrc/src/vcpkg/install.cpp
+++ b/toolsrc/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/toolsrc/src/vcpkg/paragraphs.cpp b/toolsrc/src/vcpkg/paragraphs.cpp
index ba28acccf..421f28b00 100644
--- a/toolsrc/src/vcpkg/paragraphs.cpp
+++ b/toolsrc/src/vcpkg/paragraphs.cpp
@@ -388,24 +388,6 @@ namespace vcpkg::Paragraphs
return pghs.error();
}
- static void load_port_names_from_root(std::vector<std::string>& 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 <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), 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<std::string> 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/toolsrc/src/vcpkg/portfileprovider.cpp b/toolsrc/src/vcpkg/portfileprovider.cpp
index 085219d11..391973187 100644
--- a/toolsrc/src/vcpkg/portfileprovider.cpp
+++ b/toolsrc/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/toolsrc/src/vcpkg/registries.cpp b/toolsrc/src/vcpkg/registries.cpp
index f66863068..e481685d9 100644
--- a/toolsrc/src/vcpkg/registries.cpp
+++ b/toolsrc/src/vcpkg/registries.cpp
@@ -1,27 +1,340 @@
+#include <vcpkg/base/delayed_init.h>
#include <vcpkg/base/json.h>
#include <vcpkg/base/jsonreader.h>
+#include <vcpkg/base/system.debug.h>
#include <vcpkg/configurationdeserializer.h>
#include <vcpkg/registries.h>
#include <vcpkg/vcpkgpaths.h>
+#include <vcpkg/versiont.h>
+
+#include <map>
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<RegistryEntry> 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<BuiltinEntry>(std::move(p));
+ }
+ else
+ {
+ return nullptr;
+ }
+ }
+
+ void get_all_port_names(std::vector<std::string>& 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<VersionT> get_baseline_version(const VcpkgPaths&, StringView) const override { return VersionT{}; }
+ };
+
+ struct FilesystemEntry final : RegistryEntry
+ {
+ std::map<VersionT, fs::path, VersionTMapLess> 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<VersionT>
+ {
+ StringView type_name() const override { return "a version object"; }
+ View<StringView> valid_fields() const override
+ {
+ static const StringView t[] = {"version-string", "port-version"};
+ return t;
+ }
+
+ Optional<VersionT> 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<std::pair<VersionT, fs::path>>
+ {
+ StringView type_name() const override { return "a version entry object"; }
+ View<StringView> valid_fields() const override
+ {
+ static const StringView t[] = {"version-string", "port-version", "registry-path"};
+ return t;
+ }
+
+ Optional<std::pair<VersionT, fs::path>> 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<VersionT, fs::path>{std::move(version).value_or(VersionT{}), std::move(registry_path)};
+ }
+
+ static FilesystemVersionEntryDeserializer instance;
+ };
+ FilesystemVersionEntryDeserializer FilesystemVersionEntryDeserializer::instance;
+
+ struct FilesystemEntryDeserializer final : Json::IDeserializer<FilesystemEntry>
+ {
+ StringView type_name() const override { return "a registry entry object"; }
+
+ Optional<FilesystemEntry> visit_array(Json::Reader& r, const Json::Array& arr) override
+ {
+ FilesystemEntry res;
+
+ std::pair<VersionT, fs::path> buffer;
+ for (std::size_t idx = 0; idx < arr.size(); ++idx)
+ {
+ r.visit_at_index(
+ arr[idx], static_cast<int64_t>(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<std::map<std::string, VersionT, std::less<>>>
{
- 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<type> visit_object(Json::Reader& r, const Json::Object& obj) override
+ {
+ std::map<std::string, VersionT, std::less<>> 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<RegistryEntry> 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<FilesystemEntry>(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<std::string>& 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<VersionT> 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<std::string, VersionT, std::less<>> 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<std::string, VersionT, std::less<>> 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<std::map<std::string, VersionT, std::less<>>> 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<StringView> RegistryImplDeserializer::valid_fields() const
{
@@ -65,19 +380,18 @@ namespace vcpkg
}
return static_cast<std::unique_ptr<RegistryImpl>>(std::make_unique<BuiltinRegistry>());
}
- 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::unique_ptr<RegistryImpl>>(std::make_unique<DirectoryRegistry>(std::move(path)));
+ return static_cast<std::unique_ptr<RegistryImpl>>(std::make_unique<FilesystemRegistry>(std::move(path)));
}
else
{
return nullopt;
}
}
- RegistryImplDeserializer RegistryImplDeserializer::instance;
StringView RegistryDeserializer::type_name() const { return "a registry"; }
@@ -95,7 +409,7 @@ namespace vcpkg
Optional<Registry> 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/toolsrc/src/vcpkg/vcpkgpaths.cpp b/toolsrc/src/vcpkg/vcpkgpaths.cpp
index 1d4746056..cd438fef3 100644
--- a/toolsrc/src/vcpkg/vcpkgpaths.cpp
+++ b/toolsrc/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/toolsrc/src/vcpkg/versiont.cpp b/toolsrc/src/vcpkg/versiont.cpp
index c419745c2..885312307 100644
--- a/toolsrc/src/vcpkg/versiont.cpp
+++ b/toolsrc/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) { }