diff --git a/toolsrc/include/tests.utils.h b/toolsrc/include/tests.utils.h index 485b8c2940..0c0add7abf 100644 --- a/toolsrc/include/tests.utils.h +++ b/toolsrc/include/tests.utils.h @@ -23,4 +23,18 @@ std::unique_ptr make_status_pgh(const char* name, std::unique_ptr make_status_feature_pgh(const char* name, const char* feature, const char* depends = "", - const char* triplet = "x86-windows"); \ No newline at end of file + const char* triplet = "x86-windows"); + +template +T&& unwrap(vcpkg::ExpectedT&& p) +{ + Assert::IsTrue(p.has_value()); + return std::move(*p.get()); +} + +template +T&& unwrap(vcpkg::Optional&& opt) +{ + Assert::IsTrue(opt.has_value()); + return std::move(*opt.get()); +} diff --git a/toolsrc/include/vcpkg/commands.h b/toolsrc/include/vcpkg/commands.h index 74fd80c030..b852a973e0 100644 --- a/toolsrc/include/vcpkg/commands.h +++ b/toolsrc/include/vcpkg/commands.h @@ -39,6 +39,12 @@ namespace vcpkg::Commands void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths); } + namespace Upgrade + { + extern const CommandStructure COMMAND_STRUCTURE; + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet); + } + namespace Edit { extern const CommandStructure COMMAND_STRUCTURE; diff --git a/toolsrc/include/vcpkg/dependencies.h b/toolsrc/include/vcpkg/dependencies.h index 5411ee1668..f1249dc884 100644 --- a/toolsrc/include/vcpkg/dependencies.h +++ b/toolsrc/include/vcpkg/dependencies.h @@ -92,11 +92,11 @@ namespace vcpkg::Dependencies struct AnyAction { - AnyAction(InstallPlanAction&& iplan) : install_plan(std::move(iplan)) {} - AnyAction(RemovePlanAction&& rplan) : remove_plan(std::move(rplan)) {} + AnyAction(InstallPlanAction&& iplan) : install_action(std::move(iplan)) {} + AnyAction(RemovePlanAction&& rplan) : remove_action(std::move(rplan)) {} - Optional install_plan; - Optional remove_plan; + Optional install_action; + Optional remove_action; const PackageSpec& spec() const; }; @@ -123,22 +123,44 @@ namespace vcpkg::Dependencies struct PortFileProvider { - virtual const SourceControlFile& get_control_file(const std::string& spec) const = 0; + virtual Optional get_control_file(const std::string& src_name) const = 0; }; - struct MapPortFile : Util::ResourceBase, PortFileProvider + struct MapPortFileProvider : Util::ResourceBase, PortFileProvider { + explicit MapPortFileProvider(const std::unordered_map& map); + Optional get_control_file(const std::string& src_name) const override; + + private: const std::unordered_map& ports; - explicit MapPortFile(const std::unordered_map& map); - const SourceControlFile& get_control_file(const std::string& spec) const override; }; - struct PathsPortFile : Util::ResourceBase, PortFileProvider + struct PathsPortFileProvider : Util::ResourceBase, PortFileProvider { + explicit PathsPortFileProvider(const VcpkgPaths& paths); + Optional get_control_file(const std::string& src_name) const override; + + private: const VcpkgPaths& ports; mutable std::unordered_map cache; - explicit PathsPortFile(const VcpkgPaths& paths); - const SourceControlFile& get_control_file(const std::string& spec) const override; + }; + + struct ClusterGraph; + struct GraphPlan; + + struct PackageGraph + { + PackageGraph(const PortFileProvider& provider, const StatusParagraphs& status_db); + ~PackageGraph(); + + void install(const FeatureSpec& spec); + void upgrade(const PackageSpec& spec); + + std::vector serialize() const; + + private: + std::unique_ptr m_graph_plan; + std::unique_ptr m_graph; }; std::vector create_install_plan(const PortFileProvider& port_file_provider, @@ -155,4 +177,10 @@ namespace vcpkg::Dependencies std::vector create_feature_install_plan(const std::unordered_map& map, const std::vector& specs, const StatusParagraphs& status_db); + + std::vector create_feature_install_plan(const PortFileProvider& port_file_provider, + const std::vector& specs, + const StatusParagraphs& status_db); + + void print_plan(const std::vector& action_plan, const bool is_recursive = true); } diff --git a/toolsrc/include/vcpkg/paragraphs.h b/toolsrc/include/vcpkg/paragraphs.h index c8dbea6463..e2c7f2d99d 100644 --- a/toolsrc/include/vcpkg/paragraphs.h +++ b/toolsrc/include/vcpkg/paragraphs.h @@ -32,7 +32,4 @@ namespace vcpkg::Paragraphs std::vector> load_all_ports(const Files::Filesystem& fs, const fs::path& ports_dir); - - std::map load_all_port_names_and_versions(const Files::Filesystem& fs, - const fs::path& ports_dir); } diff --git a/toolsrc/include/vcpkg/update.h b/toolsrc/include/vcpkg/update.h index 7587b9eb2f..b85f7b2b31 100644 --- a/toolsrc/include/vcpkg/update.h +++ b/toolsrc/include/vcpkg/update.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -16,7 +17,8 @@ namespace vcpkg::Update VersionDiff version_diff; }; - std::vector find_outdated_packages(const std::map& src_names_to_versions, + std::vector find_outdated_packages(const Dependencies::PortFileProvider& provider, const StatusParagraphs& status_db); + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths); -} \ No newline at end of file +} diff --git a/toolsrc/src/commands.upgrade.cpp b/toolsrc/src/commands.upgrade.cpp new file mode 100644 index 0000000000..7a32100424 --- /dev/null +++ b/toolsrc/src/commands.upgrade.cpp @@ -0,0 +1,79 @@ +#include "pch.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace vcpkg::Commands::Upgrade +{ + using Install::KeepGoing; + using Install::to_keep_going; + + static const std::string OPTION_NO_DRY_RUN = "--no-dry-run"; + static const std::string OPTION_KEEP_GOING = "--keep-going"; + + static const std::array INSTALL_SWITCHES = {{ + {OPTION_NO_DRY_RUN, "Actually upgrade"}, + {OPTION_KEEP_GOING, "Continue installing packages on failure"}, + }}; + + const CommandStructure COMMAND_STRUCTURE = { + Help::create_example_string("upgrade --no-dry-run"), + 0, + 0, + {INSTALL_SWITCHES, {}}, + nullptr, + }; + + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet&) + { + // input sanitization + const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE); + + const bool no_dry_run = Util::Sets::contains(options.switches, OPTION_NO_DRY_RUN); + const KeepGoing keep_going = to_keep_going(Util::Sets::contains(options.switches, OPTION_KEEP_GOING)); + + // create the plan + StatusParagraphs status_db = database_load_check(paths); + + Dependencies::PathsPortFileProvider provider(paths); + Dependencies::PackageGraph graph(provider, status_db); + + auto outdated_packages = Update::find_outdated_packages(provider, status_db); + for (auto&& outdated_package : outdated_packages) + graph.upgrade(outdated_package.spec); + + auto plan = graph.serialize(); + + if (plan.empty()) + { + System::println("All packages are up-to-date."); + Checks::exit_success(VCPKG_LINE_INFO); + } + + Dependencies::print_plan(plan, true); + + if (!no_dry_run) + { + System::println(System::Color::warning, + "If you are sure you want to rebuild the above packages, run this command with the " + "--no-dry-run option."); + Checks::exit_fail(VCPKG_LINE_INFO); + } + + const Install::InstallSummary summary = Install::perform(plan, keep_going, paths, status_db); + + System::println("\nTotal elapsed time: %s\n", summary.total_elapsed_time); + + if (keep_going == KeepGoing::YES) + { + summary.print(); + } + + Checks::exit_success(VCPKG_LINE_INFO); + } +} diff --git a/toolsrc/src/tests.plan.cpp b/toolsrc/src/tests.plan.cpp index 122a4ffef8..781588c910 100644 --- a/toolsrc/src/tests.plan.cpp +++ b/toolsrc/src/tests.plan.cpp @@ -36,7 +36,7 @@ namespace UnitTest1 std::vector vec, const Triplet& triplet = Triplet::X86_WINDOWS) { - const auto& plan = install_action->install_plan.value_or_exit(VCPKG_LINE_INFO); + const auto& plan = install_action->install_action.value_or_exit(VCPKG_LINE_INFO); const auto& feature_list = plan.feature_list; Assert::AreEqual(plan.spec.triplet().to_string().c_str(), triplet.to_string().c_str()); @@ -61,7 +61,7 @@ namespace UnitTest1 std::string pkg_name, const Triplet& triplet = Triplet::X86_WINDOWS) { - const auto& plan = remove_action->remove_plan.value_or_exit(VCPKG_LINE_INFO); + const auto& plan = remove_action->remove_action.value_or_exit(VCPKG_LINE_INFO); Assert::AreEqual(plan.spec.triplet().to_string().c_str(), triplet.to_string().c_str()); Assert::AreEqual(pkg_name.c_str(), plan.spec.name().c_str()); } @@ -98,7 +98,7 @@ namespace UnitTest1 auto spec_b = spec_map.emplace("b", "c"); auto spec_c = spec_map.emplace("c"); - Dependencies::MapPortFile map_port(spec_map.map); + Dependencies::MapPortFileProvider map_port(spec_map.map); auto install_plan = Dependencies::create_install_plan(map_port, {spec_a}, StatusParagraphs(std::move(status_paragraphs))); @@ -122,7 +122,7 @@ namespace UnitTest1 auto spec_g = spec_map.emplace("g"); auto spec_h = spec_map.emplace("h"); - Dependencies::MapPortFile map_port(spec_map.map); + Dependencies::MapPortFileProvider map_port(spec_map.map); auto install_plan = Dependencies::create_install_plan( map_port, {spec_a, spec_b, spec_c}, StatusParagraphs(std::move(status_paragraphs))); @@ -162,7 +162,7 @@ namespace UnitTest1 StatusParagraphs(std::move(status_paragraphs))); Assert::AreEqual(size_t(1), install_plan.size()); - auto p = install_plan[0].install_plan.get(); + auto p = install_plan[0].install_action.get(); Assert::IsNotNull(p); Assert::AreEqual("a", p->spec.name().c_str()); Assert::AreEqual(Dependencies::InstallPlanType::ALREADY_INSTALLED, p->plan_type); @@ -183,13 +183,13 @@ namespace UnitTest1 StatusParagraphs(std::move(status_paragraphs))); Assert::AreEqual(size_t(2), install_plan.size()); - auto p = install_plan[0].install_plan.get(); + auto p = install_plan[0].install_action.get(); Assert::IsNotNull(p); Assert::AreEqual("b", p->spec.name().c_str()); Assert::AreEqual(Dependencies::InstallPlanType::BUILD_AND_INSTALL, p->plan_type); Assert::AreEqual(Dependencies::RequestType::AUTO_SELECTED, p->request_type); - auto p2 = install_plan[1].install_plan.get(); + auto p2 = install_plan[1].install_action.get(); Assert::IsNotNull(p2); Assert::AreEqual("a", p2->spec.name().c_str()); Assert::AreEqual(Dependencies::InstallPlanType::BUILD_AND_INSTALL, p2->plan_type); @@ -215,7 +215,7 @@ namespace UnitTest1 auto spec_j = spec_map.emplace("j", "k"); auto spec_k = spec_map.emplace("k"); - Dependencies::MapPortFile map_port(spec_map.map); + Dependencies::MapPortFileProvider map_port(spec_map.map); auto install_plan = Dependencies::create_install_plan(map_port, {spec_a}, StatusParagraphs(std::move(status_paragraphs))); @@ -521,4 +521,138 @@ namespace UnitTest1 Assert::AreEqual("expat", remove_plan[2].spec.name().c_str()); } }; + + class UpgradePlanTests : public TestClass + { + TEST_METHOD(basic_upgrade_scheme) + { + std::vector> pghs; + pghs.push_back(make_status_pgh("a")); + StatusParagraphs status_db(std::move(pghs)); + + PackageSpecMap spec_map(Triplet::X86_WINDOWS); + auto spec_a = spec_map.emplace("a"); + + Dependencies::MapPortFileProvider provider(spec_map.map); + Dependencies::PackageGraph graph(provider, status_db); + + graph.upgrade(spec_a); + + auto plan = graph.serialize(); + + Assert::AreEqual(size_t(2), plan.size()); + Assert::AreEqual("a", plan[0].spec().name().c_str()); + Assert::IsTrue(plan[0].remove_action.has_value()); + Assert::AreEqual("a", plan[1].spec().name().c_str()); + Assert::IsTrue(plan[1].install_action.has_value()); + } + + TEST_METHOD(basic_upgrade_scheme_with_recurse) + { + std::vector> pghs; + pghs.push_back(make_status_pgh("a")); + pghs.push_back(make_status_pgh("b", "a")); + StatusParagraphs status_db(std::move(pghs)); + + PackageSpecMap spec_map(Triplet::X86_WINDOWS); + auto spec_a = spec_map.emplace("a"); + spec_map.emplace("b", "a"); + + Dependencies::MapPortFileProvider provider(spec_map.map); + Dependencies::PackageGraph graph(provider, status_db); + + graph.upgrade(spec_a); + + auto plan = graph.serialize(); + + Assert::AreEqual(size_t(4), plan.size()); + Assert::AreEqual("b", plan[0].spec().name().c_str()); + Assert::IsTrue(plan[0].remove_action.has_value()); + + Assert::AreEqual("a", plan[1].spec().name().c_str()); + Assert::IsTrue(plan[1].remove_action.has_value()); + + Assert::AreEqual("a", plan[2].spec().name().c_str()); + Assert::IsTrue(plan[2].install_action.has_value()); + + Assert::AreEqual("b", plan[3].spec().name().c_str()); + Assert::IsTrue(plan[3].install_action.has_value()); + } + + TEST_METHOD(basic_upgrade_scheme_with_bystander) + { + std::vector> pghs; + pghs.push_back(make_status_pgh("a")); + pghs.push_back(make_status_pgh("b")); + StatusParagraphs status_db(std::move(pghs)); + + PackageSpecMap spec_map(Triplet::X86_WINDOWS); + auto spec_a = spec_map.emplace("a"); + spec_map.emplace("b", "a"); + + Dependencies::MapPortFileProvider provider(spec_map.map); + Dependencies::PackageGraph graph(provider, status_db); + + graph.upgrade(spec_a); + + auto plan = graph.serialize(); + + Assert::AreEqual(size_t(2), plan.size()); + Assert::AreEqual("a", plan[0].spec().name().c_str()); + Assert::IsTrue(plan[0].remove_action.has_value()); + Assert::AreEqual("a", plan[1].spec().name().c_str()); + Assert::IsTrue(plan[1].install_action.has_value()); + } + + TEST_METHOD(basic_upgrade_scheme_with_new_dep) + { + std::vector> pghs; + pghs.push_back(make_status_pgh("a")); + StatusParagraphs status_db(std::move(pghs)); + + PackageSpecMap spec_map(Triplet::X86_WINDOWS); + auto spec_a = spec_map.emplace("a", "b"); + spec_map.emplace("b"); + + Dependencies::MapPortFileProvider provider(spec_map.map); + Dependencies::PackageGraph graph(provider, status_db); + + graph.upgrade(spec_a); + + auto plan = graph.serialize(); + + Assert::AreEqual(size_t(3), plan.size()); + Assert::AreEqual("a", plan[0].spec().name().c_str()); + Assert::IsTrue(plan[0].remove_action.has_value()); + Assert::AreEqual("b", plan[1].spec().name().c_str()); + Assert::IsTrue(plan[1].install_action.has_value()); + Assert::AreEqual("a", plan[2].spec().name().c_str()); + Assert::IsTrue(plan[2].install_action.has_value()); + } + + TEST_METHOD(basic_upgrade_scheme_with_features) + { + std::vector> pghs; + pghs.push_back(make_status_pgh("a")); + pghs.push_back(make_status_feature_pgh("a", "a1")); + StatusParagraphs status_db(std::move(pghs)); + + PackageSpecMap spec_map(Triplet::X86_WINDOWS); + auto spec_a = spec_map.emplace("a", "", {{"a1", ""}}); + + Dependencies::MapPortFileProvider provider(spec_map.map); + Dependencies::PackageGraph graph(provider, status_db); + + graph.upgrade(spec_a); + + auto plan = graph.serialize(); + + Assert::AreEqual(size_t(2), plan.size()); + + Assert::AreEqual("a", plan[0].spec().name().c_str()); + Assert::IsTrue(plan[0].remove_action.has_value()); + + features_check(&plan[1], "a", {"core", "a1"}); + } + }; } diff --git a/toolsrc/src/tests.update.cpp b/toolsrc/src/tests.update.cpp index 06ae797f4c..b6e487c17f 100644 --- a/toolsrc/src/tests.update.cpp +++ b/toolsrc/src/tests.update.cpp @@ -9,6 +9,8 @@ using namespace vcpkg::Update; namespace UnitTest1 { + using Pgh = std::vector>; + class UpdateTests : public TestClass { TEST_METHOD(find_outdated_packages_basic) @@ -19,10 +21,12 @@ namespace UnitTest1 StatusParagraphs status_db(std::move(status_paragraphs)); - std::map port_versions; - port_versions["a"] = VersionT("0"); + std::unordered_map map; + auto scf = unwrap(SourceControlFile::parse_control_file(Pgh{{{"Source", "a"}, {"Version", "0"}}})); + map.emplace("a", std::move(*scf)); + Dependencies::MapPortFileProvider provider(map); - auto pkgs = SortedVector(Update::find_outdated_packages(port_versions, status_db), + auto pkgs = SortedVector(Update::find_outdated_packages(provider, status_db), &OutdatedPackage::compare_by_name); Assert::AreEqual(size_t(1), pkgs.size()); @@ -41,10 +45,12 @@ namespace UnitTest1 StatusParagraphs status_db(std::move(status_paragraphs)); - std::map port_versions; - port_versions["a"] = VersionT("0"); + std::unordered_map map; + auto scf = unwrap(SourceControlFile::parse_control_file(Pgh{{{"Source", "a"}, {"Version", "0"}}})); + map.emplace("a", std::move(*scf)); + Dependencies::MapPortFileProvider provider(map); - auto pkgs = SortedVector(Update::find_outdated_packages(port_versions, status_db), + auto pkgs = SortedVector(Update::find_outdated_packages(provider, status_db), &OutdatedPackage::compare_by_name); Assert::AreEqual(size_t(1), pkgs.size()); @@ -65,10 +71,12 @@ namespace UnitTest1 StatusParagraphs status_db(std::move(status_paragraphs)); - std::map port_versions; - port_versions["a"] = VersionT("0"); + std::unordered_map map; + auto scf = unwrap(SourceControlFile::parse_control_file(Pgh{{{"Source", "a"}, {"Version", "0"}}})); + map.emplace("a", std::move(*scf)); + Dependencies::MapPortFileProvider provider(map); - auto pkgs = SortedVector(Update::find_outdated_packages(port_versions, status_db), + auto pkgs = SortedVector(Update::find_outdated_packages(provider, status_db), &OutdatedPackage::compare_by_name); Assert::AreEqual(size_t(1), pkgs.size()); @@ -84,10 +92,12 @@ namespace UnitTest1 StatusParagraphs status_db(std::move(status_paragraphs)); - std::map port_versions; - port_versions["a"] = VersionT("2"); + std::unordered_map map; + auto scf = unwrap(SourceControlFile::parse_control_file(Pgh{{{"Source", "a"}, {"Version", "2"}}})); + map.emplace("a", std::move(*scf)); + Dependencies::MapPortFileProvider provider(map); - auto pkgs = SortedVector(Update::find_outdated_packages(port_versions, status_db), + auto pkgs = SortedVector(Update::find_outdated_packages(provider, status_db), &OutdatedPackage::compare_by_name); Assert::AreEqual(size_t(0), pkgs.size()); diff --git a/toolsrc/src/vcpkg/commands.ci.cpp b/toolsrc/src/vcpkg/commands.ci.cpp index f2e984bf89..1a2f9b47f2 100644 --- a/toolsrc/src/vcpkg/commands.ci.cpp +++ b/toolsrc/src/vcpkg/commands.ci.cpp @@ -27,7 +27,7 @@ namespace vcpkg::Commands::CI const std::vector specs = PackageSpec::to_package_specs(ports, triplet); StatusParagraphs status_db = database_load_check(paths); - const auto& paths_port_file = Dependencies::PathsPortFile(paths); + const auto& paths_port_file = Dependencies::PathsPortFileProvider(paths); std::vector install_plan = Dependencies::create_install_plan(paths_port_file, specs, status_db); diff --git a/toolsrc/src/vcpkg/commands.cpp b/toolsrc/src/vcpkg/commands.cpp index 15b10c7ea3..ccf6fa7291 100644 --- a/toolsrc/src/vcpkg/commands.cpp +++ b/toolsrc/src/vcpkg/commands.cpp @@ -13,9 +13,10 @@ namespace vcpkg::Commands Span> get_available_commands_type_a() { static std::vector> t = { - PackageNameAndFunction{"install", &Install::perform_and_exit}, + {"install", &Install::perform_and_exit}, {"ci", &CI::perform_and_exit}, {"remove", &Remove::perform_and_exit}, + {"upgrade", &Upgrade::perform_and_exit}, {"build", &Build::Command::perform_and_exit}, {"env", &Env::perform_and_exit}, {"build-external", &BuildExternal::perform_and_exit}, diff --git a/toolsrc/src/vcpkg/commands.portsdiff.cpp b/toolsrc/src/vcpkg/commands.portsdiff.cpp index 6752715e4c..dba04ce5bb 100644 --- a/toolsrc/src/vcpkg/commands.portsdiff.cpp +++ b/toolsrc/src/vcpkg/commands.portsdiff.cpp @@ -98,8 +98,11 @@ namespace vcpkg::Commands::PortsDiff ".vcpkg-root", git_exe.u8string()); System::cmd_execute_clean(cmd); - const std::map names_and_versions = Paragraphs::load_all_port_names_and_versions( - paths.get_filesystem(), temp_checkout_path / ports_dir_name_as_string); + const auto all_ports = + Paragraphs::load_all_ports(paths.get_filesystem(), temp_checkout_path / ports_dir_name_as_string); + std::map names_and_versions; + for (auto&& port : all_ports) + names_and_versions.emplace(port->core_paragraph->name, port->core_paragraph->version); fs.remove_all(temp_checkout_path, ec); return names_and_versions; } diff --git a/toolsrc/src/vcpkg/dependencies.cpp b/toolsrc/src/vcpkg/dependencies.cpp index 0a1f798347..5148a10f5a 100644 --- a/toolsrc/src/vcpkg/dependencies.cpp +++ b/toolsrc/src/vcpkg/dependencies.cpp @@ -65,26 +65,19 @@ namespace vcpkg::Dependencies struct ClusterGraph : Util::MoveOnlyBase { - explicit ClusterGraph(std::unordered_map&& ports) - : m_ports(std::move(ports)) - { - } + explicit ClusterGraph(const PortFileProvider& provider) : m_provider(provider) {} Cluster& get(const PackageSpec& spec) { auto it = m_graph.find(spec); if (it == m_graph.end()) { - // Load on-demand from m_ports - auto it_ports = m_ports.find(spec.name()); - if (it_ports != m_ports.end()) - { - auto& clust = m_graph[spec]; - clust.spec = spec; - cluster_from_scf(*it_ports->second, clust); - return clust; - } - return m_graph[spec]; + // Load on-demand from m_provider + auto maybe_scf = m_provider.get_control_file(spec.name()); + auto& clust = m_graph[spec]; + clust.spec = spec; + if (auto p_scf = maybe_scf.get()) cluster_from_scf(*p_scf, clust); + return clust; } return it->second; } @@ -107,7 +100,7 @@ namespace vcpkg::Dependencies } std::unordered_map m_graph; - std::unordered_map m_ports; + const PortFileProvider& m_provider; }; std::vector AnyParagraph::dependencies(const Triplet& triplet) const @@ -226,12 +219,12 @@ namespace vcpkg::Dependencies const PackageSpec& AnyAction::spec() const { - if (const auto p = install_plan.get()) + if (const auto p = install_action.get()) { return p->spec; } - if (const auto p = remove_plan.get()) + if (const auto p = remove_action.get()) { return p->spec; } @@ -269,21 +262,20 @@ namespace vcpkg::Dependencies return left->spec.name() < right->spec.name(); } - MapPortFile::MapPortFile(const std::unordered_map& map) : ports(map) {} + MapPortFileProvider::MapPortFileProvider(const std::unordered_map& map) : ports(map) + { + } - const SourceControlFile& MapPortFile::get_control_file(const std::string& spec) const + Optional MapPortFileProvider::get_control_file(const std::string& spec) const { auto scf = ports.find(spec); - if (scf == ports.end()) - { - Checks::exit_fail(VCPKG_LINE_INFO); - } + if (scf == ports.end()) return nullopt; return scf->second; } - PathsPortFile::PathsPortFile(const VcpkgPaths& paths) : ports(paths) {} + PathsPortFileProvider::PathsPortFileProvider(const VcpkgPaths& paths) : ports(paths) {} - const SourceControlFile& PathsPortFile::get_control_file(const std::string& spec) const + Optional PathsPortFileProvider::get_control_file(const std::string& spec) const { auto cache_it = cache.find(spec); if (cache_it != cache.end()) @@ -298,56 +290,34 @@ namespace vcpkg::Dependencies auto it = cache.emplace(spec, std::move(*scf->get())); return it.first->second; } - print_error_message(source_control_file.error()); - Checks::exit_fail(VCPKG_LINE_INFO); + return nullopt; } std::vector create_install_plan(const PortFileProvider& port_file_provider, const std::vector& specs, const StatusParagraphs& status_db) { - struct InstallAdjacencyProvider final : Graphs::AdjacencyProvider + auto fspecs = Util::fmap(specs, [](const PackageSpec& spec) { return FeatureSpec(spec, ""); }); + auto plan = create_feature_install_plan(port_file_provider, fspecs, status_db); + + std::vector ret; + ret.reserve(plan.size()); + + for (auto&& action : plan) { - const PortFileProvider& port_file_provider; - const StatusParagraphs& status_db; - const std::unordered_set& specs_as_set; - - InstallAdjacencyProvider(const PortFileProvider& port_file_provider, - const StatusParagraphs& s, - const std::unordered_set& specs_as_set) - : port_file_provider(port_file_provider), status_db(s), specs_as_set(specs_as_set) + if (auto p_install = action.install_action.get()) { + ret.push_back(std::move(*p_install)); } - - std::vector adjacency_list(const InstallPlanAction& plan) const override + else { - if (plan.any_paragraph.status_paragraph.get()) return std::vector{}; - return plan.any_paragraph.dependencies(plan.spec.triplet()); + Checks::exit_with_message(VCPKG_LINE_INFO, + "The installation plan requires feature packages support. Please re-run the " + "command with --featurepackages."); } + } - InstallPlanAction load_vertex_data(const PackageSpec& spec) const override - { - const RequestType request_type = specs_as_set.find(spec) != specs_as_set.end() - ? RequestType::USER_REQUESTED - : RequestType::AUTO_SELECTED; - auto it = status_db.find_installed(spec); - if (it != status_db.end()) return InstallPlanAction{spec, {*it->get(), nullopt, nullopt}, request_type}; - return InstallPlanAction{ - spec, - {nullopt, nullopt, *port_file_provider.get_control_file(spec.name()).core_paragraph}, - request_type}; - } - }; - - const std::unordered_set specs_as_set(specs.cbegin(), specs.cend()); - std::vector toposort = - Graphs::topological_sort(specs, InstallAdjacencyProvider{port_file_provider, status_db, specs_as_set}); - Util::erase_remove_if(toposort, [](const InstallPlanAction& plan) { - return plan.request_type == RequestType::AUTO_SELECTED && - plan.plan_type == InstallPlanType::ALREADY_INSTALLED; - }); - - return toposort; + return ret; } std::vector create_remove_plan(const std::vector& specs, @@ -461,11 +431,12 @@ namespace vcpkg::Dependencies SUCCESS, }; - MarkPlusResult mark_plus(const std::string& feature, - Cluster& cluster, - ClusterGraph& pkg_to_cluster, - GraphPlan& graph_plan); - void mark_minus(Cluster& cluster, ClusterGraph& pkg_to_cluster, GraphPlan& graph_plan); + static MarkPlusResult mark_plus(const std::string& feature, + Cluster& cluster, + ClusterGraph& pkg_to_cluster, + GraphPlan& graph_plan); + + static void mark_minus(Cluster& cluster, ClusterGraph& pkg_to_cluster, GraphPlan& graph_plan); MarkPlusResult mark_plus(const std::string& feature, Cluster& cluster, ClusterGraph& graph, GraphPlan& graph_plan) { @@ -557,110 +528,80 @@ namespace vcpkg::Dependencies } } - static ClusterGraph create_feature_install_graph(const std::unordered_map& map, - const StatusParagraphs& status_db) + std::vector create_feature_install_plan(const PortFileProvider& provider, + const std::vector& specs, + const StatusParagraphs& status_db) { - std::unordered_map ptr_map; - for (auto&& p : map) - ptr_map.emplace(p.first, &p.second); - ClusterGraph graph(std::move(ptr_map)); + PackageGraph pgraph(provider, status_db); + for (auto&& spec : specs) + pgraph.install(spec); - auto installed_ports = get_installed_ports(status_db); - - for (auto&& status_paragraph : installed_ports) - { - Cluster& cluster = graph.get(status_paragraph->package.spec); - - cluster.transient_uninstalled = false; - - cluster.status_paragraphs.emplace_back(status_paragraph); - - auto& status_paragraph_feature = status_paragraph->package.feature; - // In this case, empty string indicates the "core" paragraph for a package. - if (status_paragraph_feature.empty()) - { - cluster.original_features.insert("core"); - } - else - { - cluster.original_features.insert(status_paragraph_feature); - } - } - - for (auto&& status_paragraph : installed_ports) - { - auto& spec = status_paragraph->package.spec; - auto& status_paragraph_feature = status_paragraph->package.feature; - auto reverse_edges = FeatureSpec::from_strings_and_triplet(status_paragraph->package.depends, - status_paragraph->package.spec.triplet()); - - for (auto&& dependency : reverse_edges) - { - auto& dep_cluster = graph.get(dependency.spec()); - - auto depends_name = dependency.feature(); - if (depends_name.empty()) depends_name = "core"; - - auto& target_node = dep_cluster.edges[depends_name]; - target_node.remove_edges.emplace_back(FeatureSpec{spec, status_paragraph_feature}); - } - } - return graph; + return pgraph.serialize(); } std::vector create_feature_install_plan(const std::unordered_map& map, const std::vector& specs, const StatusParagraphs& status_db) { - ClusterGraph graph = create_feature_install_graph(map, status_db); + MapPortFileProvider provider(map); + return create_feature_install_plan(provider, specs, status_db); + } - GraphPlan graph_plan; - for (auto&& spec : specs) + void PackageGraph::install(const FeatureSpec& spec) + { + Cluster& spec_cluster = m_graph->get(spec.spec()); + spec_cluster.request_type = RequestType::USER_REQUESTED; + if (spec.feature() == "*") { - Cluster& spec_cluster = graph.get(spec.spec()); - spec_cluster.request_type = RequestType::USER_REQUESTED; - if (spec.feature() == "*") + if (auto p_scf = spec_cluster.source_control_file.value_or(nullptr)) { - if (auto p_scf = spec_cluster.source_control_file.value_or(nullptr)) + for (auto&& feature : p_scf->feature_paragraphs) { - for (auto&& feature : p_scf->feature_paragraphs) - { - auto res = mark_plus(feature->name, spec_cluster, graph, graph_plan); - - Checks::check_exit(VCPKG_LINE_INFO, - res == MarkPlusResult::SUCCESS, - "Error: Unable to locate feature %s", - spec); - } - - auto res = mark_plus("core", spec_cluster, graph, graph_plan); + auto res = mark_plus(feature->name, spec_cluster, *m_graph, *m_graph_plan); Checks::check_exit( VCPKG_LINE_INFO, res == MarkPlusResult::SUCCESS, "Error: Unable to locate feature %s", spec); } - else - { - Checks::exit_with_message( - VCPKG_LINE_INFO, "Error: Unable to handle '*' because can't find CONTROL for %s", spec.spec()); - } - } - else - { - auto res = mark_plus(spec.feature(), spec_cluster, graph, graph_plan); + + auto res = mark_plus("core", spec_cluster, *m_graph, *m_graph_plan); Checks::check_exit( VCPKG_LINE_INFO, res == MarkPlusResult::SUCCESS, "Error: Unable to locate feature %s", spec); } + else + { + Checks::exit_with_message( + VCPKG_LINE_INFO, "Error: Unable to handle '*' because can't find CONTROL for %s", spec.spec()); + } + } + else + { + auto res = mark_plus(spec.feature(), spec_cluster, *m_graph, *m_graph_plan); - graph_plan.install_graph.add_vertex(ClusterPtr{&spec_cluster}); + Checks::check_exit( + VCPKG_LINE_INFO, res == MarkPlusResult::SUCCESS, "Error: Unable to locate feature %s", spec); } - Graphs::GraphAdjacencyProvider adjacency_remove_graph(graph_plan.remove_graph.adjacency_list()); - auto remove_vertex_list = graph_plan.remove_graph.vertex_list(); + m_graph_plan->install_graph.add_vertex(ClusterPtr{&spec_cluster}); + } + + void PackageGraph::upgrade(const PackageSpec& spec) + { + Cluster& spec_cluster = m_graph->get(spec); + spec_cluster.request_type = RequestType::USER_REQUESTED; + + mark_minus(spec_cluster, *m_graph, *m_graph_plan); + } + + std::vector PackageGraph::serialize() const + { + Graphs::GraphAdjacencyProvider adjacency_remove_graph(m_graph_plan->remove_graph.adjacency_list()); + auto remove_vertex_list = m_graph_plan->remove_graph.vertex_list(); auto remove_toposort = Graphs::topological_sort(remove_vertex_list, adjacency_remove_graph); - Graphs::GraphAdjacencyProvider adjacency_install_graph(graph_plan.install_graph.adjacency_list()); - auto insert_vertex_list = graph_plan.install_graph.vertex_list(); + Graphs::GraphAdjacencyProvider adjacency_install_graph( + m_graph_plan->install_graph.adjacency_list()); + auto insert_vertex_list = m_graph_plan->install_graph.vertex_list(); auto insert_toposort = Graphs::topological_sort(insert_vertex_list, adjacency_install_graph); std::vector plan; @@ -705,4 +646,162 @@ namespace vcpkg::Dependencies return plan; } + + static std::unique_ptr create_feature_install_graph(const PortFileProvider& map, + const StatusParagraphs& status_db) + { + std::unique_ptr graph = std::make_unique(map); + + auto installed_ports = get_installed_ports(status_db); + + for (auto&& status_paragraph : installed_ports) + { + Cluster& cluster = graph->get(status_paragraph->package.spec); + + cluster.transient_uninstalled = false; + + cluster.status_paragraphs.emplace_back(status_paragraph); + + auto& status_paragraph_feature = status_paragraph->package.feature; + // In this case, empty string indicates the "core" paragraph for a package. + if (status_paragraph_feature.empty()) + { + cluster.original_features.insert("core"); + } + else + { + cluster.original_features.insert(status_paragraph_feature); + } + } + + // Populate the graph with "remove edges", which are the reverse of the Build-Depends edges. + for (auto&& status_paragraph : installed_ports) + { + auto& spec = status_paragraph->package.spec; + auto& status_paragraph_feature = status_paragraph->package.feature; + auto reverse_edges = FeatureSpec::from_strings_and_triplet(status_paragraph->package.depends, + status_paragraph->package.spec.triplet()); + + for (auto&& dependency : reverse_edges) + { + auto& dep_cluster = graph->get(dependency.spec()); + + auto depends_name = dependency.feature(); + if (depends_name.empty()) depends_name = "core"; + + auto& target_node = dep_cluster.edges[depends_name]; + target_node.remove_edges.emplace_back(FeatureSpec{spec, status_paragraph_feature}); + } + } + return graph; + } + + PackageGraph::PackageGraph(const PortFileProvider& provider, const StatusParagraphs& status_db) + : m_graph(create_feature_install_graph(provider, status_db)), m_graph_plan(std::make_unique()) + { + } + + PackageGraph::~PackageGraph() {} + + void print_plan(const std::vector& action_plan, const bool is_recursive) + { + std::vector remove_plans; + std::vector rebuilt_plans; + std::vector only_install_plans; + std::vector new_plans; + std::vector already_installed_plans; + std::vector excluded; + + const bool has_non_user_requested_packages = Util::find_if(action_plan, [](const AnyAction& package) -> bool { + if (auto iplan = package.install_action.get()) + return iplan->request_type != RequestType::USER_REQUESTED; + else + return false; + }) != action_plan.cend(); + + for (auto&& action : action_plan) + { + if (auto install_action = action.install_action.get()) + { + // remove plans are guaranteed to come before install plans, so we know the plan will be contained if at + // all. + auto it = Util::find_if( + remove_plans, [&](const RemovePlanAction* plan) { return plan->spec == install_action->spec; }); + if (it != remove_plans.end()) + { + rebuilt_plans.emplace_back(install_action); + } + else + { + switch (install_action->plan_type) + { + case InstallPlanType::INSTALL: only_install_plans.emplace_back(install_action); break; + case InstallPlanType::ALREADY_INSTALLED: + if (install_action->request_type == RequestType::USER_REQUESTED) + already_installed_plans.emplace_back(install_action); + break; + case InstallPlanType::BUILD_AND_INSTALL: new_plans.emplace_back(install_action); break; + case InstallPlanType::EXCLUDED: excluded.emplace_back(install_action); break; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + } + } + else if (auto remove_action = action.remove_action.get()) + { + remove_plans.emplace_back(remove_action); + } + } + + std::sort(remove_plans.begin(), remove_plans.end(), &RemovePlanAction::compare_by_name); + std::sort(rebuilt_plans.begin(), rebuilt_plans.end(), &InstallPlanAction::compare_by_name); + std::sort(only_install_plans.begin(), only_install_plans.end(), &InstallPlanAction::compare_by_name); + std::sort(new_plans.begin(), new_plans.end(), &InstallPlanAction::compare_by_name); + std::sort(already_installed_plans.begin(), already_installed_plans.end(), &InstallPlanAction::compare_by_name); + std::sort(excluded.begin(), excluded.end(), &InstallPlanAction::compare_by_name); + + static auto actions_to_output_string = [](const std::vector& v) { + return Strings::join("\n", v, [](const InstallPlanAction* p) { + return to_output_string(p->request_type, p->displayname(), p->build_options); + }); + }; + + if (excluded.size() > 0) + { + System::println("The following packages are excluded:\n%s", actions_to_output_string(excluded)); + } + + if (already_installed_plans.size() > 0) + { + System::println("The following packages are already installed:\n%s", + actions_to_output_string(already_installed_plans)); + } + + if (rebuilt_plans.size() > 0) + { + System::println("The following packages will be rebuilt:\n%s", actions_to_output_string(rebuilt_plans)); + } + + if (new_plans.size() > 0) + { + System::println("The following packages will be built and installed:\n%s", + actions_to_output_string(new_plans)); + } + + if (only_install_plans.size() > 0) + { + System::println("The following packages will be directly installed:\n%s", + actions_to_output_string(only_install_plans)); + } + + if (has_non_user_requested_packages) + System::println("Additional packages (*) will be modified to complete this operation."); + + if (remove_plans.size() > 0 && !is_recursive) + { + System::println(System::Color::warning, + "If you are sure you want to rebuild the above packages, run the command with the " + "--recurse option"); + Checks::exit_fail(VCPKG_LINE_INFO); + } + } } diff --git a/toolsrc/src/vcpkg/help.cpp b/toolsrc/src/vcpkg/help.cpp index c83f0277bd..b7d3557425 100644 --- a/toolsrc/src/vcpkg/help.cpp +++ b/toolsrc/src/vcpkg/help.cpp @@ -85,6 +85,7 @@ namespace vcpkg::Help " vcpkg remove --outdated Uninstall all out-of-date packages\n" " vcpkg list List installed packages\n" " vcpkg update Display list of packages for updating\n" + " vcpkg upgrade Rebuild all outdated packages\n" " vcpkg hash [alg] Hash a file by specific algorithm, default SHA512\n" " vcpkg help topics Display the list of help topics\n" " vcpkg help Display help for a specific topic\n" diff --git a/toolsrc/src/vcpkg/install.cpp b/toolsrc/src/vcpkg/install.cpp index 76dc7a527d..08cfc2e733 100644 --- a/toolsrc/src/vcpkg/install.cpp +++ b/toolsrc/src/vcpkg/install.cpp @@ -348,108 +348,6 @@ namespace vcpkg::Install Checks::unreachable(VCPKG_LINE_INFO); } - static void print_plan(const std::vector& action_plan, const bool is_recursive) - { - std::vector remove_plans; - std::vector rebuilt_plans; - std::vector only_install_plans; - std::vector new_plans; - std::vector already_installed_plans; - std::vector excluded; - - const bool has_non_user_requested_packages = Util::find_if(action_plan, [](const AnyAction& package) -> bool { - if (auto iplan = package.install_plan.get()) - return iplan->request_type != RequestType::USER_REQUESTED; - else - return false; - }) != action_plan.cend(); - - for (auto&& action : action_plan) - { - if (auto install_action = action.install_plan.get()) - { - // remove plans are guaranteed to come before install plans, so we know the plan will be contained if at - // all. - auto it = Util::find_if( - remove_plans, [&](const RemovePlanAction* plan) { return plan->spec == install_action->spec; }); - if (it != remove_plans.end()) - { - rebuilt_plans.emplace_back(install_action); - } - else - { - switch (install_action->plan_type) - { - case InstallPlanType::INSTALL: only_install_plans.emplace_back(install_action); break; - case InstallPlanType::ALREADY_INSTALLED: - if (install_action->request_type == RequestType::USER_REQUESTED) - already_installed_plans.emplace_back(install_action); - break; - case InstallPlanType::BUILD_AND_INSTALL: new_plans.emplace_back(install_action); break; - case InstallPlanType::EXCLUDED: excluded.emplace_back(install_action); break; - default: Checks::unreachable(VCPKG_LINE_INFO); - } - } - } - else if (auto remove_action = action.remove_plan.get()) - { - remove_plans.emplace_back(remove_action); - } - } - - std::sort(remove_plans.begin(), remove_plans.end(), &RemovePlanAction::compare_by_name); - std::sort(rebuilt_plans.begin(), rebuilt_plans.end(), &InstallPlanAction::compare_by_name); - std::sort(only_install_plans.begin(), only_install_plans.end(), &InstallPlanAction::compare_by_name); - std::sort(new_plans.begin(), new_plans.end(), &InstallPlanAction::compare_by_name); - std::sort(already_installed_plans.begin(), already_installed_plans.end(), &InstallPlanAction::compare_by_name); - std::sort(excluded.begin(), excluded.end(), &InstallPlanAction::compare_by_name); - - static auto actions_to_output_string = [](const std::vector& v) { - return Strings::join("\n", v, [](const InstallPlanAction* p) { - return to_output_string(p->request_type, p->displayname(), p->build_options); - }); - }; - - if (excluded.size() > 0) - { - System::println("The following packages are excluded:\n%s", actions_to_output_string(excluded)); - } - - if (already_installed_plans.size() > 0) - { - System::println("The following packages are already installed:\n%s", - actions_to_output_string(already_installed_plans)); - } - - if (rebuilt_plans.size() > 0) - { - System::println("The following packages will be rebuilt:\n%s", actions_to_output_string(rebuilt_plans)); - } - - if (new_plans.size() > 0) - { - System::println("The following packages will be built and installed:\n%s", - actions_to_output_string(new_plans)); - } - - if (only_install_plans.size() > 0) - { - System::println("The following packages will be directly installed:\n%s", - actions_to_output_string(only_install_plans)); - } - - if (has_non_user_requested_packages) - System::println("Additional packages (*) will be installed to complete this operation."); - - if (remove_plans.size() > 0 && !is_recursive) - { - System::println(System::Color::warning, - "If you are sure you want to rebuild the above packages, run the command with the " - "--recurse option"); - Checks::exit_fail(VCPKG_LINE_INFO); - } - } - void InstallSummary::print() const { System::println("RESULTS"); @@ -499,7 +397,7 @@ namespace vcpkg::Install results.emplace_back(spec, &action); - if (const auto install_action = action.install_plan.get()) + if (const auto install_action = action.install_action.get()) { auto result = perform_install_plan_action(paths, *install_action, status_db); @@ -511,7 +409,7 @@ namespace vcpkg::Install results.back().build_result = std::move(result); } - else if (const auto remove_action = action.remove_plan.get()) + else if (const auto remove_action = action.remove_action.get()) { Checks::check_exit(VCPKG_LINE_INFO, GlobalState::feature_packages); Remove::perform_remove_plan_action(paths, *remove_action, Remove::Purge::YES, status_db); @@ -690,7 +588,7 @@ namespace vcpkg::Install } else { - Dependencies::PathsPortFile paths_port_file(paths); + Dependencies::PathsPortFileProvider paths_port_file(paths); auto install_plan = Dependencies::create_install_plan( paths_port_file, Util::fmap(specs, [](auto&& spec) { return spec.package_spec; }), status_db); @@ -700,7 +598,7 @@ namespace vcpkg::Install for (auto&& action : action_plan) { - if (auto p_install = action.install_plan.get()) + if (auto p_install = action.install_action.get()) { p_install->build_options = install_plan_options; if (p_install->request_type != RequestType::USER_REQUESTED) @@ -713,16 +611,16 @@ namespace vcpkg::Install // log the plan const std::string specs_string = Strings::join(",", action_plan, [](const AnyAction& action) { - if (auto iaction = action.install_plan.get()) + if (auto iaction = action.install_action.get()) return iaction->spec.to_string(); - else if (auto raction = action.remove_plan.get()) + else if (auto raction = action.remove_action.get()) return "R$" + raction->spec.to_string(); Checks::unreachable(VCPKG_LINE_INFO); }); Metrics::g_metrics.lock()->track_property("installplan", specs_string); - print_plan(action_plan, is_recursive); + Dependencies::print_plan(action_plan, is_recursive); if (dry_run) { @@ -752,7 +650,7 @@ namespace vcpkg::Install for (auto&& result : summary.results) { if (!result.action) continue; - if (auto p_install_action = result.action->install_plan.get()) + if (auto p_install_action = result.action->install_action.get()) { if (p_install_action->request_type != RequestType::USER_REQUESTED) continue; auto bpgh = result.get_binary_paragraph(); @@ -773,7 +671,7 @@ namespace vcpkg::Install { if (build_result.binary_control_file) return &build_result.binary_control_file->core_paragraph; if (action) - if (auto p_install_plan = action->install_plan.get()) + if (auto p_install_plan = action->install_action.get()) { if (auto p_bcf = p_install_plan->any_paragraph.binary_control_file.get()) return &p_bcf->core_paragraph; diff --git a/toolsrc/src/vcpkg/paragraphs.cpp b/toolsrc/src/vcpkg/paragraphs.cpp index b93de190c5..b66d53994f 100644 --- a/toolsrc/src/vcpkg/paragraphs.cpp +++ b/toolsrc/src/vcpkg/paragraphs.cpp @@ -289,16 +289,4 @@ namespace vcpkg::Paragraphs } return std::move(results.paragraphs); } - - std::map load_all_port_names_and_versions(const Files::Filesystem& fs, - const fs::path& ports_dir) - { - auto all_ports = load_all_ports(fs, ports_dir); - - std::map names_and_versions; - for (auto&& port : all_ports) - names_and_versions.emplace(port->core_paragraph->name, port->core_paragraph->version); - - return names_and_versions; - } } diff --git a/toolsrc/src/vcpkg/remove.cpp b/toolsrc/src/vcpkg/remove.cpp index 8ae0bc8812..4079d60c1d 100644 --- a/toolsrc/src/vcpkg/remove.cpp +++ b/toolsrc/src/vcpkg/remove.cpp @@ -207,10 +207,11 @@ namespace vcpkg::Remove System::println(System::Color::error, "Error: 'remove' accepts either libraries or '--outdated'"); Checks::exit_fail(VCPKG_LINE_INFO); } - specs = Util::fmap( - Update::find_outdated_packages( - Paragraphs::load_all_port_names_and_versions(paths.get_filesystem(), paths.ports), status_db), - [](auto&& outdated) { return outdated.spec; }); + + Dependencies::PathsPortFileProvider provider(paths); + + specs = Util::fmap(Update::find_outdated_packages(provider, status_db), + [](auto&& outdated) { return outdated.spec; }); if (specs.empty()) { diff --git a/toolsrc/src/vcpkg/update.cpp b/toolsrc/src/vcpkg/update.cpp index 29baef91e4..d6c5614ed2 100644 --- a/toolsrc/src/vcpkg/update.cpp +++ b/toolsrc/src/vcpkg/update.cpp @@ -14,7 +14,7 @@ namespace vcpkg::Update return left.spec.name() < right.spec.name(); } - std::vector find_outdated_packages(const std::map& src_names_to_versions, + std::vector find_outdated_packages(const Dependencies::PortFileProvider& provider, const StatusParagraphs& status_db) { const std::vector installed_packages = get_installed_ports(status_db); @@ -24,19 +24,23 @@ namespace vcpkg::Update { if (!pgh->package.feature.empty()) { - // Skip feature packages; only consider master packages for needing updates. + // Skip feature paragraphs; only consider master paragraphs for needing updates. continue; } - const auto it = src_names_to_versions.find(pgh->package.spec.name()); - if (it == src_names_to_versions.end()) + auto maybe_scf = provider.get_control_file(pgh->package.spec.name()); + if (auto p_scf = maybe_scf.get()) { - // Package was not installed from portfile - continue; + auto&& port_version = p_scf->core_paragraph->version; + auto&& installed_version = pgh->package.version; + if (installed_version != port_version) + { + output.push_back({pgh->package.spec, VersionDiff(installed_version, port_version)}); + } } - if (it->second != pgh->package.version) + else { - output.push_back({pgh->package.spec, VersionDiff(pgh->package.version, it->second)}); + // No portfile available } } @@ -58,10 +62,10 @@ namespace vcpkg::Update const StatusParagraphs status_db = database_load_check(paths); - const auto outdated_packages = SortedVector( - find_outdated_packages(Paragraphs::load_all_port_names_and_versions(paths.get_filesystem(), paths.ports), - status_db), - &OutdatedPackage::compare_by_name); + Dependencies::PathsPortFileProvider provider(paths); + + const auto outdated_packages = SortedVector(find_outdated_packages(provider, status_db), + &OutdatedPackage::compare_by_name); if (outdated_packages.empty()) { @@ -69,19 +73,17 @@ namespace vcpkg::Update } else { - std::string install_line; System::println("The following packages differ from their port versions:"); for (auto&& package : outdated_packages) { - install_line += package.spec.to_string(); - install_line += " "; System::println(" %-32s %s", package.spec, package.version_diff.to_string()); } System::println("\n" - "To update these packages, run\n" - " .\\vcpkg remove --outdated\n" - " .\\vcpkg install " + - install_line); + "To update these packages and all dependencies, run\n" + " .\\vcpkg upgrade\n" + "\n" + "To only remove outdated packages, run\n" + " .\\vcpkg remove --outdated\n"); } Checks::exit_success(VCPKG_LINE_INFO); diff --git a/toolsrc/vcpkglib/vcpkglib.vcxproj b/toolsrc/vcpkglib/vcpkglib.vcxproj index 9a7ad6dc0a..68345ca855 100644 --- a/toolsrc/vcpkglib/vcpkglib.vcxproj +++ b/toolsrc/vcpkglib/vcpkglib.vcxproj @@ -185,6 +185,7 @@ + Create Create diff --git a/toolsrc/vcpkglib/vcpkglib.vcxproj.filters b/toolsrc/vcpkglib/vcpkglib.vcxproj.filters index 966fc7fb95..dfbcf0c7e9 100644 --- a/toolsrc/vcpkglib/vcpkglib.vcxproj.filters +++ b/toolsrc/vcpkglib/vcpkglib.vcxproj.filters @@ -195,6 +195,9 @@ Source Files\vcpkg + + Source Files\vcpkg +