From 1cf1f7f1261ec726be1c07c0f608a8a00790d265 Mon Sep 17 00:00:00 2001 From: nicole mazzuca Date: Tue, 19 Apr 2022 13:04:52 -0700 Subject: [PATCH] [x-update-baseline] Initial implementation (#341) * start working * update vcpkgpaths to better support what I want * optional: make certain `get()` is only called when it won't result in a dangling pointer * vcpkgpaths: remove JsonStyle support as it isn't used * vcpkgpaths: switch from two functions returning Optional + Optional, to one function returning Optional * vcpkgpaths: add `get_configuration_and_location` * rename get_*_and_location -> get_* * lots of CRs * get_latest_baseline -> configuration.cpp * format * optional - add see-through operator== * add `--add-initial-baseline` option * more correct if-elses * add messages! ``` PS /../nimazzuc/projects/testproj> ../vcpkg-tool/build/vcpkg x-update-baseline --dry-run registry 'https://github.com/microsoft/vcpkg' already up to date: 89295c9fe22e97ca9e380a6c771ccf2ff09dca95 registry '/a/b' not updated: default registry 'https://foobar' not updated: default warning: git failed to fetch remote repository https://foobar Error: Failed to fetch ref HEAD from repository https://foobar. fatal: unable to access 'https://foobar/': Could not resolve host: foobar ``` * update - hopefully fix Billy's CRs * fix formatting * (optional::operator==) add tests, add opt == opt * rename things * registry_location -> pretty_location * ManifestAndLocation -> ManifestAndPath * ConfigurationAndLocation -> ConfigurationAndSource * minor other CRs * forgot to to_string * format * I think this is what robert wants? * format --- include/vcpkg/base/messages.h | 11 +- include/vcpkg/base/optional.h | 80 +++++++---- include/vcpkg/commands.update-baseline.h | 11 ++ include/vcpkg/configuration.h | 22 +++ include/vcpkg/fwd/configuration.h | 1 + include/vcpkg/registries.h | 2 + include/vcpkg/vcpkgpaths.h | 10 +- locales/messages.en.json | 7 + locales/messages.json | 13 ++ src/vcpkg-test/commands.cpp | 1 + src/vcpkg-test/optional.cpp | 124 +++++++++++++++++ src/vcpkg/commands.add.cpp | 5 +- src/vcpkg/commands.cpp | 3 + src/vcpkg/commands.update-baseline.cpp | 167 +++++++++++++++++++++++ src/vcpkg/configuration.cpp | 80 +++++++++++ src/vcpkg/install.cpp | 11 +- src/vcpkg/registries.cpp | 2 +- src/vcpkg/vcpkgpaths.cpp | 60 ++++---- 18 files changed, 535 insertions(+), 75 deletions(-) create mode 100644 include/vcpkg/commands.update-baseline.h create mode 100644 src/vcpkg/commands.update-baseline.cpp diff --git a/include/vcpkg/base/messages.h b/include/vcpkg/base/messages.h index f19fb4bec..e7d122083 100644 --- a/include/vcpkg/base/messages.h +++ b/include/vcpkg/base/messages.h @@ -106,6 +106,9 @@ namespace vcpkg return lhs.data() >= rhs.data(); } + bool empty() { return m_data.empty(); } + void clear() { m_data.clear(); } + private: std::string m_data; @@ -244,6 +247,8 @@ namespace vcpkg::msg DECLARE_MSG_ARG(expected, ""); DECLARE_MSG_ARG(actual, ""); DECLARE_MSG_ARG(list, ""); + DECLARE_MSG_ARG(old_value, ""); + DECLARE_MSG_ARG(new_value, ""); DECLARE_MSG_ARG(actual_version, "1.3.8"); DECLARE_MSG_ARG(arch, "x64"); @@ -317,10 +322,10 @@ namespace vcpkg::msg inline void print_warning(const LocalizedString& s) { - print(Color::warning, format(msgErrorMessage).append(s).appendnl()); + print(Color::warning, format(msgWarningMessage).append(s).appendnl()); } template - void print_warning(Message m, Ts... args) + typename Message::is_message_type print_warning(Message m, Ts... args) { print(Color::warning, format(msgWarningMessage).append(format(m, args...).appendnl())); } @@ -330,7 +335,7 @@ namespace vcpkg::msg print(Color::error, format(msgErrorMessage).append(s).appendnl()); } template - void print_error(Message m, Ts... args) + typename Message::is_message_type print_error(Message m, Ts... args) { print(Color::error, format(msgErrorMessage).append(format(m, args...).appendnl())); } diff --git a/include/vcpkg/base/optional.h b/include/vcpkg/base/optional.h index a23d2cda7..aa2021e48 100644 --- a/include/vcpkg/base/optional.h +++ b/include/vcpkg/base/optional.h @@ -107,6 +107,11 @@ namespace vcpkg const T& value() const { return this->m_t; } T& value() { return this->m_t; } + const T* get() const& { return m_is_present ? &m_t : nullptr; } + T* get() & { return m_is_present ? &m_t : nullptr; } + const T* get() const&& = delete; + T* get() && = delete; + void destroy() { m_is_present = false; @@ -180,6 +185,11 @@ namespace vcpkg const T& value() const { return this->m_t; } T& value() { return this->m_t; } + const T* get() const& { return m_is_present ? &m_t : nullptr; } + T* get() & { return m_is_present ? &m_t : nullptr; } + const T* get() const&& = delete; + T* get() && = delete; + template T& emplace(Args&&... args) { @@ -228,6 +238,8 @@ namespace vcpkg return *m_t; } + T* get() const { return m_t; } + private: T* m_t; }; @@ -246,6 +258,8 @@ namespace vcpkg const T& value() const { return *this->m_t; } + const T* get() const { return m_t; } + const T& emplace(const T& t) { m_t = &t; @@ -260,6 +274,10 @@ namespace vcpkg template struct Optional { + private: + details::OptionalStorage m_base; + + public: constexpr Optional() noexcept { } // Constructors are intentionally implicit @@ -320,12 +338,11 @@ namespace vcpkg return this->m_base.has_value() ? std::move(this->m_base.value()) : static_cast(default_value); } - typename std::add_pointer::type get() const - { - return this->m_base.has_value() ? &this->m_base.value() : nullptr; - } - - typename std::add_pointer::type get() { return this->m_base.has_value() ? &this->m_base.value() : nullptr; } + // this allows us to error out when `.get()` would return a pointer to a temporary + decltype(auto) get() const& { return this->m_base.get(); } + decltype(auto) get() & { return this->m_base.get(); } + decltype(auto) get() const&& { return std::move(this->m_base).get(); } + decltype(auto) get() && { return std::move(this->m_base).get(); } template using map_t = decltype(std::declval()(std::declval())); @@ -396,9 +413,6 @@ namespace vcpkg return !rhs.m_base.has_value(); } friend bool operator!=(const Optional& lhs, const Optional& rhs) noexcept { return !(lhs == rhs); } - - private: - details::OptionalStorage m_base; }; template @@ -407,28 +421,44 @@ namespace vcpkg return Optional>(std::forward(u)); } - template - bool operator==(const Optional& o, const T& t) + // these cannot be hidden friends, unfortunately + template + auto operator==(const Optional& lhs, const Optional& rhs) -> decltype(*lhs.get() == *rhs.get()) { - if (auto p = o.get()) return *p == t; - return false; + if (lhs.has_value() && rhs.has_value()) + { + return *lhs.get() == *rhs.get(); + } + return lhs.has_value() == rhs.has_value(); } - template - bool operator==(const T& t, const Optional& o) + template + auto operator==(const Optional& lhs, const U& rhs) -> decltype(*lhs.get() == rhs) { - if (auto p = o.get()) return t == *p; - return false; + return lhs.has_value() ? *lhs.get() == rhs : false; } - template - bool operator!=(const Optional& o, const T& t) + template + auto operator==(const T& lhs, const Optional& rhs) -> decltype(lhs == *rhs.get()) { - if (auto p = o.get()) return *p != t; - return true; + return rhs.has_value() ? lhs == *rhs.get() : false; } - template - bool operator!=(const T& t, const Optional& o) + + template + auto operator!=(const Optional& lhs, const Optional& rhs) -> decltype(*lhs.get() != *rhs.get()) { - if (auto p = o.get()) return t != *p; - return true; + if (lhs.has_value() && rhs.has_value()) + { + return *lhs.get() != *rhs.get(); + } + return lhs.has_value() != rhs.has_value(); + } + template + auto operator!=(const Optional& lhs, const U& rhs) -> decltype(*lhs.get() != rhs) + { + return lhs.has_value() ? *lhs.get() != rhs : true; + } + template + auto operator!=(const T& lhs, const Optional& rhs) -> decltype(lhs != *rhs.get()) + { + return rhs.has_value() ? lhs != *rhs.get() : true; } } diff --git a/include/vcpkg/commands.update-baseline.h b/include/vcpkg/commands.update-baseline.h new file mode 100644 index 000000000..90afc6bbb --- /dev/null +++ b/include/vcpkg/commands.update-baseline.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace vcpkg::Commands +{ + struct UpdateBaselineCommand : PathsCommand + { + virtual void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) const override; + }; +} diff --git a/include/vcpkg/configuration.h b/include/vcpkg/configuration.h index 4315607dc..bcd279f9b 100644 --- a/include/vcpkg/configuration.h +++ b/include/vcpkg/configuration.h @@ -23,6 +23,9 @@ namespace vcpkg Optional> packages; Json::Value serialize() const; + + ExpectedL> get_latest_baseline(const VcpkgPaths& paths) const; + StringView pretty_location() const; }; struct Configuration @@ -42,6 +45,25 @@ namespace vcpkg static View known_fields(); }; + enum class ConfigurationSource + { + None, + VcpkgConfigurationFile, + ManifestFile, + }; + + struct ConfigurationAndSource + { + Configuration config; + Path directory; + ConfigurationSource source = ConfigurationSource::None; + + std::unique_ptr instantiate_registry_set(const VcpkgPaths& paths) const + { + return config.instantiate_registry_set(paths, directory); + } + }; + struct ManifestConfiguration { Optional builtin_baseline; diff --git a/include/vcpkg/fwd/configuration.h b/include/vcpkg/fwd/configuration.h index 6be58b363..da82b0133 100644 --- a/include/vcpkg/fwd/configuration.h +++ b/include/vcpkg/fwd/configuration.h @@ -3,6 +3,7 @@ namespace vcpkg { struct Configuration; + struct ConfigurationAndSource; struct RegistryConfig; struct ManifestConfiguration; } diff --git a/include/vcpkg/registries.h b/include/vcpkg/registries.h index 3394fa0f4..351a2a973 100644 --- a/include/vcpkg/registries.h +++ b/include/vcpkg/registries.h @@ -20,6 +20,8 @@ namespace vcpkg { + constexpr StringLiteral builtin_registry_git_url() { return "https://github.com/microsoft/vcpkg"; } + struct LockFile { struct EntryData diff --git a/include/vcpkg/vcpkgpaths.h b/include/vcpkg/vcpkgpaths.h index d9250f3c5..06158cacc 100644 --- a/include/vcpkg/vcpkgpaths.h +++ b/include/vcpkg/vcpkgpaths.h @@ -58,6 +58,12 @@ namespace vcpkg struct PackageSpec; struct Triplet; + struct ManifestAndPath + { + Json::Object manifest; + Path path; + }; + struct VcpkgPaths { struct TripletFile @@ -147,8 +153,8 @@ namespace vcpkg const Path& relative_path_to_file) const; ExpectedS git_checkout_object_from_remote_registry(StringView tree) const; - Optional get_manifest() const; - Optional get_manifest_path() const; + Optional get_manifest() const; + const ConfigurationAndSource& get_configuration() const; const RegistrySet& get_registry_set() const; // Retrieve a toolset matching the requirements in prebuildinfo diff --git a/locales/messages.en.json b/locales/messages.en.json index 6df581373..34e2caa99 100644 --- a/locales/messages.en.json +++ b/locales/messages.en.json @@ -132,6 +132,13 @@ "UnknownBinaryProviderType": "unknown binary provider type: valid providers are 'clear', 'default', 'nuget', 'nugetconfig', 'interactive', and 'files'", "UnsupportedSystemName": "Error: Could not map VCPKG_CMAKE_SYSTEM_NAME '{system_name}' to a vcvarsall platform. Supported system names are '', 'Windows' and 'WindowsStore'.", "UnsupportedToolchain": "Error: in triplet {triplet}: Unable to find a valid toolchain combination.\n The requested target architecture was {arch}\n The selected Visual Studio instance is at {path}\n The available toolchain combinations are {list}\n", + "UpdateBaselineAddBaselineNoManifest": "the --{option} switch was passed, but there is no manifest file to add a `builtin-baseline` field to.", + "UpdateBaselineLocalGitError": "git failed to parse HEAD for the local vcpkg registry at '{path}'", + "UpdateBaselineNoConfiguration": "neither `vcpkg.json` nor `vcpkg-configuration.json` exist to update.", + "UpdateBaselineNoExistingBuiltinBaseline": "the manifest file currently does not contain a `builtin-baseline` field; in order to add one, pass the --{option} switch.", + "UpdateBaselineNoUpdate": "registry '{url}' not updated: '{value}'", + "UpdateBaselineRemoteGitError": "git failed to fetch remote repository '{url}'", + "UpdateBaselineUpdatedBaseline": "updated registry '{url}': baseline '{old_value}' -> '{new_value}'", "UploadedPackagesToVendor": "Uploaded {count} package(s) to {vendor} in {elapsed}", "UsingManifestAt": "Using manifest file at {path}.", "Utf8DecoderDereferencedAtEof": "dereferenced Utf8Decoder at the end of a string.", diff --git a/locales/messages.json b/locales/messages.json index 8388bd04e..55dddbc84 100644 --- a/locales/messages.json +++ b/locales/messages.json @@ -228,6 +228,19 @@ "_UnsupportedSystemName.comment": "example of {system_name} is 'Darwin'.\n", "UnsupportedToolchain": "Error: in triplet {triplet}: Unable to find a valid toolchain combination.\n The requested target architecture was {arch}\n The selected Visual Studio instance is at {path}\n The available toolchain combinations are {list}\n", "_UnsupportedToolchain.comment": "example for {list} is 'x86, arm64'\nexample of {triplet} is 'x64-windows'.\nexample of {arch} is 'x64'.\nexample of {path} is '/foo/bar'.\n", + "UpdateBaselineAddBaselineNoManifest": "the --{option} switch was passed, but there is no manifest file to add a `builtin-baseline` field to.", + "_UpdateBaselineAddBaselineNoManifest.comment": "example of {option} is 'editable'.\n", + "UpdateBaselineLocalGitError": "git failed to parse HEAD for the local vcpkg registry at '{path}'", + "_UpdateBaselineLocalGitError.comment": "example of {path} is '/foo/bar'.\n", + "UpdateBaselineNoConfiguration": "neither `vcpkg.json` nor `vcpkg-configuration.json` exist to update.", + "UpdateBaselineNoExistingBuiltinBaseline": "the manifest file currently does not contain a `builtin-baseline` field; in order to add one, pass the --{option} switch.", + "_UpdateBaselineNoExistingBuiltinBaseline.comment": "example of {option} is 'editable'.\n", + "UpdateBaselineNoUpdate": "registry '{url}' not updated: '{value}'", + "_UpdateBaselineNoUpdate.comment": "example of {value} is '5507daa796359fe8d45418e694328e878ac2b82f'\nexample of {url} is 'https://github.com/microsoft/vcpkg'.\n", + "UpdateBaselineRemoteGitError": "git failed to fetch remote repository '{url}'", + "_UpdateBaselineRemoteGitError.comment": "example of {url} is 'https://github.com/microsoft/vcpkg'.\n", + "UpdateBaselineUpdatedBaseline": "updated registry '{url}': baseline '{old_value}' -> '{new_value}'", + "_UpdateBaselineUpdatedBaseline.comment": "example of {old_value}, {new_value} is '5507daa796359fe8d45418e694328e878ac2b82f'\nexample of {url} is 'https://github.com/microsoft/vcpkg'.\n", "UploadedPackagesToVendor": "Uploaded {count} package(s) to {vendor} in {elapsed}", "_UploadedPackagesToVendor.comment": "example of {count} is '42'.\nexample of {elapsed} is '3.532 min'.\nexample of {vendor} is 'Azure'.\n", "UsingManifestAt": "Using manifest file at {path}.", diff --git a/src/vcpkg-test/commands.cpp b/src/vcpkg-test/commands.cpp index 2f0d30892..251177c75 100644 --- a/src/vcpkg-test/commands.cpp +++ b/src/vcpkg-test/commands.cpp @@ -63,6 +63,7 @@ TEST_CASE ("list of commands is correct", "[commands]") "remove", "search", "update", + "x-update-baseline", "upgrade", "use", "version", diff --git a/src/vcpkg-test/optional.cpp b/src/vcpkg-test/optional.cpp index f18efb359..1a212b489 100644 --- a/src/vcpkg-test/optional.cpp +++ b/src/vcpkg-test/optional.cpp @@ -119,3 +119,127 @@ TEST_CASE ("common_projection", "[optional]") input.push_back(1729); CHECK(!common_projection(input, identity_projection{}).has_value()); } + +TEST_CASE ("operator==/operator!=", "[optional]") +{ + using vcpkg::Optional; + using vcpkg::StringLiteral; + + SECTION ("same type - opt == opt") + { + Optional s1; + Optional s2; + + // none == none + CHECK(s1 == s2); + CHECK_FALSE(s1 != s2); + CHECK(s2 == s1); + CHECK_FALSE(s2 != s1); + + // some("") != none + s1 = ""; + CHECK_FALSE(s1 == s2); + CHECK(s1 != s2); + CHECK_FALSE(s2 == s1); + CHECK(s2 != s1); + + // some("") == some("") + s2 = ""; + CHECK(s1 == s2); + CHECK_FALSE(s1 != s2); + CHECK(s2 == s1); + CHECK_FALSE(s2 != s1); + + // some("hi") != some("") + s1 = "hi"; + CHECK_FALSE(s1 == s2); + CHECK(s1 != s2); + CHECK_FALSE(s2 == s1); + CHECK(s2 != s1); + }; + + SECTION ("same type - opt == raw") + { + Optional opt_string; + std::string string; + + // none != "" + CHECK_FALSE(opt_string == string); + CHECK(opt_string != string); + CHECK_FALSE(string == opt_string); + CHECK(string != opt_string); + + // some("") == "" + opt_string = ""; + CHECK(opt_string == string); + CHECK_FALSE(opt_string != string); + CHECK(string == opt_string); + CHECK_FALSE(string != opt_string); + + // some("hi") != "" + opt_string = "hi"; + CHECK_FALSE(opt_string == string); + CHECK(opt_string != string); + CHECK_FALSE(string == opt_string); + CHECK(string != opt_string); + }; + + SECTION ("different types - opt == opt") + { + Optional opt_string; + Optional opt_literal; + + // none == none + CHECK(opt_string == opt_literal); + CHECK_FALSE(opt_string != opt_literal); + CHECK(opt_literal == opt_string); + CHECK_FALSE(opt_literal != opt_string); + + // some("") != none + opt_string = ""; + CHECK_FALSE(opt_string == opt_literal); + CHECK(opt_string != opt_literal); + CHECK_FALSE(opt_literal == opt_string); + CHECK(opt_literal != opt_string); + + // some("") == some("") + opt_literal = ""; + CHECK(opt_string == opt_literal); + CHECK_FALSE(opt_string != opt_literal); + CHECK(opt_literal == opt_string); + CHECK_FALSE(opt_literal != opt_string); + + // some("hi") != some("") + opt_string = "hi"; + CHECK_FALSE(opt_string == opt_literal); + CHECK(opt_string != opt_literal); + CHECK_FALSE(opt_literal == opt_string); + CHECK(opt_literal != opt_string); + }; + + SECTION ("different types - opt == raw") + { + Optional opt_string; + StringLiteral literal = ""; + + // none != "" + CHECK_FALSE(opt_string == literal); + CHECK(opt_string != literal); + CHECK_FALSE(literal == opt_string); + CHECK(literal != opt_string); + + // some("") == "" + opt_string = ""; + CHECK(opt_string == literal); + CHECK_FALSE(opt_string != literal); + CHECK(literal == opt_string); + CHECK_FALSE(literal != opt_string); + + // some("hi") != "" + opt_string = "hi"; + CHECK_FALSE(opt_string == literal); + CHECK(opt_string != literal); + CHECK_FALSE(literal == opt_string); + CHECK(literal != opt_string); + }; +} diff --git a/src/vcpkg/commands.add.cpp b/src/vcpkg/commands.add.cpp index dcda0f814..b2421ef77 100644 --- a/src/vcpkg/commands.add.cpp +++ b/src/vcpkg/commands.add.cpp @@ -99,8 +99,7 @@ namespace vcpkg::Commands specs.push_back(std::move(value)); } - const auto& manifest_path = paths.get_manifest_path().value_or_exit(VCPKG_LINE_INFO); - auto maybe_manifest_scf = SourceControlFile::parse_manifest_object(manifest_path, *manifest); + auto maybe_manifest_scf = SourceControlFile::parse_manifest_object(manifest->path, manifest->manifest); if (!maybe_manifest_scf) { print_error_message(maybe_manifest_scf.error()); @@ -133,7 +132,7 @@ namespace vcpkg::Commands } paths.get_filesystem().write_contents( - manifest_path, Json::stringify(serialize_manifest(manifest_scf), {}), VCPKG_LINE_INFO); + manifest->path, Json::stringify(serialize_manifest(manifest_scf), {}), VCPKG_LINE_INFO); msg::println(msgAddPortSucceded); auto metrics = LockGuardPtr(g_metrics); diff --git a/src/vcpkg/commands.cpp b/src/vcpkg/commands.cpp index 5ab13a5ba..56145fbbd 100644 --- a/src/vcpkg/commands.cpp +++ b/src/vcpkg/commands.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -103,6 +104,7 @@ namespace vcpkg::Commands static const RegenerateCommand regenerate{}; static const SearchCommand search{}; static const Update::UpdateCommand update{}; + static const UpdateBaselineCommand update_baseline{}; static const UseCommand use{}; static const X_VSInstances::VSInstancesCommand vsinstances{}; static const ZCeCommand ce{}; @@ -126,6 +128,7 @@ namespace vcpkg::Commands {"portsdiff", &portsdiff}, {"search", &search}, {"update", &update}, + {"x-update-baseline", &update_baseline}, {"use", &use}, {"x-add-version", &add_version}, {"x-ci-clean", &ciclean}, diff --git a/src/vcpkg/commands.update-baseline.cpp b/src/vcpkg/commands.update-baseline.cpp new file mode 100644 index 000000000..85c69bdd7 --- /dev/null +++ b/src/vcpkg/commands.update-baseline.cpp @@ -0,0 +1,167 @@ +#include +#include + +#include +#include +#include +#include + +namespace +{ + using namespace vcpkg; + + DECLARE_AND_REGISTER_MESSAGE(UpdateBaselineNoConfiguration, + (), + "", + "neither `vcpkg.json` nor `vcpkg-configuration.json` exist to update."); + + DECLARE_AND_REGISTER_MESSAGE(UpdateBaselineNoExistingBuiltinBaseline, + (msg::option), + "", + "the manifest file currently does not contain a `builtin-baseline` field; in order to " + "add one, pass the --{option} switch."); + DECLARE_AND_REGISTER_MESSAGE( + UpdateBaselineAddBaselineNoManifest, + (msg::option), + "", + "the --{option} switch was passed, but there is no manifest file to add a `builtin-baseline` field to."); + + DECLARE_AND_REGISTER_MESSAGE(UpdateBaselineUpdatedBaseline, + (msg::url, msg::old_value, msg::new_value), + "example of {old_value}, {new_value} is '5507daa796359fe8d45418e694328e878ac2b82f'", + "updated registry '{url}': baseline '{old_value}' -> '{new_value}'"); + DECLARE_AND_REGISTER_MESSAGE(UpdateBaselineNoUpdate, + (msg::url, msg::value), + "example of {value} is '5507daa796359fe8d45418e694328e878ac2b82f'", + "registry '{url}' not updated: '{value}'"); +} + +namespace vcpkg::Commands +{ + static constexpr StringLiteral OPTION_ADD_INITIAL_BASELINE = "add-initial-baseline"; + static constexpr StringLiteral OPTION_DRY_RUN = "dry-run"; + + static constexpr CommandSwitch switches[] = { + {OPTION_ADD_INITIAL_BASELINE, "add a `builtin-baseline` to a vcpkg.json that doesn't already have it"}, + {OPTION_DRY_RUN, "Print out plan without execution"}, + }; + + static const CommandStructure COMMAND_STRUCTURE{ + create_example_string("x-update-baseline"), + 0, + 0, + {switches}, + }; + + static void update_baseline_in_config(const VcpkgPaths& paths, RegistryConfig& reg) + { + auto url = reg.pretty_location(); + auto new_baseline_res = reg.get_latest_baseline(paths); + + if (auto new_baseline = new_baseline_res.get()) + { + if (*new_baseline != reg.baseline) + { + msg::println(msgUpdateBaselineUpdatedBaseline, + msg::url = url, + msg::old_value = reg.baseline.value_or(""), + msg::new_value = new_baseline->value_or("")); + reg.baseline = std::move(*new_baseline); + } + // new_baseline == reg.baseline + else + { + msg::println(msgUpdateBaselineNoUpdate, msg::url = url, msg::value = reg.baseline.value_or("")); + } + + return; + } + + // this isn't an error, since we want to continue attempting to update baselines + msg::print_warning( + msg::format(msgUpdateBaselineNoUpdate, msg::url = url, msg::value = reg.baseline.value_or("")) + .appendnl() + .append(new_baseline_res.error())); + } + + void UpdateBaselineCommand::perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) const + { + auto options = args.parse_arguments(COMMAND_STRUCTURE); + + const bool add_builtin_baseline = Util::Sets::contains(options.switches, OPTION_ADD_INITIAL_BASELINE); + const bool dry_run = Util::Sets::contains(options.switches, OPTION_DRY_RUN); + + auto configuration = paths.get_configuration(); + const bool has_manifest = paths.get_manifest().has_value(); + auto manifest = has_manifest ? *paths.get_manifest().get() : ManifestAndPath{}; + + if (configuration.source == ConfigurationSource::None && !has_manifest) + { + msg::print_warning(msgUpdateBaselineNoConfiguration); + Checks::exit_success(VCPKG_LINE_INFO); + } + + bool has_builtin_baseline = manifest.manifest.contains("builtin-baseline"); + + if (add_builtin_baseline && !has_manifest) + { + Checks::msg_exit_with_error( + VCPKG_LINE_INFO, msgUpdateBaselineAddBaselineNoManifest, msg::option = OPTION_ADD_INITIAL_BASELINE); + } + if (!has_builtin_baseline && !add_builtin_baseline && configuration.source == ConfigurationSource::None) + { + msg::print_warning(msgUpdateBaselineNoExistingBuiltinBaseline, msg::option = OPTION_ADD_INITIAL_BASELINE); + Checks::exit_success(VCPKG_LINE_INFO); + } + + if (has_builtin_baseline || add_builtin_baseline) + { + // remove default_reg, since that's filled in with the builtin-baseline + configuration.config.default_reg = nullopt; + + RegistryConfig synthesized_registry; + synthesized_registry.kind = "builtin"; + if (auto p = manifest.manifest.get("builtin-baseline")) + { + synthesized_registry.baseline = p->string().to_string(); + } + + update_baseline_in_config(paths, synthesized_registry); + + if (auto p = synthesized_registry.baseline.get()) + { + manifest.manifest.insert_or_replace("builtin-baseline", std::move(*p)); + } + } + + if (auto default_reg = configuration.config.default_reg.get()) + { + update_baseline_in_config(paths, *default_reg); + } + + for (auto& reg : configuration.config.registries) + { + update_baseline_in_config(paths, reg); + } + + if (configuration.source == ConfigurationSource::ManifestFile) + { + manifest.manifest.insert_or_replace("vcpkg-configuration", configuration.config.serialize()); + } + + if (!dry_run && configuration.source == ConfigurationSource::VcpkgConfigurationFile) + { + paths.get_filesystem().write_contents(configuration.directory / "vcpkg-configuration.json", + Json::stringify(configuration.config.serialize(), {}), + VCPKG_LINE_INFO); + } + + if (!dry_run && has_manifest) + { + paths.get_filesystem().write_contents( + manifest.path, Json::stringify(manifest.manifest, {}), VCPKG_LINE_INFO); + } + + Checks::exit_success(VCPKG_LINE_INFO); + } +} diff --git a/src/vcpkg/configuration.cpp b/src/vcpkg/configuration.cpp index e97b03635..839730fe8 100644 --- a/src/vcpkg/configuration.cpp +++ b/src/vcpkg/configuration.cpp @@ -10,6 +10,15 @@ namespace { using namespace vcpkg; + DECLARE_AND_REGISTER_MESSAGE(UpdateBaselineRemoteGitError, + (msg::url), + "", + "git failed to fetch remote repository '{url}'"); + DECLARE_AND_REGISTER_MESSAGE(UpdateBaselineLocalGitError, + (msg::path), + "", + "git failed to parse HEAD for the local vcpkg registry at '{path}'"); + struct RegistryConfigDeserializer : Json::IDeserializer { constexpr static StringLiteral KIND = "kind"; @@ -535,6 +544,77 @@ namespace namespace vcpkg { + static ExpectedL> get_baseline_from_git_repo(const VcpkgPaths& paths, StringView url) + { + auto res = paths.git_fetch_from_remote_registry(url, "HEAD"); + if (auto p = res.get()) + { + return Optional(std::move(*p)); + } + else + { + return msg::format(msgUpdateBaselineRemoteGitError, msg::url = url) + .appendnl() + .append_raw(Strings::trim(res.error())); + } + } + + ExpectedL> RegistryConfig::get_latest_baseline(const VcpkgPaths& paths) const + { + if (kind == RegistryConfigDeserializer::KIND_GIT) + { + return get_baseline_from_git_repo(paths, repo.value_or_exit(VCPKG_LINE_INFO)); + } + else if (kind == RegistryConfigDeserializer::KIND_BUILTIN) + { + if (paths.use_git_default_registry()) + { + return get_baseline_from_git_repo(paths, builtin_registry_git_url()); + } + else + { + // use the vcpkg git repository sha from the user's machine + auto res = paths.get_current_git_sha(); + if (auto p = res.get()) + { + return Optional(std::move(*p)); + } + else + { + return msg::format(msgUpdateBaselineLocalGitError, msg::path = paths.root) + .appendnl() + .append_raw(Strings::trim(res.error())); + } + } + } + else + { + return baseline; + } + } + + StringView RegistryConfig::pretty_location() const + { + if (kind == RegistryConfigDeserializer::KIND_BUILTIN) + { + return builtin_registry_git_url(); + } + if (kind == RegistryConfigDeserializer::KIND_FILESYSTEM) + { + return path.value_or_exit(VCPKG_LINE_INFO); + } + if (kind == RegistryConfigDeserializer::KIND_GIT) + { + return repo.value_or_exit(VCPKG_LINE_INFO); + } + if (kind == RegistryConfigDeserializer::KIND_ARTIFACT) + { + return location.value_or_exit(VCPKG_LINE_INFO); + } + + Checks::unreachable(VCPKG_LINE_INFO); + } + View Configuration::known_fields() { static constexpr StringView known_fields[]{ diff --git a/src/vcpkg/install.cpp b/src/vcpkg/install.cpp index 9a3dd1dd6..3cb732f01 100644 --- a/src/vcpkg/install.cpp +++ b/src/vcpkg/install.cpp @@ -927,7 +927,7 @@ namespace vcpkg::Install LockGuardPtr(g_metrics)->track_property("install_manifest_mode", paths.manifest_mode_enabled()); - if (paths.manifest_mode_enabled()) + if (auto p = paths.get_manifest().get()) { bool failure = false; if (!args.command_arguments.empty()) @@ -948,7 +948,7 @@ namespace vcpkg::Install } if (failure) { - msg::println(msgUsingManifestAt, msg::path = paths.get_manifest_path().value_or_exit(VCPKG_LINE_INFO)); + msg::println(msgUsingManifestAt, msg::path = p->path); print2("\n"); print_usage(MANIFEST_COMMAND_STRUCTURE); Checks::exit_fail(VCPKG_LINE_INFO); @@ -1018,8 +1018,7 @@ namespace vcpkg::Install LockGuardPtr(g_metrics)->track_property("x-write-nuget-packages-config", "defined"); pkgsconfig = Path(it_pkgsconfig->second); } - const auto& manifest_path = paths.get_manifest_path().value_or_exit(VCPKG_LINE_INFO); - auto maybe_manifest_scf = SourceControlFile::parse_manifest_object(manifest_path, *manifest); + auto maybe_manifest_scf = SourceControlFile::parse_manifest_object(manifest->path, manifest->manifest); if (!maybe_manifest_scf) { print_error_message(maybe_manifest_scf.error()); @@ -1030,7 +1029,7 @@ namespace vcpkg::Install auto& manifest_scf = *maybe_manifest_scf.value_or_exit(VCPKG_LINE_INFO); if (auto maybe_error = manifest_scf.check_against_feature_flags( - manifest_path, paths.get_feature_flags(), paths.get_registry_set().is_default_builtin_registry())) + manifest->path, paths.get_feature_flags(), paths.get_registry_set().is_default_builtin_registry())) { Checks::exit_with_message(VCPKG_LINE_INFO, maybe_error.value_or_exit(VCPKG_LINE_INFO)); } @@ -1096,7 +1095,7 @@ namespace vcpkg::Install std::vector extended_overlay_ports; extended_overlay_ports.reserve(args.overlay_ports.size() + 2); - extended_overlay_ports.push_back(manifest_path.parent_path().to_string()); + extended_overlay_ports.push_back(manifest->path.parent_path().to_string()); Util::Vectors::append(&extended_overlay_ports, args.overlay_ports); if (paths.get_registry_set().is_default_builtin_registry() && !paths.use_git_default_registry()) { diff --git a/src/vcpkg/registries.cpp b/src/vcpkg/registries.cpp index 829f6e3d9..4b04a6bb3 100644 --- a/src/vcpkg/registries.cpp +++ b/src/vcpkg/registries.cpp @@ -1246,7 +1246,7 @@ namespace vcpkg if (paths.use_git_default_registry()) { return std::make_unique( - paths, "https://github.com/Microsoft/vcpkg", "HEAD", std::move(baseline)); + paths, builtin_registry_git_url().to_string(), "HEAD", std::move(baseline)); } else { diff --git a/src/vcpkg/vcpkgpaths.cpp b/src/vcpkg/vcpkgpaths.cpp index f4f1c16c1..00ce70510 100644 --- a/src/vcpkg/vcpkgpaths.cpp +++ b/src/vcpkg/vcpkgpaths.cpp @@ -60,7 +60,7 @@ namespace namespace vcpkg { - static std::pair load_manifest(const Filesystem& fs, const Path& manifest_dir) + static ManifestAndPath load_manifest(const Filesystem& fs, const Path& manifest_dir) { std::error_code ec; auto manifest_path = manifest_dir / "vcpkg.json"; @@ -86,15 +86,15 @@ namespace vcpkg ": Manifest files must have a top-level object\n"); Checks::exit_fail(VCPKG_LINE_INFO); } - return {std::move(manifest_value.first.object()), std::move(manifest_value.second)}; + return {std::move(manifest_value.first.object()), std::move(manifest_path)}; } - static Optional config_from_manifest( - const Path& manifest_path, const Optional>& manifest_doc) + static Optional config_from_manifest(const Path& manifest_path, + const Optional& manifest_doc) { if (auto manifest = manifest_doc.get()) { - return parse_manifest_configuration(manifest_path, manifest->first).value_or_exit(VCPKG_LINE_INFO); + return parse_manifest_configuration(manifest_path, manifest->manifest).value_or_exit(VCPKG_LINE_INFO); } return nullopt; } @@ -131,13 +131,13 @@ namespace vcpkg return parsed_config_opt; } - static Configuration merge_validate_configs(Optional&& manifest_data, - const Path& manifest_dir, - Optional&& config_data, - const Path& config_dir, - const VcpkgPaths& paths) + static ConfigurationAndSource merge_validate_configs(Optional&& manifest_data, + const Path& manifest_dir, + Optional&& config_data, + const Path& config_dir, + const VcpkgPaths& paths) { - Configuration ret; + ConfigurationAndSource ret; if (auto manifest = manifest_data.get()) { @@ -169,7 +169,7 @@ namespace vcpkg Checks::exit_fail(VCPKG_LINE_INFO); } - ret = std::move(*config); + ret = ConfigurationAndSource{std::move(*config), config_dir, ConfigurationSource::ManifestFile}; } } @@ -177,7 +177,7 @@ namespace vcpkg { config->validate_as_active(); - ret = std::move(*config); + ret = ConfigurationAndSource{std::move(*config), config_dir, ConfigurationSource::VcpkgConfigurationFile}; } if (auto manifest = manifest_data.get()) @@ -195,7 +195,7 @@ namespace vcpkg paths.get_current_git_sha_baseline_message()); } - if (ret.default_reg) + if (ret.config.default_reg) { print2(Color::warning, "warning: attempting to set builtin-baseline in vcpkg.json while overriding the " @@ -204,7 +204,7 @@ namespace vcpkg } else { - auto& default_reg = ret.default_reg.emplace(); + auto& default_reg = ret.config.default_reg.emplace(); default_reg.kind = "builtin"; default_reg.baseline = std::move(*p_baseline); } @@ -436,6 +436,7 @@ namespace vcpkg VcpkgPathsImpl(Filesystem& fs, const VcpkgCmdArguments& args, const Path& root, const Path& original_cwd) : VcpkgPathsImplStage1(fs, args, root, original_cwd) , m_config_dir(m_manifest_dir.empty() ? root : m_manifest_dir) + , m_has_configuration_file(fs.exists(m_config_dir / "vcpkg-configuration.json", VCPKG_LINE_INFO)) , m_manifest_path(m_manifest_dir.empty() ? Path{} : m_manifest_dir / "vcpkg.json") , m_registries_work_tree_dir(m_cache_root / "git") , m_registries_dot_git_dir(m_cache_root / "git" / ".git") @@ -501,6 +502,7 @@ namespace vcpkg } const Path m_config_dir; + const bool m_has_configuration_file; const Path m_manifest_path; const Path m_registries_work_tree_dir; const Path m_registries_dot_git_dir; @@ -513,8 +515,8 @@ namespace vcpkg std::unique_ptr file_lock_handle; - Optional> m_manifest_doc; - Configuration m_config; + Optional m_manifest_doc; + ConfigurationAndSource m_config; std::unique_ptr m_registry_set; }; } @@ -662,7 +664,7 @@ namespace vcpkg m_pimpl->m_config_dir, *this); - m_pimpl->m_registry_set = m_pimpl->m_config.instantiate_registry_set(*this, m_pimpl->m_config_dir); + m_pimpl->m_registry_set = m_pimpl->m_config.instantiate_registry_set(*this); } // metrics from configuration @@ -1275,29 +1277,17 @@ namespace vcpkg } } - Optional VcpkgPaths::get_manifest() const + Optional VcpkgPaths::get_manifest() const { if (auto p = m_pimpl->m_manifest_doc.get()) { - return p->first; - } - else - { - return nullopt; - } - } - Optional VcpkgPaths::get_manifest_path() const - { - if (m_pimpl->m_manifest_doc) - { - return m_pimpl->m_manifest_path; - } - else - { - return nullopt; + return *p; } + return nullopt; } + const ConfigurationAndSource& VcpkgPaths::get_configuration() const { return m_pimpl->m_config; } + const RegistrySet& VcpkgPaths::get_registry_set() const { Checks::check_exit(VCPKG_LINE_INFO, m_pimpl->m_registry_set != nullptr);