aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Shaw <danielshaw1212@gmail.com>2017-07-24 16:11:22 -0700
committerGitHub <noreply@github.com>2017-07-24 16:11:22 -0700
commitb277b4dda3a2793fd59a6cca5de96f8bc65f1357 (patch)
tree67299d7ae4d032948d4d65a2f494b61fac025b0a
parent3c841c6128ebfe8e99a372f2907bd985b533a799 (diff)
parent59389ca236b005922cf1101f66c957d2396f6371 (diff)
downloadvcpkg-b277b4dda3a2793fd59a6cca5de96f8bc65f1357.tar.gz
vcpkg-b277b4dda3a2793fd59a6cca5de96f8bc65f1357.zip
Merge pull request #1461 from Microsoft/create_install_tests
feature packages graph algorithm
-rw-r--r--toolsrc/include/BinaryParagraph.h7
-rw-r--r--toolsrc/include/PackageSpec.h6
-rw-r--r--toolsrc/include/SourceParagraph.h1
-rw-r--r--toolsrc/include/vcpkg_Build.h23
-rw-r--r--toolsrc/include/vcpkg_Commands.h1
-rw-r--r--toolsrc/include/vcpkg_Dependencies.h104
-rw-r--r--toolsrc/include/vcpkg_Graphs.h93
-rw-r--r--toolsrc/include/vcpkg_Util.h1
-rw-r--r--toolsrc/src/BinaryParagraph.cpp26
-rw-r--r--toolsrc/src/PackageSpec.cpp52
-rw-r--r--toolsrc/src/commands_ci.cpp4
-rw-r--r--toolsrc/src/commands_install.cpp40
-rw-r--r--toolsrc/src/commands_remove.cpp2
-rw-r--r--toolsrc/src/test_install_plan.cpp527
-rw-r--r--toolsrc/src/tests_paragraph.cpp20
-rw-r--r--toolsrc/src/vcpkg_Build.cpp62
-rw-r--r--toolsrc/src/vcpkg_Dependencies.cpp297
-rw-r--r--toolsrc/src/vcpkg_Input.cpp27
-rw-r--r--toolsrc/src/vcpkg_Parse.cpp12
-rw-r--r--toolsrc/vcpkgtest/vcpkgtest.vcxproj1
-rw-r--r--toolsrc/vcpkgtest/vcpkgtest.vcxproj.filters3
21 files changed, 1221 insertions, 88 deletions
diff --git a/toolsrc/include/BinaryParagraph.h b/toolsrc/include/BinaryParagraph.h
index 1c2edf790..f411b3c39 100644
--- a/toolsrc/include/BinaryParagraph.h
+++ b/toolsrc/include/BinaryParagraph.h
@@ -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);
-}
+} \ No newline at end of file
diff --git a/toolsrc/include/PackageSpec.h b/toolsrc/include/PackageSpec.h
index 62b6fc9de..15b5e5b9b 100644
--- a/toolsrc/include/PackageSpec.h
+++ b/toolsrc/include/PackageSpec.h
@@ -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);
diff --git a/toolsrc/include/SourceParagraph.h b/toolsrc/include/SourceParagraph.h
index e85884b51..7ddf999cc 100644
--- a/toolsrc/include/SourceParagraph.h
+++ b/toolsrc/include/SourceParagraph.h
@@ -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);
diff --git a/toolsrc/include/vcpkg_Build.h b/toolsrc/include/vcpkg_Build.h
index 9a4e2baeb..c4f3e6746 100644
--- a/toolsrc/include/vcpkg_Build.h
+++ b/toolsrc/include/vcpkg_Build.h
@@ -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,
diff --git a/toolsrc/include/vcpkg_Commands.h b/toolsrc/include/vcpkg_Commands.h
index 67319f240..8348a64e4 100644
--- a/toolsrc/include/vcpkg_Commands.h
+++ b/toolsrc/include/vcpkg_Commands.h
@@ -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
diff --git a/toolsrc/include/vcpkg_Dependencies.h b/toolsrc/include/vcpkg_Dependencies.h
index 018c4f5cf..e3af0fd28 100644
--- a/toolsrc/include/vcpkg_Dependencies.h
+++ b/toolsrc/include/vcpkg_Dependencies.h
@@ -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);
+}
diff --git a/toolsrc/include/vcpkg_Graphs.h b/toolsrc/include/vcpkg_Graphs.h
index 3c8c024c2..13c0a7136 100644
--- a/toolsrc/include/vcpkg_Graphs.h
+++ b/toolsrc/include/vcpkg_Graphs.h
@@ -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;
+ };
}
diff --git a/toolsrc/include/vcpkg_Util.h b/toolsrc/include/vcpkg_Util.h
index 1bd1bcc4a..671997e7e 100644
--- a/toolsrc/include/vcpkg_Util.h
+++ b/toolsrc/include/vcpkg_Util.h
@@ -1,5 +1,6 @@
#pragma once
+#include <map>
#include <utility>
#include <vector>
diff --git a/toolsrc/src/BinaryParagraph.cpp b/toolsrc/src/BinaryParagraph.cpp
index af76c6b29..b6f3e8a87 100644
--- a/toolsrc/src/BinaryParagraph.cpp
+++ b/toolsrc/src/BinaryParagraph.cpp
@@ -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: ");
diff --git a/toolsrc/src/PackageSpec.cpp b/toolsrc/src/PackageSpec.cpp
index ab005f255..a7e5648cd 100644
--- a/toolsrc/src/PackageSpec.cpp
+++ b/toolsrc/src/PackageSpec.cpp
@@ -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)
+ {
+ 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)
{
- return from_name_and_triplet(spec_as_string, default_triplet);
+ 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,
diff --git a/toolsrc/src/commands_ci.cpp b/toolsrc/src/commands_ci.cpp
index 7ffc7577a..f781adf69 100644
--- a/toolsrc/src/commands_ci.cpp
+++ b/toolsrc/src/commands_ci.cpp
@@ -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;
diff --git a/toolsrc/src/commands_install.cpp b/toolsrc/src/commands_install.cpp
index 73b3e9eab..2ce5b6c62 100644
--- a/toolsrc/src/commands_install.cpp
+++ b/toolsrc/src/commands_install.cpp
@@ -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
diff --git a/toolsrc/src/commands_remove.cpp b/toolsrc/src/commands_remove.cpp
index e2b5d12a1..eabf2b9ae 100644
--- a/toolsrc/src/commands_remove.cpp
+++ b/toolsrc/src/commands_remove.cpp
@@ -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());
diff --git a/toolsrc/src/test_install_plan.cpp b/toolsrc/src/test_install_plan.cpp
new file mode 100644
index 000000000..d02af5662
--- /dev/null
+++ b/toolsrc/src/test_install_plan.cpp
@@ -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"});
+ }
+ };
+} \ No newline at end of file
diff --git a/toolsrc/src/tests_paragraph.cpp b/toolsrc/src/tests_paragraph.cpp
index 2a53cc8b4..dd9a40160 100644
--- a/toolsrc/src/tests_paragraph.cpp
+++ b/toolsrc/src/tests_paragraph.cpp
@@ -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);
}
diff --git a/toolsrc/src/vcpkg_Build.cpp b/toolsrc/src/vcpkg_Build.cpp
index c794b5ede..a0d690f37 100644
--- a/toolsrc/src/vcpkg_Build.cpp
+++ b/toolsrc/src/vcpkg_Build.cpp
@@ -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;
diff --git a/toolsrc/src/vcpkg_Dependencies.cpp b/toolsrc/src/vcpkg_Dependencies.cpp
index bbf807c95..820e51b33 100644
--- a/toolsrc/src/vcpkg_Dependencies.cpp
+++ b/toolsrc/src/vcpkg_Dependencies.cpp
@@ -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) {
@@ -55,6 +57,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)
: InstallPlanAction()
@@ -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;
+ }
}
diff --git a/toolsrc/src/vcpkg_Input.cpp b/toolsrc/src/vcpkg_Input.cpp
index fdedd5507..f4e9a07c2 100644
--- a/toolsrc/src/vcpkg_Input.cpp
+++ b/toolsrc/src/vcpkg_Input.cpp
@@ -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)
- {
- 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())
+ 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 {*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
diff --git a/toolsrc/src/vcpkg_Parse.cpp b/toolsrc/src/vcpkg_Parse.cpp
index b63ce41a9..659af2939 100644
--- a/toolsrc/src/vcpkg_Parse.cpp
+++ b/toolsrc/src/vcpkg_Parse.cpp
@@ -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)
{
diff --git a/toolsrc/vcpkgtest/vcpkgtest.vcxproj b/toolsrc/vcpkgtest/vcpkgtest.vcxproj
index 26d5c98eb..ca66260d4 100644
--- a/toolsrc/vcpkgtest/vcpkgtest.vcxproj
+++ b/toolsrc/vcpkgtest/vcpkgtest.vcxproj
@@ -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">
diff --git a/toolsrc/vcpkgtest/vcpkgtest.vcxproj.filters b/toolsrc/vcpkgtest/vcpkgtest.vcxproj.filters
index f1230cfce..e376e59f4 100644
--- a/toolsrc/vcpkgtest/vcpkgtest.vcxproj.filters
+++ b/toolsrc/vcpkgtest/vcpkgtest.vcxproj.filters
@@ -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> \ No newline at end of file