Merge pull request #1461 from Microsoft/create_install_tests

feature packages graph algorithm
This commit is contained in:
Daniel Shaw 2017-07-24 16:11:22 -07:00 committed by GitHub
commit b277b4dda3
21 changed files with 1221 additions and 88 deletions

View File

@ -14,6 +14,9 @@ namespace vcpkg
BinaryParagraph();
explicit BinaryParagraph(std::unordered_map<std::string, std::string> fields);
BinaryParagraph(const SourceParagraph& spgh, const Triplet& triplet);
BinaryParagraph::BinaryParagraph(const SourceParagraph& spgh,
const FeatureParagraph& fpgh,
const Triplet& triplet);
std::string displayname() const;
@ -25,8 +28,10 @@ namespace vcpkg
std::string version;
std::string description;
std::string maintainer;
std::string feature;
std::vector<std::string> default_features;
std::vector<std::string> depends;
};
void serialize(const BinaryParagraph& pgh, std::string& out_str);
}
}

View File

@ -1,5 +1,6 @@
#pragma once
#include "PackageSpecParseResult.h"
#include "SourceParagraph.h"
#include "Triplet.h"
#include "vcpkg_expected.h"
@ -7,8 +8,6 @@ namespace vcpkg
{
struct PackageSpec
{
static ExpectedT<PackageSpec, PackageSpecParseResult> from_string(const std::string& spec_as_string,
const Triplet& default_triplet);
static std::string to_string(const std::string& name, const Triplet& triplet);
static ExpectedT<PackageSpec, PackageSpecParseResult> from_name_and_triplet(const std::string& name,
const Triplet& triplet);
@ -30,6 +29,9 @@ namespace vcpkg
{
PackageSpec package_spec;
std::vector<std::string> features;
static ExpectedT<FullPackageSpec, PackageSpecParseResult> from_string(const std::string& spec_as_string,
const Triplet& default_triplet);
};
bool operator==(const PackageSpec& left, const PackageSpec& right);

View File

@ -60,6 +60,7 @@ namespace vcpkg
std::vector<std::string> filter_dependencies(const std::vector<Dependency>& deps, const Triplet& t);
// zlib[uwp] becomes Dependency{"zlib", "uwp"}
std::vector<Dependency> expand_qualified_dependencies(const std::vector<std::string>& depends);
std::vector<std::string> parse_comma_list(const std::string& str);

View File

@ -95,14 +95,35 @@ namespace vcpkg::Build
const Triplet& triplet,
fs::path&& port_dir,
const BuildPackageOptions& build_package_options)
: src(src), triplet(triplet), port_dir(std::move(port_dir)), build_package_options(build_package_options)
: src(src)
, scf(nullptr)
, triplet(triplet)
, port_dir(std::move(port_dir))
, build_package_options(build_package_options)
, feature_list(nullptr)
{
}
BuildPackageConfig(const SourceControlFile& src,
const Triplet& triplet,
fs::path&& port_dir,
const BuildPackageOptions& build_package_options,
const std::unordered_set<std::string>& feature_list)
: src(*src.core_paragraph)
, scf(&src)
, triplet(triplet)
, port_dir(std::move(port_dir))
, build_package_options(build_package_options)
, feature_list(&feature_list)
{
}
const SourceParagraph& src;
const SourceControlFile* scf;
const Triplet& triplet;
fs::path port_dir;
const BuildPackageOptions& build_package_options;
const std::unordered_set<std::string>* feature_list;
};
ExtendedBuildResult build_package(const VcpkgPaths& paths,

View File

@ -77,6 +77,7 @@ namespace vcpkg::Commands
namespace Remove
{
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet);
void remove_package(const VcpkgPaths& paths, const PackageSpec& spec, StatusParagraphs* status_db);
}
namespace Update

View File

@ -2,6 +2,7 @@
#include "PackageSpec.h"
#include "StatusParagraphs.h"
#include "VcpkgPaths.h"
#include "vcpkg_Graphs.h"
#include "vcpkg_optional.h"
#include <vector>
@ -23,7 +24,49 @@ namespace vcpkg::Dependencies
Optional<StatusParagraph> status_paragraph;
Optional<BinaryParagraph> binary_paragraph;
Optional<SourceParagraph> source_paragraph;
Optional<const SourceControlFile*> source_control_file;
};
}
namespace vcpkg::Dependencies
{
struct FeatureSpec
{
PackageSpec spec;
std::string feature_name;
};
struct FeatureNodeEdges
{
std::vector<FeatureSpec> remove_edges;
std::vector<FeatureSpec> build_edges;
bool plus = false;
};
std::vector<FeatureSpec> to_feature_specs(const std::vector<std::string>& depends, const Triplet& t);
struct Cluster
{
std::vector<StatusParagraph> status_paragraphs;
Optional<const SourceControlFile*> source_control_file;
PackageSpec spec;
std::unordered_map<std::string, FeatureNodeEdges> edges;
std::unordered_set<std::string> to_install_features;
std::unordered_set<std::string> original_features;
bool will_remove = false;
bool transient_uninstalled = true;
Cluster() = default;
private:
Cluster(const Cluster&) = delete;
Cluster& operator=(const Cluster&) = delete;
};
struct ClusterPtr
{
Cluster* ptr;
};
bool operator==(const ClusterPtr& l, const ClusterPtr& r);
enum class InstallPlanType
{
@ -39,6 +82,10 @@ namespace vcpkg::Dependencies
InstallPlanAction();
InstallPlanAction(const PackageSpec& spec, const AnyParagraph& any_paragraph, const RequestType& request_type);
InstallPlanAction(const PackageSpec& spec,
const SourceControlFile& any_paragraph,
const std::unordered_set<std::string>& features,
const RequestType& request_type);
InstallPlanAction(const InstallPlanAction&) = delete;
InstallPlanAction(InstallPlanAction&&) = default;
InstallPlanAction& operator=(const InstallPlanAction&) = delete;
@ -48,6 +95,7 @@ namespace vcpkg::Dependencies
AnyParagraph any_paragraph;
InstallPlanType plan_type;
RequestType request_type;
std::unordered_set<std::string> feature_list;
};
enum class RemovePlanType
@ -73,6 +121,12 @@ namespace vcpkg::Dependencies
RequestType request_type;
};
struct AnyAction
{
Optional<InstallPlanAction> install_plan;
Optional<RemovePlanAction> remove_plan;
};
enum class ExportPlanType
{
UNKNOWN,
@ -97,7 +151,28 @@ namespace vcpkg::Dependencies
RequestType request_type;
};
std::vector<InstallPlanAction> create_install_plan(const VcpkgPaths& paths,
__interface PortFileProvider { virtual const SourceControlFile& get_control_file(const PackageSpec& spec) const; };
struct MapPortFile : PortFileProvider
{
const std::unordered_map<PackageSpec, SourceControlFile>& ports;
explicit MapPortFile(const std::unordered_map<PackageSpec, SourceControlFile>& map);
const SourceControlFile& get_control_file(const PackageSpec& spec) const override;
};
struct PathsPortFile : PortFileProvider
{
const VcpkgPaths& ports;
mutable std::unordered_map<PackageSpec, SourceControlFile> cache;
explicit PathsPortFile(const VcpkgPaths& paths);
const SourceControlFile& get_control_file(const PackageSpec& spec) const override;
private:
PathsPortFile(const PathsPortFile&) = delete;
PathsPortFile& operator=(const PathsPortFile&) = delete;
};
std::vector<InstallPlanAction> create_install_plan(const PortFileProvider& port_file_provider,
const std::vector<PackageSpec>& specs,
const StatusParagraphs& status_db);
@ -108,3 +183,30 @@ namespace vcpkg::Dependencies
const std::vector<PackageSpec>& specs,
const StatusParagraphs& status_db);
}
template<>
struct std::hash<vcpkg::Dependencies::ClusterPtr>
{
size_t operator()(const vcpkg::Dependencies::ClusterPtr& value) const
{
return std::hash<vcpkg::PackageSpec>()(value.ptr->spec);
}
};
namespace vcpkg::Dependencies
{
struct GraphPlan
{
Graphs::Graph<ClusterPtr> remove_graph;
Graphs::Graph<ClusterPtr> install_graph;
};
bool mark_plus(const std::string& feature,
Cluster& cluster,
std::unordered_map<PackageSpec, Cluster>& pkg_to_cluster,
GraphPlan& graph_plan);
void mark_minus(Cluster& cluster, std::unordered_map<PackageSpec, Cluster>& pkg_to_cluster, GraphPlan& graph_plan);
std::vector<AnyAction> create_feature_install_plan(const std::unordered_map<PackageSpec, SourceControlFile>& map,
const std::vector<FullPackageSpec>& specs,
const StatusParagraphs& status_db);
}

View File

@ -1,6 +1,7 @@
#pragma once
#include <unordered_map>
#include <unordered_set>
namespace vcpkg::Graphs
{
@ -63,4 +64,96 @@ namespace vcpkg::Graphs
return sorted;
}
template<class V>
struct GraphAdjacencyProvider final : AdjacencyProvider<V, V>
{
const std::unordered_map<V, std::unordered_set<V>>& vertices;
GraphAdjacencyProvider(const std::unordered_map<V, std::unordered_set<V>>& vertices) : vertices(vertices) {}
std::vector<V> adjacency_list(const V& vertex) const override
{
const std::unordered_set<V>& as_set = this->vertices.at(vertex);
return std::vector<V>(as_set.cbegin(), as_set.cend()); // TODO: Avoid redundant copy
}
V load_vertex_data(const V& vertex) const override { return vertex; }
};
template<class V>
struct Graph
{
public:
void add_vertex(V v) { this->vertices[v]; }
// TODO: Change with iterators
void add_vertices(const std::vector<V>& vs)
{
for (const V& v : vs)
{
this->vertices[v];
}
}
void add_edge(V u, V v)
{
this->vertices[v];
this->vertices[u].insert(v);
}
std::vector<V> topological_sort() const
{
GraphAdjacencyProvider<V> adjacency_provider{this->vertices};
std::unordered_map<V, int> indegrees = count_indegrees();
std::vector<V> sorted;
sorted.reserve(indegrees.size());
std::unordered_map<V, ExplorationStatus> exploration_status;
exploration_status.reserve(indegrees.size());
for (auto& pair : indegrees)
{
if (pair.second == 0) // Starting from vertices with indegree == 0. Not required.
{
V vertex = pair.first;
topological_sort_internal(vertex, adjacency_provider, exploration_status, sorted);
}
}
return sorted;
}
std::unordered_map<V, int> count_indegrees() const
{
std::unordered_map<V, int> indegrees;
for (auto& pair : this->vertices)
{
indegrees[pair.first];
for (V neighbour : pair.second)
{
++indegrees[neighbour];
}
}
return indegrees;
}
const std::unordered_map<V, std::unordered_set<V>>& adjacency_list() const { return this->vertices; }
std::vector<V> vertex_list() const
{
// why no &? it returns 0
std::vector<V> vertex_list;
for (const auto& vertex : this->vertices)
{
vertex_list.emplace_back(vertex.first);
}
return vertex_list;
}
private:
std::unordered_map<V, std::unordered_set<V>> vertices;
};
}

View File

@ -1,5 +1,6 @@
#pragma once
#include <map>
#include <utility>
#include <vector>

View File

@ -16,9 +16,11 @@ namespace vcpkg
namespace Fields
{
static const std::string FEATURE = "Feature";
static const std::string DESCRIPTION = "Description";
static const std::string MAINTAINER = "Maintainer";
static const std::string DEPENDS = "Depends";
static const std::string DEFAULTFEATURES = "Default-Features";
}
BinaryParagraph::BinaryParagraph() = default;
@ -38,7 +40,10 @@ namespace vcpkg
.value_or_exit(VCPKG_LINE_INFO);
}
parser.required_field(Fields::VERSION, this->version);
// one or the other
this->version = parser.optional_field(Fields::VERSION);
this->feature = parser.optional_field(Fields::FEATURE);
this->description = parser.optional_field(Fields::DESCRIPTION);
this->maintainer = parser.optional_field(Fields::MAINTAINER);
@ -46,6 +51,10 @@ namespace vcpkg
parser.required_field(Fields::MULTI_ARCH, multi_arch);
this->depends = parse_comma_list(parser.optional_field(Fields::DEPENDS));
if (this->feature.empty())
{
this->default_features = parse_comma_list(parser.optional_field(Fields::DEFAULTFEATURES));
}
if (auto err = parser.error_info(this->spec.name()))
{
@ -66,6 +75,16 @@ namespace vcpkg
this->depends = filter_dependencies(spgh.depends, triplet);
}
BinaryParagraph::BinaryParagraph(const SourceParagraph& spgh, const FeatureParagraph& fpgh, const Triplet& triplet)
{
this->spec = PackageSpec::from_name_and_triplet(spgh.name, triplet).value_or_exit(VCPKG_LINE_INFO);
this->version = "";
this->feature = fpgh.name;
this->description = fpgh.description;
this->maintainer = "";
this->depends = filter_dependencies(fpgh.depends, triplet);
}
std::string BinaryParagraph::displayname() const { return this->spec.to_string(); }
std::string BinaryParagraph::dir() const { return this->spec.dir(); }
@ -78,7 +97,10 @@ namespace vcpkg
void serialize(const BinaryParagraph& pgh, std::string& out_str)
{
out_str.append("Package: ").append(pgh.spec.name()).push_back('\n');
out_str.append("Version: ").append(pgh.version).push_back('\n');
if (!pgh.version.empty())
out_str.append("Version: ").append(pgh.version).push_back('\n');
else if (!pgh.feature.empty())
out_str.append("Feature: ").append(pgh.feature).push_back('\n');
if (!pgh.depends.empty())
{
out_str.append("Depends: ");

View File

@ -5,15 +5,52 @@
namespace vcpkg
{
static bool is_valid_package_spec_char(char c) { return (c == '-') || isdigit(c) || (isalpha(c) && islower(c)); }
static bool is_valid_package_spec_char(char c)
{
return (c == '-') || isdigit(c) || (isalpha(c) && islower(c)) || (c == '[') || (c == ']');
}
ExpectedT<PackageSpec, PackageSpecParseResult> PackageSpec::from_string(const std::string& spec_as_string,
const Triplet& default_triplet)
ExpectedT<FullPackageSpec, PackageSpecParseResult> FullPackageSpec::from_string(const std::string& spec_as_string,
const Triplet& default_triplet)
{
auto pos = spec_as_string.find(':');
if (pos == std::string::npos)
auto pos_l_bracket = spec_as_string.find('[');
auto pos_r_bracket = spec_as_string.find(']');
FullPackageSpec f;
if (pos == std::string::npos && pos_l_bracket == std::string::npos)
{
return from_name_and_triplet(spec_as_string, default_triplet);
f.package_spec =
PackageSpec::from_name_and_triplet(spec_as_string, default_triplet).value_or_exit(VCPKG_LINE_INFO);
return f;
}
else if (pos == std::string::npos)
{
if (pos_r_bracket == std::string::npos || pos_l_bracket >= pos_r_bracket)
{
return PackageSpecParseResult::INVALID_CHARACTERS;
}
const std::string name = spec_as_string.substr(0, pos_l_bracket);
f.package_spec = PackageSpec::from_name_and_triplet(name, default_triplet).value_or_exit(VCPKG_LINE_INFO);
f.features = parse_comma_list(spec_as_string.substr(pos_l_bracket + 1, pos_r_bracket - pos_l_bracket - 1));
return f;
}
else if (pos_l_bracket == std::string::npos && pos_r_bracket == std::string::npos)
{
const std::string name = spec_as_string.substr(0, pos);
const Triplet triplet = Triplet::from_canonical_name(spec_as_string.substr(pos + 1));
f.package_spec = PackageSpec::from_name_and_triplet(name, triplet).value_or_exit(VCPKG_LINE_INFO);
}
else
{
if (pos_r_bracket == std::string::npos || pos_l_bracket >= pos_r_bracket)
{
return PackageSpecParseResult::INVALID_CHARACTERS;
}
const std::string name = spec_as_string.substr(0, pos_l_bracket);
f.features = parse_comma_list(spec_as_string.substr(pos_l_bracket + 1, pos_r_bracket - pos_l_bracket - 1));
const Triplet triplet = Triplet::from_canonical_name(spec_as_string.substr(pos + 1));
f.package_spec = PackageSpec::from_name_and_triplet(name, triplet).value_or_exit(VCPKG_LINE_INFO);
}
auto pos2 = spec_as_string.find(':', pos + 1);
@ -21,10 +58,7 @@ namespace vcpkg
{
return PackageSpecParseResult::TOO_MANY_COLONS;
}
const std::string name = spec_as_string.substr(0, pos);
const Triplet triplet = Triplet::from_canonical_name(spec_as_string.substr(pos + 1));
return from_name_and_triplet(name, triplet);
return f;
}
ExpectedT<PackageSpec, PackageSpecParseResult> PackageSpec::from_name_and_triplet(const std::string& name,

View File

@ -40,7 +40,9 @@ namespace vcpkg::Commands::CI
const std::vector<PackageSpec> specs = load_all_package_specs(paths.get_filesystem(), paths.ports, triplet);
StatusParagraphs status_db = database_load_check(paths);
const std::vector<InstallPlanAction> install_plan = Dependencies::create_install_plan(paths, specs, status_db);
const auto& paths_port_file = Dependencies::PathsPortFile(paths);
const std::vector<InstallPlanAction> install_plan =
Dependencies::create_install_plan(paths_port_file, specs, status_db);
Checks::check_exit(VCPKG_LINE_INFO, !install_plan.empty(), "Install plan cannot be empty");
std::vector<BuildResult> results;

View File

@ -16,6 +16,8 @@ namespace vcpkg::Commands::Install
using Dependencies::InstallPlanAction;
using Dependencies::RequestType;
using Dependencies::InstallPlanType;
using Dependencies::RemovePlanAction;
using Dependencies::RemovePlanType;
InstallDir InstallDir::from_destination_root(const fs::path& destination_root,
const std::string& destination_subdirectory,
@ -290,7 +292,7 @@ namespace vcpkg::Commands::Install
return BuildResult::SUCCEEDED;
}
if (plan_type == InstallPlanType::BUILD_AND_INSTALL)
if (plan_type == InstallPlanType::BUILD_AND_INSTALL && !g_feature_packages)
{
if (use_head_version)
System::println("Building package %s from HEAD... ", display_name);
@ -318,7 +320,36 @@ namespace vcpkg::Commands::Install
return BuildResult::SUCCEEDED;
}
if (plan_type == InstallPlanType::INSTALL)
if (plan_type == InstallPlanType::BUILD_AND_INSTALL && g_feature_packages)
{
if (use_head_version)
System::println("Building package %s from HEAD... ", display_name);
else
System::println("Building package %s... ", display_name);
const Build::BuildPackageConfig build_config{
*action.any_paragraph.source_control_file.value_or_exit(VCPKG_LINE_INFO),
action.spec.triplet(),
paths.port_dir(action.spec),
build_package_options,
action.feature_list};
const auto result = Build::build_package(paths, build_config, status_db);
if (result.code != Build::BuildResult::SUCCEEDED)
{
System::println(System::Color::error, Build::create_error_message(result.code, action.spec));
return result.code;
}
System::println("Building package %s... done", display_name);
const BinaryParagraph bpgh =
Paragraphs::try_load_cached_package(paths, action.spec).value_or_exit(VCPKG_LINE_INFO);
System::println("Installing package %s... ", display_name);
install_package(paths, bpgh, &status_db);
System::println(System::Color::success, "Installing package %s... done", display_name);
return BuildResult::SUCCEEDED;
}
if (plan_type == InstallPlanType::INSTALL && !g_feature_packages)
{
if (use_head_version && is_user_requested)
{
@ -359,7 +390,10 @@ namespace vcpkg::Commands::Install
// create the plan
StatusParagraphs status_db = database_load_check(paths);
std::vector<InstallPlanAction> install_plan = Dependencies::create_install_plan(paths, specs, status_db);
Dependencies::PathsPortFile paths_port_file(paths);
std::vector<InstallPlanAction> install_plan =
Dependencies::create_install_plan(paths_port_file, specs, status_db);
Checks::check_exit(VCPKG_LINE_INFO, !install_plan.empty(), "Install plan cannot be empty");
// log the plan

View File

@ -14,7 +14,7 @@ namespace vcpkg::Commands::Remove
using Dependencies::RequestType;
using Update::OutdatedPackage;
static void remove_package(const VcpkgPaths& paths, const PackageSpec& spec, StatusParagraphs* status_db)
void remove_package(const VcpkgPaths& paths, const PackageSpec& spec, StatusParagraphs* status_db)
{
auto& fs = paths.get_filesystem();
StatusParagraph& pkg = **status_db->find(spec.name(), spec.triplet());

View File

@ -0,0 +1,527 @@
#include "CppUnitTest.h"
#include "vcpkg_Dependencies.h"
#include "vcpkg_Util.h"
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace vcpkg;
namespace UnitTest1
{
class InstallPlanTests : public TestClass<InstallPlanTests>
{
struct PackageSpecMap
{
std::unordered_map<PackageSpec, SourceControlFile> map;
Triplet triplet;
PackageSpecMap(const Triplet& t) { triplet = t; }
PackageSpec get_package_spec(std::vector<std::unordered_map<std::string, std::string>>&& fields)
{
auto m_pgh = vcpkg::SourceControlFile::parse_control_file(std::move(fields));
Assert::IsTrue(m_pgh.has_value());
auto& scf = *m_pgh.get();
auto spec = PackageSpec::from_name_and_triplet(scf->core_paragraph->name, triplet);
Assert::IsTrue(spec.has_value());
map.emplace(*spec.get(), std::move(*scf.get()));
return PackageSpec{*spec.get()};
}
PackageSpec set_package_map(std::string source, std::string version, std::string build_depends)
{
return get_package_spec({{{"Source", source}, {"Version", version}, {"Build-Depends", build_depends}}});
}
};
static void features_check(Dependencies::AnyAction* install_action,
std::string pkg_name,
std::vector<std::string> vec,
const Triplet& triplet = Triplet::X86_WINDOWS)
{
const auto& plan = install_action->install_plan.value_or_exit(VCPKG_LINE_INFO);
const auto& feature_list = plan.feature_list;
Assert::AreEqual(plan.spec.triplet().to_string().c_str(), triplet.to_string().c_str());
Assert::AreEqual(pkg_name.c_str(),
(*plan.any_paragraph.source_control_file.get())->core_paragraph->name.c_str());
Assert::AreEqual(size_t(vec.size()), feature_list.size());
for (auto&& feature_name : vec)
{
if (feature_name == "core" || feature_name == "")
{
Assert::IsTrue(Util::find(feature_list, "core") != feature_list.end() ||
Util::find(feature_list, "") != feature_list.end());
continue;
}
Assert::IsTrue(Util::find(feature_list, feature_name) != feature_list.end());
}
}
static void remove_plan_check(Dependencies::AnyAction* remove_action,
std::string pkg_name,
const Triplet& triplet = Triplet::X86_WINDOWS)
{
const auto& plan = remove_action->remove_plan.value_or_exit(VCPKG_LINE_INFO);
Assert::AreEqual(plan.spec.triplet().to_string().c_str(), triplet.to_string().c_str());
Assert::AreEqual(pkg_name.c_str(), plan.spec.name().c_str());
}
TEST_METHOD(basic_install_scheme)
{
std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
PackageSpecMap spec_map(Triplet::X86_WINDOWS);
auto spec_a = spec_map.set_package_map("a", "1.2.8", "b");
auto spec_b = spec_map.set_package_map("b", "1.3", "c");
auto spec_c = spec_map.set_package_map("c", "2.5.3", "");
auto map_port = Dependencies::MapPortFile(spec_map.map);
auto install_plan =
Dependencies::create_install_plan(map_port, {spec_a}, StatusParagraphs(std::move(status_paragraphs)));
Assert::AreEqual(size_t(3), install_plan.size());
Assert::AreEqual("c", install_plan[0].spec.name().c_str());
Assert::AreEqual("b", install_plan[1].spec.name().c_str());
Assert::AreEqual("a", install_plan[2].spec.name().c_str());
}
TEST_METHOD(multiple_install_scheme)
{
std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
PackageSpecMap spec_map(Triplet::X86_WINDOWS);
auto spec_a = spec_map.set_package_map("a", "1.2.8", "d");
auto spec_b = spec_map.set_package_map("b", "1.3", "d, e");
auto spec_c = spec_map.set_package_map("c", "2.5.3", "e, h");
auto spec_d = spec_map.set_package_map("d", "4.0", "f, g, h");
auto spec_e = spec_map.set_package_map("e", "1.0", "g");
auto spec_f = spec_map.set_package_map("f", "1.0", "");
auto spec_g = spec_map.set_package_map("g", "1.0", "");
auto spec_h = spec_map.set_package_map("h", "1.0", "");
auto map_port = Dependencies::MapPortFile(spec_map.map);
auto install_plan = Dependencies::create_install_plan(
map_port, {spec_a, spec_b, spec_c}, StatusParagraphs(std::move(status_paragraphs)));
auto iterator_pos = [&](const PackageSpec& spec) -> int {
auto it = std::find_if(
install_plan.begin(), install_plan.end(), [&](auto& action) { return action.spec == spec; });
Assert::IsTrue(it != install_plan.end());
return (int)(it - install_plan.begin());
};
int a_pos = iterator_pos(spec_a), b_pos = iterator_pos(spec_b), c_pos = iterator_pos(spec_c),
d_pos = iterator_pos(spec_d), e_pos = iterator_pos(spec_e), f_pos = iterator_pos(spec_f),
g_pos = iterator_pos(spec_g), h_pos = iterator_pos(spec_h);
Assert::IsTrue(a_pos > d_pos);
Assert::IsTrue(b_pos > e_pos);
Assert::IsTrue(b_pos > d_pos);
Assert::IsTrue(c_pos > e_pos);
Assert::IsTrue(c_pos > h_pos);
Assert::IsTrue(d_pos > f_pos);
Assert::IsTrue(d_pos > g_pos);
Assert::IsTrue(d_pos > h_pos);
Assert::IsTrue(e_pos > g_pos);
}
TEST_METHOD(long_install_scheme)
{
using Pgh = std::unordered_map<std::string, std::string>;
std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
status_paragraphs.push_back(std::make_unique<StatusParagraph>(Pgh{{"Package", "j"},
{"Version", "1.2.8"},
{"Architecture", "x86-windows"},
{"Multi-Arch", "same"},
{"Depends", "k"},
{"Status", "install ok installed"}}));
status_paragraphs.push_back(std::make_unique<StatusParagraph>(Pgh{{"Package", "k"},
{"Version", "1.2.8"},
{"Architecture", "x86-windows"},
{"Multi-Arch", "same"},
{"Depends", ""},
{"Status", "install ok installed"}}));
PackageSpecMap spec_map(Triplet::X86_WINDOWS);
auto spec_a = spec_map.set_package_map("a", "1.2.8", "b, c, d, e, f, g, h, j, k");
auto spec_b = spec_map.set_package_map("b", "1.2.8", "c, d, e, f, g, h, j, k");
auto spec_c = spec_map.set_package_map("c", "1.2.8", "d, e, f, g, h, j, k");
auto spec_d = spec_map.set_package_map("d", "1.2.8", "e, f, g, h, j, k");
auto spec_e = spec_map.set_package_map("e", "1.2.8", "f, g, h, j, k");
auto spec_f = spec_map.set_package_map("f", "1.2.8", "g, h, j, k");
auto spec_g = spec_map.set_package_map("g", "1.2.8", "h, j, k");
auto spec_h = spec_map.set_package_map("h", "1.2.8", "j, k");
auto spec_j = spec_map.set_package_map("j", "1.2.8", "k");
auto spec_k = spec_map.set_package_map("k", "1.2.8", "");
auto map_port = Dependencies::MapPortFile(spec_map.map);
auto install_plan =
Dependencies::create_install_plan(map_port, {spec_a}, StatusParagraphs(std::move(status_paragraphs)));
Assert::AreEqual(size_t(8), install_plan.size());
Assert::AreEqual("h", install_plan[0].spec.name().c_str());
Assert::AreEqual("g", install_plan[1].spec.name().c_str());
Assert::AreEqual("f", install_plan[2].spec.name().c_str());
Assert::AreEqual("e", install_plan[3].spec.name().c_str());
Assert::AreEqual("d", install_plan[4].spec.name().c_str());
Assert::AreEqual("c", install_plan[5].spec.name().c_str());
Assert::AreEqual("b", install_plan[6].spec.name().c_str());
Assert::AreEqual("a", install_plan[7].spec.name().c_str());
}
TEST_METHOD(basic_feature_test_1)
{
using Pgh = std::unordered_map<std::string, std::string>;
std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
status_paragraphs.push_back(std::make_unique<StatusParagraph>(Pgh{{"Package", "a"},
{"Default-Features", ""},
{"Version", "1.3.8"},
{"Architecture", "x86-windows"},
{"Multi-Arch", "same"},
{"Depends", "b, b[beefeatureone]"},
{"Status", "install ok installed"}}));
status_paragraphs.push_back(std::make_unique<StatusParagraph>(Pgh{{"Package", "b"},
{"Feature", "beefeatureone"},
{"Architecture", "x86-windows"},
{"Multi-Arch", "same"},
{"Depends", ""},
{"Status", "install ok installed"}}));
status_paragraphs.push_back(std::make_unique<StatusParagraph>(Pgh{{"Package", "b"},
{"Default-Features", "beefeatureone"},
{"Version", "1.3"},
{"Architecture", "x86-windows"},
{"Multi-Arch", "same"},
{"Depends", ""},
{"Status", "install ok installed"}}));
PackageSpecMap spec_map(Triplet::X86_WINDOWS);
auto spec_a =
FullPackageSpec{spec_map.get_package_spec({
{{"Source", "a"}, {"Version", "1.3.8"}, {"Build-Depends", "b, b[beefeatureone]"}},
{{"Feature", "featureone"},
{"Description", "the first feature for a"},
{"Build-Depends", "b[beefeaturetwo]"}},
}),
{"featureone"}};
auto spec_b = FullPackageSpec{spec_map.get_package_spec({
{{"Source", "b"}, {"Version", "1.3"}, {"Build-Depends", ""}},
{{"Feature", "beefeatureone"}, {"Description", "the first feature for b"}, {"Build-Depends", ""}},
{{"Feature", "beefeaturetwo"}, {"Description", "the second feature for b"}, {"Build-Depends", ""}},
{{"Feature", "beefeaturethree"}, {"Description", "the third feature for b"}, {"Build-Depends", ""}},
})};
auto install_plan = Dependencies::create_feature_install_plan(
spec_map.map, {spec_a}, StatusParagraphs(std::move(status_paragraphs)));
Assert::AreEqual(size_t(4), install_plan.size());
remove_plan_check(&install_plan[0], "a");
remove_plan_check(&install_plan[1], "b");
features_check(&install_plan[2], "b", {"beefeatureone", "core", "beefeatureone"});
features_check(&install_plan[3], "a", {"featureone", "core"});
}
TEST_METHOD(basic_feature_test_2)
{
using Pgh = std::unordered_map<std::string, std::string>;
std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
PackageSpecMap spec_map(Triplet::X86_WINDOWS);
auto spec_a =
FullPackageSpec{spec_map.get_package_spec(
{{{"Source", "a"}, {"Version", "1.3.8"}, {"Build-Depends", "b[beefeatureone]"}},
{{"Feature", "featureone"},
{"Description", "the first feature for a"},
{"Build-Depends", "b[beefeaturetwo]"}}
}),
{"featureone"}};
auto spec_b = FullPackageSpec{spec_map.get_package_spec({
{{"Source", "b"}, {"Version", "1.3"}, {"Build-Depends", ""}},
{{"Feature", "beefeatureone"}, {"Description", "the first feature for b"}, {"Build-Depends", ""}},
{{"Feature", "beefeaturetwo"}, {"Description", "the second feature for b"}, {"Build-Depends", ""}},
{{"Feature", "beefeaturethree"}, {"Description", "the third feature for b"}, {"Build-Depends", ""}},
})};
auto install_plan = Dependencies::create_feature_install_plan(
spec_map.map, {spec_a}, StatusParagraphs(std::move(status_paragraphs)));
Assert::AreEqual(size_t(2), install_plan.size());
features_check(&install_plan[0], "b", {"beefeatureone", "beefeaturetwo", "core"});
features_check(&install_plan[1], "a", {"featureone", "core"});
}
TEST_METHOD(basic_feature_test_3)
{
using Pgh = std::unordered_map<std::string, std::string>;
std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
status_paragraphs.push_back(std::make_unique<StatusParagraph>(Pgh{{"Package", "a"},
{"Default-Features", ""},
{"Version", "1.3"},
{"Architecture", "x86-windows"},
{"Multi-Arch", "same"},
{"Depends", ""},
{"Status", "install ok installed"}}));
PackageSpecMap spec_map(Triplet::X86_WINDOWS);
auto spec_a = FullPackageSpec{
spec_map.get_package_spec(
{{{"Source", "a"}, {"Version", "1.3"}, {"Build-Depends", "b"}},
{{"Feature", "one"}, {"Description", "the first feature for a"}, {"Build-Depends", ""}}}),
{"core"}};
auto spec_b = FullPackageSpec{spec_map.get_package_spec({
{{"Source", "b"}, {"Version", "1.3"}, {"Build-Depends", ""}},
})};
auto spec_c = FullPackageSpec{spec_map.get_package_spec({
{{"Source", "c"}, {"Version", "1.3"}, {"Build-Depends", "a[one]"}},
}),
{"core"}};
auto install_plan = Dependencies::create_feature_install_plan(
spec_map.map, {spec_c, spec_a}, StatusParagraphs(std::move(status_paragraphs)));
Assert::AreEqual(size_t(4), install_plan.size());
remove_plan_check(&install_plan[0], "a");
features_check(&install_plan[1], "b", {"core"});
features_check(&install_plan[2], "a", {"one", "core"});
features_check(&install_plan[3], "c", {"core"});
}
TEST_METHOD(basic_feature_test_4)
{
using Pgh = std::unordered_map<std::string, std::string>;
std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
status_paragraphs.push_back(std::make_unique<StatusParagraph>(Pgh{{"Package", "a"},
{"Default-Features", ""},
{"Version", "1.3"},
{"Architecture", "x86-windows"},
{"Multi-Arch", "same"},
{"Depends", ""},
{"Status", "install ok installed"}}));
status_paragraphs.push_back(std::make_unique<StatusParagraph>(Pgh{{"Package", "a"},
{"Feature", "one"},
{"Architecture", "x86-windows"},
{"Multi-Arch", "same"},
{"Depends", ""},
{"Status", "install ok installed"}}));
PackageSpecMap spec_map(Triplet::X86_WINDOWS);
auto spec_a = FullPackageSpec{
spec_map.get_package_spec(
{{{"Source", "a"}, {"Version", "1.3"}, {"Build-Depends", "b"}},
{{"Feature", "one"}, {"Description", "the first feature for a"}, {"Build-Depends", ""}}}),
};
auto spec_b = FullPackageSpec{spec_map.get_package_spec({
{{"Source", "b"}, {"Version", "1.3"}, {"Build-Depends", ""}},
})};
auto spec_c = FullPackageSpec{spec_map.get_package_spec({
{{"Source", "c"}, {"Version", "1.3"}, {"Build-Depends", "a[one]"}},
}),
{"core"}};
auto install_plan = Dependencies::create_feature_install_plan(
spec_map.map, {spec_c}, StatusParagraphs(std::move(status_paragraphs)));
Assert::AreEqual(size_t(1), install_plan.size());
features_check(&install_plan[0], "c", {"core"});
}
TEST_METHOD(basic_feature_test_5)
{
using Pgh = std::unordered_map<std::string, std::string>;
std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
PackageSpecMap spec_map(Triplet::X86_WINDOWS);
auto spec_a = FullPackageSpec{
spec_map.get_package_spec(
{{{"Source", "a"}, {"Version", "1.3"}, {"Build-Depends", ""}},
{{"Feature", "1"}, {"Description", "the first feature for a"}, {"Build-Depends", "b[1]"}},
{{"Feature", "2"}, {"Description", "the first feature for a"}, {"Build-Depends", "b[2]"}},
{{"Feature", "3"}, {"Description", "the first feature for a"}, {"Build-Depends", "a[2]"}}}),
{"3"}};
auto spec_b = FullPackageSpec{spec_map.get_package_spec({
{{"Source", "b"}, {"Version", "1.3"}, {"Build-Depends", ""}},
{{"Feature", "1"}, {"Description", "the first feature for a"}, {"Build-Depends", ""}},
{{"Feature", "2"}, {"Description", "the first feature for a"}, {"Build-Depends", ""}},
})};
auto install_plan = Dependencies::create_feature_install_plan(
spec_map.map, {spec_a}, StatusParagraphs(std::move(status_paragraphs)));
Assert::AreEqual(size_t(2), install_plan.size());
features_check(&install_plan[0], "b", {"core", "2"});
features_check(&install_plan[1], "a", {"core", "3", "2"});
}
TEST_METHOD(basic_feature_test_6)
{
using Pgh = std::unordered_map<std::string, std::string>;
std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
status_paragraphs.push_back(std::make_unique<StatusParagraph>(Pgh{{"Package", "b"},
{"Default-Features", ""},
{"Version", "1.3"},
{"Architecture", "x86-windows"},
{"Multi-Arch", "same"},
{"Depends", ""},
{"Status", "install ok installed"}}));
PackageSpecMap spec_map(Triplet::X86_WINDOWS);
auto spec_a = FullPackageSpec{spec_map.get_package_spec({
{{"Source", "a"}, {"Version", "1.3"}, {"Build-Depends", "b[core]"}},
}),
{"core"}};
auto spec_b = FullPackageSpec{
spec_map.get_package_spec({
{{"Source", "b"}, {"Version", "1.3"}, {"Build-Depends", ""}},
{{"Feature", "1"}, {"Description", "the first feature for a"}, {"Build-Depends", ""}},
}),
{"1"}};
auto install_plan = Dependencies::create_feature_install_plan(
spec_map.map, {spec_a, spec_b}, StatusParagraphs(std::move(status_paragraphs)));
Assert::AreEqual(size_t(3), install_plan.size());
remove_plan_check(&install_plan[0], "b");
features_check(&install_plan[1], "b", {"core", "1"});
features_check(&install_plan[2], "a", {"core"});
}
TEST_METHOD(basic_feature_test_7)
{
using Pgh = std::unordered_map<std::string, std::string>;
std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
status_paragraphs.push_back(std::make_unique<StatusParagraph>(Pgh{{"Package", "x"},
{"Default-Features", ""},
{"Version", "1.3"},
{"Architecture", "x86-windows"},
{"Multi-Arch", "same"},
{"Depends", "b"},
{"Status", "install ok installed"}}));
status_paragraphs.push_back(std::make_unique<StatusParagraph>(Pgh{{"Package", "b"},
{"Default-Features", ""},
{"Version", "1.3"},
{"Architecture", "x86-windows"},
{"Multi-Arch", "same"},
{"Depends", ""},
{"Status", "install ok installed"}}));
PackageSpecMap spec_map(Triplet::X86_WINDOWS);
auto spec_a = FullPackageSpec{spec_map.get_package_spec({
{{"Source", "a"}, {"Version", "1.3"}, {"Build-Depends", ""}},
})};
auto spec_x = FullPackageSpec{spec_map.get_package_spec({
{{"Source", "x"}, {"Version", "1.3"}, {"Build-Depends", "a"}},
}),
{"core"}};
auto spec_b = FullPackageSpec{
spec_map.get_package_spec({
{{"Source", "b"}, {"Version", "1.3"}, {"Build-Depends", ""}, {"Default-Features", ""}},
{{"Feature", "1"}, {"Description", "the first feature for a"}, {"Build-Depends", ""}},
}),
{"1"}};
auto install_plan = Dependencies::create_feature_install_plan(
spec_map.map, {spec_b, spec_x}, StatusParagraphs(std::move(status_paragraphs)));
Assert::AreEqual(size_t(5), install_plan.size());
remove_plan_check(&install_plan[0], "x");
remove_plan_check(&install_plan[1], "b");
// TODO: order here may change but A < X, and B anywhere
features_check(&install_plan[2], "a", {"core"});
features_check(&install_plan[3], "x", {"core"});
features_check(&install_plan[4], "b", {"core", "1"});
}
TEST_METHOD(basic_feature_test_8)
{
using Pgh = std::unordered_map<std::string, std::string>;
std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
status_paragraphs.push_back(std::make_unique<StatusParagraph>(Pgh{{"Package", "a"},
{"Default-Features", ""},
{"Version", "1.3"},
{"Architecture", "x64-windows"},
{"Multi-Arch", "same"},
{"Depends", ""},
{"Status", "install ok installed"}}));
status_paragraphs.push_back(std::make_unique<StatusParagraph>(Pgh{{"Package", "a"},
{"Default-Features", ""},
{"Version", "1.3"},
{"Architecture", "x86-windows"},
{"Multi-Arch", "same"},
{"Depends", ""},
{"Status", "install ok installed"}}));
PackageSpecMap spec_map(Triplet::X64_WINDOWS);
auto spec_a_64 = FullPackageSpec{
spec_map.get_package_spec(
{{{"Source", "a"}, {"Version", "1.3"}, {"Build-Depends", "b"}},
{{"Feature", "one"}, {"Description", "the first feature for a"}, {"Build-Depends", ""}}}),
{"core"}};
auto spec_b_64 = FullPackageSpec{spec_map.get_package_spec({
{{"Source", "b"}, {"Version", "1.3"}, {"Build-Depends", ""}},
})};
auto spec_c_64 = FullPackageSpec{spec_map.get_package_spec({
{{"Source", "c"}, {"Version", "1.3"}, {"Build-Depends", "a[one]"}},
}),
{"core"}};
spec_map.triplet = Triplet::X86_WINDOWS;
auto spec_a_86 = FullPackageSpec{
spec_map.get_package_spec(
{{{"Source", "a"}, {"Version", "1.3"}, {"Build-Depends", "b"}},
{{"Feature", "one"}, {"Description", "the first feature for a"}, {"Build-Depends", ""}}}),
{"core"}};
auto spec_b_86 = FullPackageSpec{spec_map.get_package_spec({
{{"Source", "b"}, {"Version", "1.3"}, {"Build-Depends", ""}},
})};
auto spec_c_86 = FullPackageSpec{spec_map.get_package_spec({
{{"Source", "c"}, {"Version", "1.3"}, {"Build-Depends", "a[one]"}},
}),
{"core"}};
auto install_plan =
Dependencies::create_feature_install_plan(spec_map.map,
{spec_c_64, spec_a_86, spec_a_64, spec_c_86},
StatusParagraphs(std::move(status_paragraphs)));
/*Assert::AreEqual(size_t(8), install_plan.size());
auto iterator_pos = [&](const PackageSpec& spec, size_t start) -> int {
auto it = std::find_if(install_plan.begin() + start, install_plan.end(), [&](auto& action) {
return action.spec == spec;
});
Assert::IsTrue(it != install_plan.end());
return (int)(it - install_plan.begin());
};
int a_64_1 = iterator_pos(spec_a_64.package_spec, 0), a_86_1 = iterator_pos(spec_a_86.package_spec, 0),
b_64 = iterator_pos(spec_b_64.package_spec, 0), b_86 = iterator_pos(spec_b_86.package_spec, 0),
c_64 = iterator_pos(spec_c_64.package_spec, 0), c_86 = iterator_pos(spec_c_86.package_spec, 0),
a_64_2 = iterator_pos(spec_a_64.package_spec, a_64_1 + 1),
a_86_2 = iterator_pos(spec_a_86.package_spec, a_86_1 + 1);*/
remove_plan_check(&install_plan[0], "a", Triplet::X64_WINDOWS);
remove_plan_check(&install_plan[1], "a");
features_check(&install_plan[2], "b", {"core"}, Triplet::X64_WINDOWS);
features_check(&install_plan[3], "a", {"one", "core"}, Triplet::X64_WINDOWS);
features_check(&install_plan[4], "c", {"core"}, Triplet::X64_WINDOWS);
features_check(&install_plan[5], "b", {"core"});
features_check(&install_plan[6], "a", {"one", "core"});
features_check(&install_plan[7], "c", {"core"});
}
};
}

View File

@ -372,25 +372,27 @@ namespace UnitTest1
TEST_METHOD(package_spec_parse)
{
vcpkg::ExpectedT<vcpkg::PackageSpec, vcpkg::PackageSpecParseResult> spec =
vcpkg::PackageSpec::from_string("zlib", vcpkg::Triplet::X86_WINDOWS);
vcpkg::ExpectedT<vcpkg::FullPackageSpec, vcpkg::PackageSpecParseResult> spec =
vcpkg::FullPackageSpec::from_string("zlib", vcpkg::Triplet::X86_WINDOWS);
Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, spec.error());
Assert::AreEqual("zlib", spec.get()->name().c_str());
Assert::AreEqual(vcpkg::Triplet::X86_WINDOWS.canonical_name(), spec.get()->triplet().canonical_name());
Assert::AreEqual("zlib", spec.get()->package_spec.name().c_str());
Assert::AreEqual(vcpkg::Triplet::X86_WINDOWS.canonical_name(),
spec.get()->package_spec.triplet().canonical_name());
}
TEST_METHOD(package_spec_parse_with_arch)
{
vcpkg::ExpectedT<vcpkg::PackageSpec, vcpkg::PackageSpecParseResult> spec =
vcpkg::PackageSpec::from_string("zlib:x64-uwp", vcpkg::Triplet::X86_WINDOWS);
vcpkg::ExpectedT<vcpkg::FullPackageSpec, vcpkg::PackageSpecParseResult> spec =
vcpkg::FullPackageSpec::from_string("zlib:x64-uwp", vcpkg::Triplet::X86_WINDOWS);
Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, spec.error());
Assert::AreEqual("zlib", spec.get()->name().c_str());
Assert::AreEqual(vcpkg::Triplet::X64_UWP.canonical_name(), spec.get()->triplet().canonical_name());
Assert::AreEqual("zlib", spec.get()->package_spec.name().c_str());
Assert::AreEqual(vcpkg::Triplet::X64_UWP.canonical_name(),
spec.get()->package_spec.triplet().canonical_name());
}
TEST_METHOD(package_spec_parse_with_multiple_colon)
{
auto ec = vcpkg::PackageSpec::from_string("zlib:x86-uwp:", vcpkg::Triplet::X86_WINDOWS).error();
auto ec = vcpkg::FullPackageSpec::from_string("zlib:x86-uwp:", vcpkg::Triplet::X86_WINDOWS).error();
Assert::AreEqual(vcpkg::PackageSpecParseResult::TOO_MANY_COLONS, ec);
}

View File

@ -102,6 +102,17 @@ namespace vcpkg::Build
paths.get_filesystem().write_contents(binary_control_file, Strings::serialize(bpgh));
}
static void create_binary_feature_control_file(const VcpkgPaths& paths,
const SourceParagraph& source_paragraph,
const FeatureParagraph& feature_paragraph,
const Triplet& triplet,
const BuildInfo& build_info)
{
BinaryParagraph bpgh = BinaryParagraph(source_paragraph, feature_paragraph, triplet);
const fs::path binary_control_file = paths.packages / bpgh.dir() / "CONTROL";
paths.get_filesystem().write_contents(binary_control_file, Strings::serialize(bpgh));
}
ExtendedBuildResult build_package(const VcpkgPaths& paths,
const BuildPackageConfig& config,
const StatusParagraphs& status_db)
@ -135,17 +146,36 @@ namespace vcpkg::Build
const Toolset& toolset = paths.get_toolset(pre_build_info.platform_toolset);
const auto cmd_set_environment = make_build_env_cmd(pre_build_info, toolset);
std::string features;
if (g_feature_packages)
{
if (config.feature_list)
{
for (auto&& feature : *config.feature_list)
{
features.append(feature + ";");
}
if (features.size() > 0)
{
features.pop_back();
}
}
}
const std::wstring cmd_launch_cmake = make_cmake_cmd(
cmake_exe_path,
ports_cmake_script_path,
{{L"CMD", L"BUILD"},
{L"PORT", config.src.name},
{L"CURRENT_PORT_DIR", config.port_dir / "/."},
{L"TARGET_TRIPLET", triplet.canonical_name()},
{L"VCPKG_PLATFORM_TOOLSET", toolset.version},
{L"VCPKG_USE_HEAD_VERSION", to_bool(config.build_package_options.use_head_version) ? L"1" : L"0"},
{L"_VCPKG_NO_DOWNLOADS", !to_bool(config.build_package_options.allow_downloads) ? L"1" : L"0"},
{L"GIT", git_exe_path}});
{
{L"CMD", L"BUILD"},
{L"PORT", config.src.name},
{L"CURRENT_PORT_DIR", config.port_dir / "/."},
{L"TARGET_TRIPLET", triplet.canonical_name()},
{L"VCPKG_PLATFORM_TOOLSET", toolset.version},
{L"VCPKG_USE_HEAD_VERSION", to_bool(config.build_package_options.use_head_version) ? L"1" : L"0"},
{L"_VCPKG_NO_DOWNLOADS", !to_bool(config.build_package_options.allow_downloads) ? L"1" : L"0"},
{L"GIT", git_exe_path},
{L"FEATURES", features},
});
const std::wstring command = Strings::wformat(LR"(%s && %s)", cmd_set_environment, cmd_launch_cmake);
@ -170,7 +200,21 @@ namespace vcpkg::Build
{
return {BuildResult::POST_BUILD_CHECKS_FAILED, {}};
}
if (g_feature_packages)
{
if (config.feature_list)
{
for (auto&& feature : *config.feature_list)
{
for (auto&& f_pgh : config.scf->feature_paragraphs)
{
if (f_pgh->name == feature)
create_binary_feature_control_file(
paths, *config.scf->core_paragraph, *f_pgh, triplet, build_info);
}
}
}
}
create_binary_control_file(paths, config.src, triplet, build_info);
// const fs::path port_buildtrees_dir = paths.buildtrees / spec.name;

View File

@ -12,6 +12,8 @@
namespace vcpkg::Dependencies
{
bool operator==(const ClusterPtr& l, const ClusterPtr& r) { return l.ptr == r.ptr; }
std::vector<PackageSpec> AnyParagraph::dependencies(const Triplet& triplet) const
{
auto to_package_specs = [&](const std::vector<std::string>& dependencies_as_string) {
@ -54,6 +56,20 @@ namespace vcpkg::Dependencies
{
}
InstallPlanAction::InstallPlanAction(const PackageSpec& spec,
const SourceControlFile& any_paragraph,
const std::unordered_set<std::string>& features,
const RequestType& request_type)
: InstallPlanAction()
{
this->spec = spec;
this->request_type = request_type;
this->plan_type = InstallPlanType::BUILD_AND_INSTALL;
this->any_paragraph.source_control_file = &any_paragraph;
this->feature_list = features;
}
InstallPlanAction::InstallPlanAction(const PackageSpec& spec,
const AnyParagraph& any_paragraph,
const RequestType& request_type)
@ -139,20 +155,53 @@ namespace vcpkg::Dependencies
return left->spec.name() < right->spec.name();
}
std::vector<InstallPlanAction> create_install_plan(const VcpkgPaths& paths,
MapPortFile::MapPortFile(const std::unordered_map<PackageSpec, SourceControlFile>& map) : ports(map) {}
const SourceControlFile& MapPortFile::get_control_file(const PackageSpec& spec) const
{
auto scf = ports.find(spec);
if (scf == ports.end())
{
Checks::exit_fail(VCPKG_LINE_INFO);
}
return scf->second;
}
PathsPortFile::PathsPortFile(const VcpkgPaths& paths) : ports(paths) {}
const SourceControlFile& PathsPortFile::get_control_file(const PackageSpec& spec) const
{
std::unordered_map<PackageSpec, SourceControlFile>::iterator cache_it = cache.find(spec);
if (cache_it != cache.end())
{
return cache_it->second;
}
Parse::ParseExpected<SourceControlFile> source_control_file =
Paragraphs::try_load_port(ports.get_filesystem(), ports.port_dir(spec));
if (auto scf = source_control_file.get())
{
auto it = cache.emplace(spec, std::move(*scf->get()));
return it.first->second;
}
print_error_message(source_control_file.error());
Checks::exit_fail(VCPKG_LINE_INFO);
}
std::vector<InstallPlanAction> create_install_plan(const PortFileProvider& port_file_provider,
const std::vector<PackageSpec>& specs,
const StatusParagraphs& status_db)
{
struct InstallAdjacencyProvider final : Graphs::AdjacencyProvider<PackageSpec, InstallPlanAction>
{
const VcpkgPaths& paths;
const PortFileProvider& port_file_provider;
const StatusParagraphs& status_db;
const std::unordered_set<PackageSpec>& specs_as_set;
InstallAdjacencyProvider(const VcpkgPaths& p,
InstallAdjacencyProvider(const PortFileProvider& port_file_provider,
const StatusParagraphs& s,
const std::unordered_set<PackageSpec>& specs_as_set)
: paths(p), status_db(s), specs_as_set(specs_as_set)
: port_file_provider(port_file_provider), status_db(s), specs_as_set(specs_as_set)
{
}
@ -169,23 +218,14 @@ namespace vcpkg::Dependencies
: RequestType::AUTO_SELECTED;
auto it = status_db.find_installed(spec);
if (it != status_db.end()) return InstallPlanAction{spec, {*it->get(), nullopt, nullopt}, request_type};
Expected<BinaryParagraph> maybe_bpgh = Paragraphs::try_load_cached_package(paths, spec);
if (auto bpgh = maybe_bpgh.get())
return InstallPlanAction{spec, {nullopt, *bpgh, nullopt}, request_type};
auto maybe_scf = Paragraphs::try_load_port(paths.get_filesystem(), paths.port_dir(spec));
if (auto scf = maybe_scf.get())
return InstallPlanAction{spec, {nullopt, nullopt, *scf->get()->core_paragraph}, request_type};
print_error_message(maybe_scf.error());
Checks::exit_fail(VCPKG_LINE_INFO);
return InstallPlanAction{
spec, {nullopt, nullopt, *port_file_provider.get_control_file(spec).core_paragraph}, request_type};
}
};
const std::unordered_set<PackageSpec> specs_as_set(specs.cbegin(), specs.cend());
std::vector<InstallPlanAction> toposort =
Graphs::topological_sort(specs, InstallAdjacencyProvider{paths, status_db, specs_as_set});
Graphs::topological_sort(specs, InstallAdjacencyProvider{port_file_provider, status_db, specs_as_set});
Util::erase_remove_if(toposort, [](const InstallPlanAction& plan) {
return plan.request_type == RequestType::AUTO_SELECTED &&
plan.plan_type == InstallPlanType::ALREADY_INSTALLED;
@ -298,4 +338,229 @@ namespace vcpkg::Dependencies
Graphs::topological_sort(specs, ExportAdjacencyProvider{paths, status_db, specs_as_set});
return toposort;
}
std::vector<FeatureSpec> to_feature_specs(const std::vector<std::string>& depends, const Triplet& triplet)
{
std::vector<FeatureSpec> f_specs;
for (auto&& depend : depends)
{
int end = (int)depend.find(']');
if (end != std::string::npos)
{
int start = (int)depend.find('[');
auto feature_name = depend.substr(start + 1, end - start - 1);
auto package_name = depend.substr(0, start);
auto p_spec = PackageSpec::from_name_and_triplet(package_name, triplet).value_or_exit(VCPKG_LINE_INFO);
auto feature_spec = FeatureSpec{p_spec, feature_name};
f_specs.emplace_back(std::move(feature_spec));
}
else
{
auto p_spec = PackageSpec::from_name_and_triplet(depend, triplet).value_or_exit(VCPKG_LINE_INFO);
auto feature_spec = FeatureSpec{p_spec, ""};
f_specs.emplace_back(std::move(feature_spec));
}
}
return f_specs;
}
bool mark_plus(const std::string& feature,
Cluster& cluster,
std::unordered_map<PackageSpec, Cluster>& pkg_to_cluster,
GraphPlan& graph_plan)
{
auto it = cluster.edges.find(feature);
std::string updated_feature = feature;
if (updated_feature == "")
{
updated_feature = "core";
it = cluster.edges.find("core");
}
if (it == cluster.edges.end())
{
Checks::unreachable(VCPKG_LINE_INFO);
}
if (cluster.edges[updated_feature].plus) return true;
if (cluster.original_features.find(updated_feature) == cluster.original_features.end())
{
cluster.transient_uninstalled = true;
}
if (!cluster.transient_uninstalled)
{
return false;
}
cluster.edges[updated_feature].plus = true;
if (!cluster.original_features.empty())
{
mark_minus(cluster, pkg_to_cluster, graph_plan);
}
graph_plan.install_graph.add_vertex({&cluster});
auto& tracked = cluster.to_install_features;
tracked.insert(updated_feature);
if (tracked.find("core") == tracked.end() && tracked.find("") == tracked.end())
{
cluster.to_install_features.insert("core");
for (auto&& depend : cluster.edges["core"].build_edges)
{
auto& depend_cluster = pkg_to_cluster[depend.spec];
mark_plus(depend.feature_name, depend_cluster, pkg_to_cluster, graph_plan);
graph_plan.install_graph.add_edge({&cluster}, {&depend_cluster});
}
}
for (auto&& depend : cluster.edges[updated_feature].build_edges)
{
auto& depend_cluster = pkg_to_cluster[depend.spec];
mark_plus(depend.feature_name, depend_cluster, pkg_to_cluster, graph_plan);
if (&depend_cluster == &cluster) continue;
graph_plan.install_graph.add_edge({&cluster}, {&depend_cluster});
}
return true;
}
void mark_minus(Cluster& cluster, std::unordered_map<PackageSpec, Cluster>& pkg_to_cluster, GraphPlan& graph_plan)
{
if (cluster.will_remove) return;
cluster.will_remove = true;
graph_plan.remove_graph.add_vertex({&cluster});
for (auto&& pair : cluster.edges)
{
auto& remove_edges_edges = pair.second.remove_edges;
for (auto&& depend : remove_edges_edges)
{
auto& depend_cluster = pkg_to_cluster[depend.spec];
graph_plan.remove_graph.add_edge({&cluster}, {&depend_cluster});
depend_cluster.transient_uninstalled = true;
mark_minus(depend_cluster, pkg_to_cluster, graph_plan);
}
}
for (auto&& original_feature : cluster.original_features)
{
cluster.transient_uninstalled = true;
mark_plus(original_feature, cluster, pkg_to_cluster, graph_plan);
}
}
std::vector<AnyAction> create_feature_install_plan(const std::unordered_map<PackageSpec, SourceControlFile>& map,
const std::vector<FullPackageSpec>& specs,
const StatusParagraphs& status_db)
{
std::unordered_map<PackageSpec, Cluster> pkg_spec_to_package_node;
for (const auto& it : map)
{
Cluster& node = pkg_spec_to_package_node[it.first];
node.spec = it.first;
FeatureNodeEdges core_dependencies;
auto core_depends = filter_dependencies(it.second.core_paragraph->depends, node.spec.triplet());
core_dependencies.build_edges = to_feature_specs(core_depends, node.spec.triplet());
node.edges["core"] = std::move(core_dependencies);
for (const auto& feature : it.second.feature_paragraphs)
{
FeatureNodeEdges added_edges;
auto depends = filter_dependencies(feature->depends, node.spec.triplet());
added_edges.build_edges = to_feature_specs(depends, node.spec.triplet());
node.edges.emplace(feature->name, std::move(added_edges));
}
node.source_control_file = &it.second;
}
for (auto&& status_paragraph : get_installed_ports(status_db))
{
auto& spec = status_paragraph->package.spec;
auto& status_paragraph_feature = status_paragraph->package.feature;
Cluster& cluster = pkg_spec_to_package_node[spec];
cluster.transient_uninstalled = false;
auto reverse_edges =
to_feature_specs(status_paragraph->package.depends, status_paragraph->package.spec.triplet());
for (auto&& dependency : reverse_edges)
{
auto pkg_node = pkg_spec_to_package_node.find(dependency.spec);
auto depends_name = dependency.feature_name;
if (depends_name == "")
{
for (auto&& default_feature : status_paragraph->package.default_features)
{
auto& target_node = pkg_node->second.edges[default_feature];
target_node.remove_edges.emplace_back(FeatureSpec{spec, status_paragraph_feature});
}
depends_name = "core";
}
auto& target_node = pkg_node->second.edges[depends_name];
target_node.remove_edges.emplace_back(FeatureSpec{spec, status_paragraph_feature});
}
cluster.status_paragraphs.emplace_back(*status_paragraph);
if (status_paragraph_feature == "")
{
cluster.original_features.insert("core");
}
else
{
cluster.original_features.insert(status_paragraph_feature);
}
}
GraphPlan graph_plan;
for (auto&& spec : specs)
{
Cluster& spec_cluster = pkg_spec_to_package_node[spec.package_spec];
for (auto&& feature : spec.features)
{
mark_plus(feature, spec_cluster, pkg_spec_to_package_node, graph_plan);
}
}
Graphs::GraphAdjacencyProvider<ClusterPtr> adjacency_remove_graph(graph_plan.remove_graph.adjacency_list());
auto remove_vertex_list = graph_plan.remove_graph.vertex_list();
auto remove_toposort = Graphs::topological_sort(remove_vertex_list, adjacency_remove_graph);
Graphs::GraphAdjacencyProvider<ClusterPtr> adjacency_install_graph(graph_plan.install_graph.adjacency_list());
auto insert_vertex_list = graph_plan.install_graph.vertex_list();
auto insert_toposort = Graphs::topological_sort(insert_vertex_list, adjacency_install_graph);
std::vector<AnyAction> install_plan;
for (auto&& like_cluster : remove_toposort)
{
auto scf = *like_cluster.ptr->source_control_file.get();
AnyAction any_plan;
any_plan.remove_plan = RemovePlanAction{
PackageSpec::from_name_and_triplet(scf->core_paragraph->name, like_cluster.ptr->spec.triplet())
.value_or_exit(VCPKG_LINE_INFO),
RemovePlanType::REMOVE,
RequestType::AUTO_SELECTED};
install_plan.emplace_back(std::move(any_plan));
}
for (auto&& like_cluster : insert_toposort)
{
if (!like_cluster.ptr->transient_uninstalled) continue;
auto scf = *like_cluster.ptr->source_control_file.get();
auto pkg_spec =
PackageSpec::from_name_and_triplet(scf->core_paragraph->name, like_cluster.ptr->spec.triplet())
.value_or_exit(VCPKG_LINE_INFO);
auto action =
InstallPlanAction{pkg_spec, *scf, like_cluster.ptr->to_install_features, RequestType::AUTO_SELECTED};
AnyAction any_plan;
any_plan.install_plan = std::move(action);
install_plan.emplace_back(std::move(any_plan));
}
return install_plan;
}
}

View File

@ -12,10 +12,10 @@ namespace vcpkg::Input
CStringView example_text)
{
const std::string as_lowercase = Strings::ascii_to_lowercase(package_spec_as_string);
auto expected_spec = PackageSpec::from_string(as_lowercase, default_triplet);
auto expected_spec = FullPackageSpec::from_string(as_lowercase, default_triplet);
if (auto spec = expected_spec.get())
{
return PackageSpec{*spec};
return PackageSpec{spec->package_spec};
}
// Intentionally show the lowercased string
@ -39,26 +39,11 @@ namespace vcpkg::Input
const Triplet& default_triplet,
CStringView example_text)
{
int left_pos = (int)full_package_spec_as_string.find('[');
if (left_pos == std::string::npos)
const std::string as_lowercase = Strings::ascii_to_lowercase(full_package_spec_as_string);
auto expected_spec = FullPackageSpec::from_string(as_lowercase, default_triplet);
if (auto spec = expected_spec.get())
{
return FullPackageSpec{
check_and_get_package_spec(full_package_spec_as_string, default_triplet, example_text)};
}
int right_pos = (int)full_package_spec_as_string.find(']');
if (left_pos >= right_pos)
{
System::println(System::Color::error, "Error: Argument is not formatted correctly \"%s\"");
Checks::exit_fail(VCPKG_LINE_INFO);
}
std::string package_spec_as_string = full_package_spec_as_string.substr(0, left_pos);
const std::string as_lowercase = Strings::ascii_to_lowercase(package_spec_as_string);
auto expected_spec = PackageSpec::from_string(as_lowercase, default_triplet);
if (auto&& spec = expected_spec.get())
{
return {*spec,
parse_comma_list(full_package_spec_as_string.substr(left_pos + 1, right_pos - left_pos - 1))};
return *spec;
}
// Intentionally show the lowercased string

View File

@ -6,18 +6,6 @@
namespace vcpkg::Parse
{
static Optional<std::string> get_field(const std::unordered_map<std::string, std::string>& fields,
const std::string& fieldname)
{
auto it = fields.find(fieldname);
if (it == fields.end())
{
return nullopt;
}
return it->second;
}
static Optional<std::string> remove_field(std::unordered_map<std::string, std::string>* fields,
const std::string& fieldname)
{

View File

@ -22,6 +22,7 @@
<ClCompile Include="..\src\tests_arguments.cpp" />
<ClCompile Include="..\src\tests_dependencies.cpp" />
<ClCompile Include="..\src\tests_paragraph.cpp" />
<ClCompile Include="..\src\test_install_plan.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\vcpkglib\vcpkglib.vcxproj">

View File

@ -24,5 +24,8 @@
<ClCompile Include="..\src\tests_arguments.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\test_install_plan.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>