aboutsummaryrefslogtreecommitdiff
path: root/toolsrc/src
diff options
context:
space:
mode:
authorRobert Schumacher <roschuma@microsoft.com>2017-08-22 15:14:59 -0700
committerGitHub <noreply@github.com>2017-08-22 15:14:59 -0700
commit651ab5cef2c9742869185a181e1db529dc937d21 (patch)
treed9b2239e97a6351c1a6a28cf7c8cf0cadf8c54e2 /toolsrc/src
parent6035c3228bcef651d342b3a31827157ad2ed6a85 (diff)
parent92dd1b77ed043da376c86874aacc1233270fedae (diff)
downloadvcpkg-651ab5cef2c9742869185a181e1db529dc937d21.tar.gz
vcpkg-651ab5cef2c9742869185a181e1db529dc937d21.zip
Merge pull request #1566 from Microsoft/feature_package_end_to_end
end to end hdf5 feature packages
Diffstat (limited to 'toolsrc/src')
-rw-r--r--toolsrc/src/BinaryParagraph.cpp12
-rw-r--r--toolsrc/src/PackageSpec.cpp165
-rw-r--r--toolsrc/src/Paragraphs.cpp16
-rw-r--r--toolsrc/src/SourceParagraph.cpp87
-rw-r--r--toolsrc/src/StatusParagraphs.cpp26
-rw-r--r--toolsrc/src/VcpkgPaths.cpp1
-rw-r--r--toolsrc/src/commands_depends.cpp4
-rw-r--r--toolsrc/src/commands_install.cpp323
-rw-r--r--toolsrc/src/commands_remove.cpp27
-rw-r--r--toolsrc/src/commands_search.cpp2
-rw-r--r--toolsrc/src/test_install_plan.cpp547
-rw-r--r--toolsrc/src/tests_dependencies.cpp17
-rw-r--r--toolsrc/src/tests_package_spec.cpp119
-rw-r--r--toolsrc/src/tests_paragraph.cpp56
-rw-r--r--toolsrc/src/vcpkg_Build.cpp46
-rw-r--r--toolsrc/src/vcpkg_Dependencies.cpp423
-rw-r--r--toolsrc/src/vcpkg_Parse.cpp33
-rw-r--r--toolsrc/src/vcpkglib.cpp2
18 files changed, 1190 insertions, 716 deletions
diff --git a/toolsrc/src/BinaryParagraph.cpp b/toolsrc/src/BinaryParagraph.cpp
index 49e9d58e5..9abd388b9 100644
--- a/toolsrc/src/BinaryParagraph.cpp
+++ b/toolsrc/src/BinaryParagraph.cpp
@@ -87,7 +87,17 @@ namespace vcpkg
this->depends = filter_dependencies(fpgh.depends, triplet);
}
- std::string BinaryParagraph::displayname() const { return this->spec.to_string(); }
+ std::string BinaryParagraph::displayname() const
+ {
+ if (this->feature == "")
+ {
+ return this->spec.name() + "[core]:" + this->spec.triplet().to_string();
+ }
+ else
+ {
+ return this->spec.name() + "[" + this->feature + "]:" + this->spec.triplet().to_string();
+ }
+ }
std::string BinaryParagraph::dir() const { return this->spec.dir(); }
diff --git a/toolsrc/src/PackageSpec.cpp b/toolsrc/src/PackageSpec.cpp
index a7e5648cd..a43bc5ff5 100644
--- a/toolsrc/src/PackageSpec.cpp
+++ b/toolsrc/src/PackageSpec.cpp
@@ -1,8 +1,11 @@
#include "pch.h"
#include "PackageSpec.h"
+#include "vcpkg_Parse.h"
#include "vcpkg_Util.h"
+using vcpkg::Parse::parse_comma_list;
+
namespace vcpkg
{
static bool is_valid_package_spec_char(char c)
@@ -10,55 +13,69 @@ namespace vcpkg
return (c == '-') || isdigit(c) || (isalpha(c) && islower(c)) || (c == '[') || (c == ']');
}
- ExpectedT<FullPackageSpec, PackageSpecParseResult> FullPackageSpec::from_string(const std::string& spec_as_string,
- const Triplet& default_triplet)
+ std::string FeatureSpec::to_string() const
{
- auto pos = spec_as_string.find(':');
- auto pos_l_bracket = spec_as_string.find('[');
- auto pos_r_bracket = spec_as_string.find(']');
+ if (feature().empty()) return spec().to_string();
+ return Strings::format("%s[%s]:%s", name(), feature(), triplet());
+ }
- 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)
+ std::vector<FeatureSpec> FeatureSpec::from_strings_and_triplet(const std::vector<std::string>& depends,
+ const Triplet& triplet)
+ {
+ std::vector<FeatureSpec> f_specs;
+ for (auto&& depend : depends)
{
- if (pos_r_bracket == std::string::npos || pos_l_bracket >= pos_r_bracket)
+ auto maybe_spec = ParsedSpecifier::from_string(depend);
+ if (auto spec = maybe_spec.get())
{
- return PackageSpecParseResult::INVALID_CHARACTERS;
+ Checks::check_exit(VCPKG_LINE_INFO,
+ spec->triplet.empty(),
+ "error: triplets cannot currently be specified in this context: %s",
+ depend);
+ PackageSpec pspec =
+ PackageSpec::from_name_and_triplet(spec->name, triplet).value_or_exit(VCPKG_LINE_INFO);
+
+ for (auto&& feature : spec->features)
+ f_specs.push_back(FeatureSpec{pspec, feature});
+
+ if (spec->features.empty()) f_specs.push_back(FeatureSpec{pspec, ""});
}
- 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)
+ else
{
- return PackageSpecParseResult::INVALID_CHARACTERS;
+ Checks::exit_with_message(VCPKG_LINE_INFO,
+ "error while parsing feature list: %s: %s",
+ vcpkg::to_string(maybe_spec.error()),
+ depend);
}
- 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);
}
+ return f_specs;
+ }
- auto pos2 = spec_as_string.find(':', pos + 1);
- if (pos2 != std::string::npos)
+ std::vector<FeatureSpec> FullPackageSpec::to_feature_specs(const std::vector<FullPackageSpec>& specs)
+ {
+ std::vector<FeatureSpec> ret;
+ for (auto&& spec : specs)
{
- return PackageSpecParseResult::TOO_MANY_COLONS;
+ ret.emplace_back(spec.package_spec, "");
+ for (auto&& feature : spec.features)
+ ret.emplace_back(spec.package_spec, feature);
}
- return f;
+ return ret;
+ }
+
+ ExpectedT<FullPackageSpec, PackageSpecParseResult> FullPackageSpec::from_string(const std::string& spec_as_string,
+ const Triplet& default_triplet)
+ {
+ auto res = ParsedSpecifier::from_string(spec_as_string);
+ if (auto p = res.get())
+ {
+ FullPackageSpec fspec;
+ Triplet t = p->triplet.empty() ? default_triplet : Triplet::from_canonical_name(p->triplet);
+ fspec.package_spec = PackageSpec::from_name_and_triplet(p->name, t).value_or_exit(VCPKG_LINE_INFO);
+ fspec.features = std::move(p->features);
+ return fspec;
+ }
+ return res.error();
}
ExpectedT<PackageSpec, PackageSpecParseResult> PackageSpec::from_name_and_triplet(const std::string& name,
@@ -81,11 +98,7 @@ namespace vcpkg
std::string PackageSpec::dir() const { return Strings::format("%s_%s", this->m_name, this->m_triplet); }
- std::string PackageSpec::to_string(const std::string& name, const Triplet& triplet)
- {
- return Strings::format("%s:%s", name, triplet);
- }
- std::string PackageSpec::to_string() const { return to_string(this->name(), this->triplet()); }
+ std::string PackageSpec::to_string() const { return Strings::format("%s:%s", this->name(), this->triplet()); }
bool operator==(const PackageSpec& left, const PackageSpec& right)
{
@@ -93,4 +106,70 @@ namespace vcpkg
}
bool operator!=(const PackageSpec& left, const PackageSpec& right) { return !(left == right); }
+
+ ExpectedT<ParsedSpecifier, PackageSpecParseResult> ParsedSpecifier::from_string(const std::string& input)
+ {
+ auto pos = input.find(':');
+ auto pos_l_bracket = input.find('[');
+ auto pos_r_bracket = input.find(']');
+
+ ParsedSpecifier f;
+ if (pos == std::string::npos && pos_l_bracket == std::string::npos)
+ {
+ f.name = input;
+ 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 = input.substr(0, pos_l_bracket);
+ f.name = name;
+ f.features = parse_comma_list(input.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 = input.substr(0, pos);
+ f.triplet = input.substr(pos + 1);
+ f.name = name;
+ }
+ else
+ {
+ if (pos_r_bracket == std::string::npos || pos_l_bracket >= pos_r_bracket)
+ {
+ return PackageSpecParseResult::INVALID_CHARACTERS;
+ }
+ const std::string name = input.substr(0, pos_l_bracket);
+ f.features = parse_comma_list(input.substr(pos_l_bracket + 1, pos_r_bracket - pos_l_bracket - 1));
+ f.triplet = input.substr(pos + 1);
+ f.name = name;
+ }
+
+ auto pos2 = input.find(':', pos + 1);
+ if (pos2 != std::string::npos)
+ {
+ return PackageSpecParseResult::TOO_MANY_COLONS;
+ }
+ return f;
+ }
+
+ ExpectedT<Features, PackageSpecParseResult> Features::from_string(const std::string& name)
+ {
+ auto maybe_spec = ParsedSpecifier::from_string(name);
+ if (auto spec = maybe_spec.get())
+ {
+ Checks::check_exit(
+ VCPKG_LINE_INFO, spec->triplet.empty(), "error: triplet not allowed in specifier: %s", name);
+
+ Features f;
+ f.name = spec->name;
+ f.features = spec->features;
+ return f;
+ }
+
+ return maybe_spec.error();
+ }
}
diff --git a/toolsrc/src/Paragraphs.cpp b/toolsrc/src/Paragraphs.cpp
index 3749e919e..3a30f66a3 100644
--- a/toolsrc/src/Paragraphs.cpp
+++ b/toolsrc/src/Paragraphs.cpp
@@ -3,6 +3,7 @@
#include "ParagraphParseResult.h"
#include "Paragraphs.h"
#include "vcpkg_Files.h"
+#include "vcpkg_Util.h"
using namespace vcpkg::Parse;
@@ -226,14 +227,21 @@ namespace vcpkg::Paragraphs
return error_info;
}
- Expected<BinaryParagraph> try_load_cached_package(const VcpkgPaths& paths, const PackageSpec& spec)
+ Expected<BinaryControlFile> try_load_cached_control_package(const VcpkgPaths& paths, const PackageSpec& spec)
{
- Expected<std::unordered_map<std::string, std::string>> pghs =
- get_single_paragraph(paths.get_filesystem(), paths.package_dir(spec) / "CONTROL");
+ Expected<std::vector<std::unordered_map<std::string, std::string>>> pghs =
+ get_paragraphs(paths.get_filesystem(), paths.package_dir(spec) / "CONTROL");
if (auto p = pghs.get())
{
- return BinaryParagraph(*p);
+ BinaryControlFile bcf;
+ bcf.core_paragraph = BinaryParagraph(p->front());
+ p->erase(p->begin());
+
+ bcf.features =
+ Util::fmap(*p, [&](auto&& raw_feature) -> BinaryParagraph { return BinaryParagraph(raw_feature); });
+
+ return bcf;
}
return pghs.error();
diff --git a/toolsrc/src/SourceParagraph.cpp b/toolsrc/src/SourceParagraph.cpp
index a37567f3a..54f34cbd3 100644
--- a/toolsrc/src/SourceParagraph.cpp
+++ b/toolsrc/src/SourceParagraph.cpp
@@ -1,5 +1,6 @@
#include "pch.h"
+#include "PackageSpec.h"
#include "SourceParagraph.h"
#include "Triplet.h"
#include "vcpkg_Checks.h"
@@ -28,7 +29,11 @@ namespace vcpkg
static span<const std::string> get_list_of_valid_fields()
{
static const std::string valid_fields[] = {
- Fields::SOURCE, Fields::VERSION, Fields::DESCRIPTION, Fields::MAINTAINER, Fields::BUILD_DEPENDS,
+ Fields::SOURCE,
+ Fields::VERSION,
+ Fields::DESCRIPTION,
+ Fields::MAINTAINER,
+ Fields::BUILD_DEPENDS,
};
return valid_fields;
@@ -152,57 +157,52 @@ namespace vcpkg
return std::move(control_file);
}
+ Dependency Dependency::parse_dependency(std::string name, std::string qualifier)
+ {
+ Dependency dep;
+ dep.qualifier = qualifier;
+ if (auto maybe_features = Features::from_string(name))
+ dep.depend = *maybe_features.get();
+ else
+ Checks::exit_with_message(
+ VCPKG_LINE_INFO, "error while parsing dependency: %s: %s", to_string(maybe_features.error()), name);
+ return dep;
+ }
+
+ std::string Dependency::name() const
+ {
+ std::string str = this->depend.name;
+ if (this->depend.features.empty()) return str;
+
+ str += "[";
+ for (auto&& s : this->depend.features)
+ {
+ str += s + ",";
+ }
+ str.pop_back();
+ str += "]";
+ return str;
+ }
+
std::vector<Dependency> vcpkg::expand_qualified_dependencies(const std::vector<std::string>& depends)
{
return Util::fmap(depends, [&](const std::string& depend_string) -> Dependency {
auto pos = depend_string.find(' ');
- if (pos == std::string::npos) return {depend_string, ""};
+ if (pos == std::string::npos) return Dependency::parse_dependency(depend_string, "");
// expect of the form "\w+ \[\w+\]"
Dependency dep;
- dep.name = depend_string.substr(0, pos);
+
+ dep.depend.name = depend_string.substr(0, pos);
if (depend_string.c_str()[pos + 1] != '(' || depend_string[depend_string.size() - 1] != ')')
{
// Error, but for now just slurp the entire string.
- return {depend_string, ""};
+ return Dependency::parse_dependency(depend_string, "");
}
dep.qualifier = depend_string.substr(pos + 2, depend_string.size() - pos - 3);
return dep;
});
}
- std::vector<std::string> parse_comma_list(const std::string& str)
- {
- if (str.empty())
- {
- return {};
- }
-
- std::vector<std::string> out;
-
- size_t cur = 0;
- do
- {
- auto pos = str.find(',', cur);
- if (pos == std::string::npos)
- {
- out.push_back(str.substr(cur));
- break;
- }
- out.push_back(str.substr(cur, pos - cur));
-
- // skip comma and space
- ++pos;
- if (str[pos] == ' ')
- {
- ++pos;
- }
-
- cur = pos;
- } while (cur != std::string::npos);
-
- return out;
- }
-
std::vector<std::string> filter_dependencies(const std::vector<vcpkg::Dependency>& deps, const Triplet& t)
{
std::vector<std::string> ret;
@@ -210,13 +210,22 @@ namespace vcpkg
{
if (dep.qualifier.empty() || t.canonical_name().find(dep.qualifier) != std::string::npos)
{
- ret.push_back(dep.name);
+ ret.emplace_back(dep.name());
}
}
return ret;
}
- const std::string& to_string(const Dependency& dep) { return dep.name; }
+ std::vector<FeatureSpec> filter_dependencies_to_specs(const std::vector<Dependency>& deps, const Triplet& t)
+ {
+ return FeatureSpec::from_strings_and_triplet(filter_dependencies(deps, t), t);
+ }
+
+ const std::string to_string(const Dependency& dep)
+ {
+ std::string name = dep.name();
+ return name;
+ }
ExpectedT<Supports, std::vector<std::string>> Supports::parse(const std::vector<std::string>& strs)
{
diff --git a/toolsrc/src/StatusParagraphs.cpp b/toolsrc/src/StatusParagraphs.cpp
index 27f3c30a2..02ee61f75 100644
--- a/toolsrc/src/StatusParagraphs.cpp
+++ b/toolsrc/src/StatusParagraphs.cpp
@@ -27,6 +27,30 @@ namespace vcpkg
});
}
+ std::vector<std::unique_ptr<StatusParagraph>*> StatusParagraphs::find_all(const std::string& name,
+ const Triplet& triplet)
+ {
+ std::vector<std::unique_ptr<StatusParagraph>*> spghs;
+ for (auto&& p : *this)
+ {
+ if (p->package.spec.name() == name && p->package.spec.triplet() == triplet)
+ {
+ spghs.emplace_back(&p);
+ }
+ }
+ return spghs;
+ }
+
+ StatusParagraphs::iterator StatusParagraphs::find(const std::string& name,
+ const Triplet& triplet,
+ const std::string& feature)
+ {
+ return std::find_if(begin(), end(), [&](const std::unique_ptr<StatusParagraph>& pgh) {
+ const PackageSpec& spec = pgh->package.spec;
+ return spec.name() == name && spec.triplet() == triplet && pgh->package.feature == feature;
+ });
+ }
+
StatusParagraphs::const_iterator StatusParagraphs::find_installed(const std::string& name,
const Triplet& triplet) const
{
@@ -43,7 +67,7 @@ namespace vcpkg
{
Checks::check_exit(VCPKG_LINE_INFO, pgh != nullptr, "Inserted null paragraph");
const PackageSpec& spec = pgh->package.spec;
- auto ptr = find(spec.name(), spec.triplet());
+ auto ptr = find(spec.name(), spec.triplet(), pgh->package.feature);
if (ptr == end())
{
paragraphs.push_back(std::move(pgh));
diff --git a/toolsrc/src/VcpkgPaths.cpp b/toolsrc/src/VcpkgPaths.cpp
index 60204bcdd..4be636650 100644
--- a/toolsrc/src/VcpkgPaths.cpp
+++ b/toolsrc/src/VcpkgPaths.cpp
@@ -214,6 +214,7 @@ namespace vcpkg
fs::path VcpkgPaths::package_dir(const PackageSpec& spec) const { return this->packages / spec.dir(); }
fs::path VcpkgPaths::port_dir(const PackageSpec& spec) const { return this->ports / spec.name(); }
+ fs::path VcpkgPaths::port_dir(const std::string& name) const { return this->ports / name; }
fs::path VcpkgPaths::build_info_file_path(const PackageSpec& spec) const
{
diff --git a/toolsrc/src/commands_depends.cpp b/toolsrc/src/commands_depends.cpp
index 2d1fb658b..49e1c6c01 100644
--- a/toolsrc/src/commands_depends.cpp
+++ b/toolsrc/src/commands_depends.cpp
@@ -33,7 +33,7 @@ namespace vcpkg::Commands::DependInfo
for (const Dependency& dependency : source_paragraph.depends)
{
- if (Strings::case_insensitive_ascii_contains(dependency.name, filter))
+ if (Strings::case_insensitive_ascii_contains(dependency.name(), filter))
{
return false;
}
@@ -46,7 +46,7 @@ namespace vcpkg::Commands::DependInfo
for (auto&& source_control_file : source_control_files)
{
const SourceParagraph& source_paragraph = *source_control_file->core_paragraph;
- auto s = Strings::join(", ", source_paragraph.depends, [](const Dependency& d) { return d.name; });
+ auto s = Strings::join(", ", source_paragraph.depends, [](const Dependency& d) { return d.name(); });
System::println("%s: %s", source_paragraph.name, s);
}
diff --git a/toolsrc/src/commands_install.cpp b/toolsrc/src/commands_install.cpp
index 2ce5b6c62..9e37dc057 100644
--- a/toolsrc/src/commands_install.cpp
+++ b/toolsrc/src/commands_install.cpp
@@ -13,11 +13,7 @@
namespace vcpkg::Commands::Install
{
- using Dependencies::InstallPlanAction;
- using Dependencies::RequestType;
- using Dependencies::InstallPlanType;
- using Dependencies::RemovePlanAction;
- using Dependencies::RemovePlanType;
+ using namespace Dependencies;
InstallDir InstallDir::from_destination_root(const fs::path& destination_root,
const std::string& destination_subdirectory,
@@ -176,45 +172,10 @@ namespace vcpkg::Commands::Install
return SortedVector<std::string>(std::move(installed_files));
}
- static void print_plan(const std::map<InstallPlanType, std::vector<const InstallPlanAction*>>& group_by_plan_type)
+ void install_package(const VcpkgPaths& paths, const BinaryControlFile& bcf, StatusParagraphs* status_db)
{
- static constexpr std::array<InstallPlanType, 3> order = {
- InstallPlanType::ALREADY_INSTALLED, InstallPlanType::BUILD_AND_INSTALL, InstallPlanType::INSTALL};
-
- for (const InstallPlanType plan_type : order)
- {
- auto it = group_by_plan_type.find(plan_type);
- if (it == group_by_plan_type.cend())
- {
- continue;
- }
-
- std::vector<const InstallPlanAction*> cont = it->second;
- std::sort(cont.begin(), cont.end(), &InstallPlanAction::compare_by_name);
- const std::string as_string = Strings::join("\n", cont, [](const InstallPlanAction* p) {
- return Dependencies::to_output_string(p->request_type, p->spec.to_string());
- });
-
- switch (plan_type)
- {
- case InstallPlanType::ALREADY_INSTALLED:
- System::println("The following packages are already installed:\n%s", as_string);
- continue;
- case InstallPlanType::BUILD_AND_INSTALL:
- System::println("The following packages will be built and installed:\n%s", as_string);
- continue;
- case InstallPlanType::INSTALL:
- System::println("The following packages will be installed:\n%s", as_string);
- continue;
- default: Checks::unreachable(VCPKG_LINE_INFO);
- }
- }
- }
-
- void install_package(const VcpkgPaths& paths, const BinaryParagraph& binary_paragraph, StatusParagraphs* status_db)
- {
- const fs::path package_dir = paths.package_dir(binary_paragraph.spec);
- const Triplet& triplet = binary_paragraph.spec.triplet();
+ const fs::path package_dir = paths.package_dir(bcf.core_paragraph.spec);
+ const Triplet& triplet = bcf.core_paragraph.spec.triplet();
const std::vector<StatusParagraphAndAssociatedFiles> pgh_and_files = get_installed_files(paths, *status_db);
const SortedVector<std::string> package_files =
@@ -234,7 +195,7 @@ namespace vcpkg::Commands::Install
System::println(System::Color::error,
"The following files are already installed in %s and are in conflict with %s",
triplet_install_path.generic_string(),
- binary_paragraph.spec);
+ bcf.core_paragraph.spec);
System::print("\n ");
System::println(Strings::join("\n ", intersection));
System::println("");
@@ -242,27 +203,42 @@ namespace vcpkg::Commands::Install
}
StatusParagraph source_paragraph;
- source_paragraph.package = binary_paragraph;
+ source_paragraph.package = bcf.core_paragraph;
source_paragraph.want = Want::INSTALL;
source_paragraph.state = InstallState::HALF_INSTALLED;
- for (auto&& dep : source_paragraph.package.depends)
- {
- if (status_db->find_installed(dep, source_paragraph.package.spec.triplet()) == status_db->end())
- {
- Checks::unreachable(VCPKG_LINE_INFO);
- }
- }
+
write_update(paths, source_paragraph);
status_db->insert(std::make_unique<StatusParagraph>(source_paragraph));
+ std::vector<StatusParagraph> features_spghs;
+ for (auto&& feature : bcf.features)
+ {
+ features_spghs.emplace_back();
+
+ StatusParagraph& feature_paragraph = features_spghs.back();
+ feature_paragraph.package = feature;
+ feature_paragraph.want = Want::INSTALL;
+ feature_paragraph.state = InstallState::HALF_INSTALLED;
+
+ write_update(paths, feature_paragraph);
+ status_db->insert(std::make_unique<StatusParagraph>(feature_paragraph));
+ }
+
const InstallDir install_dir = InstallDir::from_destination_root(
- paths.installed, triplet.to_string(), paths.listfile_path(binary_paragraph));
+ paths.installed, triplet.to_string(), paths.listfile_path(bcf.core_paragraph));
install_files_and_write_listfile(paths.get_filesystem(), package_dir, install_dir);
source_paragraph.state = InstallState::INSTALLED;
write_update(paths, source_paragraph);
status_db->insert(std::make_unique<StatusParagraph>(source_paragraph));
+
+ for (auto&& feature_paragraph : features_spghs)
+ {
+ feature_paragraph.state = InstallState::INSTALLED;
+ write_update(paths, feature_paragraph);
+ status_db->insert(std::make_unique<StatusParagraph>(feature_paragraph));
+ }
}
using Build::BuildResult;
@@ -312,8 +288,8 @@ namespace vcpkg::Commands::Install
}
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);
+ const BinaryControlFile bpgh =
+ Paragraphs::try_load_cached_control_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);
@@ -322,10 +298,11 @@ namespace vcpkg::Commands::Install
if (plan_type == InstallPlanType::BUILD_AND_INSTALL && g_feature_packages)
{
+ const std::string display_name_feature = action.displayname();
if (use_head_version)
- System::println("Building package %s from HEAD... ", display_name);
+ System::println("Building package %s from HEAD... ", display_name_feature);
else
- System::println("Building package %s... ", display_name);
+ System::println("Building package %s... ", display_name_feature);
const Build::BuildPackageConfig build_config{
*action.any_paragraph.source_control_file.value_or_exit(VCPKG_LINE_INFO),
@@ -339,13 +316,13 @@ namespace vcpkg::Commands::Install
System::println(System::Color::error, Build::create_error_message(result.code, action.spec));
return result.code;
}
- System::println("Building package %s... done", display_name);
+ System::println("Building package %s... done", display_name_feature);
- 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);
+ const BinaryControlFile bcf =
+ Paragraphs::try_load_cached_control_package(paths, action.spec).value_or_exit(VCPKG_LINE_INFO);
+ System::println("Installing package %s... ", display_name_feature);
+ install_package(paths, bcf, &status_db);
+ System::println(System::Color::success, "Installing package %s... done", display_name_feature);
return BuildResult::SUCCEEDED;
}
@@ -357,7 +334,9 @@ namespace vcpkg::Commands::Install
System::Color::warning, "Package %s is already built -- not building from HEAD", display_name);
}
System::println("Installing package %s... ", display_name);
- install_package(paths, action.any_paragraph.binary_paragraph.value_or_exit(VCPKG_LINE_INFO), &status_db);
+ install_package(paths,
+ BinaryControlFile{action.any_paragraph.binary_paragraph.value_or_exit(VCPKG_LINE_INFO)},
+ &status_db);
System::println(System::Color::success, "Installing package %s... done", display_name);
return BuildResult::SUCCEEDED;
}
@@ -365,72 +344,226 @@ namespace vcpkg::Commands::Install
Checks::unreachable(VCPKG_LINE_INFO);
}
+ static void print_plan(const std::vector<AnyAction>& action_plan, bool is_recursive)
+ {
+ std::vector<const RemovePlanAction*> remove_plans;
+ std::vector<const InstallPlanAction*> rebuilt_plans;
+ std::vector<const InstallPlanAction*> only_install_plans;
+ std::vector<const InstallPlanAction*> new_plans;
+
+ const bool has_non_user_requested_packages = Util::find_if(action_plan, [](const AnyAction& package) -> bool {
+ if (auto iplan = package.install_plan.get())
+ return iplan->request_type != RequestType::USER_REQUESTED;
+ else
+ return false;
+ }) != action_plan.cend();
+
+ for (auto&& action : action_plan)
+ {
+ if (auto install_action = action.install_plan.get())
+ {
+ // remove plans are guaranteed to come before install plans, so we know the plan will be contained if at
+ // all.
+ auto it = Util::find_if(
+ remove_plans, [&](const RemovePlanAction* plan) { return plan->spec == install_action->spec; });
+ if (it != remove_plans.end())
+ {
+ rebuilt_plans.emplace_back(install_action);
+ }
+ else
+ {
+ if (install_action->plan_type == InstallPlanType::INSTALL)
+ only_install_plans.emplace_back(install_action);
+ else
+ new_plans.emplace_back(install_action);
+ }
+ }
+ else if (auto remove_action = action.remove_plan.get())
+ {
+ remove_plans.emplace_back(remove_action);
+ }
+ }
+
+ std::sort(remove_plans.begin(), remove_plans.end(), &RemovePlanAction::compare_by_name);
+ std::sort(rebuilt_plans.begin(), rebuilt_plans.end(), &InstallPlanAction::compare_by_name);
+ std::sort(only_install_plans.begin(), only_install_plans.end(), &InstallPlanAction::compare_by_name);
+ std::sort(new_plans.begin(), new_plans.end(), &InstallPlanAction::compare_by_name);
+
+ const std::string rebuilt_string = Strings::join("\n", rebuilt_plans, [](const InstallPlanAction* p) {
+ return to_output_string(p->request_type, p->displayname());
+ });
+ if (rebuilt_plans.size() > 0) System::println("The following packages will be rebuilt:\n%s", rebuilt_string);
+
+ const std::string new_string = Strings::join("\n", new_plans, [](const InstallPlanAction* p) {
+ return to_output_string(p->request_type, p->displayname());
+ });
+ if (new_plans.size() > 0)
+ System::println("The following packages will be built and installed:\n%s", new_string);
+
+ const std::string only_install_string = Strings::join("\n", only_install_plans, [](const InstallPlanAction* p) {
+ return to_output_string(p->request_type, p->displayname());
+ });
+ if (only_install_plans.size() > 0)
+ System::println("The following packages will be directly installed:\n%s", only_install_string);
+
+ if (has_non_user_requested_packages)
+ System::println("Additional packages (*) will be installed to complete this operation.");
+
+ if (remove_plans.size() > 0 && !is_recursive)
+ {
+ System::println(System::Color::warning,
+ "If you are sure you want to rebuild the above packages, run the command with the "
+ "--recurse option");
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+ }
+
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet)
{
static const std::string OPTION_DRY_RUN = "--dry-run";
static const std::string OPTION_USE_HEAD_VERSION = "--head";
static const std::string OPTION_NO_DOWNLOADS = "--no-downloads";
+ static const std::string OPTION_RECURSE = "--recurse";
// input sanitization
static const std::string example =
Commands::Help::create_example_string("install zlib zlib:x64-windows curl boost");
args.check_min_arg_count(1, example);
- const std::vector<PackageSpec> specs = Util::fmap(args.command_arguments, [&](auto&& arg) {
- return Input::check_and_get_package_spec(arg, default_triplet, example);
+ const std::vector<FullPackageSpec> specs = Util::fmap(args.command_arguments, [&](auto&& arg) {
+ return Input::check_and_get_full_package_spec(arg, default_triplet, example);
});
+
for (auto&& spec : specs)
- Input::check_triplet(spec.triplet(), paths);
+ {
+ Input::check_triplet(spec.package_spec.triplet(), paths);
+ if (!spec.features.empty() && !g_feature_packages)
+ {
+ Checks::exit_with_message(
+ VCPKG_LINE_INFO, "Feature packages are experimentally available under the --featurepackages flag.");
+ }
+ }
const std::unordered_set<std::string> options = args.check_and_get_optional_command_arguments(
- {OPTION_DRY_RUN, OPTION_USE_HEAD_VERSION, OPTION_NO_DOWNLOADS});
+ {OPTION_DRY_RUN, OPTION_USE_HEAD_VERSION, OPTION_NO_DOWNLOADS, OPTION_RECURSE});
const bool dryRun = options.find(OPTION_DRY_RUN) != options.cend();
const bool use_head_version = options.find(OPTION_USE_HEAD_VERSION) != options.cend();
const bool no_downloads = options.find(OPTION_NO_DOWNLOADS) != options.cend();
+ const bool is_recursive = options.find(OPTION_RECURSE) != options.cend();
// create the plan
StatusParagraphs status_db = database_load_check(paths);
- 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
- const std::string specs_string =
- Strings::join(",", install_plan, [](const InstallPlanAction& plan) { return plan.spec.to_string(); });
- Metrics::track_property("installplan", specs_string);
-
- std::map<InstallPlanType, std::vector<const InstallPlanAction*>> group_by_plan_type;
- Util::group_by(install_plan, &group_by_plan_type, [](const InstallPlanAction& p) { return p.plan_type; });
- print_plan(group_by_plan_type);
+ const Build::BuildPackageOptions install_plan_options = {Build::to_use_head_version(use_head_version),
+ Build::to_allow_downloads(!no_downloads)};
- const bool has_non_user_requested_packages =
- Util::find_if(install_plan, [](const InstallPlanAction& package) -> bool {
- return package.request_type != RequestType::USER_REQUESTED;
- }) != install_plan.cend();
+ std::vector<AnyAction> action_plan;
- if (has_non_user_requested_packages)
+ if (g_feature_packages)
{
- System::println("Additional packages (*) will be installed to complete this operation.");
+ std::unordered_map<std::string, SourceControlFile> scf_map;
+ auto all_ports = Paragraphs::try_load_all_ports(paths.get_filesystem(), paths.ports);
+ for (auto&& port : all_ports.paragraphs)
+ {
+ scf_map[port->core_paragraph->name] = std::move(*port);
+ }
+ action_plan = create_feature_install_plan(scf_map, FullPackageSpec::to_feature_specs(specs), status_db);
+ }
+ else
+ {
+ Dependencies::PathsPortFile paths_port_file(paths);
+ auto install_plan = Dependencies::create_install_plan(
+ paths_port_file, Util::fmap(specs, [](auto&& spec) { return spec.package_spec; }), status_db);
+
+ action_plan = Util::fmap(
+ install_plan, [](InstallPlanAction& install_action) { return AnyAction(std::move(install_action)); });
}
+ // install plan will be empty if it is already installed - need to change this at status paragraph part
+ Checks::check_exit(VCPKG_LINE_INFO, !action_plan.empty(), "Install plan cannot be empty");
+
+ // log the plan
+ const std::string specs_string = Strings::join(",", action_plan, [](const AnyAction& action) {
+ if (auto iaction = action.install_plan.get())
+ return iaction->spec.to_string();
+ else if (auto raction = action.remove_plan.get())
+ return "R$" + raction->spec.to_string();
+ Checks::unreachable(VCPKG_LINE_INFO);
+ });
+ Metrics::track_property("installplan", specs_string);
+
+ print_plan(action_plan, is_recursive);
+
if (dryRun)
{
Checks::exit_success(VCPKG_LINE_INFO);
}
- const Build::BuildPackageOptions install_plan_options = {Build::to_use_head_version(use_head_version),
- Build::to_allow_downloads(!no_downloads)};
-
// execute the plan
- for (const InstallPlanAction& action : install_plan)
+ if (g_feature_packages)
+ {
+ for (const auto& action : action_plan)
+ {
+ if (auto install_action = action.install_plan.get())
+ {
+ const BuildResult result =
+ perform_install_plan_action(paths, *install_action, install_plan_options, status_db);
+ if (result != BuildResult::SUCCEEDED)
+ {
+ System::println(Build::create_user_troubleshooting_message(install_action->spec));
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+ }
+ else if (auto remove_action = action.remove_plan.get())
+ {
+ static const std::string OPTION_PURGE = "--purge";
+ static const std::string OPTION_NO_PURGE = "--no-purge";
+
+ const bool alsoRemoveFolderFromPackages = options.find(OPTION_NO_PURGE) == options.end();
+ if (options.find(OPTION_PURGE) != options.end() && !alsoRemoveFolderFromPackages)
+ {
+ // User specified --purge and --no-purge
+ System::println(System::Color::error, "Error: cannot specify both --no-purge and --purge.");
+ System::print(example);
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+ const std::string display_name = remove_action->spec.to_string();
+ switch (remove_action->plan_type)
+ {
+ case RemovePlanType::NOT_INSTALLED:
+ System::println(System::Color::success, "Package %s is not installed", display_name);
+ break;
+ case RemovePlanType::REMOVE:
+ System::println("Removing package %s... ", display_name);
+ Commands::Remove::remove_package(paths, remove_action->spec, &status_db);
+ System::println(System::Color::success, "Removing package %s... done", display_name);
+ break;
+ case RemovePlanType::UNKNOWN:
+ default: Checks::unreachable(VCPKG_LINE_INFO);
+ }
+
+ if (alsoRemoveFolderFromPackages)
+ {
+ System::println("Purging package %s... ", display_name);
+ Files::Filesystem& fs = paths.get_filesystem();
+ std::error_code ec;
+ fs.remove_all(paths.packages / remove_action->spec.dir(), ec);
+ System::println(System::Color::success, "Purging package %s... done", display_name);
+ }
+ }
+ }
+ }
+ else
{
- const BuildResult result = perform_install_plan_action(paths, action, install_plan_options, status_db);
- if (result != BuildResult::SUCCEEDED)
+ for (const auto& action : action_plan)
{
- System::println(Build::create_user_troubleshooting_message(action.spec));
- Checks::exit_fail(VCPKG_LINE_INFO);
+ const auto& iaction = action.install_plan.value_or_exit(VCPKG_LINE_INFO);
+ const BuildResult result = perform_install_plan_action(paths, iaction, install_plan_options, status_db);
+ if (result != BuildResult::SUCCEEDED)
+ {
+ System::println(Build::create_user_troubleshooting_message(iaction.spec));
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
}
}
diff --git a/toolsrc/src/commands_remove.cpp b/toolsrc/src/commands_remove.cpp
index eabf2b9ae..e480f02dd 100644
--- a/toolsrc/src/commands_remove.cpp
+++ b/toolsrc/src/commands_remove.cpp
@@ -17,13 +17,19 @@ namespace vcpkg::Commands::Remove
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());
+ auto spghs = status_db->find_all(spec.name(), spec.triplet());
+ auto core_pkg = **status_db->find(spec.name(), spec.triplet(), "");
- pkg.want = Want::PURGE;
- pkg.state = InstallState::HALF_INSTALLED;
- write_update(paths, pkg);
+ for (auto&& spgh : spghs)
+ {
+ StatusParagraph& pkg = **spgh;
+ if (pkg.state != InstallState::INSTALLED) continue;
+ pkg.want = Want::PURGE;
+ pkg.state = InstallState::HALF_INSTALLED;
+ write_update(paths, pkg);
+ }
- auto maybe_lines = fs.read_lines(paths.listfile_path(pkg.package));
+ auto maybe_lines = fs.read_lines(paths.listfile_path(core_pkg.package));
if (auto lines = maybe_lines.get())
{
@@ -80,11 +86,16 @@ namespace vcpkg::Commands::Remove
}
}
- fs.remove(paths.listfile_path(pkg.package));
+ fs.remove(paths.listfile_path(core_pkg.package));
}
- pkg.state = InstallState::NOT_INSTALLED;
- write_update(paths, pkg);
+ for (auto&& spgh : spghs)
+ {
+ StatusParagraph& pkg = **spgh;
+ if (pkg.state != InstallState::HALF_INSTALLED) continue;
+ pkg.state = InstallState::NOT_INSTALLED;
+ write_update(paths, pkg);
+ }
}
static void print_plan(const std::map<RemovePlanType, std::vector<const RemovePlanAction*>>& group_by_plan_type)
diff --git a/toolsrc/src/commands_search.cpp b/toolsrc/src/commands_search.cpp
index fee99a5db..f12c25fb6 100644
--- a/toolsrc/src/commands_search.cpp
+++ b/toolsrc/src/commands_search.cpp
@@ -39,7 +39,7 @@ namespace vcpkg::Commands::Search
s.append(Strings::format("%s;", name));
for (const Dependency& d : source_paragraph.depends)
{
- const std::string dependency_name = replace_dashes_with_underscore(d.name);
+ const std::string dependency_name = replace_dashes_with_underscore(d.name());
s.append(Strings::format("%s -> %s;", name, dependency_name));
}
}
diff --git a/toolsrc/src/test_install_plan.cpp b/toolsrc/src/test_install_plan.cpp
index d02af5662..6c9311264 100644
--- a/toolsrc/src/test_install_plan.cpp
+++ b/toolsrc/src/test_install_plan.cpp
@@ -6,32 +6,62 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace vcpkg;
-namespace UnitTest1
+namespace Microsoft::VisualStudio::CppUnitTestFramework
{
- class InstallPlanTests : public TestClass<InstallPlanTests>
+ template<>
+ inline std::wstring ToString<vcpkg::Dependencies::InstallPlanType>(const vcpkg::Dependencies::InstallPlanType& t)
{
- struct PackageSpecMap
+ switch (t)
{
- std::unordered_map<PackageSpec, SourceControlFile> map;
- Triplet triplet;
- PackageSpecMap(const Triplet& t) { triplet = t; }
+ case Dependencies::InstallPlanType::ALREADY_INSTALLED: return L"ALREADY_INSTALLED";
+ case Dependencies::InstallPlanType::BUILD_AND_INSTALL: return L"BUILD_AND_INSTALL";
+ case Dependencies::InstallPlanType::INSTALL: return L"INSTALL";
+ case Dependencies::InstallPlanType::UNKNOWN: return L"UNKNOWN";
+ default: return ToString((int)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();
+ template<>
+ inline std::wstring ToString<vcpkg::Dependencies::RequestType>(const vcpkg::Dependencies::RequestType& t)
+ {
+ switch (t)
+ {
+ case Dependencies::RequestType::AUTO_SELECTED: return L"AUTO_SELECTED";
+ case Dependencies::RequestType::USER_REQUESTED: return L"USER_REQUESTED";
+ case Dependencies::RequestType::UNKNOWN: return L"UNKNOWN";
+ default: return ToString((int)t);
+ }
+ }
+}
- 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)
+namespace UnitTest1
+{
+ class InstallPlanTests : public TestClass<InstallPlanTests>
+ {
+ static std::unique_ptr<SourceControlFile> make_control_file(
+ const char* name,
+ const char* depends,
+ const std::vector<std::pair<const char*, const char*>>& features = {})
+ {
+ using Pgh = std::unordered_map<std::string, std::string>;
+ std::vector<Pgh> scf_pghs;
+ scf_pghs.push_back(Pgh{
+ {"Source", name},
+ {"Version", "0"},
+ {"Build-Depends", depends},
+ });
+ for (auto&& feature : features)
{
- return get_package_spec({{{"Source", source}, {"Version", version}, {"Build-Depends", build_depends}}});
+ scf_pghs.push_back(Pgh{
+ {"Feature", feature.first},
+ {"Description", "feature"},
+ {"Build-Depends", feature.second},
+ });
}
- };
+ auto m_pgh = vcpkg::SourceControlFile::parse_control_file(std::move(scf_pghs));
+ Assert::IsTrue(m_pgh.has_value());
+ return std::move(*m_pgh.get());
+ }
static void features_check(Dependencies::AnyAction* install_action,
std::string pkg_name,
@@ -68,16 +98,60 @@ namespace UnitTest1
Assert::AreEqual(pkg_name.c_str(), plan.spec.name().c_str());
}
+ static std::unique_ptr<StatusParagraph> make_status_pgh(const char* name, const char* depends = "")
+ {
+ using Pgh = std::unordered_map<std::string, std::string>;
+ return std::make_unique<StatusParagraph>(Pgh{{"Package", name},
+ {"Version", "1"},
+ {"Architecture", "x86-windows"},
+ {"Multi-Arch", "same"},
+ {"Depends", depends},
+ {"Status", "install ok installed"}});
+ }
+ static std::unique_ptr<StatusParagraph> make_status_feature_pgh(const char* name,
+ const char* feature,
+ const char* depends = "")
+ {
+ using Pgh = std::unordered_map<std::string, std::string>;
+ return std::make_unique<StatusParagraph>(Pgh{{"Package", name},
+ {"Version", "1"},
+ {"Feature", feature},
+ {"Architecture", "x86-windows"},
+ {"Multi-Arch", "same"},
+ {"Depends", depends},
+ {"Status", "install ok installed"}});
+ }
+ struct PackageSpecMap
+ {
+ std::unordered_map<std::string, SourceControlFile> map;
+ Triplet triplet;
+ PackageSpecMap(const Triplet& t) { triplet = t; }
+
+ PackageSpec emplace(const char* name,
+ const char* depends = "",
+ const std::vector<std::pair<const char*, const char*>>& features = {})
+ {
+ return emplace(std::move(*make_control_file(name, depends, features)));
+ }
+ PackageSpec emplace(vcpkg::SourceControlFile&& scf)
+ {
+ auto spec = PackageSpec::from_name_and_triplet(scf.core_paragraph->name, triplet);
+ Assert::IsTrue(spec.has_value());
+ map.emplace(scf.core_paragraph->name, std::move(scf));
+ return PackageSpec{*spec.get()};
+ }
+ };
+
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 spec_a = spec_map.emplace("a", "b");
+ auto spec_b = spec_map.emplace("b", "c");
+ auto spec_c = spec_map.emplace("c");
- auto map_port = Dependencies::MapPortFile(spec_map.map);
+ Dependencies::MapPortFile map_port(spec_map.map);
auto install_plan =
Dependencies::create_install_plan(map_port, {spec_a}, StatusParagraphs(std::move(status_paragraphs)));
@@ -92,16 +166,16 @@ namespace UnitTest1
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 spec_a = spec_map.emplace("a", "d");
+ auto spec_b = spec_map.emplace("b", "d, e");
+ auto spec_c = spec_map.emplace("c", "e, h");
+ auto spec_d = spec_map.emplace("d", "f, g, h");
+ auto spec_e = spec_map.emplace("e", "g");
+ auto spec_f = spec_map.emplace("f");
+ auto spec_g = spec_map.emplace("g");
+ auto spec_h = spec_map.emplace("h");
+
+ Dependencies::MapPortFile map_port(spec_map.map);
auto install_plan = Dependencies::create_install_plan(
map_port, {spec_a, spec_b, spec_c}, StatusParagraphs(std::move(status_paragraphs)));
@@ -127,37 +201,74 @@ namespace UnitTest1
Assert::IsTrue(e_pos > g_pos);
}
+ TEST_METHOD(existing_package_scheme)
+ {
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+ status_paragraphs.push_back(make_status_pgh("a"));
+
+ PackageSpecMap spec_map(Triplet::X86_WINDOWS);
+ auto spec_a = FullPackageSpec{spec_map.emplace("a")};
+
+ auto install_plan =
+ Dependencies::create_feature_install_plan(spec_map.map,
+ FullPackageSpec::to_feature_specs({spec_a}),
+ StatusParagraphs(std::move(status_paragraphs)));
+
+ Assert::AreEqual(size_t(1), install_plan.size());
+ auto p = install_plan[0].install_plan.get();
+ Assert::IsNotNull(p);
+ Assert::AreEqual("a", p->spec.name().c_str());
+ Assert::AreEqual(Dependencies::InstallPlanType::ALREADY_INSTALLED, p->plan_type);
+ Assert::AreEqual(Dependencies::RequestType::USER_REQUESTED, p->request_type);
+ }
+
+ TEST_METHOD(user_requested_package_scheme)
+ {
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+
+ PackageSpecMap spec_map(Triplet::X86_WINDOWS);
+ auto spec_a = FullPackageSpec{spec_map.emplace("a", "b")};
+ auto spec_b = FullPackageSpec{spec_map.emplace("b")};
+
+ auto install_plan =
+ Dependencies::create_feature_install_plan(spec_map.map,
+ FullPackageSpec::to_feature_specs({spec_a}),
+ StatusParagraphs(std::move(status_paragraphs)));
+
+ Assert::AreEqual(size_t(2), install_plan.size());
+ auto p = install_plan[0].install_plan.get();
+ Assert::IsNotNull(p);
+ Assert::AreEqual("b", p->spec.name().c_str());
+ Assert::AreEqual(Dependencies::InstallPlanType::BUILD_AND_INSTALL, p->plan_type);
+ Assert::AreEqual(Dependencies::RequestType::AUTO_SELECTED, p->request_type);
+
+ auto p2 = install_plan[1].install_plan.get();
+ Assert::IsNotNull(p2);
+ Assert::AreEqual("a", p2->spec.name().c_str());
+ Assert::AreEqual(Dependencies::InstallPlanType::BUILD_AND_INSTALL, p2->plan_type);
+ Assert::AreEqual(Dependencies::RequestType::USER_REQUESTED, p2->request_type);
+ }
+
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"}}));
+ status_paragraphs.push_back(make_status_pgh("j", "k"));
+ status_paragraphs.push_back(make_status_pgh("k"));
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 spec_a = spec_map.emplace("a", "b, c, d, e, f, g, h, j, k");
+ auto spec_b = spec_map.emplace("b", "c, d, e, f, g, h, j, k");
+ auto spec_c = spec_map.emplace("c", "d, e, f, g, h, j, k");
+ auto spec_d = spec_map.emplace("d", "e, f, g, h, j, k");
+ auto spec_e = spec_map.emplace("e", "f, g, h, j, k");
+ auto spec_f = spec_map.emplace("f", "g, h, j, k");
+ auto spec_g = spec_map.emplace("g", "h, j, k");
+ auto spec_h = spec_map.emplace("h", "j, k");
+ auto spec_j = spec_map.emplace("j", "k");
+ auto spec_k = spec_map.emplace("k");
+
+ Dependencies::MapPortFile map_port(spec_map.map);
auto install_plan =
Dependencies::create_install_plan(map_port, {spec_a}, StatusParagraphs(std::move(status_paragraphs)));
@@ -174,162 +285,85 @@ namespace UnitTest1
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"}}));
+ status_paragraphs.push_back(make_status_pgh("a", "b, b[b1]"));
+ status_paragraphs.push_back(make_status_pgh("b"));
+ status_paragraphs.push_back(make_status_feature_pgh("b", "b1"));
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 spec_a = FullPackageSpec{spec_map.emplace("a", "b, b[b1]", {{"a1", "b[b2]"}}), {"a1"}};
+ auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}, {"b2", ""}, {"b3", ""}})};
- auto install_plan = Dependencies::create_feature_install_plan(
- spec_map.map, {spec_a}, StatusParagraphs(std::move(status_paragraphs)));
+ auto install_plan =
+ Dependencies::create_feature_install_plan(spec_map.map,
+ FullPackageSpec::to_feature_specs({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"});
+ features_check(&install_plan[2], "b", {"b1", "core", "b1"});
+ features_check(&install_plan[3], "a", {"a1", "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 spec_a = FullPackageSpec{spec_map.emplace("a", "b[b1]", {{"a1", "b[b2]"}}), {"a1"}};
+ auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}, {"b2", ""}, {"b3", ""}})};
- auto install_plan = Dependencies::create_feature_install_plan(
- spec_map.map, {spec_a}, StatusParagraphs(std::move(status_paragraphs)));
+ auto install_plan =
+ Dependencies::create_feature_install_plan(spec_map.map,
+ FullPackageSpec::to_feature_specs({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"});
+ features_check(&install_plan[0], "b", {"b1", "b2", "core"});
+ features_check(&install_plan[1], "a", {"a1", "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"}}));
+ status_paragraphs.push_back(make_status_pgh("a"));
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 spec_a = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}}), {"core"}};
+ auto spec_b = FullPackageSpec{spec_map.emplace("b")};
+ auto spec_c = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}};
- auto install_plan = Dependencies::create_feature_install_plan(
- spec_map.map, {spec_c, spec_a}, StatusParagraphs(std::move(status_paragraphs)));
+ auto install_plan =
+ Dependencies::create_feature_install_plan(spec_map.map,
+ FullPackageSpec::to_feature_specs({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[2], "a", {"a1", "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"}}));
+ status_paragraphs.push_back(make_status_pgh("a"));
+ status_paragraphs.push_back(make_status_feature_pgh("a", "a1", ""));
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 spec_a = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}})};
+ auto spec_b = FullPackageSpec{spec_map.emplace("b")};
+ auto spec_c = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}};
- auto install_plan = Dependencies::create_feature_install_plan(
- spec_map.map, {spec_c}, StatusParagraphs(std::move(status_paragraphs)));
+ auto install_plan =
+ Dependencies::create_feature_install_plan(spec_map.map,
+ FullPackageSpec::to_feature_specs({spec_c}),
+ StatusParagraphs(std::move(status_paragraphs)));
Assert::AreEqual(size_t(1), install_plan.size());
features_check(&install_plan[0], "c", {"core"});
@@ -337,168 +371,93 @@ namespace UnitTest1
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 spec_a =
+ FullPackageSpec{spec_map.emplace("a", "", {{"a1", "b[b1]"}, {"a2", "b[b2]"}, {"a3", "a[a2]"}}), {"a3"}};
+ auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}, {"b2", ""}})};
- auto install_plan = Dependencies::create_feature_install_plan(
- spec_map.map, {spec_a}, StatusParagraphs(std::move(status_paragraphs)));
+ auto install_plan =
+ Dependencies::create_feature_install_plan(spec_map.map,
+ FullPackageSpec::to_feature_specs({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"});
+ features_check(&install_plan[0], "b", {"core", "b2"});
+ features_check(&install_plan[1], "a", {"core", "a3", "a2"});
}
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);
+ status_paragraphs.push_back(make_status_pgh("b"));
- 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"}};
+ PackageSpecMap spec_map(Triplet::X86_WINDOWS);
+ auto spec_a = FullPackageSpec{spec_map.emplace("a", "b[core]"), {"core"}};
+ auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}}), {"b1"}};
- auto install_plan = Dependencies::create_feature_install_plan(
- spec_map.map, {spec_a, spec_b}, StatusParagraphs(std::move(status_paragraphs)));
+ auto install_plan =
+ Dependencies::create_feature_install_plan(spec_map.map,
+ FullPackageSpec::to_feature_specs({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[1], "b", {"core", "b1"});
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"}}));
+ status_paragraphs.push_back(make_status_pgh("x", "b"));
+ status_paragraphs.push_back(make_status_pgh("b"));
+
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 spec_a = FullPackageSpec{spec_map.emplace("a")};
+ auto spec_x = FullPackageSpec{spec_map.emplace("x", "a"), {"core"}};
+ auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}}), {"b1"}};
- auto install_plan = Dependencies::create_feature_install_plan(
- spec_map.map, {spec_b, spec_x}, StatusParagraphs(std::move(status_paragraphs)));
+ auto install_plan =
+ Dependencies::create_feature_install_plan(spec_map.map,
+ FullPackageSpec::to_feature_specs({spec_b}),
+ 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"});
+ features_check(&install_plan[2], "b", {"core", "b1"});
+ features_check(&install_plan[3], "a", {"core"});
+ features_check(&install_plan[4], "x", {"core"});
}
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"}}));
+ status_paragraphs.push_back(make_status_pgh("a"));
+ status_paragraphs.push_back(make_status_pgh("a"));
+ status_paragraphs.back()->package.spec =
+ PackageSpec::from_name_and_triplet("a", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO);
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"}};
+ auto spec_a_64 = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}}), {"core"}};
+ auto spec_b_64 = FullPackageSpec{spec_map.emplace("b")};
+ auto spec_c_64 = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"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 spec_a_86 = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}}), {"core"}};
+ auto spec_b_86 = FullPackageSpec{spec_map.emplace("b")};
+ auto spec_c_86 = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"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)));
+ auto install_plan = Dependencies::create_feature_install_plan(
+ spec_map.map,
+ FullPackageSpec::to_feature_specs({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 {
@@ -517,10 +476,10 @@ namespace UnitTest1
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[3], "a", {"a1", "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[6], "a", {"a1", "core"});
features_check(&install_plan[7], "c", {"core"});
}
};
diff --git a/toolsrc/src/tests_dependencies.cpp b/toolsrc/src/tests_dependencies.cpp
index 7a49bdbd0..6a6981d73 100644
--- a/toolsrc/src/tests_dependencies.cpp
+++ b/toolsrc/src/tests_dependencies.cpp
@@ -8,6 +8,7 @@
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace vcpkg;
+using Parse::parse_comma_list;
namespace UnitTest1
{
@@ -17,7 +18,7 @@ namespace UnitTest1
{
auto v = expand_qualified_dependencies(parse_comma_list("libA (windows)"));
Assert::AreEqual(size_t(1), v.size());
- Assert::AreEqual("libA", v[0].name.c_str());
+ Assert::AreEqual("libA", v[0].depend.name.c_str());
Assert::AreEqual("windows", v[0].qualifier.c_str());
}
@@ -41,7 +42,15 @@ namespace UnitTest1
TEST_METHOD(parse_supports_all)
{
auto v = Supports::parse({
- "x64", "x86", "arm", "windows", "uwp", "v140", "v141", "crt-static", "crt-dynamic",
+ "x64",
+ "x86",
+ "arm",
+ "windows",
+ "uwp",
+ "v140",
+ "v141",
+ "crt-static",
+ "crt-dynamic",
});
Assert::AreNotEqual(uintptr_t(0), uintptr_t(v.get()));
@@ -74,7 +83,9 @@ namespace UnitTest1
TEST_METHOD(parse_supports_some)
{
auto v = Supports::parse({
- "x64", "x86", "windows",
+ "x64",
+ "x86",
+ "windows",
});
Assert::AreNotEqual(uintptr_t(0), uintptr_t(v.get()));
diff --git a/toolsrc/src/tests_package_spec.cpp b/toolsrc/src/tests_package_spec.cpp
new file mode 100644
index 000000000..fa201b372
--- /dev/null
+++ b/toolsrc/src/tests_package_spec.cpp
@@ -0,0 +1,119 @@
+#include "BinaryParagraph.h"
+#include "CppUnitTest.h"
+#include "Paragraphs.h"
+#include "vcpkg_Strings.h"
+
+#pragma comment(lib, "version")
+#pragma comment(lib, "winhttp")
+
+using namespace Microsoft::VisualStudio::CppUnitTestFramework;
+
+namespace Microsoft::VisualStudio::CppUnitTestFramework
+{
+ template<>
+ inline std::wstring ToString<vcpkg::PackageSpecParseResult>(const vcpkg::PackageSpecParseResult& t)
+ {
+ return ToString(static_cast<uint32_t>(t));
+ }
+
+ template<>
+ inline std::wstring ToString<vcpkg::PackageSpec>(const vcpkg::PackageSpec& t)
+ {
+ return ToString(t.to_string());
+ }
+}
+
+namespace Strings = vcpkg::Strings;
+
+namespace UnitTest1
+{
+ using namespace vcpkg;
+
+ class SpecifierConversion : public TestClass<SpecifierConversion>
+ {
+ TEST_METHOD(full_package_spec_to_feature_specs)
+ {
+ auto a_spec = PackageSpec::from_name_and_triplet("a", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO);
+ auto b_spec = PackageSpec::from_name_and_triplet("b", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO);
+
+ auto fspecs = FullPackageSpec::to_feature_specs({{a_spec, {"0", "1"}}, {b_spec, {"2", "3"}}});
+
+ Assert::AreEqual(size_t(6), fspecs.size());
+
+ std::array<const char*, 6> features = {"", "0", "1", "", "2", "3"};
+ std::array<PackageSpec*, 6> specs = {&a_spec, &a_spec, &a_spec, &b_spec, &b_spec, &b_spec};
+
+ for (int i = 0; i < features.size(); ++i)
+ {
+ Assert::AreEqual(features[i], fspecs[i].feature().c_str());
+ Assert::AreEqual(*specs[i], fspecs[i].spec());
+ }
+ }
+ };
+
+ class SpecifierParsing : public TestClass<SpecifierParsing>
+ {
+ TEST_METHOD(parsed_specifier_from_string)
+ {
+ auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib");
+ Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, maybe_spec.error());
+ auto spec = maybe_spec.get();
+ Assert::AreEqual("zlib", spec->name.c_str());
+ Assert::AreEqual(size_t(0), spec->features.size());
+ Assert::AreEqual("", spec->triplet.c_str());
+ }
+
+ TEST_METHOD(parsed_specifier_from_string_with_triplet)
+ {
+ auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib:x64-uwp");
+ Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, maybe_spec.error());
+ auto spec = maybe_spec.get();
+ Assert::AreEqual("zlib", spec->name.c_str());
+ Assert::AreEqual("x64-uwp", spec->triplet.c_str());
+ }
+
+ TEST_METHOD(parsed_specifier_from_string_with_colons)
+ {
+ auto ec = vcpkg::ParsedSpecifier::from_string("zlib:x86-uwp:").error();
+ Assert::AreEqual(vcpkg::PackageSpecParseResult::TOO_MANY_COLONS, ec);
+ }
+
+ TEST_METHOD(parsed_specifier_from_string_with_feature)
+ {
+ auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib[feature]:x64-uwp");
+ Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, maybe_spec.error());
+ auto spec = maybe_spec.get();
+ Assert::AreEqual("zlib", spec->name.c_str());
+ Assert::IsTrue(spec->features.size() == 1);
+ Assert::AreEqual("feature", spec->features.front().c_str());
+ Assert::AreEqual("x64-uwp", spec->triplet.c_str());
+ }
+
+ TEST_METHOD(parsed_specifier_from_string_with_many_features)
+ {
+ auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib[0, 1,2]");
+ Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, maybe_spec.error());
+ auto spec = maybe_spec.get();
+ Assert::AreEqual("zlib", spec->name.c_str());
+ Assert::IsTrue(spec->features.size() == 3);
+ Assert::AreEqual("0", spec->features[0].c_str());
+ Assert::AreEqual("1", spec->features[1].c_str());
+ Assert::AreEqual("2", spec->features[2].c_str());
+ Assert::AreEqual("", spec->triplet.c_str());
+ }
+
+ TEST_METHOD(utf8_to_utf16)
+ {
+ auto str = vcpkg::Strings::to_utf16("abc");
+ Assert::AreEqual(L"abc", str.c_str());
+ }
+
+ TEST_METHOD(utf8_to_utf16_with_whitespace)
+ {
+ auto str = vcpkg::Strings::to_utf16("abc -x86-windows");
+ Assert::AreEqual(L"abc -x86-windows", str.c_str());
+ }
+ };
+
+ TEST_CLASS(Metrics){};
+}
diff --git a/toolsrc/src/tests_paragraph.cpp b/toolsrc/src/tests_paragraph.cpp
index af4b55498..47a07e12d 100644
--- a/toolsrc/src/tests_paragraph.cpp
+++ b/toolsrc/src/tests_paragraph.cpp
@@ -60,7 +60,7 @@ namespace UnitTest1
Assert::AreEqual("m", pgh->core_paragraph->maintainer.c_str());
Assert::AreEqual("d", pgh->core_paragraph->description.c_str());
Assert::AreEqual(size_t(1), pgh->core_paragraph->depends.size());
- Assert::AreEqual("bd", pgh->core_paragraph->depends[0].name.c_str());
+ Assert::AreEqual("bd", pgh->core_paragraph->depends[0].name().c_str());
Assert::AreEqual(size_t(1), pgh->core_paragraph->supports.size());
Assert::AreEqual("x64", pgh->core_paragraph->supports[0].c_str());
}
@@ -77,8 +77,8 @@ namespace UnitTest1
auto& pgh = *m_pgh.get();
Assert::AreEqual(size_t(2), pgh->core_paragraph->depends.size());
- Assert::AreEqual("z", pgh->core_paragraph->depends[0].name.c_str());
- Assert::AreEqual("openssl", pgh->core_paragraph->depends[1].name.c_str());
+ Assert::AreEqual("z", pgh->core_paragraph->depends[0].name().c_str());
+ Assert::AreEqual("openssl", pgh->core_paragraph->depends[1].name().c_str());
}
TEST_METHOD(SourceParagraph_Three_Depends)
@@ -93,9 +93,9 @@ namespace UnitTest1
auto& pgh = *m_pgh.get();
Assert::AreEqual(size_t(3), pgh->core_paragraph->depends.size());
- Assert::AreEqual("z", pgh->core_paragraph->depends[0].name.c_str());
- Assert::AreEqual("openssl", pgh->core_paragraph->depends[1].name.c_str());
- Assert::AreEqual("xyz", pgh->core_paragraph->depends[2].name.c_str());
+ Assert::AreEqual("z", pgh->core_paragraph->depends[0].name().c_str());
+ Assert::AreEqual("openssl", pgh->core_paragraph->depends[1].name().c_str());
+ Assert::AreEqual("xyz", pgh->core_paragraph->depends[2].name().c_str());
}
TEST_METHOD(SourceParagraph_Three_Supports)
@@ -131,9 +131,9 @@ namespace UnitTest1
Assert::AreEqual("", pgh->core_paragraph->maintainer.c_str());
Assert::AreEqual("", pgh->core_paragraph->description.c_str());
Assert::AreEqual(size_t(2), pgh->core_paragraph->depends.size());
- Assert::AreEqual("libA", pgh->core_paragraph->depends[0].name.c_str());
+ Assert::AreEqual("libA", pgh->core_paragraph->depends[0].name().c_str());
Assert::AreEqual("windows", pgh->core_paragraph->depends[0].qualifier.c_str());
- Assert::AreEqual("libB", pgh->core_paragraph->depends[1].name.c_str());
+ Assert::AreEqual("libB", pgh->core_paragraph->depends[1].name().c_str());
Assert::AreEqual("uwp", pgh->core_paragraph->depends[1].qualifier.c_str());
}
@@ -384,45 +384,5 @@ namespace UnitTest1
Assert::AreEqual(size_t(1), pghs.size());
Assert::AreEqual("a, b, c", pghs[0]["Depends"].c_str());
}
-
- TEST_METHOD(package_spec_parse)
- {
- 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()->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::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()->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::FullPackageSpec::from_string("zlib:x86-uwp:", vcpkg::Triplet::X86_WINDOWS).error();
- Assert::AreEqual(vcpkg::PackageSpecParseResult::TOO_MANY_COLONS, ec);
- }
-
- TEST_METHOD(utf8_to_utf16)
- {
- auto str = vcpkg::Strings::to_utf16("abc");
- Assert::AreEqual(L"abc", str.c_str());
- }
-
- TEST_METHOD(utf8_to_utf16_with_whitespace)
- {
- auto str = vcpkg::Strings::to_utf16("abc -x86-windows");
- Assert::AreEqual(L"abc -x86-windows", str.c_str());
- }
};
-
- TEST_CLASS(Metrics){};
}
diff --git a/toolsrc/src/vcpkg_Build.cpp b/toolsrc/src/vcpkg_Build.cpp
index a0d690f37..124efb7f2 100644
--- a/toolsrc/src/vcpkg_Build.cpp
+++ b/toolsrc/src/vcpkg_Build.cpp
@@ -88,29 +88,37 @@ namespace vcpkg::Build
return Strings::wformat(LR"("%s" %s %s %s 2>&1)", toolset.vcvarsall.native(), arch, target, tonull);
}
- static void create_binary_control_file(const VcpkgPaths& paths,
- const SourceParagraph& source_paragraph,
+ static void create_binary_feature_control_file(const SourceParagraph& source_paragraph,
+ const FeatureParagraph& feature_paragraph,
+ const Triplet& triplet,
+ BinaryControlFile& bcf)
+ {
+ BinaryParagraph bpgh(source_paragraph, feature_paragraph, triplet);
+ bcf.features.emplace_back(std::move(bpgh));
+ }
+
+ static void create_binary_control_file(const SourceParagraph& source_paragraph,
const Triplet& triplet,
- const BuildInfo& build_info)
+ const BuildInfo& build_info,
+ BinaryControlFile& bcf)
{
- BinaryParagraph bpgh = BinaryParagraph(source_paragraph, triplet);
+ BinaryParagraph bpgh(source_paragraph, triplet);
if (auto p_ver = build_info.version.get())
{
bpgh.version = *p_ver;
}
- const fs::path binary_control_file = paths.packages / bpgh.dir() / "CONTROL";
- paths.get_filesystem().write_contents(binary_control_file, Strings::serialize(bpgh));
+ bcf.core_paragraph = std::move(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)
+ static void write_binary_control_file(const VcpkgPaths& paths, BinaryControlFile bcf)
{
- 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));
+ std::string start = Strings::serialize(bcf.core_paragraph);
+ for (auto&& feature : bcf.features)
+ {
+ start += "\n" + Strings::serialize(feature);
+ }
+ const fs::path binary_control_file = paths.packages / bcf.core_paragraph.dir() / "CONTROL";
+ paths.get_filesystem().write_contents(binary_control_file, start);
}
ExtendedBuildResult build_package(const VcpkgPaths& paths,
@@ -196,6 +204,10 @@ namespace vcpkg::Build
auto build_info = read_build_info(paths.get_filesystem(), paths.build_info_file_path(spec));
const size_t error_count = PostBuildLint::perform_all_checks(spec, paths, pre_build_info, build_info);
+ BinaryControlFile bcf;
+
+ create_binary_control_file(config.src, triplet, build_info, bcf);
+
if (error_count != 0)
{
return {BuildResult::POST_BUILD_CHECKS_FAILED, {}};
@@ -209,13 +221,13 @@ namespace vcpkg::Build
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_feature_control_file(*config.scf->core_paragraph, *f_pgh, triplet, bcf);
}
}
}
}
- create_binary_control_file(paths, config.src, triplet, build_info);
+
+ write_binary_control_file(paths, bcf);
// const fs::path port_buildtrees_dir = paths.buildtrees / spec.name;
// delete_directory(port_buildtrees_dir);
diff --git a/toolsrc/src/vcpkg_Dependencies.cpp b/toolsrc/src/vcpkg_Dependencies.cpp
index 820e51b33..188b0f444 100644
--- a/toolsrc/src/vcpkg_Dependencies.cpp
+++ b/toolsrc/src/vcpkg_Dependencies.cpp
@@ -12,7 +12,99 @@
namespace vcpkg::Dependencies
{
+ struct FeatureNodeEdges
+ {
+ std::vector<FeatureSpec> remove_edges;
+ std::vector<FeatureSpec> build_edges;
+ bool plus = false;
+ };
+
+ struct Cluster : Util::MoveOnlyBase
+ {
+ 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;
+ RequestType request_type = RequestType::AUTO_SELECTED;
+ };
+
+ struct ClusterPtr
+ {
+ Cluster* ptr;
+
+ Cluster* operator->() { return ptr; }
+ };
+
bool operator==(const ClusterPtr& l, const ClusterPtr& r) { return l.ptr == r.ptr; }
+}
+
+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;
+ };
+
+ struct ClusterGraph : Util::MoveOnlyBase
+ {
+ explicit ClusterGraph(std::unordered_map<std::string, const SourceControlFile*>&& ports)
+ : m_ports(std::move(ports))
+ {
+ }
+
+ Cluster& get(const PackageSpec& spec)
+ {
+ auto it = m_graph.find(spec);
+ if (it == m_graph.end())
+ {
+ // Load on-demand from m_ports
+ auto it_ports = m_ports.find(spec.name());
+ if (it_ports != m_ports.end())
+ {
+ auto& clust = m_graph[spec];
+ clust.spec = spec;
+ cluster_from_scf(*it_ports->second, clust);
+ return clust;
+ }
+ return m_graph[spec];
+ }
+ return it->second;
+ }
+
+ private:
+ void cluster_from_scf(const SourceControlFile& scf, Cluster& out_cluster)
+ {
+ FeatureNodeEdges core_dependencies;
+ core_dependencies.build_edges =
+ filter_dependencies_to_specs(scf.core_paragraph->depends, out_cluster.spec.triplet());
+ out_cluster.edges.emplace("core", std::move(core_dependencies));
+
+ for (const auto& feature : scf.feature_paragraphs)
+ {
+ FeatureNodeEdges added_edges;
+ added_edges.build_edges = filter_dependencies_to_specs(feature->depends, out_cluster.spec.triplet());
+ out_cluster.edges.emplace(feature->name, std::move(added_edges));
+ }
+ out_cluster.source_control_file = &scf;
+ }
+
+ std::unordered_map<PackageSpec, Cluster> m_graph;
+ std::unordered_map<std::string, const SourceControlFile*> m_ports;
+ };
std::vector<PackageSpec> AnyParagraph::dependencies(const Triplet& triplet) const
{
@@ -51,56 +143,69 @@ namespace vcpkg::Dependencies
}
}
- InstallPlanAction::InstallPlanAction()
- : spec(), any_paragraph(), plan_type(InstallPlanType::UNKNOWN), request_type(RequestType::UNKNOWN)
- {
- }
+ InstallPlanAction::InstallPlanAction() : plan_type(InstallPlanType::UNKNOWN), request_type(RequestType::UNKNOWN) {}
InstallPlanAction::InstallPlanAction(const PackageSpec& spec,
const SourceControlFile& any_paragraph,
const std::unordered_set<std::string>& features,
const RequestType& request_type)
- : InstallPlanAction()
+ : spec(spec), plan_type(InstallPlanType::BUILD_AND_INSTALL), request_type(request_type), feature_list(features)
{
- 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 std::unordered_set<std::string>& features,
+ const RequestType& request_type)
+ : spec(spec), plan_type(InstallPlanType::ALREADY_INSTALLED), request_type(request_type), feature_list(features)
+ {
}
InstallPlanAction::InstallPlanAction(const PackageSpec& spec,
const AnyParagraph& any_paragraph,
const RequestType& request_type)
- : InstallPlanAction()
+ : spec(spec), request_type(request_type), any_paragraph(any_paragraph)
{
- this->spec = spec;
- this->request_type = request_type;
if (auto p = any_paragraph.status_paragraph.get())
{
this->plan_type = InstallPlanType::ALREADY_INSTALLED;
- this->any_paragraph.status_paragraph = *p;
return;
}
if (auto p = any_paragraph.binary_paragraph.get())
{
this->plan_type = InstallPlanType::INSTALL;
- this->any_paragraph.binary_paragraph = *p;
return;
}
if (auto p = any_paragraph.source_paragraph.get())
{
this->plan_type = InstallPlanType::BUILD_AND_INSTALL;
- this->any_paragraph.source_paragraph = *p;
return;
}
this->plan_type = InstallPlanType::UNKNOWN;
}
+ std::string InstallPlanAction::displayname() const
+ {
+ if (this->feature_list.empty())
+ {
+ return this->spec.to_string();
+ }
+ else
+ {
+ std::string features;
+ for (auto&& feature : this->feature_list)
+ {
+ features += feature + ",";
+ }
+ features.pop_back();
+
+ return this->spec.name() + "[" + features + "]:" + this->spec.triplet().to_string();
+ }
+ }
+
bool InstallPlanAction::compare_by_name(const InstallPlanAction* left, const InstallPlanAction* right)
{
return left->spec.name() < right->spec.name();
@@ -155,9 +260,9 @@ namespace vcpkg::Dependencies
return left->spec.name() < right->spec.name();
}
- MapPortFile::MapPortFile(const std::unordered_map<PackageSpec, SourceControlFile>& map) : ports(map) {}
+ MapPortFile::MapPortFile(const std::unordered_map<std::string, SourceControlFile>& map) : ports(map) {}
- const SourceControlFile& MapPortFile::get_control_file(const PackageSpec& spec) const
+ const SourceControlFile& MapPortFile::get_control_file(const std::string& spec) const
{
auto scf = ports.find(spec);
if (scf == ports.end())
@@ -169,9 +274,9 @@ namespace vcpkg::Dependencies
PathsPortFile::PathsPortFile(const VcpkgPaths& paths) : ports(paths) {}
- const SourceControlFile& PathsPortFile::get_control_file(const PackageSpec& spec) const
+ const SourceControlFile& PathsPortFile::get_control_file(const std::string& spec) const
{
- std::unordered_map<PackageSpec, SourceControlFile>::iterator cache_it = cache.find(spec);
+ auto cache_it = cache.find(spec);
if (cache_it != cache.end())
{
return cache_it->second;
@@ -219,7 +324,9 @@ namespace vcpkg::Dependencies
auto it = status_db.find_installed(spec);
if (it != status_db.end()) return InstallPlanAction{spec, {*it->get(), nullopt, nullopt}, request_type};
return InstallPlanAction{
- spec, {nullopt, nullopt, *port_file_provider.get_control_file(spec).core_paragraph}, request_type};
+ spec,
+ {nullopt, nullopt, *port_file_provider.get_control_file(spec.name()).core_paragraph},
+ request_type};
}
};
@@ -319,9 +426,9 @@ namespace vcpkg::Dependencies
? RequestType::USER_REQUESTED
: RequestType::AUTO_SELECTED;
- Expected<BinaryParagraph> maybe_bpgh = Paragraphs::try_load_cached_package(paths, spec);
- if (auto bpgh = maybe_bpgh.get())
- return ExportPlanAction{spec, {nullopt, *bpgh, nullopt}, request_type};
+ Expected<BinaryControlFile> maybe_bpgh = Paragraphs::try_load_cached_control_package(paths, spec);
+ if (auto bcf = maybe_bpgh.get())
+ return ExportPlanAction{spec, {nullopt, bcf->core_paragraph, nullopt}, request_type};
auto maybe_scf = Paragraphs::try_load_port(paths.get_filesystem(), paths.port_dir(spec));
if (auto scf = maybe_scf.get())
@@ -339,93 +446,79 @@ namespace vcpkg::Dependencies
return toposort;
}
- std::vector<FeatureSpec> to_feature_specs(const std::vector<std::string>& depends, const Triplet& triplet)
+ enum class MarkPlusResult
{
- 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);
+ FEATURE_NOT_FOUND,
+ SUCCESS,
+ };
- auto feature_spec = FeatureSpec{p_spec, ""};
- f_specs.emplace_back(std::move(feature_spec));
- }
- }
- return f_specs;
- }
+ MarkPlusResult mark_plus(const std::string& feature,
+ Cluster& cluster,
+ ClusterGraph& pkg_to_cluster,
+ GraphPlan& graph_plan);
+ void mark_minus(Cluster& cluster, ClusterGraph& pkg_to_cluster, GraphPlan& graph_plan);
- bool mark_plus(const std::string& feature,
- Cluster& cluster,
- std::unordered_map<PackageSpec, Cluster>& pkg_to_cluster,
- GraphPlan& graph_plan)
+ MarkPlusResult mark_plus(const std::string& feature, Cluster& cluster, ClusterGraph& graph, GraphPlan& graph_plan)
{
- auto it = cluster.edges.find(feature);
- std::string updated_feature = feature;
- if (updated_feature == "")
+ if (feature == "")
{
- updated_feature = "core";
- it = cluster.edges.find("core");
- }
- if (it == cluster.edges.end())
- {
- Checks::unreachable(VCPKG_LINE_INFO);
+ // Indicates that core was not specified in the reference
+ return mark_plus("core", cluster, graph, graph_plan);
}
- if (cluster.edges[updated_feature].plus) return true;
+ auto it = cluster.edges.find(feature);
+ if (it == cluster.edges.end()) return MarkPlusResult::FEATURE_NOT_FOUND;
+
+ if (cluster.edges[feature].plus) return MarkPlusResult::SUCCESS;
- if (cluster.original_features.find(updated_feature) == cluster.original_features.end())
+ if (cluster.original_features.find(feature) == cluster.original_features.end())
{
cluster.transient_uninstalled = true;
}
if (!cluster.transient_uninstalled)
{
- return false;
+ return MarkPlusResult::SUCCESS;
}
- cluster.edges[updated_feature].plus = true;
+ cluster.edges[feature].plus = true;
if (!cluster.original_features.empty())
{
- mark_minus(cluster, pkg_to_cluster, graph_plan);
+ mark_minus(cluster, graph, 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())
+ tracked.insert(feature);
+
+ if (feature != "core")
{
- 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});
- }
+ // All features implicitly depend on core
+ auto res = mark_plus("core", cluster, graph, graph_plan);
+
+ // Should be impossible for "core" to not exist
+ Checks::check_exit(VCPKG_LINE_INFO, res == MarkPlusResult::SUCCESS);
}
- for (auto&& depend : cluster.edges[updated_feature].build_edges)
+ for (auto&& depend : cluster.edges[feature].build_edges)
{
- auto& depend_cluster = pkg_to_cluster[depend.spec];
- mark_plus(depend.feature_name, depend_cluster, pkg_to_cluster, graph_plan);
+ auto& depend_cluster = graph.get(depend.spec());
+ auto res = mark_plus(depend.feature(), depend_cluster, graph, graph_plan);
+
+ Checks::check_exit(VCPKG_LINE_INFO,
+ res == MarkPlusResult::SUCCESS,
+ "Error: Unable to satisfy dependency %s of %s",
+ depend,
+ FeatureSpec(cluster.spec, feature));
+
if (&depend_cluster == &cluster) continue;
graph_plan.install_graph.add_edge({&cluster}, {&depend_cluster});
}
- return true;
+
+ return MarkPlusResult::SUCCESS;
}
- void mark_minus(Cluster& cluster, std::unordered_map<PackageSpec, Cluster>& pkg_to_cluster, GraphPlan& graph_plan)
+ void mark_minus(Cluster& cluster, ClusterGraph& graph, GraphPlan& graph_plan)
{
if (cluster.will_remove) return;
cluster.will_remove = true;
@@ -436,89 +529,93 @@ namespace vcpkg::Dependencies
auto& remove_edges_edges = pair.second.remove_edges;
for (auto&& depend : remove_edges_edges)
{
- auto& depend_cluster = pkg_to_cluster[depend.spec];
+ auto& depend_cluster = graph.get(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);
+ mark_minus(depend_cluster, graph, graph_plan);
}
}
+
+ cluster.transient_uninstalled = true;
for (auto&& original_feature : cluster.original_features)
{
- cluster.transient_uninstalled = true;
- mark_plus(original_feature, cluster, pkg_to_cluster, graph_plan);
+ auto res = mark_plus(original_feature, cluster, graph, graph_plan);
+ if (res != MarkPlusResult::SUCCESS)
+ {
+ System::println(System::Color::warning,
+ "Warning: could not reinstall feature %s",
+ FeatureSpec{cluster.spec, original_feature});
+ }
}
}
- std::vector<AnyAction> create_feature_install_plan(const std::unordered_map<PackageSpec, SourceControlFile>& map,
- const std::vector<FullPackageSpec>& specs,
- const StatusParagraphs& status_db)
+
+ static ClusterGraph create_feature_install_graph(const std::unordered_map<std::string, SourceControlFile>& map,
+ const StatusParagraphs& status_db)
{
- std::unordered_map<PackageSpec, Cluster> pkg_spec_to_package_node;
+ std::unordered_map<std::string, const SourceControlFile*> ptr_map;
+ for (auto&& p : map)
+ ptr_map.emplace(p.first, &p.second);
+ ClusterGraph graph(std::move(ptr_map));
+
+ auto installed_ports = get_installed_ports(status_db);
- for (const auto& it : map)
+ for (auto&& status_paragraph : installed_ports)
{
- Cluster& node = pkg_spec_to_package_node[it.first];
+ Cluster& cluster = graph.get(status_paragraph->package.spec);
- 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);
+ cluster.transient_uninstalled = false;
- for (const auto& feature : it.second.feature_paragraphs)
+ cluster.status_paragraphs.emplace_back(status_paragraph);
+
+ auto& status_paragraph_feature = status_paragraph->package.feature;
+ // In this case, empty string indicates the "core" paragraph for a package.
+ if (status_paragraph_feature == "")
{
- 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));
+ cluster.original_features.insert("core");
+ }
+ else
+ {
+ cluster.original_features.insert(status_paragraph_feature);
}
- node.source_control_file = &it.second;
}
- for (auto&& status_paragraph : get_installed_ports(status_db))
+ for (auto&& status_paragraph : installed_ports)
{
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());
+ auto reverse_edges = FeatureSpec::from_strings_and_triplet(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];
+ auto& dep_cluster = graph.get(dependency.spec());
+
+ auto depends_name = dependency.feature();
+ if (depends_name == "") depends_name = "core";
+
+ auto& target_node = dep_cluster.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);
- }
}
+ return graph;
+ }
+
+ std::vector<AnyAction> create_feature_install_plan(const std::unordered_map<std::string, SourceControlFile>& map,
+ const std::vector<FeatureSpec>& specs,
+ const StatusParagraphs& status_db)
+ {
+ ClusterGraph graph = create_feature_install_graph(map, status_db);
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);
- }
+ Cluster& spec_cluster = graph.get(spec.spec());
+ spec_cluster.request_type = RequestType::USER_REQUESTED;
+ auto res = mark_plus(spec.feature(), spec_cluster, graph, graph_plan);
+
+ Checks::check_exit(
+ VCPKG_LINE_INFO, res == MarkPlusResult::SUCCESS, "Error: Unable to locate feature %s", spec);
+
+ graph_plan.install_graph.add_vertex(ClusterPtr{&spec_cluster});
}
Graphs::GraphAdjacencyProvider<ClusterPtr> adjacency_remove_graph(graph_plan.remove_graph.adjacency_list());
@@ -529,38 +626,46 @@ namespace vcpkg::Dependencies
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;
+ std::vector<AnyAction> plan;
- for (auto&& like_cluster : remove_toposort)
+ for (auto&& p_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),
+ auto scf = *p_cluster->source_control_file.get();
+ auto spec = PackageSpec::from_name_and_triplet(scf->core_paragraph->name, p_cluster->spec.triplet())
+ .value_or_exit(VCPKG_LINE_INFO);
+ plan.emplace_back(RemovePlanAction{
+ std::move(spec),
RemovePlanType::REMOVE,
- RequestType::AUTO_SELECTED};
-
- install_plan.emplace_back(std::move(any_plan));
+ p_cluster->request_type,
+ });
}
- for (auto&& like_cluster : insert_toposort)
+ for (auto&& p_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));
+ if (p_cluster->transient_uninstalled)
+ {
+ // If it will be transiently uninstalled, we need to issue a full installation command
+ auto pscf = p_cluster->source_control_file.value_or_exit(VCPKG_LINE_INFO);
+ Checks::check_exit(VCPKG_LINE_INFO, pscf != nullptr);
+ plan.emplace_back(InstallPlanAction{
+ p_cluster->spec,
+ *pscf,
+ p_cluster->to_install_features,
+ p_cluster->request_type,
+ });
+ }
+ else
+ {
+ // If the package isn't transitively installed, still include it if the user explicitly requested it
+ if (p_cluster->request_type != RequestType::USER_REQUESTED) continue;
+ plan.emplace_back(InstallPlanAction{
+ p_cluster->spec,
+ p_cluster->original_features,
+ p_cluster->request_type,
+ });
+ }
}
- return install_plan;
+ return plan;
}
}
diff --git a/toolsrc/src/vcpkg_Parse.cpp b/toolsrc/src/vcpkg_Parse.cpp
index 659af2939..118cde900 100644
--- a/toolsrc/src/vcpkg_Parse.cpp
+++ b/toolsrc/src/vcpkg_Parse.cpp
@@ -44,4 +44,37 @@ namespace vcpkg::Parse
}
return nullptr;
}
+
+ std::vector<std::string> parse_comma_list(const std::string& str)
+ {
+ if (str.empty())
+ {
+ return {};
+ }
+
+ std::vector<std::string> out;
+
+ size_t cur = 0;
+ do
+ {
+ auto pos = str.find(',', cur);
+ if (pos == std::string::npos)
+ {
+ out.push_back(str.substr(cur));
+ break;
+ }
+ out.push_back(str.substr(cur, pos - cur));
+
+ // skip comma and space
+ ++pos;
+ if (str[pos] == ' ')
+ {
+ ++pos;
+ }
+
+ cur = pos;
+ } while (cur != std::string::npos);
+
+ return out;
+ }
}
diff --git a/toolsrc/src/vcpkglib.cpp b/toolsrc/src/vcpkglib.cpp
index 6b180b532..428ae090d 100644
--- a/toolsrc/src/vcpkglib.cpp
+++ b/toolsrc/src/vcpkglib.cpp
@@ -191,7 +191,7 @@ namespace vcpkg
for (const std::unique_ptr<StatusParagraph>& pgh : status_db)
{
- if (pgh->state != InstallState::INSTALLED)
+ if (pgh->state != InstallState::INSTALLED || pgh->package.feature != "")
{
continue;
}