[vcpkg] Introduce create_versioned_install_plan() (#14633)

* [vcpkg] Implement constraints in manifests

* [vcpkg] Add SourceControlFile::check_against_feature_flags to prevent accidentally ignoring versioning fields

* [vcpkg] Switch check_against_feature_flags to accept fs::path

* [vcpkg] Implement overrides parsing in manifests

* [vcpkg] Address CR comments

* [vcpkg] Initial implementation of create_versioned_install_plan()

* [vcpkg] Implement port-version minimums

* [vcpkg] Implement relaxation phase

* [vcpkg] Refactor tests to use check_name_and_version

* [vcpkg] Implemented simple relaxed scheme

* [vcpkg] More relaxed scheme tests

* [vcpkg] Mixed scheme testing

* [vcpkg] Support versions and features without defaults

* [vcpkg] Support versions and features without defaults 2

* [vcpkg] Only consider greater of toplevel and baseilne

* [vcpkg] Implement overrides

* [vcpkg] Install defaults

* [vcpkg] Handle defaults of transitive packages

* [vcpkg] Fix warnings for Span of initializer_list

* [vcpkg] Use CMakeVarProvider during versioned install

* [vcpkg] Handle inter-feature dependencies

* [vcpkg] Correctly handle qualified Dependencies at toplevel

* [vcpkg] Address CR comments

Co-authored-by: Robert Schumacher <roschuma@microsoft.com>
This commit is contained in:
ras0219 2020-11-27 19:05:47 -08:00 committed by GitHub
parent 6c9cda1635
commit 896498fdba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1925 additions and 149 deletions

View File

@ -6,19 +6,23 @@ namespace vcpkg::Test
{
struct MockCMakeVarProvider : CMakeVars::CMakeVarProvider
{
void load_generic_triplet_vars(Triplet triplet) const override { generic_triplet_vars[triplet] = {}; }
using SMap = std::unordered_map<std::string, std::string>;
void load_generic_triplet_vars(Triplet triplet) const override
{
generic_triplet_vars.emplace(triplet, SMap{});
}
void load_dep_info_vars(Span<const PackageSpec> specs) const override
{
for (auto&& spec : specs)
dep_info_vars[spec] = {};
dep_info_vars.emplace(spec, SMap{});
}
void load_tag_vars(Span<const FullPackageSpec> specs,
const PortFileProvider::PortFileProvider& port_provider) const override
{
for (auto&& spec : specs)
tag_vars[spec.package_spec] = {};
tag_vars.emplace(spec.package_spec, SMap{});
(void)(port_provider);
}

View File

@ -173,5 +173,15 @@ namespace vcpkg::Dependencies
std::vector<std::string> features,
CMakeVars::CMakeVarProvider& var_provider);
/// <param name="provider">Contains the ports of the current environment.</param>
/// <param name="specs">Feature specifications to resolve dependencies for.</param>
/// <param name="status_db">Status of installed packages in the current environment.</param>
ExpectedS<ActionPlan> create_versioned_install_plan(const PortFileProvider::IVersionedPortfileProvider& vprovider,
const PortFileProvider::IBaselineProvider& bprovider,
const CMakeVars::CMakeVarProvider& var_provider,
const std::vector<Dependency>& deps,
const std::vector<DependencyOverride>& overrides,
const PackageSpec& toplevel);
void print_plan(const ActionPlan& action_plan, const bool is_recursive = true, const fs::path& vcpkg_root_dir = {});
}

View File

@ -4,4 +4,6 @@ namespace vcpkg::PortFileProvider
{
struct PortFileProvider;
struct PathsPortFileProvider;
struct IVersionedPortfileProvider;
struct IBaselineProvider;
}

View File

@ -163,9 +163,9 @@ namespace vcpkg
struct DependencyOverride
{
std::string name;
Versions::Scheme version_scheme = Versions::Scheme::String;
std::string version;
int port_version = 0;
Versions::Scheme version_scheme = Versions::Scheme::String;
Json::Object extra_info;

View File

@ -4,6 +4,8 @@
namespace vcpkg::Versions
{
using Version = VersionT;
enum class Scheme
{
Relaxed,
@ -16,11 +18,10 @@ namespace vcpkg::Versions
{
std::string port_name;
VersionT version;
Scheme scheme;
VersionSpec(const std::string& port_name, const VersionT& version, Scheme scheme);
VersionSpec(const std::string& port_name, const VersionT& version);
VersionSpec(const std::string& port_name, const std::string& version_string, int port_version, Scheme scheme);
VersionSpec(const std::string& port_name, const std::string& version_string, int port_version);
friend bool operator==(const VersionSpec& lhs, const VersionSpec& rhs);
friend bool operator!=(const VersionSpec& lhs, const VersionSpec& rhs);

View File

@ -11,15 +11,19 @@ namespace vcpkg
VersionT(const std::string& value, int port_version);
std::string to_string() const;
void to_string(std::string& out) const;
friend bool operator==(const VersionT& left, const VersionT& right);
friend bool operator!=(const VersionT& left, const VersionT& right);
friend struct VersionTMapLess;
const std::string text() const { return m_text; }
int port_version() const { return m_port_version; }
private:
std::string value;
int port_version;
std::string m_text;
int m_port_version;
};
struct VersionDiff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,152 @@
#include <catch2/catch.hpp>
#include <vcpkg/dependencies.h>
#include <vcpkg/paragraphparser.h>
#include <vcpkg/portfileprovider.h>
#include <vcpkg/sourceparagraph.h>
#include <vcpkg-test/mockcmakevarprovider.h>
#include <vcpkg-test/util.h>
using namespace vcpkg;
using namespace vcpkg::Parse;
TEST_CASE ("parse depends", "[dependencies]")
{
auto w = parse_dependencies_list("liba (windows)");
REQUIRE(w);
auto& v = *w.get();
REQUIRE(v.size() == 1);
REQUIRE(v.at(0).name == "liba");
REQUIRE(v.at(0).platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", ""}}));
REQUIRE(v.at(0).platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "WindowsStore"}}));
REQUIRE(!v.at(0).platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "Darwin"}}));
}
TEST_CASE ("filter depends", "[dependencies]")
{
const std::unordered_map<std::string, std::string> x64_win_cmake_vars{{"VCPKG_TARGET_ARCHITECTURE", "x64"},
{"VCPKG_CMAKE_SYSTEM_NAME", ""}};
const std::unordered_map<std::string, std::string> arm_uwp_cmake_vars{{"VCPKG_TARGET_ARCHITECTURE", "arm"},
{"VCPKG_CMAKE_SYSTEM_NAME", "WindowsStore"}};
auto deps_ = parse_dependencies_list("liba (!uwp), libb, libc (uwp)");
REQUIRE(deps_);
auto& deps = *deps_.get();
auto v = filter_dependencies(deps, Test::X64_WINDOWS, x64_win_cmake_vars);
REQUIRE(v.size() == 2);
REQUIRE(v.at(0).package_spec.name() == "liba");
REQUIRE(v.at(1).package_spec.name() == "libb");
auto v2 = filter_dependencies(deps, Test::ARM_UWP, arm_uwp_cmake_vars);
REQUIRE(v.size() == 2);
REQUIRE(v2.at(0).package_spec.name() == "libb");
REQUIRE(v2.at(1).package_spec.name() == "libc");
}
TEST_CASE ("parse feature depends", "[dependencies]")
{
auto u_ = parse_dependencies_list("libwebp[anim, gif2webp, img2webp, info, mux, nearlossless, "
"simd, cwebp, dwebp], libwebp[vwebp-sdl, extras] (!osx)");
REQUIRE(u_);
auto& v = *u_.get();
REQUIRE(v.size() == 2);
auto&& a0 = v.at(0);
REQUIRE(a0.name == "libwebp");
REQUIRE(a0.features.size() == 9);
REQUIRE(a0.platform.is_empty());
auto&& a1 = v.at(1);
REQUIRE(a1.name == "libwebp");
REQUIRE(a1.features.size() == 2);
REQUIRE(!a1.platform.is_empty());
REQUIRE(a1.platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", ""}}));
REQUIRE(a1.platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "Linux"}}));
REQUIRE_FALSE(a1.platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "Darwin"}}));
}
TEST_CASE ("qualified dependency", "[dependencies]")
{
using namespace Test;
PackageSpecMap spec_map;
auto spec_a = FullPackageSpec{spec_map.emplace("a", "b, b[b1] (linux)"), {}};
auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}}), {}};
PortFileProvider::MapPortFileProvider map_port{spec_map.map};
MockCMakeVarProvider var_provider;
auto plan = vcpkg::Dependencies::create_feature_install_plan(map_port, var_provider, {spec_a}, {});
REQUIRE(plan.install_actions.size() == 2);
REQUIRE(plan.install_actions.at(0).feature_list == std::vector<std::string>{"core"});
FullPackageSpec linspec_a{{"a", Triplet::from_canonical_name("x64-linux")}, {}};
var_provider.dep_info_vars[linspec_a.package_spec].emplace("VCPKG_CMAKE_SYSTEM_NAME", "Linux");
auto plan2 = vcpkg::Dependencies::create_feature_install_plan(map_port, var_provider, {linspec_a}, {});
REQUIRE(plan2.install_actions.size() == 2);
REQUIRE(plan2.install_actions.at(0).feature_list == std::vector<std::string>{"b1", "core"});
}
TEST_CASE ("resolve_deps_as_top_level", "[dependencies]")
{
using namespace Test;
PackageSpecMap spec_map;
FullPackageSpec spec_a{spec_map.emplace("a", "b, b[b1] (linux)"), {}};
FullPackageSpec spec_b{spec_map.emplace("b", "", {{"b1", ""}}), {}};
FullPackageSpec spec_c{spec_map.emplace("c", "b", {{"c1", "b[b1]"}, {"c2", "c[c1], a"}}, {"c1"}), {"core"}};
FullPackageSpec spec_d{spec_map.emplace("d", "c[core]"), {}};
PortFileProvider::MapPortFileProvider map_port{spec_map.map};
MockCMakeVarProvider var_provider;
Triplet t_linux = Triplet::from_canonical_name("x64-linux");
var_provider.dep_info_vars[{"a", t_linux}].emplace("VCPKG_CMAKE_SYSTEM_NAME", "Linux");
{
auto deps = vcpkg::Dependencies::resolve_deps_as_top_level(
*spec_map.map.at("a").source_control_file, Test::X86_WINDOWS, {}, var_provider);
REQUIRE(deps.size() == 1);
REQUIRE(deps.at(0) == spec_b);
}
{
auto deps = vcpkg::Dependencies::resolve_deps_as_top_level(
*spec_map.map.at("a").source_control_file, t_linux, {}, var_provider);
REQUIRE(deps.size() == 1);
REQUIRE(deps.at(0) == FullPackageSpec({"b", t_linux}, {"b1"}));
}
{
// without defaults
auto deps = vcpkg::Dependencies::resolve_deps_as_top_level(
*spec_map.map.at("c").source_control_file, Test::X86_WINDOWS, {}, var_provider);
REQUIRE(deps.size() == 1);
REQUIRE(deps.at(0) == spec_b);
}
FullPackageSpec spec_b_with_b1{spec_b.package_spec, {"b1"}};
{
// with defaults of c (c1)
auto deps = vcpkg::Dependencies::resolve_deps_as_top_level(
*spec_map.map.at("c").source_control_file, Test::X86_WINDOWS, {"default"}, var_provider);
REQUIRE(deps.size() == 1);
REQUIRE(deps.at(0) == spec_b_with_b1);
}
{
// with c1
auto deps = vcpkg::Dependencies::resolve_deps_as_top_level(
*spec_map.map.at("c").source_control_file, Test::X86_WINDOWS, {"c1"}, var_provider);
REQUIRE(deps.size() == 1);
REQUIRE(deps.at(0) == spec_b_with_b1);
}
{
// with c2 implying c1
auto deps = vcpkg::Dependencies::resolve_deps_as_top_level(
*spec_map.map.at("c").source_control_file, Test::X86_WINDOWS, {"c2"}, var_provider);
REQUIRE(deps.size() == 2);
REQUIRE(deps.at(0) == spec_a);
REQUIRE(deps.at(1) == spec_b_with_b1);
}
{
// d -> c[core]
auto deps = vcpkg::Dependencies::resolve_deps_as_top_level(
*spec_map.map.at("d").source_control_file, Test::X86_WINDOWS, {}, var_provider);
REQUIRE(deps.size() == 1);
REQUIRE(deps.at(0) == spec_c);
}
}

View File

@ -628,7 +628,7 @@ namespace vcpkg::Dependencies
auto ctx = [&]() -> const PlatformExpression::Context& {
if (!ctx_storage)
{
var_provider.load_dep_info_vars({{spec}});
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);
@ -1157,4 +1157,662 @@ namespace vcpkg::Dependencies
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 CMakeVars::CMakeVarProvider& var_provider)
: m_ver_provider(ver_provider), m_base_provider(base_provider), 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 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;
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);
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
{
// 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;
}
enum class VerComp
{
unk,
lt,
eq,
gt,
};
static VerComp compare_versions(Versions::Scheme sa,
const Versions::Version& a,
Versions::Scheme sb,
const Versions::Version& b)
{
if (sa != sb) return VerComp::unk;
switch (sa)
{
case Versions::Scheme::String:
{
if (a.text() != b.text()) return VerComp::unk;
if (a.port_version() < b.port_version()) return VerComp::lt;
if (a.port_version() > b.port_version()) return VerComp::gt;
return VerComp::eq;
}
case Versions::Scheme::Relaxed:
{
auto i1 = atoi(a.text().c_str());
auto i2 = atoi(b.text().c_str());
if (i1 < i2) return VerComp::lt;
if (i1 > i2) return VerComp::gt;
if (a.port_version() < b.port_version()) return VerComp::lt;
if (a.port_version() > b.port_version()) return VerComp::gt;
return VerComp::eq;
}
default: Checks::unreachable(VCPKG_LINE_INFO);
}
}
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 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)
{
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);
}
auto 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;
}
static Optional<Versions::Version> dep_to_version(const std::string& name,
const DependencyConstraint& dc,
const PortFileProvider::IBaselineProvider& base_provider)
{
auto maybe_cons = to_version(dc);
if (maybe_cons)
{
return maybe_cons;
}
else
{
return 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 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 over_it = m_overrides.find(spec.name());
VersionedPackageGraph::VersionSchemeInfo* p_vnode;
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 (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, m_base_provider);
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 CMakeVars::CMakeVarProvider& var_provider,
const std::vector<Dependency>& deps,
const std::vector<DependencyOverride>& overrides,
const PackageSpec& toplevel)
{
VersionedPackageGraph vpg(provider, bprovider, 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();
}
}

View File

@ -273,6 +273,7 @@ namespace vcpkg
if (lhs.features != rhs.features) return false;
if (!structurally_equal(lhs.platform, rhs.platform)) return false;
if (lhs.extra_info != rhs.extra_info) return false;
if (lhs.constraint != rhs.constraint) return false;
return true;
}

View File

@ -370,7 +370,7 @@ namespace vcpkg::PortFileProvider
auto port = port_name.to_string();
for (auto&& version_entry : version_entries)
{
VersionSpec spec(port, version_entry.version, version_entry.scheme);
VersionSpec spec(port, version_entry.version);
m_impl->versions_cache[port].push_back(spec);
m_impl->git_tree_cache.emplace(std::move(spec), std::move(version_entry.git_tree));
}

View File

@ -2,22 +2,19 @@
namespace vcpkg::Versions
{
VersionSpec::VersionSpec(const std::string& port_name, const VersionT& version, Scheme scheme)
: port_name(port_name), version(version), scheme(scheme)
VersionSpec::VersionSpec(const std::string& port_name, const VersionT& version)
: port_name(port_name), version(version)
{
}
VersionSpec::VersionSpec(const std::string& port_name,
const std::string& version_string,
int port_version,
Scheme scheme)
: port_name(port_name), version(version_string, port_version), scheme(scheme)
VersionSpec::VersionSpec(const std::string& port_name, const std::string& version_string, int port_version)
: port_name(port_name), version(version_string, port_version)
{
}
bool operator==(const VersionSpec& lhs, const VersionSpec& rhs)
{
return std::tie(lhs.port_name, lhs.version, lhs.scheme) == std::tie(rhs.port_name, rhs.version, rhs.scheme);
return std::tie(lhs.port_name, lhs.version) == std::tie(rhs.port_name, rhs.version);
}
bool operator!=(const VersionSpec& lhs, const VersionSpec& rhs) { return !(lhs == rhs); }
@ -28,7 +25,6 @@ namespace vcpkg::Versions
using std::size_t;
using std::string;
return ((hash<string>()(key.port_name) ^ (hash<string>()(key.version.to_string()) << 1)) >> 1) ^
(hash<int>()(static_cast<int>(key.scheme)) << 1);
return hash<string>()(key.port_name) ^ (hash<string>()(key.version.to_string()) >> 1);
}
}

View File

@ -4,24 +4,28 @@
namespace vcpkg
{
VersionT::VersionT() noexcept : value("0.0.0"), port_version(0) { }
VersionT::VersionT(std::string&& value, int port_version) : value(std::move(value)), port_version(port_version) { }
VersionT::VersionT(const std::string& value, int port_version) : value(value), port_version(port_version) { }
std::string VersionT::to_string() const
VersionT::VersionT() noexcept : m_text("0.0.0"), m_port_version(0) { }
VersionT::VersionT(std::string&& value, int port_version) : m_text(std::move(value)), m_port_version(port_version)
{
return port_version == 0 ? value : Strings::format("%s#%d", value, port_version);
}
VersionT::VersionT(const std::string& value, int port_version) : m_text(value), m_port_version(port_version) { }
std::string VersionT::to_string() const { return Strings::concat(*this); }
void VersionT::to_string(std::string& out) const
{
out.append(m_text);
if (m_port_version) Strings::append(out, '#', m_port_version);
}
bool operator==(const VersionT& left, const VersionT& right)
{
return left.port_version == right.port_version && left.value == right.value;
return left.m_port_version == right.m_port_version && left.m_text == right.m_text;
}
bool operator!=(const VersionT& left, const VersionT& right) { return !(left == right); }
bool VersionTMapLess::operator()(const VersionT& left, const VersionT& right) const
{
auto cmp = left.value.compare(right.value);
auto cmp = left.m_text.compare(right.m_text);
if (cmp < 0)
{
return true;
@ -31,7 +35,7 @@ namespace vcpkg
return false;
}
return left.port_version < right.port_version;
return left.m_port_version < right.m_port_version;
}
VersionDiff::VersionDiff() noexcept : left(), right() { }