aboutsummaryrefslogtreecommitdiff
path: root/toolsrc/src
diff options
context:
space:
mode:
authorRobert Schumacher <roschuma@microsoft.com>2020-02-04 15:50:10 -0800
committerRobert Schumacher <roschuma@microsoft.com>2020-02-04 15:50:10 -0800
commit6f66ad14fe9da11d4bf50f5b25b4da86ed971c53 (patch)
tree0f5dbcd1719cd6a8e486c4058cfefd607d58aa6c /toolsrc/src
parentd502f061bb3ee0258d6453acbf258b9e5d93d564 (diff)
parentd808514c9df44bb97d6eccff952bfe8ec4e156f7 (diff)
downloadvcpkg-6f66ad14fe9da11d4bf50f5b25b4da86ed971c53.tar.gz
vcpkg-6f66ad14fe9da11d4bf50f5b25b4da86ed971c53.zip
Merge remote-tracking branch 'origin/master' into HEAD
Diffstat (limited to 'toolsrc/src')
-rw-r--r--toolsrc/src/vcpkg-test/dependencies.cpp69
-rw-r--r--toolsrc/src/vcpkg-test/mockcmakevarsprovider.cpp28
-rw-r--r--toolsrc/src/vcpkg-test/paragraph.cpp96
-rw-r--r--toolsrc/src/vcpkg-test/plan.cpp727
-rw-r--r--toolsrc/src/vcpkg-test/specifier.cpp16
-rw-r--r--toolsrc/src/vcpkg-test/supports.cpp79
-rw-r--r--toolsrc/src/vcpkg-test/update.cpp16
-rw-r--r--toolsrc/src/vcpkg-test/util.cpp42
-rw-r--r--toolsrc/src/vcpkg.cpp30
-rw-r--r--toolsrc/src/vcpkg/base/downloads.cpp23
-rw-r--r--toolsrc/src/vcpkg/base/hash.cpp8
-rw-r--r--toolsrc/src/vcpkg/base/machinetype.cpp2
-rw-r--r--toolsrc/src/vcpkg/base/system.cpp10
-rw-r--r--toolsrc/src/vcpkg/binaryparagraph.cpp31
-rw-r--r--toolsrc/src/vcpkg/build.cpp403
-rw-r--r--toolsrc/src/vcpkg/cmakevars.cpp259
-rw-r--r--toolsrc/src/vcpkg/commands.autocomplete.cpp6
-rw-r--r--toolsrc/src/vcpkg/commands.buildexternal.cpp8
-rw-r--r--toolsrc/src/vcpkg/commands.ci.cpp313
-rw-r--r--toolsrc/src/vcpkg/commands.dependinfo.cpp29
-rw-r--r--toolsrc/src/vcpkg/commands.env.cpp11
-rw-r--r--toolsrc/src/vcpkg/commands.search.cpp2
-rw-r--r--toolsrc/src/vcpkg/commands.upgrade.cpp31
-rw-r--r--toolsrc/src/vcpkg/dependencies.cpp1075
-rw-r--r--toolsrc/src/vcpkg/export.cpp10
-rw-r--r--toolsrc/src/vcpkg/help.cpp32
-rw-r--r--toolsrc/src/vcpkg/install.cpp137
-rw-r--r--toolsrc/src/vcpkg/logicexpression.cpp196
-rw-r--r--toolsrc/src/vcpkg/packagespec.cpp44
-rw-r--r--toolsrc/src/vcpkg/paragraphs.cpp5
-rw-r--r--toolsrc/src/vcpkg/portfileprovider.cpp151
-rw-r--r--toolsrc/src/vcpkg/postbuildlint.cpp104
-rw-r--r--toolsrc/src/vcpkg/remove.cpp2
-rw-r--r--toolsrc/src/vcpkg/sourceparagraph.cpp179
-rw-r--r--toolsrc/src/vcpkg/statusparagraph.cpp28
-rw-r--r--toolsrc/src/vcpkg/statusparagraphs.cpp2
-rw-r--r--toolsrc/src/vcpkg/update.cpp4
-rw-r--r--toolsrc/src/vcpkg/vcpkgpaths.cpp27
38 files changed, 2551 insertions, 1684 deletions
diff --git a/toolsrc/src/vcpkg-test/dependencies.cpp b/toolsrc/src/vcpkg-test/dependencies.cpp
index 2344bb990..63e7cfee7 100644
--- a/toolsrc/src/vcpkg-test/dependencies.cpp
+++ b/toolsrc/src/vcpkg-test/dependencies.cpp
@@ -1,5 +1,7 @@
#include <catch2/catch.hpp>
-
+#include <vcpkg-test/mockcmakevarprovider.h>
+#include <vcpkg-test/util.h>
+#include <vcpkg/dependencies.h>
#include <vcpkg/sourceparagraph.h>
using namespace vcpkg;
@@ -7,22 +9,69 @@ using Parse::parse_comma_list;
TEST_CASE ("parse depends", "[dependencies]")
{
- auto v = expand_qualified_dependencies(parse_comma_list("libA (windows)"));
+ auto v = expand_qualified_dependencies(parse_comma_list("liba (windows)"));
REQUIRE(v.size() == 1);
- REQUIRE(v.at(0).depend.name == "libA");
+ REQUIRE(v.at(0).depend.name == "liba");
REQUIRE(v.at(0).qualifier == "windows");
}
TEST_CASE ("filter depends", "[dependencies]")
{
- auto deps = expand_qualified_dependencies(parse_comma_list("libA (windows), libB, libC (uwp)"));
- auto v = filter_dependencies(deps, Triplet::X64_WINDOWS);
+ const std::unordered_map<std::string, std::string> x64_win_cmake_vars{{"VCPKG_TARGET_ARCHITECTURE", "x64"},
+ {"VCPKG_CMAKE_SYSTEM_NAME", ""}};
+
+ const std::unordered_map<std::string, std::string> arm_uwp_cmake_vars{{"VCPKG_TARGET_ARCHITECTURE", "arm"},
+ {"VCPKG_CMAKE_SYSTEM_NAME", "WindowsStore"}};
+
+ auto deps = expand_qualified_dependencies(parse_comma_list("liba (windows), libb, libc (uwp)"));
+ auto v = filter_dependencies(deps, Triplet::X64_WINDOWS, x64_win_cmake_vars);
REQUIRE(v.size() == 2);
- REQUIRE(v.at(0) == "libA");
- REQUIRE(v.at(1) == "libB");
+ REQUIRE(v.at(0).package_spec.name() == "liba");
+ REQUIRE(v.at(1).package_spec.name() == "libb");
- auto v2 = filter_dependencies(deps, Triplet::ARM_UWP);
+ auto v2 = filter_dependencies(deps, Triplet::ARM_UWP, arm_uwp_cmake_vars);
+ REQUIRE(v.size() == 2);
+ REQUIRE(v2.at(0).package_spec.name() == "libb");
+ REQUIRE(v2.at(1).package_spec.name() == "libc");
+}
+
+TEST_CASE ("parse feature depends", "[dependencies]")
+{
+ auto u = parse_comma_list("libwebp[anim, gif2webp, img2webp, info, mux, nearlossless, "
+ "simd, cwebp, dwebp], libwebp[vwebp_sdl, extras] (!osx)");
+ REQUIRE(u.at(1) == "libwebp[vwebp_sdl, extras] (!osx)");
+ auto v = expand_qualified_dependencies(u);
REQUIRE(v.size() == 2);
- REQUIRE(v2.at(0) == "libB");
- REQUIRE(v2.at(1) == "libC");
+ auto&& a0 = v.at(0);
+ REQUIRE(a0.depend.name == "libwebp");
+ REQUIRE(a0.depend.features.size() == 9);
+ REQUIRE(a0.qualifier.empty());
+
+ auto&& a1 = v.at(1);
+ REQUIRE(a1.depend.name == "libwebp");
+ REQUIRE(a1.depend.features.size() == 2);
+ REQUIRE(a1.qualifier == "!osx");
+}
+
+TEST_CASE ("qualified dependency", "[dependencies]")
+{
+ using namespace Test;
+ PackageSpecMap spec_map;
+ auto spec_a = FullPackageSpec{spec_map.emplace("a", "b, b[b1] (linux)"), {}};
+ auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}}), {}};
+
+ PortFileProvider::MapPortFileProvider map_port{spec_map.map};
+ MockCMakeVarProvider var_provider;
+
+ auto plan = vcpkg::Dependencies::create_feature_install_plan(map_port, var_provider, {spec_a}, {});
+ REQUIRE(plan.install_actions.size() == 2);
+ REQUIRE(plan.install_actions.at(0).feature_list == std::vector<std::string>{"core"});
+
+ FullPackageSpec linspec_a{PackageSpec::from_name_and_triplet("a", Triplet::from_canonical_name("x64-linux"))
+ .value_or_exit(VCPKG_LINE_INFO),
+ {}};
+ var_provider.dep_info_vars[linspec_a.package_spec].emplace("VCPKG_CMAKE_SYSTEM_NAME", "Linux");
+ auto plan2 = vcpkg::Dependencies::create_feature_install_plan(map_port, var_provider, {linspec_a}, {});
+ REQUIRE(plan2.install_actions.size() == 2);
+ REQUIRE(plan2.install_actions.at(0).feature_list == std::vector<std::string>{"b1", "core"});
}
diff --git a/toolsrc/src/vcpkg-test/mockcmakevarsprovider.cpp b/toolsrc/src/vcpkg-test/mockcmakevarsprovider.cpp
new file mode 100644
index 000000000..eda1a7a64
--- /dev/null
+++ b/toolsrc/src/vcpkg-test/mockcmakevarsprovider.cpp
@@ -0,0 +1,28 @@
+#include <vcpkg-test/mockcmakevarprovider.h>
+
+namespace vcpkg::Test
+{
+ Optional<const std::unordered_map<std::string, std::string>&> MockCMakeVarProvider::get_generic_triplet_vars(
+ const Triplet& triplet) const
+ {
+ auto it = generic_triplet_vars.find(triplet);
+ if (it == generic_triplet_vars.end()) return nullopt;
+ return it->second;
+ }
+
+ Optional<const std::unordered_map<std::string, std::string>&> MockCMakeVarProvider::get_dep_info_vars(
+ const PackageSpec& spec) const
+ {
+ auto it = dep_info_vars.find(spec);
+ if (it == dep_info_vars.end()) return nullopt;
+ return it->second;
+ }
+
+ Optional<const std::unordered_map<std::string, std::string>&> MockCMakeVarProvider::get_tag_vars(
+ const PackageSpec& spec) const
+ {
+ auto it = tag_vars.find(spec);
+ if (it == tag_vars.end()) return nullopt;
+ return it->second;
+ }
+} \ No newline at end of file
diff --git a/toolsrc/src/vcpkg-test/paragraph.cpp b/toolsrc/src/vcpkg-test/paragraph.cpp
index 85c37851d..2ee4efe2f 100644
--- a/toolsrc/src/vcpkg-test/paragraph.cpp
+++ b/toolsrc/src/vcpkg-test/paragraph.cpp
@@ -10,10 +10,11 @@ namespace Strings = vcpkg::Strings;
TEST_CASE ("SourceParagraph construct minimum", "[paragraph]")
{
auto m_pgh =
- vcpkg::SourceControlFile::parse_control_file(std::vector<std::unordered_map<std::string, std::string>>{{
- {"Source", "zlib"},
- {"Version", "1.2.8"},
- }});
+ vcpkg::SourceControlFile::parse_control_file("",
+ std::vector<std::unordered_map<std::string, std::string>>{{
+ {"Source", "zlib"},
+ {"Version", "1.2.8"},
+ }});
REQUIRE(m_pgh.has_value());
auto& pgh = **m_pgh.get();
@@ -28,15 +29,15 @@ TEST_CASE ("SourceParagraph construct minimum", "[paragraph]")
TEST_CASE ("SourceParagraph construct maximum", "[paragraph]")
{
auto m_pgh =
- vcpkg::SourceControlFile::parse_control_file(std::vector<std::unordered_map<std::string, std::string>>{{
- {"Source", "s"},
- {"Version", "v"},
- {"Maintainer", "m"},
- {"Description", "d"},
- {"Build-Depends", "bd"},
- {"Default-Features", "df"},
- {"Supports", "x64"},
- }});
+ vcpkg::SourceControlFile::parse_control_file("",
+ std::vector<std::unordered_map<std::string, std::string>>{{
+ {"Source", "s"},
+ {"Version", "v"},
+ {"Maintainer", "m"},
+ {"Description", "d"},
+ {"Build-Depends", "bd"},
+ {"Default-Features", "df"},
+ }});
REQUIRE(m_pgh.has_value());
auto& pgh = **m_pgh.get();
@@ -48,18 +49,17 @@ TEST_CASE ("SourceParagraph construct maximum", "[paragraph]")
REQUIRE(pgh.core_paragraph->depends[0].name() == "bd");
REQUIRE(pgh.core_paragraph->default_features.size() == 1);
REQUIRE(pgh.core_paragraph->default_features[0] == "df");
- REQUIRE(pgh.core_paragraph->supports.size() == 1);
- REQUIRE(pgh.core_paragraph->supports[0] == "x64");
}
TEST_CASE ("SourceParagraph two depends", "[paragraph]")
{
auto m_pgh =
- vcpkg::SourceControlFile::parse_control_file(std::vector<std::unordered_map<std::string, std::string>>{{
- {"Source", "zlib"},
- {"Version", "1.2.8"},
- {"Build-Depends", "z, openssl"},
- }});
+ vcpkg::SourceControlFile::parse_control_file("",
+ std::vector<std::unordered_map<std::string, std::string>>{{
+ {"Source", "zlib"},
+ {"Version", "1.2.8"},
+ {"Build-Depends", "z, openssl"},
+ }});
REQUIRE(m_pgh.has_value());
auto& pgh = **m_pgh.get();
@@ -71,11 +71,12 @@ TEST_CASE ("SourceParagraph two depends", "[paragraph]")
TEST_CASE ("SourceParagraph three depends", "[paragraph]")
{
auto m_pgh =
- vcpkg::SourceControlFile::parse_control_file(std::vector<std::unordered_map<std::string, std::string>>{{
- {"Source", "zlib"},
- {"Version", "1.2.8"},
- {"Build-Depends", "z, openssl, xyz"},
- }});
+ vcpkg::SourceControlFile::parse_control_file("",
+ std::vector<std::unordered_map<std::string, std::string>>{{
+ {"Source", "zlib"},
+ {"Version", "1.2.8"},
+ {"Build-Depends", "z, openssl, xyz"},
+ }});
REQUIRE(m_pgh.has_value());
auto& pgh = **m_pgh.get();
@@ -85,31 +86,15 @@ TEST_CASE ("SourceParagraph three depends", "[paragraph]")
REQUIRE(pgh.core_paragraph->depends[2].name() == "xyz");
}
-TEST_CASE ("SourceParagraph three supports", "[paragraph]")
-{
- auto m_pgh =
- vcpkg::SourceControlFile::parse_control_file(std::vector<std::unordered_map<std::string, std::string>>{{
- {"Source", "zlib"},
- {"Version", "1.2.8"},
- {"Supports", "x64, windows, uwp"},
- }});
- REQUIRE(m_pgh.has_value());
- auto& pgh = **m_pgh.get();
-
- REQUIRE(pgh.core_paragraph->supports.size() == 3);
- REQUIRE(pgh.core_paragraph->supports[0] == "x64");
- REQUIRE(pgh.core_paragraph->supports[1] == "windows");
- REQUIRE(pgh.core_paragraph->supports[2] == "uwp");
-}
-
TEST_CASE ("SourceParagraph construct qualified depends", "[paragraph]")
{
auto m_pgh =
- vcpkg::SourceControlFile::parse_control_file(std::vector<std::unordered_map<std::string, std::string>>{{
- {"Source", "zlib"},
- {"Version", "1.2.8"},
- {"Build-Depends", "libA (windows), libB (uwp)"},
- }});
+ vcpkg::SourceControlFile::parse_control_file("",
+ std::vector<std::unordered_map<std::string, std::string>>{{
+ {"Source", "zlib"},
+ {"Version", "1.2.8"},
+ {"Build-Depends", "libA (windows), libB (uwp)"},
+ }});
REQUIRE(m_pgh.has_value());
auto& pgh = **m_pgh.get();
@@ -127,11 +112,12 @@ TEST_CASE ("SourceParagraph construct qualified depends", "[paragraph]")
TEST_CASE ("SourceParagraph default features", "[paragraph]")
{
auto m_pgh =
- vcpkg::SourceControlFile::parse_control_file(std::vector<std::unordered_map<std::string, std::string>>{{
- {"Source", "a"},
- {"Version", "1.0"},
- {"Default-Features", "a1"},
- }});
+ vcpkg::SourceControlFile::parse_control_file("",
+ std::vector<std::unordered_map<std::string, std::string>>{{
+ {"Source", "a"},
+ {"Version", "1.0"},
+ {"Default-Features", "a1"},
+ }});
REQUIRE(m_pgh.has_value());
auto& pgh = **m_pgh.get();
@@ -380,11 +366,12 @@ TEST_CASE ("BinaryParagraph serialize min", "[paragraph]")
auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss).value_or_exit(VCPKG_LINE_INFO);
REQUIRE(pghs.size() == 1);
- REQUIRE(pghs[0].size() == 4);
+ REQUIRE(pghs[0].size() == 5);
REQUIRE(pghs[0]["Package"] == "zlib");
REQUIRE(pghs[0]["Version"] == "1.2.8");
REQUIRE(pghs[0]["Architecture"] == "x86-windows");
REQUIRE(pghs[0]["Multi-Arch"] == "same");
+ REQUIRE(pghs[0]["Type"] == "Port");
}
TEST_CASE ("BinaryParagraph serialize max", "[paragraph]")
@@ -402,13 +389,14 @@ TEST_CASE ("BinaryParagraph serialize max", "[paragraph]")
auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss).value_or_exit(VCPKG_LINE_INFO);
REQUIRE(pghs.size() == 1);
- REQUIRE(pghs[0].size() == 7);
+ REQUIRE(pghs[0].size() == 8);
REQUIRE(pghs[0]["Package"] == "zlib");
REQUIRE(pghs[0]["Version"] == "1.2.8");
REQUIRE(pghs[0]["Architecture"] == "x86-windows");
REQUIRE(pghs[0]["Multi-Arch"] == "same");
REQUIRE(pghs[0]["Description"] == "first line\n second line");
REQUIRE(pghs[0]["Depends"] == "dep");
+ REQUIRE(pghs[0]["Type"] == "Port");
}
TEST_CASE ("BinaryParagraph serialize multiple deps", "[paragraph]")
diff --git a/toolsrc/src/vcpkg-test/plan.cpp b/toolsrc/src/vcpkg-test/plan.cpp
index e354b7551..594c9783b 100644
--- a/toolsrc/src/vcpkg-test/plan.cpp
+++ b/toolsrc/src/vcpkg-test/plan.cpp
@@ -1,7 +1,10 @@
#include <catch2/catch.hpp>
+#include <vcpkg-test/mockcmakevarprovider.h>
#include <vcpkg-test/util.h>
+#include <vcpkg/base/graphs.h>
#include <vcpkg/dependencies.h>
+#include <vcpkg/portfileprovider.h>
#include <vcpkg/sourceparagraph.h>
#include <vcpkg/triplet.h>
@@ -11,54 +14,28 @@
using namespace vcpkg;
+using Test::make_control_file;
using Test::make_status_feature_pgh;
using Test::make_status_pgh;
+using Test::MockCMakeVarProvider;
+using Test::PackageSpecMap;
using Test::unsafe_pspec;
-static std::unique_ptr<SourceControlFile> make_control_file(
- const char* name,
- const char* depends,
- const std::vector<std::pair<const char*, const char*>>& features = {},
- const std::vector<const char*>& default_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},
- {"Default-Features", Strings::join(", ", default_features)}});
- for (auto&& feature : features)
- {
- 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));
- REQUIRE(m_pgh.has_value());
- return std::move(*m_pgh.get());
-}
-
/// <summary>
/// Assert that the given action an install of given features from given package.
/// </summary>
-static void features_check(Dependencies::AnyAction& install_action,
+static void features_check(Dependencies::InstallPlanAction& plan,
std::string pkg_name,
- std::vector<std::string> vec,
+ std::vector<std::string> expected_features,
const Triplet& triplet = Triplet::X86_WINDOWS)
{
- REQUIRE(install_action.install_action.has_value());
- const auto& plan = install_action.install_action.value_or_exit(VCPKG_LINE_INFO);
const auto& feature_list = plan.feature_list;
REQUIRE(plan.spec.triplet().to_string() == triplet.to_string());
+ REQUIRE(pkg_name == plan.spec.name());
+ REQUIRE(feature_list.size() == expected_features.size());
- auto& scfl = *plan.source_control_file_location.get();
- REQUIRE(pkg_name == scfl.source_control_file->core_paragraph->name);
- REQUIRE(feature_list.size() == vec.size());
-
- for (auto&& feature_name : vec)
+ for (auto&& feature_name : expected_features)
{
// TODO: see if this can be simplified
if (feature_name == "core" || feature_name == "")
@@ -74,42 +51,14 @@ static void features_check(Dependencies::AnyAction& install_action,
/// <summary>
/// Assert that the given action is a remove of given package.
/// </summary>
-static void remove_plan_check(Dependencies::AnyAction& remove_action,
+static void remove_plan_check(Dependencies::RemovePlanAction& plan,
std::string pkg_name,
const Triplet& triplet = Triplet::X86_WINDOWS)
{
- const auto& plan = remove_action.remove_action.value_or_exit(VCPKG_LINE_INFO);
REQUIRE(plan.spec.triplet().to_string() == triplet.to_string());
REQUIRE(pkg_name == plan.spec.name());
}
-/// <summary>
-/// Map of source control files by their package name.
-/// </summary>
-struct PackageSpecMap
-{
- std::unordered_map<std::string, SourceControlFileLocation> map;
- Triplet triplet;
- PackageSpecMap(const Triplet& t = Triplet::X86_WINDOWS) noexcept { triplet = t; }
-
- PackageSpec emplace(const char* name,
- const char* depends = "",
- const std::vector<std::pair<const char*, const char*>>& features = {},
- const std::vector<const char*>& default_features = {})
- {
- auto scfl = SourceControlFileLocation{make_control_file(name, depends, features, default_features), ""};
- return emplace(std::move(scfl));
- }
-
- PackageSpec emplace(vcpkg::SourceControlFileLocation&& scfl)
- {
- auto spec = PackageSpec::from_name_and_triplet(scfl.source_control_file->core_paragraph->name, triplet);
- REQUIRE(spec.has_value());
- map.emplace(scfl.source_control_file->core_paragraph->name, std::move(scfl));
- return PackageSpec{*spec.get()};
- }
-};
-
TEST_CASE ("basic install scheme", "[plan]")
{
std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
@@ -119,14 +68,16 @@ TEST_CASE ("basic install scheme", "[plan]")
auto spec_b = spec_map.emplace("b", "c");
auto spec_c = spec_map.emplace("c");
- Dependencies::MapPortFileProvider map_port(spec_map.map);
+ PortFileProvider::MapPortFileProvider map_port(spec_map.map);
+ MockCMakeVarProvider var_provider;
+
auto install_plan = Dependencies::create_feature_install_plan(
- map_port, {FeatureSpec{spec_a, ""}}, StatusParagraphs(std::move(status_paragraphs)));
+ map_port, var_provider, {FullPackageSpec{spec_a, {}}}, StatusParagraphs(std::move(status_paragraphs)));
REQUIRE(install_plan.size() == 3);
- REQUIRE(install_plan.at(0).spec().name() == "c");
- REQUIRE(install_plan.at(1).spec().name() == "b");
- REQUIRE(install_plan.at(2).spec().name() == "a");
+ REQUIRE(install_plan.install_actions.at(0).spec.name() == "c");
+ REQUIRE(install_plan.install_actions.at(1).spec.name() == "b");
+ REQUIRE(install_plan.install_actions.at(2).spec.name() == "a");
}
TEST_CASE ("multiple install scheme", "[plan]")
@@ -143,17 +94,21 @@ TEST_CASE ("multiple install scheme", "[plan]")
auto spec_g = spec_map.emplace("g");
auto spec_h = spec_map.emplace("h");
- Dependencies::MapPortFileProvider map_port(spec_map.map);
+ PortFileProvider::MapPortFileProvider map_port(spec_map.map);
+ MockCMakeVarProvider var_provider;
+
auto install_plan = Dependencies::create_feature_install_plan(
map_port,
- {FeatureSpec{spec_a, ""}, FeatureSpec{spec_b, ""}, FeatureSpec{spec_c, ""}},
+ var_provider,
+ {FullPackageSpec{spec_a}, FullPackageSpec{spec_b}, FullPackageSpec{spec_c}},
StatusParagraphs(std::move(status_paragraphs)));
auto iterator_pos = [&](const PackageSpec& spec) {
- auto it =
- std::find_if(install_plan.begin(), install_plan.end(), [&](auto& action) { return action.spec() == spec; });
- REQUIRE(it != install_plan.end());
- return it - install_plan.begin();
+ auto it = std::find_if(install_plan.install_actions.begin(),
+ install_plan.install_actions.end(),
+ [&](auto& action) { return action.spec == spec; });
+ REQUIRE(it != install_plan.install_actions.end());
+ return it - install_plan.install_actions.begin();
};
const auto a_pos = iterator_pos(spec_a);
@@ -184,12 +139,14 @@ TEST_CASE ("existing package scheme", "[plan]")
PackageSpecMap spec_map;
auto spec_a = FullPackageSpec{spec_map.emplace("a")};
+ PortFileProvider::MapPortFileProvider map_port{spec_map.map};
+ MockCMakeVarProvider var_provider;
+
auto install_plan = Dependencies::create_feature_install_plan(
- spec_map.map, FullPackageSpec::to_feature_specs({spec_a}), StatusParagraphs(std::move(status_paragraphs)));
+ map_port, var_provider, {spec_a}, StatusParagraphs(std::move(status_paragraphs)));
REQUIRE(install_plan.size() == 1);
- const auto p = install_plan.at(0).install_action.get();
- REQUIRE(p);
+ const auto p = &install_plan.already_installed.at(0);
REQUIRE(p->spec.name() == "a");
REQUIRE(p->plan_type == Dependencies::InstallPlanType::ALREADY_INSTALLED);
REQUIRE(p->request_type == Dependencies::RequestType::USER_REQUESTED);
@@ -203,18 +160,19 @@ TEST_CASE ("user requested package scheme", "[plan]")
const auto spec_a = FullPackageSpec{spec_map.emplace("a", "b")};
const auto spec_b = FullPackageSpec{spec_map.emplace("b")};
+ PortFileProvider::MapPortFileProvider map_port{spec_map.map};
+ MockCMakeVarProvider var_provider;
+
const auto install_plan = Dependencies::create_feature_install_plan(
- spec_map.map, FullPackageSpec::to_feature_specs({spec_a}), StatusParagraphs(std::move(status_paragraphs)));
+ map_port, var_provider, {spec_a}, StatusParagraphs(std::move(status_paragraphs)));
REQUIRE(install_plan.size() == 2);
- const auto p = install_plan.at(0).install_action.get();
- REQUIRE(p);
+ const auto p = &install_plan.install_actions.at(0);
REQUIRE(p->spec.name() == "b");
REQUIRE(p->plan_type == Dependencies::InstallPlanType::BUILD_AND_INSTALL);
REQUIRE(p->request_type == Dependencies::RequestType::AUTO_SELECTED);
- const auto p2 = install_plan.at(1).install_action.get();
- REQUIRE(p2);
+ const auto p2 = &install_plan.install_actions.at(1);
REQUIRE(p2->spec.name() == "a");
REQUIRE(p2->plan_type == Dependencies::InstallPlanType::BUILD_AND_INSTALL);
REQUIRE(p2->request_type == Dependencies::RequestType::USER_REQUESTED);
@@ -239,19 +197,21 @@ TEST_CASE ("long install scheme", "[plan]")
auto spec_j = spec_map.emplace("j", "k");
auto spec_k = spec_map.emplace("k");
- Dependencies::MapPortFileProvider map_port(spec_map.map);
- auto install_plan = Dependencies::create_feature_install_plan(
- map_port, {FeatureSpec{spec_a, ""}}, StatusParagraphs(std::move(status_paragraphs)));
+ PortFileProvider::MapPortFileProvider map_port(spec_map.map);
+ MockCMakeVarProvider var_provider;
+ auto plan = Dependencies::create_feature_install_plan(
+ map_port, var_provider, {FullPackageSpec{spec_a}}, StatusParagraphs(std::move(status_paragraphs)));
+ auto& install_plan = plan.install_actions;
REQUIRE(install_plan.size() == 8);
- REQUIRE(install_plan.at(0).spec().name() == "h");
- REQUIRE(install_plan.at(1).spec().name() == "g");
- REQUIRE(install_plan.at(2).spec().name() == "f");
- REQUIRE(install_plan.at(3).spec().name() == "e");
- REQUIRE(install_plan.at(4).spec().name() == "d");
- REQUIRE(install_plan.at(5).spec().name() == "c");
- REQUIRE(install_plan.at(6).spec().name() == "b");
- REQUIRE(install_plan.at(7).spec().name() == "a");
+ REQUIRE(install_plan.at(0).spec.name() == "h");
+ REQUIRE(install_plan.at(1).spec.name() == "g");
+ REQUIRE(install_plan.at(2).spec.name() == "f");
+ REQUIRE(install_plan.at(3).spec.name() == "e");
+ REQUIRE(install_plan.at(4).spec.name() == "d");
+ REQUIRE(install_plan.at(5).spec.name() == "c");
+ REQUIRE(install_plan.at(6).spec.name() == "b");
+ REQUIRE(install_plan.at(7).spec.name() == "a");
}
TEST_CASE ("basic feature test 1", "[plan]")
@@ -265,14 +225,17 @@ TEST_CASE ("basic feature test 1", "[plan]")
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, FullPackageSpec::to_feature_specs({spec_a}), StatusParagraphs(std::move(status_paragraphs)));
+ PortFileProvider::MapPortFileProvider map_port{spec_map.map};
+ MockCMakeVarProvider var_provider;
- REQUIRE(install_plan.size() == 4);
- remove_plan_check(install_plan.at(0), "a");
- remove_plan_check(install_plan.at(1), "b");
- features_check(install_plan.at(2), "b", {"b1", "core", "b1"});
- features_check(install_plan.at(3), "a", {"a1", "core"});
+ auto plan = Dependencies::create_feature_install_plan(
+ map_port, var_provider, {spec_a}, StatusParagraphs(std::move(status_paragraphs)));
+
+ REQUIRE(plan.size() == 4);
+ remove_plan_check(plan.remove_actions.at(0), "a");
+ remove_plan_check(plan.remove_actions.at(1), "b");
+ features_check(plan.install_actions.at(0), "b", {"b1", "core", "b1"});
+ features_check(plan.install_actions.at(1), "a", {"a1", "core"});
}
TEST_CASE ("basic feature test 2", "[plan]")
@@ -284,9 +247,13 @@ TEST_CASE ("basic feature test 2", "[plan]")
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, FullPackageSpec::to_feature_specs({spec_a}), StatusParagraphs(std::move(status_paragraphs)));
+ PortFileProvider::MapPortFileProvider map_port{spec_map.map};
+ MockCMakeVarProvider var_provider;
+
+ auto plan = Dependencies::create_feature_install_plan(
+ map_port, var_provider, {spec_a}, StatusParagraphs(std::move(status_paragraphs)));
+ auto& install_plan = plan.install_actions;
REQUIRE(install_plan.size() == 2);
features_check(install_plan.at(0), "b", {"b1", "b2", "core"});
features_check(install_plan.at(1), "a", {"a1", "core"});
@@ -303,15 +270,18 @@ TEST_CASE ("basic feature test 3", "[plan]")
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,
- FullPackageSpec::to_feature_specs({spec_c, spec_a}),
- StatusParagraphs(std::move(status_paragraphs)));
+ PortFileProvider::MapPortFileProvider map_port{spec_map.map};
+ MockCMakeVarProvider var_provider;
+
+ auto plan = Dependencies::create_feature_install_plan(
+ map_port, var_provider, {spec_c, spec_a}, StatusParagraphs(std::move(status_paragraphs)));
- REQUIRE(install_plan.size() == 4);
- remove_plan_check(install_plan.at(0), "a");
- features_check(install_plan.at(1), "b", {"core"});
- features_check(install_plan.at(2), "a", {"a1", "core"});
- features_check(install_plan.at(3), "c", {"core"});
+ REQUIRE(plan.size() == 4);
+ remove_plan_check(plan.remove_actions.at(0), "a");
+ auto& install_plan = plan.install_actions;
+ features_check(install_plan.at(0), "b", {"core"});
+ features_check(install_plan.at(1), "a", {"a1", "core"});
+ features_check(install_plan.at(2), "c", {"core"});
}
TEST_CASE ("basic feature test 4", "[plan]")
@@ -326,11 +296,14 @@ TEST_CASE ("basic feature test 4", "[plan]")
auto spec_b = FullPackageSpec{spec_map.emplace("b")};
auto spec_c = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}};
+ PortFileProvider::MapPortFileProvider map_port{spec_map.map};
+ MockCMakeVarProvider var_provider;
+
auto install_plan = Dependencies::create_feature_install_plan(
- spec_map.map, FullPackageSpec::to_feature_specs({spec_c}), StatusParagraphs(std::move(status_paragraphs)));
+ map_port, var_provider, {spec_c}, StatusParagraphs(std::move(status_paragraphs)));
REQUIRE(install_plan.size() == 1);
- features_check(install_plan.at(0), "c", {"core"});
+ features_check(install_plan.install_actions.at(0), "c", {"core"});
}
TEST_CASE ("basic feature test 5", "[plan]")
@@ -343,12 +316,15 @@ TEST_CASE ("basic feature test 5", "[plan]")
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", ""}})};
+ PortFileProvider::MapPortFileProvider map_port{spec_map.map};
+ MockCMakeVarProvider var_provider;
+
auto install_plan = Dependencies::create_feature_install_plan(
- spec_map.map, FullPackageSpec::to_feature_specs({spec_a}), StatusParagraphs(std::move(status_paragraphs)));
+ map_port, var_provider, {spec_a}, StatusParagraphs(std::move(status_paragraphs)));
REQUIRE(install_plan.size() == 2);
- features_check(install_plan.at(0), "b", {"core", "b2"});
- features_check(install_plan.at(1), "a", {"core", "a3", "a2"});
+ features_check(install_plan.install_actions.at(0), "b", {"core", "b2"});
+ features_check(install_plan.install_actions.at(1), "a", {"core", "a3", "a2"});
}
TEST_CASE ("basic feature test 6", "[plan]")
@@ -360,14 +336,16 @@ TEST_CASE ("basic feature test 6", "[plan]")
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,
- FullPackageSpec::to_feature_specs({spec_a, spec_b}),
- StatusParagraphs(std::move(status_paragraphs)));
+ PortFileProvider::MapPortFileProvider map_port{spec_map.map};
+ MockCMakeVarProvider var_provider;
- REQUIRE(install_plan.size() == 3);
- remove_plan_check(install_plan.at(0), "b");
- features_check(install_plan.at(1), "b", {"core", "b1"});
- features_check(install_plan.at(2), "a", {"core"});
+ auto plan = Dependencies::create_feature_install_plan(
+ map_port, var_provider, {spec_a, spec_b}, StatusParagraphs(std::move(status_paragraphs)));
+
+ REQUIRE(plan.size() == 3);
+ remove_plan_check(plan.remove_actions.at(0), "b");
+ features_check(plan.install_actions.at(0), "b", {"core", "b1"});
+ features_check(plan.install_actions.at(1), "a", {"core"});
}
TEST_CASE ("basic feature test 7", "[plan]")
@@ -382,20 +360,23 @@ TEST_CASE ("basic feature test 7", "[plan]")
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, FullPackageSpec::to_feature_specs({spec_b}), StatusParagraphs(std::move(status_paragraphs)));
+ PortFileProvider::MapPortFileProvider map_port{spec_map.map};
+ MockCMakeVarProvider var_provider;
+
+ auto plan = Dependencies::create_feature_install_plan(
+ map_port, var_provider, {spec_b}, StatusParagraphs(std::move(status_paragraphs)));
- REQUIRE(install_plan.size() == 5);
- remove_plan_check(install_plan.at(0), "x");
- remove_plan_check(install_plan.at(1), "b");
+ REQUIRE(plan.size() == 5);
+ remove_plan_check(plan.remove_actions.at(0), "x");
+ remove_plan_check(plan.remove_actions.at(1), "b");
// TODO: order here may change but A < X, and B anywhere
- features_check(install_plan.at(2), "b", {"core", "b1"});
- features_check(install_plan.at(3), "a", {"core"});
- features_check(install_plan.at(4), "x", {"core"});
+ features_check(plan.install_actions.at(0), "b", {"core", "b1"});
+ features_check(plan.install_actions.at(1), "a", {"core"});
+ features_check(plan.install_actions.at(2), "x", {"core"});
}
-TEST_CASE ("basic feature test 8", "[plan][!mayfail]")
+TEST_CASE ("basic feature test 8", "[plan]")
{
std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
status_paragraphs.push_back(make_status_pgh("a"));
@@ -413,19 +394,23 @@ TEST_CASE ("basic feature test 8", "[plan][!mayfail]")
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,
- FullPackageSpec::to_feature_specs({spec_c_64, spec_a_86, spec_a_64, spec_c_86}),
- StatusParagraphs(std::move(status_paragraphs)));
+ PortFileProvider::MapPortFileProvider map_port{spec_map.map};
+ MockCMakeVarProvider var_provider;
+
+ auto plan = Dependencies::create_feature_install_plan(map_port,
+ var_provider,
+ {spec_c_64, spec_a_86, spec_a_64, spec_c_86},
+ StatusParagraphs(std::move(status_paragraphs)));
- remove_plan_check(install_plan.at(0), "a", Triplet::X64_WINDOWS);
- remove_plan_check(install_plan.at(1), "a");
- features_check(install_plan.at(2), "b", {"core"}, Triplet::X64_WINDOWS);
- features_check(install_plan.at(3), "a", {"a1", "core"}, Triplet::X64_WINDOWS);
+ remove_plan_check(plan.remove_actions.at(0), "a", Triplet::X64_WINDOWS);
+ remove_plan_check(plan.remove_actions.at(1), "a");
+ auto& install_plan = plan.install_actions;
+ features_check(install_plan.at(0), "b", {"core"}, Triplet::X64_WINDOWS);
+ features_check(install_plan.at(1), "a", {"a1", "core"}, Triplet::X64_WINDOWS);
+ features_check(install_plan.at(2), "b", {"core"});
+ features_check(install_plan.at(3), "a", {"a1", "core"});
features_check(install_plan.at(4), "c", {"core"}, Triplet::X64_WINDOWS);
- features_check(install_plan.at(5), "b", {"core"});
- features_check(install_plan.at(6), "a", {"a1", "core"});
- features_check(install_plan.at(7), "c", {"core"});
+ features_check(install_plan.at(5), "c", {"core"});
}
TEST_CASE ("install all features test", "[plan]")
@@ -438,13 +423,17 @@ TEST_CASE ("install all features test", "[plan]")
auto install_specs = FullPackageSpec::from_string("a[*]", Triplet::X64_WINDOWS);
REQUIRE(install_specs.has_value());
if (!install_specs.has_value()) return;
- auto install_plan = Dependencies::create_feature_install_plan(
- spec_map.map,
- FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}),
- StatusParagraphs(std::move(status_paragraphs)));
+
+ PortFileProvider::MapPortFileProvider map_port{spec_map.map};
+ MockCMakeVarProvider var_provider;
+
+ auto install_plan = Dependencies::create_feature_install_plan(map_port,
+ var_provider,
+ {install_specs.value_or_exit(VCPKG_LINE_INFO)},
+ StatusParagraphs(std::move(status_paragraphs)));
REQUIRE(install_plan.size() == 1);
- features_check(install_plan.at(0), "a", {"0", "1", "core"}, Triplet::X64_WINDOWS);
+ features_check(install_plan.install_actions.at(0), "a", {"0", "1", "core"}, Triplet::X64_WINDOWS);
}
TEST_CASE ("install default features test 1", "[plan]")
@@ -457,14 +446,18 @@ TEST_CASE ("install default features test 1", "[plan]")
// Install "a" (without explicit feature specification)
auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS);
- auto install_plan = Dependencies::create_feature_install_plan(
- spec_map.map,
- FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}),
- StatusParagraphs(std::move(status_paragraphs)));
+
+ PortFileProvider::MapPortFileProvider map_port{spec_map.map};
+ MockCMakeVarProvider var_provider;
+
+ auto install_plan = Dependencies::create_feature_install_plan(map_port,
+ var_provider,
+ {install_specs.value_or_exit(VCPKG_LINE_INFO)},
+ StatusParagraphs(std::move(status_paragraphs)));
// Expect the default feature "1" to be installed, but not "0"
REQUIRE(install_plan.size() == 1);
- features_check(install_plan.at(0), "a", {"1", "core"}, Triplet::X64_WINDOWS);
+ features_check(install_plan.install_actions.at(0), "a", {"1", "core"}, Triplet::X64_WINDOWS);
}
TEST_CASE ("install default features test 2", "[plan]")
@@ -482,16 +475,20 @@ TEST_CASE ("install default features test 2", "[plan]")
// Install "a" (without explicit feature specification)
auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS);
- auto install_plan = Dependencies::create_feature_install_plan(
- spec_map.map,
- FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}),
- StatusParagraphs(std::move(status_paragraphs)));
+
+ PortFileProvider::MapPortFileProvider map_port{spec_map.map};
+ MockCMakeVarProvider var_provider;
+
+ auto install_plan = Dependencies::create_feature_install_plan(map_port,
+ var_provider,
+ {install_specs.value_or_exit(VCPKG_LINE_INFO)},
+ StatusParagraphs(std::move(status_paragraphs)));
// Expect "a" to get removed for rebuild and then installed with default
// features.
REQUIRE(install_plan.size() == 2);
- remove_plan_check(install_plan.at(0), "a", Triplet::X64_WINDOWS);
- features_check(install_plan.at(1), "a", {"a1", "core"}, Triplet::X64_WINDOWS);
+ remove_plan_check(install_plan.remove_actions.at(0), "a", Triplet::X64_WINDOWS);
+ features_check(install_plan.install_actions.at(0), "a", {"a1", "core"}, Triplet::X64_WINDOWS);
}
TEST_CASE ("install default features test 3", "[plan]")
@@ -504,14 +501,18 @@ TEST_CASE ("install default features test 3", "[plan]")
// Explicitly install "a" without default features
auto install_specs = FullPackageSpec::from_string("a[core]", Triplet::X64_WINDOWS);
- auto install_plan = Dependencies::create_feature_install_plan(
- spec_map.map,
- FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}),
- StatusParagraphs(std::move(status_paragraphs)));
+
+ PortFileProvider::MapPortFileProvider map_port{spec_map.map};
+ MockCMakeVarProvider var_provider;
+
+ auto install_plan = Dependencies::create_feature_install_plan(map_port,
+ var_provider,
+ {install_specs.value_or_exit(VCPKG_LINE_INFO)},
+ StatusParagraphs(std::move(status_paragraphs)));
// Expect the default feature not to get installed.
REQUIRE(install_plan.size() == 1);
- features_check(install_plan.at(0), "a", {"core"}, Triplet::X64_WINDOWS);
+ features_check(install_plan.install_actions.at(0), "a", {"core"}, Triplet::X64_WINDOWS);
}
TEST_CASE ("install default features of dependency test 1", "[plan]")
@@ -526,16 +527,76 @@ TEST_CASE ("install default features of dependency test 1", "[plan]")
// Install "a" (without explicit feature specification)
auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS);
+ PortFileProvider::MapPortFileProvider map_port{spec_map.map};
+ MockCMakeVarProvider var_provider;
+
+ auto install_plan = Dependencies::create_feature_install_plan(map_port,
+ var_provider,
+ {install_specs.value_or_exit(VCPKG_LINE_INFO)},
+ StatusParagraphs(std::move(status_paragraphs)));
+
+ // Expect "a" to get installed and defaults of "b" through the dependency,
+ // as no explicit features of "b" are installed by the user.
+ REQUIRE(install_plan.size() == 2);
+ features_check(install_plan.install_actions.at(0), "b", {"b1", "core"}, Triplet::X64_WINDOWS);
+ features_check(install_plan.install_actions.at(1), "a", {"core"}, Triplet::X64_WINDOWS);
+}
+
+TEST_CASE ("do not install default features of dependency test 1", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+
+ // Add a port "a" which depends on the core of "b"
+ PackageSpecMap spec_map(Triplet::X64_WINDOWS);
+ spec_map.emplace("a", "b[core]");
+ // "b" has two features, of which "b1" is default.
+ spec_map.emplace("b", "", {{"b0", ""}, {"b1", ""}}, {"b1"});
+
+ // Install "a" (without explicit feature specification)
+ auto spec_a = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS);
+ auto spec_b = FullPackageSpec::from_string("b[core]", Triplet::X64_WINDOWS);
+ PortFileProvider::MapPortFileProvider map_port{spec_map.map};
+ MockCMakeVarProvider var_provider;
+
auto install_plan = Dependencies::create_feature_install_plan(
- spec_map.map,
- FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}),
+ map_port,
+ var_provider,
+ {spec_a.value_or_exit(VCPKG_LINE_INFO), spec_b.value_or_exit(VCPKG_LINE_INFO)},
StatusParagraphs(std::move(status_paragraphs)));
// Expect "a" to get installed and defaults of "b" through the dependency,
// as no explicit features of "b" are installed by the user.
REQUIRE(install_plan.size() == 2);
- features_check(install_plan.at(0), "b", {"b1", "core"}, Triplet::X64_WINDOWS);
- features_check(install_plan.at(1), "a", {"core"}, Triplet::X64_WINDOWS);
+ features_check(install_plan.install_actions.at(0), "b", {"core"}, Triplet::X64_WINDOWS);
+ features_check(install_plan.install_actions.at(1), "a", {"core"}, Triplet::X64_WINDOWS);
+}
+
+TEST_CASE ("install default features of dependency test 2", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+
+ // Add a port "a" which depends on the default features of "b"
+ PackageSpecMap spec_map(Triplet::X64_WINDOWS);
+ spec_map.emplace("a", "b");
+ // "b" has two features, of which "b1" is default.
+ spec_map.emplace("b", "", {{"b0", ""}, {"b1", ""}}, {"b1"});
+
+ // Install "a" (without explicit feature specification)
+ auto spec_a = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS);
+ auto spec_b = FullPackageSpec::from_string("b[core]", Triplet::X64_WINDOWS);
+ PortFileProvider::MapPortFileProvider map_port{spec_map.map};
+ MockCMakeVarProvider var_provider;
+
+ auto install_plan = Dependencies::create_feature_install_plan(
+ map_port,
+ var_provider,
+ {spec_a.value_or_exit(VCPKG_LINE_INFO), spec_b.value_or_exit(VCPKG_LINE_INFO)},
+ StatusParagraphs(std::move(status_paragraphs)));
+
+ // Expect "a" to get installed and defaults of "b" through the dependency
+ REQUIRE(install_plan.size() == 2);
+ features_check(install_plan.install_actions.at(0), "b", {"b1", "core"}, Triplet::X64_WINDOWS);
+ features_check(install_plan.install_actions.at(1), "a", {"core"}, Triplet::X64_WINDOWS);
}
TEST_CASE ("do not install default features of existing dependency", "[plan]")
@@ -554,17 +615,20 @@ TEST_CASE ("do not install default features of existing dependency", "[plan]")
// Install "a" (without explicit feature specification)
auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS);
- auto install_plan = Dependencies::create_feature_install_plan(
- spec_map.map,
- FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}),
- StatusParagraphs(std::move(status_paragraphs)));
+ PortFileProvider::MapPortFileProvider map_port{spec_map.map};
+ MockCMakeVarProvider var_provider;
+
+ auto install_plan = Dependencies::create_feature_install_plan(map_port,
+ var_provider,
+ {install_specs.value_or_exit(VCPKG_LINE_INFO)},
+ StatusParagraphs(std::move(status_paragraphs)));
// Expect "a" to get installed, but not require rebuilding "b"
REQUIRE(install_plan.size() == 1);
- features_check(install_plan.at(0), "a", {"core"}, Triplet::X64_WINDOWS);
+ features_check(install_plan.install_actions.at(0), "a", {"core"}, Triplet::X64_WINDOWS);
}
-TEST_CASE ("install default features of dependency test 2", "[plan]")
+TEST_CASE ("install default features of dependency test 3", "[plan]")
{
std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
status_paragraphs.push_back(make_status_pgh("b"));
@@ -580,15 +644,18 @@ TEST_CASE ("install default features of dependency test 2", "[plan]")
// Install "a" (without explicit feature specification)
auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS);
- auto install_plan = Dependencies::create_feature_install_plan(
- spec_map.map,
- FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}),
- StatusParagraphs(std::move(status_paragraphs)));
+ PortFileProvider::MapPortFileProvider map_port{spec_map.map};
+ MockCMakeVarProvider var_provider;
+
+ auto install_plan = Dependencies::create_feature_install_plan(map_port,
+ var_provider,
+ {install_specs.value_or_exit(VCPKG_LINE_INFO)},
+ StatusParagraphs(std::move(status_paragraphs)));
// Expect "a" to get installed, not the defaults of "b", as the required
// dependencies are already there, installed explicitly by the user.
REQUIRE(install_plan.size() == 1);
- features_check(install_plan.at(0), "a", {"core"}, Triplet::X64_WINDOWS);
+ features_check(install_plan.install_actions.at(0), "a", {"core"}, Triplet::X64_WINDOWS);
}
TEST_CASE ("install plan action dependencies", "[plan]")
@@ -604,19 +671,23 @@ TEST_CASE ("install plan action dependencies", "[plan]")
// Install "a" (without explicit feature specification)
auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS);
- auto install_plan = Dependencies::create_feature_install_plan(
- spec_map.map,
- FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}),
- StatusParagraphs(std::move(status_paragraphs)));
+ PortFileProvider::MapPortFileProvider map_port{spec_map.map};
+ MockCMakeVarProvider var_provider;
+
+ auto install_plan = Dependencies::create_feature_install_plan(map_port,
+ var_provider,
+ {install_specs.value_or_exit(VCPKG_LINE_INFO)},
+ StatusParagraphs(std::move(status_paragraphs)));
REQUIRE(install_plan.size() == 3);
- features_check(install_plan.at(0), "c", {"core"}, Triplet::X64_WINDOWS);
+ features_check(install_plan.install_actions.at(0), "c", {"core"}, Triplet::X64_WINDOWS);
- features_check(install_plan.at(1), "b", {"core"}, Triplet::X64_WINDOWS);
- REQUIRE(install_plan.at(1).install_action.get()->computed_dependencies == std::vector<PackageSpec>{spec_c});
+ // TODO: Figure out what to do with these tests
+ features_check(install_plan.install_actions.at(1), "b", {"core"}, Triplet::X64_WINDOWS);
+ // REQUIRE(install_plan.at(1).install_action.get()->computed_dependencies == std::vector<PackageSpec>{spec_c});
- features_check(install_plan.at(2), "a", {"core"}, Triplet::X64_WINDOWS);
- REQUIRE(install_plan.at(2).install_action.get()->computed_dependencies == std::vector<PackageSpec>{spec_b});
+ features_check(install_plan.install_actions.at(2), "a", {"core"}, Triplet::X64_WINDOWS);
+ // REQUIRE(install_plan.at(2).install_action.get()->computed_dependencies == std::vector<PackageSpec>{spec_b});
}
TEST_CASE ("install plan action dependencies 2", "[plan]")
@@ -632,19 +703,23 @@ TEST_CASE ("install plan action dependencies 2", "[plan]")
// Install "a" (without explicit feature specification)
auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS);
- auto install_plan = Dependencies::create_feature_install_plan(
- spec_map.map,
- FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}),
- StatusParagraphs(std::move(status_paragraphs)));
+ PortFileProvider::MapPortFileProvider map_port{spec_map.map};
+ MockCMakeVarProvider var_provider;
+
+ auto install_plan = Dependencies::create_feature_install_plan(map_port,
+ var_provider,
+ {install_specs.value_or_exit(VCPKG_LINE_INFO)},
+ StatusParagraphs(std::move(status_paragraphs)));
REQUIRE(install_plan.size() == 3);
- features_check(install_plan.at(0), "c", {"core"}, Triplet::X64_WINDOWS);
+ features_check(install_plan.install_actions.at(0), "c", {"core"}, Triplet::X64_WINDOWS);
- features_check(install_plan.at(1), "b", {"core"}, Triplet::X64_WINDOWS);
- REQUIRE(install_plan.at(1).install_action.get()->computed_dependencies == std::vector<PackageSpec>{spec_c});
+ features_check(install_plan.install_actions.at(1), "b", {"core"}, Triplet::X64_WINDOWS);
+ // REQUIRE(install_plan.at(1).install_action.get()->computed_dependencies == std::vector<PackageSpec>{spec_c});
- features_check(install_plan.at(2), "a", {"core"}, Triplet::X64_WINDOWS);
- REQUIRE(install_plan.at(2).install_action.get()->computed_dependencies == std::vector<PackageSpec>{spec_b, spec_c});
+ features_check(install_plan.install_actions.at(2), "a", {"core"}, Triplet::X64_WINDOWS);
+ // REQUIRE(install_plan.at(2).install_action.get()->computed_dependencies == std::vector<PackageSpec>{spec_b,
+ // spec_c});
}
TEST_CASE ("install plan action dependencies 3", "[plan]")
@@ -658,14 +733,17 @@ TEST_CASE ("install plan action dependencies 3", "[plan]")
// Install "a" (without explicit feature specification)
auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS);
- auto install_plan = Dependencies::create_feature_install_plan(
- spec_map.map,
- FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}),
- StatusParagraphs(std::move(status_paragraphs)));
+ PortFileProvider::MapPortFileProvider map_port{spec_map.map};
+ MockCMakeVarProvider var_provider;
+
+ auto install_plan = Dependencies::create_feature_install_plan(map_port,
+ var_provider,
+ {install_specs.value_or_exit(VCPKG_LINE_INFO)},
+ StatusParagraphs(std::move(status_paragraphs)));
REQUIRE(install_plan.size() == 1);
- features_check(install_plan.at(0), "a", {"1", "0", "core"}, Triplet::X64_WINDOWS);
- REQUIRE(install_plan.at(0).install_action.get()->computed_dependencies == std::vector<PackageSpec>{});
+ features_check(install_plan.install_actions.at(0), "a", {"1", "0", "core"}, Triplet::X64_WINDOWS);
+ // REQUIRE(install_plan.at(0).install_action.get()->computed_dependencies == std::vector<PackageSpec>{});
}
TEST_CASE ("install with default features", "[plan]")
@@ -678,14 +756,20 @@ TEST_CASE ("install with default features", "[plan]")
auto b_spec = spec_map.emplace("b", "", {{"0", ""}}, {"0"});
auto a_spec = spec_map.emplace("a", "b[core]", {{"0", ""}});
- // Install "a" and indicate that "b" should not install default features
- auto install_plan = Dependencies::create_feature_install_plan(
- spec_map.map, {FeatureSpec{a_spec, "0"}, FeatureSpec{b_spec, "core"}}, status_db);
+ PortFileProvider::MapPortFileProvider map_port{spec_map.map};
+ MockCMakeVarProvider var_provider;
+
+ auto install_plan =
+ Dependencies::create_feature_install_plan(map_port,
+ var_provider,
+ {FullPackageSpec{a_spec, {"0"}}, FullPackageSpec{b_spec, {"core"}}},
+ StatusParagraphs(std::move(status_db)));
+ // Install "a" and indicate that "b" should not install default features
REQUIRE(install_plan.size() == 3);
- remove_plan_check(install_plan.at(0), "a");
- features_check(install_plan.at(1), "b", {"core"});
- features_check(install_plan.at(2), "a", {"0", "core"});
+ remove_plan_check(install_plan.remove_actions.at(0), "a");
+ features_check(install_plan.install_actions.at(0), "b", {"core"});
+ features_check(install_plan.install_actions.at(1), "a", {"0", "core"});
}
TEST_CASE ("upgrade with default features 1", "[plan]")
@@ -699,18 +783,15 @@ TEST_CASE ("upgrade with default features 1", "[plan]")
PackageSpecMap spec_map;
auto spec_a = spec_map.emplace("a", "", {{"0", ""}, {"1", ""}}, {"1"});
- Dependencies::MapPortFileProvider provider(spec_map.map);
- Dependencies::PackageGraph graph(provider, status_db);
-
- graph.upgrade(spec_a);
- auto plan = graph.serialize();
+ PortFileProvider::MapPortFileProvider provider(spec_map.map);
+ MockCMakeVarProvider var_provider;
+ auto plan = Dependencies::create_upgrade_plan(provider, var_provider, {spec_a}, status_db);
// The upgrade should not install the default feature
REQUIRE(plan.size() == 2);
- REQUIRE(plan.at(0).spec().name() == "a");
- remove_plan_check(plan.at(0), "a");
- features_check(plan.at(1), "a", {"core", "0"});
+ remove_plan_check(plan.remove_actions.at(0), "a");
+ features_check(plan.install_actions.at(0), "a", {"core", "0"});
}
TEST_CASE ("upgrade with default features 2", "[plan]")
@@ -726,19 +807,16 @@ TEST_CASE ("upgrade with default features 2", "[plan]")
auto spec_a = spec_map.emplace("a", "b[core]");
auto spec_b = spec_map.emplace("b", "", {{"b0", ""}, {"b1", ""}}, {"b0", "b1"});
- Dependencies::MapPortFileProvider provider(spec_map.map);
- Dependencies::PackageGraph graph(provider, status_db);
-
- graph.upgrade(spec_a);
- graph.upgrade(spec_b);
- auto plan = graph.serialize();
+ PortFileProvider::MapPortFileProvider provider(spec_map.map);
+ MockCMakeVarProvider var_provider;
+ auto plan = Dependencies::create_upgrade_plan(provider, var_provider, {spec_a, spec_b}, status_db);
// The upgrade should install the new default feature b1 but not b0
REQUIRE(plan.size() == 4);
- remove_plan_check(plan.at(0), "a", Triplet::X64_WINDOWS);
- remove_plan_check(plan.at(1), "b", Triplet::X64_WINDOWS);
- features_check(plan.at(2), "b", {"core", "b1"}, Triplet::X64_WINDOWS);
- features_check(plan.at(3), "a", {"core"}, Triplet::X64_WINDOWS);
+ remove_plan_check(plan.remove_actions.at(0), "a", Triplet::X64_WINDOWS);
+ remove_plan_check(plan.remove_actions.at(1), "b", Triplet::X64_WINDOWS);
+ features_check(plan.install_actions.at(0), "b", {"core", "b1"}, Triplet::X64_WINDOWS);
+ features_check(plan.install_actions.at(1), "a", {"core"}, Triplet::X64_WINDOWS);
}
TEST_CASE ("upgrade with default features 3", "[plan]")
@@ -754,17 +832,15 @@ TEST_CASE ("upgrade with default features 3", "[plan]")
auto spec_a = spec_map.emplace("a", "b[core]");
spec_map.emplace("b", "", {{"b0", ""}, {"b1", ""}}, {"b0"});
- Dependencies::MapPortFileProvider provider(spec_map.map);
- Dependencies::PackageGraph graph(provider, status_db);
-
- graph.upgrade(spec_a);
- auto plan = graph.serialize();
+ PortFileProvider::MapPortFileProvider provider(spec_map.map);
+ MockCMakeVarProvider var_provider;
+ auto plan = Dependencies::create_upgrade_plan(provider, var_provider, {spec_a}, status_db);
// The upgrade should install the default feature
REQUIRE(plan.size() == 3);
- remove_plan_check(plan.at(0), "a", Triplet::X64_WINDOWS);
- features_check(plan.at(1), "b", {"b0", "core"}, Triplet::X64_WINDOWS);
- features_check(plan.at(2), "a", {"core"}, Triplet::X64_WINDOWS);
+ remove_plan_check(plan.remove_actions.at(0), "a", Triplet::X64_WINDOWS);
+ features_check(plan.install_actions.at(0), "b", {"b0", "core"}, Triplet::X64_WINDOWS);
+ features_check(plan.install_actions.at(1), "a", {"core"}, Triplet::X64_WINDOWS);
}
TEST_CASE ("upgrade with new default feature", "[plan]")
@@ -777,16 +853,14 @@ TEST_CASE ("upgrade with new default feature", "[plan]")
PackageSpecMap spec_map;
auto spec_a = spec_map.emplace("a", "", {{"0", ""}, {"1", ""}, {"2", ""}}, {"0", "1"});
- Dependencies::MapPortFileProvider provider(spec_map.map);
- Dependencies::PackageGraph graph(provider, status_db);
-
- graph.upgrade(spec_a);
- auto plan = graph.serialize();
+ PortFileProvider::MapPortFileProvider provider(spec_map.map);
+ MockCMakeVarProvider var_provider;
+ auto plan = Dependencies::create_upgrade_plan(provider, var_provider, {spec_a}, status_db);
// The upgrade should install the new default feature but not the old default feature 0
REQUIRE(plan.size() == 2);
- remove_plan_check(plan.at(0), "a", Triplet::X86_WINDOWS);
- features_check(plan.at(1), "a", {"core", "1"}, Triplet::X86_WINDOWS);
+ remove_plan_check(plan.remove_actions.at(0), "a", Triplet::X86_WINDOWS);
+ features_check(plan.install_actions.at(0), "a", {"core", "1"}, Triplet::X86_WINDOWS);
}
TEST_CASE ("transitive features test", "[plan]")
@@ -801,15 +875,18 @@ TEST_CASE ("transitive features test", "[plan]")
auto install_specs = FullPackageSpec::from_string("a[*]", Triplet::X64_WINDOWS);
REQUIRE(install_specs.has_value());
if (!install_specs.has_value()) return;
- auto install_plan = Dependencies::create_feature_install_plan(
- spec_map.map,
- FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}),
- StatusParagraphs(std::move(status_paragraphs)));
+
+ PortFileProvider::MapPortFileProvider provider(spec_map.map);
+ MockCMakeVarProvider var_provider;
+ auto install_plan = Dependencies::create_feature_install_plan(provider,
+ var_provider,
+ {install_specs.value_or_exit(VCPKG_LINE_INFO)},
+ StatusParagraphs(std::move(status_paragraphs)));
REQUIRE(install_plan.size() == 3);
- features_check(install_plan.at(0), "c", {"0", "core"}, Triplet::X64_WINDOWS);
- features_check(install_plan.at(1), "b", {"0", "core"}, Triplet::X64_WINDOWS);
- features_check(install_plan.at(2), "a", {"0", "core"}, Triplet::X64_WINDOWS);
+ features_check(install_plan.install_actions.at(0), "c", {"0", "core"}, Triplet::X64_WINDOWS);
+ features_check(install_plan.install_actions.at(1), "b", {"0", "core"}, Triplet::X64_WINDOWS);
+ features_check(install_plan.install_actions.at(2), "a", {"0", "core"}, Triplet::X64_WINDOWS);
}
TEST_CASE ("no transitive features test", "[plan]")
@@ -824,15 +901,17 @@ TEST_CASE ("no transitive features test", "[plan]")
auto install_specs = FullPackageSpec::from_string("a[*]", Triplet::X64_WINDOWS);
REQUIRE(install_specs.has_value());
if (!install_specs.has_value()) return;
- auto install_plan = Dependencies::create_feature_install_plan(
- spec_map.map,
- FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}),
- StatusParagraphs(std::move(status_paragraphs)));
+ PortFileProvider::MapPortFileProvider provider(spec_map.map);
+ MockCMakeVarProvider var_provider;
+ auto install_plan = Dependencies::create_feature_install_plan(provider,
+ var_provider,
+ {install_specs.value_or_exit(VCPKG_LINE_INFO)},
+ StatusParagraphs(std::move(status_paragraphs)));
REQUIRE(install_plan.size() == 3);
- features_check(install_plan.at(0), "c", {"core"}, Triplet::X64_WINDOWS);
- features_check(install_plan.at(1), "b", {"core"}, Triplet::X64_WINDOWS);
- features_check(install_plan.at(2), "a", {"0", "core"}, Triplet::X64_WINDOWS);
+ features_check(install_plan.install_actions.at(0), "c", {"core"}, Triplet::X64_WINDOWS);
+ features_check(install_plan.install_actions.at(1), "b", {"core"}, Triplet::X64_WINDOWS);
+ features_check(install_plan.install_actions.at(2), "a", {"0", "core"}, Triplet::X64_WINDOWS);
}
TEST_CASE ("only transitive features test", "[plan]")
@@ -847,15 +926,17 @@ TEST_CASE ("only transitive features test", "[plan]")
auto install_specs = FullPackageSpec::from_string("a[*]", Triplet::X64_WINDOWS);
REQUIRE(install_specs.has_value());
if (!install_specs.has_value()) return;
- auto install_plan = Dependencies::create_feature_install_plan(
- spec_map.map,
- FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}),
- StatusParagraphs(std::move(status_paragraphs)));
+ PortFileProvider::MapPortFileProvider provider(spec_map.map);
+ MockCMakeVarProvider var_provider;
+ auto install_plan = Dependencies::create_feature_install_plan(provider,
+ var_provider,
+ {install_specs.value_or_exit(VCPKG_LINE_INFO)},
+ StatusParagraphs(std::move(status_paragraphs)));
REQUIRE(install_plan.size() == 3);
- features_check(install_plan.at(0), "c", {"0", "core"}, Triplet::X64_WINDOWS);
- features_check(install_plan.at(1), "b", {"0", "core"}, Triplet::X64_WINDOWS);
- features_check(install_plan.at(2), "a", {"0", "core"}, Triplet::X64_WINDOWS);
+ features_check(install_plan.install_actions.at(0), "c", {"0", "core"}, Triplet::X64_WINDOWS);
+ features_check(install_plan.install_actions.at(1), "b", {"0", "core"}, Triplet::X64_WINDOWS);
+ features_check(install_plan.install_actions.at(2), "a", {"0", "core"}, Triplet::X64_WINDOWS);
}
TEST_CASE ("basic remove scheme", "[plan]")
@@ -973,18 +1054,13 @@ TEST_CASE ("basic upgrade scheme", "[plan]")
PackageSpecMap spec_map;
auto spec_a = spec_map.emplace("a");
- Dependencies::MapPortFileProvider provider(spec_map.map);
- Dependencies::PackageGraph graph(provider, status_db);
-
- graph.upgrade(spec_a);
-
- auto plan = graph.serialize();
+ PortFileProvider::MapPortFileProvider provider(spec_map.map);
+ MockCMakeVarProvider var_provider;
+ auto plan = Dependencies::create_upgrade_plan(provider, var_provider, {spec_a}, status_db);
REQUIRE(plan.size() == 2);
- REQUIRE(plan.at(0).spec().name() == "a");
- REQUIRE(plan.at(0).remove_action.has_value());
- REQUIRE(plan.at(1).spec().name() == "a");
- REQUIRE(plan.at(1).install_action.has_value());
+ remove_plan_check(plan.remove_actions.at(0), "a");
+ features_check(plan.install_actions.at(0), "a", {"core"});
}
TEST_CASE ("basic upgrade scheme with recurse", "[plan]")
@@ -998,25 +1074,15 @@ TEST_CASE ("basic upgrade scheme with recurse", "[plan]")
auto spec_a = spec_map.emplace("a");
spec_map.emplace("b", "a");
- Dependencies::MapPortFileProvider provider(spec_map.map);
- Dependencies::PackageGraph graph(provider, status_db);
-
- graph.upgrade(spec_a);
-
- auto plan = graph.serialize();
+ PortFileProvider::MapPortFileProvider provider(spec_map.map);
+ MockCMakeVarProvider var_provider;
+ auto plan = Dependencies::create_upgrade_plan(provider, var_provider, {spec_a}, status_db);
REQUIRE(plan.size() == 4);
- REQUIRE(plan.at(0).spec().name() == "b");
- REQUIRE(plan.at(0).remove_action.has_value());
-
- REQUIRE(plan.at(1).spec().name() == "a");
- REQUIRE(plan.at(1).remove_action.has_value());
-
- REQUIRE(plan.at(2).spec().name() == "a");
- REQUIRE(plan.at(2).install_action.has_value());
-
- REQUIRE(plan.at(3).spec().name() == "b");
- REQUIRE(plan.at(3).install_action.has_value());
+ remove_plan_check(plan.remove_actions.at(0), "b");
+ remove_plan_check(plan.remove_actions.at(1), "a");
+ features_check(plan.install_actions.at(0), "a", {"core"});
+ features_check(plan.install_actions.at(1), "b", {"core"});
}
TEST_CASE ("basic upgrade scheme with bystander", "[plan]")
@@ -1030,18 +1096,13 @@ TEST_CASE ("basic upgrade scheme with bystander", "[plan]")
auto spec_a = spec_map.emplace("a");
spec_map.emplace("b", "a");
- Dependencies::MapPortFileProvider provider(spec_map.map);
- Dependencies::PackageGraph graph(provider, status_db);
-
- graph.upgrade(spec_a);
-
- auto plan = graph.serialize();
+ PortFileProvider::MapPortFileProvider provider(spec_map.map);
+ MockCMakeVarProvider var_provider;
+ auto plan = Dependencies::create_upgrade_plan(provider, var_provider, {spec_a}, status_db);
REQUIRE(plan.size() == 2);
- REQUIRE(plan.at(0).spec().name() == "a");
- REQUIRE(plan.at(0).remove_action.has_value());
- REQUIRE(plan.at(1).spec().name() == "a");
- REQUIRE(plan.at(1).install_action.has_value());
+ remove_plan_check(plan.remove_actions.at(0), "a");
+ features_check(plan.install_actions.at(0), "a", {"core"});
}
TEST_CASE ("basic upgrade scheme with new dep", "[plan]")
@@ -1054,20 +1115,14 @@ TEST_CASE ("basic upgrade scheme with new dep", "[plan]")
auto spec_a = spec_map.emplace("a", "b");
spec_map.emplace("b");
- Dependencies::MapPortFileProvider provider(spec_map.map);
- Dependencies::PackageGraph graph(provider, status_db);
-
- graph.upgrade(spec_a);
-
- auto plan = graph.serialize();
+ PortFileProvider::MapPortFileProvider provider(spec_map.map);
+ MockCMakeVarProvider var_provider;
+ auto plan = Dependencies::create_upgrade_plan(provider, var_provider, {spec_a}, status_db);
REQUIRE(plan.size() == 3);
- REQUIRE(plan.at(0).spec().name() == "a");
- REQUIRE(plan.at(0).remove_action.has_value());
- REQUIRE(plan.at(1).spec().name() == "b");
- REQUIRE(plan.at(1).install_action.has_value());
- REQUIRE(plan.at(2).spec().name() == "a");
- REQUIRE(plan.at(2).install_action.has_value());
+ remove_plan_check(plan.remove_actions.at(0), "a");
+ features_check(plan.install_actions.at(0), "b", {"core"});
+ features_check(plan.install_actions.at(1), "a", {"core"});
}
TEST_CASE ("basic upgrade scheme with features", "[plan]")
@@ -1080,19 +1135,13 @@ TEST_CASE ("basic upgrade scheme with features", "[plan]")
PackageSpecMap spec_map;
auto spec_a = spec_map.emplace("a", "", {{"a1", ""}});
- Dependencies::MapPortFileProvider provider(spec_map.map);
- Dependencies::PackageGraph graph(provider, status_db);
-
- graph.upgrade(spec_a);
-
- auto plan = graph.serialize();
+ PortFileProvider::MapPortFileProvider provider(spec_map.map);
+ MockCMakeVarProvider var_provider;
+ auto plan = Dependencies::create_upgrade_plan(provider, var_provider, {spec_a}, status_db);
REQUIRE(plan.size() == 2);
-
- REQUIRE(plan.at(0).spec().name() == "a");
- REQUIRE(plan.at(0).remove_action.has_value());
-
- features_check(plan.at(1), "a", {"core", "a1"});
+ remove_plan_check(plan.remove_actions.at(0), "a");
+ features_check(plan.install_actions.at(0), "a", {"core", "a1"});
}
TEST_CASE ("basic upgrade scheme with new default feature", "[plan]")
@@ -1106,19 +1155,13 @@ TEST_CASE ("basic upgrade scheme with new default feature", "[plan]")
PackageSpecMap spec_map;
auto spec_a = spec_map.emplace("a", "", {{"a1", ""}}, {"a1"});
- Dependencies::MapPortFileProvider provider(spec_map.map);
- Dependencies::PackageGraph graph(provider, status_db);
-
- graph.upgrade(spec_a);
-
- auto plan = graph.serialize();
+ PortFileProvider::MapPortFileProvider provider(spec_map.map);
+ MockCMakeVarProvider var_provider;
+ auto plan = Dependencies::create_upgrade_plan(provider, var_provider, {spec_a}, status_db);
REQUIRE(plan.size() == 2);
-
- REQUIRE(plan.at(0).spec().name() == "a");
- REQUIRE(plan.at(0).remove_action.has_value());
-
- features_check(plan.at(1), "a", {"core", "a1"});
+ remove_plan_check(plan.remove_actions.at(0), "a");
+ features_check(plan.install_actions.at(0), "a", {"core", "a1"});
}
TEST_CASE ("basic upgrade scheme with self features", "[plan]")
@@ -1132,21 +1175,13 @@ TEST_CASE ("basic upgrade scheme with self features", "[plan]")
PackageSpecMap spec_map;
auto spec_a = spec_map.emplace("a", "", {{"a1", ""}, {"a2", "a[a1]"}});
- Dependencies::MapPortFileProvider provider(spec_map.map);
- Dependencies::PackageGraph graph(provider, status_db);
-
- graph.upgrade(spec_a);
-
- auto plan = graph.serialize();
+ PortFileProvider::MapPortFileProvider provider(spec_map.map);
+ MockCMakeVarProvider var_provider;
+ auto plan = Dependencies::create_upgrade_plan(provider, var_provider, {spec_a}, status_db);
REQUIRE(plan.size() == 2);
-
- REQUIRE(plan.at(0).spec().name() == "a");
- REQUIRE(plan.at(0).remove_action.has_value());
-
- REQUIRE(plan.at(1).spec().name() == "a");
- REQUIRE(plan.at(1).install_action.has_value());
- REQUIRE(plan.at(1).install_action.get()->feature_list == std::set<std::string>{"core", "a1", "a2"});
+ remove_plan_check(plan.remove_actions.at(0), "a");
+ features_check(plan.install_actions.at(0), "a", {"a1", "a2", "core"});
}
TEST_CASE ("basic export scheme", "[plan]")
diff --git a/toolsrc/src/vcpkg-test/specifier.cpp b/toolsrc/src/vcpkg-test/specifier.cpp
index 33df8ba83..a0a3725e8 100644
--- a/toolsrc/src/vcpkg-test/specifier.cpp
+++ b/toolsrc/src/vcpkg-test/specifier.cpp
@@ -1,5 +1,6 @@
#include <catch2/catch.hpp>
+#include <vcpkg/base/system.print.h>
#include <vcpkg/base/util.h>
#include <vcpkg/packagespec.h>
@@ -14,11 +15,13 @@ TEST_CASE ("specifier conversion", "[specifier]")
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"}}});
-
+ auto fspecs = FullPackageSpec{a_spec, {"0", "1"}}.to_feature_specs({}, {});
+ auto fspecs2 = FullPackageSpec{b_spec, {"2", "3"}}.to_feature_specs({}, {});
+ Util::Vectors::append(&fspecs, fspecs2);
+ Util::sort(fspecs);
REQUIRE(fspecs.size() == SPEC_SIZE);
- std::array<const char*, SPEC_SIZE> features = {"", "0", "1", "", "2", "3"};
+ std::array<const char*, SPEC_SIZE> features = {"0", "1", "core", "2", "3", "core"};
std::array<PackageSpec*, SPEC_SIZE> specs = {&a_spec, &a_spec, &a_spec, &b_spec, &b_spec, &b_spec};
for (std::size_t i = 0; i < SPEC_SIZE; ++i)
@@ -101,19 +104,20 @@ TEST_CASE ("specifier parsing", "[specifier]")
auto zlib = vcpkg::FullPackageSpec::from_string("zlib[0,1]", Triplet::X86_UWP).value_or_exit(VCPKG_LINE_INFO);
auto openssl =
vcpkg::FullPackageSpec::from_string("openssl[*]", Triplet::X86_UWP).value_or_exit(VCPKG_LINE_INFO);
- auto specs = FullPackageSpec::to_feature_specs({zlib, openssl});
+ auto specs = zlib.to_feature_specs({}, {});
+ auto specs2 = openssl.to_feature_specs({}, {});
+ Util::Vectors::append(&specs, specs2);
Util::sort(specs);
+
auto spectargets = FeatureSpec::from_strings_and_triplet(
{
"openssl",
"zlib",
- "openssl[*]",
"zlib[0]",
"zlib[1]",
},
Triplet::X86_UWP);
Util::sort(spectargets);
-
REQUIRE(specs.size() == spectargets.size());
REQUIRE(Util::all_equal(specs, spectargets));
}
diff --git a/toolsrc/src/vcpkg-test/supports.cpp b/toolsrc/src/vcpkg-test/supports.cpp
deleted file mode 100644
index f4d8dc65a..000000000
--- a/toolsrc/src/vcpkg-test/supports.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-#include <catch2/catch.hpp>
-
-#include <vcpkg/sourceparagraph.h>
-
-using namespace vcpkg;
-using Parse::parse_comma_list;
-
-TEST_CASE ("parse supports all", "[supports]")
-{
- auto v = Supports::parse({
- "x64",
- "x86",
- "arm",
- "windows",
- "uwp",
- "v140",
- "v141",
- "crt-static",
- "crt-dynamic",
- });
-
- REQUIRE(v.has_value());
-
- REQUIRE(v.get()->is_supported(System::CPUArchitecture::X64,
- Supports::Platform::UWP,
- Supports::Linkage::DYNAMIC,
- Supports::ToolsetVersion::V140));
- REQUIRE(v.get()->is_supported(System::CPUArchitecture::ARM,
- Supports::Platform::WINDOWS,
- Supports::Linkage::STATIC,
- Supports::ToolsetVersion::V141));
-}
-
-TEST_CASE ("parse supports invalid", "[supports]")
-{
- auto v = Supports::parse({"arm64"});
-
- REQUIRE_FALSE(v.has_value());
-
- REQUIRE(v.error().size() == 1);
- REQUIRE(v.error().at(0) == "arm64");
-}
-
-TEST_CASE ("parse supports case sensitive", "[supports]")
-{
- auto v = Supports::parse({"Windows"});
-
- REQUIRE_FALSE(v.has_value());
- REQUIRE(v.error().size() == 1);
- REQUIRE(v.error().at(0) == "Windows");
-}
-
-TEST_CASE ("parse supports some", "[supports]")
-{
- auto v = Supports::parse({
- "x64",
- "x86",
- "windows",
- });
-
- REQUIRE(v.has_value());
-
- REQUIRE(v.get()->is_supported(System::CPUArchitecture::X64,
- Supports::Platform::WINDOWS,
- Supports::Linkage::DYNAMIC,
- Supports::ToolsetVersion::V140));
- REQUIRE_FALSE(v.get()->is_supported(System::CPUArchitecture::ARM,
- Supports::Platform::WINDOWS,
- Supports::Linkage::DYNAMIC,
- Supports::ToolsetVersion::V140));
- REQUIRE_FALSE(v.get()->is_supported(System::CPUArchitecture::X64,
- Supports::Platform::UWP,
- Supports::Linkage::DYNAMIC,
- Supports::ToolsetVersion::V140));
- REQUIRE(v.get()->is_supported(System::CPUArchitecture::X64,
- Supports::Platform::WINDOWS,
- Supports::Linkage::STATIC,
- Supports::ToolsetVersion::V141));
-}
diff --git a/toolsrc/src/vcpkg-test/update.cpp b/toolsrc/src/vcpkg-test/update.cpp
index 6f1a87d23..a362df720 100644
--- a/toolsrc/src/vcpkg-test/update.cpp
+++ b/toolsrc/src/vcpkg-test/update.cpp
@@ -20,9 +20,9 @@ TEST_CASE ("find outdated packages basic", "[update]")
StatusParagraphs status_db(std::move(status_paragraphs));
std::unordered_map<std::string, SourceControlFileLocation> map;
- auto scf = unwrap(SourceControlFile::parse_control_file(Pgh{{{"Source", "a"}, {"Version", "0"}}}));
+ auto scf = unwrap(SourceControlFile::parse_control_file("", Pgh{{{"Source", "a"}, {"Version", "0"}}}));
map.emplace("a", SourceControlFileLocation{std::move(scf), ""});
- Dependencies::MapPortFileProvider provider(map);
+ PortFileProvider::MapPortFileProvider provider(map);
auto pkgs = SortedVector<OutdatedPackage>(Update::find_outdated_packages(provider, status_db),
&OutdatedPackage::compare_by_name);
@@ -44,9 +44,9 @@ TEST_CASE ("find outdated packages features", "[update]")
StatusParagraphs status_db(std::move(status_paragraphs));
std::unordered_map<std::string, SourceControlFileLocation> map;
- auto scf = unwrap(SourceControlFile::parse_control_file(Pgh{{{"Source", "a"}, {"Version", "0"}}}));
+ auto scf = unwrap(SourceControlFile::parse_control_file("", Pgh{{{"Source", "a"}, {"Version", "0"}}}));
map.emplace("a", SourceControlFileLocation{std::move(scf), ""});
- Dependencies::MapPortFileProvider provider(map);
+ PortFileProvider::MapPortFileProvider provider(map);
auto pkgs = SortedVector<OutdatedPackage>(Update::find_outdated_packages(provider, status_db),
&OutdatedPackage::compare_by_name);
@@ -70,9 +70,9 @@ TEST_CASE ("find outdated packages features 2", "[update]")
StatusParagraphs status_db(std::move(status_paragraphs));
std::unordered_map<std::string, SourceControlFileLocation> map;
- auto scf = unwrap(SourceControlFile::parse_control_file(Pgh{{{"Source", "a"}, {"Version", "0"}}}));
+ auto scf = unwrap(SourceControlFile::parse_control_file("", Pgh{{{"Source", "a"}, {"Version", "0"}}}));
map.emplace("a", SourceControlFileLocation{std::move(scf), ""});
- Dependencies::MapPortFileProvider provider(map);
+ PortFileProvider::MapPortFileProvider provider(map);
auto pkgs = SortedVector<OutdatedPackage>(Update::find_outdated_packages(provider, status_db),
&OutdatedPackage::compare_by_name);
@@ -91,9 +91,9 @@ TEST_CASE ("find outdated packages none", "[update]")
StatusParagraphs status_db(std::move(status_paragraphs));
std::unordered_map<std::string, SourceControlFileLocation> map;
- auto scf = unwrap(SourceControlFile::parse_control_file(Pgh{{{"Source", "a"}, {"Version", "2"}}}));
+ auto scf = unwrap(SourceControlFile::parse_control_file("", Pgh{{{"Source", "a"}, {"Version", "2"}}}));
map.emplace("a", SourceControlFileLocation{std::move(scf), ""});
- Dependencies::MapPortFileProvider provider(map);
+ PortFileProvider::MapPortFileProvider provider(map);
auto pkgs = SortedVector<OutdatedPackage>(Update::find_outdated_packages(provider, status_db),
&OutdatedPackage::compare_by_name);
diff --git a/toolsrc/src/vcpkg-test/util.cpp b/toolsrc/src/vcpkg-test/util.cpp
index daa21567d..db310f7a0 100644
--- a/toolsrc/src/vcpkg-test/util.cpp
+++ b/toolsrc/src/vcpkg-test/util.cpp
@@ -38,6 +38,31 @@
namespace vcpkg::Test
{
+ std::unique_ptr<SourceControlFile> make_control_file(
+ const char* name,
+ const char* depends,
+ const std::vector<std::pair<const char*, const char*>>& features,
+ const std::vector<const char*>& default_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},
+ {"Default-Features", Strings::join(", ", default_features)}});
+ for (auto&& feature : features)
+ {
+ 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));
+ REQUIRE(m_pgh.has_value());
+ return std::move(*m_pgh.get());
+ }
+
std::unique_ptr<vcpkg::StatusParagraph> make_status_pgh(const char* name,
const char* depends,
const char* default_features,
@@ -68,6 +93,23 @@ namespace vcpkg::Test
{"Status", "install ok installed"}});
}
+ PackageSpec PackageSpecMap::emplace(const char* name,
+ const char* depends,
+ const std::vector<std::pair<const char*, const char*>>& features,
+ const std::vector<const char*>& default_features)
+ {
+ auto scfl = SourceControlFileLocation{make_control_file(name, depends, features, default_features), ""};
+ return emplace(std::move(scfl));
+ }
+
+ PackageSpec PackageSpecMap::emplace(vcpkg::SourceControlFileLocation&& scfl)
+ {
+ auto spec = PackageSpec::from_name_and_triplet(scfl.source_control_file->core_paragraph->name, triplet);
+ REQUIRE(spec.has_value());
+ map.emplace(scfl.source_control_file->core_paragraph->name, std::move(scfl));
+ return PackageSpec{*spec.get()};
+ }
+
PackageSpec unsafe_pspec(std::string name, Triplet t)
{
auto m_ret = PackageSpec::from_name_and_triplet(name, t);
diff --git a/toolsrc/src/vcpkg.cpp b/toolsrc/src/vcpkg.cpp
index c336d2f63..1f212dd94 100644
--- a/toolsrc/src/vcpkg.cpp
+++ b/toolsrc/src/vcpkg.cpp
@@ -262,31 +262,19 @@ static void load_config()
}
#if defined(_WIN32)
-static std::string trim_path_from_command_line(const std::string& full_command_line)
-{
- Checks::check_exit(
- VCPKG_LINE_INFO, !full_command_line.empty(), "Internal failure - cannot have empty command line");
-
- if (full_command_line[0] == '"')
- {
- auto it = std::find(full_command_line.cbegin() + 1, full_command_line.cend(), '"');
- if (it != full_command_line.cend()) // Skip over the quote
- ++it;
- while (it != full_command_line.cend() && *it == ' ') // Skip over a space
- ++it;
- return std::string(it, full_command_line.cend());
- }
+// note: this prevents a false positive for -Wmissing-prototypes on clang-cl
+int wmain(int, const wchar_t* const* const);
- auto it = std::find(full_command_line.cbegin(), full_command_line.cend(), ' ');
- while (it != full_command_line.cend() && *it == ' ')
- ++it;
- return std::string(it, full_command_line.cend());
+#if !defined(_MSC_VER)
+#include <shellapi.h>
+int main(int argc, const char* const* const /*argv*/)
+{
+ wchar_t **wargv;
+ wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
+ return wmain(argc, wargv);
}
#endif
-#if defined(_WIN32)
-// note: this prevents a false positive for -Wmissing-prototypes on clang-cl
-int wmain(int, const wchar_t* const*);
int wmain(const int argc, const wchar_t* const* const argv)
#else
int main(const int argc, const char* const* const argv)
diff --git a/toolsrc/src/vcpkg/base/downloads.cpp b/toolsrc/src/vcpkg/base/downloads.cpp
index df6b1be09..995e2d4f3 100644
--- a/toolsrc/src/vcpkg/base/downloads.cpp
+++ b/toolsrc/src/vcpkg/base/downloads.cpp
@@ -2,13 +2,12 @@
#include <vcpkg/base/downloads.h>
#include <vcpkg/base/hash.h>
+#include <vcpkg/base/system.h>
#include <vcpkg/base/system.process.h>
#include <vcpkg/base/util.h>
#if defined(_WIN32)
#include <VersionHelpers.h>
-#else
-#include <vcpkg/base/system.h>
#endif
namespace vcpkg::Downloads
@@ -43,8 +42,22 @@ namespace vcpkg::Downloads
0);
Checks::check_exit(VCPKG_LINE_INFO, hSession, "WinHttpOpen() failed: %d", GetLastError());
+ // If the environment variable HTTPS_PROXY is set
+ // use that variable as proxy. This situation might exist when user is in a company network
+ // with restricted network/proxy settings
+ auto maybe_https_proxy_env = System::get_environment_variable("HTTPS_PROXY");
+ if (auto p_https_proxy = maybe_https_proxy_env.get())
+ {
+ std::wstring env_proxy_settings = Strings::to_utf16(*p_https_proxy);
+ WINHTTP_PROXY_INFO proxy;
+ proxy.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
+ proxy.lpszProxy = env_proxy_settings.data();
+ proxy.lpszProxyBypass = nullptr;
+
+ WinHttpSetOption(hSession, WINHTTP_OPTION_PROXY, &proxy, sizeof(proxy));
+ }
// Win7 IE Proxy fallback
- if (IsWindows7OrGreater() && !IsWindows8Point1OrGreater())
+ else if (IsWindows7OrGreater() && !IsWindows8Point1OrGreater())
{
// First check if any proxy has been found automatically
WINHTTP_PROXY_INFO proxyInfo;
@@ -63,6 +76,9 @@ namespace vcpkg::Downloads
proxy.lpszProxy = ieProxy.lpszProxy;
proxy.lpszProxyBypass = ieProxy.lpszProxyBypass;
WinHttpSetOption(hSession, WINHTTP_OPTION_PROXY, &proxy, sizeof(proxy));
+ GlobalFree(ieProxy.lpszProxy);
+ GlobalFree(ieProxy.lpszProxyBypass);
+ GlobalFree(ieProxy.lpszAutoConfigUrl);
}
}
}
@@ -89,6 +105,7 @@ namespace vcpkg::Downloads
// Send a request.
auto bResults =
WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
+
Checks::check_exit(VCPKG_LINE_INFO, bResults, "WinHttpSendRequest() failed: %d", GetLastError());
// End the request.
diff --git a/toolsrc/src/vcpkg/base/hash.cpp b/toolsrc/src/vcpkg/base/hash.cpp
index 11df3e329..81635b875 100644
--- a/toolsrc/src/vcpkg/base/hash.cpp
+++ b/toolsrc/src/vcpkg/base/hash.cpp
@@ -123,9 +123,9 @@ namespace vcpkg::Hash
void top_bits(T) = delete;
static constexpr uchar top_bits(uchar x) noexcept { return x; }
- static constexpr uchar top_bits(std::uint32_t x) noexcept { return (x >> 24) & 0xFF; }
- static constexpr uchar top_bits(std::uint64_t x) noexcept { return (x >> 56) & 0xFF; }
- static constexpr uchar top_bits(UInt128 x) noexcept { return top_bits(x.top); }
+ [[maybe_unused]] static constexpr uchar top_bits(std::uint32_t x) noexcept { return (x >> 24) & 0xFF; }
+ [[maybe_unused]] static constexpr uchar top_bits(std::uint64_t x) noexcept { return (x >> 56) & 0xFF; }
+ [[maybe_unused]] static constexpr uchar top_bits(UInt128 x) noexcept { return top_bits(x.top); }
// treats UIntTy as big endian for the purpose of this mapping
template<class UIntTy>
@@ -145,7 +145,7 @@ namespace vcpkg::Hash
for (uchar& ch : buff)
{
ch = top_bits(tmp);
- tmp <<= 8;
+ tmp = UIntTy(tmp << 8);
}
for (const auto byte : buff)
diff --git a/toolsrc/src/vcpkg/base/machinetype.cpp b/toolsrc/src/vcpkg/base/machinetype.cpp
index 2b7bd5e3a..237e1eb8b 100644
--- a/toolsrc/src/vcpkg/base/machinetype.cpp
+++ b/toolsrc/src/vcpkg/base/machinetype.cpp
@@ -35,7 +35,7 @@ namespace vcpkg
case MachineType::SH5:
case MachineType::THUMB:
case MachineType::WCEMIPSV2: return t;
- default: Checks::exit_with_message(VCPKG_LINE_INFO, "Unknown machine type code 0x%x", value);
+ default: Checks::exit_with_message(VCPKG_LINE_INFO, "Unknown machine type code 0x%hx", value);
}
}
}
diff --git a/toolsrc/src/vcpkg/base/system.cpp b/toolsrc/src/vcpkg/base/system.cpp
index c84b7f8be..b71ab7c44 100644
--- a/toolsrc/src/vcpkg/base/system.cpp
+++ b/toolsrc/src/vcpkg/base/system.cpp
@@ -378,7 +378,7 @@ namespace vcpkg
g_ctrl_c_state.transition_to_spawn_process();
auto clean_env = compute_clean_environment(extra_env, prepend_to_path);
- windows_create_process(cmd_line, clean_env.data(), process_info, NULL);
+ windows_create_process(cmd_line, clean_env.data(), process_info, 0);
CloseHandle(process_info.hThread);
@@ -444,7 +444,7 @@ namespace vcpkg
Debug::print("_wpopen(", actual_cmd_line, ")\n");
std::wstring output;
- wchar_t buf[1024];
+ auto buf = std::make_unique<wchar_t[]>(1024 * 32);
g_ctrl_c_state.transition_to_spawn_process();
// Flush stdout before launching external process
fflush(stdout);
@@ -454,9 +454,9 @@ namespace vcpkg
g_ctrl_c_state.transition_from_spawn_process();
return {1, Strings::to_utf8(output.c_str())};
}
- while (fgetws(buf, 1024, pipe))
+ while (fgetws(buf.get(), 1024 * 32, pipe))
{
- output.append(buf);
+ output.append(buf.get());
}
if (!feof(pipe))
{
@@ -542,7 +542,7 @@ namespace vcpkg
{
HKEY k = nullptr;
const LSTATUS ec =
- RegOpenKeyExW(reinterpret_cast<HKEY>(base_hkey), Strings::to_utf16(sub_key).c_str(), NULL, KEY_READ, &k);
+ RegOpenKeyExW(reinterpret_cast<HKEY>(base_hkey), Strings::to_utf16(sub_key).c_str(), 0, KEY_READ, &k);
if (ec != ERROR_SUCCESS) return nullopt;
auto w_valuename = Strings::to_utf16(valuename);
diff --git a/toolsrc/src/vcpkg/binaryparagraph.cpp b/toolsrc/src/vcpkg/binaryparagraph.cpp
index 8b1886098..3404a6a48 100644
--- a/toolsrc/src/vcpkg/binaryparagraph.cpp
+++ b/toolsrc/src/vcpkg/binaryparagraph.cpp
@@ -2,6 +2,8 @@
#include <vcpkg/base/checks.h>
#include <vcpkg/base/system.print.h>
+#include <vcpkg/base/util.h>
+
#include <vcpkg/binaryparagraph.h>
#include <vcpkg/parse.h>
@@ -23,6 +25,7 @@ namespace vcpkg
static const std::string MAINTAINER = "Maintainer";
static const std::string DEPENDS = "Depends";
static const std::string DEFAULTFEATURES = "Default-Features";
+ static const std::string TYPE = "Type";
}
BinaryParagraph::BinaryParagraph() = default;
@@ -60,6 +63,8 @@ namespace vcpkg
this->default_features = parse_comma_list(parser.optional_field(Fields::DEFAULTFEATURES));
}
+ this->type = Type::from_string(parser.optional_field(Fields::TYPE));
+
if (const auto err = parser.error_info(this->spec.to_string()))
{
System::print2(System::Color::error, "Error: while parsing the Binary Paragraph for ", this->spec, '\n');
@@ -71,18 +76,30 @@ namespace vcpkg
Checks::check_exit(VCPKG_LINE_INFO, multi_arch == "same", "Multi-Arch must be 'same' but was %s", multi_arch);
}
- BinaryParagraph::BinaryParagraph(const SourceParagraph& spgh, const Triplet& triplet, const std::string& abi_tag)
- : version(spgh.version), description(spgh.description), maintainer(spgh.maintainer), abi(abi_tag)
+ BinaryParagraph::BinaryParagraph(const SourceParagraph& spgh,
+ const Triplet& triplet,
+ const std::string& abi_tag,
+ const std::vector<FeatureSpec>& deps)
+ : version(spgh.version)
+ , description(spgh.description)
+ , maintainer(spgh.maintainer)
+ , abi(abi_tag)
+ , type(spgh.type)
{
this->spec = PackageSpec::from_name_and_triplet(spgh.name, triplet).value_or_exit(VCPKG_LINE_INFO);
- this->depends = filter_dependencies(spgh.depends, triplet);
+ this->depends = Util::fmap(deps, [](const FeatureSpec& spec) { return spec.to_string(); });
+ Util::sort_unique_erase(this->depends);
}
- BinaryParagraph::BinaryParagraph(const SourceParagraph& spgh, const FeatureParagraph& fpgh, const Triplet& triplet)
- : version(), description(fpgh.description), maintainer(), feature(fpgh.name)
+ BinaryParagraph::BinaryParagraph(const SourceParagraph& spgh,
+ const FeatureParagraph& fpgh,
+ const Triplet& triplet,
+ const std ::vector<FeatureSpec>& deps)
+ : version(), description(fpgh.description), maintainer(), feature(fpgh.name), type(spgh.type)
{
this->spec = PackageSpec::from_name_and_triplet(spgh.name, triplet).value_or_exit(VCPKG_LINE_INFO);
- this->depends = filter_dependencies(fpgh.depends, triplet);
+ this->depends = Util::fmap(deps, [](const FeatureSpec& spec) { return spec.to_string(); });
+ Util::sort_unique_erase(this->depends);
}
std::string BinaryParagraph::displayname() const
@@ -119,5 +136,7 @@ namespace vcpkg
if (!pgh.maintainer.empty()) out_str.append("Maintainer: ").append(pgh.maintainer).push_back('\n');
if (!pgh.abi.empty()) out_str.append("Abi: ").append(pgh.abi).push_back('\n');
if (!pgh.description.empty()) out_str.append("Description: ").append(pgh.description).push_back('\n');
+
+ out_str.append("Type: ").append(Type::to_string(pgh.type)).push_back('\n');
}
}
diff --git a/toolsrc/src/vcpkg/build.cpp b/toolsrc/src/vcpkg/build.cpp
index f13dd2a25..bcacb331a 100644
--- a/toolsrc/src/vcpkg/build.cpp
+++ b/toolsrc/src/vcpkg/build.cpp
@@ -23,10 +23,11 @@
#include <vcpkg/statusparagraphs.h>
#include <vcpkg/vcpkglib.h>
+using namespace vcpkg;
using vcpkg::Build::BuildResult;
-using vcpkg::Dependencies::PathsPortFileProvider;
using vcpkg::Parse::ParseControlErrorInfo;
using vcpkg::Parse::ParseExpected;
+using vcpkg::PortFileProvider::PathsPortFileProvider;
namespace vcpkg::Build::Command
{
@@ -35,12 +36,21 @@ namespace vcpkg::Build::Command
void perform_and_exit_ex(const FullPackageSpec& full_spec,
const SourceControlFileLocation& scfl,
+ const PathsPortFileProvider& provider,
const ParsedArguments& options,
const VcpkgPaths& paths)
{
vcpkg::Util::unused(options);
- const StatusParagraphs status_db = database_load_check(paths);
+ CMakeVars::TripletCMakeVarProvider var_provider(paths);
+ var_provider.load_dep_info_vars(std::array<PackageSpec, 1>{full_spec.package_spec});
+ var_provider.load_tag_vars(std::array<FullPackageSpec, 1>{full_spec}, provider);
+
+ StatusParagraphs status_db = database_load_check(paths);
+
+ auto action_plan = Dependencies::create_feature_install_plan(
+ provider, var_provider, std::vector<FullPackageSpec>{full_spec}, status_db);
+
const PackageSpec& spec = full_spec.package_spec;
const SourceControlFile& scf = *scfl.source_control_file;
@@ -62,10 +72,39 @@ namespace vcpkg::Build::Command
Build::FailOnTombstone::NO,
};
- std::set<std::string> features_as_set(full_spec.features.begin(), full_spec.features.end());
- features_as_set.emplace("core");
+ std::unordered_map<std::string, std::vector<FeatureSpec>>* feature_dependencies = nullptr;
+ std::vector<PackageSpec>* package_dependencies = nullptr;
+ std::vector<std::string>* feature_list = nullptr;
+ for (auto& install_action : action_plan.already_installed)
+ {
+ if (install_action.spec == full_spec.package_spec)
+ {
+ feature_dependencies = &install_action.feature_dependencies;
+ package_dependencies = &install_action.package_dependencies;
+ feature_list = &install_action.feature_list;
+ }
+ }
+ for (auto& install_action : action_plan.install_actions)
+ {
+ if (install_action.spec == full_spec.package_spec)
+ {
+ feature_dependencies = &install_action.feature_dependencies;
+ package_dependencies = &install_action.package_dependencies;
+ feature_list = &install_action.feature_list;
+ }
+ }
+
+ Checks::check_exit(VCPKG_LINE_INFO, feature_dependencies != nullptr);
+ Checks::check_exit(VCPKG_LINE_INFO, package_dependencies != nullptr);
+ Checks::check_exit(VCPKG_LINE_INFO, feature_list != nullptr);
- const Build::BuildPackageConfig build_config{scfl, spec.triplet(), build_package_options, features_as_set};
+ const Build::BuildPackageConfig build_config{scfl,
+ spec.triplet(),
+ build_package_options,
+ var_provider,
+ *feature_dependencies,
+ *package_dependencies,
+ *feature_list};
const auto build_timer = Chrono::ElapsedTimer::create_started();
const auto result = Build::build_package(paths, build_config, status_db);
@@ -121,7 +160,7 @@ namespace vcpkg::Build::Command
Checks::check_exit(VCPKG_LINE_INFO, scfl != nullptr, "Error: Couldn't find port '%s'", port_name);
- perform_and_exit_ex(spec, *scfl, options, paths);
+ perform_and_exit_ex(spec, *scfl, provider, options, paths);
}
}
@@ -133,6 +172,7 @@ namespace vcpkg::Build
static const std::string NAME_ONLY_RELEASE_CRT = "PolicyOnlyReleaseCRT";
static const std::string NAME_EMPTY_INCLUDE_FOLDER = "PolicyEmptyIncludeFolder";
static const std::string NAME_ALLOW_OBSOLETE_MSVCRT = "PolicyAllowObsoleteMsvcrt";
+ static const std::string NAME_ALLOW_RESTRICTED_HEADERS = "PolicyAllowRestrictedHeaders";
const std::string& to_string(BuildPolicy policy)
{
@@ -144,6 +184,7 @@ namespace vcpkg::Build
case BuildPolicy::ONLY_RELEASE_CRT: return NAME_ONLY_RELEASE_CRT;
case BuildPolicy::EMPTY_INCLUDE_FOLDER: return NAME_EMPTY_INCLUDE_FOLDER;
case BuildPolicy::ALLOW_OBSOLETE_MSVCRT: return NAME_ALLOW_OBSOLETE_MSVCRT;
+ case BuildPolicy::ALLOW_RESTRICTED_HEADERS: return NAME_ALLOW_RESTRICTED_HEADERS;
default: Checks::unreachable(VCPKG_LINE_INFO);
}
}
@@ -158,6 +199,7 @@ namespace vcpkg::Build
case BuildPolicy::ONLY_RELEASE_CRT: return "VCPKG_POLICY_ONLY_RELEASE_CRT";
case BuildPolicy::EMPTY_INCLUDE_FOLDER: return "VCPKG_POLICY_EMPTY_INCLUDE_FOLDER";
case BuildPolicy::ALLOW_OBSOLETE_MSVCRT: return "VCPKG_POLICY_ALLOW_OBSOLETE_MSVCRT";
+ case BuildPolicy::ALLOW_RESTRICTED_HEADERS: return "VCPKG_POLICY_ALLOW_RESTRICTED_HEADERS";
default: Checks::unreachable(VCPKG_LINE_INFO);
}
}
@@ -260,20 +302,15 @@ namespace vcpkg::Build
tonull);
}
- static BinaryParagraph create_binary_feature_control_file(const SourceParagraph& source_paragraph,
- const FeatureParagraph& feature_paragraph,
- const Triplet& triplet)
- {
- return BinaryParagraph(source_paragraph, feature_paragraph, triplet);
- }
-
- static std::unique_ptr<BinaryControlFile> create_binary_control_file(const SourceParagraph& source_paragraph,
- const Triplet& triplet,
- const BuildInfo& build_info,
- const std::string& abi_tag)
+ static std::unique_ptr<BinaryControlFile> create_binary_control_file(
+ const SourceParagraph& source_paragraph,
+ const Triplet& triplet,
+ const BuildInfo& build_info,
+ const std::string& abi_tag,
+ const std::vector<FeatureSpec>& core_dependencies)
{
auto bcf = std::make_unique<BinaryControlFile>();
- BinaryParagraph bpgh(source_paragraph, triplet, abi_tag);
+ BinaryParagraph bpgh(source_paragraph, triplet, abi_tag, core_dependencies);
if (const auto p_ver = build_info.version.get())
{
bpgh.version = *p_ver;
@@ -294,71 +331,6 @@ namespace vcpkg::Build
paths.get_filesystem().write_contents(binary_control_file, start, VCPKG_LINE_INFO);
}
- static std::vector<Features> get_dependencies(const SourceControlFile& scf,
- const std::set<std::string>& feature_list,
- const Triplet& triplet)
- {
- return Util::fmap_flatten(feature_list, [&](std::string const& feature) -> std::vector<Features> {
- if (feature == "core")
- {
- return filter_dependencies_to_features(scf.core_paragraph->depends, triplet);
- }
-
- auto maybe_feature = scf.find_feature(feature);
- Checks::check_exit(VCPKG_LINE_INFO, maybe_feature.has_value());
-
- return filter_dependencies_to_features(maybe_feature.get()->depends, triplet);
- });
- }
-
- static std::vector<std::string> get_dependency_names(const SourceControlFile& scf,
- const std::set<std::string>& feature_list,
- const Triplet& triplet)
- {
- return Util::sort_unique_erase(
- Util::fmap(get_dependencies(scf, feature_list, triplet), [&](const Features& feat) { return feat.name; }));
- }
-
- static std::vector<FeatureSpec> compute_required_feature_specs(const BuildPackageConfig& config,
- const StatusParagraphs& status_db)
- {
- const Triplet& triplet = config.triplet;
-
- const std::vector<std::string> dep_strings = get_dependency_names(config.scf, config.feature_list, triplet);
-
- auto dep_fspecs = FeatureSpec::from_strings_and_triplet(dep_strings, triplet);
- Util::sort_unique_erase(dep_fspecs);
-
- // expand defaults
- std::vector<FeatureSpec> ret;
- for (auto&& fspec : dep_fspecs)
- {
- if (fspec.feature().empty())
- {
- // reference to default features
- const auto it = status_db.find_installed(fspec.spec());
- if (it == status_db.end())
- {
- // not currently installed, so just leave the default reference so it will fail later
- ret.push_back(fspec);
- }
- else
- {
- ret.emplace_back(fspec.spec(), "core");
- for (auto&& default_feature : it->get()->package.default_features)
- ret.emplace_back(fspec.spec(), default_feature);
- }
- }
- else
- {
- ret.push_back(fspec);
- }
- }
- Util::sort_unique_erase(ret);
-
- return ret;
- }
-
static int get_concurrency()
{
static int concurrency = [] {
@@ -423,30 +395,22 @@ namespace vcpkg::Build
}
const Files::Filesystem& fs = paths.get_filesystem();
- if (fs.is_regular_file(config.port_dir / "environment-overrides.cmake"))
- {
- variables.emplace_back("VCPKG_ENV_OVERRIDES_FILE", config.port_dir / "environment-overrides.cmake");
- }
-
- std::vector<FeatureSpec> dependencies =
- filter_dependencies_to_specs(config.scfl.source_control_file->core_paragraph->depends, triplet);
- std::vector<std::string> port_toolchains;
- for (const FeatureSpec& dependency : dependencies)
+ std::vector<std::string> port_configs;
+ for (const PackageSpec& dependency : config.package_dependencies)
{
- const fs::path port_toolchain_path = paths.installed / dependency.triplet().canonical_name() / "share" /
- dependency.spec().name() / "port-toolchain.cmake";
+ const fs::path port_config_path = paths.installed / dependency.triplet().canonical_name() / "share" /
+ dependency.name() / "vcpkg-port-config.cmake";
- if (fs.is_regular_file(port_toolchain_path))
+ if (fs.is_regular_file(port_config_path))
{
- System::print2(port_toolchain_path.u8string());
- port_toolchains.emplace_back(port_toolchain_path.u8string());
+ port_configs.emplace_back(port_config_path.u8string());
}
}
- if (!port_toolchains.empty())
+ if (!port_configs.empty())
{
- variables.emplace_back("VCPKG_PORT_TOOLCHAINS", Strings::join(";", port_toolchains));
+ variables.emplace_back("VCPKG_PORT_CONFIGS", Strings::join(";", port_configs));
}
return variables;
@@ -497,6 +461,8 @@ namespace vcpkg::Build
else
{
const auto algo = Hash::Algorithm::Sha1;
+ // TODO: Use file path as part of hash.
+ // REASON: Copying a triplet file without modifying it produces the same hash as the original.
hash = Hash::get_file_hash(VCPKG_LINE_INFO, fs, triplet_file_path, algo);
if (auto p = pre_build_info.external_toolchain_file.get())
@@ -550,10 +516,18 @@ namespace vcpkg::Build
const Triplet& triplet = spec.triplet();
const auto& triplet_file_path = paths.get_triplet_file_path(spec.triplet()).u8string();
- if (!Strings::case_insensitive_ascii_starts_with(triplet_file_path, paths.triplets.u8string()))
+ if (Strings::case_insensitive_ascii_starts_with(triplet_file_path, paths.community_triplets.u8string()))
{
- System::printf("-- Loading triplet configuration from: %s\n", triplet_file_path);
+ System::printf(vcpkg::System::Color::warning,
+ "-- Using community triplet %s. This triplet configuration is not guaranteed to succeed.\n",
+ triplet.canonical_name());
+ System::printf("-- [COMMUNITY] Loading triplet configuration from: %s\n", triplet_file_path);
}
+ else if (!Strings::case_insensitive_ascii_starts_with(triplet_file_path, paths.triplets.u8string()))
+ {
+ System::printf("-- [OVERLAY] Loading triplet configuration from: %s\n", triplet_file_path);
+ }
+
if (!Strings::case_insensitive_ascii_starts_with(config.port_dir.u8string(), paths.ports.u8string()))
{
System::printf("-- Installing port from location: %s\n", config.port_dir.u8string());
@@ -605,8 +579,11 @@ namespace vcpkg::Build
const size_t error_count =
PostBuildLint::perform_all_checks(spec, paths, pre_build_info, build_info, config.port_dir);
- std::unique_ptr<BinaryControlFile> bcf =
- create_binary_control_file(*config.scf.core_paragraph, triplet, build_info, abi_tag);
+ auto find_itr = config.feature_dependencies.find("core");
+ Checks::check_exit(VCPKG_LINE_INFO, find_itr != config.feature_dependencies.end());
+
+ std::unique_ptr<BinaryControlFile> bcf = create_binary_control_file(
+ *config.scf.core_paragraph, triplet, build_info, abi_tag, std::move(find_itr->second));
if (error_count != 0)
{
@@ -617,8 +594,13 @@ namespace vcpkg::Build
for (auto&& f_pgh : config.scf.feature_paragraphs)
{
if (f_pgh->name == feature)
- bcf->features.push_back(
- create_binary_feature_control_file(*config.scf.core_paragraph, *f_pgh, triplet));
+ {
+ find_itr = config.feature_dependencies.find(feature);
+ Checks::check_exit(VCPKG_LINE_INFO, find_itr != config.feature_dependencies.end());
+
+ bcf->features.emplace_back(
+ *config.scf.core_paragraph, *f_pgh, triplet, std::move(find_itr->second));
+ }
}
}
@@ -713,8 +695,11 @@ namespace vcpkg::Build
paths.scripts / "cmake" / "vcpkg_fixup_cmake_targets.cmake",
Hash::Algorithm::Sha1));
+ abi_tag_entries.emplace_back("post_build_checks", "2");
abi_tag_entries.emplace_back("triplet", pre_build_info.triplet_abi_tag);
- abi_tag_entries.emplace_back("features", Strings::join(";", config.feature_list));
+ std::vector<std::string> sorted_feature_list(std::begin(config.feature_list), std::end(config.feature_list));
+ Util::sort(sorted_feature_list);
+ abi_tag_entries.emplace_back("features", Strings::join(";", sorted_feature_list));
if (pre_build_info.public_abi_override)
{
@@ -724,6 +709,14 @@ namespace vcpkg::Build
Hash::Algorithm::Sha1));
}
+ // No need to sort, the variables are stored in the same order they are written down in the abi-settings file
+ for (const auto& env_var : pre_build_info.passthrough_env_vars)
+ {
+ abi_tag_entries.emplace_back(
+ "ENV:" + env_var,
+ Hash::get_string_hash(System::get_environment_variable(env_var).value_or(""), Hash::Algorithm::Sha1));
+ }
+
if (config.build_package_options.use_head_version == UseHeadVersion::YES)
abi_tag_entries.emplace_back("head", "");
@@ -732,12 +725,13 @@ namespace vcpkg::Build
if (Debug::g_debugging)
{
- System::print2("[DEBUG] <abientries>\n");
+ std::string message = "[DEBUG] <abientries>\n";
for (auto&& entry : abi_tag_entries)
{
- System::print2("[DEBUG] ", entry.key, "|", entry.value, "\n");
+ Strings::append(message, "[DEBUG] ", entry.key, "|", entry.value, "\n");
}
- System::print2("[DEBUG] </abientries>\n");
+ Strings::append(message, "[DEBUG] </abientries>\n");
+ System::print2(message);
}
auto abi_tag_entries_missing = abi_tag_entries;
@@ -819,33 +813,27 @@ namespace vcpkg::Build
const Triplet& triplet = config.triplet;
const std::string& name = config.scf.core_paragraph->name;
- std::vector<FeatureSpec> required_fspecs = compute_required_feature_specs(config, status_db);
-
- // extract out the actual package ids
- auto dep_pspecs = Util::fmap(required_fspecs, [](FeatureSpec const& fspec) { return fspec.spec(); });
- Util::sort_unique_erase(dep_pspecs);
-
- // Find all features that aren't installed. This mutates required_fspecs.
- // Skip this validation when running in Download Mode.
- if (config.build_package_options.only_downloads != Build::OnlyDownloads::YES)
+ std::vector<FeatureSpec> missing_fspecs;
+ for (const auto& kv : config.feature_dependencies)
{
- Util::erase_remove_if(required_fspecs, [&](FeatureSpec const& fspec) {
- return status_db.is_installed(fspec) || fspec.name() == name;
- });
-
- if (!required_fspecs.empty())
+ for (const FeatureSpec& spec : kv.second)
{
- return {BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES, std::move(required_fspecs)};
+ if (!(status_db.is_installed(spec) || spec.name() == name))
+ {
+ missing_fspecs.emplace_back(spec);
+ }
}
}
- const PackageSpec spec =
- PackageSpec::from_name_and_triplet(config.scf.core_paragraph->name, triplet).value_or_exit(VCPKG_LINE_INFO);
+ if (!missing_fspecs.empty())
+ {
+ return {BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES, std::move(missing_fspecs)};
+ }
- std::vector<AbiEntry> dependency_abis;
+ const PackageSpec spec = PackageSpec::from_name_and_triplet(name, triplet).value_or_exit(VCPKG_LINE_INFO);
- // dep_pspecs was not destroyed
- for (auto&& pspec : dep_pspecs)
+ std::vector<AbiEntry> dependency_abis;
+ for (auto&& pspec : config.package_dependencies)
{
if (pspec == spec || Util::Enum::to_bool(config.build_package_options.only_downloads))
{
@@ -857,7 +845,9 @@ namespace vcpkg::Build
AbiEntry{status_it->get()->package.spec.name(), status_it->get()->package.abi});
}
- const auto pre_build_info = PreBuildInfo::from_triplet_file(paths, triplet, config.scfl);
+ const std::unordered_map<std::string, std::string>& cmake_vars =
+ config.var_provider.get_tag_vars(spec).value_or_exit(VCPKG_LINE_INFO);
+ const PreBuildInfo pre_build_info(paths, triplet, cmake_vars);
auto maybe_abi_tag_and_file = compute_abi_tag(paths, config, pre_build_info, dependency_abis);
if (!maybe_abi_tag_and_file)
@@ -887,12 +877,22 @@ namespace vcpkg::Build
if (archive_result != 0)
{
System::print2("Failed to decompress archive package\n");
- return BuildResult::BUILD_FAILED;
+ if (config.build_package_options.purge_decompress_failure == PurgeDecompressFailure::NO)
+ {
+ return BuildResult::BUILD_FAILED;
+ }
+ else
+ {
+ System::print2("Purging bad archive\n");
+ fs.remove(archive_path, ec);
+ }
+ }
+ else
+ {
+ auto maybe_bcf = Paragraphs::try_load_cached_package(paths, spec);
+ auto bcf = std::make_unique<BinaryControlFile>(std::move(maybe_bcf).value_or_exit(VCPKG_LINE_INFO));
+ return {BuildResult::SUCCEEDED, std::move(bcf)};
}
-
- auto maybe_bcf = Paragraphs::try_load_cached_package(paths, spec);
- auto bcf = std::make_unique<BinaryControlFile>(std::move(maybe_bcf).value_or_exit(VCPKG_LINE_INFO));
- return {BuildResult::SUCCEEDED, std::move(bcf)};
}
if (fs.exists(archive_tombstone_path))
@@ -1077,110 +1077,55 @@ namespace vcpkg::Build
return inner_create_buildinfo(*pghs.get());
}
- PreBuildInfo PreBuildInfo::from_triplet_file(const VcpkgPaths& paths,
- const Triplet& triplet,
- Optional<const SourceControlFileLocation&> port)
+ PreBuildInfo::PreBuildInfo(const VcpkgPaths& paths,
+ const Triplet& triplet,
+ const std::unordered_map<std::string, std::string>& cmakevars)
{
- static constexpr CStringView FLAG_GUID = "c35112b6-d1ba-415b-aa5d-81de856ef8eb";
-
- const fs::path& cmake_exe_path = paths.get_tool_exe(Tools::CMAKE);
- const fs::path ports_cmake_script_path = paths.scripts / "get_triplet_environment.cmake";
- const fs::path triplet_file_path = paths.get_triplet_file_path(triplet);
-
- std::vector<System::CMakeVariable> args{{"CMAKE_TRIPLET_FILE", triplet_file_path}};
-
- if (port)
+ for (auto&& kv : VCPKG_OPTIONS)
{
- const SourceControlFileLocation& scfl = port.value_or_exit(VCPKG_LINE_INFO);
-
- if (paths.get_filesystem().is_regular_file(scfl.source_location / "environment-overrides.cmake"))
+ auto find_itr = cmakevars.find(kv.first);
+ if (find_itr == cmakevars.end())
{
- args.emplace_back("VCPKG_ENV_OVERRIDES_FILE", scfl.source_location / "environment-overrides.cmake");
+ continue;
}
- }
-
- const auto cmd_launch_cmake = System::make_cmake_cmd(cmake_exe_path, ports_cmake_script_path, args);
-
- const auto ec_data = System::cmd_execute_and_capture_output(cmd_launch_cmake);
- Checks::check_exit(VCPKG_LINE_INFO, ec_data.exit_code == 0, ec_data.output);
-
- const std::vector<std::string> lines = Strings::split(ec_data.output, "\n");
-
- PreBuildInfo pre_build_info;
-
- pre_build_info.port = port;
-
- const auto e = lines.cend();
- auto cur = std::find(lines.cbegin(), e, FLAG_GUID);
- if (cur != e) ++cur;
-
- for (; cur != e; ++cur)
- {
- auto&& line = *cur;
- const std::vector<std::string> s = Strings::split(line, "=");
- Checks::check_exit(VCPKG_LINE_INFO,
- s.size() == 1 || s.size() == 2,
- "Expected format is [VARIABLE_NAME=VARIABLE_VALUE], but was [%s]",
- line);
+ const std::string& variable_value = find_itr->second;
- const bool variable_with_no_value = s.size() == 1;
- const std::string variable_name = s.at(0);
- const std::string variable_value = variable_with_no_value ? "" : s.at(1);
-
- auto maybe_option = VCPKG_OPTIONS.find(variable_name);
- if (maybe_option != VCPKG_OPTIONS.end())
+ switch (kv.second)
{
- switch (maybe_option->second)
- {
- case VcpkgTripletVar::TARGET_ARCHITECTURE:
- pre_build_info.target_architecture = variable_value;
- break;
- case VcpkgTripletVar::CMAKE_SYSTEM_NAME: pre_build_info.cmake_system_name = variable_value; break;
- case VcpkgTripletVar::CMAKE_SYSTEM_VERSION:
- pre_build_info.cmake_system_version = variable_value;
- break;
- case VcpkgTripletVar::PLATFORM_TOOLSET:
- pre_build_info.platform_toolset =
- variable_value.empty() ? nullopt : Optional<std::string>{variable_value};
- break;
- case VcpkgTripletVar::VISUAL_STUDIO_PATH:
- pre_build_info.visual_studio_path =
- variable_value.empty() ? nullopt : Optional<fs::path>{variable_value};
- break;
- case VcpkgTripletVar::CHAINLOAD_TOOLCHAIN_FILE:
- pre_build_info.external_toolchain_file =
- variable_value.empty() ? nullopt : Optional<std::string>{variable_value};
- break;
- case VcpkgTripletVar::BUILD_TYPE:
- if (variable_value.empty())
- pre_build_info.build_type = nullopt;
- else if (Strings::case_insensitive_ascii_equals(variable_value, "debug"))
- pre_build_info.build_type = ConfigurationType::DEBUG;
- else if (Strings::case_insensitive_ascii_equals(variable_value, "release"))
- pre_build_info.build_type = ConfigurationType::RELEASE;
- else
- Checks::exit_with_message(
- VCPKG_LINE_INFO, "Unknown setting for VCPKG_BUILD_TYPE: %s", variable_value);
- break;
- case VcpkgTripletVar::ENV_PASSTHROUGH:
- pre_build_info.passthrough_env_vars = Strings::split(variable_value, ";");
- break;
- case VcpkgTripletVar::PUBLIC_ABI_OVERRIDE:
- pre_build_info.public_abi_override =
- variable_value.empty() ? nullopt : Optional<std::string>{variable_value};
- break;
- }
- }
- else
- {
- Checks::exit_with_message(VCPKG_LINE_INFO, "Unknown variable name %s", line);
+ case VcpkgTripletVar::TARGET_ARCHITECTURE: target_architecture = variable_value; break;
+ case VcpkgTripletVar::CMAKE_SYSTEM_NAME: cmake_system_name = variable_value; break;
+ case VcpkgTripletVar::CMAKE_SYSTEM_VERSION: cmake_system_version = variable_value; break;
+ case VcpkgTripletVar::PLATFORM_TOOLSET:
+ platform_toolset = variable_value.empty() ? nullopt : Optional<std::string>{variable_value};
+ break;
+ case VcpkgTripletVar::VISUAL_STUDIO_PATH:
+ visual_studio_path = variable_value.empty() ? nullopt : Optional<fs::path>{variable_value};
+ break;
+ case VcpkgTripletVar::CHAINLOAD_TOOLCHAIN_FILE:
+ external_toolchain_file = variable_value.empty() ? nullopt : Optional<std::string>{variable_value};
+ break;
+ case VcpkgTripletVar::BUILD_TYPE:
+ if (variable_value.empty())
+ build_type = nullopt;
+ else if (Strings::case_insensitive_ascii_equals(variable_value, "debug"))
+ build_type = ConfigurationType::DEBUG;
+ else if (Strings::case_insensitive_ascii_equals(variable_value, "release"))
+ build_type = ConfigurationType::RELEASE;
+ else
+ Checks::exit_with_message(
+ VCPKG_LINE_INFO, "Unknown setting for VCPKG_BUILD_TYPE: %s", variable_value);
+ break;
+ case VcpkgTripletVar::ENV_PASSTHROUGH:
+ passthrough_env_vars = Strings::split(variable_value, ";");
+ break;
+ case VcpkgTripletVar::PUBLIC_ABI_OVERRIDE:
+ public_abi_override = variable_value.empty() ? nullopt : Optional<std::string>{variable_value};
+ break;
}
}
- pre_build_info.triplet_abi_tag = get_triplet_abi(paths, pre_build_info, triplet);
-
- return pre_build_info;
+ triplet_abi_tag = get_triplet_abi(paths, *this, triplet);
}
ExtendedBuildResult::ExtendedBuildResult(BuildResult code) : code(code) {}
diff --git a/toolsrc/src/vcpkg/cmakevars.cpp b/toolsrc/src/vcpkg/cmakevars.cpp
new file mode 100644
index 000000000..6a9d26ec8
--- /dev/null
+++ b/toolsrc/src/vcpkg/cmakevars.cpp
@@ -0,0 +1,259 @@
+#include "pch.h"
+
+#include <vcpkg/base/optional.h>
+#include <vcpkg/base/span.h>
+#include <vcpkg/base/util.h>
+
+#include <vcpkg/cmakevars.h>
+
+using namespace vcpkg;
+using vcpkg::Optional;
+using vcpkg::CMakeVars::TripletCMakeVarProvider;
+
+namespace vcpkg::CMakeVars
+{
+ fs::path TripletCMakeVarProvider::create_tag_extraction_file(
+ const Span<const std::pair<const FullPackageSpec*, std::string>>& spec_abi_settings) const
+ {
+ Files::Filesystem& fs = paths.get_filesystem();
+ static int tag_extract_id = 0;
+
+ std::string extraction_file("include(" + get_tags_path.generic_u8string() + ")\n\n");
+
+ std::map<Triplet, int> emitted_triplets;
+ int emitted_triplet_id = 0;
+ for (const auto& spec_abi_setting : spec_abi_settings)
+ {
+ emitted_triplets[spec_abi_setting.first->package_spec.triplet()] = emitted_triplet_id++;
+ }
+ Strings::append(extraction_file, "macro(vcpkg_triplet_file VCPKG_TRIPLET_ID)\n");
+ for (auto& p : emitted_triplets)
+ {
+ Strings::append(extraction_file,
+ "if(VCPKG_TRIPLET_ID EQUAL ",
+ p.second,
+ ")\n",
+ fs.read_contents(paths.get_triplet_file_path(p.first), VCPKG_LINE_INFO),
+ "\nendif()\n");
+ }
+ Strings::append(extraction_file, "endmacro()\n");
+ for (const auto& spec_abi_setting : spec_abi_settings)
+ {
+ const FullPackageSpec& spec = *spec_abi_setting.first;
+
+ Strings::append(extraction_file,
+ "vcpkg_get_tags(\"",
+ spec.package_spec.name(),
+ "\" \"",
+ Strings::join(";", spec.features),
+ "\" \"",
+ emitted_triplets[spec.package_spec.triplet()],
+ "\" \"",
+ spec_abi_setting.second,
+ "\")\n");
+ }
+
+ fs::path path = paths.buildtrees / Strings::concat(tag_extract_id++, ".vcpkg_tags.cmake");
+
+ std::error_code ec;
+ fs.create_directories(paths.buildtrees, ec);
+ fs.write_contents(path, extraction_file, VCPKG_LINE_INFO);
+
+ return path;
+ }
+
+ fs::path TripletCMakeVarProvider::create_dep_info_extraction_file(const Span<const PackageSpec> specs) const
+ {
+ static int dep_info_id = 0;
+ Files::Filesystem& fs = paths.get_filesystem();
+
+ std::string extraction_file("include(" + get_dep_info_path.generic_u8string() + ")\n\n");
+
+ std::map<Triplet, int> emitted_triplets;
+ int emitted_triplet_id = 0;
+ for (const auto& spec : specs)
+ {
+ emitted_triplets[spec.triplet()] = emitted_triplet_id++;
+ }
+ Strings::append(extraction_file, "macro(vcpkg_triplet_file VCPKG_TRIPLET_ID)\n");
+ for (auto& p : emitted_triplets)
+ {
+ Strings::append(extraction_file,
+ "if(VCPKG_TRIPLET_ID EQUAL ",
+ p.second,
+ ")\n",
+ fs.read_contents(paths.get_triplet_file_path(p.first), VCPKG_LINE_INFO),
+ "\nendif()\n");
+ }
+ Strings::append(extraction_file, "endmacro()\n");
+
+ for (const PackageSpec& spec : specs)
+ {
+ Strings::append(
+ extraction_file, "vcpkg_get_dep_info(", spec.name(), " ", emitted_triplets[spec.triplet()], ")\n");
+ }
+
+ fs::path path = paths.buildtrees / Strings::concat(dep_info_id++, ".vcpkg_dep_info.cmake");
+
+ std::error_code ec;
+ fs.create_directories(paths.buildtrees, ec);
+ fs.write_contents(path, extraction_file, VCPKG_LINE_INFO);
+
+ return path;
+ }
+
+ void TripletCMakeVarProvider::launch_and_split(
+ const fs::path& script_path, std::vector<std::vector<std::pair<std::string, std::string>>>& vars) const
+ {
+ static constexpr CStringView PORT_START_GUID = "d8187afd-ea4a-4fc3-9aa4-a6782e1ed9af";
+ static constexpr CStringView PORT_END_GUID = "8c504940-be29-4cba-9f8f-6cd83e9d87b7";
+ static constexpr CStringView BLOCK_START_GUID = "c35112b6-d1ba-415b-aa5d-81de856ef8eb";
+ static constexpr CStringView BLOCK_END_GUID = "e1e74b5c-18cb-4474-a6bd-5c1c8bc81f3f";
+
+ const auto cmd_launch_cmake = System::make_cmake_cmd(cmake_exe_path, script_path, {});
+ const auto ec_data = System::cmd_execute_and_capture_output(cmd_launch_cmake);
+ Checks::check_exit(VCPKG_LINE_INFO, ec_data.exit_code == 0, ec_data.output);
+
+ const std::vector<std::string> lines = Strings::split(ec_data.output, "\n");
+
+ const auto end = lines.cend();
+
+ auto port_start = std::find(lines.cbegin(), end, PORT_START_GUID);
+ auto port_end = std::find(port_start, end, PORT_END_GUID);
+
+ for (auto var_itr = vars.begin(); port_start != end && var_itr != vars.end(); ++var_itr)
+ {
+ auto block_start = std::find(port_start, port_end, BLOCK_START_GUID);
+ auto block_end = std::find(++block_start, port_end, BLOCK_END_GUID);
+
+ while (block_start != port_end)
+ {
+ while (block_start != block_end)
+ {
+ const std::string& line = *block_start;
+
+ std::vector<std::string> s = Strings::split(line, "=");
+ Checks::check_exit(VCPKG_LINE_INFO,
+ s.size() == 1 || s.size() == 2,
+ "Expected format is [VARIABLE_NAME=VARIABLE_VALUE], but was [%s]",
+ line);
+
+ var_itr->emplace_back(std::move(s[0]), s.size() == 1 ? "" : std::move(s[1]));
+
+ ++block_start;
+ }
+
+ block_start = std::find(block_end, port_end, BLOCK_START_GUID);
+ block_end = std::find(block_start, port_end, BLOCK_END_GUID);
+ }
+
+ port_start = std::find(port_end, end, PORT_START_GUID);
+ port_end = std::find(port_start, end, PORT_END_GUID);
+ }
+ }
+
+ void TripletCMakeVarProvider::load_generic_triplet_vars(const Triplet& triplet) const
+ {
+ std::vector<std::vector<std::pair<std::string, std::string>>> vars(1);
+ FullPackageSpec full_spec = FullPackageSpec::from_string("", triplet).value_or_exit(VCPKG_LINE_INFO);
+ const fs::path file_path =
+ create_tag_extraction_file(std::array<std::pair<const FullPackageSpec*, std::string>, 1>{
+ std::pair<const FullPackageSpec*, std::string>{&full_spec, ""}});
+ launch_and_split(file_path, vars);
+ paths.get_filesystem().remove(file_path, VCPKG_LINE_INFO);
+
+ generic_triplet_vars[triplet].insert(std::make_move_iterator(vars.front().begin()),
+ std::make_move_iterator(vars.front().end()));
+ }
+
+ void TripletCMakeVarProvider::load_dep_info_vars(Span<const PackageSpec> specs) const
+ {
+ if (specs.size() == 0) return;
+ std::vector<std::vector<std::pair<std::string, std::string>>> vars(specs.size());
+ const fs::path file_path = create_dep_info_extraction_file(specs);
+ if (specs.size() > 100)
+ {
+ System::print2("Loading dependency information for ", specs.size(), " packages...\n");
+ }
+ launch_and_split(file_path, vars);
+ paths.get_filesystem().remove(file_path, VCPKG_LINE_INFO);
+
+ auto var_list_itr = vars.begin();
+ for (const PackageSpec& spec : specs)
+ {
+ dep_resolution_vars.emplace(std::piecewise_construct,
+ std::forward_as_tuple(spec),
+ std::forward_as_tuple(std::make_move_iterator(var_list_itr->begin()),
+ std::make_move_iterator(var_list_itr->end())));
+ ++var_list_itr;
+ }
+ }
+
+ void TripletCMakeVarProvider::load_tag_vars(Span<const FullPackageSpec> specs,
+ const PortFileProvider::PortFileProvider& port_provider) const
+ {
+ if (specs.size() == 0) return;
+ std::vector<std::pair<const FullPackageSpec*, std::string>> spec_abi_settings;
+ spec_abi_settings.reserve(specs.size());
+
+ for (const FullPackageSpec& spec : specs)
+ {
+ auto& scfl = port_provider.get_control_file(spec.package_spec.name()).value_or_exit(VCPKG_LINE_INFO);
+ const fs::path override_path = scfl.source_location / "vcpkg-abi-settings.cmake";
+ spec_abi_settings.emplace_back(&spec, override_path.generic_u8string());
+ }
+
+ std::vector<std::vector<std::pair<std::string, std::string>>> vars(spec_abi_settings.size());
+ const fs::path file_path = create_tag_extraction_file(spec_abi_settings);
+ launch_and_split(file_path, vars);
+ paths.get_filesystem().remove(file_path, VCPKG_LINE_INFO);
+
+ auto var_list_itr = vars.begin();
+ for (const auto& spec_abi_setting : spec_abi_settings)
+ {
+ const FullPackageSpec& spec = *spec_abi_setting.first;
+
+ tag_vars.emplace(std::piecewise_construct,
+ std::forward_as_tuple(spec.package_spec),
+ std::forward_as_tuple(std::make_move_iterator(var_list_itr->begin()),
+ std::make_move_iterator(var_list_itr->end())));
+ ++var_list_itr;
+ }
+ }
+
+ Optional<const std::unordered_map<std::string, std::string>&> TripletCMakeVarProvider::get_generic_triplet_vars(
+ const Triplet& triplet) const
+ {
+ auto find_itr = generic_triplet_vars.find(triplet);
+ if (find_itr != generic_triplet_vars.end())
+ {
+ return find_itr->second;
+ }
+
+ return nullopt;
+ }
+
+ Optional<const std::unordered_map<std::string, std::string>&> TripletCMakeVarProvider::get_dep_info_vars(
+ const PackageSpec& spec) const
+ {
+ auto find_itr = dep_resolution_vars.find(spec);
+ if (find_itr != dep_resolution_vars.end())
+ {
+ return find_itr->second;
+ }
+
+ return nullopt;
+ }
+
+ Optional<const std::unordered_map<std::string, std::string>&> TripletCMakeVarProvider::get_tag_vars(
+ const PackageSpec& spec) const
+ {
+ auto find_itr = tag_vars.find(spec);
+ if (find_itr != tag_vars.end())
+ {
+ return find_itr->second;
+ }
+
+ return nullopt;
+ }
+}
diff --git a/toolsrc/src/vcpkg/commands.autocomplete.cpp b/toolsrc/src/vcpkg/commands.autocomplete.cpp
index 8449b7096..2857988ba 100644
--- a/toolsrc/src/vcpkg/commands.autocomplete.cpp
+++ b/toolsrc/src/vcpkg/commands.autocomplete.cpp
@@ -97,7 +97,7 @@ namespace vcpkg::Commands::Autocomplete
Checks::exit_success(VCPKG_LINE_INFO);
}
- std::vector<std::string> triplets = paths.get_available_triplets();
+ std::vector<std::string> triplets = paths.get_available_triplets_names();
Util::erase_remove_if(triplets, [&](const std::string& s) {
return !Strings::case_insensitive_ascii_starts_with(s, triplet_prefix);
});
@@ -158,8 +158,8 @@ namespace vcpkg::Commands::Autocomplete
if (command.name == "install" && results.size() == 1 && !is_option)
{
const auto port_at_each_triplet =
- combine_port_with_triplets(results[0], paths.get_available_triplets());
- Util::Vectors::concatenate(&results, port_at_each_triplet);
+ combine_port_with_triplets(results[0], paths.get_available_triplets_names());
+ Util::Vectors::append(&results, port_at_each_triplet);
}
output_sorted_results_and_exit(VCPKG_LINE_INFO, std::move(results));
diff --git a/toolsrc/src/vcpkg/commands.buildexternal.cpp b/toolsrc/src/vcpkg/commands.buildexternal.cpp
index 1c3c511c2..135ef132b 100644
--- a/toolsrc/src/vcpkg/commands.buildexternal.cpp
+++ b/toolsrc/src/vcpkg/commands.buildexternal.cpp
@@ -1,6 +1,7 @@
#include "pch.h"
#include <vcpkg/build.h>
+#include <vcpkg/cmakevars.h>
#include <vcpkg/commands.h>
#include <vcpkg/help.h>
#include <vcpkg/input.h>
@@ -25,10 +26,13 @@ namespace vcpkg::Commands::BuildExternal
auto overlays = args.overlay_ports ? *args.overlay_ports : std::vector<std::string>();
overlays.insert(overlays.begin(), args.command_arguments.at(1));
- Dependencies::PathsPortFileProvider provider(paths, &overlays);
+
+ PortFileProvider::PathsPortFileProvider provider(paths, &overlays);
auto maybe_scfl = provider.get_control_file(spec.package_spec.name());
+
Checks::check_exit(
VCPKG_LINE_INFO, maybe_scfl.has_value(), "could not load control file for %s", spec.package_spec.name());
- Build::Command::perform_and_exit_ex(spec, maybe_scfl.value_or_exit(VCPKG_LINE_INFO), options, paths);
+
+ Build::Command::perform_and_exit_ex(spec, maybe_scfl.value_or_exit(VCPKG_LINE_INFO), provider, options, paths);
}
}
diff --git a/toolsrc/src/vcpkg/commands.ci.cpp b/toolsrc/src/vcpkg/commands.ci.cpp
index 419d65f03..b8ac97563 100644
--- a/toolsrc/src/vcpkg/commands.ci.cpp
+++ b/toolsrc/src/vcpkg/commands.ci.cpp
@@ -13,8 +13,12 @@
#include <vcpkg/help.h>
#include <vcpkg/input.h>
#include <vcpkg/install.h>
+#include <vcpkg/logicexpression.h>
+#include <vcpkg/packagespec.h>
#include <vcpkg/vcpkglib.h>
+using namespace vcpkg;
+
namespace vcpkg::Commands::CI
{
using Build::BuildResult;
@@ -178,7 +182,7 @@ namespace vcpkg::Commands::CI
std::string feature_list;
for (const auto& feature : test.features)
{
- if (!feature_list.empty())
+ if (!feature_list.empty())
{
feature_list += ", ";
}
@@ -215,14 +219,44 @@ namespace vcpkg::Commands::CI
std::vector<FullPackageSpec> unknown;
std::map<PackageSpec, Build::BuildResult> known;
std::map<PackageSpec, std::vector<std::string>> features;
+ std::unordered_map<std::string, SourceControlFileLocation> default_feature_provider;
std::map<PackageSpec, std::string> abi_tag_map;
};
+ static bool supported_for_triplet(const CMakeVars::TripletCMakeVarProvider& var_provider,
+
+ const InstallPlanAction* install_plan)
+ {
+ const std::string& supports_expression =
+ install_plan->source_control_file_location.value_or_exit(VCPKG_LINE_INFO)
+ .source_control_file->core_paragraph->supports_expression;
+ if (supports_expression.empty())
+ {
+ return true; // default to 'supported'
+ }
+
+ ExpressionContext context = {var_provider.get_tag_vars(install_plan->spec).value_or_exit(VCPKG_LINE_INFO),
+ install_plan->spec.triplet().canonical_name()};
+ auto maybe_result = evaluate_expression(supports_expression, context);
+ if (auto b = maybe_result.get())
+ return *b;
+ else
+ {
+ System::print2(System::Color::error,
+ "Error: while processing ",
+ install_plan->spec.to_string(),
+ "\n",
+ maybe_result.error());
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+ }
+
static std::unique_ptr<UnknownCIPortsResults> find_unknown_ports_for_ci(
const VcpkgPaths& paths,
const std::set<std::string>& exclusions,
- const Dependencies::PortFileProvider& provider,
- const std::vector<FeatureSpec>& fspecs,
+ const PortFileProvider::PortFileProvider& provider,
+ const CMakeVars::TripletCMakeVarProvider& var_provider,
+ const std::vector<FullPackageSpec>& specs,
const bool purge_tombstones)
{
auto ret = std::make_unique<UnknownCIPortsResults>();
@@ -243,101 +277,149 @@ namespace vcpkg::Commands::CI
Build::FailOnTombstone::YES,
};
- vcpkg::Cache<Triplet, Build::PreBuildInfo> pre_build_info_cache;
-
- auto action_plan = Dependencies::create_feature_install_plan(provider, fspecs, {}, {});
-
- auto timer = Chrono::ElapsedTimer::create_started();
-
- for (auto&& action : action_plan)
+ std::vector<PackageSpec> packages_with_qualified_deps;
+ auto has_qualifier = [](Dependency const& dep) { return !dep.qualifier.empty(); };
+ for (auto&& spec : specs)
{
- if (auto p = action.install_action.get())
+ auto&& scfl = provider.get_control_file(spec.package_spec.name()).value_or_exit(VCPKG_LINE_INFO);
+ if (Util::any_of(scfl.source_control_file->core_paragraph->depends, has_qualifier) ||
+ Util::any_of(scfl.source_control_file->feature_paragraphs,
+ [&](auto&& pgh) { return Util::any_of(pgh->depends, has_qualifier); }))
{
- // determine abi tag
- std::string abi;
- if (auto scfl = p->source_control_file_location.get())
- {
- auto triplet = p->spec.triplet();
+ packages_with_qualified_deps.push_back(spec.package_spec);
+ }
+ }
+
+ var_provider.load_dep_info_vars(packages_with_qualified_deps);
+ auto action_plan = Dependencies::create_feature_install_plan(provider, var_provider, specs, {}, {});
- const Build::BuildPackageConfig build_config{*scfl, triplet, build_options, p->feature_list};
+ std::vector<FullPackageSpec> install_specs;
+ for (auto&& install_action : action_plan.install_actions)
+ {
+ install_specs.emplace_back(FullPackageSpec{
+ install_action.spec,
+ std::vector<std::string>{install_action.feature_list.begin(), install_action.feature_list.end()}});
+ }
- auto dependency_abis =
- Util::fmap(p->computed_dependencies, [&](const PackageSpec& spec) -> Build::AbiEntry {
- auto it = ret->abi_tag_map.find(spec);
+ var_provider.load_tag_vars(install_specs, provider);
- if (it == ret->abi_tag_map.end())
- return {spec.name(), ""};
- else
- return {spec.name(), it->second};
- });
- const auto& pre_build_info = pre_build_info_cache.get_lazy(
- triplet, [&]() { return Build::PreBuildInfo::from_triplet_file(paths, triplet, *scfl); });
+ auto timer = Chrono::ElapsedTimer::create_started();
- auto maybe_tag_and_file =
- Build::compute_abi_tag(paths, build_config, pre_build_info, dependency_abis);
- if (auto tag_and_file = maybe_tag_and_file.get())
- {
- abi = tag_and_file->tag;
- ret->abi_tag_map.emplace(p->spec, abi);
- }
- }
- else if (auto ipv = p->installed_package.get())
+ Checks::check_exit(VCPKG_LINE_INFO,
+ action_plan.already_installed.empty(),
+ "Cannot use CI command with packages already installed.");
+ std::string stdout_buffer;
+ for (auto&& action : action_plan.install_actions)
+ {
+ auto p = &action;
+ // determine abi tag
+ std::string abi;
+ if (auto scfl = p->source_control_file_location.get())
+ {
+ auto emp = ret->default_feature_provider.emplace(p->spec.name(), *scfl);
+ emp.first->second.source_control_file->core_paragraph->default_features = p->feature_list;
+
+ auto triplet = p->spec.triplet();
+
+ const Build::BuildPackageConfig build_config{*scfl,
+ triplet,
+ build_options,
+ var_provider,
+ p->feature_dependencies,
+ p->package_dependencies,
+ p->feature_list};
+
+ auto dependency_abis =
+ Util::fmap(build_config.package_dependencies, [&](const PackageSpec& spec) -> Build::AbiEntry {
+ auto it = ret->abi_tag_map.find(spec);
+
+ if (it == ret->abi_tag_map.end())
+ return {spec.name(), ""};
+ else
+ return {spec.name(), it->second};
+ });
+
+ const auto pre_build_info = Build::PreBuildInfo(
+ paths, triplet, var_provider.get_tag_vars(p->spec).value_or_exit(VCPKG_LINE_INFO));
+
+ auto maybe_tag_and_file = Build::compute_abi_tag(paths, build_config, pre_build_info, dependency_abis);
+ if (auto tag_and_file = maybe_tag_and_file.get())
{
- abi = ipv->core->package.abi;
- if (!abi.empty()) ret->abi_tag_map.emplace(p->spec, abi);
+ abi = tag_and_file->tag;
+ ret->abi_tag_map.emplace(p->spec, abi);
}
+ }
+ else if (auto ipv = p->installed_package.get())
+ {
+ abi = ipv->core->package.abi;
+ if (!abi.empty()) ret->abi_tag_map.emplace(p->spec, abi);
+ }
- std::string state;
-
- auto archives_root_dir = paths.root / "archives";
- auto archive_name = abi + ".zip";
- auto archive_subpath = fs::u8path(abi.substr(0, 2)) / archive_name;
- auto archive_path = archives_root_dir / archive_subpath;
- auto archive_tombstone_path = archives_root_dir / "fail" / archive_subpath;
+ auto archives_root_dir = paths.root / "archives";
+ auto archive_name = abi + ".zip";
+ auto archive_subpath = fs::u8path(abi.substr(0, 2)) / archive_name;
+ auto archive_path = archives_root_dir / archive_subpath;
+ auto archive_tombstone_path = archives_root_dir / "fail" / archive_subpath;
- if (purge_tombstones)
- {
- std::error_code ec;
- fs.remove(archive_tombstone_path, ec); // Ignore error
- }
+ if (purge_tombstones)
+ {
+ std::error_code ec;
+ fs.remove(archive_tombstone_path, ec); // Ignore error
+ }
- bool b_will_build = false;
+ bool b_will_build = false;
- ret->features.emplace(p->spec,
- std::vector<std::string>{p->feature_list.begin(), p->feature_list.end()});
+ std::string state;
- if (Util::Sets::contains(exclusions, p->spec.name()))
- {
- ret->known.emplace(p->spec, BuildResult::EXCLUDED);
- will_fail.emplace(p->spec);
- }
- else if (std::any_of(p->computed_dependencies.begin(),
- p->computed_dependencies.end(),
- [&](const PackageSpec& spec) { return Util::Sets::contains(will_fail, spec); }))
- {
- ret->known.emplace(p->spec, BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES);
- will_fail.emplace(p->spec);
- }
- else if (fs.exists(archive_path))
- {
- state += "pass";
- ret->known.emplace(p->spec, BuildResult::SUCCEEDED);
- }
- else if (fs.exists(archive_tombstone_path))
- {
- state += "fail";
- ret->known.emplace(p->spec, BuildResult::BUILD_FAILED);
- will_fail.emplace(p->spec);
- }
- else
- {
- ret->unknown.push_back({p->spec, {p->feature_list.begin(), p->feature_list.end()}});
- b_will_build = true;
- }
+ if (Util::Sets::contains(exclusions, p->spec.name()))
+ {
+ state = "skip";
+ ret->known.emplace(p->spec, BuildResult::EXCLUDED);
+ will_fail.emplace(p->spec);
+ }
+ else if (!supported_for_triplet(var_provider, p))
+ {
+ // This treats unsupported ports as if they are excluded
+ // which means the ports dependent on it will be cascaded due to missing dependencies
+ // Should this be changed so instead it is a failure to depend on a unsupported port?
+ state = "n/a";
+ ret->known.emplace(p->spec, BuildResult::EXCLUDED);
+ will_fail.emplace(p->spec);
+ }
+ else if (Util::any_of(p->package_dependencies,
+ [&](const PackageSpec& spec) { return Util::Sets::contains(will_fail, spec); }))
+ {
+ state = "cascade";
+ ret->known.emplace(p->spec, BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES);
+ will_fail.emplace(p->spec);
+ }
+ else if (fs.exists(archive_path))
+ {
+ state = "pass";
+ ret->known.emplace(p->spec, BuildResult::SUCCEEDED);
+ }
+ else if (fs.exists(archive_tombstone_path))
+ {
+ state = "fail";
+ ret->known.emplace(p->spec, BuildResult::BUILD_FAILED);
+ will_fail.emplace(p->spec);
+ }
+ else
+ {
+ ret->unknown.push_back(FullPackageSpec{p->spec, {p->feature_list.begin(), p->feature_list.end()}});
+ b_will_build = true;
+ }
- System::printf("%40s: %1s %8s: %s\n", p->spec, (b_will_build ? "*" : " "), state, abi);
+ Strings::append(stdout_buffer,
+ Strings::format("%40s: %1s %8s: %s\n", p->spec, (b_will_build ? "*" : " "), state, abi));
+ if (stdout_buffer.size() > 2048)
+ {
+ System::print2(stdout_buffer);
+ stdout_buffer.clear();
}
}
+ System::print2(stdout_buffer);
+ stdout_buffer.clear();
System::printf("Time to determine pass/fail: %s\n", timer.elapsed());
@@ -374,7 +456,8 @@ namespace vcpkg::Commands::CI
StatusParagraphs status_db = database_load_check(paths);
- Dependencies::PathsPortFileProvider provider(paths, args.overlay_ports.get());
+ PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports.get());
+ CMakeVars::TripletCMakeVarProvider var_provider(paths);
const Build::BuildPackageOptions install_plan_options = {
Build::UseHeadVersion::NO,
@@ -386,6 +469,7 @@ namespace vcpkg::Commands::CI
Build::DownloadTool::BUILT_IN,
GlobalState::g_binary_caching ? Build::BinaryCaching::YES : Build::BinaryCaching::NO,
Build::FailOnTombstone::YES,
+ Build::PurgeDecompressFailure::YES,
};
std::vector<std::map<PackageSpec, BuildResult>> all_known_results;
@@ -394,7 +478,7 @@ namespace vcpkg::Commands::CI
XunitTestResults xunitTestResults;
std::vector<std::string> all_ports =
- Util::fmap(provider.load_all_control_files(), [](auto&& scfl) -> std::string {
+ Util::fmap(provider.load_all_control_files(), [](const SourceControlFileLocation*& scfl) -> std::string {
return scfl->source_control_file.get()->core_paragraph->name;
});
std::vector<TripletAndSummary> results;
@@ -405,17 +489,18 @@ namespace vcpkg::Commands::CI
xunitTestResults.push_collection(triplet.canonical_name());
- Dependencies::PackageGraph pgraph(provider, status_db);
-
std::vector<PackageSpec> specs = PackageSpec::to_package_specs(all_ports, triplet);
// Install the default features for every package
- auto all_feature_specs = Util::fmap(specs, [](auto& spec) { return FeatureSpec(spec, ""); });
- auto split_specs =
- find_unknown_ports_for_ci(paths, exclusions_set, provider, all_feature_specs, purge_tombstones);
- auto feature_specs = FullPackageSpec::to_feature_specs(split_specs->unknown);
+ auto all_default_full_specs = Util::fmap(specs, [&](auto& spec) {
+ std::vector<std::string> default_features =
+ provider.get_control_file(spec.name()).get()->source_control_file->core_paragraph->default_features;
+ default_features.emplace_back("core");
+ return FullPackageSpec{spec, std::move(default_features)};
+ });
- for (auto&& fspec : feature_specs)
- pgraph.install(fspec);
+ auto split_specs = find_unknown_ports_for_ci(
+ paths, exclusions_set, provider, var_provider, all_default_full_specs, purge_tombstones);
+ PortFileProvider::MapPortFileProvider new_default_provider(split_specs->default_feature_provider);
Dependencies::CreateInstallPlanOptions serialize_options;
@@ -436,36 +521,20 @@ namespace vcpkg::Commands::CI
serialize_options.randomizer = &randomizer_instance;
}
- auto action_plan = [&]() {
- int iterations = 0;
- do
- {
- bool inconsistent = false;
- auto action_plan = pgraph.serialize(serialize_options);
-
- for (auto&& action : action_plan)
- {
- if (auto p = action.install_action.get())
- {
- p->build_options = install_plan_options;
- if (Util::Sets::contains(exclusions_set, p->spec.name()))
- {
- p->plan_type = InstallPlanType::EXCLUDED;
- }
-
- for (auto&& feature : split_specs->features[p->spec])
- if (p->feature_list.find(feature) == p->feature_list.end())
- {
- pgraph.install({p->spec, feature});
- inconsistent = true;
- }
- }
- }
+ auto action_plan = Dependencies::create_feature_install_plan(
+ new_default_provider, var_provider, split_specs->unknown, status_db, serialize_options);
- if (!inconsistent) return action_plan;
- Checks::check_exit(VCPKG_LINE_INFO, ++iterations < 100);
- } while (true);
- }();
+ for (auto&& action : action_plan.install_actions)
+ {
+ if (Util::Sets::contains(exclusions_set, action.spec.name()))
+ {
+ action.plan_type = InstallPlanType::EXCLUDED;
+ }
+ else
+ {
+ action.build_options = install_plan_options;
+ }
+ }
if (is_dry_run)
{
@@ -474,7 +543,7 @@ namespace vcpkg::Commands::CI
else
{
auto collection_timer = Chrono::ElapsedTimer::create_started();
- auto summary = Install::perform(action_plan, Install::KeepGoing::YES, paths, status_db);
+ auto summary = Install::perform(action_plan, Install::KeepGoing::YES, paths, status_db, var_provider);
auto collection_time_elapsed = collection_timer.elapsed();
// Adding results for ports that were built or pulled from an archive
diff --git a/toolsrc/src/vcpkg/commands.dependinfo.cpp b/toolsrc/src/vcpkg/commands.dependinfo.cpp
index 79cbba590..eecc45634 100644
--- a/toolsrc/src/vcpkg/commands.dependinfo.cpp
+++ b/toolsrc/src/vcpkg/commands.dependinfo.cpp
@@ -11,10 +11,9 @@
#include <vcpkg/packagespec.h>
#include <vector>
-using vcpkg::Dependencies::AnyAction;
-using vcpkg::Dependencies::create_feature_install_plan;
+using vcpkg::Dependencies::ActionPlan;
using vcpkg::Dependencies::InstallPlanAction;
-using vcpkg::Dependencies::PathsPortFileProvider;
+using vcpkg::PortFileProvider::PathsPortFileProvider;
namespace vcpkg::Commands::DependInfo
{
@@ -44,7 +43,7 @@ namespace vcpkg::Commands::DependInfo
{
std::string package;
int depth;
- std::set<std::string> features;
+ std::unordered_set<std::string> features;
std::vector<std::string> dependencies;
};
@@ -205,9 +204,10 @@ namespace vcpkg::Commands::DependInfo
const InstallPlanAction& install_action = *pia;
const std::vector<std::string> dependencies = Util::fmap(
- install_action.computed_dependencies, [](const PackageSpec& spec) { return spec.name(); });
+ install_action.package_dependencies, [](const PackageSpec& spec) { return spec.name(); });
- std::set<std::string> features{install_action.feature_list};
+ std::unordered_set<std::string> features{install_action.feature_list.begin(),
+ install_action.feature_list.end()};
features.erase("core");
std::string port_name = install_action.spec.name();
@@ -252,19 +252,18 @@ namespace vcpkg::Commands::DependInfo
}
PathsPortFileProvider provider(paths, args.overlay_ports.get());
+ CMakeVars::TripletCMakeVarProvider var_provider(paths);
// By passing an empty status_db, we should get a plan containing all dependencies.
// All actions in the plan should be install actions, as there's no installed packages to remove.
StatusParagraphs status_db;
- std::vector<AnyAction> action_plan =
- create_feature_install_plan(provider, FullPackageSpec::to_feature_specs(specs), status_db);
- std::vector<const InstallPlanAction*> install_actions = Util::fmap(action_plan, [&](const AnyAction& action) {
- if (auto install_action = action.install_action.get())
- {
- return install_action;
- }
- Checks::exit_with_message(VCPKG_LINE_INFO, "Only install actions should exist in the plan");
- });
+ auto action_plan = Dependencies::create_feature_install_plan(provider, var_provider, specs, status_db);
+ Checks::check_exit(
+ VCPKG_LINE_INFO, action_plan.remove_actions.empty(), "Only install actions should exist in the plan");
+ std::vector<const InstallPlanAction*> install_actions =
+ Util::fmap(action_plan.already_installed, [&](const auto& action) { return &action; });
+ for (auto&& action : action_plan.install_actions)
+ install_actions.push_back(&action);
std::vector<PackageDependInfo> depend_info = extract_depend_info(install_actions, max_depth);
diff --git a/toolsrc/src/vcpkg/commands.env.cpp b/toolsrc/src/vcpkg/commands.env.cpp
index b81aeae55..c54f73e76 100644
--- a/toolsrc/src/vcpkg/commands.env.cpp
+++ b/toolsrc/src/vcpkg/commands.env.cpp
@@ -2,7 +2,9 @@
#include <vcpkg/base/strings.h>
#include <vcpkg/base/system.process.h>
+
#include <vcpkg/build.h>
+#include <vcpkg/cmakevars.h>
#include <vcpkg/commands.h>
#include <vcpkg/help.h>
@@ -30,13 +32,20 @@ namespace vcpkg::Commands::Env
nullptr,
};
+ // This command should probably optionally take a port
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& triplet)
{
const auto& fs = paths.get_filesystem();
const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
- const auto pre_build_info = Build::PreBuildInfo::from_triplet_file(paths, triplet);
+ PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports.get());
+ CMakeVars::TripletCMakeVarProvider var_provider(paths);
+
+ var_provider.load_generic_triplet_vars(triplet);
+
+ const Build::PreBuildInfo pre_build_info(
+ paths, triplet, var_provider.get_generic_triplet_vars(triplet).value_or_exit(VCPKG_LINE_INFO));
const Toolset& toolset = paths.get_toolset(pre_build_info);
auto env_cmd = Build::make_build_env_cmd(pre_build_info, toolset);
diff --git a/toolsrc/src/vcpkg/commands.search.cpp b/toolsrc/src/vcpkg/commands.search.cpp
index 943233502..372c394fa 100644
--- a/toolsrc/src/vcpkg/commands.search.cpp
+++ b/toolsrc/src/vcpkg/commands.search.cpp
@@ -9,7 +9,7 @@
#include <vcpkg/sourceparagraph.h>
#include <vcpkg/vcpkglib.h>
-using vcpkg::Dependencies::PathsPortFileProvider;
+using vcpkg::PortFileProvider::PathsPortFileProvider;
namespace vcpkg::Commands::Search
{
diff --git a/toolsrc/src/vcpkg/commands.upgrade.cpp b/toolsrc/src/vcpkg/commands.upgrade.cpp
index b1dbf6194..e8afabed5 100644
--- a/toolsrc/src/vcpkg/commands.upgrade.cpp
+++ b/toolsrc/src/vcpkg/commands.upgrade.cpp
@@ -44,8 +44,8 @@ namespace vcpkg::Commands::Upgrade
StatusParagraphs status_db = database_load_check(paths);
// Load ports from ports dirs
- Dependencies::PathsPortFileProvider provider(paths, args.overlay_ports.get());
- Dependencies::PackageGraph graph(provider, status_db);
+ PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports.get());
+ CMakeVars::TripletCMakeVarProvider var_provider(paths);
// input sanitization
const std::vector<PackageSpec> specs = Util::fmap(args.command_arguments, [&](auto&& arg) {
@@ -57,6 +57,7 @@ namespace vcpkg::Commands::Upgrade
Input::check_triplet(spec.triplet(), paths);
}
+ Dependencies::ActionPlan action_plan;
if (specs.empty())
{
// If no packages specified, upgrade all outdated packages.
@@ -68,8 +69,11 @@ namespace vcpkg::Commands::Upgrade
Checks::exit_success(VCPKG_LINE_INFO);
}
- for (auto&& outdated_package : outdated_packages)
- graph.upgrade(outdated_package.spec);
+ action_plan = Dependencies::create_upgrade_plan(
+ provider,
+ var_provider,
+ Util::fmap(outdated_packages, [](const Update::OutdatedPackage& package) { return package.spec; }),
+ status_db);
}
else
{
@@ -143,13 +147,10 @@ namespace vcpkg::Commands::Upgrade
if (to_upgrade.empty()) Checks::exit_success(VCPKG_LINE_INFO);
- for (auto&& spec : to_upgrade)
- graph.upgrade(spec);
+ action_plan = Dependencies::create_upgrade_plan(provider, var_provider, to_upgrade, status_db);
}
- auto plan = graph.serialize();
-
- Checks::check_exit(VCPKG_LINE_INFO, !plan.empty());
+ Checks::check_exit(VCPKG_LINE_INFO, !action_plan.empty());
const Build::BuildPackageOptions install_plan_options = {
Build::UseHeadVersion::NO,
@@ -164,15 +165,12 @@ namespace vcpkg::Commands::Upgrade
};
// Set build settings for all install actions
- for (auto&& action : plan)
+ for (auto&& action : action_plan.install_actions)
{
- if (auto p_install = action.install_action.get())
- {
- p_install->build_options = install_plan_options;
- }
+ action.build_options = install_plan_options;
}
- Dependencies::print_plan(plan, true, paths.ports);
+ Dependencies::print_plan(action_plan, true, paths.ports);
if (!no_dry_run)
{
@@ -182,7 +180,8 @@ namespace vcpkg::Commands::Upgrade
Checks::exit_fail(VCPKG_LINE_INFO);
}
- const Install::InstallSummary summary = Install::perform(plan, keep_going, paths, status_db);
+ const Install::InstallSummary summary =
+ Install::perform(action_plan, keep_going, paths, status_db, var_provider);
System::print2("\nTotal elapsed time: ", summary.total_elapsed_time, "\n\n");
diff --git a/toolsrc/src/vcpkg/dependencies.cpp b/toolsrc/src/vcpkg/dependencies.cpp
index 7c3f2df4c..a395b167f 100644
--- a/toolsrc/src/vcpkg/dependencies.cpp
+++ b/toolsrc/src/vcpkg/dependencies.cpp
@@ -7,25 +7,40 @@
#include <vcpkg/dependencies.h>
#include <vcpkg/packagespec.h>
#include <vcpkg/paragraphs.h>
+#include <vcpkg/portfileprovider.h>
#include <vcpkg/statusparagraphs.h>
#include <vcpkg/vcpkglib.h>
#include <vcpkg/vcpkgpaths.h>
+using namespace vcpkg;
+
namespace vcpkg::Dependencies
{
namespace
{
struct ClusterInstalled
{
+ ClusterInstalled(const InstalledPackageView& ipv) : ipv(ipv)
+ {
+ original_features.emplace("core");
+ for (auto&& feature : ipv.features)
+ {
+ original_features.emplace(feature->package.feature);
+ }
+ }
+
InstalledPackageView ipv;
- std::set<PackageSpec> remove_edges;
- std::set<std::string> original_features;
+ std::unordered_set<PackageSpec> remove_edges;
+ std::unordered_set<std::string> original_features;
+
+ // Tracks whether an incoming request has asked for the default features -- on reinstall, add them
+ bool defaults_requested = false;
};
- struct ClusterSource
+ struct ClusterInstallInfo
{
- const SourceControlFileLocation* scfl = nullptr;
std::unordered_map<std::string, std::vector<FeatureSpec>> build_edges;
+ bool defaults_requested = false;
};
/// <summary>
@@ -33,56 +48,189 @@ namespace vcpkg::Dependencies
/// </summary>
struct Cluster : Util::MoveOnlyBase
{
- PackageSpec spec;
+ Cluster(const InstalledPackageView& ipv, const SourceControlFileLocation& scfl)
+ : m_spec(ipv.spec()), m_scfl(scfl), m_installed(ipv)
+ {
+ }
+
+ Cluster(const PackageSpec& spec, const SourceControlFileLocation& scfl) : m_spec(spec), m_scfl(scfl) {}
+
+ bool has_feature_installed(const std::string& feature) const
+ {
+ if (const ClusterInstalled* inst = m_installed.get())
+ {
+ return inst->original_features.find(feature) != inst->original_features.end();
+ }
+ return false;
+ }
+
+ bool has_defaults_installed() const
+ {
+ if (const ClusterInstalled* inst = m_installed.get())
+ {
+ return std::all_of(inst->ipv.core->package.default_features.begin(),
+ inst->ipv.core->package.default_features.end(),
+ [&](const std::string& feature) {
+ return inst->original_features.find(feature) !=
+ inst->original_features.end();
+ });
+ }
+ return false;
+ }
+
+ // Returns dependencies which were added as a result of this call.
+ // Precondition: must have called "mark_for_reinstall()" or "create_install_info()" on this cluster
+ void add_feature(const std::string& feature,
+ const CMakeVars::CMakeVarProvider& var_provider,
+ std::vector<FeatureSpec>& out_new_dependencies)
+ {
+ ClusterInstallInfo& info = m_install_info.value_or_exit(VCPKG_LINE_INFO);
+ if (feature == "default")
+ {
+ if (!info.defaults_requested)
+ {
+ info.defaults_requested = true;
+ for (auto&& f : m_scfl.source_control_file->core_paragraph->default_features)
+ out_new_dependencies.emplace_back(m_spec, f);
+ }
+ return;
+ }
+
+ if (Util::Sets::contains(info.build_edges, feature))
+ {
+ // This feature has already been completely handled
+ return;
+ }
+ auto maybe_vars = var_provider.get_dep_info_vars(m_spec);
+ const std::vector<Dependency>* qualified_deps =
+ &m_scfl.source_control_file->find_dependencies_for_feature(feature).value_or_exit(VCPKG_LINE_INFO);
+
+ std::vector<FeatureSpec> dep_list;
+ if (maybe_vars)
+ {
+ // Qualified dependency resolution is available
+ auto fullspec_list = filter_dependencies(
+ *qualified_deps, m_spec.triplet(), maybe_vars.value_or_exit(VCPKG_LINE_INFO));
+
+ for (auto&& fspec : fullspec_list)
+ {
+ // TODO: this is incorrect and does not handle default features nor "*"
+ Util::Vectors::append(&dep_list, fspec.to_feature_specs({"default"}, {}));
+ }
+
+ Util::sort_unique_erase(dep_list);
+ info.build_edges.emplace(feature, dep_list);
+ }
+ else
+ {
+ bool requires_qualified_resolution = false;
+ for (const Dependency& dep : *qualified_deps)
+ {
+ if (dep.qualifier.empty())
+ {
+ Util::Vectors::append(
+ &dep_list,
+ FullPackageSpec(PackageSpec::from_name_and_triplet(dep.depend.name, m_spec.triplet())
+ .value_or_exit(VCPKG_LINE_INFO),
+ dep.depend.features)
+ .to_feature_specs({"default"}, {}));
+ }
+ else
+ {
+ requires_qualified_resolution = true;
+ }
+ }
+ Util::sort_unique_erase(dep_list);
+ if (!requires_qualified_resolution)
+ {
+ info.build_edges.emplace(feature, dep_list);
+ }
+ }
+ Util::Vectors::append(&out_new_dependencies, dep_list);
+ }
+
+ void create_install_info(std::vector<FeatureSpec>& out_reinstall_requirements)
+ {
+ bool defaults_requested = false;
+ if (const ClusterInstalled* inst = m_installed.get())
+ {
+ for (const std::string& installed_feature : inst->original_features)
+ {
+ out_reinstall_requirements.emplace_back(m_spec, installed_feature);
+ }
+ defaults_requested = inst->defaults_requested;
+ }
+
+ Checks::check_exit(VCPKG_LINE_INFO, !m_install_info.has_value());
+ m_install_info = make_optional(ClusterInstallInfo{});
+
+ if (defaults_requested)
+ {
+ for (auto&& def_feature : m_scfl.source_control_file->core_paragraph->default_features)
+ out_reinstall_requirements.emplace_back(m_spec, def_feature);
+ }
+ else if (request_type != RequestType::USER_REQUESTED)
+ {
+ // If the user did not explicitly request this installation, we need to add all new default features
+ auto&& new_defaults = m_scfl.source_control_file->core_paragraph->default_features;
+ std::set<std::string> defaults_set{new_defaults.begin(), new_defaults.end()};
+
+ // Install only features that were not previously available
+ if (auto p_inst = m_installed.get())
+ {
+ for (auto&& prev_default : p_inst->ipv.core->package.default_features)
+ {
+ defaults_set.erase(prev_default);
+ }
+ }
+
+ for (const std::string& feature : defaults_set)
+ {
+ // Instead of dealing with adding default features to each of our dependencies right
+ // away we just defer to the next pass of the loop.
+ out_reinstall_requirements.emplace_back(m_spec, feature);
+ }
+ }
+ }
+
+ PackageSpec m_spec;
+ const SourceControlFileLocation& m_scfl;
- Optional<ClusterInstalled> installed;
- Optional<ClusterSource> source;
+ Optional<ClusterInstalled> m_installed;
+ Optional<ClusterInstallInfo> m_install_info;
- // Note: this map can contain "special" strings such as "" and "*"
- std::unordered_map<std::string, bool> plus;
- std::set<std::string> to_install_features;
- bool minus = false;
- bool transient_uninstalled = true;
RequestType request_type = RequestType::AUTO_SELECTED;
};
- struct ClusterPtr
+ struct PackageGraph
{
- Cluster* ptr;
+ PackageGraph(const PortFileProvider::PortFileProvider& provider,
+ const CMakeVars::CMakeVarProvider& var_provider,
+ const StatusParagraphs& status_db);
+ ~PackageGraph();
- Cluster* operator->() const { return ptr; }
- };
+ void install(Span<const FeatureSpec> specs);
+ void upgrade(Span<const PackageSpec> specs);
+ void mark_user_requested(const PackageSpec& spec);
- bool operator==(const ClusterPtr& l, const ClusterPtr& r) { return l.ptr == r.ptr; }
- }
-}
+ ActionPlan serialize(const CreateInstallPlanOptions& options = {}) const;
-namespace std
-{
- template<>
- struct hash<vcpkg::Dependencies::ClusterPtr>
- {
- size_t operator()(const vcpkg::Dependencies::ClusterPtr& value) const
- {
- return std::hash<vcpkg::PackageSpec>()(value.ptr->spec);
- }
- };
-}
+ void mark_for_reinstall(const PackageSpec& spec, std::vector<FeatureSpec>& out_reinstall_requirements);
+ const CMakeVars::CMakeVarProvider& m_var_provider;
-namespace vcpkg::Dependencies
-{
- struct GraphPlan
- {
- Graphs::Graph<ClusterPtr> remove_graph;
- Graphs::Graph<ClusterPtr> install_graph;
- };
+ std::unique_ptr<ClusterGraph> m_graph;
+ };
+
+ }
/// <summary>
/// Directional graph representing a collection of packages with their features connected by their dependencies.
/// </summary>
struct ClusterGraph : Util::MoveOnlyBase
{
- explicit ClusterGraph(const PortFileProvider& provider) : m_provider(provider) {}
+ explicit ClusterGraph(const PortFileProvider::PortFileProvider& port_provider) : m_port_provider(port_provider)
+ {
+ }
/// <summary>
/// Find the cluster associated with spec or if not found, create it from the PortFileProvider.
@@ -94,35 +242,64 @@ namespace vcpkg::Dependencies
auto it = m_graph.find(spec);
if (it == m_graph.end())
{
- // Load on-demand from m_provider
- auto maybe_scfl = m_provider.get_control_file(spec.name());
- auto& clust = m_graph[spec];
- clust.spec = spec;
- if (auto p_scfl = maybe_scfl.get())
- {
- clust.source = cluster_from_scf(*p_scfl, clust.spec.triplet());
- }
- return clust;
+ const SourceControlFileLocation* scfl = m_port_provider.get_control_file(spec.name()).get();
+
+ Checks::check_exit(
+ VCPKG_LINE_INFO, scfl, "Error: Cannot find definition for package `%s`.", spec.name());
+
+ return m_graph
+ .emplace(std::piecewise_construct, std::forward_as_tuple(spec), std::forward_as_tuple(spec, *scfl))
+ .first->second;
}
+
return it->second;
}
- private:
- static ClusterSource cluster_from_scf(const SourceControlFileLocation& scfl, Triplet t)
+ Cluster& get(const InstalledPackageView& ipv)
{
- ClusterSource ret;
- ret.build_edges.emplace("core",
- filter_dependencies_to_specs(scfl.source_control_file->core_paragraph->depends, t));
+ auto it = m_graph.find(ipv.spec());
+
+ if (it == m_graph.end())
+ {
+ Optional<const SourceControlFileLocation&> maybe_scfl =
+ m_port_provider.get_control_file(ipv.spec().name());
+
+ if (!maybe_scfl)
+ Checks::exit_with_message(VCPKG_LINE_INFO,
+ "We could not find a CONTROL file for ",
+ ipv.spec().to_string(),
+ ". Please run \"vcpkg remove ",
+ ipv.spec().to_string(),
+ "\" and re-attempt.");
+
+ return m_graph
+ .emplace(std::piecewise_construct,
+ std::forward_as_tuple(ipv.spec()),
+ std::forward_as_tuple(ipv, *maybe_scfl.get()))
+ .first->second;
+ }
+
+ if (!it->second.m_installed)
+ {
+ it->second.m_installed = {ipv};
+ }
- for (const auto& feature : scfl.source_control_file->feature_paragraphs)
- ret.build_edges.emplace(feature->name, filter_dependencies_to_specs(feature->depends, t));
+ return it->second;
+ }
- ret.scfl = &scfl;
- return ret;
+ const Cluster& find_or_exit(const PackageSpec& spec, LineInfo linfo) const
+ {
+ auto it = m_graph.find(spec);
+ Checks::check_exit(linfo, it != m_graph.end(), "Failed to locate spec in graph");
+ return it->second;
}
+ auto begin() const { return m_graph.begin(); }
+ auto end() const { return m_graph.end(); }
+
+ private:
std::unordered_map<PackageSpec, Cluster> m_graph;
- const PortFileProvider& m_provider;
+ const PortFileProvider::PortFileProvider& m_port_provider;
};
static std::string to_output_string(RequestType request_type,
@@ -178,30 +355,44 @@ namespace vcpkg::Dependencies
InstallPlanAction::InstallPlanAction(const PackageSpec& spec,
const SourceControlFileLocation& scfl,
- const std::set<std::string>& features,
const RequestType& request_type,
- std::vector<PackageSpec>&& dependencies)
+ std::unordered_map<std::string, std::vector<FeatureSpec>>&& dependencies)
: spec(spec)
, source_control_file_location(scfl)
, plan_type(InstallPlanType::BUILD_AND_INSTALL)
, request_type(request_type)
, build_options{}
- , feature_list(features)
- , computed_dependencies(std::move(dependencies))
+ , feature_dependencies(std::move(dependencies))
{
+ for (const auto& kv : feature_dependencies)
+ {
+ feature_list.emplace_back(kv.first);
+ for (const FeatureSpec& fspec : kv.second)
+ {
+ if (spec != fspec.spec())
+ {
+ package_dependencies.emplace_back(fspec.spec());
+ }
+ }
+ }
+
+ Util::sort_unique_erase(package_dependencies);
+ Util::sort_unique_erase(feature_list);
}
- InstallPlanAction::InstallPlanAction(InstalledPackageView&& ipv,
- const std::set<std::string>& features,
- const RequestType& request_type)
+ InstallPlanAction::InstallPlanAction(InstalledPackageView&& ipv, const RequestType& request_type)
: spec(ipv.spec())
, installed_package(std::move(ipv))
, plan_type(InstallPlanType::ALREADY_INSTALLED)
, request_type(request_type)
, build_options{}
- , feature_list(features)
- , computed_dependencies(installed_package.get()->dependencies())
+ , feature_dependencies(installed_package.get()->feature_dependencies())
+ , package_dependencies(installed_package.get()->dependencies())
{
+ for (const auto& kv : feature_dependencies)
+ {
+ feature_list.emplace_back(kv.first);
+ }
}
std::string InstallPlanAction::displayname() const
@@ -211,7 +402,7 @@ namespace vcpkg::Dependencies
return this->spec.to_string();
}
- const std::string features = Strings::join(",", this->feature_list);
+ const std::string features = Strings::join(",", feature_list);
return Strings::format("%s[%s]:%s", this->spec.name(), features, this->spec.triplet());
}
@@ -232,21 +423,6 @@ namespace vcpkg::Dependencies
{
}
- const PackageSpec& AnyAction::spec() const
- {
- if (const auto p = install_action.get())
- {
- return p->spec;
- }
-
- if (const auto p = remove_action.get())
- {
- return p->spec;
- }
-
- Checks::exit_with_message(VCPKG_LINE_INFO, "Null action");
- }
-
bool ExportPlanAction::compare_by_name(const ExportPlanAction* left, const ExportPlanAction* right)
{
return left->spec.name() < right->spec.name();
@@ -294,145 +470,6 @@ namespace vcpkg::Dependencies
return left->spec.name() < right->spec.name();
}
- MapPortFileProvider::MapPortFileProvider(const std::unordered_map<std::string, SourceControlFileLocation>& map)
- : ports(map)
- {
- }
-
- Optional<const SourceControlFileLocation&> MapPortFileProvider::get_control_file(const std::string& spec) const
- {
- auto scf = ports.find(spec);
- if (scf == ports.end()) return nullopt;
- return scf->second;
- }
-
- std::vector<const SourceControlFileLocation*> MapPortFileProvider::load_all_control_files() const
- {
- return Util::fmap(ports, [](auto&& kvpair) -> const SourceControlFileLocation* { return &kvpair.second; });
- }
-
- PathsPortFileProvider::PathsPortFileProvider(const vcpkg::VcpkgPaths& paths,
- const std::vector<std::string>* ports_dirs_paths)
- : filesystem(paths.get_filesystem())
- {
- auto& fs = Files::get_real_filesystem();
- if (ports_dirs_paths)
- {
- for (auto&& overlay_path : *ports_dirs_paths)
- {
- if (!overlay_path.empty())
- {
- auto overlay = fs::stdfs::canonical(fs::u8path(overlay_path));
-
- Checks::check_exit(VCPKG_LINE_INFO,
- filesystem.exists(overlay),
- "Error: Path \"%s\" does not exist",
- overlay.string());
-
- Checks::check_exit(VCPKG_LINE_INFO,
- fs::is_directory(fs.status(VCPKG_LINE_INFO, overlay)),
- "Error: Path \"%s\" must be a directory",
- overlay.string());
-
- ports_dirs.emplace_back(overlay);
- }
- }
- }
- ports_dirs.emplace_back(paths.ports);
- }
-
- Optional<const SourceControlFileLocation&> PathsPortFileProvider::get_control_file(const std::string& spec) const
- {
- auto cache_it = cache.find(spec);
- if (cache_it != cache.end())
- {
- return cache_it->second;
- }
-
- for (auto&& ports_dir : ports_dirs)
- {
- // Try loading individual port
- if (filesystem.exists(ports_dir / "CONTROL"))
- {
- auto maybe_scf = Paragraphs::try_load_port(filesystem, ports_dir);
- if (auto scf = maybe_scf.get())
- {
- if (scf->get()->core_paragraph->name == spec)
- {
- SourceControlFileLocation scfl{std::move(*scf), ports_dir};
- auto it = cache.emplace(spec, std::move(scfl));
- return it.first->second;
- }
- }
- else
- {
- vcpkg::print_error_message(maybe_scf.error());
- Checks::exit_with_message(
- VCPKG_LINE_INFO, "Error: Failed to load port from %s", spec, ports_dir.u8string());
- }
- }
-
- auto found_scf = Paragraphs::try_load_port(filesystem, ports_dir / spec);
- if (auto scf = found_scf.get())
- {
- if (scf->get()->core_paragraph->name == spec)
- {
- SourceControlFileLocation scfl{std::move(*scf), ports_dir / spec};
- auto it = cache.emplace(spec, std::move(scfl));
- return it.first->second;
- }
- }
- }
-
- return nullopt;
- }
-
- std::vector<const SourceControlFileLocation*> PathsPortFileProvider::load_all_control_files() const
- {
- // Reload cache with ports contained in all ports_dirs
- cache.clear();
- std::vector<const SourceControlFileLocation*> ret;
- for (auto&& ports_dir : ports_dirs)
- {
- // Try loading individual port
- if (filesystem.exists(ports_dir / "CONTROL"))
- {
- auto maybe_scf = Paragraphs::try_load_port(filesystem, ports_dir);
- if (auto scf = maybe_scf.get())
- {
- auto port_name = scf->get()->core_paragraph->name;
- if (cache.find(port_name) == cache.end())
- {
- SourceControlFileLocation scfl{std::move(*scf), ports_dir};
- auto it = cache.emplace(port_name, std::move(scfl));
- ret.emplace_back(&it.first->second);
- }
- }
- else
- {
- vcpkg::print_error_message(maybe_scf.error());
- Checks::exit_with_message(
- VCPKG_LINE_INFO, "Error: Failed to load port from %s", ports_dir.u8string());
- }
- continue;
- }
-
- // Try loading all ports inside ports_dir
- auto found_scf = Paragraphs::load_all_ports(filesystem, ports_dir);
- for (auto&& scf : found_scf)
- {
- auto port_name = scf->core_paragraph->name;
- if (cache.find(port_name) == cache.end())
- {
- SourceControlFileLocation scfl{std::move(scf), ports_dir / port_name};
- auto it = cache.emplace(port_name, std::move(scfl));
- ret.emplace_back(&it.first->second);
- }
- }
- }
- return ret;
- }
-
std::vector<RemovePlanAction> create_remove_plan(const std::vector<PackageSpec>& specs,
const StatusParagraphs& status_db)
{
@@ -488,7 +525,8 @@ namespace vcpkg::Dependencies
auto installed_ports = get_installed_ports(status_db);
const std::unordered_set<PackageSpec> specs_as_set(specs.cbegin(), specs.cend());
- return Graphs::topological_sort(specs, RemoveAdjacencyProvider{status_db, installed_ports, specs_as_set}, {});
+ return Graphs::topological_sort(
+ std::move(specs), RemoveAdjacencyProvider{status_db, installed_ports, specs_as_set}, {});
}
std::vector<ExportPlanAction> create_export_plan(const std::vector<PackageSpec>& specs,
@@ -534,362 +572,333 @@ namespace vcpkg::Dependencies
return toposort;
}
- enum class MarkPlusResult
+ void PackageGraph::mark_user_requested(const PackageSpec& spec)
{
- FEATURE_NOT_FOUND,
- SUCCESS,
- };
+ m_graph->get(spec).request_type = RequestType::USER_REQUESTED;
+ }
- static MarkPlusResult mark_plus(const std::string& feature,
- Cluster& cluster,
- ClusterGraph& graph,
- GraphPlan& graph_plan,
- const std::unordered_set<std::string>& prevent_default_features);
-
- static void mark_minus(Cluster& cluster,
- ClusterGraph& graph,
- GraphPlan& graph_plan,
- const std::unordered_set<std::string>& prevent_default_features);
-
- static MarkPlusResult follow_plus_dependencies(const std::string& feature,
- Cluster& cluster,
- ClusterGraph& graph,
- GraphPlan& graph_plan,
- const std::unordered_set<std::string>& prevent_default_features)
+ ActionPlan create_feature_install_plan(const PortFileProvider::PortFileProvider& port_provider,
+ const CMakeVars::CMakeVarProvider& var_provider,
+ const std::vector<FullPackageSpec>& specs,
+ const StatusParagraphs& status_db,
+ const CreateInstallPlanOptions& options)
{
- if (auto p_source = cluster.source.get())
- {
- auto it_build_edges = p_source->build_edges.find(feature);
- if (it_build_edges != p_source->build_edges.end())
- {
- // mark this package for rebuilding if needed
- mark_minus(cluster, graph, graph_plan, prevent_default_features);
+ PackageGraph pgraph(port_provider, var_provider, status_db);
- graph_plan.install_graph.add_vertex({&cluster});
- cluster.to_install_features.insert(feature);
-
- if (feature != "core")
- {
- // All features implicitly depend on core
- auto res = mark_plus("core", cluster, graph, graph_plan, prevent_default_features);
-
- // Should be impossible for "core" to not exist
- Checks::check_exit(VCPKG_LINE_INFO, res == MarkPlusResult::SUCCESS);
- }
-
- if (!cluster.installed.get() && !Util::Sets::contains(prevent_default_features, cluster.spec.name()))
- {
- // Add the default features of this package if it was not previously installed and it isn't being
- // suppressed.
- auto res = mark_plus("", cluster, graph, graph_plan, prevent_default_features);
-
- Checks::check_exit(VCPKG_LINE_INFO,
- res == MarkPlusResult::SUCCESS,
- "Error: Unable to satisfy default dependencies of %s",
- cluster.spec);
- }
+ std::vector<FeatureSpec> feature_specs;
+ for (const FullPackageSpec& spec : specs)
+ {
+ const SourceControlFileLocation* scfl = port_provider.get_control_file(spec.package_spec.name()).get();
- for (auto&& depend : it_build_edges->second)
- {
- auto& depend_cluster = graph.get(depend.spec());
- auto res = mark_plus(depend.feature(), depend_cluster, graph, graph_plan, prevent_default_features);
+ Checks::check_exit(
+ VCPKG_LINE_INFO, scfl, "Error: Cannot find definition for package `%s`.", spec.package_spec.name());
- Checks::check_exit(VCPKG_LINE_INFO,
- res == MarkPlusResult::SUCCESS,
- "Error: Unable to satisfy dependency %s of %s",
- depend,
- FeatureSpec(cluster.spec, feature));
+ const std::vector<std::string> all_features =
+ Util::fmap(scfl->source_control_file->feature_paragraphs,
+ [](auto&& feature_paragraph) { return feature_paragraph->name; });
- if (&depend_cluster == &cluster) continue;
- graph_plan.install_graph.add_edge({&cluster}, {&depend_cluster});
- }
+ auto fspecs =
+ spec.to_feature_specs(scfl->source_control_file->core_paragraph->default_features, all_features);
+ feature_specs.insert(
+ feature_specs.end(), std::make_move_iterator(fspecs.begin()), std::make_move_iterator(fspecs.end()));
+ }
+ Util::sort_unique_erase(feature_specs);
- return MarkPlusResult::SUCCESS;
- }
+ for (const FeatureSpec& spec : feature_specs)
+ {
+ pgraph.mark_user_requested(spec.spec());
}
+ pgraph.install(feature_specs);
- // The feature was not available in the installed package nor the source paragraph.
- return MarkPlusResult::FEATURE_NOT_FOUND;
+ return pgraph.serialize(options);
}
- MarkPlusResult mark_plus(const std::string& feature,
- Cluster& cluster,
- ClusterGraph& graph,
- GraphPlan& graph_plan,
- const std::unordered_set<std::string>& prevent_default_features)
+ void PackageGraph::mark_for_reinstall(const PackageSpec& first_remove_spec,
+ std::vector<FeatureSpec>& out_reinstall_requirements)
{
- auto& plus = cluster.plus[feature];
- if (plus) return MarkPlusResult::SUCCESS;
- plus = true;
+ std::set<PackageSpec> removed;
+ std::vector<PackageSpec> to_remove{first_remove_spec};
- const auto p_source = cluster.source.get();
- if (p_source == nullptr)
+ while (!to_remove.empty())
{
- Checks::exit_with_message(
- VCPKG_LINE_INFO, "Error: Cannot find definition for package `%s`.", cluster.spec.name());
- }
+ PackageSpec remove_spec = std::move(to_remove.back());
+ to_remove.pop_back();
- auto&& control_file = *p_source->scfl->source_control_file.get();
- if (feature.empty())
- {
- // Add default features for this package. This is an exact reference, so ignore prevent_default_features.
- for (auto&& default_feature : control_file.core_paragraph.get()->default_features)
- {
- auto res = mark_plus(default_feature, cluster, graph, graph_plan, prevent_default_features);
- if (res != MarkPlusResult::SUCCESS)
- {
- return res;
- }
- }
+ if (!removed.insert(remove_spec).second) continue;
- // "core" is always required.
- return mark_plus("core", cluster, graph, graph_plan, prevent_default_features);
- }
+ Cluster& clust = m_graph->get(remove_spec);
+ ClusterInstalled& info = clust.m_installed.value_or_exit(VCPKG_LINE_INFO);
- if (feature == "*")
- {
- for (auto&& fpgh : control_file.feature_paragraphs)
+ if (!clust.m_install_info)
{
- auto res = mark_plus(fpgh->name, cluster, graph, graph_plan, prevent_default_features);
-
- Checks::check_exit(VCPKG_LINE_INFO,
- res == MarkPlusResult::SUCCESS,
- "Error: Internal error while installing feature %s in %s",
- fpgh->name,
- cluster.spec);
+ clust.create_install_info(out_reinstall_requirements);
}
- auto res = mark_plus("core", cluster, graph, graph_plan, prevent_default_features);
-
- Checks::check_exit(VCPKG_LINE_INFO, res == MarkPlusResult::SUCCESS);
- return MarkPlusResult::SUCCESS;
+ to_remove.insert(to_remove.end(), info.remove_edges.begin(), info.remove_edges.end());
}
-
- if (auto p_installed = cluster.installed.get())
- {
- if (p_installed->original_features.find(feature) != p_installed->original_features.end())
- {
- return MarkPlusResult::SUCCESS;
- }
- }
-
- // The feature was not previously installed. Mark the cluster
- // (aka the entire port) to be removed before re-adding it.
- mark_minus(cluster, graph, graph_plan, prevent_default_features);
-
- return follow_plus_dependencies(feature, cluster, graph, graph_plan, prevent_default_features);
}
- void mark_minus(Cluster& cluster,
- ClusterGraph& graph,
- GraphPlan& graph_plan,
- const std::unordered_set<std::string>& prevent_default_features)
+ /// The list of specs to install should already have default features expanded
+ void PackageGraph::install(Span<const FeatureSpec> specs)
{
- if (cluster.minus) return;
- cluster.minus = true;
- cluster.transient_uninstalled = true;
-
- auto p_installed = cluster.installed.get();
- auto p_source = cluster.source.get();
-
- Checks::check_exit(
- VCPKG_LINE_INFO,
- p_source,
- "Error: cannot locate new portfile for %s. Please explicitly remove this package with `vcpkg remove %s`.",
- cluster.spec,
- cluster.spec);
+ // We batch resolving qualified dependencies, because it's an invocation of CMake which
+ // takes ~150ms per call.
+ std::vector<FeatureSpec> qualified_dependencies;
+ std::vector<FeatureSpec> next_dependencies{specs.begin(), specs.end()};
- if (p_installed)
+ // Keep running while there is any chance of finding more dependencies
+ while (!next_dependencies.empty())
{
- graph_plan.remove_graph.add_vertex({&cluster});
- for (auto&& edge : p_installed->remove_edges)
+ // Keep running until the only dependencies left are qualified
+ while (!next_dependencies.empty())
{
- auto& depend_cluster = graph.get(edge);
- Checks::check_exit(VCPKG_LINE_INFO, &cluster != &depend_cluster);
- graph_plan.remove_graph.add_edge({&cluster}, {&depend_cluster});
- mark_minus(depend_cluster, graph, graph_plan, prevent_default_features);
- }
+ // Extract the top of the stack
+ FeatureSpec spec = std::move(next_dependencies.back());
+ next_dependencies.pop_back();
- // Reinstall all original features. Don't use mark_plus because it will ignore them since they are
- // "already installed".
- for (auto&& f : p_installed->original_features)
- {
- auto res = follow_plus_dependencies(f, cluster, graph, graph_plan, prevent_default_features);
- if (res != MarkPlusResult::SUCCESS)
+ // Get the cluster for the PackageSpec of the FeatureSpec we are adding to the install graph
+ Cluster& clust = m_graph->get(spec.spec());
+
+ // If this spec hasn't already had its qualified dependencies resolved
+ if (!m_var_provider.get_dep_info_vars(spec.spec()).has_value())
{
- System::print2(System::Color::warning,
- "Warning: could not reinstall feature ",
- FeatureSpec{cluster.spec, f},
- "\n");
+ // TODO: There's always the chance that we don't find the feature we're looking for (probably a
+ // malformed CONTROL file somewhere). We should probably output a better error.
+ const std::vector<Dependency>* paragraph_depends = nullptr;
+ if (spec.feature() == "core")
+ {
+ paragraph_depends = &clust.m_scfl.source_control_file->core_paragraph->depends;
+ }
+ else if (spec.feature() == "default")
+ {
+ }
+ else
+ {
+ auto maybe_paragraph = clust.m_scfl.source_control_file->find_feature(spec.feature());
+ Checks::check_exit(VCPKG_LINE_INFO,
+ maybe_paragraph.has_value(),
+ "Package %s does not have a %s feature",
+ spec.name(),
+ spec.feature());
+ paragraph_depends = &maybe_paragraph.value_or_exit(VCPKG_LINE_INFO).depends;
+ }
+
+ // And it has at least one qualified dependency
+ if (paragraph_depends && Util::any_of(*paragraph_depends,
+ [](auto&& dep) { return !dep.qualifier.empty(); }))
+ {
+ // Add it to the next batch run
+ qualified_dependencies.emplace_back(spec);
+ }
}
- }
- // Check if any default features have been added
- auto& previous_df = p_installed->ipv.core->package.default_features;
- auto&& control_file = *p_source->scfl->source_control_file.get();
- for (auto&& default_feature : control_file.core_paragraph->default_features)
- {
- if (std::find(previous_df.begin(), previous_df.end(), default_feature) == previous_df.end())
+ if (clust.m_install_info.has_value())
{
- // This is a new default feature, mark it for installation
- auto res = mark_plus(default_feature, cluster, graph, graph_plan, prevent_default_features);
- if (res != MarkPlusResult::SUCCESS)
+ clust.add_feature(spec.feature(), m_var_provider, next_dependencies);
+ }
+ else
+ {
+ if (!clust.m_installed.has_value())
+ {
+ clust.create_install_info(next_dependencies);
+ clust.add_feature(spec.feature(), m_var_provider, next_dependencies);
+ }
+ else
{
- System::print2(System::Color::warning,
- "Warning: could not install new default feature ",
- FeatureSpec{cluster.spec, default_feature},
- "\n");
+ if (spec.feature() == "default")
+ {
+ if (!clust.m_installed.get()->defaults_requested)
+ {
+ clust.m_installed.get()->defaults_requested = true;
+ if (!clust.has_defaults_installed())
+ {
+ mark_for_reinstall(spec.spec(), next_dependencies);
+ }
+ }
+ }
+ else if (!clust.has_feature_installed(spec.feature()))
+ {
+ // If install_info is not present and it is already installed, we have never added a feature
+ // which hasn't already been installed to this cluster. In this case, we need to reinstall
+ // the port if the feature isn't already present.
+ mark_for_reinstall(spec.spec(), next_dependencies);
+ clust.add_feature(spec.feature(), m_var_provider, next_dependencies);
+ }
}
}
}
+
+ if (!qualified_dependencies.empty())
+ {
+ Util::sort_unique_erase(qualified_dependencies);
+
+ // Extract the package specs we need to get dependency info from. We don't run the triplet on a per
+ // feature basis. We run it once for the whole port.
+ auto qualified_package_specs =
+ Util::fmap(qualified_dependencies, [](const FeatureSpec& fspec) { return fspec.spec(); });
+ Util::sort_unique_erase(qualified_package_specs);
+ m_var_provider.load_dep_info_vars(qualified_package_specs);
+
+ // Put all the FeatureSpecs for which we had qualified dependencies back on the dependencies stack.
+ // We need to recheck if evaluating the triplet revealed any new dependencies.
+ next_dependencies.insert(next_dependencies.end(),
+ std::make_move_iterator(qualified_dependencies.begin()),
+ std::make_move_iterator(qualified_dependencies.end()));
+ qualified_dependencies.clear();
+ }
}
}
- std::vector<AnyAction> create_feature_install_plan(const PortFileProvider& provider,
- const std::vector<FeatureSpec>& specs,
- const StatusParagraphs& status_db,
- const CreateInstallPlanOptions& options)
+ void PackageGraph::upgrade(Span<const PackageSpec> specs)
{
- std::unordered_set<std::string> prevent_default_features;
- for (auto&& spec : specs)
- {
- // When "core" is explicitly listed, default features should not be installed.
- if (spec.feature() == "core") prevent_default_features.insert(spec.name());
- }
+ std::vector<FeatureSpec> reinstall_reqs;
- PackageGraph pgraph(provider, status_db);
- for (auto&& spec : specs)
- {
- // If preventing default features, ignore the automatically generated "" references
- if (spec.feature().empty() && Util::Sets::contains(prevent_default_features, spec.name())) continue;
- pgraph.install(spec, prevent_default_features);
- }
+ for (const PackageSpec& spec : specs)
+ mark_for_reinstall(spec, reinstall_reqs);
- return pgraph.serialize(options);
+ Util::sort_unique_erase(reinstall_reqs);
+
+ install(reinstall_reqs);
}
- /// <summary>Figure out which actions are required to install features specifications in `specs`.</summary>
- /// <param name="map">Map of all source control files in the current environment.</param>
- /// <param name="specs">Feature specifications to resolve dependencies for.</param>
- /// <param name="status_db">Status of installed packages in the current environment.</param>
- std::vector<AnyAction> create_feature_install_plan(
- const std::unordered_map<std::string, SourceControlFileLocation>& map,
- const std::vector<FeatureSpec>& specs,
- const StatusParagraphs& status_db)
+ ActionPlan create_upgrade_plan(const PortFileProvider::PortFileProvider& port_provider,
+ const CMakeVars::CMakeVarProvider& var_provider,
+ const std::vector<PackageSpec>& specs,
+ const StatusParagraphs& status_db,
+ const CreateInstallPlanOptions& options)
{
- MapPortFileProvider provider(map);
- return create_feature_install_plan(provider, specs, status_db);
+ PackageGraph pgraph(port_provider, var_provider, status_db);
+
+ pgraph.upgrade(specs);
+
+ return pgraph.serialize(options);
}
- /// <param name="prevent_default_features">
- /// List of package names for which default features should not be installed instead of the core package (e.g. if
- /// the user is currently installing specific features of that package).
- /// </param>
- void PackageGraph::install(const FeatureSpec& spec,
- const std::unordered_set<std::string>& prevent_default_features) const
+ ActionPlan PackageGraph::serialize(const CreateInstallPlanOptions& options) const
{
- Cluster& spec_cluster = m_graph->get(spec.spec());
- spec_cluster.request_type = RequestType::USER_REQUESTED;
+ struct BaseEdgeProvider : Graphs::AdjacencyProvider<PackageSpec, const Cluster*>
+ {
+ BaseEdgeProvider(const ClusterGraph& parent) : m_parent(parent) {}
- auto res = mark_plus(spec.feature(), spec_cluster, *m_graph, *m_graph_plan, prevent_default_features);
+ std::string to_string(const PackageSpec& spec) const override { return spec.to_string(); }
+ const Cluster* load_vertex_data(const PackageSpec& spec) const override
+ {
+ return &m_parent.find_or_exit(spec, VCPKG_LINE_INFO);
+ }
- Checks::check_exit(VCPKG_LINE_INFO,
- res == MarkPlusResult::SUCCESS,
- "Error: `%s` is not a feature of package `%s`",
- spec.feature(),
- spec.name());
+ const ClusterGraph& m_parent;
+ };
- m_graph_plan->install_graph.add_vertex(ClusterPtr{&spec_cluster});
- }
+ struct RemoveEdgeProvider final : BaseEdgeProvider
+ {
+ using BaseEdgeProvider::BaseEdgeProvider;
- void PackageGraph::upgrade(const PackageSpec& spec) const
- {
- Cluster& spec_cluster = m_graph->get(spec);
- spec_cluster.request_type = RequestType::USER_REQUESTED;
+ std::vector<PackageSpec> adjacency_list(const Cluster* const& vertex) const override
+ {
+ auto&& set = vertex->m_installed.value_or_exit(VCPKG_LINE_INFO).remove_edges;
+ return {set.begin(), set.end()};
+ }
+ } removeedgeprovider(*m_graph);
- mark_minus(spec_cluster, *m_graph, *m_graph_plan, {});
- }
+ struct InstallEdgeProvider final : BaseEdgeProvider
+ {
+ using BaseEdgeProvider::BaseEdgeProvider;
- std::vector<AnyAction> PackageGraph::serialize(const CreateInstallPlanOptions& options) const
- {
- auto remove_vertex_list = m_graph_plan->remove_graph.vertex_list();
- auto remove_toposort =
- Graphs::topological_sort(remove_vertex_list, m_graph_plan->remove_graph, options.randomizer);
+ std::vector<PackageSpec> adjacency_list(const Cluster* const& vertex) const override
+ {
+ if (!vertex->m_install_info.has_value()) return {};
- auto insert_vertex_list = m_graph_plan->install_graph.vertex_list();
- auto insert_toposort =
- Graphs::topological_sort(insert_vertex_list, m_graph_plan->install_graph, options.randomizer);
+ auto& info = vertex->m_install_info.value_or_exit(VCPKG_LINE_INFO);
+ std::vector<PackageSpec> deps;
+ for (auto&& kv : info.build_edges)
+ for (auto&& e : kv.second)
+ {
+ auto spec = e.spec();
+ if (spec != vertex->m_spec) deps.push_back(std::move(spec));
+ }
+ Util::sort_unique_erase(deps);
+ return deps;
+ }
+ } installedgeprovider(*m_graph);
- std::vector<AnyAction> plan;
+ std::vector<PackageSpec> removed_vertices;
+ std::vector<PackageSpec> installed_vertices;
+ for (auto&& kv : *m_graph)
+ {
+ if (kv.second.m_install_info.has_value() && kv.second.m_installed.has_value())
+ {
+ removed_vertices.push_back(kv.first);
+ }
+ if (kv.second.m_install_info.has_value() || kv.second.request_type == RequestType::USER_REQUESTED)
+ {
+ installed_vertices.push_back(kv.first);
+ }
+ }
+ auto remove_toposort = Graphs::topological_sort(removed_vertices, removeedgeprovider, options.randomizer);
+ auto insert_toposort = Graphs::topological_sort(installed_vertices, installedgeprovider, options.randomizer);
+
+ ActionPlan plan;
for (auto&& p_cluster : remove_toposort)
{
- plan.emplace_back(RemovePlanAction{
- std::move(p_cluster->spec),
- RemovePlanType::REMOVE,
- p_cluster->request_type,
- });
+ plan.remove_actions.emplace_back(p_cluster->m_spec, RemovePlanType::REMOVE, p_cluster->request_type);
}
for (auto&& p_cluster : insert_toposort)
{
- if (p_cluster->transient_uninstalled)
+ // Every cluster that has an install_info needs to be built
+ // If a cluster only has an installed object and is marked as user requested we should still report it.
+ if (auto info_ptr = p_cluster->m_install_info.get())
{
- // If it will be transiently uninstalled, we need to issue a full installation command
- auto* pscfl = p_cluster->source.value_or_exit(VCPKG_LINE_INFO).scfl;
- Checks::check_exit(
- VCPKG_LINE_INFO, pscfl != nullptr, "Error: Expected a SourceControlFileLocation to exist");
- auto&& scfl = *pscfl;
-
- auto dep_specs = Util::fmap(m_graph_plan->install_graph.adjacency_list(p_cluster),
- [](ClusterPtr const& p) { return p->spec; });
- Util::sort_unique_erase(dep_specs);
-
- plan.emplace_back(InstallPlanAction{
- p_cluster->spec,
- scfl,
- p_cluster->to_install_features,
- p_cluster->request_type,
- std::move(dep_specs),
- });
+ auto&& scfl = p_cluster->m_scfl;
+
+ std::unordered_map<std::string, std::vector<FeatureSpec>> computed_edges;
+ for (auto&& kv : info_ptr->build_edges)
+ {
+ std::set<FeatureSpec> fspecs;
+ for (auto&& fspec : kv.second)
+ {
+ if (fspec.feature() != "default")
+ {
+ fspecs.insert(fspec);
+ continue;
+ }
+ auto&& dep_clust = m_graph->get(fspec.spec());
+ const auto& default_features =
+ [&]{
+ if (dep_clust.m_install_info.has_value()) return dep_clust.m_scfl.source_control_file->core_paragraph->default_features;
+ if (auto p = dep_clust.m_installed.get()) return p->ipv.core->package.default_features;
+ Checks::unreachable(VCPKG_LINE_INFO);
+ }();
+ for (auto&& default_feature : default_features)
+ fspecs.emplace(fspec.spec(), default_feature);
+ }
+ computed_edges[kv.first].assign(fspecs.begin(), fspecs.end());
+ }
+
+ plan.install_actions.emplace_back(
+ p_cluster->m_spec, scfl, p_cluster->request_type, std::move(computed_edges));
}
- else
+ else if (p_cluster->request_type == RequestType::USER_REQUESTED && p_cluster->m_installed.has_value())
{
- // 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;
- auto&& installed = p_cluster->installed.value_or_exit(VCPKG_LINE_INFO);
- plan.emplace_back(InstallPlanAction{
- InstalledPackageView{installed.ipv},
- installed.original_features,
- p_cluster->request_type,
- });
+ auto&& installed = p_cluster->m_installed.value_or_exit(VCPKG_LINE_INFO);
+ plan.already_installed.emplace_back(Util::copy(installed.ipv), p_cluster->request_type);
}
}
return plan;
}
- static std::unique_ptr<ClusterGraph> create_feature_install_graph(const PortFileProvider& map,
- const StatusParagraphs& status_db)
+ static std::unique_ptr<ClusterGraph> create_feature_install_graph(
+ const PortFileProvider::PortFileProvider& port_provider, const StatusParagraphs& status_db)
{
- std::unique_ptr<ClusterGraph> graph = std::make_unique<ClusterGraph>(map);
+ std::unique_ptr<ClusterGraph> graph = std::make_unique<ClusterGraph>(port_provider);
auto installed_ports = get_installed_ports(status_db);
for (auto&& ipv : installed_ports)
{
- Cluster& cluster = graph->get(ipv.spec());
-
- cluster.transient_uninstalled = false;
-
- cluster.installed = [](const InstalledPackageView& ipv) -> ClusterInstalled {
- ClusterInstalled ret;
- ret.ipv = ipv;
- ret.original_features.emplace("core");
- for (auto&& feature : ipv.features)
- ret.original_features.emplace(feature->package.feature);
- return ret;
- }(ipv);
+ graph->get(ipv);
}
// Populate the graph with "remove edges", which are the reverse of the Build-Depends edges.
@@ -899,7 +908,7 @@ namespace vcpkg::Dependencies
for (auto&& dep : deps)
{
- auto p_installed = graph->get(dep).installed.get();
+ auto p_installed = graph->get(dep).m_installed.get();
Checks::check_exit(VCPKG_LINE_INFO,
p_installed,
"Error: database corrupted. Package %s is installed but dependency %s is not.",
@@ -911,16 +920,16 @@ namespace vcpkg::Dependencies
return graph;
}
- PackageGraph::PackageGraph(const PortFileProvider& provider, const StatusParagraphs& status_db)
- : m_graph_plan(std::make_unique<GraphPlan>()), m_graph(create_feature_install_graph(provider, status_db))
+ PackageGraph::PackageGraph(const PortFileProvider::PortFileProvider& port_provider,
+ const CMakeVars::CMakeVarProvider& var_provider,
+ const StatusParagraphs& status_db)
+ : m_var_provider(var_provider), m_graph(create_feature_install_graph(port_provider, status_db))
{
}
PackageGraph::~PackageGraph() = default;
- void print_plan(const std::vector<AnyAction>& action_plan,
- const bool is_recursive,
- const fs::path& default_ports_dir)
+ void print_plan(const ActionPlan& action_plan, const bool is_recursive, const fs::path& default_ports_dir)
{
std::vector<const RemovePlanAction*> remove_plans;
std::vector<const InstallPlanAction*> rebuilt_plans;
@@ -929,44 +938,38 @@ namespace vcpkg::Dependencies
std::vector<const InstallPlanAction*> already_installed_plans;
std::vector<const InstallPlanAction*> excluded;
- const bool has_non_user_requested_packages = Util::find_if(action_plan, [](const AnyAction& package) -> bool {
- if (auto iplan = package.install_action.get())
- return iplan->request_type != RequestType::USER_REQUESTED;
- else
- return false;
- }) != action_plan.cend();
+ const bool has_non_user_requested_packages =
+ Util::find_if(action_plan.install_actions, [](const InstallPlanAction& action) -> bool {
+ return action.request_type != RequestType::USER_REQUESTED;
+ }) != action_plan.install_actions.cend();
- for (auto&& action : action_plan)
+ for (auto&& remove_action : action_plan.remove_actions)
+ {
+ remove_plans.emplace_back(&remove_action);
+ }
+ for (auto&& install_action : action_plan.install_actions)
{
- if (auto install_action = action.install_action.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())
{
- // 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
- {
- switch (install_action->plan_type)
- {
- case InstallPlanType::ALREADY_INSTALLED:
- if (install_action->request_type == RequestType::USER_REQUESTED)
- already_installed_plans.emplace_back(install_action);
- break;
- case InstallPlanType::BUILD_AND_INSTALL: new_plans.emplace_back(install_action); break;
- case InstallPlanType::EXCLUDED: excluded.emplace_back(install_action); break;
- default: Checks::unreachable(VCPKG_LINE_INFO);
- }
- }
+ rebuilt_plans.push_back(&install_action);
}
- else if (auto remove_action = action.remove_action.get())
+ else
{
- remove_plans.emplace_back(remove_action);
+ if (install_action.plan_type == InstallPlanType::EXCLUDED)
+ excluded.push_back(&install_action);
+ else
+ new_plans.push_back(&install_action);
}
}
+ for (auto&& action : action_plan.already_installed)
+ {
+ if (action.request_type == RequestType::USER_REQUESTED) already_installed_plans.emplace_back(&action);
+ }
+ already_installed_plans = Util::fmap(action_plan.already_installed, [](auto&& action) { return &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);
diff --git a/toolsrc/src/vcpkg/export.cpp b/toolsrc/src/vcpkg/export.cpp
index 34f9053ea..0094b712c 100644
--- a/toolsrc/src/vcpkg/export.cpp
+++ b/toolsrc/src/vcpkg/export.cpp
@@ -2,9 +2,9 @@
#include <vcpkg/commands.h>
#include <vcpkg/dependencies.h>
+#include <vcpkg/export.chocolatey.h>
#include <vcpkg/export.h>
#include <vcpkg/export.ifw.h>
-#include <vcpkg/export.chocolatey.h>
#include <vcpkg/help.h>
#include <vcpkg/input.h>
#include <vcpkg/install.h>
@@ -307,8 +307,10 @@ namespace vcpkg::Export
{OPTION_IFW_REPOSITORY_DIR_PATH, "Specify the directory path for the exported repository"},
{OPTION_IFW_CONFIG_FILE_PATH, "Specify the temporary file path for the installer configuration"},
{OPTION_IFW_INSTALLER_FILE_PATH, "Specify the file path for the exported installer"},
- {OPTION_CHOCOLATEY_MAINTAINER, "Specify the maintainer for the exported Chocolatey package (experimental feature)"},
- {OPTION_CHOCOLATEY_VERSION_SUFFIX, "Specify the version suffix to add for the exported Chocolatey package (experimental feature)"},
+ {OPTION_CHOCOLATEY_MAINTAINER,
+ "Specify the maintainer for the exported Chocolatey package (experimental feature)"},
+ {OPTION_CHOCOLATEY_VERSION_SUFFIX,
+ "Specify the version suffix to add for the exported Chocolatey package (experimental feature)"},
}};
const CommandStructure COMMAND_STRUCTURE = {
@@ -521,7 +523,7 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console
const StatusParagraphs status_db = database_load_check(paths);
// Load ports from ports dirs
- Dependencies::PathsPortFileProvider provider(paths, args.overlay_ports.get());
+ PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports.get());
std::vector<ExportPlanAction> export_plan = Dependencies::create_export_plan(opts.specs, status_db);
Checks::check_exit(VCPKG_LINE_INFO, !export_plan.empty(), "Export plan cannot be empty");
diff --git a/toolsrc/src/vcpkg/help.cpp b/toolsrc/src/vcpkg/help.cpp
index a4908e02e..78bc43539 100644
--- a/toolsrc/src/vcpkg/help.cpp
+++ b/toolsrc/src/vcpkg/help.cpp
@@ -74,10 +74,36 @@ namespace vcpkg::Help
void help_topic_valid_triplet(const VcpkgPaths& paths)
{
- System::print2("Available architecture triplets:\n");
- for (auto&& triplet : paths.get_available_triplets())
+ std::map<std::string, std::vector<const VcpkgPaths::TripletFile*>> triplets_per_location;
+ vcpkg::Util::group_by(paths.get_available_triplets(),
+ &triplets_per_location,
+ [](const VcpkgPaths::TripletFile& triplet_file) -> std::string {
+ return triplet_file.location.u8string();
+ });
+
+ System::print2("Available architecture triplets\n");
+
+ System::print2("VCPKG built-in triplets:\n");
+ for (auto* triplet : triplets_per_location[paths.triplets.u8string()])
+ {
+ System::print2(" ", triplet->name, '\n');
+ }
+ triplets_per_location.erase(paths.triplets.u8string());
+
+ System::print2("\nVCPKG community triplets:\n");
+ for (auto* triplet : triplets_per_location[paths.community_triplets.u8string()])
+ {
+ System::print2(" ", triplet->name, '\n');
+ }
+ triplets_per_location.erase(paths.community_triplets.u8string());
+
+ for (auto&& kv_pair : triplets_per_location)
{
- System::print2(" ", triplet, '\n');
+ System::print2("\nOverlay triplets from ", kv_pair.first, ":\n");
+ for (auto* triplet : kv_pair.second)
+ {
+ System::print2(" ", triplet->name, '\n');
+ }
}
}
diff --git a/toolsrc/src/vcpkg/install.cpp b/toolsrc/src/vcpkg/install.cpp
index a082f1b95..0af9a33a1 100644
--- a/toolsrc/src/vcpkg/install.cpp
+++ b/toolsrc/src/vcpkg/install.cpp
@@ -5,6 +5,7 @@
#include <vcpkg/base/system.print.h>
#include <vcpkg/base/util.h>
#include <vcpkg/build.h>
+#include <vcpkg/cmakevars.h>
#include <vcpkg/commands.h>
#include <vcpkg/dependencies.h>
#include <vcpkg/globalstate.h>
@@ -18,6 +19,7 @@
namespace vcpkg::Install
{
+ using namespace vcpkg;
using namespace Dependencies;
using file_pack = std::pair<std::string, std::string>;
@@ -295,8 +297,9 @@ namespace vcpkg::Install
using Build::ExtendedBuildResult;
ExtendedBuildResult perform_install_plan_action(const VcpkgPaths& paths,
- const InstallPlanAction& action,
- StatusParagraphs& status_db)
+ InstallPlanAction& action,
+ StatusParagraphs& status_db,
+ const CMakeVars::CMakeVarProvider& var_provider)
{
const InstallPlanType& plan_type = action.plan_type;
const std::string display_name = action.spec.to_string();
@@ -338,8 +341,13 @@ namespace vcpkg::Install
auto result = [&]() -> Build::ExtendedBuildResult {
const auto& scfl = action.source_control_file_location.value_or_exit(VCPKG_LINE_INFO);
- const Build::BuildPackageConfig build_config{
- scfl, action.spec.triplet(), action.build_options, action.feature_list};
+ const Build::BuildPackageConfig build_config{scfl,
+ action.spec.triplet(),
+ action.build_options,
+ var_provider,
+ std::move(action.feature_dependencies),
+ std::move(action.package_dependencies),
+ std::move(action.feature_list)};
return Build::build_package(paths, build_config, status_db);
}();
@@ -422,53 +430,60 @@ namespace vcpkg::Install
}
}
- InstallSummary perform(const std::vector<AnyAction>& action_plan,
+ InstallSummary perform(ActionPlan& action_plan,
const KeepGoing keep_going,
const VcpkgPaths& paths,
- StatusParagraphs& status_db)
+ StatusParagraphs& status_db,
+ const CMakeVars::CMakeVarProvider& var_provider)
{
std::vector<SpecSummary> results;
const auto timer = Chrono::ElapsedTimer::create_started();
size_t counter = 0;
- const size_t package_count = action_plan.size();
+ const size_t package_count = action_plan.remove_actions.size() + action_plan.install_actions.size();
- for (const auto& action : action_plan)
- {
+ auto with_tracking = [&](const PackageSpec& spec, auto f) {
const auto build_timer = Chrono::ElapsedTimer::create_started();
counter++;
- const PackageSpec& spec = action.spec();
const std::string display_name = spec.to_string();
System::printf("Starting package %zd/%zd: %s\n", counter, package_count, display_name);
- results.emplace_back(spec, &action);
+ results.emplace_back(spec, nullptr);
- if (const auto install_action = action.install_action.get())
- {
- auto result = perform_install_plan_action(paths, *install_action, status_db);
+ f();
+
+ results.back().timing = build_timer.elapsed();
+ System::printf("Elapsed time for package %s: %s\n", display_name, results.back().timing);
+ };
+
+ for (auto&& action : action_plan.remove_actions)
+ {
+ with_tracking(action.spec,
+ [&]() { Remove::perform_remove_plan_action(paths, action, Remove::Purge::YES, &status_db); });
+ }
+
+ for (auto&& action : action_plan.already_installed)
+ {
+ results.emplace_back(action.spec, &action);
+ results.back().build_result = perform_install_plan_action(paths, action, status_db, var_provider);
+ }
+
+ for (auto&& action : action_plan.install_actions)
+ {
+ with_tracking(action.spec, [&]() {
+ auto result = perform_install_plan_action(paths, action, status_db, var_provider);
if (result.code != BuildResult::SUCCEEDED && keep_going == KeepGoing::NO)
{
- System::print2(Build::create_user_troubleshooting_message(install_action->spec), '\n');
+ System::print2(Build::create_user_troubleshooting_message(action.spec), '\n');
Checks::exit_fail(VCPKG_LINE_INFO);
}
+ results.back().action = &action;
results.back().build_result = std::move(result);
- }
- else if (const auto remove_action = action.remove_action.get())
- {
- Remove::perform_remove_plan_action(paths, *remove_action, Remove::Purge::YES, &status_db);
- }
- else
- {
- Checks::unreachable(VCPKG_LINE_INFO);
- }
-
- results.back().timing = build_timer.elapsed();
- System::printf("Elapsed time for package %s: %s\n", display_name, results.back().timing);
+ });
}
-
return InstallSummary{std::move(results), timer.to_string()};
}
@@ -514,7 +529,8 @@ namespace vcpkg::Install
static void print_cmake_information(const BinaryParagraph& bpgh, const VcpkgPaths& paths)
{
- static const std::regex cmake_library_regex(R"(\badd_library\(([^\s\)]+)\s)", std::regex_constants::ECMAScript);
+ static const std::regex cmake_library_regex(R"(\badd_library\(([^\$\s\)]+)\s)",
+ std::regex_constants::ECMAScript);
auto& fs = paths.get_filesystem();
@@ -651,6 +667,7 @@ namespace vcpkg::Install
auto& fs = paths.get_filesystem();
// create the plan
+ System::print2("Computing installation plan...\n");
StatusParagraphs status_db = database_load_check(paths);
Build::DownloadTool download_tool = Build::DownloadTool::BUILT_IN;
@@ -669,33 +686,39 @@ namespace vcpkg::Install
};
//// Load ports from ports dirs
- PathsPortFileProvider provider(paths, args.overlay_ports.get());
+ PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports.get());
+ CMakeVars::TripletCMakeVarProvider var_provider(paths);
// Note: action_plan will hold raw pointers to SourceControlFileLocations from this map
- std::vector<AnyAction> action_plan =
- create_feature_install_plan(provider, FullPackageSpec::to_feature_specs(specs), status_db);
+ auto action_plan = Dependencies::create_feature_install_plan(provider, var_provider, specs, status_db);
- for (auto&& action : action_plan)
+ std::vector<FullPackageSpec> install_package_specs;
+ for (auto&& action : action_plan.install_actions)
{
- if (auto p_install = action.install_action.get())
- {
- p_install->build_options = install_plan_options;
- if (p_install->request_type != RequestType::USER_REQUESTED)
- p_install->build_options.use_head_version = Build::UseHeadVersion::NO;
- }
+ action.build_options = install_plan_options;
+ if (action.request_type != RequestType::USER_REQUESTED)
+ action.build_options.use_head_version = Build::UseHeadVersion::NO;
+
+ install_package_specs.emplace_back(FullPackageSpec{action.spec, action.feature_list});
}
+ var_provider.load_tag_vars(install_package_specs, provider);
+
// 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_action.get())
- return Hash::get_string_hash(iaction->spec.to_string(), Hash::Algorithm::Sha256);
- else if (auto raction = action.remove_action.get())
- return "R$" + Hash::get_string_hash(raction->spec.to_string(), Hash::Algorithm::Sha256);
- Checks::unreachable(VCPKG_LINE_INFO);
- });
+ std::string specs_string;
+ for (auto&& remove_action : action_plan.remove_actions)
+ {
+ if (!specs_string.empty()) specs_string += ",";
+ specs_string += "R$" + Hash::get_string_hash(remove_action.spec.to_string(), Hash::Algorithm::Sha256);
+ }
+ for (auto&& install_action : action_plan.install_actions)
+ {
+ if (!specs_string.empty()) specs_string += ",";
+ specs_string += Hash::get_string_hash(install_action.spec.to_string(), Hash::Algorithm::Sha256);
+ }
Metrics::g_metrics.lock()->track_property("installplan_1", specs_string);
@@ -706,7 +729,7 @@ namespace vcpkg::Install
Checks::exit_success(VCPKG_LINE_INFO);
}
- const InstallSummary summary = perform(action_plan, keep_going, paths, status_db);
+ const InstallSummary summary = perform(action_plan, keep_going, paths, status_db, var_provider);
System::print2("\nTotal elapsed time: ", summary.total_elapsed_time, "\n\n");
@@ -729,19 +752,16 @@ namespace vcpkg::Install
for (auto&& result : summary.results)
{
if (!result.action) continue;
- if (auto p_install_action = result.action->install_action.get())
- {
- if (p_install_action->request_type != RequestType::USER_REQUESTED) continue;
- auto bpgh = result.get_binary_paragraph();
- if (!bpgh) continue;
- print_cmake_information(*bpgh, paths);
- }
+ if (result.action->request_type != RequestType::USER_REQUESTED) continue;
+ auto bpgh = result.get_binary_paragraph();
+ if (!bpgh) continue;
+ print_cmake_information(*bpgh, paths);
}
Checks::exit_success(VCPKG_LINE_INFO);
}
- SpecSummary::SpecSummary(const PackageSpec& spec, const Dependencies::AnyAction* action)
+ SpecSummary::SpecSummary(const PackageSpec& spec, const Dependencies::InstallPlanAction* action)
: spec(spec), build_result{BuildResult::NULLVALUE, nullptr}, action(action)
{
}
@@ -750,12 +770,9 @@ namespace vcpkg::Install
{
if (build_result.binary_control_file) return &build_result.binary_control_file->core_paragraph;
if (action)
- if (auto p_install_plan = action->install_action.get())
+ if (auto p_status = action->installed_package.get())
{
- if (auto p_status = p_install_plan->installed_package.get())
- {
- return &p_status->core->package;
- }
+ return &p_status->core->package;
}
return nullptr;
}
diff --git a/toolsrc/src/vcpkg/logicexpression.cpp b/toolsrc/src/vcpkg/logicexpression.cpp
index ccb8b00c4..2f646e80a 100644
--- a/toolsrc/src/vcpkg/logicexpression.cpp
+++ b/toolsrc/src/vcpkg/logicexpression.cpp
@@ -1,7 +1,6 @@
-
#include "pch.h"
-#include <vcpkg/base/checks.h>
+#include <vcpkg/base/strings.h>
#include <vcpkg/base/system.print.h>
#include <vcpkg/logicexpression.h>
@@ -18,22 +17,37 @@ namespace vcpkg
const std::string line;
const std::string message;
- void print_error() const
+ std::string format_error() const
{
- System::print2(System::Color::error,
- "Error: ",
- message,
- "\n"
- " on expression: \"",
- line,
- "\"\n",
- " ",
- std::string(column, ' '),
- "^\n");
- Checks::exit_fail(VCPKG_LINE_INFO);
+ return Strings::concat("Error: ",
+ message,
+ "\n"
+ " on expression: \"",
+ line,
+ "\"\n",
+ " ",
+ std::string(column, ' '),
+ "^\n");
}
};
+ enum class Identifier
+ {
+ invalid, // not a recognized identifier
+ x64,
+ x86,
+ arm,
+ arm64,
+
+ windows,
+ linux,
+ osx,
+ uwp,
+ android,
+
+ static_link,
+ };
+
// logic expression supports the following :
// primary-expression:
// ( logic-expression )
@@ -53,12 +67,33 @@ namespace vcpkg
class ExpressionParser
{
public:
- ExpressionParser(const std::string& str, const std::string& evaluation_context)
+ ExpressionParser(const std::string& str, const ExpressionContext& context)
: raw_text(str)
- , evaluation_context(evaluation_context)
+ , evaluation_context(context)
, current_iter(raw_text.begin())
, current_char(get_current_char())
{
+ {
+ auto override_vars = evaluation_context.cmake_context.find("VCPKG_DEP_INFO_OVERRIDE_VARS");
+ if (override_vars != evaluation_context.cmake_context.end())
+ {
+ auto cmake_list = Strings::split(override_vars->second, ";");
+ for (auto& override_id : cmake_list)
+ {
+ if (!override_id.empty())
+ {
+ if (override_id[0] == '!')
+ {
+ context_override.insert({override_id.substr(1), false});
+ }
+ else
+ {
+ context_override.insert({override_id, true});
+ }
+ }
+ }
+ }
+ }
skip_whitespace();
final_result = logic_expression();
@@ -67,21 +102,18 @@ namespace vcpkg
{
add_error("Invalid logic expression");
}
-
- if (err)
- {
- err->print_error();
- final_result = false;
- }
}
bool get_result() const { return final_result; }
- bool has_error() const { return err == nullptr; }
+ const ParseError* get_error() const { return err.get(); }
private:
const std::string& raw_text;
- const std::string& evaluation_context;
+
+ const ExpressionContext& evaluation_context;
+ std::map<std::string, bool> context_override;
+
std::string::const_iterator current_iter;
char current_char;
@@ -143,9 +175,106 @@ namespace vcpkg
return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || (ch == '-');
}
- bool evaluate_identifier(const std::string name) const
+ // Legacy evaluation only searches for substrings. Use this only for diagnostic purposes.
+ bool evaluate_identifier_legacy(const std::string name) const
{
- return evaluation_context.find(name) != std::string::npos;
+ return evaluation_context.legacy_context.find(name) != std::string::npos;
+ }
+
+ static Identifier string2identifier(const std::string& name)
+ {
+ static const std::map<std::string, Identifier> id_map = {
+ {"x64", Identifier::x64},
+ {"x86", Identifier::x86},
+ {"arm", Identifier::arm},
+ {"arm64", Identifier::arm64},
+ {"windows", Identifier::windows},
+ {"linux", Identifier::linux},
+ {"osx", Identifier::osx},
+ {"uwp", Identifier::uwp},
+ {"android", Identifier::android},
+ {"static", Identifier::static_link},
+ };
+
+ auto id_pair = id_map.find(name);
+
+ if (id_pair == id_map.end())
+ {
+ return Identifier::invalid;
+ }
+
+ return id_pair->second;
+ }
+
+ bool true_if_exists_and_equal(const std::string& variable_name, const std::string& value)
+ {
+ auto iter = evaluation_context.cmake_context.find(variable_name);
+ if (iter == evaluation_context.cmake_context.end())
+ {
+ return false;
+ }
+ return iter->second == value;
+ }
+
+ // If an identifier is on the explicit override list, return the override value
+ // Otherwise fall back to the built in logic to evaluate
+ // All unrecognized identifiers are an error
+ bool evaluate_identifier_cmake(const std::string name, int column)
+ {
+ auto id = string2identifier(name);
+
+ switch (id)
+ {
+ case Identifier::invalid:
+ // Point out in the diagnostic that they should add to the override list because that is what
+ // most users should do, however it is also valid to update the built in identifiers to recognize
+ // the name.
+ add_error("Unrecognized identifer name. Add to override list in triplet file.", column);
+ break;
+
+ case Identifier::x64: return true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "x64");
+ case Identifier::x86: return true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "x86");
+ case Identifier::arm:
+ // For backwards compatability arm is also true for arm64.
+ // This is because it previously was only checking for a substring.
+ return true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "arm") ||
+ true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "arm64");
+ case Identifier::arm64: return true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "arm64");
+ case Identifier::windows: return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "") || true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "WindowsStore");
+ case Identifier::linux: return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "Linux");
+ case Identifier::osx: return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "Darwin");
+ case Identifier::uwp: return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "WindowsStore");
+ case Identifier::android: return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "Android");
+ case Identifier::static_link: return true_if_exists_and_equal("VCPKG_LIBRARY_LINKAGE", "static");
+ }
+
+ return evaluation_context.legacy_context.find(name) != std::string::npos;
+ }
+
+ bool evaluate_identifier(const std::string name, int column)
+ {
+ if (!context_override.empty())
+ {
+ auto override_id = context_override.find(name);
+ if (override_id != context_override.end())
+ {
+ return override_id->second;
+ }
+ // Fall through to use the cmake logic if the id does not have an override
+ }
+
+ bool legacy = evaluate_identifier_legacy(name);
+ bool cmake = evaluate_identifier_cmake(name, column);
+ if (legacy != cmake)
+ {
+ // Legacy evaluation only used the name of the triplet, now we use the actual
+ // cmake variables. This has the potential to break custom triplets.
+ // For now just print a message, this will need to change once we start introducing
+ // new variables that did not exist previously (such as host-*)
+ System::print2(
+ "Warning: Identifier logic evaluation does not match legacy evaluation:\n ", name, '\n');
+ }
+ return cmake;
}
// identifier:
@@ -154,6 +283,7 @@ namespace vcpkg
{
auto curr = current();
std::string name;
+ auto starting_column = current_column();
for (curr = current(); is_alphanum(curr); curr = next())
{
@@ -166,7 +296,7 @@ namespace vcpkg
return false;
}
- bool result = evaluate_identifier(name);
+ bool result = evaluate_identifier(name, starting_column);
skip_whitespace();
return result;
}
@@ -194,6 +324,7 @@ namespace vcpkg
while (next() == oper)
{
};
+ skip_whitespace();
seed = operation(not_expression(), seed);
} while (current() == oper);
@@ -242,7 +373,7 @@ namespace vcpkg
bool result = logic_expression();
if (current() != ')')
{
- add_error("Error: missing closing )");
+ add_error("missing closing )");
return result;
}
next_skip_whitespace();
@@ -253,9 +384,14 @@ namespace vcpkg
}
};
- bool evaluate_expression(const std::string& expression, const std::string& evaluation_context)
+ ExpectedT<bool, std::string> evaluate_expression(const std::string& expression, const ExpressionContext& context)
{
- ExpressionParser parser(expression, evaluation_context);
+ ExpressionParser parser(expression, context);
+
+ if (auto err = parser.get_error())
+ {
+ return err->format_error();
+ }
return parser.get_result();
}
diff --git a/toolsrc/src/vcpkg/packagespec.cpp b/toolsrc/src/vcpkg/packagespec.cpp
index 52edf4b6f..ea25f57e1 100644
--- a/toolsrc/src/vcpkg/packagespec.cpp
+++ b/toolsrc/src/vcpkg/packagespec.cpp
@@ -46,7 +46,7 @@ namespace vcpkg
for (auto&& feature : spec->features)
f_specs.push_back(FeatureSpec{pspec, feature});
- if (spec->features.empty()) f_specs.push_back(FeatureSpec{pspec, ""});
+ if (spec->features.empty()) f_specs.push_back(FeatureSpec{pspec, "core"});
}
else
{
@@ -59,16 +59,44 @@ namespace vcpkg
return f_specs;
}
- std::vector<FeatureSpec> FullPackageSpec::to_feature_specs(const std::vector<FullPackageSpec>& specs)
+ std::vector<FeatureSpec> FullPackageSpec::to_feature_specs(const std::vector<std::string>& default_features,
+ const std::vector<std::string>& all_features) const
{
- std::vector<FeatureSpec> ret;
- for (auto&& spec : specs)
+ std::vector<FeatureSpec> feature_specs;
+
+ if (Util::find(features, "*") != features.end())
{
- ret.emplace_back(spec.package_spec, "");
- for (auto&& feature : spec.features)
- ret.emplace_back(spec.package_spec, feature);
+ feature_specs.emplace_back(package_spec, "core");
+ for (const std::string& feature : all_features)
+ {
+ feature_specs.emplace_back(package_spec, feature);
+ }
}
- return ret;
+ else
+ {
+ bool core = false;
+ for (const std::string& feature : features)
+ {
+ feature_specs.emplace_back(package_spec, feature);
+
+ if (!core)
+ {
+ core = feature == "core";
+ }
+ }
+
+ if (!core)
+ {
+ feature_specs.emplace_back(package_spec, "core");
+
+ for (const std::string& def : default_features)
+ {
+ feature_specs.emplace_back(package_spec, def);
+ }
+ }
+ }
+
+ return feature_specs;
}
ExpectedT<FullPackageSpec, PackageSpecParseResult> FullPackageSpec::from_string(const std::string& spec_as_string,
diff --git a/toolsrc/src/vcpkg/paragraphs.cpp b/toolsrc/src/vcpkg/paragraphs.cpp
index 9cb7caff1..797d681cd 100644
--- a/toolsrc/src/vcpkg/paragraphs.cpp
+++ b/toolsrc/src/vcpkg/paragraphs.cpp
@@ -205,10 +205,11 @@ namespace vcpkg::Paragraphs
ParseExpected<SourceControlFile> try_load_port(const Files::Filesystem& fs, const fs::path& path)
{
- Expected<std::vector<RawParagraph>> pghs = get_paragraphs(fs, path / "CONTROL");
+ const auto path_to_control = path / "CONTROL";
+ Expected<std::vector<RawParagraph>> pghs = get_paragraphs(fs, path_to_control);
if (auto vector_pghs = pghs.get())
{
- return SourceControlFile::parse_control_file(std::move(*vector_pghs));
+ return SourceControlFile::parse_control_file(path_to_control, std::move(*vector_pghs));
}
auto error_info = std::make_unique<ParseControlErrorInfo>();
error_info->name = path.filename().generic_u8string();
diff --git a/toolsrc/src/vcpkg/portfileprovider.cpp b/toolsrc/src/vcpkg/portfileprovider.cpp
new file mode 100644
index 000000000..6540f2949
--- /dev/null
+++ b/toolsrc/src/vcpkg/portfileprovider.cpp
@@ -0,0 +1,151 @@
+#include <pch.h>
+
+#include <vcpkg/paragraphs.h>
+#include <vcpkg/portfileprovider.h>
+#include <vcpkg/sourceparagraph.h>
+
+namespace vcpkg::PortFileProvider
+{
+ MapPortFileProvider::MapPortFileProvider(const std::unordered_map<std::string, SourceControlFileLocation>& map)
+ : ports(map)
+ {
+ }
+
+ Optional<const SourceControlFileLocation&> MapPortFileProvider::get_control_file(const std::string& spec) const
+ {
+ auto scf = ports.find(spec);
+ if (scf == ports.end()) return nullopt;
+ return scf->second;
+ }
+
+ std::vector<const SourceControlFileLocation*> MapPortFileProvider::load_all_control_files() const
+ {
+ return Util::fmap(ports, [](auto&& kvpair) -> const SourceControlFileLocation* { return &kvpair.second; });
+ }
+
+ PathsPortFileProvider::PathsPortFileProvider(const vcpkg::VcpkgPaths& paths,
+ const std::vector<std::string>* ports_dirs_paths)
+ : filesystem(paths.get_filesystem())
+ {
+ auto& fs = Files::get_real_filesystem();
+ if (ports_dirs_paths)
+ {
+ for (auto&& overlay_path : *ports_dirs_paths)
+ {
+ if (!overlay_path.empty())
+ {
+ auto overlay = fs::stdfs::canonical(fs::u8path(overlay_path));
+
+ Checks::check_exit(VCPKG_LINE_INFO,
+ filesystem.exists(overlay),
+ "Error: Path \"%s\" does not exist",
+ overlay.string());
+
+ Checks::check_exit(VCPKG_LINE_INFO,
+ fs::is_directory(fs.status(VCPKG_LINE_INFO, overlay)),
+ "Error: Path \"%s\" must be a directory",
+ overlay.string());
+
+ ports_dirs.emplace_back(overlay);
+ }
+ }
+ }
+ ports_dirs.emplace_back(paths.ports);
+ }
+
+ Optional<const SourceControlFileLocation&> PathsPortFileProvider::get_control_file(const std::string& spec) const
+ {
+ auto cache_it = cache.find(spec);
+ if (cache_it != cache.end())
+ {
+ return cache_it->second;
+ }
+
+ for (auto&& ports_dir : ports_dirs)
+ {
+ // Try loading individual port
+ if (filesystem.exists(ports_dir / "CONTROL"))
+ {
+ auto maybe_scf = Paragraphs::try_load_port(filesystem, ports_dir);
+ if (auto scf = maybe_scf.get())
+ {
+ if (scf->get()->core_paragraph->name == spec)
+ {
+ auto it = cache.emplace(std::piecewise_construct,
+ std::forward_as_tuple(spec),
+ std::forward_as_tuple(std::move(*scf), ports_dir));
+ return it.first->second;
+ }
+ }
+ else
+ {
+ vcpkg::print_error_message(maybe_scf.error());
+ Checks::exit_with_message(
+ VCPKG_LINE_INFO, "Error: Failed to load port from %s", spec, ports_dir.u8string());
+ }
+ }
+
+ auto found_scf = Paragraphs::try_load_port(filesystem, ports_dir / spec);
+ if (auto scf = found_scf.get())
+ {
+ if (scf->get()->core_paragraph->name == spec)
+ {
+ auto it = cache.emplace(std::piecewise_construct,
+ std::forward_as_tuple(spec),
+ std::forward_as_tuple(std::move(*scf), ports_dir / spec));
+ return it.first->second;
+ }
+ }
+ }
+
+ return nullopt;
+ }
+
+ std::vector<const SourceControlFileLocation*> PathsPortFileProvider::load_all_control_files() const
+ {
+ // Reload cache with ports contained in all ports_dirs
+ cache.clear();
+ std::vector<const SourceControlFileLocation*> ret;
+ for (auto&& ports_dir : ports_dirs)
+ {
+ // Try loading individual port
+ if (filesystem.exists(ports_dir / "CONTROL"))
+ {
+ auto maybe_scf = Paragraphs::try_load_port(filesystem, ports_dir);
+ if (auto scf = maybe_scf.get())
+ {
+ auto port_name = scf->get()->core_paragraph->name;
+ if (cache.find(port_name) == cache.end())
+ {
+ auto it = cache.emplace(std::piecewise_construct,
+ std::forward_as_tuple(port_name),
+ std::forward_as_tuple(std::move(*scf), ports_dir));
+ ret.emplace_back(&it.first->second);
+ }
+ }
+ else
+ {
+ vcpkg::print_error_message(maybe_scf.error());
+ Checks::exit_with_message(
+ VCPKG_LINE_INFO, "Error: Failed to load port from %s", ports_dir.u8string());
+ }
+ continue;
+ }
+
+ // Try loading all ports inside ports_dir
+ auto found_scf = Paragraphs::load_all_ports(filesystem, ports_dir);
+ for (auto&& scf : found_scf)
+ {
+ auto port_name = scf->core_paragraph->name;
+ if (cache.find(port_name) == cache.end())
+ {
+ auto it = cache.emplace(std::piecewise_construct,
+ std::forward_as_tuple(port_name),
+ std::forward_as_tuple(std::move(scf), ports_dir / port_name));
+ ret.emplace_back(&it.first->second);
+ }
+ }
+ }
+ return ret;
+ }
+}
diff --git a/toolsrc/src/vcpkg/postbuildlint.cpp b/toolsrc/src/vcpkg/postbuildlint.cpp
index 2a13d2786..b346e1721 100644
--- a/toolsrc/src/vcpkg/postbuildlint.cpp
+++ b/toolsrc/src/vcpkg/postbuildlint.cpp
@@ -98,6 +98,93 @@ namespace vcpkg::PostBuildLint
return LintStatus::SUCCESS;
}
+ static LintStatus check_for_restricted_include_files(const Files::Filesystem& fs,
+ const Build::BuildPolicies& policies,
+ const fs::path& package_dir)
+ {
+ if (policies.is_enabled(BuildPolicy::ALLOW_RESTRICTED_HEADERS))
+ {
+ return LintStatus::SUCCESS;
+ }
+
+ // These files are taken from the libc6-dev package on Ubuntu inside /usr/include/x86_64-linux-gnu/sys/
+ static constexpr StringLiteral restricted_sys_filenames[] = {
+ "acct.h", "auxv.h", "bitypes.h", "cdefs.h", "debugreg.h", "dir.h", "elf.h",
+ "epoll.h", "errno.h", "eventfd.h", "fanotify.h", "fcntl.h", "file.h", "fsuid.h",
+ "gmon.h", "gmon_out.h", "inotify.h", "io.h", "ioctl.h", "ipc.h", "kd.h",
+ "klog.h", "mman.h", "mount.h", "msg.h", "mtio.h", "param.h", "pci.h",
+ "perm.h", "personality.h", "poll.h", "prctl.h", "procfs.h", "profil.h", "ptrace.h",
+ "queue.h", "quota.h", "random.h", "raw.h", "reboot.h", "reg.h", "resource.h",
+ "select.h", "sem.h", "sendfile.h", "shm.h", "signal.h", "signalfd.h", "socket.h",
+ "socketvar.h", "soundcard.h", "stat.h", "statfs.h", "statvfs.h", "stropts.h", "swap.h",
+ "syscall.h", "sysctl.h", "sysinfo.h", "syslog.h", "sysmacros.h", "termios.h", "time.h",
+ "timeb.h", "timerfd.h", "times.h", "timex.h", "ttychars.h", "ttydefaults.h", "types.h",
+ "ucontext.h", "uio.h", "un.h", "unistd.h", "user.h", "ustat.h", "utsname.h",
+ "vfs.h", "vlimit.h", "vm86.h", "vt.h", "vtimes.h", "wait.h", "xattr.h",
+ };
+ // These files are taken from the libc6-dev package on Ubuntu inside the /usr/include/ folder
+ static constexpr StringLiteral restricted_crt_filenames[] = {
+ "_G_config.h", "aio.h", "aliases.h", "alloca.h", "ar.h", "argp.h",
+ "argz.h", "assert.h", "byteswap.h", "complex.h", "cpio.h", "crypt.h",
+ "ctype.h", "dirent.h", "dlfcn.h", "elf.h", "endian.h", "envz.h",
+ "err.h", "errno.h", "error.h", "execinfo.h", "fcntl.h", "features.h",
+ "fenv.h", "fmtmsg.h", "fnmatch.h", "fstab.h", "fts.h", "ftw.h",
+ "gconv.h", "getopt.h", "glob.h", "gnu-versions.h", "grp.h", "gshadow.h",
+ "iconv.h", "ifaddrs.h", "inttypes.h", "langinfo.h", "lastlog.h", "libgen.h",
+ "libintl.h", "libio.h", "limits.h", "link.h", "locale.h", "malloc.h",
+ "math.h", "mcheck.h", "memory.h", "mntent.h", "monetary.h", "mqueue.h",
+ "netash", "netdb.h", "nl_types.h", "nss.h", "obstack.h", "paths.h",
+ "poll.h", "printf.h", "proc_service.h", "pthread.h", "pty.h", "pwd.h",
+ "re_comp.h", "regex.h", "regexp.h", "resolv.h", "sched.h", "search.h",
+ "semaphore.h", "setjmp.h", "sgtty.h", "shadow.h", "signal.h", "spawn.h",
+ "stab.h", "stdc-predef.h", "stdint.h", "stdio.h", "stdio_ext.h", "stdlib.h",
+ "string.h", "strings.h", "stropts.h", "syscall.h", "sysexits.h", "syslog.h",
+ "tar.h", "termio.h", "termios.h", "tgmath.h", "thread_db.h", "time.h",
+ "ttyent.h", "uchar.h", "ucontext.h", "ulimit.h", "unistd.h", "ustat.h",
+ "utime.h", "utmp.h", "utmpx.h", "values.h", "wait.h", "wchar.h",
+ "wctype.h", "wordexp.h",
+ };
+ // These files are general names that have shown to be problematic in the past
+ static constexpr StringLiteral restricted_general_filenames[] = {
+ "json.h",
+ "parser.h",
+ "lexer.h",
+ "config.h",
+ "local.h",
+ "slice.h",
+ };
+ static constexpr Span<const StringLiteral> restricted_lists[] = {
+ restricted_sys_filenames, restricted_crt_filenames, restricted_general_filenames};
+ const fs::path include_dir = package_dir / "include";
+ auto files = fs.get_files_non_recursive(include_dir);
+ auto filenames_v = Util::fmap(files, [](const auto& file) { return file.filename().u8string(); });
+ std::set<std::string> filenames_s(filenames_v.begin(), filenames_v.end());
+
+ std::vector<fs::path> violations;
+ for (auto&& flist : restricted_lists)
+ for (auto&& f : flist)
+ {
+ if (Util::Sets::contains(filenames_s, f))
+ {
+ violations.push_back(fs::u8path("include") / fs::u8path(f.c_str()));
+ }
+ }
+
+ if (!violations.empty())
+ {
+ System::print2(System::Color::warning,
+ "Restricted headers paths are present. These files can prevent the core C++ runtime and "
+ "other packages from compiling correctly:\n");
+ Files::print_paths(violations);
+ System::print2("In exceptional circumstances, this policy can be disabled via ",
+ Build::to_cmake_variable(BuildPolicy::ALLOW_RESTRICTED_HEADERS),
+ "\n");
+ return LintStatus::ERROR_DETECTED;
+ }
+
+ return LintStatus::SUCCESS;
+ }
+
static LintStatus check_for_files_in_debug_include_directory(const Files::Filesystem& fs,
const fs::path& package_dir)
{
@@ -142,10 +229,10 @@ namespace vcpkg::PostBuildLint
const fs::path lib_cmake = package_dir / "lib" / "cmake";
if (fs.exists(lib_cmake))
{
- System::printf(
- System::Color::warning,
- "The /lib/cmake folder should be merged with /debug/lib/cmake and moved to /share/%s/cmake.\n",
- spec.name());
+ System::printf(System::Color::warning,
+ "The /lib/cmake folder should be merged with /debug/lib/cmake and moved to "
+ "/share/%s/cmake.\nPlease use the helper function `vcpkg_fixup_cmake_targets()`\n",
+ spec.name());
return LintStatus::ERROR_DETECTED;
}
@@ -295,10 +382,12 @@ namespace vcpkg::PostBuildLint
return LintStatus::SUCCESS;
}
- static LintStatus check_exports_of_dlls(const Build::BuildPolicies& policies, const std::vector<fs::path>& dlls, const fs::path& dumpbin_exe)
+ static LintStatus check_exports_of_dlls(const Build::BuildPolicies& policies,
+ const std::vector<fs::path>& dlls,
+ const fs::path& dumpbin_exe)
{
if (policies.is_enabled(BuildPolicy::DLLS_WITHOUT_EXPORTS)) return LintStatus::SUCCESS;
-
+
std::vector<fs::path> dlls_with_no_exports;
for (const fs::path& dll : dlls)
{
@@ -571,7 +660,7 @@ namespace vcpkg::PostBuildLint
R"(If the creation of bin\ and/or debug\bin\ cannot be disabled, use this in the portfile to remove them)"
"\n"
"\n"
- R"###( if(VCPKG_LIBRARY_LINKAGE STREQUAL static))###"
+ R"###( if(VCPKG_LIBRARY_LINKAGE STREQUAL "static"))###"
"\n"
R"###( file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/bin ${CURRENT_PACKAGES_DIR}/debug/bin))###"
"\n"
@@ -760,6 +849,7 @@ namespace vcpkg::PostBuildLint
}
error_count += check_for_files_in_include_directory(fs, build_info.policies, package_dir);
+ error_count += check_for_restricted_include_files(fs, build_info.policies, package_dir);
error_count += check_for_files_in_debug_include_directory(fs, package_dir);
error_count += check_for_files_in_debug_share_directory(fs, package_dir);
error_count += check_folder_lib_cmake(fs, package_dir, spec);
diff --git a/toolsrc/src/vcpkg/remove.cpp b/toolsrc/src/vcpkg/remove.cpp
index 65e00668a..1bcfe58a0 100644
--- a/toolsrc/src/vcpkg/remove.cpp
+++ b/toolsrc/src/vcpkg/remove.cpp
@@ -229,7 +229,7 @@ namespace vcpkg::Remove
}
// Load ports from ports dirs
- Dependencies::PathsPortFileProvider provider(paths, args.overlay_ports.get());
+ PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports.get());
specs = Util::fmap(Update::find_outdated_packages(provider, status_db),
[](auto&& outdated) { return outdated.spec; });
diff --git a/toolsrc/src/vcpkg/sourceparagraph.cpp b/toolsrc/src/vcpkg/sourceparagraph.cpp
index ebb9cd4f4..26681e107 100644
--- a/toolsrc/src/vcpkg/sourceparagraph.cpp
+++ b/toolsrc/src/vcpkg/sourceparagraph.cpp
@@ -23,10 +23,10 @@ namespace vcpkg
static const std::string FEATURE = "Feature";
static const std::string MAINTAINER = "Maintainer";
static const std::string SOURCE = "Source";
- static const std::string SUPPORTS = "Supports";
static const std::string VERSION = "Version";
static const std::string HOMEPAGE = "Homepage";
static const std::string TYPE = "Type";
+ static const std::string SUPPORTS = "Supports";
}
static Span<const std::string> get_list_of_valid_fields()
@@ -39,6 +39,7 @@ namespace vcpkg
SourceParagraphFields::BUILD_DEPENDS,
SourceParagraphFields::HOMEPAGE,
SourceParagraphFields::TYPE,
+ SourceParagraphFields::SUPPORTS,
};
return valid_fields;
@@ -101,7 +102,24 @@ namespace vcpkg
}
}
- static ParseExpected<SourceParagraph> parse_source_paragraph(RawParagraph&& fields)
+ std::string Type::to_string(const Type& t)
+ {
+ switch (t.type)
+ {
+ case Type::ALIAS: return "Alias";
+ case Type::PORT: return "Port";
+ default: return "Unknown";
+ }
+ }
+
+ Type Type::from_string(const std::string& t)
+ {
+ if (t == "Alias") return Type{Type::ALIAS};
+ if (t == "Port" || t == "") return Type{Type::PORT};
+ return Type{Type::UNKNOWN};
+ }
+
+ static ParseExpected<SourceParagraph> parse_source_paragraph(const fs::path& path_to_control, RawParagraph&& fields)
{
ParagraphParser parser(std::move(fields));
@@ -115,17 +133,18 @@ namespace vcpkg
spgh->homepage = parser.optional_field(SourceParagraphFields::HOMEPAGE);
spgh->depends = expand_qualified_dependencies(
parse_comma_list(parser.optional_field(SourceParagraphFields::BUILD_DEPENDS)));
- spgh->supports = parse_comma_list(parser.optional_field(SourceParagraphFields::SUPPORTS));
spgh->default_features = parse_comma_list(parser.optional_field(SourceParagraphFields::DEFAULTFEATURES));
-
- auto err = parser.error_info(spgh->name);
+ spgh->supports_expression = parser.optional_field(SourceParagraphFields::SUPPORTS);
+ spgh->type = Type::from_string(parser.optional_field(SourceParagraphFields::TYPE));
+ auto err = parser.error_info(spgh->name.empty() ? path_to_control.u8string() : spgh->name);
if (err)
- return std::move(err);
+ return err;
else
- return std::move(spgh);
+ return spgh;
}
- static ParseExpected<FeatureParagraph> parse_feature_paragraph(RawParagraph&& fields)
+ static ParseExpected<FeatureParagraph> parse_feature_paragraph(const fs::path& path_to_control,
+ RawParagraph&& fields)
{
ParagraphParser parser(std::move(fields));
@@ -137,24 +156,26 @@ namespace vcpkg
fpgh->depends = expand_qualified_dependencies(
parse_comma_list(parser.optional_field(SourceParagraphFields::BUILD_DEPENDS)));
- auto err = parser.error_info(fpgh->name);
+ auto err = parser.error_info(fpgh->name.empty() ? path_to_control.u8string() : fpgh->name);
if (err)
- return std::move(err);
+ return err;
else
- return std::move(fpgh);
+ return fpgh;
}
ParseExpected<SourceControlFile> SourceControlFile::parse_control_file(
- std::vector<Parse::RawParagraph>&& control_paragraphs)
+ const fs::path& path_to_control, std::vector<Parse::RawParagraph>&& control_paragraphs)
{
if (control_paragraphs.size() == 0)
{
- return std::make_unique<Parse::ParseControlErrorInfo>();
+ auto ret = std::make_unique<Parse::ParseControlErrorInfo>();
+ ret->name = path_to_control.u8string();
+ return ret;
}
auto control_file = std::make_unique<SourceControlFile>();
- auto maybe_source = parse_source_paragraph(std::move(control_paragraphs.front()));
+ auto maybe_source = parse_source_paragraph(path_to_control, std::move(control_paragraphs.front()));
if (const auto source = maybe_source.get())
control_file->core_paragraph = std::move(*source);
else
@@ -164,14 +185,14 @@ namespace vcpkg
for (auto&& feature_pgh : control_paragraphs)
{
- auto maybe_feature = parse_feature_paragraph(std::move(feature_pgh));
+ auto maybe_feature = parse_feature_paragraph(path_to_control, std::move(feature_pgh));
if (const auto feature = maybe_feature.get())
control_file->feature_paragraphs.emplace_back(std::move(*feature));
else
return std::move(maybe_feature).error();
}
- return std::move(control_file);
+ return control_file;
}
Optional<const FeatureParagraph&> SourceControlFile::find_feature(const std::string& featurename) const
@@ -183,6 +204,18 @@ namespace vcpkg
else
return nullopt;
}
+ Optional<const std::vector<Dependency>&> SourceControlFile::find_dependencies_for_feature(
+ const std::string& featurename) const
+ {
+ if (featurename == "core")
+ {
+ return core_paragraph->depends;
+ }
+ else if (auto p_feature = find_feature(featurename).get())
+ return p_feature->depends;
+ else
+ return nullopt;
+ }
Dependency Dependency::parse_dependency(std::string name, std::string qualifier)
{
@@ -207,96 +240,62 @@ namespace vcpkg
std::vector<Dependency> 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 Dependency::parse_dependency(depend_string, "");
- // expect of the form "\w+ \[\w+\]"
- if (depend_string.c_str()[pos + 1] != '(' || depend_string[depend_string.size() - 1] != ')')
+ // First, try to find beginning and end of features list
+ auto end_of_features = depend_string.find(']');
+ if (end_of_features != std::string::npos)
{
- // Error, but for now just slurp the entire string.
- return Dependency::parse_dependency(depend_string, "");
+ ++end_of_features;
+ }
+ else
+ {
+ end_of_features = depend_string.find(' ');
+ if (end_of_features == std::string::npos) end_of_features = depend_string.size();
}
- return Dependency::parse_dependency(depend_string.substr(0, pos),
- depend_string.substr(pos + 2, depend_string.size() - pos - 3));
- });
- }
- std::vector<std::string> filter_dependencies(const std::vector<vcpkg::Dependency>& deps, const Triplet& t)
- {
- std::vector<std::string> ret;
- for (auto&& dep : deps)
- {
- const auto& qualifier = dep.qualifier;
- if (qualifier.empty() || evaluate_expression(qualifier, t.canonical_name()))
+ auto begin_of_qualifier = depend_string.find('(', end_of_features);
+ if (begin_of_qualifier == std::string::npos)
{
- ret.emplace_back(dep.name());
+ return Dependency::parse_dependency(depend_string.substr(0, end_of_features), "");
}
- }
- return ret;
+ else
+ {
+ int depth = 1;
+ auto i = begin_of_qualifier + 1;
+ for (; i != depend_string.size(); ++i)
+ {
+ auto ch = depend_string[i];
+ if (ch == '(')
+ ++depth;
+ else if (ch == ')')
+ --depth;
+
+ if (depth == 0) break;
+ }
+ return Dependency::parse_dependency(
+ depend_string.substr(0, end_of_features),
+ depend_string.substr(begin_of_qualifier + 1, i - begin_of_qualifier - 1));
+ }
+ });
}
- std::vector<Features> filter_dependencies_to_features(const std::vector<vcpkg::Dependency>& deps, const Triplet& t)
+ std::vector<FullPackageSpec> filter_dependencies(const std::vector<vcpkg::Dependency>& deps,
+ const Triplet& t,
+ const std::unordered_map<std::string, std::string>& cmake_vars)
{
- std::vector<Features> ret;
+ std::vector<FullPackageSpec> ret;
for (auto&& dep : deps)
{
const auto& qualifier = dep.qualifier;
- if (qualifier.empty() || evaluate_expression(qualifier, t.canonical_name()))
+ if (qualifier.empty() ||
+ evaluate_expression(qualifier, {cmake_vars, t.canonical_name()}).value_or_exit(VCPKG_LINE_INFO))
{
- ret.emplace_back(dep.depend);
+ ret.emplace_back(FullPackageSpec(
+ PackageSpec::from_name_and_triplet(dep.depend.name, t).value_or_exit(VCPKG_LINE_INFO),
+ dep.depend.features));
}
}
return ret;
}
- 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);
- }
-
std::string to_string(const Dependency& dep) { return dep.name(); }
-
- ExpectedT<Supports, std::vector<std::string>> Supports::parse(const std::vector<std::string>& strs)
- {
- Supports ret;
- std::vector<std::string> unrecognized;
-
- for (auto&& str : strs)
- {
- if (str == "x64")
- ret.architectures.push_back(Architecture::X64);
- else if (str == "x86")
- ret.architectures.push_back(Architecture::X86);
- else if (str == "arm")
- ret.architectures.push_back(Architecture::ARM);
- else if (str == "windows")
- ret.platforms.push_back(Platform::WINDOWS);
- else if (str == "uwp")
- ret.platforms.push_back(Platform::UWP);
- else if (str == "v140")
- ret.toolsets.push_back(ToolsetVersion::V140);
- else if (str == "v141")
- ret.toolsets.push_back(ToolsetVersion::V141);
- else if (str == "crt-static")
- ret.crt_linkages.push_back(Linkage::STATIC);
- else if (str == "crt-dynamic")
- ret.crt_linkages.push_back(Linkage::DYNAMIC);
- else
- unrecognized.push_back(str);
- }
-
- if (unrecognized.empty())
- return std::move(ret);
- else
- return std::move(unrecognized);
- }
-
- bool Supports::is_supported(Architecture arch, Platform plat, Linkage crt, ToolsetVersion tools)
- {
- const auto is_in_or_empty = [](auto v, auto&& c) -> bool { return c.empty() || c.end() != Util::find(c, v); };
- if (!is_in_or_empty(arch, architectures)) return false;
- if (!is_in_or_empty(plat, platforms)) return false;
- if (!is_in_or_empty(crt, crt_linkages)) return false;
- if (!is_in_or_empty(tools, toolsets)) return false;
- return true;
- }
}
diff --git a/toolsrc/src/vcpkg/statusparagraph.cpp b/toolsrc/src/vcpkg/statusparagraph.cpp
index f7e00f21c..c0ecca3a0 100644
--- a/toolsrc/src/vcpkg/statusparagraph.cpp
+++ b/toolsrc/src/vcpkg/statusparagraph.cpp
@@ -85,6 +85,34 @@ namespace vcpkg
default: return "error";
}
}
+
+ std::unordered_map<std::string, std::vector<FeatureSpec>> InstalledPackageView::feature_dependencies() const
+ {
+ auto extract_deps = [&](const std::string& dep) {
+ FullPackageSpec dependency =
+ FullPackageSpec::from_string(dep, spec().triplet()).value_or_exit(VCPKG_LINE_INFO);
+ std::vector<FeatureSpec> fspecs;
+
+ for (std::string& feature : dependency.features)
+ {
+ fspecs.emplace_back(dependency.package_spec, std::move(feature));
+ }
+
+ return fspecs;
+ };
+
+ std::unordered_map<std::string, std::vector<FeatureSpec>> deps;
+
+ for (const StatusParagraph* const& feature : features)
+ {
+ deps.emplace(feature->package.feature, Util::fmap_flatten(feature->package.depends, extract_deps));
+ }
+
+ deps.emplace("core", Util::fmap_flatten(core->package.depends, extract_deps));
+
+ return deps;
+ }
+
std::vector<PackageSpec> InstalledPackageView::dependencies() const
{
// accumulate all features in installed dependencies
diff --git a/toolsrc/src/vcpkg/statusparagraphs.cpp b/toolsrc/src/vcpkg/statusparagraphs.cpp
index 3c81728bb..2fa9df7df 100644
--- a/toolsrc/src/vcpkg/statusparagraphs.cpp
+++ b/toolsrc/src/vcpkg/statusparagraphs.cpp
@@ -46,7 +46,7 @@ namespace vcpkg
}
}
if (ipv.core != nullptr)
- return std::move(ipv);
+ return ipv;
else
return nullopt;
}
diff --git a/toolsrc/src/vcpkg/update.cpp b/toolsrc/src/vcpkg/update.cpp
index 6320bae5b..8cb2ac557 100644
--- a/toolsrc/src/vcpkg/update.cpp
+++ b/toolsrc/src/vcpkg/update.cpp
@@ -14,7 +14,7 @@ namespace vcpkg::Update
return left.spec.name() < right.spec.name();
}
- std::vector<OutdatedPackage> find_outdated_packages(const Dependencies::PortFileProvider& provider,
+ std::vector<OutdatedPackage> find_outdated_packages(const PortFileProvider::PortFileProvider& provider,
const StatusParagraphs& status_db)
{
auto installed_packages = get_installed_ports(status_db);
@@ -57,7 +57,7 @@ namespace vcpkg::Update
const StatusParagraphs status_db = database_load_check(paths);
- Dependencies::PathsPortFileProvider provider(paths, args.overlay_ports.get());
+ PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports.get());
const auto outdated_packages = SortedVector<OutdatedPackage>(find_outdated_packages(provider, status_db),
&OutdatedPackage::compare_by_name);
diff --git a/toolsrc/src/vcpkg/vcpkgpaths.cpp b/toolsrc/src/vcpkg/vcpkgpaths.cpp
index 078121fcc..692cf1634 100644
--- a/toolsrc/src/vcpkg/vcpkgpaths.cpp
+++ b/toolsrc/src/vcpkg/vcpkgpaths.cpp
@@ -67,6 +67,7 @@ namespace vcpkg
paths.ports = paths.root / "ports";
paths.installed = paths.root / "installed";
paths.triplets = paths.root / "triplets";
+ paths.community_triplets = paths.triplets / "community";
if (auto scripts_dir = vcpkg_scripts_root_dir.get())
{
@@ -110,7 +111,8 @@ namespace vcpkg
paths.triplets_dirs.emplace_back(fs::stdfs::canonical(path));
}
}
- paths.triplets_dirs.emplace_back(fs::stdfs::canonical(paths.root / "triplets"));
+ paths.triplets_dirs.emplace_back(fs::stdfs::canonical(paths.triplets));
+ paths.triplets_dirs.emplace_back(fs::stdfs::canonical(paths.community_triplets));
return paths;
}
@@ -130,23 +132,32 @@ namespace vcpkg
bool VcpkgPaths::is_valid_triplet(const Triplet& t) const
{
const auto it = Util::find_if(this->get_available_triplets(), [&](auto&& available_triplet) {
- return t.canonical_name() == available_triplet;
+ return t.canonical_name() == available_triplet.name;
});
return it != this->get_available_triplets().cend();
}
- const std::vector<std::string>& VcpkgPaths::get_available_triplets() const
+ const std::vector<std::string> VcpkgPaths::get_available_triplets_names() const
{
- return this->available_triplets.get_lazy([this]() -> std::vector<std::string> {
- std::vector<std::string> output;
+ return vcpkg::Util::fmap(this->get_available_triplets(),
+ [](auto&& triplet_file) -> std::string { return triplet_file.name; });
+ }
+
+ const std::vector<VcpkgPaths::TripletFile>& VcpkgPaths::get_available_triplets() const
+ {
+ return this->available_triplets.get_lazy([this]() -> std::vector<TripletFile> {
+ std::vector<TripletFile> output;
+ Files::Filesystem& fs = this->get_filesystem();
for (auto&& triplets_dir : triplets_dirs)
{
- for (auto&& path : this->get_filesystem().get_files_non_recursive(triplets_dir))
+ for (auto&& path : fs.get_files_non_recursive(triplets_dir))
{
- output.push_back(path.stem().filename().string());
+ if (fs::is_regular_file(fs.status(VCPKG_LINE_INFO, path)))
+ {
+ output.emplace_back(TripletFile(path.stem().filename().u8string(), triplets_dir));
+ }
}
}
- Util::sort_unique_erase(output);
return output;
});
}