diff --git a/include/vcpkg/packagespec.h b/include/vcpkg/packagespec.h index 44b1fbab36..444051005a 100644 --- a/include/vcpkg/packagespec.h +++ b/include/vcpkg/packagespec.h @@ -62,7 +62,7 @@ namespace vcpkg { FeatureSpec(const PackageSpec& spec, const std::string& feature) : m_spec(spec), m_feature(feature) { } - const std::string& name() const { return m_spec.name(); } + const std::string& port() const { return m_spec.name(); } const std::string& feature() const { return m_feature; } Triplet triplet() const { return m_spec.triplet(); } @@ -73,8 +73,8 @@ namespace vcpkg bool operator<(const FeatureSpec& other) const { - if (name() < other.name()) return true; - if (name() > other.name()) return false; + if (port() < other.port()) return true; + if (port() > other.port()) return false; if (feature() < other.feature()) return true; if (feature() > other.feature()) return false; return triplet() < other.triplet(); @@ -82,7 +82,7 @@ namespace vcpkg bool operator==(const FeatureSpec& other) const { - return triplet() == other.triplet() && name() == other.name() && feature() == other.feature(); + return triplet() == other.triplet() && port() == other.port() && feature() == other.feature(); } bool operator!=(const FeatureSpec& other) const { return !(*this == other); } diff --git a/include/vcpkg/statusparagraphs.h b/include/vcpkg/statusparagraphs.h index 8fd85b4e05..bbf70f7b04 100644 --- a/include/vcpkg/statusparagraphs.h +++ b/include/vcpkg/statusparagraphs.h @@ -28,7 +28,7 @@ namespace vcpkg /// Find the StatusParagraph for given feature spec. /// Feature specification to find the status paragraph for /// Iterator for found spec - const_iterator find(const FeatureSpec& spec) const { return find(spec.name(), spec.triplet(), spec.feature()); } + const_iterator find(const FeatureSpec& spec) const { return find(spec.port(), spec.triplet(), spec.feature()); } /// Find a StatusParagraph by name, triplet and feature. /// Package name diff --git a/src/vcpkg/build.cpp b/src/vcpkg/build.cpp index f822b4db16..498fbb72e7 100644 --- a/src/vcpkg/build.cpp +++ b/src/vcpkg/build.cpp @@ -1221,7 +1221,7 @@ namespace vcpkg::Build { for (const FeatureSpec& fspec : kv.second) { - if (!status_db.is_installed(fspec) && !(fspec.name() == name && fspec.triplet() == spec.triplet())) + if (!status_db.is_installed(fspec) && !(fspec.port() == name && fspec.triplet() == spec.triplet())) { missing_fspecs.emplace_back(fspec); } diff --git a/src/vcpkg/dependencies.cpp b/src/vcpkg/dependencies.cpp index abc2cd79ee..6fb89de069 100644 --- a/src/vcpkg/dependencies.cpp +++ b/src/vcpkg/dependencies.cpp @@ -243,7 +243,7 @@ namespace vcpkg::Dependencies Checks::check_maybe_upgrade(VCPKG_LINE_INFO, maybe_paragraph.has_value(), "Package %s does not have a %s feature", - spec.name(), + spec.port(), spec.feature()); return maybe_paragraph.get()->supports_expression; @@ -858,7 +858,7 @@ namespace vcpkg::Dependencies Checks::check_maybe_upgrade(VCPKG_LINE_INFO, maybe_paragraph.has_value(), "Package %s does not have a %s feature", - spec.name(), + spec.port(), spec.feature()); paragraph_depends = &maybe_paragraph.value_or_exit(VCPKG_LINE_INFO).dependencies; has_supports = !maybe_paragraph.get()->supports_expression.is_empty(); @@ -882,7 +882,7 @@ namespace vcpkg::Dependencies m_var_provider.get_dep_info_vars(spec.spec()).value_or_exit(VCPKG_LINE_INFO))) { const auto msg = Strings::format("%s[%s] is only supported on '%s'", - spec.name(), + spec.port(), spec.feature(), to_string(*supports_expression.get())); if (unsupported_port_action == UnsupportedPortAction::Error) @@ -1300,9 +1300,8 @@ namespace vcpkg::Dependencies std::vector 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 + // This object contains the current version within a given version scheme (except for the "string" scheme, + // there we save an object for every version) struct VersionSchemeInfo { Versions::Scheme scheme; @@ -1310,6 +1309,7 @@ namespace vcpkg::Dependencies Versions::Version version; // This tracks a list of constraint sources for debugging purposes std::vector origins; + // mapping from feature name -> dependencies of this feature std::map> deps; bool is_less_than(const Versions::Version& new_ver) const; @@ -1317,16 +1317,36 @@ namespace vcpkg::Dependencies struct PackageNode { + // Mapping from version to the newest version in the corresponding version scheme + // For example, given the versions: + // - "version-string": "1.0.0" + // - "version": "1.0.1" + // - "version": "1.0.2" + // you'd have a map: + // { + // "1.0.0": { "version-string": "1.0.0" }, + // "1.0.1": { "version": "1.0.2" }, + // "1.0.2": { "version": "1.0.2" } + // } std::map vermap; + // We don't know how to compare "version-string" versions, so keep all the versions separately std::map exacts; + // for each version type besides string (relaxed, semver, date), we only track the latest version + // required Optional> relaxed; Optional> semver; Optional> date; - std::set features; + std::set requested_features; bool default_features = true; bool user_requested = false; VersionSchemeInfo* get_node(const Versions::Version& ver); + // Adds the version to the version resolver: + // - for string version schemes, just adds the newer version to the set + // - for non-string version schemes: + // - if the scheme doesn't exist in the set, adds the version to the set + // - if the scheme already exists in the set, and the version is newer than the existing entry, + // replaces the current entry for the scheme VersionSchemeInfo& emplace_node(Versions::Scheme scheme, const Versions::Version& ver); PackageNode() = default; @@ -1357,24 +1377,25 @@ namespace vcpkg::Dependencies } }; + // the roots of the dependency graph (given in the manifest file) std::vector m_roots; + // mapping from portname -> version. "overrides" field in manifest file std::map m_overrides; + // mapping from { package specifier -> node containing resolution information for that package } std::map m_graph; std::pair& emplace_package(const PackageSpec& spec); - void add_constraint(std::pair& ref, - const Dependency& dep, - const std::string& origin); - void add_constraint(std::pair& ref, - const Versions::Version& ver, - const std::string& origin); - void add_constraint(std::pair& ref, - const std::string& feature, - const std::string& origin); - - void add_constraint_default_features(std::pair& ref, - const std::string& origin); + // the following 4 functions will add stuff recursivly + void require_dependency(std::pair& ref, + const Dependency& dep, + const std::string& origin); + void require_port_version(std::pair& graph_entry, + const Versions::Version& ver, + const std::string& origin); + void require_port_feature(std::pair& ref, + const std::string& feature, + const std::string& origin); void add_feature_to(std::pair& ref, VersionSchemeInfo& vsi, @@ -1549,12 +1570,12 @@ namespace vcpkg::Dependencies // this is a feature dependency for oneself for (auto&& f : dep.features) { - add_constraint(ref, f, ref.first.name()); + require_port_feature(ref, f, ref.first.name()); } } else { - add_constraint(dep_node, dep, ref.first.name()); + require_dependency(dep_node, dep, ref.first.name()); } p.first->second.emplace_back(dep_spec, "core"); @@ -1565,36 +1586,20 @@ namespace vcpkg::Dependencies } } - void VersionedPackageGraph::add_constraint_default_features(std::pair& ref, - const std::string& origin) - { - (void)origin; - if (!ref.second.default_features) - { - ref.second.default_features = true; - ref.second.foreach_vsi([this, &ref](VersionSchemeInfo& vsi) { - for (auto&& f : vsi.scfl->source_control_file->core_paragraph->default_features) - { - this->add_feature_to(ref, vsi, f); - } - }); - } - } - - void VersionedPackageGraph::add_constraint(std::pair& ref, - const Dependency& dep, - const std::string& origin) + void VersionedPackageGraph::require_dependency(std::pair& 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); + require_port_version(ref, overlay_version, origin); } else if (over_it != m_overrides.end()) { - add_constraint(ref, over_it->second, origin); + require_port_version(ref, over_it->second, origin); } else { @@ -1603,84 +1608,89 @@ namespace vcpkg::Dependencies if (auto dv = dep_ver.get()) { - add_constraint(ref, *dv, origin); + require_port_version(ref, *dv, origin); } if (auto bv = base_ver.get()) { - add_constraint(ref, *bv, origin); + require_port_version(ref, *bv, origin); } } for (auto&& f : dep.features) { - add_constraint(ref, f, origin); + require_port_feature(ref, f, origin); } } - void VersionedPackageGraph::add_constraint(std::pair& ref, - const Versions::Version& version, - const std::string& origin) + void VersionedPackageGraph::require_port_version(std::pair& graph_entry, + const Versions::Version& version, + const std::string& origin) { ExpectedS maybe_scfl; - auto maybe_overlay = m_o_provider.get_control_file(ref.first.name()); + // if this port is an overlay port, ignore the given version and use the version from the overlay + auto maybe_overlay = m_o_provider.get_control_file(graph_entry.first.name()); if (auto p_overlay = maybe_overlay.get()) { auto overlay_version = to_version(*p_overlay->source_control_file); + // If the original request did not match the overlay version, restart this function to operate on the + // overlay version if (version != overlay_version) { - return add_constraint(ref, overlay_version, origin); + return require_port_version(graph_entry, overlay_version, origin); } maybe_scfl = *p_overlay; } else { - auto over_it = m_overrides.find(ref.first.name()); + // if there is a override, ignore the given version and use the version from the override + auto over_it = m_overrides.find(graph_entry.first.name()); if (over_it != m_overrides.end() && over_it->second != version) { - return add_constraint(ref, over_it->second, origin); + return require_port_version(graph_entry, over_it->second, origin); } - maybe_scfl = m_ver_provider.get_control_file({ref.first.name(), version}); + maybe_scfl = m_ver_provider.get_control_file({graph_entry.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); - + auto& versioned_graph_entry = graph_entry.second.emplace_node( + p_scfl->source_control_file->core_paragraph->version_scheme, version); + versioned_graph_entry.origins.push_back(origin); + // Use the new source control file if we currently don't have one or the new one is newer bool replace; - if (exact_ref.scfl == nullptr) + if (versioned_graph_entry.scfl == nullptr) { replace = true; } - else if (exact_ref.scfl == p_scfl) + else if (versioned_graph_entry.scfl == p_scfl) { replace = false; } else { - replace = exact_ref.is_less_than(version); + replace = versioned_graph_entry.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(); + versioned_graph_entry.scfl = p_scfl; + versioned_graph_entry.version = to_version(*p_scfl->source_control_file); + versioned_graph_entry.deps.clear(); - add_feature_to(ref, exact_ref, "core"); + // add all dependencies to the graph + add_feature_to(graph_entry, versioned_graph_entry, "core"); - for (auto&& f : ref.second.features) + for (auto&& f : graph_entry.second.requested_features) { - add_feature_to(ref, exact_ref, f); + add_feature_to(graph_entry, versioned_graph_entry, f); } - if (ref.second.default_features) + if (graph_entry.second.default_features) { for (auto&& f : p_scfl->source_control_file->core_paragraph->default_features) { - add_feature_to(ref, exact_ref, f); + add_feature_to(graph_entry, versioned_graph_entry, f); } } } @@ -1691,11 +1701,11 @@ namespace vcpkg::Dependencies } } - void VersionedPackageGraph::add_constraint(std::pair& ref, - const std::string& feature, - const std::string& origin) + void VersionedPackageGraph::require_port_feature(std::pair& ref, + const std::string& feature, + const std::string& origin) { - auto inserted = ref.second.features.emplace(feature).second; + auto inserted = ref.second.requested_features.emplace(feature).second; if (inserted) { ref.second.foreach_vsi( @@ -1767,6 +1777,11 @@ namespace vcpkg::Dependencies const auto& vars = m_var_provider.get_dep_info_vars(toplevel).value_or_exit(VCPKG_LINE_INFO); std::vector active_deps; + // First add all top level packages to ensure the default_features is set to false before recursing into the + // individual packages. Otherwise, a case like: + // A -> B, C[core] + // B -> C + // could install the default features of C. (A is the manifest/vcpkg.json) for (auto&& dep : deps) { if (!dep.platform.evaluate(vars)) continue; @@ -1796,12 +1811,12 @@ namespace vcpkg::Dependencies { auto ver = to_version(*p_overlay->source_control_file); m_roots.push_back(DepSpec{spec, ver, dep.features}); - add_constraint(node, ver, toplevel.name()); + require_port_version(node, ver, toplevel.name()); } else 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()); + require_port_version(node, over_it->second, toplevel.name()); } else { @@ -1824,13 +1839,13 @@ namespace vcpkg::Dependencies *p_base_ver); if (r == VerComp::lt) { - add_constraint(node, *p_base_ver, "baseline"); - add_constraint(node, *p_dep_ver, toplevel.name()); + require_port_version(node, *p_base_ver, "baseline"); + require_port_version(node, *p_dep_ver, toplevel.name()); } else { - add_constraint(node, *p_dep_ver, toplevel.name()); - add_constraint(node, *p_base_ver, "baseline"); + require_port_version(node, *p_dep_ver, toplevel.name()); + require_port_version(node, *p_base_ver, "baseline"); } } else @@ -1841,13 +1856,13 @@ namespace vcpkg::Dependencies } else { - add_constraint(node, *p_dep_ver, toplevel.name()); + require_port_version(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()); + require_port_version(node, *p_base_ver, toplevel.name()); } else { @@ -1857,11 +1872,7 @@ namespace vcpkg::Dependencies 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()); + require_port_feature(node, f, toplevel.name()); } } } @@ -1919,6 +1930,7 @@ namespace vcpkg::Dependencies }; std::vector stack; + // Adds a new Frame to the stack if the spec was not already added auto push = [&emitted, this, &stack, unsupported_port_action, &ret]( const PackageSpec& spec, const Versions::Version& new_ver, diff --git a/src/vcpkg/packagespec.cpp b/src/vcpkg/packagespec.cpp index 3f35e118e0..14692d8f2a 100644 --- a/src/vcpkg/packagespec.cpp +++ b/src/vcpkg/packagespec.cpp @@ -16,7 +16,7 @@ namespace vcpkg void FeatureSpec::to_string(std::string& out) const { if (feature().empty()) return spec().to_string(out); - Strings::append(out, name(), '[', feature(), "]:", triplet()); + Strings::append(out, port(), '[', feature(), "]:", triplet()); } std::vector FullPackageSpec::to_feature_specs(const std::vector& default_features,