diff options
Diffstat (limited to 'toolsrc/src/vcpkg/dependencies.cpp')
| -rw-r--r-- | toolsrc/src/vcpkg/dependencies.cpp | 1896 |
1 files changed, 0 insertions, 1896 deletions
diff --git a/toolsrc/src/vcpkg/dependencies.cpp b/toolsrc/src/vcpkg/dependencies.cpp deleted file mode 100644 index aafdec065..000000000 --- a/toolsrc/src/vcpkg/dependencies.cpp +++ /dev/null @@ -1,1896 +0,0 @@ -#include <vcpkg/base/files.h> -#include <vcpkg/base/graphs.h> -#include <vcpkg/base/strings.h> -#include <vcpkg/base/util.h> - -#include <vcpkg/cmakevars.h> -#include <vcpkg/dependencies.h> -#include <vcpkg/packagespec.h> -#include <vcpkg/paragraphs.h> -#include <vcpkg/portfileprovider.h> -#include <vcpkg/statusparagraphs.h> -#include <vcpkg/vcpkglib.h> -#include <vcpkg/vcpkgpaths.h> - -using namespace vcpkg; - -namespace vcpkg::Dependencies -{ - namespace - { - struct ClusterInstalled - { - ClusterInstalled(const InstalledPackageView& ipv) : ipv(ipv) - { - original_features.emplace("core"); - for (auto&& feature : ipv.features) - { - original_features.emplace(feature->package.feature); - } - } - - InstalledPackageView ipv; - std::unordered_set<PackageSpec> remove_edges; - std::unordered_set<std::string> original_features; - - // Tracks whether an incoming request has asked for the default features -- on reinstall, add them - bool defaults_requested = false; - }; - - struct ClusterInstallInfo - { - std::map<std::string, std::vector<FeatureSpec>> build_edges; - bool defaults_requested = false; - }; - - /// <summary> - /// Representation of a package and its features in a ClusterGraph. - /// </summary> - struct Cluster : Util::MoveOnlyBase - { - Cluster(const InstalledPackageView& ipv, ExpectedS<const SourceControlFileLocation&>&& scfl) - : m_spec(ipv.spec()), m_scfl(std::move(scfl)), m_installed(ipv) - { - } - - Cluster(const PackageSpec& spec, const SourceControlFileLocation& scfl) : m_spec(spec), m_scfl(scfl) { } - - bool has_feature_installed(const std::string& feature) const - { - if (const ClusterInstalled* inst = m_installed.get()) - { - return inst->original_features.find(feature) != inst->original_features.end(); - } - return false; - } - - bool has_defaults_installed() const - { - if (const ClusterInstalled* inst = m_installed.get()) - { - return std::all_of(inst->ipv.core->package.default_features.begin(), - inst->ipv.core->package.default_features.end(), - [&](const std::string& feature) { - return inst->original_features.find(feature) != - inst->original_features.end(); - }); - } - return false; - } - - // Returns dependencies which were added as a result of this call. - // Precondition: must have called "mark_for_reinstall()" or "create_install_info()" on this cluster - void add_feature(const std::string& feature, - const CMakeVars::CMakeVarProvider& var_provider, - std::vector<FeatureSpec>& out_new_dependencies) - { - ClusterInstallInfo& info = m_install_info.value_or_exit(VCPKG_LINE_INFO); - if (feature == "default") - { - if (!info.defaults_requested) - { - info.defaults_requested = true; - for (auto&& f : get_scfl_or_exit().source_control_file->core_paragraph->default_features) - out_new_dependencies.emplace_back(m_spec, f); - } - return; - } - - if (Util::Sets::contains(info.build_edges, feature)) - { - // This feature has already been completely handled - return; - } - auto maybe_vars = var_provider.get_dep_info_vars(m_spec); - const std::vector<Dependency>* qualified_deps = - &get_scfl_or_exit().source_control_file->find_dependencies_for_feature(feature).value_or_exit( - VCPKG_LINE_INFO); - - std::vector<FeatureSpec> dep_list; - if (auto vars = maybe_vars.get()) - { - // Qualified dependency resolution is available - auto fullspec_list = filter_dependencies(*qualified_deps, m_spec.triplet(), *vars); - - for (auto&& fspec : fullspec_list) - { - Util::Vectors::append(&dep_list, fspec.to_feature_specs({"default"}, {"default"})); - } - - Util::sort_unique_erase(dep_list); - info.build_edges.emplace(feature, dep_list); - } - else - { - bool requires_qualified_resolution = false; - for (const Dependency& dep : *qualified_deps) - { - if (dep.platform.is_empty()) - { - Util::Vectors::append(&dep_list, - FullPackageSpec({dep.name, m_spec.triplet()}, dep.features) - .to_feature_specs({"default"}, {"default"})); - } - else - { - requires_qualified_resolution = true; - } - } - Util::sort_unique_erase(dep_list); - if (!requires_qualified_resolution) - { - info.build_edges.emplace(feature, dep_list); - } - } - Util::Vectors::append(&out_new_dependencies, dep_list); - } - - void create_install_info(std::vector<FeatureSpec>& out_reinstall_requirements) - { - bool defaults_requested = false; - if (const ClusterInstalled* inst = m_installed.get()) - { - for (const std::string& installed_feature : inst->original_features) - { - out_reinstall_requirements.emplace_back(m_spec, installed_feature); - } - defaults_requested = inst->defaults_requested; - } - - Checks::check_exit(VCPKG_LINE_INFO, !m_install_info.has_value()); - m_install_info = make_optional(ClusterInstallInfo{}); - - if (defaults_requested) - { - for (auto&& def_feature : get_scfl_or_exit().source_control_file->core_paragraph->default_features) - out_reinstall_requirements.emplace_back(m_spec, def_feature); - } - else if (request_type != RequestType::USER_REQUESTED) - { - // If the user did not explicitly request this installation, we need to add all new default features - auto&& new_defaults = get_scfl_or_exit().source_control_file->core_paragraph->default_features; - std::set<std::string> defaults_set{new_defaults.begin(), new_defaults.end()}; - - // Install only features that were not previously available - if (auto p_inst = m_installed.get()) - { - for (auto&& prev_default : p_inst->ipv.core->package.default_features) - { - defaults_set.erase(prev_default); - } - } - - for (const std::string& feature : defaults_set) - { - // Instead of dealing with adding default features to each of our dependencies right - // away we just defer to the next pass of the loop. - out_reinstall_requirements.emplace_back(m_spec, feature); - } - } - } - - const SourceControlFileLocation& get_scfl_or_exit() const - { -#if defined(_WIN32) - static auto vcpkg_remove_cmd = ".\\vcpkg"; -#else - static auto vcpkg_remove_cmd = "./vcpkg"; -#endif - if (!m_scfl) - { - Checks::exit_maybe_upgrade( - VCPKG_LINE_INFO, - "Error: while loading control file for %s: %s.\nPlease run \"%s remove %s\" and re-attempt.", - m_spec, - m_scfl.error(), - vcpkg_remove_cmd, - m_spec); - } - - return *m_scfl.get(); - } - - PackageSpec m_spec; - ExpectedS<const SourceControlFileLocation&> m_scfl; - - Optional<ClusterInstalled> m_installed; - Optional<ClusterInstallInfo> m_install_info; - - RequestType request_type = RequestType::AUTO_SELECTED; - }; - - struct PackageGraph - { - PackageGraph(const PortFileProvider::PortFileProvider& provider, - const CMakeVars::CMakeVarProvider& var_provider, - const StatusParagraphs& status_db); - ~PackageGraph(); - - void install(Span<const FeatureSpec> specs); - void upgrade(Span<const PackageSpec> specs); - void mark_user_requested(const PackageSpec& spec); - - ActionPlan serialize(const CreateInstallPlanOptions& options = {}) const; - - void mark_for_reinstall(const PackageSpec& spec, std::vector<FeatureSpec>& out_reinstall_requirements); - const CMakeVars::CMakeVarProvider& m_var_provider; - - std::unique_ptr<ClusterGraph> m_graph; - }; - - } - - /// <summary> - /// Directional graph representing a collection of packages with their features connected by their dependencies. - /// </summary> - struct ClusterGraph : Util::MoveOnlyBase - { - explicit ClusterGraph(const PortFileProvider::PortFileProvider& port_provider) : m_port_provider(port_provider) - { - } - - /// <summary> - /// Find the cluster associated with spec or if not found, create it from the PortFileProvider. - /// </summary> - /// <param name="spec">Package spec to get the cluster for.</param> - /// <returns>The cluster found or created for spec.</returns> - Cluster& get(const PackageSpec& spec) - { - auto it = m_graph.find(spec); - if (it == m_graph.end()) - { - const SourceControlFileLocation* scfl = m_port_provider.get_control_file(spec.name()).get(); - - Checks::check_maybe_upgrade( - VCPKG_LINE_INFO, scfl, "Error: Cannot find definition for package `%s`.", spec.name()); - - return m_graph - .emplace(std::piecewise_construct, std::forward_as_tuple(spec), std::forward_as_tuple(spec, *scfl)) - .first->second; - } - - return it->second; - } - - Cluster& get(const InstalledPackageView& ipv) - { - auto it = m_graph.find(ipv.spec()); - - if (it == m_graph.end()) - { - ExpectedS<const SourceControlFileLocation&> maybe_scfl = - m_port_provider.get_control_file(ipv.spec().name()); - - return m_graph - .emplace(std::piecewise_construct, - std::forward_as_tuple(ipv.spec()), - std::forward_as_tuple(ipv, std::move(maybe_scfl))) - .first->second; - } - - if (!it->second.m_installed) - { - it->second.m_installed = {ipv}; - } - - return it->second; - } - - const Cluster& find_or_exit(const PackageSpec& spec, LineInfo linfo) const - { - auto it = m_graph.find(spec); - Checks::check_maybe_upgrade(linfo, it != m_graph.end(), "Failed to locate spec in graph"); - return it->second; - } - - auto begin() const { return m_graph.begin(); } - auto end() const { return m_graph.end(); } - - private: - std::map<PackageSpec, Cluster> m_graph; - const PortFileProvider::PortFileProvider& m_port_provider; - }; - - static std::string to_output_string(RequestType request_type, - const CStringView s, - const Build::BuildPackageOptions& options, - const SourceControlFileLocation* scfl, - const InstalledPackageView* ipv, - const fs::path& builtin_ports_dir) - { - std::string ret; - switch (request_type) - { - case RequestType::AUTO_SELECTED: Strings::append(ret, " * "); break; - case RequestType::USER_REQUESTED: Strings::append(ret, " "); break; - default: Checks::unreachable(VCPKG_LINE_INFO); - } - Strings::append(ret, s); - if (scfl) - { - Strings::append(ret, " -> ", scfl->to_versiont()); - } - else if (ipv) - { - Strings::append(ret, " -> ", VersionT{ipv->core->package.version, ipv->core->package.port_version}); - } - if (options.use_head_version == Build::UseHeadVersion::YES) - { - Strings::append(ret, " (+HEAD)"); - } - if (scfl) - { - const auto s_install_port_path = fs::u8string(scfl->source_location); - if (!builtin_ports_dir.empty() && - !Strings::case_insensitive_ascii_starts_with(s_install_port_path, fs::u8string(builtin_ports_dir))) - { - Strings::append(ret, " -- ", s_install_port_path); - } - } - return ret; - } - - std::string to_output_string(RequestType request_type, - const CStringView s, - const Build::BuildPackageOptions& options) - { - return to_output_string(request_type, s, options, {}, {}, {}); - } - - std::string to_output_string(RequestType request_type, const CStringView s) - { - return to_output_string(request_type, s, {}, {}, {}, {}); - } - - InstallPlanAction::InstallPlanAction() noexcept - : plan_type(InstallPlanType::UNKNOWN), request_type(RequestType::UNKNOWN), build_options{} - { - } - - InstallPlanAction::InstallPlanAction(const PackageSpec& spec, - const SourceControlFileLocation& scfl, - const RequestType& request_type, - std::map<std::string, std::vector<FeatureSpec>>&& dependencies) - : spec(spec) - , source_control_file_location(scfl) - , plan_type(InstallPlanType::BUILD_AND_INSTALL) - , request_type(request_type) - , build_options{} - , feature_dependencies(std::move(dependencies)) - { - for (const auto& kv : feature_dependencies) - { - feature_list.emplace_back(kv.first); - for (const FeatureSpec& fspec : kv.second) - { - if (spec != fspec.spec()) - { - package_dependencies.emplace_back(fspec.spec()); - } - } - } - - Util::sort_unique_erase(package_dependencies); - Util::sort_unique_erase(feature_list); - } - - InstallPlanAction::InstallPlanAction(InstalledPackageView&& ipv, const RequestType& request_type) - : spec(ipv.spec()) - , installed_package(std::move(ipv)) - , plan_type(InstallPlanType::ALREADY_INSTALLED) - , request_type(request_type) - , build_options{} - , feature_dependencies(installed_package.get()->feature_dependencies()) - , package_dependencies(installed_package.get()->dependencies()) - { - for (const auto& kv : feature_dependencies) - { - feature_list.emplace_back(kv.first); - } - } - - std::string InstallPlanAction::displayname() const - { - if (this->feature_list.empty()) - { - return this->spec.to_string(); - } - - const std::string features = Strings::join(",", feature_list); - return Strings::format("%s[%s]:%s", this->spec.name(), features, this->spec.triplet()); - } - const std::string& InstallPlanAction::public_abi() const - { - switch (plan_type) - { - case InstallPlanType::ALREADY_INSTALLED: - return installed_package.value_or_exit(VCPKG_LINE_INFO).core->package.abi; - case InstallPlanType::BUILD_AND_INSTALL: - { - auto&& i = abi_info.value_or_exit(VCPKG_LINE_INFO); - if (auto o = i.pre_build_info->public_abi_override.get()) - return *o; - else - return i.package_abi; - } - default: Checks::unreachable(VCPKG_LINE_INFO); - } - } - bool InstallPlanAction::has_package_abi() const - { - if (!abi_info) return false; - return !abi_info.get()->package_abi.empty(); - } - Optional<const std::string&> InstallPlanAction::package_abi() const - { - if (!abi_info) return nullopt; - if (abi_info.get()->package_abi.empty()) return nullopt; - return abi_info.get()->package_abi; - } - const Build::PreBuildInfo& InstallPlanAction::pre_build_info(LineInfo linfo) const - { - return *abi_info.value_or_exit(linfo).pre_build_info.get(); - } - - bool InstallPlanAction::compare_by_name(const InstallPlanAction* left, const InstallPlanAction* right) - { - return left->spec.name() < right->spec.name(); - } - - RemovePlanAction::RemovePlanAction() noexcept - : plan_type(RemovePlanType::UNKNOWN), request_type(RequestType::UNKNOWN) - { - } - - RemovePlanAction::RemovePlanAction(const PackageSpec& spec, - const RemovePlanType& plan_type, - const RequestType& request_type) - : spec(spec), plan_type(plan_type), request_type(request_type) - { - } - - bool ExportPlanAction::compare_by_name(const ExportPlanAction* left, const ExportPlanAction* right) - { - return left->spec.name() < right->spec.name(); - } - - ExportPlanAction::ExportPlanAction() noexcept - : plan_type(ExportPlanType::UNKNOWN), request_type(RequestType::UNKNOWN) - { - } - - ExportPlanAction::ExportPlanAction(const PackageSpec& spec, - InstalledPackageView&& installed_package, - const RequestType& request_type) - : spec(spec) - , plan_type(ExportPlanType::ALREADY_BUILT) - , request_type(request_type) - , m_installed_package(std::move(installed_package)) - { - } - - ExportPlanAction::ExportPlanAction(const PackageSpec& spec, const RequestType& request_type) - : spec(spec), plan_type(ExportPlanType::NOT_BUILT), request_type(request_type) - { - } - - Optional<const BinaryParagraph&> ExportPlanAction::core_paragraph() const - { - if (auto p_ip = m_installed_package.get()) - { - return p_ip->core->package; - } - return nullopt; - } - - std::vector<PackageSpec> ExportPlanAction::dependencies() const - { - if (auto p_ip = m_installed_package.get()) - return p_ip->dependencies(); - else - return {}; - } - - bool RemovePlanAction::compare_by_name(const RemovePlanAction* left, const RemovePlanAction* right) - { - return left->spec.name() < right->spec.name(); - } - - std::vector<RemovePlanAction> create_remove_plan(const std::vector<PackageSpec>& specs, - const StatusParagraphs& status_db) - { - struct RemoveAdjacencyProvider final : Graphs::AdjacencyProvider<PackageSpec, RemovePlanAction> - { - const StatusParagraphs& status_db; - const std::vector<InstalledPackageView>& installed_ports; - const std::unordered_set<PackageSpec>& specs_as_set; - - RemoveAdjacencyProvider(const StatusParagraphs& status_db, - const std::vector<InstalledPackageView>& installed_ports, - const std::unordered_set<PackageSpec>& specs_as_set) - : status_db(status_db), installed_ports(installed_ports), specs_as_set(specs_as_set) - { - } - - std::vector<PackageSpec> adjacency_list(const RemovePlanAction& plan) const override - { - if (plan.plan_type == RemovePlanType::NOT_INSTALLED) - { - return {}; - } - - const PackageSpec& spec = plan.spec; - std::vector<PackageSpec> dependents; - for (auto&& ipv : installed_ports) - { - auto deps = ipv.dependencies(); - - if (std::find(deps.begin(), deps.end(), spec) == deps.end()) continue; - - dependents.push_back(ipv.spec()); - } - - return dependents; - } - - RemovePlanAction 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; - const StatusParagraphs::const_iterator it = status_db.find_installed(spec); - if (it == status_db.end()) - { - return RemovePlanAction{spec, RemovePlanType::NOT_INSTALLED, request_type}; - } - return RemovePlanAction{spec, RemovePlanType::REMOVE, request_type}; - } - - std::string to_string(const PackageSpec& spec) const override { return spec.to_string(); } - }; - - auto installed_ports = get_installed_ports(status_db); - const std::unordered_set<PackageSpec> specs_as_set(specs.cbegin(), specs.cend()); - return Graphs::topological_sort( - std::move(specs), RemoveAdjacencyProvider{status_db, installed_ports, specs_as_set}, {}); - } - - std::vector<ExportPlanAction> create_export_plan(const std::vector<PackageSpec>& specs, - const StatusParagraphs& status_db) - { - struct ExportAdjacencyProvider final : Graphs::AdjacencyProvider<PackageSpec, ExportPlanAction> - { - const StatusParagraphs& status_db; - const std::unordered_set<PackageSpec>& specs_as_set; - - ExportAdjacencyProvider(const StatusParagraphs& s, const std::unordered_set<PackageSpec>& specs_as_set) - : status_db(s), specs_as_set(specs_as_set) - { - } - - std::vector<PackageSpec> adjacency_list(const ExportPlanAction& plan) const override - { - return plan.dependencies(); - } - - ExportPlanAction 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 maybe_ipv = status_db.get_installed_package_view(spec); - - if (auto p_ipv = maybe_ipv.get()) - { - return ExportPlanAction{spec, std::move(*p_ipv), request_type}; - } - - return ExportPlanAction{spec, request_type}; - } - - std::string to_string(const PackageSpec& spec) const override { return spec.to_string(); } - }; - - const std::unordered_set<PackageSpec> specs_as_set(specs.cbegin(), specs.cend()); - std::vector<ExportPlanAction> toposort = - Graphs::topological_sort(specs, ExportAdjacencyProvider{status_db, specs_as_set}, {}); - return toposort; - } - - void PackageGraph::mark_user_requested(const PackageSpec& spec) - { - m_graph->get(spec).request_type = RequestType::USER_REQUESTED; - } - - // `features` should have "default" instead of missing "core" - std::vector<FullPackageSpec> resolve_deps_as_top_level(const SourceControlFile& scf, - Triplet triplet, - std::vector<std::string> features, - CMakeVars::CMakeVarProvider& var_provider) - { - PackageSpec spec{scf.core_paragraph->name, triplet}; - std::map<std::string, std::vector<std::string>> specs_to_features; - - Optional<const PlatformExpression::Context&> ctx_storage = var_provider.get_dep_info_vars(spec); - auto ctx = [&]() -> const PlatformExpression::Context& { - if (!ctx_storage) - { - var_provider.load_dep_info_vars({&spec, 1}); - ctx_storage = var_provider.get_dep_info_vars(spec); - } - return ctx_storage.value_or_exit(VCPKG_LINE_INFO); - }; - - auto handle_deps = [&](View<Dependency> deps) { - for (auto&& dep : deps) - { - if (dep.platform.is_empty() || dep.platform.evaluate(ctx())) - { - if (dep.name == spec.name()) - Util::Vectors::append(&features, dep.features); - else - Util::Vectors::append(&specs_to_features[dep.name], dep.features); - } - } - }; - - handle_deps(scf.core_paragraph->dependencies); - enum class State - { - NotVisited = 0, - Visited, - }; - std::map<std::string, State> feature_state; - while (!features.empty()) - { - auto feature = std::move(features.back()); - features.pop_back(); - - if (feature_state[feature] == State::Visited) continue; - feature_state[feature] = State::Visited; - if (feature == "default") - { - Util::Vectors::append(&features, scf.core_paragraph->default_features); - } - else - { - auto it = - Util::find_if(scf.feature_paragraphs, [&feature](const std::unique_ptr<FeatureParagraph>& ptr) { - return ptr->name == feature; - }); - if (it != scf.feature_paragraphs.end()) handle_deps(it->get()->dependencies); - } - } - return Util::fmap(specs_to_features, [triplet](std::pair<const std::string, std::vector<std::string>>& p) { - return FullPackageSpec({p.first, triplet}, Util::sort_unique_erase(std::move(p.second))); - }); - } - - ActionPlan create_feature_install_plan(const PortFileProvider::PortFileProvider& port_provider, - const CMakeVars::CMakeVarProvider& var_provider, - const std::vector<FullPackageSpec>& specs, - const StatusParagraphs& status_db, - const CreateInstallPlanOptions& options) - { - PackageGraph pgraph(port_provider, var_provider, status_db); - - std::vector<FeatureSpec> feature_specs; - for (const FullPackageSpec& spec : specs) - { - auto maybe_scfl = port_provider.get_control_file(spec.package_spec.name()); - - Checks::check_maybe_upgrade(VCPKG_LINE_INFO, - maybe_scfl.has_value(), - "Error: while loading port `%s`: %s", - spec.package_spec.name(), - maybe_scfl.error()); - - const SourceControlFileLocation* scfl = maybe_scfl.get(); - - const std::vector<std::string> all_features = - Util::fmap(scfl->source_control_file->feature_paragraphs, - [](auto&& feature_paragraph) { return feature_paragraph->name; }); - - auto fspecs = - spec.to_feature_specs(scfl->source_control_file->core_paragraph->default_features, all_features); - feature_specs.insert( - feature_specs.end(), std::make_move_iterator(fspecs.begin()), std::make_move_iterator(fspecs.end())); - } - Util::sort_unique_erase(feature_specs); - - for (const FeatureSpec& spec : feature_specs) - { - pgraph.mark_user_requested(spec.spec()); - } - pgraph.install(feature_specs); - - auto res = pgraph.serialize(options); - - return res; - } - - void PackageGraph::mark_for_reinstall(const PackageSpec& first_remove_spec, - std::vector<FeatureSpec>& out_reinstall_requirements) - { - std::set<PackageSpec> removed; - std::vector<PackageSpec> to_remove{first_remove_spec}; - - while (!to_remove.empty()) - { - PackageSpec remove_spec = std::move(to_remove.back()); - to_remove.pop_back(); - - if (!removed.insert(remove_spec).second) continue; - - Cluster& clust = m_graph->get(remove_spec); - ClusterInstalled& info = clust.m_installed.value_or_exit(VCPKG_LINE_INFO); - - if (!clust.m_install_info) - { - clust.create_install_info(out_reinstall_requirements); - } - - to_remove.insert(to_remove.end(), info.remove_edges.begin(), info.remove_edges.end()); - } - } - - /// The list of specs to install should already have default features expanded - void PackageGraph::install(Span<const FeatureSpec> specs) - { - // We batch resolving qualified dependencies, because it's an invocation of CMake which - // takes ~150ms per call. - std::vector<FeatureSpec> qualified_dependencies; - std::vector<FeatureSpec> next_dependencies{specs.begin(), specs.end()}; - - // Keep running while there is any chance of finding more dependencies - while (!next_dependencies.empty()) - { - // Keep running until the only dependencies left are qualified - while (!next_dependencies.empty()) - { - // Extract the top of the stack - FeatureSpec spec = std::move(next_dependencies.back()); - next_dependencies.pop_back(); - - // Get the cluster for the PackageSpec of the FeatureSpec we are adding to the install graph - Cluster& clust = m_graph->get(spec.spec()); - - // If this spec hasn't already had its qualified dependencies resolved - if (!m_var_provider.get_dep_info_vars(spec.spec()).has_value()) - { - // TODO: There's always the chance that we don't find the feature we're looking for (probably a - // malformed CONTROL file somewhere). We should probably output a better error. - const std::vector<Dependency>* paragraph_depends = nullptr; - if (spec.feature() == "core") - { - paragraph_depends = &clust.get_scfl_or_exit().source_control_file->core_paragraph->dependencies; - } - else if (spec.feature() == "default") - { - } - else - { - auto maybe_paragraph = - clust.get_scfl_or_exit().source_control_file->find_feature(spec.feature()); - Checks::check_maybe_upgrade(VCPKG_LINE_INFO, - maybe_paragraph.has_value(), - "Package %s does not have a %s feature", - spec.name(), - spec.feature()); - paragraph_depends = &maybe_paragraph.value_or_exit(VCPKG_LINE_INFO).dependencies; - } - - // And it has at least one qualified dependency - if (paragraph_depends && - Util::any_of(*paragraph_depends, [](auto&& dep) { return !dep.platform.is_empty(); })) - { - // Add it to the next batch run - qualified_dependencies.emplace_back(spec); - } - } - - if (clust.m_install_info.has_value()) - { - clust.add_feature(spec.feature(), m_var_provider, next_dependencies); - } - else - { - if (!clust.m_installed.has_value()) - { - clust.create_install_info(next_dependencies); - clust.add_feature(spec.feature(), m_var_provider, next_dependencies); - } - else - { - if (spec.feature() == "default") - { - if (!clust.m_installed.get()->defaults_requested) - { - clust.m_installed.get()->defaults_requested = true; - if (!clust.has_defaults_installed()) - { - mark_for_reinstall(spec.spec(), next_dependencies); - } - } - } - else if (!clust.has_feature_installed(spec.feature())) - { - // If install_info is not present and it is already installed, we have never added a feature - // which hasn't already been installed to this cluster. In this case, we need to reinstall - // the port if the feature isn't already present. - mark_for_reinstall(spec.spec(), next_dependencies); - clust.add_feature(spec.feature(), m_var_provider, next_dependencies); - } - } - } - } - - if (!qualified_dependencies.empty()) - { - Util::sort_unique_erase(qualified_dependencies); - - // Extract the package specs we need to get dependency info from. We don't run the triplet on a per - // feature basis. We run it once for the whole port. - auto qualified_package_specs = - Util::fmap(qualified_dependencies, [](const FeatureSpec& fspec) { return fspec.spec(); }); - Util::sort_unique_erase(qualified_package_specs); - m_var_provider.load_dep_info_vars(qualified_package_specs); - - // Put all the FeatureSpecs for which we had qualified dependencies back on the dependencies stack. - // We need to recheck if evaluating the triplet revealed any new dependencies. - next_dependencies.insert(next_dependencies.end(), - std::make_move_iterator(qualified_dependencies.begin()), - std::make_move_iterator(qualified_dependencies.end())); - qualified_dependencies.clear(); - } - } - } - - void PackageGraph::upgrade(Span<const PackageSpec> specs) - { - std::vector<FeatureSpec> reinstall_reqs; - - for (const PackageSpec& spec : specs) - mark_for_reinstall(spec, reinstall_reqs); - - Util::sort_unique_erase(reinstall_reqs); - - install(reinstall_reqs); - } - - ActionPlan create_upgrade_plan(const PortFileProvider::PortFileProvider& port_provider, - const CMakeVars::CMakeVarProvider& var_provider, - const std::vector<PackageSpec>& specs, - const StatusParagraphs& status_db, - const CreateInstallPlanOptions& options) - { - PackageGraph pgraph(port_provider, var_provider, status_db); - - pgraph.upgrade(specs); - - return pgraph.serialize(options); - } - - ActionPlan PackageGraph::serialize(const CreateInstallPlanOptions& options) const - { - struct BaseEdgeProvider : Graphs::AdjacencyProvider<PackageSpec, const Cluster*> - { - BaseEdgeProvider(const ClusterGraph& parent) : m_parent(parent) { } - - std::string to_string(const PackageSpec& spec) const override { return spec.to_string(); } - const Cluster* load_vertex_data(const PackageSpec& spec) const override - { - return &m_parent.find_or_exit(spec, VCPKG_LINE_INFO); - } - - const ClusterGraph& m_parent; - }; - - struct RemoveEdgeProvider final : BaseEdgeProvider - { - using BaseEdgeProvider::BaseEdgeProvider; - - std::vector<PackageSpec> adjacency_list(const Cluster* const& vertex) const override - { - auto&& set = vertex->m_installed.value_or_exit(VCPKG_LINE_INFO).remove_edges; - return {set.begin(), set.end()}; - } - } removeedgeprovider(*m_graph); - - struct InstallEdgeProvider final : BaseEdgeProvider - { - using BaseEdgeProvider::BaseEdgeProvider; - - std::vector<PackageSpec> adjacency_list(const Cluster* const& vertex) const override - { - if (!vertex->m_install_info.has_value()) return {}; - - auto& info = vertex->m_install_info.value_or_exit(VCPKG_LINE_INFO); - std::vector<PackageSpec> deps; - for (auto&& kv : info.build_edges) - for (auto&& e : kv.second) - { - auto spec = e.spec(); - if (spec != vertex->m_spec) deps.push_back(std::move(spec)); - } - Util::sort_unique_erase(deps); - return deps; - } - } installedgeprovider(*m_graph); - - std::vector<PackageSpec> removed_vertices; - std::vector<PackageSpec> installed_vertices; - for (auto&& kv : *m_graph) - { - if (kv.second.m_install_info.has_value() && kv.second.m_installed.has_value()) - { - removed_vertices.push_back(kv.first); - } - if (kv.second.m_install_info.has_value() || kv.second.request_type == RequestType::USER_REQUESTED) - { - installed_vertices.push_back(kv.first); - } - } - auto remove_toposort = Graphs::topological_sort(removed_vertices, removeedgeprovider, options.randomizer); - auto insert_toposort = Graphs::topological_sort(installed_vertices, installedgeprovider, options.randomizer); - - ActionPlan plan; - - for (auto&& p_cluster : remove_toposort) - { - plan.remove_actions.emplace_back(p_cluster->m_spec, RemovePlanType::REMOVE, p_cluster->request_type); - } - - for (auto&& p_cluster : insert_toposort) - { - // Every cluster that has an install_info needs to be built - // If a cluster only has an installed object and is marked as user requested we should still report it. - if (auto info_ptr = p_cluster->m_install_info.get()) - { - std::map<std::string, std::vector<FeatureSpec>> computed_edges; - for (auto&& kv : info_ptr->build_edges) - { - std::set<FeatureSpec> fspecs; - for (auto&& fspec : kv.second) - { - if (fspec.feature() != "default") - { - fspecs.insert(fspec); - continue; - } - auto&& dep_clust = m_graph->get(fspec.spec()); - const auto& default_features = [&] { - if (dep_clust.m_install_info.has_value()) - return dep_clust.get_scfl_or_exit() - .source_control_file->core_paragraph->default_features; - if (auto p = dep_clust.m_installed.get()) return p->ipv.core->package.default_features; - Checks::unreachable(VCPKG_LINE_INFO); - }(); - for (auto&& default_feature : default_features) - fspecs.emplace(fspec.spec(), default_feature); - } - computed_edges[kv.first].assign(fspecs.begin(), fspecs.end()); - } - plan.install_actions.emplace_back(p_cluster->m_spec, - p_cluster->get_scfl_or_exit(), - p_cluster->request_type, - std::move(computed_edges)); - } - else if (p_cluster->request_type == RequestType::USER_REQUESTED && p_cluster->m_installed.has_value()) - { - auto&& installed = p_cluster->m_installed.value_or_exit(VCPKG_LINE_INFO); - plan.already_installed.emplace_back(InstalledPackageView(installed.ipv), p_cluster->request_type); - } - } - - return plan; - } - - static std::unique_ptr<ClusterGraph> create_feature_install_graph( - const PortFileProvider::PortFileProvider& port_provider, const StatusParagraphs& status_db) - { - std::unique_ptr<ClusterGraph> graph = std::make_unique<ClusterGraph>(port_provider); - - auto installed_ports = get_installed_ports(status_db); - - for (auto&& ipv : installed_ports) - { - graph->get(ipv); - } - - // Populate the graph with "remove edges", which are the reverse of the Build-Depends edges. - for (auto&& ipv : installed_ports) - { - auto deps = ipv.dependencies(); - - for (auto&& dep : deps) - { - auto p_installed = graph->get(dep).m_installed.get(); - Checks::check_maybe_upgrade( - VCPKG_LINE_INFO, - p_installed, - "Error: database corrupted. Package %s is installed but dependency %s is not.", - ipv.spec(), - dep); - p_installed->remove_edges.emplace(ipv.spec()); - } - } - return graph; - } - - PackageGraph::PackageGraph(const PortFileProvider::PortFileProvider& port_provider, - const CMakeVars::CMakeVarProvider& var_provider, - const StatusParagraphs& status_db) - : m_var_provider(var_provider), m_graph(create_feature_install_graph(port_provider, status_db)) - { - } - - PackageGraph::~PackageGraph() = default; - - 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()) - { - System::print2("All requested packages are currently installed.\n"); - return; - } - - std::set<PackageSpec> remove_specs; - std::vector<const InstallPlanAction*> rebuilt_plans; - std::vector<const InstallPlanAction*> only_install_plans; - std::vector<const InstallPlanAction*> new_plans; - std::vector<const InstallPlanAction*> already_installed_plans; - std::vector<const InstallPlanAction*> excluded; - - const bool has_non_user_requested_packages = - Util::find_if(action_plan.install_actions, [](const InstallPlanAction& action) -> bool { - return action.request_type != RequestType::USER_REQUESTED; - }) != action_plan.install_actions.cend(); - - for (auto&& remove_action : action_plan.remove_actions) - { - remove_specs.emplace(remove_action.spec); - } - for (auto&& install_action : action_plan.install_actions) - { - // remove plans are guaranteed to come before install plans, so we know the plan will be contained - // if at all. - auto it = remove_specs.find(install_action.spec); - if (it != remove_specs.end()) - { - remove_specs.erase(it); - rebuilt_plans.push_back(&install_action); - } - else - { - if (install_action.plan_type == InstallPlanType::EXCLUDED) - excluded.push_back(&install_action); - else - new_plans.push_back(&install_action); - } - } - for (auto&& action : action_plan.already_installed) - { - if (action.request_type == RequestType::USER_REQUESTED) already_installed_plans.emplace_back(&action); - } - already_installed_plans = Util::fmap(action_plan.already_installed, [](auto&& action) { return &action; }); - - 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<const InstallPlanAction*>& v) { - return Strings::join("\n", v, [&](const InstallPlanAction* p) { - return to_output_string(p->request_type, - p->displayname(), - p->build_options, - p->source_control_file_location.get(), - p->installed_package.get(), - builtin_ports_dir); - }); - }; - - if (!excluded.empty()) - { - System::print2("The following packages are excluded:\n", actions_to_output_string(excluded), '\n'); - } - - if (!already_installed_plans.empty()) - { - System::print2("The following packages are already installed:\n", - actions_to_output_string(already_installed_plans), - '\n'); - } - - if (!remove_specs.empty()) - { - std::string msg = "The following packages will be removed:\n"; - for (auto spec : remove_specs) - { - Strings::append(msg, to_output_string(RequestType::USER_REQUESTED, spec.to_string()), '\n'); - } - System::print2(msg); - } - - if (!rebuilt_plans.empty()) - { - System::print2("The following packages will be rebuilt:\n", actions_to_output_string(rebuilt_plans), '\n'); - } - - if (!new_plans.empty()) - { - System::print2( - "The following packages will be built and installed:\n", actions_to_output_string(new_plans), '\n'); - } - - if (!only_install_plans.empty()) - { - System::print2("The following packages will be directly installed:\n", - actions_to_output_string(only_install_plans), - '\n'); - } - - if (has_non_user_requested_packages) - System::print2("Additional packages (*) will be modified to complete this operation.\n"); - bool have_removals = !remove_specs.empty() || !rebuilt_plans.empty(); - if (have_removals && !is_recursive) - { - System::print2(System::Color::warning, - "If you are sure you want to rebuild the above packages, run the command with the " - "--recurse option\n"); - Checks::exit_fail(VCPKG_LINE_INFO); - } - } - - namespace - { - struct VersionedPackageGraph - { - private: - using IVersionedPortfileProvider = PortFileProvider::IVersionedPortfileProvider; - using IBaselineProvider = PortFileProvider::IBaselineProvider; - - public: - VersionedPackageGraph(const IVersionedPortfileProvider& ver_provider, - const IBaselineProvider& base_provider, - const PortFileProvider::IOverlayProvider& oprovider, - const CMakeVars::CMakeVarProvider& var_provider) - : m_ver_provider(ver_provider) - , m_base_provider(base_provider) - , m_o_provider(oprovider) - , m_var_provider(var_provider) - { - } - - void add_override(const std::string& name, const Versions::Version& v); - - void add_roots(View<Dependency> dep, const PackageSpec& toplevel); - - ExpectedS<ActionPlan> finalize_extract_plan(); - - private: - const IVersionedPortfileProvider& m_ver_provider; - const IBaselineProvider& m_base_provider; - const PortFileProvider::IOverlayProvider& m_o_provider; - const CMakeVars::CMakeVarProvider& m_var_provider; - - struct DepSpec - { - PackageSpec spec; - Versions::Version ver; - std::vector<std::string> features; - }; - - // This object contains the current version within a given column. - // Each "string" scheme text is treated as a separate column - // "relaxed" versions all share the same column - struct VersionSchemeInfo - { - const SourceControlFileLocation* scfl = nullptr; - Versions::Version version; - // This tracks a list of constraint sources for debugging purposes - std::vector<std::string> origins; - std::map<std::string, std::vector<FeatureSpec>> deps; - - bool is_less_than(const Versions::Version& new_ver) const; - }; - - struct PackageNode : Util::MoveOnlyBase - { - std::map<Versions::Version, VersionSchemeInfo*, VersionTMapLess> vermap; - std::map<std::string, VersionSchemeInfo> exacts; - Optional<std::unique_ptr<VersionSchemeInfo>> relaxed; - Optional<std::unique_ptr<VersionSchemeInfo>> semver; - Optional<std::unique_ptr<VersionSchemeInfo>> date; - std::set<std::string> features; - bool default_features = true; - - VersionSchemeInfo* get_node(const Versions::Version& ver); - VersionSchemeInfo& emplace_node(Versions::Scheme scheme, const Versions::Version& ver); - }; - - std::vector<DepSpec> m_roots; - std::map<std::string, Versions::Version> m_overrides; - std::map<PackageSpec, PackageNode> m_graph; - - std::pair<const PackageSpec, PackageNode>& emplace_package(const PackageSpec& spec); - - void add_constraint(std::pair<const PackageSpec, PackageNode>& ref, - const Dependency& dep, - const std::string& origin); - void add_constraint(std::pair<const PackageSpec, PackageNode>& ref, - const Versions::Version& ver, - const std::string& origin); - void add_constraint(std::pair<const PackageSpec, PackageNode>& ref, - const std::string& feature, - const std::string& origin); - - void add_constraint_default_features(std::pair<const PackageSpec, PackageNode>& ref, - const std::string& origin); - - void add_feature_to(std::pair<const PackageSpec, PackageNode>& ref, - VersionSchemeInfo& vsi, - const std::string& feature); - - Optional<Versions::Version> dep_to_version(const std::string& name, const DependencyConstraint& dc); - - std::vector<std::string> m_errors; - }; - - VersionedPackageGraph::VersionSchemeInfo& VersionedPackageGraph::PackageNode::emplace_node( - Versions::Scheme scheme, const Versions::Version& ver) - { - auto it = vermap.find(ver); - if (it != vermap.end()) return *it->second; - - VersionSchemeInfo* vsi = nullptr; - if (scheme == Versions::Scheme::String) - { - vsi = &exacts[ver.text()]; - } - else if (scheme == Versions::Scheme::Relaxed) - { - if (auto p = relaxed.get()) - { - vsi = p->get(); - } - else - { - relaxed = std::make_unique<VersionSchemeInfo>(); - vsi = relaxed.get()->get(); - } - } - else if (scheme == Versions::Scheme::Semver) - { - if (auto p = semver.get()) - { - vsi = p->get(); - } - else - { - semver = std::make_unique<VersionSchemeInfo>(); - vsi = semver.get()->get(); - } - } - else if (scheme == Versions::Scheme::Date) - { - if (auto p = date.get()) - { - vsi = p->get(); - } - else - { - date = std::make_unique<VersionSchemeInfo>(); - vsi = date.get()->get(); - } - } - else - { - // not implemented - Checks::unreachable(VCPKG_LINE_INFO); - } - vermap.emplace(ver, vsi); - return *vsi; - } - - VersionedPackageGraph::VersionSchemeInfo* VersionedPackageGraph::PackageNode::get_node( - const Versions::Version& ver) - { - auto it = vermap.find(ver); - return it == vermap.end() ? nullptr : it->second; - } - - using Versions::VerComp; - - static VerComp compare_versions(Versions::Scheme sa, - const Versions::Version& a, - Versions::Scheme sb, - const Versions::Version& b) - { - if (sa != sb) return VerComp::unk; - - if (a.text() != b.text()) - { - auto result = Versions::compare(a.text(), b.text(), sa); - if (result != VerComp::eq) return result; - } - - if (a.port_version() < b.port_version()) return VerComp::lt; - if (a.port_version() > b.port_version()) return VerComp::gt; - return VerComp::eq; - } - - bool VersionedPackageGraph::VersionSchemeInfo::is_less_than(const Versions::Version& new_ver) const - { - Checks::check_exit(VCPKG_LINE_INFO, scfl); - ASSUME(scfl != nullptr); - auto scheme = scfl->source_control_file->core_paragraph->version_scheme; - auto r = compare_versions(scheme, version, scheme, new_ver); - Checks::check_exit(VCPKG_LINE_INFO, r != VerComp::unk); - return r == VerComp::lt; - } - - Versions::Version to_version(const SourceControlFile& scf) - { - return { - scf.core_paragraph->version, - scf.core_paragraph->port_version, - }; - } - Optional<Versions::Version> to_version(const DependencyConstraint& dc) - { - if (dc.type == Versions::Constraint::Type::None) - { - return nullopt; - } - else - { - return Versions::Version{ - dc.value, - dc.port_version, - }; - } - } - - void VersionedPackageGraph::add_feature_to(std::pair<const PackageSpec, PackageNode>& ref, - VersionSchemeInfo& vsi, - const std::string& feature) - { - auto deps = vsi.scfl->source_control_file->find_dependencies_for_feature(feature); - if (!deps) - { - // This version doesn't have this feature. This may result in an error during finalize if the - // constraint is not removed via upgrades. - return; - } - auto p = vsi.deps.emplace(feature, std::vector<FeatureSpec>{}); - if (!p.second) - { - // This feature has already been handled - return; - } - - for (auto&& dep : *deps.get()) - { - PackageSpec dep_spec(dep.name, ref.first.triplet()); - - if (!dep.platform.is_empty()) - { - auto maybe_vars = m_var_provider.get_dep_info_vars(ref.first); - if (!maybe_vars) - { - m_var_provider.load_dep_info_vars({&ref.first, 1}); - maybe_vars = m_var_provider.get_dep_info_vars(ref.first); - } - - if (!dep.platform.evaluate(maybe_vars.value_or_exit(VCPKG_LINE_INFO))) - { - continue; - } - } - - auto& dep_node = emplace_package(dep_spec); - // Todo: cycle detection - add_constraint(dep_node, dep, ref.first.name()); - - p.first->second.emplace_back(dep_spec, "core"); - for (auto&& f : dep.features) - { - p.first->second.emplace_back(dep_spec, f); - } - } - } - - void VersionedPackageGraph::add_constraint_default_features(std::pair<const PackageSpec, PackageNode>& ref, - const std::string& origin) - { - (void)origin; - if (!ref.second.default_features) - { - ref.second.default_features = true; - - if (auto relaxed = ref.second.relaxed.get()) - { - for (auto&& f : relaxed->get()->scfl->source_control_file->core_paragraph->default_features) - { - add_feature_to(ref, **relaxed, f); - } - } - for (auto&& vsi : ref.second.exacts) - { - for (auto&& f : vsi.second.scfl->source_control_file->core_paragraph->default_features) - { - add_feature_to(ref, vsi.second, f); - } - } - } - } - - void VersionedPackageGraph::add_constraint(std::pair<const PackageSpec, PackageNode>& ref, - const Dependency& dep, - const std::string& origin) - { - auto maybe_overlay = m_o_provider.get_control_file(ref.first.name()); - auto over_it = m_overrides.find(ref.first.name()); - if (auto p_overlay = maybe_overlay.get()) - { - auto overlay_version = to_version(*p_overlay->source_control_file); - add_constraint(ref, overlay_version, origin); - } - else if (over_it != m_overrides.end()) - { - add_constraint(ref, over_it->second, origin); - } - else - { - auto base_ver = m_base_provider.get_baseline_version(dep.name); - auto dep_ver = to_version(dep.constraint); - - if (auto dv = dep_ver.get()) - { - add_constraint(ref, *dv, origin); - } - - if (auto bv = base_ver.get()) - { - add_constraint(ref, *bv, origin); - } - } - - for (auto&& f : dep.features) - { - add_constraint(ref, f, origin); - } - } - void VersionedPackageGraph::add_constraint(std::pair<const PackageSpec, PackageNode>& ref, - const Versions::Version& version, - const std::string& origin) - { - ExpectedS<const vcpkg::SourceControlFileLocation&> maybe_scfl; - - auto maybe_overlay = m_o_provider.get_control_file(ref.first.name()); - if (auto p_overlay = maybe_overlay.get()) - { - auto overlay_version = to_version(*p_overlay->source_control_file); - if (version != overlay_version) - { - return add_constraint(ref, overlay_version, origin); - } - maybe_scfl = *p_overlay; - } - else - { - auto over_it = m_overrides.find(ref.first.name()); - if (over_it != m_overrides.end() && over_it->second != version) - { - return add_constraint(ref, over_it->second, origin); - } - maybe_scfl = m_ver_provider.get_control_file({ref.first.name(), version}); - } - - if (auto p_scfl = maybe_scfl.get()) - { - auto& exact_ref = - ref.second.emplace_node(p_scfl->source_control_file->core_paragraph->version_scheme, version); - exact_ref.origins.push_back(origin); - - bool replace; - if (exact_ref.scfl == nullptr) - { - replace = true; - } - else if (exact_ref.scfl == p_scfl) - { - replace = false; - } - else - { - replace = exact_ref.is_less_than(version); - } - - if (replace) - { - exact_ref.scfl = p_scfl; - exact_ref.version = to_version(*p_scfl->source_control_file); - exact_ref.deps.clear(); - - add_feature_to(ref, exact_ref, "core"); - - for (auto&& f : ref.second.features) - { - add_feature_to(ref, exact_ref, f); - } - - if (ref.second.default_features) - { - for (auto&& f : p_scfl->source_control_file->core_paragraph->default_features) - { - add_feature_to(ref, exact_ref, f); - } - } - } - } - else - { - m_errors.push_back(maybe_scfl.error()); - } - } - - void VersionedPackageGraph::add_constraint(std::pair<const PackageSpec, PackageNode>& ref, - const std::string& feature, - const std::string& origin) - { - auto inserted = ref.second.features.emplace(feature).second; - if (inserted) - { - if (auto relaxed = ref.second.relaxed.get()) - { - add_feature_to(ref, **relaxed, feature); - } - for (auto&& vsi : ref.second.exacts) - { - add_feature_to(ref, vsi.second, feature); - } - } - (void)origin; - } - - std::pair<const PackageSpec, VersionedPackageGraph::PackageNode>& VersionedPackageGraph::emplace_package( - const PackageSpec& spec) - { - return *m_graph.emplace(spec, PackageNode{}).first; - } - - Optional<Versions::Version> VersionedPackageGraph::dep_to_version(const std::string& name, - const DependencyConstraint& dc) - { - auto maybe_overlay = m_o_provider.get_control_file(name); - if (auto p_overlay = maybe_overlay.get()) - { - return to_version(*p_overlay->source_control_file); - } - - auto over_it = m_overrides.find(name); - if (over_it != m_overrides.end()) - { - return over_it->second; - } - - auto maybe_cons = to_version(dc); - if (maybe_cons) - { - return maybe_cons; - } - else - { - return m_base_provider.get_baseline_version(name); - } - } - - void VersionedPackageGraph::add_override(const std::string& name, const Versions::Version& v) - { - m_overrides.emplace(name, v); - } - - void VersionedPackageGraph::add_roots(View<Dependency> deps, const PackageSpec& toplevel) - { - auto specs = Util::fmap(deps, [&toplevel](const Dependency& d) { - return PackageSpec{d.name, toplevel.triplet()}; - }); - specs.push_back(toplevel); - Util::sort_unique_erase(specs); - m_var_provider.load_dep_info_vars(specs); - const auto& vars = m_var_provider.get_dep_info_vars(toplevel).value_or_exit(VCPKG_LINE_INFO); - std::vector<const Dependency*> active_deps; - - for (auto&& dep : deps) - { - PackageSpec spec(dep.name, toplevel.triplet()); - if (!dep.platform.evaluate(vars)) continue; - - active_deps.push_back(&dep); - - // Disable default features for deps with [core] as a dependency - // Note: x[core], x[y] will still eventually depend on defaults due to the second x[y] - if (Util::find(dep.features, "core") != dep.features.end()) - { - auto& node = emplace_package(spec); - node.second.default_features = false; - } - } - - for (auto pdep : active_deps) - { - const auto& dep = *pdep; - PackageSpec spec(dep.name, toplevel.triplet()); - - auto& node = emplace_package(spec); - - auto maybe_overlay = m_o_provider.get_control_file(dep.name); - if (auto p_overlay = maybe_overlay.get()) - { - auto ver = to_version(*p_overlay->source_control_file); - m_roots.push_back(DepSpec{spec, ver, dep.features}); - add_constraint(node, ver, toplevel.name()); - continue; - } - - auto over_it = m_overrides.find(dep.name); - if (over_it != m_overrides.end()) - { - m_roots.push_back(DepSpec{spec, over_it->second, dep.features}); - add_constraint(node, over_it->second, toplevel.name()); - continue; - } - - auto dep_ver = to_version(dep.constraint); - auto base_ver = m_base_provider.get_baseline_version(dep.name); - if (auto p_dep_ver = dep_ver.get()) - { - m_roots.push_back(DepSpec{spec, *p_dep_ver, dep.features}); - if (auto p_base_ver = base_ver.get()) - { - // Compare version constraint with baseline to only evaluate the "tighter" constraint - auto dep_scfl = m_ver_provider.get_control_file({dep.name, *p_dep_ver}); - auto base_scfl = m_ver_provider.get_control_file({dep.name, *p_base_ver}); - if (dep_scfl && base_scfl) - { - auto r = - compare_versions(dep_scfl.get()->source_control_file->core_paragraph->version_scheme, - *p_dep_ver, - base_scfl.get()->source_control_file->core_paragraph->version_scheme, - *p_base_ver); - if (r == VerComp::lt) - { - add_constraint(node, *p_base_ver, "baseline"); - add_constraint(node, *p_dep_ver, toplevel.name()); - } - else - { - add_constraint(node, *p_dep_ver, toplevel.name()); - add_constraint(node, *p_base_ver, "baseline"); - } - } - else - { - if (!dep_scfl) m_errors.push_back(dep_scfl.error()); - if (!base_scfl) m_errors.push_back(base_scfl.error()); - } - } - else - { - add_constraint(node, *p_dep_ver, toplevel.name()); - } - } - else if (auto p_base_ver = base_ver.get()) - { - m_roots.push_back(DepSpec{spec, *p_base_ver, dep.features}); - add_constraint(node, *p_base_ver, toplevel.name()); - } - else - { - m_errors.push_back(Strings::concat("Cannot resolve unversioned dependency from top-level to ", - dep.name, - " without a baseline entry or override.")); - } - - for (auto&& f : dep.features) - { - add_constraint(node, f, toplevel.name()); - } - if (Util::find(dep.features, "core") == dep.features.end()) - { - add_constraint_default_features(node, toplevel.name()); - } - } - } - - ExpectedS<ActionPlan> VersionedPackageGraph::finalize_extract_plan() - { - if (m_errors.size() > 0) - { - return Strings::join("\n", m_errors); - } - - ActionPlan ret; - - // second == nullptr means "in progress" - std::map<PackageSpec, VersionSchemeInfo*> emitted; - struct Frame - { - InstallPlanAction ipa; - std::vector<DepSpec> deps; - }; - std::vector<Frame> stack; - - auto push = [&emitted, this, &stack](const PackageSpec& spec, - const Versions::Version& new_ver) -> Optional<std::string> { - auto&& node = m_graph[spec]; - auto overlay = m_o_provider.get_control_file(spec.name()); - auto over_it = m_overrides.find(spec.name()); - - VersionedPackageGraph::VersionSchemeInfo* p_vnode; - if (auto p_overlay = overlay.get()) - p_vnode = node.get_node(to_version(*p_overlay->source_control_file)); - else if (over_it != m_overrides.end()) - p_vnode = node.get_node(over_it->second); - else - p_vnode = node.get_node(new_ver); - - if (!p_vnode) return Strings::concat("Version was not found during discovery: ", spec, "@", new_ver); - - auto p = emitted.emplace(spec, nullptr); - if (p.second) - { - // Newly inserted - if (!overlay && over_it == m_overrides.end()) - { - // Not overridden -- Compare against baseline - if (auto baseline = m_base_provider.get_baseline_version(spec.name())) - { - if (auto base_node = node.get_node(*baseline.get())) - { - if (base_node != p_vnode) - { - return Strings::concat("Version conflict on ", - spec.name(), - "@", - new_ver, - ": baseline required ", - *baseline.get()); - } - } - } - } - - // -> Add stack frame - auto maybe_vars = m_var_provider.get_dep_info_vars(spec); - - InstallPlanAction ipa(spec, *p_vnode->scfl, RequestType::USER_REQUESTED, std::move(p_vnode->deps)); - std::vector<DepSpec> deps; - for (auto&& f : ipa.feature_list) - { - if (auto maybe_deps = - p_vnode->scfl->source_control_file->find_dependencies_for_feature(f).get()) - { - for (auto&& dep : *maybe_deps) - { - if (dep.name == spec.name()) continue; - - if (!dep.platform.is_empty() && - !dep.platform.evaluate(maybe_vars.value_or_exit(VCPKG_LINE_INFO))) - { - continue; - } - auto maybe_cons = dep_to_version(dep.name, dep.constraint); - - if (auto cons = maybe_cons.get()) - { - deps.emplace_back(DepSpec{{dep.name, spec.triplet()}, std::move(*cons)}); - } - else - { - return Strings::concat("Cannot resolve unconstrained dependency from ", - spec.name(), - " to ", - dep.name, - " without a baseline entry or override."); - } - } - } - } - stack.push_back(Frame{std::move(ipa), std::move(deps)}); - return nullopt; - } - else - { - // spec already present in map - if (p.first->second == nullptr) - { - return Strings::concat( - "Cycle detected during ", - spec, - ":\n", - Strings::join("\n", stack, [](const auto& p) -> const PackageSpec& { return p.ipa.spec; })); - } - else if (p.first->second != p_vnode) - { - // comparable versions should retrieve the same info node - return Strings::concat( - "Version conflict on ", spec.name(), "@", p.first->second->version, ": required ", new_ver); - } - return nullopt; - } - }; - - for (auto&& root : m_roots) - { - if (auto err = push(root.spec, root.ver)) - { - return std::move(*err.get()); - } - - while (stack.size() > 0) - { - auto& back = stack.back(); - if (back.deps.empty()) - { - emitted[back.ipa.spec] = m_graph[back.ipa.spec].get_node( - to_version(*back.ipa.source_control_file_location.get()->source_control_file)); - ret.install_actions.push_back(std::move(back.ipa)); - stack.pop_back(); - } - else - { - auto dep = std::move(back.deps.back()); - back.deps.pop_back(); - if (auto err = push(dep.spec, dep.ver)) - { - return std::move(*err.get()); - } - } - } - } - return ret; - } - } - - ExpectedS<ActionPlan> create_versioned_install_plan(const PortFileProvider::IVersionedPortfileProvider& provider, - const PortFileProvider::IBaselineProvider& bprovider, - const PortFileProvider::IOverlayProvider& oprovider, - const CMakeVars::CMakeVarProvider& var_provider, - const std::vector<Dependency>& deps, - const std::vector<DependencyOverride>& overrides, - const PackageSpec& toplevel) - { - VersionedPackageGraph vpg(provider, bprovider, oprovider, var_provider); - for (auto&& o : overrides) - vpg.add_override(o.name, {o.version, o.port_version}); - vpg.add_roots(deps, toplevel); - return vpg.finalize_extract_plan(); - } -} |
