aboutsummaryrefslogtreecommitdiff
path: root/toolsrc/src
diff options
context:
space:
mode:
authorJackBoosY <47264268+JackBoosY@users.noreply.github.com>2019-08-16 10:01:32 +0800
committerGitHub <noreply@github.com>2019-08-16 10:01:32 +0800
commitadb84c2658b9774ff535eb88f377ee818dd429be (patch)
treefcfcf4a93057037152a14aa2885598b45c5b03bd /toolsrc/src
parent2865da8f4a6d911617cdd9b147816c4cd02bbf7e (diff)
parent22e787f9448a25dae734ca06c80e7e5af5fb6537 (diff)
downloadvcpkg-adb84c2658b9774ff535eb88f377ee818dd429be.tar.gz
vcpkg-adb84c2658b9774ff535eb88f377ee818dd429be.zip
Merge branch 'master' into dev/jack/4167
Diffstat (limited to 'toolsrc/src')
-rw-r--r--toolsrc/src/tests.arguments.cpp104
-rw-r--r--toolsrc/src/tests.chrono.cpp41
-rw-r--r--toolsrc/src/tests.dependencies.cpp110
-rw-r--r--toolsrc/src/tests.packagespec.cpp136
-rw-r--r--toolsrc/src/tests.paragraph.cpp441
-rw-r--r--toolsrc/src/tests.pch.cpp1
-rw-r--r--toolsrc/src/tests.plan.cpp1261
-rw-r--r--toolsrc/src/tests.statusparagraphs.cpp115
-rw-r--r--toolsrc/src/tests.update.cpp106
-rw-r--r--toolsrc/src/tests.utils.cpp42
-rw-r--r--toolsrc/src/vcpkg-test/arguments.cpp109
-rw-r--r--toolsrc/src/vcpkg-test/catch.cpp11
-rw-r--r--toolsrc/src/vcpkg-test/chrono.cpp34
-rw-r--r--toolsrc/src/vcpkg-test/dependencies.cpp28
-rw-r--r--toolsrc/src/vcpkg-test/files.cpp243
-rw-r--r--toolsrc/src/vcpkg-test/paragraph.cpp445
-rw-r--r--toolsrc/src/vcpkg-test/plan.cpp1241
-rw-r--r--toolsrc/src/vcpkg-test/specifier.cpp134
-rw-r--r--toolsrc/src/vcpkg-test/statusparagraphs.cpp110
-rw-r--r--toolsrc/src/vcpkg-test/strings.cpp33
-rw-r--r--toolsrc/src/vcpkg-test/supports.cpp79
-rw-r--r--toolsrc/src/vcpkg-test/update.cpp102
-rw-r--r--toolsrc/src/vcpkg-test/util.cpp182
-rw-r--r--toolsrc/src/vcpkg.cpp14
-rw-r--r--toolsrc/src/vcpkg/archives.cpp5
-rw-r--r--toolsrc/src/vcpkg/base/checks.cpp3
-rw-r--r--toolsrc/src/vcpkg/base/files.cpp338
-rw-r--r--toolsrc/src/vcpkg/base/strings.cpp40
-rw-r--r--toolsrc/src/vcpkg/binaryparagraph.cpp2
-rw-r--r--toolsrc/src/vcpkg/build.cpp412
-rw-r--r--toolsrc/src/vcpkg/commands.cache.cpp2
-rw-r--r--toolsrc/src/vcpkg/commands.ci.cpp12
-rw-r--r--toolsrc/src/vcpkg/commands.cpp2
-rw-r--r--toolsrc/src/vcpkg/commands.dependinfo.cpp340
-rw-r--r--toolsrc/src/vcpkg/commands.exportifw.cpp16
-rw-r--r--toolsrc/src/vcpkg/commands.import.cpp2
-rw-r--r--toolsrc/src/vcpkg/commands.portsdiff.cpp2
-rw-r--r--toolsrc/src/vcpkg/commands.search.cpp8
-rw-r--r--toolsrc/src/vcpkg/dependencies.cpp81
-rw-r--r--toolsrc/src/vcpkg/export.cpp6
-rw-r--r--toolsrc/src/vcpkg/install.cpp18
-rw-r--r--toolsrc/src/vcpkg/logicexpression.cpp268
-rw-r--r--toolsrc/src/vcpkg/paragraphs.cpp41
-rw-r--r--toolsrc/src/vcpkg/parse.cpp3
-rw-r--r--toolsrc/src/vcpkg/remove.cpp7
-rw-r--r--toolsrc/src/vcpkg/sourceparagraph.cpp28
-rw-r--r--toolsrc/src/vcpkg/statusparagraph.cpp2
-rw-r--r--toolsrc/src/vcpkg/tools.cpp45
-rw-r--r--toolsrc/src/vcpkg/userconfig.cpp2
-rw-r--r--toolsrc/src/vcpkg/vcpkgcmdarguments.cpp35
-rw-r--r--toolsrc/src/vcpkg/vcpkgpaths.cpp42
51 files changed, 3993 insertions, 2891 deletions
diff --git a/toolsrc/src/tests.arguments.cpp b/toolsrc/src/tests.arguments.cpp
deleted file mode 100644
index e108b983a..000000000
--- a/toolsrc/src/tests.arguments.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-#include "tests.pch.h"
-
-#if defined(_WIN32)
-#pragma comment(lib, "version")
-#pragma comment(lib, "winhttp")
-#endif
-
-using namespace Microsoft::VisualStudio::CppUnitTestFramework;
-
-using namespace vcpkg;
-
-namespace UnitTest1
-{
- class ArgumentTests : public TestClass<ArgumentTests>
- {
- TEST_METHOD(create_from_arg_sequence_options_lower)
- {
- std::vector<std::string> t = {
- "--vcpkg-root", "C:\\vcpkg",
- "--scripts-root=C:\\scripts",
- "--debug",
- "--sendmetrics",
- "--printmetrics",
- "--overlay-ports=C:\\ports1",
- "--overlay-ports=C:\\ports2",
- "--overlay-triplets=C:\\tripletsA",
- "--overlay-triplets=C:\\tripletsB"
- };
- auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size());
- Assert::AreEqual("C:\\vcpkg", v.vcpkg_root_dir.get()->c_str());
- Assert::AreEqual("C:\\scripts", v.scripts_root_dir.get()->c_str());
- Assert::IsTrue(v.debug && *v.debug.get());
- Assert::IsTrue(v.sendmetrics && v.sendmetrics.get());
- Assert::IsTrue(v.printmetrics && *v.printmetrics.get());
-
- Assert::IsTrue(v.overlay_ports.get()->size() == 2);
- Assert::AreEqual("C:\\ports1", v.overlay_ports.get()->at(0).c_str());
- Assert::AreEqual("C:\\ports2", v.overlay_ports.get()->at(1).c_str());
-
- Assert::IsTrue(v.overlay_triplets.get()->size() == 2);
- Assert::AreEqual("C:\\tripletsA", v.overlay_triplets.get()->at(0).c_str());
- Assert::AreEqual("C:\\tripletsB", v.overlay_triplets.get()->at(1).c_str());
- }
-
- TEST_METHOD(create_from_arg_sequence_options_upper)
- {
- std::vector<std::string> t = {
- "--VCPKG-ROOT", "C:\\vcpkg",
- "--SCRIPTS-ROOT=C:\\scripts",
- "--DEBUG",
- "--SENDMETRICS",
- "--PRINTMETRICS",
- "--OVERLAY-PORTS=C:\\ports1",
- "--OVERLAY-PORTS=C:\\ports2",
- "--OVERLAY-TRIPLETS=C:\\tripletsA",
- "--OVERLAY-TRIPLETS=C:\\tripletsB"
- };
- auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size());
- Assert::AreEqual("C:\\vcpkg", v.vcpkg_root_dir.get()->c_str());
- Assert::AreEqual("C:\\scripts", v.scripts_root_dir.get()->c_str());
- Assert::IsTrue(v.debug && *v.debug.get());
- Assert::IsTrue(v.sendmetrics && v.sendmetrics.get());
- Assert::IsTrue(v.printmetrics && *v.printmetrics.get());
-
- Assert::IsTrue(v.overlay_ports.get()->size() == 2);
- Assert::AreEqual("C:\\ports1", v.overlay_ports.get()->at(0).c_str());
- Assert::AreEqual("C:\\ports2", v.overlay_ports.get()->at(1).c_str());
-
- Assert::IsTrue(v.overlay_triplets.get()->size() == 2);
- Assert::AreEqual("C:\\tripletsA", v.overlay_triplets.get()->at(0).c_str());
- Assert::AreEqual("C:\\tripletsB", v.overlay_triplets.get()->at(1).c_str());
- }
-
- TEST_METHOD(create_from_arg_sequence_valued_options)
- {
- std::array<CommandSetting, 1> settings = {{{"--a", ""}}};
- CommandStructure cmdstruct = {"", 0, SIZE_MAX, {{}, settings}, nullptr};
-
- std::vector<std::string> t = {"--a=b", "command", "argument"};
- auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size());
- auto opts = v.parse_arguments(cmdstruct);
- Assert::AreEqual("b", opts.settings["--a"].c_str());
- Assert::AreEqual(size_t{1}, v.command_arguments.size());
- Assert::AreEqual("argument", v.command_arguments[0].c_str());
- Assert::AreEqual("command", v.command.c_str());
- }
-
- TEST_METHOD(create_from_arg_sequence_valued_options2)
- {
- std::array<CommandSwitch, 2> switches = {{{"--a", ""}, {"--c", ""}}};
- std::array<CommandSetting, 2> settings = {{{"--b", ""}, {"--d", ""}}};
- CommandStructure cmdstruct = {"", 0, SIZE_MAX, {switches, settings}, nullptr};
-
- std::vector<std::string> t = {"--a", "--b=c"};
- auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size());
- auto opts = v.parse_arguments(cmdstruct);
- Assert::AreEqual("c", opts.settings["--b"].c_str());
- Assert::IsTrue(opts.settings.find("--d") == opts.settings.end());
- Assert::IsTrue(opts.switches.find("--a") != opts.switches.end());
- Assert::IsTrue(opts.settings.find("--c") == opts.settings.end());
- Assert::AreEqual(size_t{0}, v.command_arguments.size());
- }
- };
-}
diff --git a/toolsrc/src/tests.chrono.cpp b/toolsrc/src/tests.chrono.cpp
deleted file mode 100644
index 269cdca58..000000000
--- a/toolsrc/src/tests.chrono.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-#include "tests.pch.h"
-
-using namespace Microsoft::VisualStudio::CppUnitTestFramework;
-
-namespace Chrono = vcpkg::Chrono;
-
-namespace UnitTest1
-{
- class ChronoTests : public TestClass<ChronoTests>
- {
- TEST_METHOD(parse_time)
- {
- auto timestring = "1990-02-03T04:05:06.0Z";
- auto maybe_time = Chrono::CTime::parse(timestring);
-
- Assert::IsTrue(maybe_time.has_value());
-
- Assert::AreEqual(timestring, maybe_time.get()->to_string().c_str());
- }
-
- TEST_METHOD(parse_time_blank)
- {
- auto maybe_time = Chrono::CTime::parse("");
-
- Assert::IsFalse(maybe_time.has_value());
- }
-
- TEST_METHOD(time_difference)
- {
- auto maybe_time1 = Chrono::CTime::parse("1990-02-03T04:05:06.0Z");
- auto maybe_time2 = Chrono::CTime::parse("1990-02-10T04:05:06.0Z");
-
- Assert::IsTrue(maybe_time1.has_value());
- Assert::IsTrue(maybe_time2.has_value());
-
- auto delta = maybe_time2.get()->to_time_point() - maybe_time1.get()->to_time_point();
-
- Assert::AreEqual(24 * 7, std::chrono::duration_cast<std::chrono::hours>(delta).count());
- }
- };
-}
diff --git a/toolsrc/src/tests.dependencies.cpp b/toolsrc/src/tests.dependencies.cpp
deleted file mode 100644
index 7d8283ed6..000000000
--- a/toolsrc/src/tests.dependencies.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-#include "tests.pch.h"
-
-#if defined(_WIN32)
-#pragma comment(lib, "version")
-#pragma comment(lib, "winhttp")
-#endif
-
-using namespace Microsoft::VisualStudio::CppUnitTestFramework;
-
-using namespace vcpkg;
-using Parse::parse_comma_list;
-
-namespace UnitTest1
-{
- class DependencyTests : public TestClass<DependencyTests>
- {
- TEST_METHOD(parse_depends_one)
- {
- auto v = expand_qualified_dependencies(parse_comma_list("libA (windows)"));
- Assert::AreEqual(size_t(1), v.size());
- Assert::AreEqual("libA", v[0].depend.name.c_str());
- Assert::AreEqual("windows", v[0].qualifier.c_str());
- }
-
- TEST_METHOD(filter_depends)
- {
- auto deps = expand_qualified_dependencies(parse_comma_list("libA (windows), libB, libC (uwp)"));
- auto v = filter_dependencies(deps, Triplet::X64_WINDOWS);
- Assert::AreEqual(size_t(2), v.size());
- Assert::AreEqual("libA", v[0].c_str());
- Assert::AreEqual("libB", v[1].c_str());
-
- auto v2 = filter_dependencies(deps, Triplet::ARM_UWP);
- Assert::AreEqual(size_t(2), v.size());
- Assert::AreEqual("libB", v2[0].c_str());
- Assert::AreEqual("libC", v2[1].c_str());
- }
- };
-
- class SupportsTests : public TestClass<SupportsTests>
- {
- TEST_METHOD(parse_supports_all)
- {
- auto v = Supports::parse({
- "x64",
- "x86",
- "arm",
- "windows",
- "uwp",
- "v140",
- "v141",
- "crt-static",
- "crt-dynamic",
- });
- Assert::AreNotEqual(uintptr_t(0), uintptr_t(v.get()));
-
- Assert::IsTrue(v.get()->is_supported(System::CPUArchitecture::X64,
- Supports::Platform::UWP,
- Supports::Linkage::DYNAMIC,
- Supports::ToolsetVersion::V140));
- Assert::IsTrue(v.get()->is_supported(System::CPUArchitecture::ARM,
- Supports::Platform::WINDOWS,
- Supports::Linkage::STATIC,
- Supports::ToolsetVersion::V141));
- }
-
- TEST_METHOD(parse_supports_invalid)
- {
- auto v = Supports::parse({"arm64"});
- Assert::AreEqual(uintptr_t(0), uintptr_t(v.get()));
- Assert::AreEqual(size_t(1), v.error().size());
- Assert::AreEqual("arm64", v.error()[0].c_str());
- }
-
- TEST_METHOD(parse_supports_case_sensitive)
- {
- auto v = Supports::parse({"Windows"});
- Assert::AreEqual(uintptr_t(0), uintptr_t(v.get()));
- Assert::AreEqual(size_t(1), v.error().size());
- Assert::AreEqual("Windows", v.error()[0].c_str());
- }
-
- TEST_METHOD(parse_supports_some)
- {
- auto v = Supports::parse({
- "x64",
- "x86",
- "windows",
- });
- Assert::AreNotEqual(uintptr_t(0), uintptr_t(v.get()));
-
- Assert::IsTrue(v.get()->is_supported(System::CPUArchitecture::X64,
- Supports::Platform::WINDOWS,
- Supports::Linkage::DYNAMIC,
- Supports::ToolsetVersion::V140));
- Assert::IsFalse(v.get()->is_supported(System::CPUArchitecture::ARM,
- Supports::Platform::WINDOWS,
- Supports::Linkage::DYNAMIC,
- Supports::ToolsetVersion::V140));
- Assert::IsFalse(v.get()->is_supported(System::CPUArchitecture::X64,
- Supports::Platform::UWP,
- Supports::Linkage::DYNAMIC,
- Supports::ToolsetVersion::V140));
- Assert::IsTrue(v.get()->is_supported(System::CPUArchitecture::X64,
- Supports::Platform::WINDOWS,
- Supports::Linkage::STATIC,
- Supports::ToolsetVersion::V141));
- }
- };
-}
diff --git a/toolsrc/src/tests.packagespec.cpp b/toolsrc/src/tests.packagespec.cpp
deleted file mode 100644
index d3bc18c79..000000000
--- a/toolsrc/src/tests.packagespec.cpp
+++ /dev/null
@@ -1,136 +0,0 @@
-#include "tests.pch.h"
-
-#include <tests.utils.h>
-
-#if defined(_WIN32)
-#pragma comment(lib, "version")
-#pragma comment(lib, "winhttp")
-#endif
-
-using namespace Microsoft::VisualStudio::CppUnitTestFramework;
-
-namespace UnitTest1
-{
- using namespace vcpkg;
-
- class SpecifierConversion : public TestClass<SpecifierConversion>
- {
- TEST_METHOD(full_package_spec_to_feature_specs)
- {
- auto a_spec = PackageSpec::from_name_and_triplet("a", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO);
- auto b_spec = PackageSpec::from_name_and_triplet("b", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO);
-
- auto fspecs = FullPackageSpec::to_feature_specs({{a_spec, {"0", "1"}}, {b_spec, {"2", "3"}}});
-
- Assert::AreEqual(size_t(6), fspecs.size());
-
- std::array<const char*, 6> features = {"", "0", "1", "", "2", "3"};
- std::array<PackageSpec*, 6> specs = {&a_spec, &a_spec, &a_spec, &b_spec, &b_spec, &b_spec};
-
- for (size_t i = 0; i < features.size(); ++i)
- {
- Assert::AreEqual(features[i], fspecs[i].feature().c_str());
- Assert::AreEqual(*specs[i], fspecs[i].spec());
- }
- }
- };
-
- class SpecifierParsing : public TestClass<SpecifierParsing>
- {
- TEST_METHOD(parsed_specifier_from_string)
- {
- auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib");
- Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, maybe_spec.error());
- auto spec = maybe_spec.get();
- Assert::AreEqual("zlib", spec->name.c_str());
- Assert::AreEqual(size_t(0), spec->features.size());
- Assert::AreEqual("", spec->triplet.c_str());
- }
-
- TEST_METHOD(parsed_specifier_from_string_with_triplet)
- {
- auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib:x64-uwp");
- Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, maybe_spec.error());
- auto spec = maybe_spec.get();
- Assert::AreEqual("zlib", spec->name.c_str());
- Assert::AreEqual("x64-uwp", spec->triplet.c_str());
- }
-
- TEST_METHOD(parsed_specifier_from_string_with_colons)
- {
- auto ec = vcpkg::ParsedSpecifier::from_string("zlib:x86-uwp:").error();
- Assert::AreEqual(vcpkg::PackageSpecParseResult::TOO_MANY_COLONS, ec);
- }
-
- TEST_METHOD(parsed_specifier_from_string_with_feature)
- {
- auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib[feature]:x64-uwp");
- Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, maybe_spec.error());
- auto spec = maybe_spec.get();
- Assert::AreEqual("zlib", spec->name.c_str());
- Assert::IsTrue(spec->features.size() == 1);
- Assert::AreEqual("feature", spec->features.front().c_str());
- Assert::AreEqual("x64-uwp", spec->triplet.c_str());
- }
-
- TEST_METHOD(parsed_specifier_from_string_with_many_features)
- {
- auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib[0, 1,2]");
- Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, maybe_spec.error());
- auto spec = maybe_spec.get();
- Assert::AreEqual("zlib", spec->name.c_str());
- Assert::IsTrue(spec->features.size() == 3);
- Assert::AreEqual("0", spec->features[0].c_str());
- Assert::AreEqual("1", spec->features[1].c_str());
- Assert::AreEqual("2", spec->features[2].c_str());
- Assert::AreEqual("", spec->triplet.c_str());
- }
-
- TEST_METHOD(parsed_specifier_wildcard_feature)
- {
- auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib[*]");
- Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, maybe_spec.error());
- auto spec = maybe_spec.get();
- Assert::AreEqual("zlib", spec->name.c_str());
- Assert::IsTrue(spec->features.size() == 1);
- Assert::AreEqual("*", spec->features[0].c_str());
- Assert::AreEqual("", spec->triplet.c_str());
- }
-
- TEST_METHOD(expand_wildcards)
- {
- 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});
- Util::sort(specs);
- auto spectargets = FeatureSpec::from_strings_and_triplet(
- {
- "openssl",
- "zlib",
- "openssl[*]",
- "zlib[0]",
- "zlib[1]",
- },
- Triplet::X86_UWP);
- Util::sort(spectargets);
- Assert::IsTrue(specs.size() == spectargets.size());
- Assert::IsTrue(Util::all_equal(specs, spectargets));
- }
-
- TEST_METHOD(utf8_to_utf16)
- {
- auto str = vcpkg::Strings::to_utf16("abc");
- Assert::AreEqual(L"abc", str.c_str());
- }
-
- TEST_METHOD(utf8_to_utf16_with_whitespace)
- {
- auto str = vcpkg::Strings::to_utf16("abc -x86-windows");
- Assert::AreEqual(L"abc -x86-windows", str.c_str());
- }
- };
-
- TEST_CLASS(Metrics){};
-}
diff --git a/toolsrc/src/tests.paragraph.cpp b/toolsrc/src/tests.paragraph.cpp
deleted file mode 100644
index e99d07694..000000000
--- a/toolsrc/src/tests.paragraph.cpp
+++ /dev/null
@@ -1,441 +0,0 @@
-#include "tests.pch.h"
-
-#if defined(_WIN32)
-#pragma comment(lib, "version")
-#pragma comment(lib, "winhttp")
-#endif
-
-using namespace Microsoft::VisualStudio::CppUnitTestFramework;
-
-namespace Strings = vcpkg::Strings;
-
-namespace UnitTest1
-{
- class ControlParsing : public TestClass<ControlParsing>
- {
- TEST_METHOD(SourceParagraph_Construct_Minimum)
- {
- auto m_pgh =
- vcpkg::SourceControlFile::parse_control_file(std::vector<std::unordered_map<std::string, std::string>>{{
- {"Source", "zlib"},
- {"Version", "1.2.8"},
- }});
-
- Assert::IsTrue(m_pgh.has_value());
- auto& pgh = *m_pgh.get();
-
- Assert::AreEqual("zlib", pgh->core_paragraph->name.c_str());
- Assert::AreEqual("1.2.8", pgh->core_paragraph->version.c_str());
- Assert::AreEqual("", pgh->core_paragraph->maintainer.c_str());
- Assert::AreEqual("", pgh->core_paragraph->description.c_str());
- Assert::AreEqual(size_t(0), pgh->core_paragraph->depends.size());
- }
-
- TEST_METHOD(SourceParagraph_Construct_Maximum)
- {
- 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"},
- }});
- Assert::IsTrue(m_pgh.has_value());
- auto& pgh = *m_pgh.get();
-
- Assert::AreEqual("s", pgh->core_paragraph->name.c_str());
- Assert::AreEqual("v", pgh->core_paragraph->version.c_str());
- Assert::AreEqual("m", pgh->core_paragraph->maintainer.c_str());
- Assert::AreEqual("d", pgh->core_paragraph->description.c_str());
- Assert::AreEqual(size_t(1), pgh->core_paragraph->depends.size());
- Assert::AreEqual("bd", pgh->core_paragraph->depends[0].name().c_str());
- Assert::AreEqual(size_t(1), pgh->core_paragraph->default_features.size());
- Assert::AreEqual("df", pgh->core_paragraph->default_features[0].c_str());
- Assert::AreEqual(size_t(1), pgh->core_paragraph->supports.size());
- Assert::AreEqual("x64", pgh->core_paragraph->supports[0].c_str());
- }
-
- TEST_METHOD(SourceParagraph_Two_Depends)
- {
- 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"},
- }});
- Assert::IsTrue(m_pgh.has_value());
- auto& pgh = *m_pgh.get();
-
- Assert::AreEqual(size_t(2), pgh->core_paragraph->depends.size());
- Assert::AreEqual("z", pgh->core_paragraph->depends[0].name().c_str());
- Assert::AreEqual("openssl", pgh->core_paragraph->depends[1].name().c_str());
- }
-
- TEST_METHOD(SourceParagraph_Three_Depends)
- {
- 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"},
- }});
- Assert::IsTrue(m_pgh.has_value());
- auto& pgh = *m_pgh.get();
-
- Assert::AreEqual(size_t(3), pgh->core_paragraph->depends.size());
- Assert::AreEqual("z", pgh->core_paragraph->depends[0].name().c_str());
- Assert::AreEqual("openssl", pgh->core_paragraph->depends[1].name().c_str());
- Assert::AreEqual("xyz", pgh->core_paragraph->depends[2].name().c_str());
- }
-
- TEST_METHOD(SourceParagraph_Three_Supports)
- {
- 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"},
- }});
- Assert::IsTrue(m_pgh.has_value());
- auto& pgh = *m_pgh.get();
-
- Assert::AreEqual(size_t(3), pgh->core_paragraph->supports.size());
- Assert::AreEqual("x64", pgh->core_paragraph->supports[0].c_str());
- Assert::AreEqual("windows", pgh->core_paragraph->supports[1].c_str());
- Assert::AreEqual("uwp", pgh->core_paragraph->supports[2].c_str());
- }
-
- TEST_METHOD(SourceParagraph_Construct_Qualified_Depends)
- {
- 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)"},
- }});
- Assert::IsTrue(m_pgh.has_value());
- auto& pgh = *m_pgh.get();
-
- Assert::AreEqual("zlib", pgh->core_paragraph->name.c_str());
- Assert::AreEqual("1.2.8", pgh->core_paragraph->version.c_str());
- Assert::AreEqual("", pgh->core_paragraph->maintainer.c_str());
- Assert::AreEqual("", pgh->core_paragraph->description.c_str());
- Assert::AreEqual(size_t(2), pgh->core_paragraph->depends.size());
- Assert::AreEqual("libA", pgh->core_paragraph->depends[0].name().c_str());
- Assert::AreEqual("windows", pgh->core_paragraph->depends[0].qualifier.c_str());
- Assert::AreEqual("libB", pgh->core_paragraph->depends[1].name().c_str());
- Assert::AreEqual("uwp", pgh->core_paragraph->depends[1].qualifier.c_str());
- }
-
- TEST_METHOD(SourceParagraph_Default_Features)
- {
- 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"},
- }});
- Assert::IsTrue(m_pgh.has_value());
- auto& pgh = *m_pgh.get();
-
- Assert::AreEqual(size_t(1), pgh->core_paragraph->default_features.size());
- Assert::AreEqual("a1", pgh->core_paragraph->default_features[0].c_str());
- }
-
- TEST_METHOD(BinaryParagraph_Construct_Minimum)
- {
- vcpkg::BinaryParagraph pgh({
- {"Package", "zlib"},
- {"Version", "1.2.8"},
- {"Architecture", "x86-windows"},
- {"Multi-Arch", "same"},
- });
-
- Assert::AreEqual("zlib", pgh.spec.name().c_str());
- Assert::AreEqual("1.2.8", pgh.version.c_str());
- Assert::AreEqual("", pgh.maintainer.c_str());
- Assert::AreEqual("", pgh.description.c_str());
- Assert::AreEqual("x86-windows", pgh.spec.triplet().canonical_name().c_str());
- Assert::AreEqual(size_t(0), pgh.depends.size());
- }
-
- TEST_METHOD(BinaryParagraph_Construct_Maximum)
- {
- vcpkg::BinaryParagraph pgh({
- {"Package", "s"},
- {"Version", "v"},
- {"Architecture", "x86-windows"},
- {"Multi-Arch", "same"},
- {"Maintainer", "m"},
- {"Description", "d"},
- {"Depends", "bd"},
- });
- Assert::AreEqual("s", pgh.spec.name().c_str());
- Assert::AreEqual("v", pgh.version.c_str());
- Assert::AreEqual("m", pgh.maintainer.c_str());
- Assert::AreEqual("d", pgh.description.c_str());
- Assert::AreEqual(size_t(1), pgh.depends.size());
- Assert::AreEqual("bd", pgh.depends[0].c_str());
- }
-
- TEST_METHOD(BinaryParagraph_Three_Depends)
- {
- vcpkg::BinaryParagraph pgh({
- {"Package", "zlib"},
- {"Version", "1.2.8"},
- {"Architecture", "x86-windows"},
- {"Multi-Arch", "same"},
- {"Depends", "a, b, c"},
- });
-
- Assert::AreEqual(size_t(3), pgh.depends.size());
- Assert::AreEqual("a", pgh.depends[0].c_str());
- Assert::AreEqual("b", pgh.depends[1].c_str());
- Assert::AreEqual("c", pgh.depends[2].c_str());
- }
-
- TEST_METHOD(BinaryParagraph_Abi)
- {
- vcpkg::BinaryParagraph pgh({
- {"Package", "zlib"},
- {"Version", "1.2.8"},
- {"Architecture", "x86-windows"},
- {"Multi-Arch", "same"},
- {"Abi", "abcd123"},
- });
-
- Assert::AreEqual(size_t(0), pgh.depends.size());
- Assert::IsTrue(pgh.abi == "abcd123");
- }
-
- TEST_METHOD(BinaryParagraph_Default_Features)
- {
- vcpkg::BinaryParagraph pgh({
- {"Package", "a"},
- {"Version", "1.0"},
- {"Architecture", "x86-windows"},
- {"Multi-Arch", "same"},
- {"Default-Features", "a1"},
- });
-
- Assert::AreEqual(size_t(0), pgh.depends.size());
- Assert::AreEqual(size_t(1), pgh.default_features.size());
- Assert::IsTrue(pgh.default_features[0] == "a1");
- }
-
- TEST_METHOD(parse_paragraphs_empty)
- {
- const char* str = "";
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO);
- Assert::IsTrue(pghs.empty());
- }
-
- TEST_METHOD(parse_paragraphs_one_field)
- {
- const char* str = "f1: v1";
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO);
- Assert::AreEqual(size_t(1), pghs.size());
- Assert::AreEqual(size_t(1), pghs[0].size());
- Assert::AreEqual("v1", pghs[0]["f1"].c_str());
- }
-
- TEST_METHOD(parse_paragraphs_one_pgh)
- {
- const char* str = "f1: v1\n"
- "f2: v2";
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO);
- Assert::AreEqual(size_t(1), pghs.size());
- Assert::AreEqual(size_t(2), pghs[0].size());
- Assert::AreEqual("v1", pghs[0]["f1"].c_str());
- Assert::AreEqual("v2", pghs[0]["f2"].c_str());
- }
-
- TEST_METHOD(parse_paragraphs_two_pgh)
- {
- const char* str = "f1: v1\n"
- "f2: v2\n"
- "\n"
- "f3: v3\n"
- "f4: v4";
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO);
- Assert::AreEqual(size_t(2), pghs.size());
- Assert::AreEqual(size_t(2), pghs[0].size());
- Assert::AreEqual("v1", pghs[0]["f1"].c_str());
- Assert::AreEqual("v2", pghs[0]["f2"].c_str());
- Assert::AreEqual(size_t(2), pghs[1].size());
- Assert::AreEqual("v3", pghs[1]["f3"].c_str());
- Assert::AreEqual("v4", pghs[1]["f4"].c_str());
- }
-
- TEST_METHOD(parse_paragraphs_field_names)
- {
- const char* str = "1:\n"
- "f:\n"
- "F:\n"
- "0:\n"
- "F-2:\n";
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO);
- Assert::AreEqual(size_t(1), pghs.size());
- Assert::AreEqual(size_t(5), pghs[0].size());
- }
-
- TEST_METHOD(parse_paragraphs_multiple_blank_lines)
- {
- const char* str = "f1: v1\n"
- "f2: v2\n"
- "\n"
- "\n"
- "f3: v3\n"
- "f4: v4";
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO);
- Assert::AreEqual(size_t(2), pghs.size());
- }
-
- TEST_METHOD(parse_paragraphs_empty_fields)
- {
- const char* str = "f1:\n"
- "f2: ";
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO);
- Assert::AreEqual(size_t(1), pghs.size());
- Assert::AreEqual(size_t(2), pghs[0].size());
- Assert::AreEqual("", pghs[0]["f1"].c_str());
- Assert::AreEqual("", pghs[0]["f2"].c_str());
- Assert::AreEqual(size_t(2), pghs[0].size());
- }
-
- TEST_METHOD(parse_paragraphs_multiline_fields)
- {
- const char* str = "f1: simple\n"
- " f1\r\n"
- "f2:\r\n"
- " f2\r\n"
- " continue\r\n";
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO);
- Assert::AreEqual(size_t(1), pghs.size());
- Assert::AreEqual("simple\n f1", pghs[0]["f1"].c_str());
- Assert::AreEqual("\n f2\n continue", pghs[0]["f2"].c_str());
- }
-
- TEST_METHOD(parse_paragraphs_crlfs)
- {
- const char* str = "f1: v1\r\n"
- "f2: v2\r\n"
- "\r\n"
- "f3: v3\r\n"
- "f4: v4";
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO);
- Assert::AreEqual(size_t(2), pghs.size());
- Assert::AreEqual(size_t(2), pghs[0].size());
- Assert::AreEqual("v1", pghs[0]["f1"].c_str());
- Assert::AreEqual("v2", pghs[0]["f2"].c_str());
- Assert::AreEqual(size_t(2), pghs[1].size());
- Assert::AreEqual("v3", pghs[1]["f3"].c_str());
- Assert::AreEqual("v4", pghs[1]["f4"].c_str());
- }
-
- TEST_METHOD(parse_paragraphs_comment)
- {
- const char* str = "f1: v1\r\n"
- "#comment\r\n"
- "f2: v2\r\n"
- "#comment\r\n"
- "\r\n"
- "#comment\r\n"
- "f3: v3\r\n"
- "#comment\r\n"
- "f4: v4";
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO);
- Assert::AreEqual(size_t(2), pghs.size());
- Assert::AreEqual(size_t(2), pghs[0].size());
- Assert::AreEqual("v1", pghs[0]["f1"].c_str());
- Assert::AreEqual("v2", pghs[0]["f2"].c_str());
- Assert::AreEqual(size_t(2), pghs[1].size());
- Assert::AreEqual("v3", pghs[1]["f3"].c_str());
- Assert::AreEqual("v4", pghs[1]["f4"].c_str());
- }
-
- TEST_METHOD(parse_comment_before_single_slashN)
- {
- const char* str = "f1: v1\r\n"
- "#comment\n";
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO);
- Assert::AreEqual(size_t(1), pghs[0].size());
- Assert::AreEqual("v1", pghs[0]["f1"].c_str());
- }
-
- TEST_METHOD(BinaryParagraph_serialize_min)
- {
- vcpkg::BinaryParagraph pgh({
- {"Package", "zlib"},
- {"Version", "1.2.8"},
- {"Architecture", "x86-windows"},
- {"Multi-Arch", "same"},
- });
- std::string ss = Strings::serialize(pgh);
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss).value_or_exit(VCPKG_LINE_INFO);
- Assert::AreEqual(size_t(1), pghs.size());
- Assert::AreEqual(size_t(4), pghs[0].size());
- Assert::AreEqual("zlib", pghs[0]["Package"].c_str());
- Assert::AreEqual("1.2.8", pghs[0]["Version"].c_str());
- Assert::AreEqual("x86-windows", pghs[0]["Architecture"].c_str());
- Assert::AreEqual("same", pghs[0]["Multi-Arch"].c_str());
- }
-
- TEST_METHOD(BinaryParagraph_serialize_max)
- {
- vcpkg::BinaryParagraph pgh({
- {"Package", "zlib"},
- {"Version", "1.2.8"},
- {"Architecture", "x86-windows"},
- {"Description", "first line\n second line"},
- {"Maintainer", "abc <abc@abc.abc>"},
- {"Depends", "dep"},
- {"Multi-Arch", "same"},
- });
- std::string ss = Strings::serialize(pgh);
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss).value_or_exit(VCPKG_LINE_INFO);
- Assert::AreEqual(size_t(1), pghs.size());
- Assert::AreEqual(size_t(7), pghs[0].size());
- Assert::AreEqual("zlib", pghs[0]["Package"].c_str());
- Assert::AreEqual("1.2.8", pghs[0]["Version"].c_str());
- Assert::AreEqual("x86-windows", pghs[0]["Architecture"].c_str());
- Assert::AreEqual("same", pghs[0]["Multi-Arch"].c_str());
- Assert::AreEqual("first line\n second line", pghs[0]["Description"].c_str());
- Assert::AreEqual("dep", pghs[0]["Depends"].c_str());
- }
-
- TEST_METHOD(BinaryParagraph_serialize_multiple_deps)
- {
- vcpkg::BinaryParagraph pgh({
- {"Package", "zlib"},
- {"Version", "1.2.8"},
- {"Architecture", "x86-windows"},
- {"Multi-Arch", "same"},
- {"Depends", "a, b, c"},
- });
- std::string ss = Strings::serialize(pgh);
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss).value_or_exit(VCPKG_LINE_INFO);
- Assert::AreEqual(size_t(1), pghs.size());
- Assert::AreEqual("a, b, c", pghs[0]["Depends"].c_str());
- }
-
- TEST_METHOD(BinaryParagraph_serialize_abi)
- {
- vcpkg::BinaryParagraph pgh({
- {"Package", "zlib"},
- {"Version", "1.2.8"},
- {"Architecture", "x86-windows"},
- {"Multi-Arch", "same"},
- {"Depends", "a, b, c"},
- {"Abi", "123abc"},
- });
- std::string ss = Strings::serialize(pgh);
- auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss).value_or_exit(VCPKG_LINE_INFO);
- Assert::AreEqual(size_t(1), pghs.size());
- Assert::AreEqual("123abc", pghs[0]["Abi"].c_str());
- }
- };
-}
diff --git a/toolsrc/src/tests.pch.cpp b/toolsrc/src/tests.pch.cpp
deleted file mode 100644
index bdddab76a..000000000
--- a/toolsrc/src/tests.pch.cpp
+++ /dev/null
@@ -1 +0,0 @@
-#include "tests.pch.h"
diff --git a/toolsrc/src/tests.plan.cpp b/toolsrc/src/tests.plan.cpp
deleted file mode 100644
index 9d8717878..000000000
--- a/toolsrc/src/tests.plan.cpp
+++ /dev/null
@@ -1,1261 +0,0 @@
-#include "tests.pch.h"
-
-#include <tests.utils.h>
-
-using namespace Microsoft::VisualStudio::CppUnitTestFramework;
-
-using namespace vcpkg;
-
-namespace UnitTest1
-{
- 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));
- Assert::IsTrue(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,
- std::string pkg_name,
- std::vector<std::string> vec,
- const Triplet& triplet = Triplet::X86_WINDOWS)
- {
- Assert::IsTrue(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;
-
- Assert::AreEqual(plan.spec.triplet().to_string().c_str(), triplet.to_string().c_str());
-
- auto* scfl = plan.source_control_file_location.get();
- Assert::AreEqual(pkg_name.c_str(), scfl->source_control_file->core_paragraph->name.c_str());
- Assert::AreEqual(size_t(vec.size()), feature_list.size());
-
- for (auto&& feature_name : vec)
- {
- if (feature_name == "core" || feature_name == "")
- {
- Assert::IsTrue(Util::find(feature_list, "core") != feature_list.end() ||
- Util::find(feature_list, "") != feature_list.end());
- continue;
- }
- Assert::IsTrue(Util::find(feature_list, feature_name) != feature_list.end());
- }
- }
-
- /// <summary>
- /// Assert that the given action is a remove of given package.
- /// </summary>
- static void remove_plan_check(Dependencies::AnyAction* remove_action,
- std::string pkg_name,
- const Triplet& triplet = Triplet::X86_WINDOWS)
- {
- const auto& plan = remove_action->remove_action.value_or_exit(VCPKG_LINE_INFO);
- Assert::AreEqual(plan.spec.triplet().to_string().c_str(), triplet.to_string().c_str());
- Assert::AreEqual(pkg_name.c_str(), plan.spec.name().c_str());
- }
-
- /// <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);
- Assert::IsTrue(spec.has_value());
- map.emplace(scfl.source_control_file->core_paragraph->name, std::move(scfl));
- return PackageSpec{*spec.get()};
- }
- };
-
- class InstallPlanTests : public TestClass<InstallPlanTests>
- {
- TEST_METHOD(basic_install_scheme)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
-
- PackageSpecMap spec_map;
- auto spec_a = spec_map.emplace("a", "b");
- auto spec_b = spec_map.emplace("b", "c");
- auto spec_c = spec_map.emplace("c");
-
- 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)));
-
- Assert::AreEqual(size_t(3), install_plan.size());
- Assert::AreEqual("c", install_plan[0].spec().name().c_str());
- Assert::AreEqual("b", install_plan[1].spec().name().c_str());
- Assert::AreEqual("a", install_plan[2].spec().name().c_str());
- }
-
- TEST_METHOD(multiple_install_scheme)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
-
- PackageSpecMap spec_map;
- auto spec_a = spec_map.emplace("a", "d");
- auto spec_b = spec_map.emplace("b", "d, e");
- auto spec_c = spec_map.emplace("c", "e, h");
- auto spec_d = spec_map.emplace("d", "f, g, h");
- auto spec_e = spec_map.emplace("e", "g");
- auto spec_f = spec_map.emplace("f");
- auto spec_g = spec_map.emplace("g");
- auto spec_h = spec_map.emplace("h");
-
- Dependencies::MapPortFileProvider map_port(spec_map.map);
- auto install_plan = Dependencies::create_feature_install_plan(
- map_port,
- {FeatureSpec{spec_a, ""}, FeatureSpec{spec_b, ""}, FeatureSpec{spec_c, ""}},
- StatusParagraphs(std::move(status_paragraphs)));
-
- auto iterator_pos = [&](const PackageSpec& spec) -> int {
- auto it = std::find_if(
- install_plan.begin(), install_plan.end(), [&](auto& action) { return action.spec() == spec; });
- Assert::IsTrue(it != install_plan.end());
- return (int)(it - install_plan.begin());
- };
-
- int a_pos = iterator_pos(spec_a), b_pos = iterator_pos(spec_b), c_pos = iterator_pos(spec_c),
- d_pos = iterator_pos(spec_d), e_pos = iterator_pos(spec_e), f_pos = iterator_pos(spec_f),
- g_pos = iterator_pos(spec_g), h_pos = iterator_pos(spec_h);
-
- Assert::IsTrue(a_pos > d_pos);
- Assert::IsTrue(b_pos > e_pos);
- Assert::IsTrue(b_pos > d_pos);
- Assert::IsTrue(c_pos > e_pos);
- Assert::IsTrue(c_pos > h_pos);
- Assert::IsTrue(d_pos > f_pos);
- Assert::IsTrue(d_pos > g_pos);
- Assert::IsTrue(d_pos > h_pos);
- Assert::IsTrue(e_pos > g_pos);
- }
-
- TEST_METHOD(existing_package_scheme)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
- status_paragraphs.push_back(make_status_pgh("a"));
-
- PackageSpecMap spec_map;
- auto spec_a = FullPackageSpec{spec_map.emplace("a")};
-
- auto install_plan =
- Dependencies::create_feature_install_plan(spec_map.map,
- FullPackageSpec::to_feature_specs({spec_a}),
- StatusParagraphs(std::move(status_paragraphs)));
-
- Assert::AreEqual(size_t(1), install_plan.size());
- auto p = install_plan[0].install_action.get();
- Assert::IsNotNull(p);
- Assert::AreEqual("a", p->spec.name().c_str());
- Assert::AreEqual(Dependencies::InstallPlanType::ALREADY_INSTALLED, p->plan_type);
- Assert::AreEqual(Dependencies::RequestType::USER_REQUESTED, p->request_type);
- }
-
- TEST_METHOD(user_requested_package_scheme)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
-
- PackageSpecMap spec_map;
- auto spec_a = FullPackageSpec{spec_map.emplace("a", "b")};
- auto spec_b = FullPackageSpec{spec_map.emplace("b")};
-
- auto install_plan =
- Dependencies::create_feature_install_plan(spec_map.map,
- FullPackageSpec::to_feature_specs({spec_a}),
- StatusParagraphs(std::move(status_paragraphs)));
-
- Assert::AreEqual(size_t(2), install_plan.size());
- auto p = install_plan[0].install_action.get();
- Assert::IsNotNull(p);
- Assert::AreEqual("b", p->spec.name().c_str());
- Assert::AreEqual(Dependencies::InstallPlanType::BUILD_AND_INSTALL, p->plan_type);
- Assert::AreEqual(Dependencies::RequestType::AUTO_SELECTED, p->request_type);
-
- auto p2 = install_plan[1].install_action.get();
- Assert::IsNotNull(p2);
- Assert::AreEqual("a", p2->spec.name().c_str());
- Assert::AreEqual(Dependencies::InstallPlanType::BUILD_AND_INSTALL, p2->plan_type);
- Assert::AreEqual(Dependencies::RequestType::USER_REQUESTED, p2->request_type);
- }
-
- TEST_METHOD(long_install_scheme)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
- status_paragraphs.push_back(make_status_pgh("j", "k"));
- status_paragraphs.push_back(make_status_pgh("k"));
-
- PackageSpecMap spec_map;
-
- auto spec_a = spec_map.emplace("a", "b, c, d, e, f, g, h, j, k");
- auto spec_b = spec_map.emplace("b", "c, d, e, f, g, h, j, k");
- auto spec_c = spec_map.emplace("c", "d, e, f, g, h, j, k");
- auto spec_d = spec_map.emplace("d", "e, f, g, h, j, k");
- auto spec_e = spec_map.emplace("e", "f, g, h, j, k");
- auto spec_f = spec_map.emplace("f", "g, h, j, k");
- auto spec_g = spec_map.emplace("g", "h, j, k");
- auto spec_h = spec_map.emplace("h", "j, k");
- auto spec_j = spec_map.emplace("j", "k");
- auto spec_k = spec_map.emplace("k");
-
- Dependencies::MapPortFileProvider map_port(spec_map.map);
- auto install_plan = Dependencies::create_feature_install_plan(
- map_port, {FeatureSpec{spec_a, ""}}, StatusParagraphs(std::move(status_paragraphs)));
-
- Assert::AreEqual(size_t(8), install_plan.size());
- Assert::AreEqual("h", install_plan[0].spec().name().c_str());
- Assert::AreEqual("g", install_plan[1].spec().name().c_str());
- Assert::AreEqual("f", install_plan[2].spec().name().c_str());
- Assert::AreEqual("e", install_plan[3].spec().name().c_str());
- Assert::AreEqual("d", install_plan[4].spec().name().c_str());
- Assert::AreEqual("c", install_plan[5].spec().name().c_str());
- Assert::AreEqual("b", install_plan[6].spec().name().c_str());
- Assert::AreEqual("a", install_plan[7].spec().name().c_str());
- }
-
- TEST_METHOD(basic_feature_test_1)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
- status_paragraphs.push_back(make_status_pgh("a", "b, b[b1]"));
- status_paragraphs.push_back(make_status_pgh("b"));
- status_paragraphs.push_back(make_status_feature_pgh("b", "b1"));
-
- PackageSpecMap spec_map;
- 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)));
-
- Assert::AreEqual(size_t(4), install_plan.size());
- remove_plan_check(&install_plan[0], "a");
- remove_plan_check(&install_plan[1], "b");
- features_check(&install_plan[2], "b", {"b1", "core", "b1"});
- features_check(&install_plan[3], "a", {"a1", "core"});
- }
-
- TEST_METHOD(basic_feature_test_2)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
-
- PackageSpecMap spec_map;
-
- 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)));
-
- Assert::AreEqual(size_t(2), install_plan.size());
- features_check(&install_plan[0], "b", {"b1", "b2", "core"});
- features_check(&install_plan[1], "a", {"a1", "core"});
- }
-
- TEST_METHOD(basic_feature_test_3)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
- status_paragraphs.push_back(make_status_pgh("a"));
-
- PackageSpecMap spec_map;
-
- auto spec_a = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}}), {"core"}};
- auto spec_b = FullPackageSpec{spec_map.emplace("b")};
- auto spec_c = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}};
-
- auto install_plan =
- Dependencies::create_feature_install_plan(spec_map.map,
- FullPackageSpec::to_feature_specs({spec_c, spec_a}),
- StatusParagraphs(std::move(status_paragraphs)));
-
- Assert::AreEqual(size_t(4), install_plan.size());
- remove_plan_check(&install_plan[0], "a");
- features_check(&install_plan[1], "b", {"core"});
- features_check(&install_plan[2], "a", {"a1", "core"});
- features_check(&install_plan[3], "c", {"core"});
- }
-
- TEST_METHOD(basic_feature_test_4)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
- status_paragraphs.push_back(make_status_pgh("a"));
- status_paragraphs.push_back(make_status_feature_pgh("a", "a1", ""));
-
- PackageSpecMap spec_map;
-
- auto spec_a = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}})};
- auto spec_b = FullPackageSpec{spec_map.emplace("b")};
- auto spec_c = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}};
-
- auto install_plan =
- Dependencies::create_feature_install_plan(spec_map.map,
- FullPackageSpec::to_feature_specs({spec_c}),
- StatusParagraphs(std::move(status_paragraphs)));
-
- Assert::AreEqual(size_t(1), install_plan.size());
- features_check(&install_plan[0], "c", {"core"});
- }
-
- TEST_METHOD(basic_feature_test_5)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
-
- PackageSpecMap spec_map;
-
- auto spec_a =
- FullPackageSpec{spec_map.emplace("a", "", {{"a1", "b[b1]"}, {"a2", "b[b2]"}, {"a3", "a[a2]"}}), {"a3"}};
- auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}, {"b2", ""}})};
-
- auto install_plan =
- Dependencies::create_feature_install_plan(spec_map.map,
- FullPackageSpec::to_feature_specs({spec_a}),
- StatusParagraphs(std::move(status_paragraphs)));
-
- Assert::AreEqual(size_t(2), install_plan.size());
- features_check(&install_plan[0], "b", {"core", "b2"});
- features_check(&install_plan[1], "a", {"core", "a3", "a2"});
- }
-
- TEST_METHOD(basic_feature_test_6)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
- status_paragraphs.push_back(make_status_pgh("b"));
-
- PackageSpecMap spec_map;
- 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)));
-
- Assert::AreEqual(size_t(3), install_plan.size());
- remove_plan_check(&install_plan[0], "b");
- features_check(&install_plan[1], "b", {"core", "b1"});
- features_check(&install_plan[2], "a", {"core"});
- }
-
- TEST_METHOD(basic_feature_test_7)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
- status_paragraphs.push_back(make_status_pgh("x", "b"));
- status_paragraphs.push_back(make_status_pgh("b"));
-
- PackageSpecMap spec_map;
-
- auto spec_a = FullPackageSpec{spec_map.emplace("a")};
- auto spec_x = FullPackageSpec{spec_map.emplace("x", "a"), {"core"}};
- auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}}), {"b1"}};
-
- auto install_plan =
- Dependencies::create_feature_install_plan(spec_map.map,
- FullPackageSpec::to_feature_specs({spec_b}),
- StatusParagraphs(std::move(status_paragraphs)));
-
- Assert::AreEqual(size_t(5), install_plan.size());
- remove_plan_check(&install_plan[0], "x");
- remove_plan_check(&install_plan[1], "b");
-
- // TODO: order here may change but A < X, and B anywhere
- features_check(&install_plan[2], "b", {"core", "b1"});
- features_check(&install_plan[3], "a", {"core"});
- features_check(&install_plan[4], "x", {"core"});
- }
-
- TEST_METHOD(basic_feature_test_8)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
- status_paragraphs.push_back(make_status_pgh("a"));
- status_paragraphs.push_back(make_status_pgh("a"));
- status_paragraphs.back()->package.spec =
- PackageSpec::from_name_and_triplet("a", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO);
-
- PackageSpecMap spec_map(Triplet::X64_WINDOWS);
- auto spec_a_64 = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}}), {"core"}};
- auto spec_b_64 = FullPackageSpec{spec_map.emplace("b")};
- auto spec_c_64 = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}};
-
- spec_map.triplet = Triplet::X86_WINDOWS;
- auto spec_a_86 = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}}), {"core"}};
- auto spec_b_86 = FullPackageSpec{spec_map.emplace("b")};
- auto spec_c_86 = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}};
-
- auto install_plan = Dependencies::create_feature_install_plan(
- spec_map.map,
- FullPackageSpec::to_feature_specs({spec_c_64, spec_a_86, spec_a_64, spec_c_86}),
- StatusParagraphs(std::move(status_paragraphs)));
-
- remove_plan_check(&install_plan[0], "a", Triplet::X64_WINDOWS);
- remove_plan_check(&install_plan[1], "a");
- features_check(&install_plan[2], "b", {"core"}, Triplet::X64_WINDOWS);
- features_check(&install_plan[3], "a", {"a1", "core"}, Triplet::X64_WINDOWS);
- features_check(&install_plan[4], "c", {"core"}, Triplet::X64_WINDOWS);
- features_check(&install_plan[5], "b", {"core"});
- features_check(&install_plan[6], "a", {"a1", "core"});
- features_check(&install_plan[7], "c", {"core"});
- }
-
- TEST_METHOD(install_all_features_test)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
-
- PackageSpecMap spec_map(Triplet::X64_WINDOWS);
- auto spec_a_64 = FullPackageSpec{spec_map.emplace("a", "", {{"0", ""}, {"1", ""}}), {"core"}};
-
- auto install_specs = FullPackageSpec::from_string("a[*]", Triplet::X64_WINDOWS);
- Assert::IsTrue(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)));
-
- Assert::IsTrue(install_plan.size() == 1);
- features_check(&install_plan[0], "a", {"0", "1", "core"}, Triplet::X64_WINDOWS);
- }
-
- TEST_METHOD(install_default_features_test_1)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
-
- // Add a port "a" with default features "1" and features "0" and "1".
- PackageSpecMap spec_map(Triplet::X64_WINDOWS);
- spec_map.emplace("a", "", {{"0", ""}, {"1", ""}}, {"1"});
-
- // 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)));
-
- // Expect the default feature "1" to be installed, but not "0"
- Assert::IsTrue(install_plan.size() == 1);
- features_check(&install_plan[0], "a", {"1", "core"}, Triplet::X64_WINDOWS);
- }
-
- TEST_METHOD(install_default_features_test_2)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
- status_paragraphs.push_back(make_status_pgh("a"));
- status_paragraphs.back()->package.spec =
- PackageSpec::from_name_and_triplet("a", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO);
-
- // Add a port "a" of which "core" is already installed, but we will
- // install the default features "explicitly"
- // "a" has two features, of which "a1" is default.
- PackageSpecMap spec_map(Triplet::X64_WINDOWS);
- spec_map.emplace("a", "", {{"a0", ""}, {"a1", ""}}, {"a1"});
-
- // 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)));
-
- // Expect "a" to get removed for rebuild and then installed with default
- // features.
- Assert::IsTrue(install_plan.size() == 2);
- remove_plan_check(&install_plan[0], "a", Triplet::X64_WINDOWS);
- features_check(&install_plan[1], "a", {"a1", "core"}, Triplet::X64_WINDOWS);
- }
-
- TEST_METHOD(install_default_features_test_3)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
-
- // "a" has two features, of which "a1" is default.
- PackageSpecMap spec_map(Triplet::X64_WINDOWS);
- spec_map.emplace("a", "", {{"a0", ""}, {"a1", ""}}, {"a1"});
-
- // 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)));
-
- // Expect the default feature not to get installed.
- Assert::IsTrue(install_plan.size() == 1);
- features_check(&install_plan[0], "a", {"core"}, Triplet::X64_WINDOWS);
- }
-
- TEST_METHOD(install_default_features_of_dependency_test_1)
- {
- 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 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)));
-
- // Expect "a" to get installed and defaults of "b" through the dependency,
- // as no explicit features of "b" are installed by the user.
- Assert::IsTrue(install_plan.size() == 2);
- features_check(&install_plan[0], "b", {"b1", "core"}, Triplet::X64_WINDOWS);
- features_check(&install_plan[1], "a", {"core"}, Triplet::X64_WINDOWS);
- }
-
- TEST_METHOD(do_not_install_default_features_of_existing_dependency)
- {
- // 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"});
-
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
- // "b[core]" is already installed
- status_paragraphs.push_back(make_status_pgh("b"));
- status_paragraphs.back()->package.spec =
- PackageSpec::from_name_and_triplet("b", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO);
-
- // 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)));
-
- // Expect "a" to get installed, but not require rebuilding "b"
- Assert::IsTrue(install_plan.size() == 1);
- features_check(&install_plan[0], "a", {"core"}, Triplet::X64_WINDOWS);
- }
-
- TEST_METHOD(install_default_features_of_dependency_test_2)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
- status_paragraphs.push_back(make_status_pgh("b"));
- status_paragraphs.back()->package.spec =
- PackageSpec::from_name_and_triplet("b", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO);
-
- // Add a port "a" which depends on the core of "b", which was already
- // installed explicitly
- 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 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)));
-
- // Expect "a" to get installed, not the defaults of "b", as the required
- // dependencies are already there, installed explicitly by the user.
- Assert::IsTrue(install_plan.size() == 1);
- features_check(&install_plan[0], "a", {"core"}, Triplet::X64_WINDOWS);
- }
-
- TEST_METHOD(install_plan_action_dependencies)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
-
- // Add a port "a" which depends on the core of "b", which was already
- // installed explicitly
- PackageSpecMap spec_map(Triplet::X64_WINDOWS);
- auto spec_c = spec_map.emplace("c");
- auto spec_b = spec_map.emplace("b", "c");
- spec_map.emplace("a", "b");
-
- // 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)));
-
- Assert::IsTrue(install_plan.size() == 3);
- features_check(&install_plan[0], "c", {"core"}, Triplet::X64_WINDOWS);
-
- features_check(&install_plan[1], "b", {"core"}, Triplet::X64_WINDOWS);
- Assert::IsTrue(install_plan[1].install_action.get()->computed_dependencies ==
- std::vector<PackageSpec>{spec_c});
-
- features_check(&install_plan[2], "a", {"core"}, Triplet::X64_WINDOWS);
- Assert::IsTrue(install_plan[2].install_action.get()->computed_dependencies ==
- std::vector<PackageSpec>{spec_b});
- }
-
- TEST_METHOD(install_plan_action_dependencies_2)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
-
- // Add a port "a" which depends on the core of "b", which was already
- // installed explicitly
- PackageSpecMap spec_map(Triplet::X64_WINDOWS);
- auto spec_c = spec_map.emplace("c");
- auto spec_b = spec_map.emplace("b", "c");
- spec_map.emplace("a", "c, b");
-
- // 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)));
-
- Assert::IsTrue(install_plan.size() == 3);
- features_check(&install_plan[0], "c", {"core"}, Triplet::X64_WINDOWS);
-
- features_check(&install_plan[1], "b", {"core"}, Triplet::X64_WINDOWS);
- Assert::IsTrue(install_plan[1].install_action.get()->computed_dependencies ==
- std::vector<PackageSpec>{spec_c});
-
- features_check(&install_plan[2], "a", {"core"}, Triplet::X64_WINDOWS);
- Assert::IsTrue(install_plan[2].install_action.get()->computed_dependencies ==
- std::vector<PackageSpec>{spec_b, spec_c});
- }
-
- TEST_METHOD(install_plan_action_dependencies_3)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
-
- // Add a port "a" which depends on the core of "b", which was already
- // installed explicitly
- PackageSpecMap spec_map(Triplet::X64_WINDOWS);
- spec_map.emplace("a", "", {{"0", ""}, {"1", "a[0]"}}, {"1"});
-
- // 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)));
-
- Assert::IsTrue(install_plan.size() == 1);
- features_check(&install_plan[0], "a", {"1", "0", "core"}, Triplet::X64_WINDOWS);
- Assert::IsTrue(install_plan[0].install_action.get()->computed_dependencies == std::vector<PackageSpec>{});
- }
-
- TEST_METHOD(install_with_default_features)
- {
- std::vector<std::unique_ptr<StatusParagraph>> pghs;
- pghs.push_back(make_status_pgh("a", ""));
- StatusParagraphs status_db(std::move(pghs));
-
- PackageSpecMap spec_map;
- 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);
-
- Assert::IsTrue(install_plan.size() == 3);
- remove_plan_check(&install_plan[0], "a");
- features_check(&install_plan[1], "b", {"core"});
- features_check(&install_plan[2], "a", {"0", "core"});
- }
-
- TEST_METHOD(upgrade_with_default_features_1)
- {
- std::vector<std::unique_ptr<StatusParagraph>> pghs;
- pghs.push_back(make_status_pgh("a", "", "1"));
- pghs.push_back(make_status_feature_pgh("a", "0"));
- StatusParagraphs status_db(std::move(pghs));
-
- // Add a port "a" of which "core" and "0" are already installed.
- 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();
-
- // The upgrade should not install the default feature
- Assert::AreEqual(size_t(2), plan.size());
-
- Assert::AreEqual("a", plan[0].spec().name().c_str());
- remove_plan_check(&plan[0], "a");
- features_check(&plan[1], "a", {"core", "0"});
- }
-
- TEST_METHOD(upgrade_with_default_features_2)
- {
- std::vector<std::unique_ptr<StatusParagraph>> pghs;
- // B is currently installed _without_ default feature b0
- pghs.push_back(make_status_pgh("b", "", "b0", "x64-windows"));
- pghs.push_back(make_status_pgh("a", "b[core]", "", "x64-windows"));
-
- StatusParagraphs status_db(std::move(pghs));
-
- PackageSpecMap spec_map(Triplet::X64_WINDOWS);
- 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();
-
- // The upgrade should install the new default feature b1 but not b0
- Assert::AreEqual(size_t(4), plan.size());
- remove_plan_check(&plan[0], "a", Triplet::X64_WINDOWS);
- remove_plan_check(&plan[1], "b", Triplet::X64_WINDOWS);
- features_check(&plan[2], "b", {"core", "b1"}, Triplet::X64_WINDOWS);
- features_check(&plan[3], "a", {"core"}, Triplet::X64_WINDOWS);
- }
-
- TEST_METHOD(upgrade_with_default_features_3)
- {
- std::vector<std::unique_ptr<StatusParagraph>> pghs;
- // note: unrelated package due to x86 triplet
- pghs.push_back(make_status_pgh("b", "", "", "x86-windows"));
- pghs.push_back(make_status_pgh("a", "", "", "x64-windows"));
-
- StatusParagraphs status_db(std::move(pghs));
-
- PackageSpecMap spec_map(Triplet::X64_WINDOWS);
- 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();
-
- // The upgrade should install the default feature
- Assert::AreEqual(size_t(3), plan.size());
- remove_plan_check(&plan[0], "a", Triplet::X64_WINDOWS);
- features_check(&plan[1], "b", {"b0", "core"}, Triplet::X64_WINDOWS);
- features_check(&plan[2], "a", {"core"}, Triplet::X64_WINDOWS);
- }
-
- TEST_METHOD(upgrade_with_new_default_feature)
- {
- std::vector<std::unique_ptr<StatusParagraph>> pghs;
- pghs.push_back(make_status_pgh("a", "", "0", "x86-windows"));
-
- StatusParagraphs status_db(std::move(pghs));
-
- 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();
-
- // The upgrade should install the new default feature but not the old default feature 0
- Assert::AreEqual(size_t(2), plan.size());
- remove_plan_check(&plan[0], "a", Triplet::X86_WINDOWS);
- features_check(&plan[1], "a", {"core", "1"}, Triplet::X86_WINDOWS);
- }
-
- TEST_METHOD(transitive_features_test)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
-
- PackageSpecMap spec_map(Triplet::X64_WINDOWS);
- auto spec_a_64 = FullPackageSpec{spec_map.emplace("a", "b", {{"0", "b[0]"}}), {"core"}};
- auto spec_b_64 = FullPackageSpec{spec_map.emplace("b", "c", {{"0", "c[0]"}}), {"core"}};
- auto spec_c_64 = FullPackageSpec{spec_map.emplace("c", "", {{"0", ""}}), {"core"}};
-
- auto install_specs = FullPackageSpec::from_string("a[*]", Triplet::X64_WINDOWS);
- Assert::IsTrue(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)));
-
- Assert::IsTrue(install_plan.size() == 3);
- features_check(&install_plan[0], "c", {"0", "core"}, Triplet::X64_WINDOWS);
- features_check(&install_plan[1], "b", {"0", "core"}, Triplet::X64_WINDOWS);
- features_check(&install_plan[2], "a", {"0", "core"}, Triplet::X64_WINDOWS);
- }
-
- TEST_METHOD(no_transitive_features_test)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
-
- PackageSpecMap spec_map(Triplet::X64_WINDOWS);
- auto spec_a_64 = FullPackageSpec{spec_map.emplace("a", "b", {{"0", ""}}), {"core"}};
- auto spec_b_64 = FullPackageSpec{spec_map.emplace("b", "c", {{"0", ""}}), {"core"}};
- auto spec_c_64 = FullPackageSpec{spec_map.emplace("c", "", {{"0", ""}}), {"core"}};
-
- auto install_specs = FullPackageSpec::from_string("a[*]", Triplet::X64_WINDOWS);
- Assert::IsTrue(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)));
-
- Assert::IsTrue(install_plan.size() == 3);
- features_check(&install_plan[0], "c", {"core"}, Triplet::X64_WINDOWS);
- features_check(&install_plan[1], "b", {"core"}, Triplet::X64_WINDOWS);
- features_check(&install_plan[2], "a", {"0", "core"}, Triplet::X64_WINDOWS);
- }
-
- TEST_METHOD(only_transitive_features_test)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
-
- PackageSpecMap spec_map(Triplet::X64_WINDOWS);
- auto spec_a_64 = FullPackageSpec{spec_map.emplace("a", "", {{"0", "b[0]"}}), {"core"}};
- auto spec_b_64 = FullPackageSpec{spec_map.emplace("b", "", {{"0", "c[0]"}}), {"core"}};
- auto spec_c_64 = FullPackageSpec{spec_map.emplace("c", "", {{"0", ""}}), {"core"}};
-
- auto install_specs = FullPackageSpec::from_string("a[*]", Triplet::X64_WINDOWS);
- Assert::IsTrue(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)));
-
- Assert::IsTrue(install_plan.size() == 3);
- features_check(&install_plan[0], "c", {"0", "core"}, Triplet::X64_WINDOWS);
- features_check(&install_plan[1], "b", {"0", "core"}, Triplet::X64_WINDOWS);
- features_check(&install_plan[2], "a", {"0", "core"}, Triplet::X64_WINDOWS);
- }
- };
-
- class RemovePlanTests : public TestClass<RemovePlanTests>
- {
- TEST_METHOD(basic_remove_scheme)
- {
- std::vector<std::unique_ptr<StatusParagraph>> pghs;
- pghs.push_back(make_status_pgh("a"));
- StatusParagraphs status_db(std::move(pghs));
-
- auto remove_plan = Dependencies::create_remove_plan({unsafe_pspec("a")}, status_db);
-
- Assert::AreEqual(size_t(1), remove_plan.size());
- Assert::AreEqual("a", remove_plan[0].spec.name().c_str());
- }
-
- TEST_METHOD(recurse_remove_scheme)
- {
- std::vector<std::unique_ptr<StatusParagraph>> pghs;
- pghs.push_back(make_status_pgh("a"));
- pghs.push_back(make_status_pgh("b", "a"));
- StatusParagraphs status_db(std::move(pghs));
-
- auto remove_plan = Dependencies::create_remove_plan({unsafe_pspec("a")}, status_db);
-
- Assert::AreEqual(size_t(2), remove_plan.size());
- Assert::AreEqual("b", remove_plan[0].spec.name().c_str());
- Assert::AreEqual("a", remove_plan[1].spec.name().c_str());
- }
-
- TEST_METHOD(features_depend_remove_scheme)
- {
- std::vector<std::unique_ptr<StatusParagraph>> pghs;
- pghs.push_back(make_status_pgh("a"));
- pghs.push_back(make_status_pgh("b"));
- pghs.push_back(make_status_feature_pgh("b", "0", "a"));
- StatusParagraphs status_db(std::move(pghs));
-
- auto remove_plan = Dependencies::create_remove_plan({unsafe_pspec("a")}, status_db);
-
- Assert::AreEqual(size_t(2), remove_plan.size());
- Assert::AreEqual("b", remove_plan[0].spec.name().c_str());
- Assert::AreEqual("a", remove_plan[1].spec.name().c_str());
- }
-
- TEST_METHOD(features_depend_remove_scheme_once_removed)
- {
- std::vector<std::unique_ptr<StatusParagraph>> pghs;
- pghs.push_back(make_status_pgh("expat"));
- pghs.push_back(make_status_pgh("vtk", "expat"));
- pghs.push_back(make_status_pgh("opencv"));
- pghs.push_back(make_status_feature_pgh("opencv", "vtk", "vtk"));
- StatusParagraphs status_db(std::move(pghs));
-
- auto remove_plan = Dependencies::create_remove_plan({unsafe_pspec("expat")}, status_db);
-
- Assert::AreEqual(size_t(3), remove_plan.size());
- Assert::AreEqual("opencv", remove_plan[0].spec.name().c_str());
- Assert::AreEqual("vtk", remove_plan[1].spec.name().c_str());
- Assert::AreEqual("expat", remove_plan[2].spec.name().c_str());
- }
-
- TEST_METHOD(features_depend_remove_scheme_once_removed_x64)
- {
- std::vector<std::unique_ptr<StatusParagraph>> pghs;
- pghs.push_back(make_status_pgh("expat", "", "", "x64"));
- pghs.push_back(make_status_pgh("vtk", "expat", "", "x64"));
- pghs.push_back(make_status_pgh("opencv", "", "", "x64"));
- pghs.push_back(make_status_feature_pgh("opencv", "vtk", "vtk", "x64"));
- StatusParagraphs status_db(std::move(pghs));
-
- auto remove_plan = Dependencies::create_remove_plan(
- {unsafe_pspec("expat", Triplet::from_canonical_name("x64"))}, status_db);
-
- Assert::AreEqual(size_t(3), remove_plan.size());
- Assert::AreEqual("opencv", remove_plan[0].spec.name().c_str());
- Assert::AreEqual("vtk", remove_plan[1].spec.name().c_str());
- Assert::AreEqual("expat", remove_plan[2].spec.name().c_str());
- }
-
- TEST_METHOD(features_depend_core_remove_scheme)
- {
- std::vector<std::unique_ptr<StatusParagraph>> pghs;
- pghs.push_back(make_status_pgh("curl", "", "", "x64"));
- pghs.push_back(make_status_pgh("cpr", "curl[core]", "", "x64"));
- StatusParagraphs status_db(std::move(pghs));
-
- auto remove_plan = Dependencies::create_remove_plan(
- {unsafe_pspec("curl", Triplet::from_canonical_name("x64"))}, status_db);
-
- Assert::AreEqual(size_t(2), remove_plan.size());
- Assert::AreEqual("cpr", remove_plan[0].spec.name().c_str());
- Assert::AreEqual("curl", remove_plan[1].spec.name().c_str());
- }
-
- TEST_METHOD(features_depend_core_remove_scheme_2)
- {
- std::vector<std::unique_ptr<StatusParagraph>> pghs;
- pghs.push_back(make_status_pgh("curl", "", "", "x64"));
- pghs.push_back(make_status_feature_pgh("curl", "a", "", "x64"));
- pghs.push_back(make_status_feature_pgh("curl", "b", "curl[a]", "x64"));
- StatusParagraphs status_db(std::move(pghs));
-
- auto remove_plan = Dependencies::create_remove_plan(
- {unsafe_pspec("curl", Triplet::from_canonical_name("x64"))}, status_db);
-
- Assert::AreEqual(size_t(1), remove_plan.size());
- Assert::AreEqual("curl", remove_plan[0].spec.name().c_str());
- }
- };
-
- class UpgradePlanTests : public TestClass<UpgradePlanTests>
- {
- TEST_METHOD(basic_upgrade_scheme)
- {
- std::vector<std::unique_ptr<StatusParagraph>> pghs;
- pghs.push_back(make_status_pgh("a"));
- StatusParagraphs status_db(std::move(pghs));
-
- 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();
-
- Assert::AreEqual(size_t(2), plan.size());
- Assert::AreEqual("a", plan[0].spec().name().c_str());
- Assert::IsTrue(plan[0].remove_action.has_value());
- Assert::AreEqual("a", plan[1].spec().name().c_str());
- Assert::IsTrue(plan[1].install_action.has_value());
- }
-
- TEST_METHOD(basic_upgrade_scheme_with_recurse)
- {
- std::vector<std::unique_ptr<StatusParagraph>> pghs;
- pghs.push_back(make_status_pgh("a"));
- pghs.push_back(make_status_pgh("b", "a"));
- StatusParagraphs status_db(std::move(pghs));
-
- PackageSpecMap spec_map;
- 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();
-
- Assert::AreEqual(size_t(4), plan.size());
- Assert::AreEqual("b", plan[0].spec().name().c_str());
- Assert::IsTrue(plan[0].remove_action.has_value());
-
- Assert::AreEqual("a", plan[1].spec().name().c_str());
- Assert::IsTrue(plan[1].remove_action.has_value());
-
- Assert::AreEqual("a", plan[2].spec().name().c_str());
- Assert::IsTrue(plan[2].install_action.has_value());
-
- Assert::AreEqual("b", plan[3].spec().name().c_str());
- Assert::IsTrue(plan[3].install_action.has_value());
- }
-
- TEST_METHOD(basic_upgrade_scheme_with_bystander)
- {
- std::vector<std::unique_ptr<StatusParagraph>> pghs;
- pghs.push_back(make_status_pgh("a"));
- pghs.push_back(make_status_pgh("b"));
- StatusParagraphs status_db(std::move(pghs));
-
- PackageSpecMap spec_map;
- 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();
-
- Assert::AreEqual(size_t(2), plan.size());
- Assert::AreEqual("a", plan[0].spec().name().c_str());
- Assert::IsTrue(plan[0].remove_action.has_value());
- Assert::AreEqual("a", plan[1].spec().name().c_str());
- Assert::IsTrue(plan[1].install_action.has_value());
- }
-
- TEST_METHOD(basic_upgrade_scheme_with_new_dep)
- {
- std::vector<std::unique_ptr<StatusParagraph>> pghs;
- pghs.push_back(make_status_pgh("a"));
- StatusParagraphs status_db(std::move(pghs));
-
- PackageSpecMap spec_map;
- 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();
-
- Assert::AreEqual(size_t(3), plan.size());
- Assert::AreEqual("a", plan[0].spec().name().c_str());
- Assert::IsTrue(plan[0].remove_action.has_value());
- Assert::AreEqual("b", plan[1].spec().name().c_str());
- Assert::IsTrue(plan[1].install_action.has_value());
- Assert::AreEqual("a", plan[2].spec().name().c_str());
- Assert::IsTrue(plan[2].install_action.has_value());
- }
-
- TEST_METHOD(basic_upgrade_scheme_with_features)
- {
- std::vector<std::unique_ptr<StatusParagraph>> pghs;
- pghs.push_back(make_status_pgh("a"));
- pghs.push_back(make_status_feature_pgh("a", "a1"));
- StatusParagraphs status_db(std::move(pghs));
-
- 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();
-
- Assert::AreEqual(size_t(2), plan.size());
-
- Assert::AreEqual("a", plan[0].spec().name().c_str());
- Assert::IsTrue(plan[0].remove_action.has_value());
-
- features_check(&plan[1], "a", {"core", "a1"});
- }
-
- TEST_METHOD(basic_upgrade_scheme_with_new_default_feature)
- {
- // only core of package "a" is installed
- std::vector<std::unique_ptr<StatusParagraph>> pghs;
- pghs.push_back(make_status_pgh("a"));
- StatusParagraphs status_db(std::move(pghs));
-
- // a1 was added as a default feature and should be installed in upgrade
- 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();
-
- Assert::AreEqual(size_t(2), plan.size());
-
- Assert::AreEqual("a", plan[0].spec().name().c_str());
- Assert::IsTrue(plan[0].remove_action.has_value());
-
- features_check(&plan[1], "a", {"core", "a1"});
- }
-
- TEST_METHOD(basic_upgrade_scheme_with_self_features)
- {
- std::vector<std::unique_ptr<StatusParagraph>> pghs;
- pghs.push_back(make_status_pgh("a"));
- pghs.push_back(make_status_feature_pgh("a", "a1", ""));
- pghs.push_back(make_status_feature_pgh("a", "a2", "a[a1]"));
- StatusParagraphs status_db(std::move(pghs));
-
- 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();
-
- Assert::AreEqual(size_t(2), plan.size());
-
- Assert::AreEqual("a", plan[0].spec().name().c_str());
- Assert::IsTrue(plan[0].remove_action.has_value());
-
- Assert::AreEqual("a", plan[1].spec().name().c_str());
- Assert::IsTrue(plan[1].install_action.has_value());
- Assert::IsTrue(plan[1].install_action.get()->feature_list == std::set<std::string>{"core", "a1", "a2"});
- }
- };
-
- class ExportPlanTests : public TestClass<ExportPlanTests>
- {
- TEST_METHOD(basic_export_scheme)
- {
- std::vector<std::unique_ptr<StatusParagraph>> pghs;
- pghs.push_back(make_status_pgh("a"));
- StatusParagraphs status_db(std::move(pghs));
-
- PackageSpecMap spec_map;
- auto spec_a = spec_map.emplace("a");
-
- auto plan = Dependencies::create_export_plan({spec_a}, status_db);
-
- Assert::AreEqual(size_t(1), plan.size());
- Assert::AreEqual("a", plan[0].spec.name().c_str());
- Assert::IsTrue(plan[0].plan_type == Dependencies::ExportPlanType::ALREADY_BUILT);
- }
-
- TEST_METHOD(basic_export_scheme_with_recurse)
- {
- std::vector<std::unique_ptr<StatusParagraph>> pghs;
- pghs.push_back(make_status_pgh("a"));
- pghs.push_back(make_status_pgh("b", "a"));
- StatusParagraphs status_db(std::move(pghs));
-
- PackageSpecMap spec_map;
- auto spec_a = spec_map.emplace("a");
- auto spec_b = spec_map.emplace("b", "a");
-
- auto plan = Dependencies::create_export_plan({spec_b}, status_db);
-
- Assert::AreEqual(size_t(2), plan.size());
- Assert::AreEqual("a", plan[0].spec.name().c_str());
- Assert::IsTrue(plan[0].plan_type == Dependencies::ExportPlanType::ALREADY_BUILT);
-
- Assert::AreEqual("b", plan[1].spec.name().c_str());
- Assert::IsTrue(plan[1].plan_type == Dependencies::ExportPlanType::ALREADY_BUILT);
- }
-
- TEST_METHOD(basic_export_scheme_with_bystander)
- {
- std::vector<std::unique_ptr<StatusParagraph>> pghs;
- pghs.push_back(make_status_pgh("a"));
- pghs.push_back(make_status_pgh("b"));
- StatusParagraphs status_db(std::move(pghs));
-
- PackageSpecMap spec_map;
- auto spec_a = spec_map.emplace("a");
- auto spec_b = spec_map.emplace("b", "a");
-
- auto plan = Dependencies::create_export_plan({spec_a}, status_db);
-
- Assert::AreEqual(size_t(1), plan.size());
- Assert::AreEqual("a", plan[0].spec.name().c_str());
- Assert::IsTrue(plan[0].plan_type == Dependencies::ExportPlanType::ALREADY_BUILT);
- }
-
- TEST_METHOD(basic_export_scheme_with_missing)
- {
- StatusParagraphs status_db;
-
- PackageSpecMap spec_map;
- auto spec_a = spec_map.emplace("a");
-
- auto plan = Dependencies::create_export_plan({spec_a}, status_db);
-
- Assert::AreEqual(size_t(1), plan.size());
- Assert::AreEqual("a", plan[0].spec.name().c_str());
- Assert::IsTrue(plan[0].plan_type == Dependencies::ExportPlanType::NOT_BUILT);
- }
-
- TEST_METHOD(basic_export_scheme_with_features)
- {
- std::vector<std::unique_ptr<StatusParagraph>> pghs;
- pghs.push_back(make_status_pgh("b"));
- pghs.push_back(make_status_pgh("a"));
- pghs.push_back(make_status_feature_pgh("a", "a1", "b[core]"));
- StatusParagraphs status_db(std::move(pghs));
-
- PackageSpecMap spec_map;
- auto spec_a = spec_map.emplace("a", "", {{"a1", ""}});
-
- auto plan = Dependencies::create_export_plan({spec_a}, status_db);
-
- Assert::AreEqual(size_t(2), plan.size());
-
- Assert::AreEqual("b", plan[0].spec.name().c_str());
- Assert::IsTrue(plan[0].plan_type == Dependencies::ExportPlanType::ALREADY_BUILT);
-
- Assert::AreEqual("a", plan[1].spec.name().c_str());
- Assert::IsTrue(plan[1].plan_type == Dependencies::ExportPlanType::ALREADY_BUILT);
- }
- };
-}
diff --git a/toolsrc/src/tests.statusparagraphs.cpp b/toolsrc/src/tests.statusparagraphs.cpp
deleted file mode 100644
index fa0d54fac..000000000
--- a/toolsrc/src/tests.statusparagraphs.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-#include "tests.pch.h"
-
-#include <tests.utils.h>
-
-using namespace Microsoft::VisualStudio::CppUnitTestFramework;
-
-using namespace vcpkg;
-using namespace vcpkg::Paragraphs;
-
-namespace UnitTest1
-{
- class StatusParagraphsTests : public TestClass<StatusParagraphsTests>
- {
- TEST_METHOD(find_installed)
- {
- auto pghs = parse_paragraphs(R"(
-Package: ffmpeg
-Version: 3.3.3
-Architecture: x64-windows
-Multi-Arch: same
-Description:
-Status: install ok installed
-)");
- Assert::IsTrue(!!pghs);
- if (!pghs) return;
-
- StatusParagraphs status_db(Util::fmap(
- *pghs.get(), [](RawParagraph& rpgh) { return std::make_unique<StatusParagraph>(std::move(rpgh)); }));
-
- auto it = status_db.find_installed(unsafe_pspec("ffmpeg", Triplet::X64_WINDOWS));
- Assert::IsTrue(it != status_db.end());
- }
-
- TEST_METHOD(find_not_installed)
- {
- auto pghs = parse_paragraphs(R"(
-Package: ffmpeg
-Version: 3.3.3
-Architecture: x64-windows
-Multi-Arch: same
-Description:
-Status: purge ok not-installed
-)");
- Assert::IsTrue(!!pghs);
- if (!pghs) return;
-
- StatusParagraphs status_db(Util::fmap(
- *pghs.get(), [](RawParagraph& rpgh) { return std::make_unique<StatusParagraph>(std::move(rpgh)); }));
-
- auto it = status_db.find_installed(unsafe_pspec("ffmpeg", Triplet::X64_WINDOWS));
- Assert::IsTrue(it == status_db.end());
- }
-
- TEST_METHOD(find_with_feature_packages)
- {
- auto pghs = parse_paragraphs(R"(
-Package: ffmpeg
-Version: 3.3.3
-Architecture: x64-windows
-Multi-Arch: same
-Description:
-Status: install ok installed
-
-Package: ffmpeg
-Feature: openssl
-Depends: openssl
-Architecture: x64-windows
-Multi-Arch: same
-Description:
-Status: purge ok not-installed
-)");
- Assert::IsTrue(!!pghs);
- if (!pghs) return;
-
- StatusParagraphs status_db(Util::fmap(
- *pghs.get(), [](RawParagraph& rpgh) { return std::make_unique<StatusParagraph>(std::move(rpgh)); }));
-
- auto it = status_db.find_installed(unsafe_pspec("ffmpeg", Triplet::X64_WINDOWS));
- Assert::IsTrue(it != status_db.end());
-
- // Feature "openssl" is not installed and should not be found
- auto it1 = status_db.find_installed({unsafe_pspec("ffmpeg", Triplet::X64_WINDOWS), "openssl"});
- Assert::IsTrue(it1 == status_db.end());
- }
-
- TEST_METHOD(find_for_feature_packages)
- {
- auto pghs = parse_paragraphs(R"(
-Package: ffmpeg
-Version: 3.3.3
-Architecture: x64-windows
-Multi-Arch: same
-Description:
-Status: install ok installed
-
-Package: ffmpeg
-Feature: openssl
-Depends: openssl
-Architecture: x64-windows
-Multi-Arch: same
-Description:
-Status: install ok installed
-)");
- Assert::IsTrue(!!pghs);
- if (!pghs) return;
-
- StatusParagraphs status_db(Util::fmap(
- *pghs.get(), [](RawParagraph& rpgh) { return std::make_unique<StatusParagraph>(std::move(rpgh)); }));
-
- // Feature "openssl" is installed and should therefore be found
- auto it = status_db.find_installed({unsafe_pspec("ffmpeg", Triplet::X64_WINDOWS), "openssl"});
- Assert::IsTrue(it != status_db.end());
- }
- };
-}
diff --git a/toolsrc/src/tests.update.cpp b/toolsrc/src/tests.update.cpp
deleted file mode 100644
index 5e3f9f3e2..000000000
--- a/toolsrc/src/tests.update.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-#include "tests.pch.h"
-
-#include <tests.utils.h>
-
-using namespace Microsoft::VisualStudio::CppUnitTestFramework;
-
-using namespace vcpkg;
-using namespace vcpkg::Update;
-
-namespace UnitTest1
-{
- using Pgh = std::vector<std::unordered_map<std::string, std::string>>;
-
- class UpdateTests : public TestClass<UpdateTests>
- {
- TEST_METHOD(find_outdated_packages_basic)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
- status_paragraphs.push_back(make_status_pgh("a"));
- status_paragraphs.back()->package.version = "2";
-
- 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"}}}));
- map.emplace("a", SourceControlFileLocation { std::move(scf), "" });
- Dependencies::MapPortFileProvider provider(map);
-
- auto pkgs = SortedVector<OutdatedPackage>(Update::find_outdated_packages(provider, status_db),
- &OutdatedPackage::compare_by_name);
-
- Assert::AreEqual(size_t(1), pkgs.size());
- Assert::AreEqual("2", pkgs[0].version_diff.left.to_string().c_str());
- Assert::AreEqual("0", pkgs[0].version_diff.right.to_string().c_str());
- }
-
- TEST_METHOD(find_outdated_packages_features)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
- status_paragraphs.push_back(make_status_pgh("a"));
- status_paragraphs.back()->package.version = "2";
-
- status_paragraphs.push_back(make_status_feature_pgh("a", "b"));
- status_paragraphs.back()->package.version = "2";
-
- 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"}}}));
- map.emplace("a", SourceControlFileLocation { std::move(scf), "" });
- Dependencies::MapPortFileProvider provider(map);
-
- auto pkgs = SortedVector<OutdatedPackage>(Update::find_outdated_packages(provider, status_db),
- &OutdatedPackage::compare_by_name);
-
- Assert::AreEqual(size_t(1), pkgs.size());
- Assert::AreEqual("2", pkgs[0].version_diff.left.to_string().c_str());
- Assert::AreEqual("0", pkgs[0].version_diff.right.to_string().c_str());
- }
-
- TEST_METHOD(find_outdated_packages_features_2)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
- status_paragraphs.push_back(make_status_pgh("a"));
- status_paragraphs.back()->package.version = "2";
-
- status_paragraphs.push_back(make_status_feature_pgh("a", "b"));
- status_paragraphs.back()->package.version = "0";
- status_paragraphs.back()->state = InstallState::NOT_INSTALLED;
- status_paragraphs.back()->want = Want::PURGE;
-
- 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"}}}));
- map.emplace("a", SourceControlFileLocation{ std::move(scf), "" });
- Dependencies::MapPortFileProvider provider(map);
-
- auto pkgs = SortedVector<OutdatedPackage>(Update::find_outdated_packages(provider, status_db),
- &OutdatedPackage::compare_by_name);
-
- Assert::AreEqual(size_t(1), pkgs.size());
- Assert::AreEqual("2", pkgs[0].version_diff.left.to_string().c_str());
- Assert::AreEqual("0", pkgs[0].version_diff.right.to_string().c_str());
- }
-
- TEST_METHOD(find_outdated_packages_none)
- {
- std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
- status_paragraphs.push_back(make_status_pgh("a"));
- status_paragraphs.back()->package.version = "2";
-
- 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"}}}));
- map.emplace("a", SourceControlFileLocation{ std::move(scf), "" });
- Dependencies::MapPortFileProvider provider(map);
-
- auto pkgs = SortedVector<OutdatedPackage>(Update::find_outdated_packages(provider, status_db),
- &OutdatedPackage::compare_by_name);
-
- Assert::AreEqual(size_t(0), pkgs.size());
- }
- };
-}
diff --git a/toolsrc/src/tests.utils.cpp b/toolsrc/src/tests.utils.cpp
deleted file mode 100644
index ac391f559..000000000
--- a/toolsrc/src/tests.utils.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-#include "tests.pch.h"
-
-#include "tests.utils.h"
-
-using namespace Microsoft::VisualStudio::CppUnitTestFramework;
-using namespace vcpkg;
-
-std::unique_ptr<StatusParagraph> make_status_pgh(const char* name,
- const char* depends,
- const char* default_features,
- const char* triplet)
-{
- using Pgh = std::unordered_map<std::string, std::string>;
- return std::make_unique<StatusParagraph>(Pgh{{"Package", name},
- {"Version", "1"},
- {"Architecture", triplet},
- {"Multi-Arch", "same"},
- {"Depends", depends},
- {"Default-Features", default_features},
- {"Status", "install ok installed"}});
-}
-std::unique_ptr<StatusParagraph> make_status_feature_pgh(const char* name,
- const char* feature,
- const char* depends,
- const char* triplet)
-{
- using Pgh = std::unordered_map<std::string, std::string>;
- return std::make_unique<StatusParagraph>(Pgh{{"Package", name},
- {"Version", "1"},
- {"Feature", feature},
- {"Architecture", triplet},
- {"Multi-Arch", "same"},
- {"Depends", depends},
- {"Status", "install ok installed"}});
-}
-
-PackageSpec unsafe_pspec(std::string name, Triplet t)
-{
- auto m_ret = PackageSpec::from_name_and_triplet(name, t);
- Assert::IsTrue(m_ret.has_value());
- return m_ret.value_or_exit(VCPKG_LINE_INFO);
-}
diff --git a/toolsrc/src/vcpkg-test/arguments.cpp b/toolsrc/src/vcpkg-test/arguments.cpp
new file mode 100644
index 000000000..3fe5fa420
--- /dev/null
+++ b/toolsrc/src/vcpkg-test/arguments.cpp
@@ -0,0 +1,109 @@
+#include <vcpkg-test/catch.h>
+
+#include <vcpkg/vcpkgcmdarguments.h>
+
+#include <vector>
+
+using vcpkg::CommandSetting;
+using vcpkg::CommandStructure;
+using vcpkg::CommandSwitch;
+using vcpkg::VcpkgCmdArguments;
+
+TEST_CASE ("VcpkgCmdArguments from lowercase argument sequence", "[arguments]")
+{
+ std::vector<std::string> t = {"--vcpkg-root",
+ "C:\\vcpkg",
+ "--scripts-root=C:\\scripts",
+ "--debug",
+ "--sendmetrics",
+ "--printmetrics",
+ "--overlay-ports=C:\\ports1",
+ "--overlay-ports=C:\\ports2",
+ "--overlay-triplets=C:\\tripletsA",
+ "--overlay-triplets=C:\\tripletsB"};
+ auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size());
+
+ REQUIRE(*v.vcpkg_root_dir == "C:\\vcpkg");
+ REQUIRE(*v.scripts_root_dir == "C:\\scripts");
+ REQUIRE(v.debug);
+ REQUIRE(*v.debug.get());
+ REQUIRE(v.sendmetrics);
+ REQUIRE(*v.sendmetrics.get());
+ REQUIRE(v.printmetrics);
+ REQUIRE(*v.printmetrics.get());
+
+ REQUIRE(v.overlay_ports->size() == 2);
+ REQUIRE(v.overlay_ports->at(0) == "C:\\ports1");
+ REQUIRE(v.overlay_ports->at(1) == "C:\\ports2");
+
+ REQUIRE(v.overlay_triplets->size() == 2);
+ REQUIRE(v.overlay_triplets->at(0) == "C:\\tripletsA");
+ REQUIRE(v.overlay_triplets->at(1) == "C:\\tripletsB");
+}
+
+TEST_CASE ("VcpkgCmdArguments from uppercase argument sequence", "[arguments]")
+{
+ std::vector<std::string> t = {"--VCPKG-ROOT",
+ "C:\\vcpkg",
+ "--SCRIPTS-ROOT=C:\\scripts",
+ "--DEBUG",
+ "--SENDMETRICS",
+ "--PRINTMETRICS",
+ "--OVERLAY-PORTS=C:\\ports1",
+ "--OVERLAY-PORTS=C:\\ports2",
+ "--OVERLAY-TRIPLETS=C:\\tripletsA",
+ "--OVERLAY-TRIPLETS=C:\\tripletsB"};
+ auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size());
+
+ REQUIRE(*v.vcpkg_root_dir == "C:\\vcpkg");
+ REQUIRE(*v.scripts_root_dir == "C:\\scripts");
+ REQUIRE(v.debug);
+ REQUIRE(*v.debug.get());
+ REQUIRE(v.sendmetrics);
+ REQUIRE(*v.sendmetrics.get());
+ REQUIRE(v.printmetrics);
+ REQUIRE(*v.printmetrics.get());
+
+ REQUIRE(v.overlay_ports->size() == 2);
+ REQUIRE(v.overlay_ports->at(0) == "C:\\ports1");
+ REQUIRE(v.overlay_ports->at(1) == "C:\\ports2");
+
+ REQUIRE(v.overlay_triplets->size() == 2);
+ REQUIRE(v.overlay_triplets->at(0) == "C:\\tripletsA");
+ REQUIRE(v.overlay_triplets->at(1) == "C:\\tripletsB");
+}
+
+TEST_CASE ("VcpkgCmdArguments from argument sequence with valued options", "[arguments]")
+{
+ SECTION ("case 1")
+ {
+ std::array<CommandSetting, 1> settings = {{{"--a", ""}}};
+ CommandStructure cmdstruct = {"", 0, SIZE_MAX, {{}, settings}, nullptr};
+
+ std::vector<std::string> t = {"--a=b", "command", "argument"};
+ auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size());
+ auto opts = v.parse_arguments(cmdstruct);
+
+ REQUIRE(opts.settings["--a"] == "b");
+ REQUIRE(v.command_arguments.size() == 1);
+ REQUIRE(v.command_arguments[0] == "argument");
+ REQUIRE(v.command == "command");
+ }
+
+ SECTION ("case 2")
+ {
+ std::array<CommandSwitch, 2> switches = {{{"--a", ""}, {"--c", ""}}};
+ std::array<CommandSetting, 2> settings = {{{"--b", ""}, {"--d", ""}}};
+ CommandStructure cmdstruct = {"", 0, SIZE_MAX, {switches, settings}, nullptr};
+
+ std::vector<std::string> t = {"--a", "--b=c"};
+ auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size());
+ auto opts = v.parse_arguments(cmdstruct);
+
+ REQUIRE(opts.settings["--b"] == "c");
+ REQUIRE(opts.settings.find("--d") == opts.settings.end());
+ REQUIRE(opts.switches.find("--a") != opts.switches.end());
+ REQUIRE(opts.settings.find("--c") == opts.settings.end());
+ REQUIRE(v.command_arguments.size() == 0);
+ }
+}
diff --git a/toolsrc/src/vcpkg-test/catch.cpp b/toolsrc/src/vcpkg-test/catch.cpp
new file mode 100644
index 000000000..8b5d1aa15
--- /dev/null
+++ b/toolsrc/src/vcpkg-test/catch.cpp
@@ -0,0 +1,11 @@
+#define CATCH_CONFIG_RUNNER
+#include <vcpkg-test/catch.h>
+
+#include <vcpkg/base/system.debug.h>
+
+int main(int argc, char** argv)
+{
+ vcpkg::Debug::g_debugging = true;
+
+ return Catch::Session().run(argc, argv);
+}
diff --git a/toolsrc/src/vcpkg-test/chrono.cpp b/toolsrc/src/vcpkg-test/chrono.cpp
new file mode 100644
index 000000000..306217ad0
--- /dev/null
+++ b/toolsrc/src/vcpkg-test/chrono.cpp
@@ -0,0 +1,34 @@
+#include <vcpkg-test/catch.h>
+
+#include <vcpkg/base/chrono.h>
+
+namespace Chrono = vcpkg::Chrono;
+
+TEST_CASE ("parse time", "[chrono]")
+{
+ auto timestring = "1990-02-03T04:05:06.0Z";
+ auto maybe_time = Chrono::CTime::parse(timestring);
+
+ REQUIRE(maybe_time.has_value());
+ REQUIRE(maybe_time.get()->to_string() == timestring);
+}
+
+TEST_CASE ("parse blank time", "[chrono]")
+{
+ auto maybe_time = Chrono::CTime::parse("");
+
+ REQUIRE_FALSE(maybe_time.has_value());
+}
+
+TEST_CASE ("difference of times", "[chrono]")
+{
+ auto maybe_time1 = Chrono::CTime::parse("1990-02-03T04:05:06.0Z");
+ auto maybe_time2 = Chrono::CTime::parse("1990-02-10T04:05:06.0Z");
+
+ REQUIRE(maybe_time1.has_value());
+ REQUIRE(maybe_time2.has_value());
+
+ auto delta = maybe_time2.get()->to_time_point() - maybe_time1.get()->to_time_point();
+
+ REQUIRE(std::chrono::duration_cast<std::chrono::hours>(delta).count() == 24 * 7);
+}
diff --git a/toolsrc/src/vcpkg-test/dependencies.cpp b/toolsrc/src/vcpkg-test/dependencies.cpp
new file mode 100644
index 000000000..5ed05cc07
--- /dev/null
+++ b/toolsrc/src/vcpkg-test/dependencies.cpp
@@ -0,0 +1,28 @@
+#include <vcpkg-test/catch.h>
+
+#include <vcpkg/sourceparagraph.h>
+
+using namespace vcpkg;
+using Parse::parse_comma_list;
+
+TEST_CASE ("parse depends", "[dependencies]")
+{
+ 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).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);
+ REQUIRE(v.size() == 2);
+ REQUIRE(v.at(0) == "libA");
+ REQUIRE(v.at(1) == "libB");
+
+ auto v2 = filter_dependencies(deps, Triplet::ARM_UWP);
+ REQUIRE(v.size() == 2);
+ REQUIRE(v2.at(0) == "libB");
+ REQUIRE(v2.at(1) == "libC");
+}
diff --git a/toolsrc/src/vcpkg-test/files.cpp b/toolsrc/src/vcpkg-test/files.cpp
new file mode 100644
index 000000000..d40edb3bd
--- /dev/null
+++ b/toolsrc/src/vcpkg-test/files.cpp
@@ -0,0 +1,243 @@
+#include <vcpkg-test/catch.h>
+#include <vcpkg-test/util.h>
+
+#include <vcpkg/base/files.h>
+#include <vcpkg/base/strings.h>
+
+#include <iostream>
+#include <random>
+
+#include <vector>
+
+using vcpkg::Test::AllowSymlinks;
+using vcpkg::Test::base_temporary_directory;
+using vcpkg::Test::can_create_symlinks;
+
+#define CHECK_EC_ON_FILE(file, ec) \
+ do \
+ { \
+ if (ec) \
+ { \
+ FAIL(file << ": " << ec.message()); \
+ } \
+ } while (0)
+
+namespace
+{
+ using uid_t = std::uniform_int_distribution<std::uint64_t>;
+ using urbg_t = std::mt19937_64;
+
+ urbg_t get_urbg(std::uint64_t index)
+ {
+ // smallest prime > 2**63 - 1
+ return urbg_t{index + 9223372036854775837ULL};
+ }
+
+ std::string get_random_filename(urbg_t& urbg) { return vcpkg::Strings::b32_encode(uid_t{}(urbg)); }
+
+ struct MaxDepth
+ {
+ std::uint64_t i;
+ explicit MaxDepth(std::uint64_t i) : i(i) {}
+ operator uint64_t() const { return i; }
+ };
+
+ struct Width
+ {
+ std::uint64_t i;
+ explicit Width(std::uint64_t i) : i(i) {}
+ operator uint64_t() const { return i; }
+ };
+
+ struct CurrentDepth
+ {
+ std::uint64_t i;
+ explicit CurrentDepth(std::uint64_t i) : i(i) {}
+ operator uint64_t() const { return i; }
+ CurrentDepth incremented() const { return CurrentDepth{i + 1}; }
+ };
+
+ void create_directory_tree(urbg_t& urbg,
+ vcpkg::Files::Filesystem& fs,
+ const fs::path& base,
+ MaxDepth max_depth,
+ AllowSymlinks allow_symlinks = AllowSymlinks::Yes,
+ Width width = Width{5},
+ CurrentDepth current_depth = CurrentDepth{0})
+ {
+ std::random_device rd;
+
+ // we want ~70% of our "files" to be directories, and then a third
+ // each of the remaining ~30% to be regular files, directory symlinks,
+ // and regular symlinks
+ constexpr std::uint64_t directory_min_tag = 0;
+ constexpr std::uint64_t directory_max_tag = 6;
+ constexpr std::uint64_t regular_file_tag = 7;
+ constexpr std::uint64_t regular_symlink_tag = 8;
+ constexpr std::uint64_t directory_symlink_tag = 9;
+
+ allow_symlinks = AllowSymlinks{allow_symlinks && can_create_symlinks()};
+
+ // if we're at the max depth, we only want to build non-directories
+ std::uint64_t file_type;
+ if (current_depth >= max_depth)
+ {
+ file_type = uid_t{regular_file_tag, directory_symlink_tag}(urbg);
+ }
+ else if (current_depth < 2)
+ {
+ file_type = directory_min_tag;
+ }
+ else
+ {
+ file_type = uid_t{directory_min_tag, regular_symlink_tag}(urbg);
+ }
+
+ if (!allow_symlinks && file_type > regular_file_tag)
+ {
+ file_type = regular_file_tag;
+ }
+
+ std::error_code ec;
+ if (file_type <= directory_max_tag)
+ {
+ fs.create_directory(base, ec);
+ if (ec)
+ {
+ CHECK_EC_ON_FILE(base, ec);
+ }
+
+ for (int i = 0; i < width; ++i)
+ {
+ create_directory_tree(urbg,
+ fs,
+ base / get_random_filename(urbg),
+ max_depth,
+ allow_symlinks,
+ width,
+ current_depth.incremented());
+ }
+ }
+ else if (file_type == regular_file_tag)
+ {
+ // regular file
+ fs.write_contents(base, "", ec);
+ }
+ else if (file_type == regular_symlink_tag)
+ {
+ // regular symlink
+ auto base_link = base;
+ base_link.replace_filename(base.filename().u8string() + "-orig");
+ fs.write_contents(base_link, "", ec);
+ CHECK_EC_ON_FILE(base_link, ec);
+ vcpkg::Test::create_symlink(base_link, base, ec);
+ }
+ else // type == directory_symlink_tag
+ {
+ // directory symlink
+ auto parent = base;
+ parent.remove_filename();
+ vcpkg::Test::create_directory_symlink(parent, base, ec);
+ }
+
+ CHECK_EC_ON_FILE(base, ec);
+ REQUIRE(fs::exists(fs.symlink_status(base, ec)));
+ CHECK_EC_ON_FILE(base, ec);
+ }
+
+ vcpkg::Files::Filesystem& setup()
+ {
+ auto& fs = vcpkg::Files::get_real_filesystem();
+
+ std::error_code ec;
+ fs.create_directory(base_temporary_directory(), ec);
+ CHECK_EC_ON_FILE(base_temporary_directory(), ec);
+
+ return fs;
+ }
+}
+
+TEST_CASE ("remove all", "[files]")
+{
+ auto urbg = get_urbg(0);
+
+ auto& fs = setup();
+
+ fs::path temp_dir = base_temporary_directory() / get_random_filename(urbg);
+ INFO("temp dir is: " << temp_dir);
+
+ create_directory_tree(urbg, fs, temp_dir, MaxDepth{5});
+
+ std::error_code ec;
+ fs::path fp;
+ fs.remove_all(temp_dir, ec, fp);
+ CHECK_EC_ON_FILE(fp, ec);
+
+ REQUIRE_FALSE(fs.exists(temp_dir, ec));
+ CHECK_EC_ON_FILE(temp_dir, ec);
+}
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+TEST_CASE ("remove all -- benchmarks", "[files][!benchmark]")
+{
+ auto urbg = get_urbg(1);
+ auto& fs = setup();
+
+ struct
+ {
+ urbg_t& urbg;
+ vcpkg::Files::Filesystem& fs;
+
+ void operator()(Catch::Benchmark::Chronometer& meter, MaxDepth max_depth, AllowSymlinks allow_symlinks) const
+ {
+ std::vector<fs::path> temp_dirs;
+ temp_dirs.resize(meter.runs());
+
+ std::generate(begin(temp_dirs), end(temp_dirs), [&] {
+ fs::path temp_dir = base_temporary_directory() / get_random_filename(urbg);
+ create_directory_tree(urbg, fs, temp_dir, max_depth, allow_symlinks);
+ return temp_dir;
+ });
+
+ meter.measure([&](int run) {
+ std::error_code ec;
+ fs::path fp;
+ const auto& temp_dir = temp_dirs[run];
+
+ fs.remove_all(temp_dir, ec, fp);
+ CHECK_EC_ON_FILE(fp, ec);
+ });
+
+ for (const auto& dir : temp_dirs)
+ {
+ std::error_code ec;
+ REQUIRE_FALSE(fs.exists(dir, ec));
+ CHECK_EC_ON_FILE(dir, ec);
+ }
+ }
+ } do_benchmark = {urbg, fs};
+
+ BENCHMARK_ADVANCED("small directory, no symlinks")(Catch::Benchmark::Chronometer meter)
+ {
+ do_benchmark(meter, MaxDepth{2}, AllowSymlinks::No);
+ };
+
+ BENCHMARK_ADVANCED("large directory, no symlinks")(Catch::Benchmark::Chronometer meter)
+ {
+ do_benchmark(meter, MaxDepth{5}, AllowSymlinks::No);
+ };
+
+ if (can_create_symlinks())
+ {
+ BENCHMARK_ADVANCED("small directory, symlinks")(Catch::Benchmark::Chronometer meter)
+ {
+ do_benchmark(meter, MaxDepth{2}, AllowSymlinks::Yes);
+ };
+
+ BENCHMARK_ADVANCED("large directory, symlinks")(Catch::Benchmark::Chronometer meter)
+ {
+ do_benchmark(meter, MaxDepth{5}, AllowSymlinks::Yes);
+ };
+ }
+}
+#endif
diff --git a/toolsrc/src/vcpkg-test/paragraph.cpp b/toolsrc/src/vcpkg-test/paragraph.cpp
new file mode 100644
index 000000000..a95879cfa
--- /dev/null
+++ b/toolsrc/src/vcpkg-test/paragraph.cpp
@@ -0,0 +1,445 @@
+#include <vcpkg-test/catch.h>
+#include <vcpkg-test/util.h>
+
+#include <vcpkg/base/strings.h>
+
+#include <vcpkg/paragraphs.h>
+
+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"},
+ }});
+
+ REQUIRE(m_pgh.has_value());
+ auto& pgh = **m_pgh.get();
+
+ REQUIRE(pgh.core_paragraph->name == "zlib");
+ REQUIRE(pgh.core_paragraph->version == "1.2.8");
+ REQUIRE(pgh.core_paragraph->maintainer == "");
+ REQUIRE(pgh.core_paragraph->description == "");
+ REQUIRE(pgh.core_paragraph->depends.size() == 0);
+}
+
+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"},
+ }});
+ REQUIRE(m_pgh.has_value());
+ auto& pgh = **m_pgh.get();
+
+ REQUIRE(pgh.core_paragraph->name == "s");
+ REQUIRE(pgh.core_paragraph->version == "v");
+ REQUIRE(pgh.core_paragraph->maintainer == "m");
+ REQUIRE(pgh.core_paragraph->description == "d");
+ REQUIRE(pgh.core_paragraph->depends.size() == 1);
+ 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"},
+ }});
+ REQUIRE(m_pgh.has_value());
+ auto& pgh = **m_pgh.get();
+
+ REQUIRE(pgh.core_paragraph->depends.size() == 2);
+ REQUIRE(pgh.core_paragraph->depends[0].name() == "z");
+ REQUIRE(pgh.core_paragraph->depends[1].name() == "openssl");
+}
+
+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"},
+ }});
+ REQUIRE(m_pgh.has_value());
+ auto& pgh = **m_pgh.get();
+
+ REQUIRE(pgh.core_paragraph->depends.size() == 3);
+ REQUIRE(pgh.core_paragraph->depends[0].name() == "z");
+ REQUIRE(pgh.core_paragraph->depends[1].name() == "openssl");
+ 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)"},
+ }});
+ REQUIRE(m_pgh.has_value());
+ auto& pgh = **m_pgh.get();
+
+ REQUIRE(pgh.core_paragraph->name == "zlib");
+ REQUIRE(pgh.core_paragraph->version == "1.2.8");
+ REQUIRE(pgh.core_paragraph->maintainer == "");
+ REQUIRE(pgh.core_paragraph->description == "");
+ REQUIRE(pgh.core_paragraph->depends.size() == 2);
+ REQUIRE(pgh.core_paragraph->depends[0].name() == "libA");
+ REQUIRE(pgh.core_paragraph->depends[0].qualifier == "windows");
+ REQUIRE(pgh.core_paragraph->depends[1].name() == "libB");
+ REQUIRE(pgh.core_paragraph->depends[1].qualifier == "uwp");
+}
+
+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"},
+ }});
+ REQUIRE(m_pgh.has_value());
+ auto& pgh = **m_pgh.get();
+
+ REQUIRE(pgh.core_paragraph->default_features.size() == 1);
+ REQUIRE(pgh.core_paragraph->default_features[0] == "a1");
+}
+
+TEST_CASE ("BinaryParagraph construct minimum", "[paragraph]")
+{
+ vcpkg::BinaryParagraph pgh({
+ {"Package", "zlib"},
+ {"Version", "1.2.8"},
+ {"Architecture", "x86-windows"},
+ {"Multi-Arch", "same"},
+ });
+
+ REQUIRE(pgh.spec.name() == "zlib");
+ REQUIRE(pgh.version == "1.2.8");
+ REQUIRE(pgh.maintainer == "");
+ REQUIRE(pgh.description == "");
+ REQUIRE(pgh.spec.triplet().canonical_name() == "x86-windows");
+ REQUIRE(pgh.depends.size() == 0);
+}
+
+TEST_CASE ("BinaryParagraph construct maximum", "[paragraph]")
+{
+ vcpkg::BinaryParagraph pgh({
+ {"Package", "s"},
+ {"Version", "v"},
+ {"Architecture", "x86-windows"},
+ {"Multi-Arch", "same"},
+ {"Maintainer", "m"},
+ {"Description", "d"},
+ {"Depends", "bd"},
+ });
+
+ REQUIRE(pgh.spec.name() == "s");
+ REQUIRE(pgh.version == "v");
+ REQUIRE(pgh.maintainer == "m");
+ REQUIRE(pgh.description == "d");
+ REQUIRE(pgh.depends.size() == 1);
+ REQUIRE(pgh.depends[0] == "bd");
+}
+
+TEST_CASE ("BinaryParagraph three depends", "[paragraph]")
+{
+ vcpkg::BinaryParagraph pgh({
+ {"Package", "zlib"},
+ {"Version", "1.2.8"},
+ {"Architecture", "x86-windows"},
+ {"Multi-Arch", "same"},
+ {"Depends", "a, b, c"},
+ });
+
+ REQUIRE(pgh.depends.size() == 3);
+ REQUIRE(pgh.depends[0] == "a");
+ REQUIRE(pgh.depends[1] == "b");
+ REQUIRE(pgh.depends[2] == "c");
+}
+
+TEST_CASE ("BinaryParagraph abi", "[paragraph]")
+{
+ vcpkg::BinaryParagraph pgh({
+ {"Package", "zlib"},
+ {"Version", "1.2.8"},
+ {"Architecture", "x86-windows"},
+ {"Multi-Arch", "same"},
+ {"Abi", "abcd123"},
+ });
+
+ REQUIRE(pgh.depends.size() == 0);
+ REQUIRE(pgh.abi == "abcd123");
+}
+
+TEST_CASE ("BinaryParagraph default features", "[paragraph]")
+{
+ vcpkg::BinaryParagraph pgh({
+ {"Package", "a"},
+ {"Version", "1.0"},
+ {"Architecture", "x86-windows"},
+ {"Multi-Arch", "same"},
+ {"Default-Features", "a1"},
+ });
+
+ REQUIRE(pgh.depends.size() == 0);
+ REQUIRE(pgh.default_features.size() == 1);
+ REQUIRE(pgh.default_features[0] == "a1");
+}
+
+TEST_CASE ("parse paragraphs empty", "[paragraph]")
+{
+ const char* str = "";
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO);
+ REQUIRE(pghs.empty());
+}
+
+TEST_CASE ("parse paragraphs one field", "[paragraph]")
+{
+ const char* str = "f1: v1";
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO);
+ REQUIRE(pghs.size() == 1);
+ REQUIRE(pghs[0].size() == 1);
+ REQUIRE(pghs[0]["f1"] == "v1");
+}
+
+TEST_CASE ("parse paragraphs one pgh", "[paragraph]")
+{
+ const char* str = "f1: v1\n"
+ "f2: v2";
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO);
+ REQUIRE(pghs.size() == 1);
+ REQUIRE(pghs[0].size() == 2);
+ REQUIRE(pghs[0]["f1"] == "v1");
+ REQUIRE(pghs[0]["f2"] == "v2");
+}
+
+TEST_CASE ("parse paragraphs two pgh", "[paragraph]")
+{
+ const char* str = "f1: v1\n"
+ "f2: v2\n"
+ "\n"
+ "f3: v3\n"
+ "f4: v4";
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO);
+
+ REQUIRE(pghs.size() == 2);
+ REQUIRE(pghs[0].size() == 2);
+ REQUIRE(pghs[0]["f1"] == "v1");
+ REQUIRE(pghs[0]["f2"] == "v2");
+ REQUIRE(pghs[1].size() == 2);
+ REQUIRE(pghs[1]["f3"] == "v3");
+ REQUIRE(pghs[1]["f4"] == "v4");
+}
+
+TEST_CASE ("parse paragraphs field names", "[paragraph]")
+{
+ const char* str = "1:\n"
+ "f:\n"
+ "F:\n"
+ "0:\n"
+ "F-2:\n";
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO);
+
+ REQUIRE(pghs.size() == 1);
+ REQUIRE(pghs[0].size() == 5);
+}
+
+TEST_CASE ("parse paragraphs multiple blank lines", "[paragraph]")
+{
+ const char* str = "f1: v1\n"
+ "f2: v2\n"
+ "\n"
+ "\n"
+ "f3: v3\n"
+ "f4: v4";
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO);
+
+ REQUIRE(pghs.size() == 2);
+}
+
+TEST_CASE ("parse paragraphs empty fields", "[paragraph]")
+{
+ const char* str = "f1:\n"
+ "f2: ";
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO);
+
+ REQUIRE(pghs.size() == 1);
+ REQUIRE(pghs[0].size() == 2);
+ REQUIRE(pghs[0]["f1"] == "");
+ REQUIRE(pghs[0]["f2"] == "");
+ REQUIRE(pghs[0].size() == 2);
+}
+
+TEST_CASE ("parse paragraphs multiline fields", "[paragraph]")
+{
+ const char* str = "f1: simple\n"
+ " f1\r\n"
+ "f2:\r\n"
+ " f2\r\n"
+ " continue\r\n";
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO);
+
+ REQUIRE(pghs.size() == 1);
+ REQUIRE(pghs[0]["f1"] == "simple\n f1");
+ REQUIRE(pghs[0]["f2"] == "\n f2\n continue");
+}
+
+TEST_CASE ("parse paragraphs crlfs", "[paragraph]")
+{
+ const char* str = "f1: v1\r\n"
+ "f2: v2\r\n"
+ "\r\n"
+ "f3: v3\r\n"
+ "f4: v4";
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO);
+
+ REQUIRE(pghs.size() == 2);
+ REQUIRE(pghs[0].size() == 2);
+ REQUIRE(pghs[0]["f1"] == "v1");
+ REQUIRE(pghs[0]["f2"] == "v2");
+ REQUIRE(pghs[1].size() == 2);
+ REQUIRE(pghs[1]["f3"] == "v3");
+ REQUIRE(pghs[1]["f4"] == "v4");
+}
+
+TEST_CASE ("parse paragraphs comment", "[paragraph]")
+{
+ const char* str = "f1: v1\r\n"
+ "#comment\r\n"
+ "f2: v2\r\n"
+ "#comment\r\n"
+ "\r\n"
+ "#comment\r\n"
+ "f3: v3\r\n"
+ "#comment\r\n"
+ "f4: v4";
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO);
+
+ REQUIRE(pghs.size() == 2);
+ REQUIRE(pghs[0].size() == 2);
+ REQUIRE(pghs[0]["f1"] == "v1");
+ REQUIRE(pghs[0]["f2"] == "v2");
+ REQUIRE(pghs[1].size());
+ REQUIRE(pghs[1]["f3"] == "v3");
+ REQUIRE(pghs[1]["f4"] == "v4");
+}
+
+TEST_CASE ("parse comment before single line feed", "[paragraph]")
+{
+ const char* str = "f1: v1\r\n"
+ "#comment\n";
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO);
+ REQUIRE(pghs[0].size() == 1);
+ REQUIRE(pghs[0]["f1"] == "v1");
+}
+
+TEST_CASE ("BinaryParagraph serialize min", "[paragraph]")
+{
+ vcpkg::BinaryParagraph pgh({
+ {"Package", "zlib"},
+ {"Version", "1.2.8"},
+ {"Architecture", "x86-windows"},
+ {"Multi-Arch", "same"},
+ });
+ std::string ss = Strings::serialize(pgh);
+ 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]["Package"] == "zlib");
+ REQUIRE(pghs[0]["Version"] == "1.2.8");
+ REQUIRE(pghs[0]["Architecture"] == "x86-windows");
+ REQUIRE(pghs[0]["Multi-Arch"] == "same");
+}
+
+TEST_CASE ("BinaryParagraph serialize max", "[paragraph]")
+{
+ vcpkg::BinaryParagraph pgh({
+ {"Package", "zlib"},
+ {"Version", "1.2.8"},
+ {"Architecture", "x86-windows"},
+ {"Description", "first line\n second line"},
+ {"Maintainer", "abc <abc@abc.abc>"},
+ {"Depends", "dep"},
+ {"Multi-Arch", "same"},
+ });
+ std::string ss = Strings::serialize(pgh);
+ 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]["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");
+}
+
+TEST_CASE ("BinaryParagraph serialize multiple deps", "[paragraph]")
+{
+ vcpkg::BinaryParagraph pgh({
+ {"Package", "zlib"},
+ {"Version", "1.2.8"},
+ {"Architecture", "x86-windows"},
+ {"Multi-Arch", "same"},
+ {"Depends", "a, b, c"},
+ });
+ std::string ss = Strings::serialize(pgh);
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss).value_or_exit(VCPKG_LINE_INFO);
+
+ REQUIRE(pghs.size() == 1);
+ REQUIRE(pghs[0]["Depends"] == "a, b, c");
+}
+
+TEST_CASE ("BinaryParagraph serialize abi", "[paragraph]")
+{
+ vcpkg::BinaryParagraph pgh({
+ {"Package", "zlib"},
+ {"Version", "1.2.8"},
+ {"Architecture", "x86-windows"},
+ {"Multi-Arch", "same"},
+ {"Depends", "a, b, c"},
+ {"Abi", "123abc"},
+ });
+ std::string ss = Strings::serialize(pgh);
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss).value_or_exit(VCPKG_LINE_INFO);
+
+ REQUIRE(pghs.size() == 1);
+ REQUIRE(pghs[0]["Abi"] == "123abc");
+}
diff --git a/toolsrc/src/vcpkg-test/plan.cpp b/toolsrc/src/vcpkg-test/plan.cpp
new file mode 100644
index 000000000..049ef2066
--- /dev/null
+++ b/toolsrc/src/vcpkg-test/plan.cpp
@@ -0,0 +1,1241 @@
+#include <vcpkg-test/catch.h>
+#include <vcpkg-test/util.h>
+
+#include <vcpkg/dependencies.h>
+#include <vcpkg/sourceparagraph.h>
+#include <vcpkg/triplet.h>
+
+#include <memory>
+#include <unordered_map>
+#include <vector>
+
+using namespace vcpkg;
+
+using Test::make_status_feature_pgh;
+using Test::make_status_pgh;
+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,
+ std::string pkg_name,
+ std::vector<std::string> vec,
+ 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());
+
+ 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)
+ {
+ // TODO: see if this can be simplified
+ if (feature_name == "core" || feature_name == "")
+ {
+ REQUIRE((Util::find(feature_list, "core") != feature_list.end() ||
+ Util::find(feature_list, "") != feature_list.end()));
+ continue;
+ }
+ REQUIRE(Util::find(feature_list, feature_name) != feature_list.end());
+ }
+}
+
+/// <summary>
+/// Assert that the given action is a remove of given package.
+/// </summary>
+static void remove_plan_check(Dependencies::AnyAction& remove_action,
+ std::string pkg_name,
+ const Triplet& triplet = Triplet::X86_WINDOWS)
+{
+ const auto& plan = remove_action.remove_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;
+
+ PackageSpecMap spec_map;
+ auto spec_a = spec_map.emplace("a", "b");
+ auto spec_b = spec_map.emplace("b", "c");
+ auto spec_c = spec_map.emplace("c");
+
+ 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)));
+
+ 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");
+}
+
+TEST_CASE ("multiple install scheme", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+
+ PackageSpecMap spec_map;
+ auto spec_a = spec_map.emplace("a", "d");
+ auto spec_b = spec_map.emplace("b", "d, e");
+ auto spec_c = spec_map.emplace("c", "e, h");
+ auto spec_d = spec_map.emplace("d", "f, g, h");
+ auto spec_e = spec_map.emplace("e", "g");
+ auto spec_f = spec_map.emplace("f");
+ auto spec_g = spec_map.emplace("g");
+ auto spec_h = spec_map.emplace("h");
+
+ Dependencies::MapPortFileProvider map_port(spec_map.map);
+ auto install_plan = Dependencies::create_feature_install_plan(
+ map_port,
+ {FeatureSpec{spec_a, ""}, FeatureSpec{spec_b, ""}, FeatureSpec{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();
+ };
+
+ const auto a_pos = iterator_pos(spec_a);
+ const auto b_pos = iterator_pos(spec_b);
+ const auto c_pos = iterator_pos(spec_c);
+ const auto d_pos = iterator_pos(spec_d);
+ const auto e_pos = iterator_pos(spec_e);
+ const auto f_pos = iterator_pos(spec_f);
+ const auto g_pos = iterator_pos(spec_g);
+ const auto h_pos = iterator_pos(spec_h);
+
+ REQUIRE(a_pos > d_pos);
+ REQUIRE(b_pos > e_pos);
+ REQUIRE(b_pos > d_pos);
+ REQUIRE(c_pos > e_pos);
+ REQUIRE(c_pos > h_pos);
+ REQUIRE(d_pos > f_pos);
+ REQUIRE(d_pos > g_pos);
+ REQUIRE(d_pos > h_pos);
+ REQUIRE(e_pos > g_pos);
+}
+
+TEST_CASE ("existing package scheme", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+ status_paragraphs.push_back(vcpkg::Test::make_status_pgh("a"));
+
+ PackageSpecMap spec_map;
+ auto spec_a = FullPackageSpec{spec_map.emplace("a")};
+
+ auto install_plan = Dependencies::create_feature_install_plan(
+ spec_map.map, FullPackageSpec::to_feature_specs({spec_a}), StatusParagraphs(std::move(status_paragraphs)));
+
+ REQUIRE(install_plan.size() == 1);
+ const auto p = install_plan.at(0).install_action.get();
+ REQUIRE(p);
+ REQUIRE(p->spec.name() == "a");
+ REQUIRE(p->plan_type == Dependencies::InstallPlanType::ALREADY_INSTALLED);
+ REQUIRE(p->request_type == Dependencies::RequestType::USER_REQUESTED);
+}
+
+TEST_CASE ("user requested package scheme", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+
+ PackageSpecMap spec_map;
+ const auto spec_a = FullPackageSpec{spec_map.emplace("a", "b")};
+ const auto spec_b = FullPackageSpec{spec_map.emplace("b")};
+
+ const auto install_plan = Dependencies::create_feature_install_plan(
+ spec_map.map, FullPackageSpec::to_feature_specs({spec_a}), StatusParagraphs(std::move(status_paragraphs)));
+
+ REQUIRE(install_plan.size() == 2);
+ const auto p = install_plan.at(0).install_action.get();
+ REQUIRE(p);
+ 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);
+ REQUIRE(p2->spec.name() == "a");
+ REQUIRE(p2->plan_type == Dependencies::InstallPlanType::BUILD_AND_INSTALL);
+ REQUIRE(p2->request_type == Dependencies::RequestType::USER_REQUESTED);
+}
+
+TEST_CASE ("long install scheme", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+ status_paragraphs.push_back(make_status_pgh("j", "k"));
+ status_paragraphs.push_back(make_status_pgh("k"));
+
+ PackageSpecMap spec_map;
+
+ auto spec_a = spec_map.emplace("a", "b, c, d, e, f, g, h, j, k");
+ auto spec_b = spec_map.emplace("b", "c, d, e, f, g, h, j, k");
+ auto spec_c = spec_map.emplace("c", "d, e, f, g, h, j, k");
+ auto spec_d = spec_map.emplace("d", "e, f, g, h, j, k");
+ auto spec_e = spec_map.emplace("e", "f, g, h, j, k");
+ auto spec_f = spec_map.emplace("f", "g, h, j, k");
+ auto spec_g = spec_map.emplace("g", "h, j, k");
+ auto spec_h = spec_map.emplace("h", "j, k");
+ auto spec_j = spec_map.emplace("j", "k");
+ auto spec_k = spec_map.emplace("k");
+
+ Dependencies::MapPortFileProvider map_port(spec_map.map);
+ auto install_plan = Dependencies::create_feature_install_plan(
+ map_port, {FeatureSpec{spec_a, ""}}, StatusParagraphs(std::move(status_paragraphs)));
+
+ 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");
+}
+
+TEST_CASE ("basic feature test 1", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+ status_paragraphs.push_back(make_status_pgh("a", "b, b[b1]"));
+ status_paragraphs.push_back(make_status_pgh("b"));
+ status_paragraphs.push_back(make_status_feature_pgh("b", "b1"));
+
+ PackageSpecMap spec_map;
+ 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)));
+
+ 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"});
+}
+
+TEST_CASE ("basic feature test 2", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+
+ PackageSpecMap spec_map;
+
+ 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)));
+
+ REQUIRE(install_plan.size() == 2);
+ features_check(install_plan.at(0), "b", {"b1", "b2", "core"});
+ features_check(install_plan.at(1), "a", {"a1", "core"});
+}
+
+TEST_CASE ("basic feature test 3", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+ status_paragraphs.push_back(make_status_pgh("a"));
+
+ PackageSpecMap spec_map;
+
+ auto spec_a = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}}), {"core"}};
+ auto spec_b = FullPackageSpec{spec_map.emplace("b")};
+ auto spec_c = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}};
+
+ auto install_plan = Dependencies::create_feature_install_plan(spec_map.map,
+ FullPackageSpec::to_feature_specs({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"});
+}
+
+TEST_CASE ("basic feature test 4", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+ status_paragraphs.push_back(make_status_pgh("a"));
+ status_paragraphs.push_back(make_status_feature_pgh("a", "a1", ""));
+
+ PackageSpecMap spec_map;
+
+ auto spec_a = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}})};
+ auto spec_b = FullPackageSpec{spec_map.emplace("b")};
+ auto spec_c = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}};
+
+ auto install_plan = Dependencies::create_feature_install_plan(
+ spec_map.map, FullPackageSpec::to_feature_specs({spec_c}), StatusParagraphs(std::move(status_paragraphs)));
+
+ REQUIRE(install_plan.size() == 1);
+ features_check(install_plan.at(0), "c", {"core"});
+}
+
+TEST_CASE ("basic feature test 5", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+
+ PackageSpecMap spec_map;
+
+ auto spec_a =
+ FullPackageSpec{spec_map.emplace("a", "", {{"a1", "b[b1]"}, {"a2", "b[b2]"}, {"a3", "a[a2]"}}), {"a3"}};
+ auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}, {"b2", ""}})};
+
+ auto install_plan = Dependencies::create_feature_install_plan(
+ spec_map.map, FullPackageSpec::to_feature_specs({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"});
+}
+
+TEST_CASE ("basic feature test 6", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+ status_paragraphs.push_back(make_status_pgh("b"));
+
+ PackageSpecMap spec_map;
+ 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)));
+
+ 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"});
+}
+
+TEST_CASE ("basic feature test 7", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+ status_paragraphs.push_back(make_status_pgh("x", "b"));
+ status_paragraphs.push_back(make_status_pgh("b"));
+
+ PackageSpecMap spec_map;
+
+ auto spec_a = FullPackageSpec{spec_map.emplace("a")};
+ auto spec_x = FullPackageSpec{spec_map.emplace("x", "a"), {"core"}};
+ auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}}), {"b1"}};
+
+ auto install_plan = Dependencies::create_feature_install_plan(
+ spec_map.map, FullPackageSpec::to_feature_specs({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");
+
+ // 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"});
+}
+
+TEST_CASE ("basic feature test 8", "[plan][!mayfail]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+ status_paragraphs.push_back(make_status_pgh("a"));
+ status_paragraphs.push_back(make_status_pgh("a"));
+ status_paragraphs.back()->package.spec =
+ PackageSpec::from_name_and_triplet("a", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO);
+
+ PackageSpecMap spec_map(Triplet::X64_WINDOWS);
+ auto spec_a_64 = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}}), {"core"}};
+ auto spec_b_64 = FullPackageSpec{spec_map.emplace("b")};
+ auto spec_c_64 = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}};
+
+ spec_map.triplet = Triplet::X86_WINDOWS;
+ auto spec_a_86 = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}}), {"core"}};
+ auto spec_b_86 = FullPackageSpec{spec_map.emplace("b")};
+ auto spec_c_86 = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}};
+
+ auto install_plan = Dependencies::create_feature_install_plan(
+ spec_map.map,
+ FullPackageSpec::to_feature_specs({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);
+ 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"});
+}
+
+TEST_CASE ("install all features test", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+
+ PackageSpecMap spec_map(Triplet::X64_WINDOWS);
+ auto spec_a_64 = FullPackageSpec{spec_map.emplace("a", "", {{"0", ""}, {"1", ""}}), {"core"}};
+
+ 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)));
+
+ REQUIRE(install_plan.size() == 1);
+ features_check(install_plan.at(0), "a", {"0", "1", "core"}, Triplet::X64_WINDOWS);
+}
+
+TEST_CASE ("install default features test 1", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+
+ // Add a port "a" with default features "1" and features "0" and "1".
+ PackageSpecMap spec_map(Triplet::X64_WINDOWS);
+ spec_map.emplace("a", "", {{"0", ""}, {"1", ""}}, {"1"});
+
+ // 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)));
+
+ // 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);
+}
+
+TEST_CASE ("install default features test 2", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+ status_paragraphs.push_back(make_status_pgh("a"));
+ status_paragraphs.back()->package.spec =
+ PackageSpec::from_name_and_triplet("a", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO);
+
+ // Add a port "a" of which "core" is already installed, but we will
+ // install the default features "explicitly"
+ // "a" has two features, of which "a1" is default.
+ PackageSpecMap spec_map(Triplet::X64_WINDOWS);
+ spec_map.emplace("a", "", {{"a0", ""}, {"a1", ""}}, {"a1"});
+
+ // 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)));
+
+ // 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);
+}
+
+TEST_CASE ("install default features test 3", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+
+ // "a" has two features, of which "a1" is default.
+ PackageSpecMap spec_map(Triplet::X64_WINDOWS);
+ spec_map.emplace("a", "", {{"a0", ""}, {"a1", ""}}, {"a1"});
+
+ // 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)));
+
+ // Expect the default feature not to get installed.
+ REQUIRE(install_plan.size() == 1);
+ features_check(install_plan.at(0), "a", {"core"}, Triplet::X64_WINDOWS);
+}
+
+TEST_CASE ("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 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)));
+
+ // 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);
+}
+
+TEST_CASE ("do not install default features of existing dependency", "[plan]")
+{
+ // 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"});
+
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+ // "b[core]" is already installed
+ status_paragraphs.push_back(make_status_pgh("b"));
+ status_paragraphs.back()->package.spec =
+ PackageSpec::from_name_and_triplet("b", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO);
+
+ // 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)));
+
+ // 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);
+}
+
+TEST_CASE ("install default features of dependency test 2", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+ status_paragraphs.push_back(make_status_pgh("b"));
+ status_paragraphs.back()->package.spec =
+ PackageSpec::from_name_and_triplet("b", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO);
+
+ // Add a port "a" which depends on the core of "b", which was already
+ // installed explicitly
+ 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 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)));
+
+ // 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);
+}
+
+TEST_CASE ("install plan action dependencies", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+
+ // Add a port "a" which depends on the core of "b", which was already
+ // installed explicitly
+ PackageSpecMap spec_map(Triplet::X64_WINDOWS);
+ auto spec_c = spec_map.emplace("c");
+ auto spec_b = spec_map.emplace("b", "c");
+ spec_map.emplace("a", "b");
+
+ // 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)));
+
+ 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);
+ 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});
+}
+
+TEST_CASE ("install plan action dependencies 2", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+
+ // Add a port "a" which depends on the core of "b", which was already
+ // installed explicitly
+ PackageSpecMap spec_map(Triplet::X64_WINDOWS);
+ auto spec_c = spec_map.emplace("c");
+ auto spec_b = spec_map.emplace("b", "c");
+ spec_map.emplace("a", "c, b");
+
+ // 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)));
+
+ 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);
+ 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});
+}
+
+TEST_CASE ("install plan action dependencies 3", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+
+ // Add a port "a" which depends on the core of "b", which was already
+ // installed explicitly
+ PackageSpecMap spec_map(Triplet::X64_WINDOWS);
+ spec_map.emplace("a", "", {{"0", ""}, {"1", "a[0]"}}, {"1"});
+
+ // 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)));
+
+ 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>{});
+}
+
+TEST_CASE ("install with default features", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> pghs;
+ pghs.push_back(make_status_pgh("a", ""));
+ StatusParagraphs status_db(std::move(pghs));
+
+ PackageSpecMap spec_map;
+ 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);
+
+ 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"});
+}
+
+TEST_CASE ("upgrade with default features 1", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> pghs;
+ pghs.push_back(make_status_pgh("a", "", "1"));
+ pghs.push_back(make_status_feature_pgh("a", "0"));
+ StatusParagraphs status_db(std::move(pghs));
+
+ // Add a port "a" of which "core" and "0" are already installed.
+ 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();
+
+ // 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"});
+}
+
+TEST_CASE ("upgrade with default features 2", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> pghs;
+ // B is currently installed _without_ default feature b0
+ pghs.push_back(make_status_pgh("b", "", "b0", "x64-windows"));
+ pghs.push_back(make_status_pgh("a", "b[core]", "", "x64-windows"));
+
+ StatusParagraphs status_db(std::move(pghs));
+
+ PackageSpecMap spec_map(Triplet::X64_WINDOWS);
+ 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();
+
+ // 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);
+}
+
+TEST_CASE ("upgrade with default features 3", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> pghs;
+ // note: unrelated package due to x86 triplet
+ pghs.push_back(make_status_pgh("b", "", "", "x86-windows"));
+ pghs.push_back(make_status_pgh("a", "", "", "x64-windows"));
+
+ StatusParagraphs status_db(std::move(pghs));
+
+ PackageSpecMap spec_map(Triplet::X64_WINDOWS);
+ 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();
+
+ // 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);
+}
+
+TEST_CASE ("upgrade with new default feature", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> pghs;
+ pghs.push_back(make_status_pgh("a", "", "0", "x86-windows"));
+
+ StatusParagraphs status_db(std::move(pghs));
+
+ 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();
+
+ // 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);
+}
+
+TEST_CASE ("transitive features test", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+
+ PackageSpecMap spec_map(Triplet::X64_WINDOWS);
+ auto spec_a_64 = FullPackageSpec{spec_map.emplace("a", "b", {{"0", "b[0]"}}), {"core"}};
+ auto spec_b_64 = FullPackageSpec{spec_map.emplace("b", "c", {{"0", "c[0]"}}), {"core"}};
+ auto spec_c_64 = FullPackageSpec{spec_map.emplace("c", "", {{"0", ""}}), {"core"}};
+
+ 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)));
+
+ 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);
+}
+
+TEST_CASE ("no transitive features test", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+
+ PackageSpecMap spec_map(Triplet::X64_WINDOWS);
+ auto spec_a_64 = FullPackageSpec{spec_map.emplace("a", "b", {{"0", ""}}), {"core"}};
+ auto spec_b_64 = FullPackageSpec{spec_map.emplace("b", "c", {{"0", ""}}), {"core"}};
+ auto spec_c_64 = FullPackageSpec{spec_map.emplace("c", "", {{"0", ""}}), {"core"}};
+
+ 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)));
+
+ 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);
+}
+
+TEST_CASE ("only transitive features test", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+
+ PackageSpecMap spec_map(Triplet::X64_WINDOWS);
+ auto spec_a_64 = FullPackageSpec{spec_map.emplace("a", "", {{"0", "b[0]"}}), {"core"}};
+ auto spec_b_64 = FullPackageSpec{spec_map.emplace("b", "", {{"0", "c[0]"}}), {"core"}};
+ auto spec_c_64 = FullPackageSpec{spec_map.emplace("c", "", {{"0", ""}}), {"core"}};
+
+ 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)));
+
+ 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);
+}
+
+TEST_CASE ("basic remove scheme", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> pghs;
+ pghs.push_back(make_status_pgh("a"));
+ StatusParagraphs status_db(std::move(pghs));
+
+ auto remove_plan = Dependencies::create_remove_plan({unsafe_pspec("a")}, status_db);
+
+ REQUIRE(remove_plan.size() == 1);
+ REQUIRE(remove_plan.at(0).spec.name() == "a");
+}
+
+TEST_CASE ("recurse remove scheme", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> pghs;
+ pghs.push_back(make_status_pgh("a"));
+ pghs.push_back(make_status_pgh("b", "a"));
+ StatusParagraphs status_db(std::move(pghs));
+
+ auto remove_plan = Dependencies::create_remove_plan({unsafe_pspec("a")}, status_db);
+
+ REQUIRE(remove_plan.size() == 2);
+ REQUIRE(remove_plan.at(0).spec.name() == "b");
+ REQUIRE(remove_plan.at(1).spec.name() == "a");
+}
+
+TEST_CASE ("features depend remove scheme", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> pghs;
+ pghs.push_back(make_status_pgh("a"));
+ pghs.push_back(make_status_pgh("b"));
+ pghs.push_back(make_status_feature_pgh("b", "0", "a"));
+ StatusParagraphs status_db(std::move(pghs));
+
+ auto remove_plan = Dependencies::create_remove_plan({unsafe_pspec("a")}, status_db);
+
+ REQUIRE(remove_plan.size() == 2);
+ REQUIRE(remove_plan.at(0).spec.name() == "b");
+ REQUIRE(remove_plan.at(1).spec.name() == "a");
+}
+
+TEST_CASE ("features depend remove scheme once removed", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> pghs;
+ pghs.push_back(make_status_pgh("expat"));
+ pghs.push_back(make_status_pgh("vtk", "expat"));
+ pghs.push_back(make_status_pgh("opencv"));
+ pghs.push_back(make_status_feature_pgh("opencv", "vtk", "vtk"));
+ StatusParagraphs status_db(std::move(pghs));
+
+ auto remove_plan = Dependencies::create_remove_plan({unsafe_pspec("expat")}, status_db);
+
+ REQUIRE(remove_plan.size() == 3);
+ REQUIRE(remove_plan.at(0).spec.name() == "opencv");
+ REQUIRE(remove_plan.at(1).spec.name() == "vtk");
+ REQUIRE(remove_plan.at(2).spec.name() == "expat");
+}
+
+TEST_CASE ("features depend remove scheme once removed x64", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> pghs;
+ pghs.push_back(make_status_pgh("expat", "", "", "x64"));
+ pghs.push_back(make_status_pgh("vtk", "expat", "", "x64"));
+ pghs.push_back(make_status_pgh("opencv", "", "", "x64"));
+ pghs.push_back(make_status_feature_pgh("opencv", "vtk", "vtk", "x64"));
+ StatusParagraphs status_db(std::move(pghs));
+
+ auto remove_plan =
+ Dependencies::create_remove_plan({unsafe_pspec("expat", Triplet::from_canonical_name("x64"))}, status_db);
+
+ REQUIRE(remove_plan.size() == 3);
+ REQUIRE(remove_plan.at(0).spec.name() == "opencv");
+ REQUIRE(remove_plan.at(1).spec.name() == "vtk");
+ REQUIRE(remove_plan.at(2).spec.name() == "expat");
+}
+
+TEST_CASE ("features depend core remove scheme", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> pghs;
+ pghs.push_back(make_status_pgh("curl", "", "", "x64"));
+ pghs.push_back(make_status_pgh("cpr", "curl[core]", "", "x64"));
+ StatusParagraphs status_db(std::move(pghs));
+
+ auto remove_plan =
+ Dependencies::create_remove_plan({unsafe_pspec("curl", Triplet::from_canonical_name("x64"))}, status_db);
+
+ REQUIRE(remove_plan.size() == 2);
+ REQUIRE(remove_plan.at(0).spec.name() == "cpr");
+ REQUIRE(remove_plan.at(1).spec.name() == "curl");
+}
+
+TEST_CASE ("features depend core remove scheme 2", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> pghs;
+ pghs.push_back(make_status_pgh("curl", "", "", "x64"));
+ pghs.push_back(make_status_feature_pgh("curl", "a", "", "x64"));
+ pghs.push_back(make_status_feature_pgh("curl", "b", "curl[a]", "x64"));
+ StatusParagraphs status_db(std::move(pghs));
+
+ auto remove_plan =
+ Dependencies::create_remove_plan({unsafe_pspec("curl", Triplet::from_canonical_name("x64"))}, status_db);
+
+ REQUIRE(remove_plan.size() == 1);
+ REQUIRE(remove_plan.at(0).spec.name() == "curl");
+}
+
+TEST_CASE ("basic upgrade scheme", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> pghs;
+ pghs.push_back(make_status_pgh("a"));
+ StatusParagraphs status_db(std::move(pghs));
+
+ 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();
+
+ 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());
+}
+
+TEST_CASE ("basic upgrade scheme with recurse", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> pghs;
+ pghs.push_back(make_status_pgh("a"));
+ pghs.push_back(make_status_pgh("b", "a"));
+ StatusParagraphs status_db(std::move(pghs));
+
+ PackageSpecMap spec_map;
+ 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();
+
+ 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());
+}
+
+TEST_CASE ("basic upgrade scheme with bystander", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> pghs;
+ pghs.push_back(make_status_pgh("a"));
+ pghs.push_back(make_status_pgh("b"));
+ StatusParagraphs status_db(std::move(pghs));
+
+ PackageSpecMap spec_map;
+ 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();
+
+ 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());
+}
+
+TEST_CASE ("basic upgrade scheme with new dep", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> pghs;
+ pghs.push_back(make_status_pgh("a"));
+ StatusParagraphs status_db(std::move(pghs));
+
+ PackageSpecMap spec_map;
+ 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();
+
+ 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());
+}
+
+TEST_CASE ("basic upgrade scheme with features", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> pghs;
+ pghs.push_back(make_status_pgh("a"));
+ pghs.push_back(make_status_feature_pgh("a", "a1"));
+ StatusParagraphs status_db(std::move(pghs));
+
+ 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();
+
+ 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"});
+}
+
+TEST_CASE ("basic upgrade scheme with new default feature", "[plan]")
+{
+ // only core of package "a" is installed
+ std::vector<std::unique_ptr<StatusParagraph>> pghs;
+ pghs.push_back(make_status_pgh("a"));
+ StatusParagraphs status_db(std::move(pghs));
+
+ // a1 was added as a default feature and should be installed in upgrade
+ 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();
+
+ 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"});
+}
+
+TEST_CASE ("basic upgrade scheme with self features", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> pghs;
+ pghs.push_back(make_status_pgh("a"));
+ pghs.push_back(make_status_feature_pgh("a", "a1", ""));
+ pghs.push_back(make_status_feature_pgh("a", "a2", "a[a1]"));
+ StatusParagraphs status_db(std::move(pghs));
+
+ 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();
+
+ 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"});
+}
+
+TEST_CASE ("basic export scheme", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> pghs;
+ pghs.push_back(make_status_pgh("a"));
+ StatusParagraphs status_db(std::move(pghs));
+
+ PackageSpecMap spec_map;
+ auto spec_a = spec_map.emplace("a");
+
+ auto plan = Dependencies::create_export_plan({spec_a}, status_db);
+
+ REQUIRE(plan.size() == 1);
+ REQUIRE(plan.at(0).spec.name() == "a");
+ REQUIRE(plan.at(0).plan_type == Dependencies::ExportPlanType::ALREADY_BUILT);
+}
+
+TEST_CASE ("basic export scheme with recurse", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> pghs;
+ pghs.push_back(make_status_pgh("a"));
+ pghs.push_back(make_status_pgh("b", "a"));
+ StatusParagraphs status_db(std::move(pghs));
+
+ PackageSpecMap spec_map;
+ auto spec_a = spec_map.emplace("a");
+ auto spec_b = spec_map.emplace("b", "a");
+
+ auto plan = Dependencies::create_export_plan({spec_b}, status_db);
+
+ REQUIRE(plan.size() == 2);
+ REQUIRE(plan.at(0).spec.name() == "a");
+ REQUIRE(plan.at(0).plan_type == Dependencies::ExportPlanType::ALREADY_BUILT);
+
+ REQUIRE(plan.at(1).spec.name() == "b");
+ REQUIRE(plan.at(1).plan_type == Dependencies::ExportPlanType::ALREADY_BUILT);
+}
+
+TEST_CASE ("basic export scheme with bystander", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> pghs;
+ pghs.push_back(make_status_pgh("a"));
+ pghs.push_back(make_status_pgh("b"));
+ StatusParagraphs status_db(std::move(pghs));
+
+ PackageSpecMap spec_map;
+ auto spec_a = spec_map.emplace("a");
+ auto spec_b = spec_map.emplace("b", "a");
+
+ auto plan = Dependencies::create_export_plan({spec_a}, status_db);
+
+ REQUIRE(plan.size() == 1);
+ REQUIRE(plan.at(0).spec.name() == "a");
+ REQUIRE(plan.at(0).plan_type == Dependencies::ExportPlanType::ALREADY_BUILT);
+}
+
+TEST_CASE ("basic export scheme with missing", "[plan]")
+{
+ StatusParagraphs status_db;
+
+ PackageSpecMap spec_map;
+ auto spec_a = spec_map.emplace("a");
+
+ auto plan = Dependencies::create_export_plan({spec_a}, status_db);
+
+ REQUIRE(plan.size() == 1);
+ REQUIRE(plan.at(0).spec.name() == "a");
+ REQUIRE(plan.at(0).plan_type == Dependencies::ExportPlanType::NOT_BUILT);
+}
+
+TEST_CASE ("basic export scheme with features", "[plan]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> pghs;
+ pghs.push_back(make_status_pgh("b"));
+ pghs.push_back(make_status_pgh("a"));
+ pghs.push_back(make_status_feature_pgh("a", "a1", "b[core]"));
+ StatusParagraphs status_db(std::move(pghs));
+
+ PackageSpecMap spec_map;
+ auto spec_a = spec_map.emplace("a", "", {{"a1", ""}});
+
+ auto plan = Dependencies::create_export_plan({spec_a}, status_db);
+
+ REQUIRE(plan.size() == 2);
+
+ REQUIRE(plan.at(0).spec.name() == "b");
+ REQUIRE(plan.at(0).plan_type == Dependencies::ExportPlanType::ALREADY_BUILT);
+
+ REQUIRE(plan.at(1).spec.name() == "a");
+ REQUIRE(plan.at(1).plan_type == Dependencies::ExportPlanType::ALREADY_BUILT);
+}
diff --git a/toolsrc/src/vcpkg-test/specifier.cpp b/toolsrc/src/vcpkg-test/specifier.cpp
new file mode 100644
index 000000000..330a96d78
--- /dev/null
+++ b/toolsrc/src/vcpkg-test/specifier.cpp
@@ -0,0 +1,134 @@
+#include <vcpkg-test/catch.h>
+
+#include <vcpkg/base/util.h>
+#include <vcpkg/packagespec.h>
+
+using namespace vcpkg;
+
+TEST_CASE ("specifier conversion", "[specifier]")
+{
+ SECTION ("full package spec to feature specs")
+ {
+ constexpr std::size_t SPEC_SIZE = 6;
+
+ 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"}}});
+
+ REQUIRE(fspecs.size() == SPEC_SIZE);
+
+ std::array<const char*, SPEC_SIZE> features = {"", "0", "1", "", "2", "3"};
+ 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)
+ {
+ REQUIRE(features.at(i) == fspecs.at(i).feature());
+ REQUIRE(*specs.at(i) == fspecs.at(i).spec());
+ }
+ }
+}
+
+TEST_CASE ("specifier parsing", "[specifier]")
+{
+ SECTION ("parsed specifier from string")
+ {
+ auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib");
+ REQUIRE(maybe_spec.error() == vcpkg::PackageSpecParseResult::SUCCESS);
+
+ auto& spec = *maybe_spec.get();
+ REQUIRE(spec.name == "zlib");
+ REQUIRE(spec.features.size() == 0);
+ REQUIRE(spec.triplet == "");
+ }
+
+ SECTION ("parsed specifier from string with triplet")
+ {
+ auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib:x64-uwp");
+ REQUIRE(maybe_spec.error() == vcpkg::PackageSpecParseResult::SUCCESS);
+
+ auto& spec = *maybe_spec.get();
+ REQUIRE(spec.name == "zlib");
+ REQUIRE(spec.triplet == "x64-uwp");
+ }
+
+ SECTION ("parsed specifier from string with colons")
+ {
+ auto ec = vcpkg::ParsedSpecifier::from_string("zlib:x86-uwp:").error();
+ REQUIRE(ec == vcpkg::PackageSpecParseResult::TOO_MANY_COLONS);
+ }
+
+ SECTION ("parsed specifier from string with feature")
+ {
+ auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib[feature]:x64-uwp");
+ REQUIRE(maybe_spec.error() == vcpkg::PackageSpecParseResult::SUCCESS);
+
+ auto& spec = *maybe_spec.get();
+ REQUIRE(spec.name == "zlib");
+ REQUIRE(spec.features.size() == 1);
+ REQUIRE(spec.features.at(0) == "feature");
+ REQUIRE(spec.triplet == "x64-uwp");
+ }
+
+ SECTION ("parsed specifier from string with many features")
+ {
+ auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib[0, 1,2]");
+ REQUIRE(maybe_spec.error() == vcpkg::PackageSpecParseResult::SUCCESS);
+
+ auto& spec = *maybe_spec.get();
+ REQUIRE(spec.name == "zlib");
+ REQUIRE(spec.features.size() == 3);
+ REQUIRE(spec.features.at(0) == "0");
+ REQUIRE(spec.features.at(1) == "1");
+ REQUIRE(spec.features.at(2) == "2");
+ REQUIRE(spec.triplet == "");
+ }
+
+ SECTION ("parsed specifier wildcard feature")
+ {
+ auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib[*]");
+ REQUIRE(maybe_spec.error() == vcpkg::PackageSpecParseResult::SUCCESS);
+
+ auto& spec = *maybe_spec.get();
+ REQUIRE(spec.name == "zlib");
+ REQUIRE(spec.features.size() == 1);
+ REQUIRE(spec.features.at(0) == "*");
+ REQUIRE(spec.triplet == "");
+ }
+
+ SECTION ("expand wildcards")
+ {
+ 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});
+ 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));
+ }
+
+#if defined(_WIN32)
+ SECTION ("ASCII to utf16")
+ {
+ auto str = vcpkg::Strings::to_utf16("abc");
+ REQUIRE(str == L"abc");
+ }
+
+ SECTION ("ASCII to utf16 with whitespace")
+ {
+ auto str = vcpkg::Strings::to_utf16("abc -x86-windows");
+ REQUIRE(str == L"abc -x86-windows");
+ }
+#endif
+};
diff --git a/toolsrc/src/vcpkg-test/statusparagraphs.cpp b/toolsrc/src/vcpkg-test/statusparagraphs.cpp
new file mode 100644
index 000000000..c0833e8ba
--- /dev/null
+++ b/toolsrc/src/vcpkg-test/statusparagraphs.cpp
@@ -0,0 +1,110 @@
+#include <vcpkg-test/catch.h>
+#include <vcpkg-test/util.h>
+
+#include <vcpkg/base/util.h>
+#include <vcpkg/paragraphs.h>
+#include <vcpkg/statusparagraphs.h>
+
+using namespace vcpkg;
+using namespace vcpkg::Paragraphs;
+using namespace vcpkg::Test;
+
+TEST_CASE ("find installed", "[statusparagraphs]")
+{
+ auto pghs = parse_paragraphs(R"(
+Package: ffmpeg
+Version: 3.3.3
+Architecture: x64-windows
+Multi-Arch: same
+Description:
+Status: install ok installed
+)");
+
+ REQUIRE(pghs);
+
+ StatusParagraphs status_db(
+ Util::fmap(*pghs.get(), [](RawParagraph& rpgh) { return std::make_unique<StatusParagraph>(std::move(rpgh)); }));
+
+ auto it = status_db.find_installed(unsafe_pspec("ffmpeg", Triplet::X64_WINDOWS));
+ REQUIRE(it != status_db.end());
+}
+
+TEST_CASE ("find not installed", "[statusparagraphs]")
+{
+ auto pghs = parse_paragraphs(R"(
+Package: ffmpeg
+Version: 3.3.3
+Architecture: x64-windows
+Multi-Arch: same
+Description:
+Status: purge ok not-installed
+)");
+
+ REQUIRE(pghs);
+
+ StatusParagraphs status_db(
+ Util::fmap(*pghs.get(), [](RawParagraph& rpgh) { return std::make_unique<StatusParagraph>(std::move(rpgh)); }));
+
+ auto it = status_db.find_installed(unsafe_pspec("ffmpeg", Triplet::X64_WINDOWS));
+ REQUIRE(it == status_db.end());
+}
+
+TEST_CASE ("find with feature packages", "[statusparagraphs]")
+{
+ auto pghs = parse_paragraphs(R"(
+Package: ffmpeg
+Version: 3.3.3
+Architecture: x64-windows
+Multi-Arch: same
+Description:
+Status: install ok installed
+
+Package: ffmpeg
+Feature: openssl
+Depends: openssl
+Architecture: x64-windows
+Multi-Arch: same
+Description:
+Status: purge ok not-installed
+)");
+
+ REQUIRE(pghs);
+
+ StatusParagraphs status_db(
+ Util::fmap(*pghs.get(), [](RawParagraph& rpgh) { return std::make_unique<StatusParagraph>(std::move(rpgh)); }));
+
+ auto it = status_db.find_installed(unsafe_pspec("ffmpeg", Triplet::X64_WINDOWS));
+ REQUIRE(it != status_db.end());
+
+ // Feature "openssl" is not installed and should not be found
+ auto it1 = status_db.find_installed({unsafe_pspec("ffmpeg", Triplet::X64_WINDOWS), "openssl"});
+ REQUIRE(it1 == status_db.end());
+}
+
+TEST_CASE ("find for feature packages", "[statusparagraphs]")
+{
+ auto pghs = parse_paragraphs(R"(
+Package: ffmpeg
+Version: 3.3.3
+Architecture: x64-windows
+Multi-Arch: same
+Description:
+Status: install ok installed
+
+Package: ffmpeg
+Feature: openssl
+Depends: openssl
+Architecture: x64-windows
+Multi-Arch: same
+Description:
+Status: install ok installed
+)");
+ REQUIRE(pghs);
+
+ StatusParagraphs status_db(
+ Util::fmap(*pghs.get(), [](RawParagraph& rpgh) { return std::make_unique<StatusParagraph>(std::move(rpgh)); }));
+
+ // Feature "openssl" is installed and should therefore be found
+ auto it = status_db.find_installed({unsafe_pspec("ffmpeg", Triplet::X64_WINDOWS), "openssl"});
+ REQUIRE(it != status_db.end());
+}
diff --git a/toolsrc/src/vcpkg-test/strings.cpp b/toolsrc/src/vcpkg-test/strings.cpp
new file mode 100644
index 000000000..6b744eee6
--- /dev/null
+++ b/toolsrc/src/vcpkg-test/strings.cpp
@@ -0,0 +1,33 @@
+#include <vcpkg-test/catch.h>
+
+#include <vcpkg/base/strings.h>
+
+#include <cstdint>
+#include <utility>
+#include <vector>
+
+TEST_CASE ("b32 encoding", "[strings]")
+{
+ using u64 = std::uint64_t;
+
+ std::vector<std::pair<std::uint64_t, std::string>> map;
+
+ map.emplace_back(0, "AAAAAAAAAAAAA");
+ map.emplace_back(1, "BAAAAAAAAAAAA");
+
+ map.emplace_back(u64(1) << 32, "AAAAAAEAAAAAA");
+ map.emplace_back((u64(1) << 32) + 1, "BAAAAAEAAAAAA");
+
+ map.emplace_back(0xE4D0'1065'D11E'0229, "JRA4RIXMQAUJO");
+ map.emplace_back(0xA626'FE45'B135'07FF, "77BKTYWI6XJMK");
+ map.emplace_back(0xEE36'D228'0C31'D405, "FAVDDGAFSWN4O");
+ map.emplace_back(0x1405'64E7'FE7E'A88C, "MEK5H774ELBIB");
+ map.emplace_back(0xFFFF'FFFF'FFFF'FFFF, "777777777777P");
+
+ std::string result;
+ for (const auto& pr : map)
+ {
+ result = vcpkg::Strings::b32_encode(pr.first);
+ REQUIRE(vcpkg::Strings::b32_encode(pr.first) == pr.second);
+ }
+}
diff --git a/toolsrc/src/vcpkg-test/supports.cpp b/toolsrc/src/vcpkg-test/supports.cpp
new file mode 100644
index 000000000..8bd386da0
--- /dev/null
+++ b/toolsrc/src/vcpkg-test/supports.cpp
@@ -0,0 +1,79 @@
+#include <vcpkg-test/catch.h>
+
+#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
new file mode 100644
index 000000000..70b2f04c1
--- /dev/null
+++ b/toolsrc/src/vcpkg-test/update.cpp
@@ -0,0 +1,102 @@
+#include <vcpkg-test/catch.h>
+#include <vcpkg-test/util.h>
+
+#include <vcpkg/base/sortedvector.h>
+
+#include <vcpkg/update.h>
+
+using namespace vcpkg;
+using namespace vcpkg::Update;
+using namespace vcpkg::Test;
+
+using Pgh = std::vector<std::unordered_map<std::string, std::string>>;
+
+TEST_CASE ("find outdated packages basic", "[update]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+ status_paragraphs.push_back(make_status_pgh("a"));
+ status_paragraphs.back()->package.version = "2";
+
+ 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"}}}));
+ map.emplace("a", SourceControlFileLocation{std::move(scf), ""});
+ Dependencies::MapPortFileProvider provider(map);
+
+ auto pkgs = SortedVector<OutdatedPackage>(Update::find_outdated_packages(provider, status_db),
+ &OutdatedPackage::compare_by_name);
+
+ REQUIRE(pkgs.size() == 1);
+ REQUIRE(pkgs[0].version_diff.left.to_string() == "2");
+ REQUIRE(pkgs[0].version_diff.right.to_string() == "0");
+}
+
+TEST_CASE ("find outdated packages features", "[update]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+ status_paragraphs.push_back(make_status_pgh("a"));
+ status_paragraphs.back()->package.version = "2";
+
+ status_paragraphs.push_back(make_status_feature_pgh("a", "b"));
+ status_paragraphs.back()->package.version = "2";
+
+ 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"}}}));
+ map.emplace("a", SourceControlFileLocation{std::move(scf), ""});
+ Dependencies::MapPortFileProvider provider(map);
+
+ auto pkgs = SortedVector<OutdatedPackage>(Update::find_outdated_packages(provider, status_db),
+ &OutdatedPackage::compare_by_name);
+
+ REQUIRE(pkgs.size() == 1);
+ REQUIRE(pkgs[0].version_diff.left.to_string() == "2");
+ REQUIRE(pkgs[0].version_diff.right.to_string() == "0");
+}
+
+TEST_CASE ("find outdated packages features 2", "[update]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+ status_paragraphs.push_back(make_status_pgh("a"));
+ status_paragraphs.back()->package.version = "2";
+
+ status_paragraphs.push_back(make_status_feature_pgh("a", "b"));
+ status_paragraphs.back()->package.version = "0";
+ status_paragraphs.back()->state = InstallState::NOT_INSTALLED;
+ status_paragraphs.back()->want = Want::PURGE;
+
+ 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"}}}));
+ map.emplace("a", SourceControlFileLocation{std::move(scf), ""});
+ Dependencies::MapPortFileProvider provider(map);
+
+ auto pkgs = SortedVector<OutdatedPackage>(Update::find_outdated_packages(provider, status_db),
+ &OutdatedPackage::compare_by_name);
+
+ REQUIRE(pkgs.size() == 1);
+ REQUIRE(pkgs[0].version_diff.left.to_string() == "2");
+ REQUIRE(pkgs[0].version_diff.right.to_string() == "0");
+}
+
+TEST_CASE ("find outdated packages none", "[update]")
+{
+ std::vector<std::unique_ptr<StatusParagraph>> status_paragraphs;
+ status_paragraphs.push_back(make_status_pgh("a"));
+ status_paragraphs.back()->package.version = "2";
+
+ 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"}}}));
+ map.emplace("a", SourceControlFileLocation{std::move(scf), ""});
+ Dependencies::MapPortFileProvider provider(map);
+
+ auto pkgs = SortedVector<OutdatedPackage>(Update::find_outdated_packages(provider, status_db),
+ &OutdatedPackage::compare_by_name);
+
+ REQUIRE(pkgs.size() == 0);
+}
diff --git a/toolsrc/src/vcpkg-test/util.cpp b/toolsrc/src/vcpkg-test/util.cpp
new file mode 100644
index 000000000..a2343c21b
--- /dev/null
+++ b/toolsrc/src/vcpkg-test/util.cpp
@@ -0,0 +1,182 @@
+#include <vcpkg-test/catch.h>
+#include <vcpkg-test/util.h>
+
+#include <vcpkg/base/checks.h>
+#include <vcpkg/base/files.h>
+#include <vcpkg/statusparagraph.h>
+
+// used to get the implementation specific compiler flags (i.e., __cpp_lib_filesystem)
+#include <ciso646>
+
+#include <iostream>
+#include <memory>
+
+#if defined(_WIN32)
+#include <windows.h>
+#endif
+
+#define FILESYSTEM_SYMLINK_STD 0
+#define FILESYSTEM_SYMLINK_UNIX 1
+#define FILESYSTEM_SYMLINK_NONE 2
+
+#if defined(__cpp_lib_filesystem)
+
+#define FILESYSTEM_SYMLINK FILESYSTEM_SYMLINK_STD
+#include <filesystem> // required for filesystem::create_{directory_}symlink
+
+#elif !defined(_MSC_VER)
+
+#define FILESYSTEM_SYMLINK FILESYSTEM_SYMLINK_UNIX
+#include <unistd.h>
+
+#else
+
+#define FILESYSTEM_SYMLINK FILESYSTEM_SYMLINK_NONE
+
+#endif
+
+namespace vcpkg::Test
+{
+ std::unique_ptr<vcpkg::StatusParagraph> make_status_pgh(const char* name,
+ const char* depends,
+ const char* default_features,
+ const char* triplet)
+ {
+ using Pgh = std::unordered_map<std::string, std::string>;
+ return std::make_unique<StatusParagraph>(Pgh{{"Package", name},
+ {"Version", "1"},
+ {"Architecture", triplet},
+ {"Multi-Arch", "same"},
+ {"Depends", depends},
+ {"Default-Features", default_features},
+ {"Status", "install ok installed"}});
+ }
+
+ std::unique_ptr<StatusParagraph> make_status_feature_pgh(const char* name,
+ const char* feature,
+ const char* depends,
+ const char* triplet)
+ {
+ using Pgh = std::unordered_map<std::string, std::string>;
+ return std::make_unique<StatusParagraph>(Pgh{{"Package", name},
+ {"Version", "1"},
+ {"Feature", feature},
+ {"Architecture", triplet},
+ {"Multi-Arch", "same"},
+ {"Depends", depends},
+ {"Status", "install ok installed"}});
+ }
+
+ PackageSpec unsafe_pspec(std::string name, Triplet t)
+ {
+ auto m_ret = PackageSpec::from_name_and_triplet(name, t);
+ REQUIRE(m_ret.has_value());
+ return m_ret.value_or_exit(VCPKG_LINE_INFO);
+ }
+
+ static AllowSymlinks internal_can_create_symlinks() noexcept
+ {
+#if FILESYSTEM_SYMLINK == FILESYSTEM_SYMLINK_NONE
+ return AllowSymlinks::No;
+#elif FILESYSTEM_SYMLINK == FILESYSTEM_SYMLINK_UNIX
+ return AllowSymlinks::Yes;
+#elif !defined(_WIN32) // FILESYSTEM_SYMLINK == FILESYSTEM_SYMLINK_STD
+ return AllowSymlinks::Yes;
+#else
+ HKEY key;
+ bool allow_symlinks = true;
+
+ const auto status = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE, LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock)", 0, 0, &key);
+
+ if (status == ERROR_FILE_NOT_FOUND)
+ {
+ allow_symlinks = false;
+ std::clog << "Symlinks are not allowed on this system\n";
+ }
+
+ if (status == ERROR_SUCCESS) RegCloseKey(key);
+
+ return allow_symlinks ? AllowSymlinks::Yes : AllowSymlinks::No;
+#endif
+ }
+ const static AllowSymlinks CAN_CREATE_SYMLINKS = internal_can_create_symlinks();
+
+ AllowSymlinks can_create_symlinks() noexcept { return CAN_CREATE_SYMLINKS; }
+
+ static fs::path internal_base_temporary_directory()
+ {
+#if defined(_WIN32)
+ wchar_t* tmp = static_cast<wchar_t*>(std::calloc(32'767, 2));
+
+ if (!GetEnvironmentVariableW(L"TEMP", tmp, 32'767))
+ {
+ std::cerr << "No temporary directory found.\n";
+ std::abort();
+ }
+
+ fs::path result = tmp;
+ std::free(tmp);
+
+ return result / L"vcpkg-test";
+#else
+ return "/tmp/vcpkg-test";
+#endif
+ }
+
+ const static fs::path BASE_TEMPORARY_DIRECTORY = internal_base_temporary_directory();
+
+ const fs::path& base_temporary_directory() noexcept { return BASE_TEMPORARY_DIRECTORY; }
+
+#if FILESYSTEM_SYMLINK == FILESYSTEM_SYMLINK_NONE
+ constexpr char no_filesystem_message[] =
+ "<filesystem> doesn't exist; on windows, we don't attempt to use the win32 calls to create symlinks";
+#endif
+
+ void create_symlink(const fs::path& target, const fs::path& file, std::error_code& ec)
+ {
+#if FILESYSTEM_SYMLINK == FILESYSTEM_SYMLINK_STD
+ if (can_create_symlinks())
+ {
+ std::filesystem::path targetp = target.native();
+ std::filesystem::path filep = file.native();
+
+ std::filesystem::create_symlink(targetp, filep, ec);
+ }
+ else
+ {
+ vcpkg::Checks::exit_with_message(VCPKG_LINE_INFO, "Symlinks are not allowed on this system");
+ }
+#elif FILESYSTEM_SYMLINK == FILESYSTEM_SYMLINK_UNIX
+ if (symlink(target.c_str(), file.c_str()) != 0)
+ {
+ ec.assign(errno, std::system_category());
+ }
+#else
+ static_cast<void>(ec);
+ vcpkg::Checks::exit_with_message(VCPKG_LINE_INFO, no_filesystem_message);
+#endif
+ }
+
+ void create_directory_symlink(const fs::path& target, const fs::path& file, std::error_code& ec)
+ {
+#if FILESYSTEM_SYMLINK == FILESYSTEM_SYMLINK_STD
+ if (can_create_symlinks())
+ {
+ std::filesystem::path targetp = target.native();
+ std::filesystem::path filep = file.native();
+
+ std::filesystem::create_directory_symlink(targetp, filep, ec);
+ }
+ else
+ {
+ vcpkg::Checks::exit_with_message(VCPKG_LINE_INFO, "Symlinks are not allowed on this system");
+ }
+#elif FILESYSTEM_SYMLINK == FILESYSTEM_SYMLINK_UNIX
+ ::vcpkg::Test::create_symlink(target, file, ec);
+#else
+ static_cast<void>(ec);
+ vcpkg::Checks::exit_with_message(VCPKG_LINE_INFO, no_filesystem_message);
+#endif
+ }
+}
diff --git a/toolsrc/src/vcpkg.cpp b/toolsrc/src/vcpkg.cpp
index 363b39814..3fdbd0d3e 100644
--- a/toolsrc/src/vcpkg.cpp
+++ b/toolsrc/src/vcpkg.cpp
@@ -125,12 +125,8 @@ static void inner(const VcpkgCmdArguments& args)
auto default_vs_path = System::get_environment_variable("VCPKG_VISUAL_STUDIO_PATH").value_or("");
-
-
- const Expected<VcpkgPaths> expected_paths = VcpkgPaths::create(vcpkg_root_dir,
- vcpkg_scripts_root_dir,
- default_vs_path,
- args.overlay_triplets.get());
+ const Expected<VcpkgPaths> expected_paths =
+ VcpkgPaths::create(vcpkg_root_dir, vcpkg_scripts_root_dir, default_vs_path, args.overlay_triplets.get());
Checks::check_exit(VCPKG_LINE_INFO,
!expected_paths.error(),
"Error: Invalid vcpkg root directory %s: %s",
@@ -143,7 +139,11 @@ static void inner(const VcpkgCmdArguments& args)
#else
const int exit_code = chdir(paths.root.c_str());
#endif
- Checks::check_exit(VCPKG_LINE_INFO, exit_code == 0, "Changing the working dir failed");
+ Checks::check_exit(
+ VCPKG_LINE_INFO,
+ exit_code == 0,
+ "Changing the working directory to the vcpkg root directory failed. Did you incorrectly define the VCPKG_ROOT "
+ "environment variable, or did you mistakenly create a file named .vcpkg-root somewhere?");
if (args.command == "install" || args.command == "remove" || args.command == "export" || args.command == "update")
{
diff --git a/toolsrc/src/vcpkg/archives.cpp b/toolsrc/src/vcpkg/archives.cpp
index 69a916828..d22e841de 100644
--- a/toolsrc/src/vcpkg/archives.cpp
+++ b/toolsrc/src/vcpkg/archives.cpp
@@ -15,9 +15,10 @@ namespace vcpkg::Archives
#endif
;
+ fs.remove_all(to_path, VCPKG_LINE_INFO);
+ fs.remove_all(to_path_partial, VCPKG_LINE_INFO);
+ // TODO: check this error code
std::error_code ec;
- fs.remove_all(to_path, ec);
- fs.remove_all(to_path_partial, ec);
fs.create_directories(to_path_partial, ec);
const auto ext = archive.extension();
#if defined(_WIN32)
diff --git a/toolsrc/src/vcpkg/base/checks.cpp b/toolsrc/src/vcpkg/base/checks.cpp
index 0266ad683..c7584258a 100644
--- a/toolsrc/src/vcpkg/base/checks.cpp
+++ b/toolsrc/src/vcpkg/base/checks.cpp
@@ -27,9 +27,8 @@ namespace vcpkg
#if defined(_WIN32)
::TerminateProcess(::GetCurrentProcess(), exit_code);
-#else
- std::exit(exit_code);
#endif
+ std::exit(exit_code);
}
void Checks::unreachable(const LineInfo& line_info)
diff --git a/toolsrc/src/vcpkg/base/files.cpp b/toolsrc/src/vcpkg/base/files.cpp
index 5099795e9..bbf37fd25 100644
--- a/toolsrc/src/vcpkg/base/files.cpp
+++ b/toolsrc/src/vcpkg/base/files.cpp
@@ -6,6 +6,7 @@
#include <vcpkg/base/system.print.h>
#include <vcpkg/base/system.process.h>
#include <vcpkg/base/util.h>
+#include <vcpkg/base/work_queue.h>
#if defined(__linux__) || defined(__APPLE__)
#include <fcntl.h>
@@ -24,6 +25,123 @@ namespace vcpkg::Files
{
static const std::regex FILESYSTEM_INVALID_CHARACTERS_REGEX = std::regex(R"([\/:*?"<>|])");
+ namespace
+ {
+ fs::file_status status_implementation(bool follow_symlinks, const fs::path& p, std::error_code& ec) noexcept
+ {
+ using fs::file_type;
+ using fs::perms;
+#if defined(_WIN32)
+ WIN32_FILE_ATTRIBUTE_DATA file_attributes;
+ auto ft = file_type::unknown;
+ auto permissions = perms::unknown;
+ if (!GetFileAttributesExW(p.c_str(), GetFileExInfoStandard, &file_attributes))
+ {
+ const auto err = GetLastError();
+ if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND)
+ {
+ ft = file_type::not_found;
+ }
+ else
+ {
+ ec.assign(err, std::system_category());
+ }
+ }
+ else if (!follow_symlinks && file_attributes.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
+ {
+ // this also gives junctions file_type::directory_symlink
+ if (file_attributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ {
+ ft = file_type::directory_symlink;
+ }
+ else
+ {
+ ft = file_type::symlink;
+ }
+ }
+ else if (file_attributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ {
+ ft = file_type::directory;
+ }
+ else
+ {
+ // otherwise, the file is a regular file
+ ft = file_type::regular;
+ }
+
+ if (file_attributes.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
+ {
+ constexpr auto all_write = perms::group_write | perms::owner_write | perms::others_write;
+ permissions = perms::all & ~all_write;
+ }
+ else if (ft != file_type::none && ft != file_type::none)
+ {
+ permissions = perms::all;
+ }
+
+ return fs::file_status(ft, permissions);
+
+#else
+ auto result = follow_symlinks ? fs::stdfs::status(p, ec) : fs::stdfs::symlink_status(p, ec);
+ // libstdc++ doesn't correctly not-set ec on nonexistent paths
+ if (ec.value() == ENOENT || ec.value() == ENOTDIR)
+ {
+ ec.clear();
+ return fs::file_status(file_type::not_found, perms::unknown);
+ }
+ return fs::file_status(result.type(), result.permissions());
+#endif
+ }
+
+ fs::file_status status(const fs::path& p, std::error_code& ec) noexcept
+ {
+ return status_implementation(true, p, ec);
+ }
+ fs::file_status symlink_status(const fs::path& p, std::error_code& ec) noexcept
+ {
+ return status_implementation(false, p, ec);
+ }
+
+ // does _not_ follow symlinks
+ void set_writeable(const fs::path& path, std::error_code& ec) noexcept
+ {
+#if defined(_WIN32)
+ auto const file_name = path.c_str();
+ WIN32_FILE_ATTRIBUTE_DATA attributes;
+ if (!GetFileAttributesExW(file_name, GetFileExInfoStandard, &attributes))
+ {
+ ec.assign(GetLastError(), std::system_category());
+ return;
+ }
+
+ auto dw_attributes = attributes.dwFileAttributes;
+ dw_attributes &= ~FILE_ATTRIBUTE_READONLY;
+ if (!SetFileAttributesW(file_name, dw_attributes))
+ {
+ ec.assign(GetLastError(), std::system_category());
+ }
+#else
+ struct stat s;
+ if (lstat(path.c_str(), &s))
+ {
+ ec.assign(errno, std::system_category());
+ return;
+ }
+
+ auto mode = s.st_mode;
+ // if the file is a symlink, perms don't matter
+ if (!(mode & S_IFLNK))
+ {
+ mode |= S_IWUSR;
+ if (chmod(path.c_str(), mode))
+ {
+ ec.assign(errno, std::system_category());
+ }
+ }
+#endif
+ }
+ }
+
std::string Filesystem::read_contents(const fs::path& path, LineInfo linfo) const
{
auto maybe_contents = this->read_contents(path);
@@ -56,6 +174,43 @@ namespace vcpkg::Files
return r;
}
+ bool Filesystem::exists(const fs::path& path, std::error_code& ec) const
+ {
+ return fs::exists(this->symlink_status(path, ec));
+ }
+
+ bool Filesystem::exists(LineInfo li, const fs::path& path) const
+ {
+ std::error_code ec;
+ auto result = this->exists(path, ec);
+ if (ec) Checks::exit_with_message(li, "error checking existence of file %s: %s", path.u8string(), ec.message());
+ return result;
+ }
+ bool Filesystem::exists(const fs::path& path) const
+ {
+ std::error_code ec;
+ // drop this on the floor, for compatibility with existing code
+ return exists(path, ec);
+ }
+
+ fs::file_status Filesystem::status(vcpkg::LineInfo li, const fs::path& p) const noexcept
+ {
+ std::error_code ec;
+ auto result = this->status(p, ec);
+ if (ec) vcpkg::Checks::exit_with_message(li, "error getting status of path %s: %s", p.string(), ec.message());
+
+ return result;
+ }
+
+ fs::file_status Filesystem::symlink_status(vcpkg::LineInfo li, const fs::path& p) const noexcept
+ {
+ std::error_code ec;
+ auto result = this->symlink_status(p, ec);
+ if (ec) vcpkg::Checks::exit_with_message(li, "error getting status of path %s: %s", p.string(), ec.message());
+
+ return result;
+ }
+
void Filesystem::write_lines(const fs::path& path, const std::vector<std::string>& lines, LineInfo linfo)
{
std::error_code ec;
@@ -63,6 +218,33 @@ namespace vcpkg::Files
if (ec) Checks::exit_with_message(linfo, "error writing lines: %s: %s", path.u8string(), ec.message());
}
+ void Filesystem::remove_all(const fs::path& path, LineInfo li)
+ {
+ std::error_code ec;
+ fs::path failure_point;
+
+ this->remove_all(path, ec, failure_point);
+
+ if (ec)
+ {
+ Checks::exit_with_message(li,
+ "Failure to remove_all(%s) due to file %s: %s",
+ path.string(),
+ failure_point.string(),
+ ec.message());
+ }
+ }
+
+ fs::path Filesystem::canonical(LineInfo li, const fs::path& path) const
+ {
+ std::error_code ec;
+
+ const auto result = this->canonical(path, ec);
+
+ if (ec) Checks::exit_with_message(li, "Error getting canonicalization of %s: %s", path.string(), ec.message());
+ return result;
+ }
+
struct RealFilesystem final : Filesystem
{
virtual Expected<std::string> read_contents(const fs::path& file_path) const override
@@ -87,7 +269,7 @@ namespace vcpkg::Files
file_stream.read(&output[0], length);
file_stream.close();
- return std::move(output);
+ return output;
}
virtual Expected<std::vector<std::string>> read_lines(const fs::path& file_path) const override
{
@@ -101,17 +283,20 @@ namespace vcpkg::Files
std::string line;
while (std::getline(file_stream, line))
{
+ // Remove the trailing \r to accomodate Windows line endings.
+ if ((!line.empty()) && (line.back() == '\r')) line.pop_back();
+
output.push_back(line);
}
file_stream.close();
- return std::move(output);
+ return output;
}
virtual fs::path find_file_recursively_up(const fs::path& starting_dir,
const std::string& filename) const override
{
fs::path current_dir = starting_dir;
- if (exists(current_dir / filename))
+ if (exists(VCPKG_LINE_INFO, current_dir / filename))
{
return current_dir;
}
@@ -136,7 +321,7 @@ namespace vcpkg::Files
current_dir = std::move(parent);
const fs::path candidate = current_dir / filename;
- if (exists(candidate))
+ if (exists(VCPKG_LINE_INFO, candidate))
{
return current_dir;
}
@@ -254,30 +439,132 @@ namespace vcpkg::Files
#endif
}
virtual bool remove(const fs::path& path, std::error_code& ec) override { return fs::stdfs::remove(path, ec); }
- virtual std::uintmax_t remove_all(const fs::path& path, std::error_code& ec) override
+ virtual void remove_all(const fs::path& path, std::error_code& ec, fs::path& failure_point) override
{
- // Working around the currently buggy remove_all()
- std::uintmax_t out = fs::stdfs::remove_all(path, ec);
+ /*
+ does not use the std::experimental::filesystem call since this is
+ quite a bit faster, and also supports symlinks
+ */
- for (int i = 0; i < 5 && this->exists(path); i++)
+ struct remove
{
- using namespace std::chrono_literals;
- std::this_thread::sleep_for(i * 100ms);
- out += fs::stdfs::remove_all(path, ec);
- }
+ struct ErrorInfo : Util::ResourceBase
+ {
+ std::error_code ec;
+ fs::path failure_point;
+ };
+ /*
+ if `current_path` is a directory, first `remove`s all
+ elements of the directory, then removes current_path.
+
+ else if `current_path` exists, removes current_path
+
+ else does nothing
+ */
+ static void do_remove(const fs::path& current_path, ErrorInfo& err)
+ {
+ std::error_code ec;
+ const auto path_status = Files::symlink_status(current_path, ec);
+ if (check_ec(ec, current_path, err)) return;
+ if (!fs::exists(path_status)) return;
+
+ const auto path_type = path_status.type();
- if (this->exists(path))
+ if ((path_status.permissions() & fs::perms::owner_write) != fs::perms::owner_write)
+ {
+ set_writeable(current_path, ec);
+ if (check_ec(ec, current_path, err)) return;
+ }
+
+ if (path_type == fs::file_type::directory)
+ {
+ for (const auto& entry : fs::stdfs::directory_iterator(current_path))
+ {
+ do_remove(entry, err);
+ if (err.ec) return;
+ }
+#if defined(_WIN32)
+ if (!RemoveDirectoryW(current_path.c_str()))
+ {
+ ec.assign(GetLastError(), std::system_category());
+ }
+#else
+ if (rmdir(current_path.c_str()))
+ {
+ ec.assign(errno, std::system_category());
+ }
+#endif
+ }
+#if defined(_WIN32)
+ else if (path_type == fs::file_type::directory_symlink)
+ {
+ if (!RemoveDirectoryW(current_path.c_str()))
+ {
+ ec.assign(GetLastError(), std::system_category());
+ }
+ }
+ else
+ {
+ if (!DeleteFileW(current_path.c_str()))
+ {
+ ec.assign(GetLastError(), std::system_category());
+ }
+ }
+#else
+ else
+ {
+ if (unlink(current_path.c_str()))
+ {
+ ec.assign(errno, std::system_category());
+ }
+ }
+#endif
+
+ check_ec(ec, current_path, err);
+ }
+
+ static bool check_ec(const std::error_code& ec, const fs::path& current_path, ErrorInfo& err)
+ {
+ if (ec)
+ {
+ err.ec = ec;
+ err.failure_point = current_path;
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ };
+
+ /*
+ we need to do backoff on the removal of the top level directory,
+ so we can only delete the directory after all the
+ lower levels have been deleted.
+ */
+
+ remove::ErrorInfo err;
+ for (int backoff = 0; backoff < 5; ++backoff)
{
- System::print2(
- System::Color::warning,
- "Some files in ",
- path.u8string(),
- " were unable to be removed. Close any editors operating in this directory and retry.\n");
+ if (backoff)
+ {
+ using namespace std::chrono_literals;
+ auto backoff_time = 100ms * backoff;
+ std::this_thread::sleep_for(backoff_time);
+ }
+
+ remove::do_remove(path, err);
+ if (!err.ec)
+ {
+ break;
+ }
}
- return out;
+ ec = std::move(err.ec);
+ failure_point = std::move(err.failure_point);
}
- virtual bool exists(const fs::path& path) const override { return fs::stdfs::exists(path); }
virtual bool is_directory(const fs::path& path) const override { return fs::stdfs::is_directory(path); }
virtual bool is_regular_file(const fs::path& path) const override { return fs::stdfs::is_regular_file(path); }
virtual bool is_empty(const fs::path& path) const override { return fs::stdfs::is_empty(path); }
@@ -307,11 +594,11 @@ namespace vcpkg::Files
virtual fs::file_status status(const fs::path& path, std::error_code& ec) const override
{
- return fs::stdfs::status(path, ec);
+ return Files::status(path, ec);
}
virtual fs::file_status symlink_status(const fs::path& path, std::error_code& ec) const override
{
- return fs::stdfs::symlink_status(path, ec);
+ return Files::symlink_status(path, ec);
}
virtual void write_contents(const fs::path& file_path, const std::string& data, std::error_code& ec) override
{
@@ -342,6 +629,11 @@ namespace vcpkg::Files
}
}
+ virtual fs::path canonical(const fs::path& path, std::error_code& ec) const override
+ {
+ return fs::stdfs::canonical(path, ec);
+ }
+
virtual std::vector<fs::path> find_from_PATH(const std::string& name) const override
{
#if defined(_WIN32)
@@ -355,7 +647,7 @@ namespace vcpkg::Files
for (auto&& ext : EXTS)
{
auto p = fs::u8path(base + ext.c_str());
- if (Util::find(ret, p) == ret.end() && this->exists(p))
+ if (Util::find(ret, p) == ret.end() && this->exists(VCPKG_LINE_INFO, p))
{
ret.push_back(p);
Debug::print("Found path: ", p.u8string(), '\n');
diff --git a/toolsrc/src/vcpkg/base/strings.cpp b/toolsrc/src/vcpkg/base/strings.cpp
index 54a74a7a1..46e78a363 100644
--- a/toolsrc/src/vcpkg/base/strings.cpp
+++ b/toolsrc/src/vcpkg/base/strings.cpp
@@ -288,3 +288,43 @@ bool Strings::contains(StringView haystack, StringView needle)
{
return Strings::search(haystack, needle) != haystack.end();
}
+
+namespace vcpkg::Strings
+{
+ namespace
+ {
+ template<class Integral>
+ std::string b32_encode_implementation(Integral x)
+ {
+ static_assert(std::is_integral<Integral>::value, "b64url_encode must take an integer type");
+ using Unsigned = std::make_unsigned_t<Integral>;
+ auto value = static_cast<Unsigned>(x);
+
+ // 32 values, plus the implicit \0
+ constexpr static char map[33] = "ABCDEFGHIJKLMNOP"
+ "QRSTUVWXYZ234567";
+
+ // log2(32)
+ constexpr static int shift = 5;
+ // 32 - 1
+ constexpr static auto mask = 31;
+
+ // ceiling(bitsize(Integral) / log2(32))
+ constexpr static auto result_size = (sizeof(value) * 8 + shift - 1) / shift;
+
+ std::string result;
+ result.reserve(result_size);
+
+ for (std::size_t i = 0; i < result_size; ++i)
+ {
+ result.push_back(map[value & mask]);
+ value >>= shift;
+ }
+
+ return result;
+ }
+ }
+
+ std::string b32_encode(std::uint64_t x) noexcept { return b32_encode_implementation(x); }
+
+}
diff --git a/toolsrc/src/vcpkg/binaryparagraph.cpp b/toolsrc/src/vcpkg/binaryparagraph.cpp
index 4b80debab..8b1886098 100644
--- a/toolsrc/src/vcpkg/binaryparagraph.cpp
+++ b/toolsrc/src/vcpkg/binaryparagraph.cpp
@@ -27,7 +27,7 @@ namespace vcpkg
BinaryParagraph::BinaryParagraph() = default;
- BinaryParagraph::BinaryParagraph(std::unordered_map<std::string, std::string> fields)
+ BinaryParagraph::BinaryParagraph(Parse::RawParagraph fields)
{
using namespace vcpkg::Parse;
diff --git a/toolsrc/src/vcpkg/build.cpp b/toolsrc/src/vcpkg/build.cpp
index 68df1f965..b809db0bc 100644
--- a/toolsrc/src/vcpkg/build.cpp
+++ b/toolsrc/src/vcpkg/build.cpp
@@ -32,24 +32,16 @@ namespace vcpkg::Build::Command
using Dependencies::InstallPlanAction;
using Dependencies::InstallPlanType;
- static constexpr StringLiteral OPTION_CHECKS_ONLY = "--checks-only";
-
void perform_and_exit_ex(const FullPackageSpec& full_spec,
const SourceControlFileLocation& scfl,
const ParsedArguments& options,
const VcpkgPaths& paths)
{
+ vcpkg::Util::unused(options);
+
+ const StatusParagraphs status_db = database_load_check(paths);
const PackageSpec& spec = full_spec.package_spec;
- const auto& scf = *scfl.source_control_file;
- if (Util::Sets::contains(options.switches, OPTION_CHECKS_ONLY))
- {
- const auto pre_build_info = Build::PreBuildInfo::from_triplet_file(paths, spec.triplet());
- const auto build_info = Build::read_build_info(paths.get_filesystem(), paths.build_info_file_path(spec));
- const size_t error_count =
- PostBuildLint::perform_all_checks(spec, paths, pre_build_info, build_info, scfl.source_location);
- Checks::check_exit(VCPKG_LINE_INFO, error_count == 0);
- Checks::exit_success(VCPKG_LINE_INFO);
- }
+ const SourceControlFile& scf = *scfl.source_control_file;
Checks::check_exit(VCPKG_LINE_INFO,
spec.name() == scf.core_paragraph->name,
@@ -57,7 +49,6 @@ namespace vcpkg::Build::Command
scf.core_paragraph->name,
spec.name());
- const StatusParagraphs status_db = database_load_check(paths);
const Build::BuildPackageOptions build_package_options{
Build::UseHeadVersion::NO,
Build::AllowDownloads::YES,
@@ -72,8 +63,7 @@ namespace vcpkg::Build::Command
std::set<std::string> features_as_set(full_spec.features.begin(), full_spec.features.end());
features_as_set.emplace("core");
- const Build::BuildPackageConfig build_config{
- scf, spec.triplet(), fs::path(scfl.source_location), build_package_options, features_as_set};
+ const Build::BuildPackageConfig build_config{scfl, spec.triplet(), build_package_options, features_as_set};
const auto build_timer = Chrono::ElapsedTimer::create_started();
const auto result = Build::build_package(paths, build_config, status_db);
@@ -104,15 +94,11 @@ namespace vcpkg::Build::Command
Checks::exit_success(VCPKG_LINE_INFO);
}
- static constexpr std::array<CommandSwitch, 1> BUILD_SWITCHES = {{
- {OPTION_CHECKS_ONLY, "Only run checks, do not rebuild package"},
- }};
-
const CommandStructure COMMAND_STRUCTURE = {
Help::create_example_string("build zlib:x64-windows"),
1,
1,
- {BUILD_SWITCHES, {}},
+ {{}, {}},
nullptr,
};
@@ -230,6 +216,23 @@ namespace vcpkg::Build
}));
}
+ std::unordered_map<std::string, std::string> make_env_passthrough(const PreBuildInfo& pre_build_info)
+ {
+ std::unordered_map<std::string, std::string> env;
+
+ for (auto&& env_var : pre_build_info.passthrough_env_vars)
+ {
+ auto env_val = System::get_environment_variable(env_var);
+
+ if (env_val)
+ {
+ env[env_var] = env_val.value_or_exit(VCPKG_LINE_INFO);
+ }
+ }
+
+ return env;
+ }
+
std::string make_build_env_cmd(const PreBuildInfo& pre_build_info, const Toolset& toolset)
{
if (pre_build_info.external_toolchain_file.has_value()) return "";
@@ -274,7 +277,7 @@ namespace vcpkg::Build
return bcf;
}
- static void write_binary_control_file(const VcpkgPaths& paths, BinaryControlFile bcf)
+ static void write_binary_control_file(const VcpkgPaths& paths, const BinaryControlFile& bcf)
{
std::string start = Strings::serialize(bcf.core_paragraph);
for (auto&& feature : bcf.features)
@@ -285,23 +288,37 @@ 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::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 =
- Util::fmap_flatten(config.feature_list, [&](std::string const& feature) -> std::vector<std::string> {
- if (feature == "core")
- {
- return filter_dependencies(config.scf.core_paragraph->depends, triplet);
- }
-
- auto maybe_feature = config.scf.find_feature(feature);
- Checks::check_exit(VCPKG_LINE_INFO, maybe_feature.has_value());
-
- return filter_dependencies(maybe_feature.get()->depends, 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);
@@ -353,40 +370,18 @@ namespace vcpkg::Build
return concurrency;
}
- static ExtendedBuildResult do_build_package(const VcpkgPaths& paths,
- const PreBuildInfo& pre_build_info,
- const PackageSpec& spec,
- const std::string& abi_tag,
- const BuildPackageConfig& config)
+ static std::vector<System::CMakeVariable> get_cmake_vars(const VcpkgPaths& paths,
+ const BuildPackageConfig& config,
+ const Triplet& triplet,
+ const Toolset& toolset)
{
- auto& fs = paths.get_filesystem();
- 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()))
- {
- System::printf("-- 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());
- }
-
#if !defined(_WIN32)
// TODO: remove when vcpkg.exe is in charge for acquiring tools. Change introduced in vcpkg v0.0.107.
// bootstrap should have already downloaded ninja, but making sure it is present in case it was deleted.
vcpkg::Util::unused(paths.get_tool_exe(Tools::NINJA));
#endif
- const fs::path& cmake_exe_path = paths.get_tool_exe(Tools::CMAKE);
const fs::path& git_exe_path = paths.get_tool_exe(Tools::GIT);
-#if defined(_WIN32)
- const fs::path& powershell_exe_path = paths.get_tool_exe("powershell-core");
- if (!fs.exists(powershell_exe_path.parent_path() / "powershell.exe"))
- {
- fs.copy(powershell_exe_path, powershell_exe_path.parent_path() / "powershell.exe", fs::copy_options::none);
- }
-#endif
std::string all_features;
for (auto& feature : config.scf.feature_paragraphs)
@@ -394,14 +389,13 @@ namespace vcpkg::Build
all_features.append(feature->name + ";");
}
- const Toolset& toolset = paths.get_toolset(pre_build_info);
-
std::vector<System::CMakeVariable> variables{
{"CMD", "BUILD"},
{"PORT", config.scf.core_paragraph->name},
{"CURRENT_PORT_DIR", config.port_dir},
- {"TARGET_TRIPLET", spec.triplet().canonical_name()},
- {"TARGET_TRIPLET_FILE", triplet_file_path},
+ {"TARGET_TRIPLET", triplet.canonical_name()},
+ {"TARGET_TRIPLET_FILE", paths.get_triplet_file_path(triplet).u8string()},
+ {"ENV_OVERRIDES_FILE", config.port_dir / "environment-overrides.cmake"},
{"VCPKG_PLATFORM_TOOLSET", toolset.version.c_str()},
{"VCPKG_USE_HEAD_VERSION", Util::Enum::to_bool(config.build_package_options.use_head_version) ? "1" : "0"},
{"DOWNLOADS", paths.downloads},
@@ -417,9 +411,21 @@ namespace vcpkg::Build
variables.push_back({"GIT", git_exe_path});
}
+ return variables;
+ }
+
+ static std::string make_build_cmd(const VcpkgPaths& paths,
+ const PreBuildInfo& pre_build_info,
+ const BuildPackageConfig& config,
+ const Triplet& triplet)
+ {
+ const Toolset& toolset = paths.get_toolset(pre_build_info);
+ const fs::path& cmake_exe_path = paths.get_tool_exe(Tools::CMAKE);
+ std::vector<System::CMakeVariable> variables = get_cmake_vars(paths, config, triplet, toolset);
+
const std::string cmd_launch_cmake = System::make_cmake_cmd(cmake_exe_path, paths.ports_cmake, variables);
- auto command = make_build_env_cmd(pre_build_info, toolset);
+ std::string command = make_build_env_cmd(pre_build_info, toolset);
if (!command.empty())
{
#ifdef _WIN32
@@ -428,16 +434,102 @@ namespace vcpkg::Build
command.append(" && ");
#endif
}
+
command.append(cmd_launch_cmake);
+
+ return command;
+ }
+
+ static std::string get_triplet_abi(const VcpkgPaths& paths,
+ const PreBuildInfo& pre_build_info,
+ const Triplet& triplet)
+ {
+ static std::map<fs::path, std::string> s_hash_cache;
+
+ const fs::path triplet_file_path = paths.get_triplet_file_path(triplet);
+ const auto& fs = paths.get_filesystem();
+
+ std::string hash;
+
+ auto it_hash = s_hash_cache.find(triplet_file_path);
+ if (it_hash != s_hash_cache.end())
+ {
+ hash = it_hash->second;
+ }
+ else
+ {
+ hash = Hash::get_file_hash(fs, triplet_file_path, "SHA1");
+
+ if (auto p = pre_build_info.external_toolchain_file.get())
+ {
+ hash += "-";
+ hash += Hash::get_file_hash(fs, *p, "SHA1");
+ }
+ else if (pre_build_info.cmake_system_name == "Linux")
+ {
+ hash += "-";
+ hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "linux.cmake", "SHA1");
+ }
+ else if (pre_build_info.cmake_system_name == "Darwin")
+ {
+ hash += "-";
+ hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "osx.cmake", "SHA1");
+ }
+ else if (pre_build_info.cmake_system_name == "FreeBSD")
+ {
+ hash += "-";
+ hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "freebsd.cmake", "SHA1");
+ }
+ else if (pre_build_info.cmake_system_name == "Android")
+ {
+ hash += "-";
+ hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "android.cmake", "SHA1");
+ }
+
+ s_hash_cache.emplace(triplet_file_path, hash);
+ }
+
+ return hash;
+ }
+
+ static ExtendedBuildResult do_build_package(const VcpkgPaths& paths,
+ const PreBuildInfo& pre_build_info,
+ const PackageSpec& spec,
+ const std::string& abi_tag,
+ const BuildPackageConfig& config)
+ {
+ auto& fs = paths.get_filesystem();
+#if defined(_WIN32)
+ const fs::path& powershell_exe_path = paths.get_tool_exe("powershell-core");
+ if (!fs.exists(powershell_exe_path.parent_path() / "powershell.exe"))
+ {
+ fs.copy(powershell_exe_path, powershell_exe_path.parent_path() / "powershell.exe", fs::copy_options::none);
+ }
+#endif
+
+ 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()))
+ {
+ System::printf("-- 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());
+ }
+
const auto timer = Chrono::ElapsedTimer::create_started();
- const int return_code = System::cmd_execute_clean(
- command,
- {}
-#ifdef _WIN32
- ,
- powershell_exe_path.parent_path().u8string() + ";"
+
+ std::string command = make_build_cmd(paths, pre_build_info, config, triplet);
+ std::unordered_map<std::string, std::string> env = make_env_passthrough(pre_build_info);
+
+#if defined(_WIN32)
+ const int return_code =
+ System::cmd_execute_clean(command, env, powershell_exe_path.parent_path().u8string() + ";");
+#else
+ const int return_code = System::cmd_execute_clean(command, env);
#endif
- );
const auto buildtimeus = timer.microseconds();
const auto spec_string = spec.to_string();
@@ -495,7 +587,8 @@ namespace vcpkg::Build
if (fs.is_directory(file)) // Will only keep the logs
{
std::error_code ec;
- fs.remove_all(file, ec);
+ fs::path failure_point;
+ fs.remove_all(file, ec, failure_point);
}
}
}
@@ -516,6 +609,9 @@ namespace vcpkg::Build
std::vector<AbiEntry> abi_tag_entries(dependency_abis.begin(), dependency_abis.end());
+#if defined(_WIN32)
+ abi_tag_entries.emplace_back(AbiEntry{"powershell", paths.get_tool_version("powershell-core")});
+#endif
abi_tag_entries.emplace_back(AbiEntry{"cmake", paths.get_tool_version(Tools::CMAKE)});
// If there is an unusually large number of files in the port then
@@ -527,7 +623,7 @@ namespace vcpkg::Build
std::vector<fs::path> port_files;
for (auto& port_file : fs::stdfs::recursive_directory_iterator(config.port_dir))
{
- if (fs::is_regular_file(status(port_file)))
+ if (fs::is_regular_file(fs.status(VCPKG_LINE_INFO, port_file)))
{
port_files.push_back(port_file);
if (port_files.size() > max_port_file_count)
@@ -610,8 +706,8 @@ namespace vcpkg::Build
auto& fs = paths.get_filesystem();
auto pkg_path = paths.package_dir(spec);
+ fs.remove_all(pkg_path, VCPKG_LINE_INFO);
std::error_code ec;
- fs.remove_all(pkg_path, ec);
fs.create_directories(pkg_path, ec);
auto files = fs.get_files_non_recursive(pkg_path);
Checks::check_exit(VCPKG_LINE_INFO, files.empty(), "unable to clear path: %s", pkg_path.u8string());
@@ -693,7 +789,7 @@ 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);
+ const auto pre_build_info = PreBuildInfo::from_triplet_file(paths, triplet, config.scfl);
auto maybe_abi_tag_and_file = compute_abi_tag(paths, config, pre_build_info, dependency_abis);
@@ -711,7 +807,7 @@ namespace vcpkg::Build
{
System::print2("Using cached binary package: ", archive_path.u8string(), "\n");
- auto archive_result = decompress_archive(paths, spec, archive_path);
+ int archive_result = decompress_archive(paths, spec, archive_path);
if (archive_result != 0)
{
@@ -794,7 +890,7 @@ namespace vcpkg::Build
fs.rename_or_copy(tmp_failure_zip, archive_tombstone_path, ".tmp", ec);
// clean up temporary directory
- fs.remove_all(tmp_log_path, ec);
+ fs.remove_all(tmp_log_path, VCPKG_LINE_INFO);
}
}
@@ -845,7 +941,7 @@ namespace vcpkg::Build
Commands::Version::version());
}
- static BuildInfo inner_create_buildinfo(std::unordered_map<std::string, std::string> pgh)
+ static BuildInfo inner_create_buildinfo(Parse::RawParagraph pgh)
{
Parse::ParagraphParser parser(std::move(pgh));
@@ -902,13 +998,14 @@ namespace vcpkg::Build
BuildInfo read_build_info(const Files::Filesystem& fs, const fs::path& filepath)
{
- const Expected<std::unordered_map<std::string, std::string>> pghs =
- Paragraphs::get_single_paragraph(fs, filepath);
+ const Expected<Parse::RawParagraph> pghs = Paragraphs::get_single_paragraph(fs, filepath);
Checks::check_exit(VCPKG_LINE_INFO, pghs.get() != nullptr, "Invalid BUILD_INFO file for package");
return inner_create_buildinfo(*pghs.get());
}
- PreBuildInfo PreBuildInfo::from_triplet_file(const VcpkgPaths& paths, const Triplet& triplet)
+ PreBuildInfo PreBuildInfo::from_triplet_file(const VcpkgPaths& paths,
+ const Triplet& triplet,
+ Optional<const SourceControlFileLocation&> port)
{
static constexpr CStringView FLAG_GUID = "c35112b6-d1ba-415b-aa5d-81de856ef8eb";
@@ -916,11 +1013,16 @@ namespace vcpkg::Build
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);
- const auto cmd_launch_cmake = System::make_cmake_cmd(cmake_exe_path,
- ports_cmake_script_path,
- {
- {"CMAKE_TRIPLET_FILE", triplet_file_path},
- });
+ std::vector<System::CMakeVariable> args{{"CMAKE_TRIPLET_FILE", triplet_file_path}};
+
+ if (port)
+ {
+ args.emplace_back("CMAKE_ENV_OVERRIDES_FILE",
+ port.value_or_exit(VCPKG_LINE_INFO).source_location / "environment-overrides.cmake");
+ }
+
+ 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);
@@ -946,111 +1048,57 @@ namespace vcpkg::Build
const std::string variable_name = s.at(0);
const std::string variable_value = variable_with_no_value ? "" : s.at(1);
- if (variable_name == "VCPKG_TARGET_ARCHITECTURE")
- {
- pre_build_info.target_architecture = variable_value;
- continue;
- }
-
- if (variable_name == "VCPKG_CMAKE_SYSTEM_NAME")
- {
- pre_build_info.cmake_system_name = variable_value;
- continue;
- }
-
- if (variable_name == "VCPKG_CMAKE_SYSTEM_VERSION")
- {
- pre_build_info.cmake_system_version = variable_value;
- continue;
- }
-
- if (variable_name == "VCPKG_PLATFORM_TOOLSET")
- {
- pre_build_info.platform_toolset =
- variable_value.empty() ? nullopt : Optional<std::string>{variable_value};
- continue;
- }
-
- if (variable_name == "VCPKG_VISUAL_STUDIO_PATH")
+ auto maybe_option = VCPKG_OPTIONS.find(variable_name);
+ if (maybe_option != VCPKG_OPTIONS.end())
{
- pre_build_info.visual_studio_path =
- variable_value.empty() ? nullopt : Optional<fs::path>{variable_value};
- continue;
- }
-
- if (variable_name == "VCPKG_CHAINLOAD_TOOLCHAIN_FILE")
- {
- pre_build_info.external_toolchain_file =
- variable_value.empty() ? nullopt : Optional<std::string>{variable_value};
- continue;
+ 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;
+ }
}
-
- if (variable_name == "VCPKG_BUILD_TYPE")
+ else
{
- 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);
- continue;
+ Checks::exit_with_message(VCPKG_LINE_INFO, "Unknown variable name %s", line);
}
-
- Checks::exit_with_message(VCPKG_LINE_INFO, "Unknown variable name %s", line);
}
- pre_build_info.triplet_abi_tag = [&]() {
- const auto& fs = paths.get_filesystem();
- static std::map<fs::path, std::string> s_hash_cache;
-
- auto it_hash = s_hash_cache.find(triplet_file_path);
- if (it_hash != s_hash_cache.end())
- {
- return it_hash->second;
- }
- auto hash = Hash::get_file_hash(fs, triplet_file_path, "SHA1");
-
- if (auto p = pre_build_info.external_toolchain_file.get())
- {
- hash += "-";
- hash += Hash::get_file_hash(fs, *p, "SHA1");
- }
- else if (pre_build_info.cmake_system_name.empty() ||
- pre_build_info.cmake_system_name == "WindowsStore")
- {
- hash += "-";
- hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "windows.cmake", "SHA1");
- }
- else if (pre_build_info.cmake_system_name == "Linux")
- {
- hash += "-";
- hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "linux.cmake", "SHA1");
- }
- else if (pre_build_info.cmake_system_name == "Darwin")
- {
- hash += "-";
- hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "osx.cmake", "SHA1");
- }
- else if (pre_build_info.cmake_system_name == "FreeBSD")
- {
- hash += "-";
- hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "freebsd.cmake", "SHA1");
- }
- else if (pre_build_info.cmake_system_name == "Android")
- {
- hash += "-";
- hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "android.cmake", "SHA1");
- }
-
- s_hash_cache.emplace(triplet_file_path, hash);
- return hash;
- }();
+ pre_build_info.triplet_abi_tag = get_triplet_abi(paths, pre_build_info, triplet);
return pre_build_info;
}
+
ExtendedBuildResult::ExtendedBuildResult(BuildResult code) : code(code) {}
ExtendedBuildResult::ExtendedBuildResult(BuildResult code, std::unique_ptr<BinaryControlFile>&& bcf)
: code(code), binary_control_file(std::move(bcf))
diff --git a/toolsrc/src/vcpkg/commands.cache.cpp b/toolsrc/src/vcpkg/commands.cache.cpp
index c321de3b5..4c49db004 100644
--- a/toolsrc/src/vcpkg/commands.cache.cpp
+++ b/toolsrc/src/vcpkg/commands.cache.cpp
@@ -14,7 +14,7 @@ namespace vcpkg::Commands::Cache
std::vector<BinaryParagraph> output;
for (auto&& path : paths.get_filesystem().get_files_non_recursive(paths.packages))
{
- const Expected<std::unordered_map<std::string, std::string>> pghs =
+ const Expected<Parse::RawParagraph> pghs =
Paragraphs::get_single_paragraph(paths.get_filesystem(), path / "CONTROL");
if (const auto p = pghs.get())
{
diff --git a/toolsrc/src/vcpkg/commands.ci.cpp b/toolsrc/src/vcpkg/commands.ci.cpp
index c12c26ff7..f0f162f5c 100644
--- a/toolsrc/src/vcpkg/commands.ci.cpp
+++ b/toolsrc/src/vcpkg/commands.ci.cpp
@@ -236,13 +236,7 @@ namespace vcpkg::Commands::CI
{
auto triplet = p->spec.triplet();
- const Build::BuildPackageConfig build_config{
- *scfl->source_control_file,
- triplet,
- static_cast<fs::path>(scfl->source_location),
- build_options,
- p->feature_list
- };
+ const Build::BuildPackageConfig build_config{*scfl, triplet, build_options, p->feature_list};
auto dependency_abis =
Util::fmap(p->computed_dependencies, [&](const PackageSpec& spec) -> Build::AbiEntry {
@@ -254,7 +248,7 @@ namespace vcpkg::Commands::CI
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); });
+ triplet, [&]() { return Build::PreBuildInfo::from_triplet_file(paths, triplet, *scfl); });
auto maybe_tag_and_file =
Build::compute_abi_tag(paths, build_config, pre_build_info, dependency_abis);
@@ -356,7 +350,7 @@ namespace vcpkg::Commands::CI
}
StatusParagraphs status_db = database_load_check(paths);
-
+
Dependencies::PathsPortFileProvider provider(paths, args.overlay_ports.get());
const Build::BuildPackageOptions install_plan_options = {
diff --git a/toolsrc/src/vcpkg/commands.cpp b/toolsrc/src/vcpkg/commands.cpp
index 54e9346ba..3ac568979 100644
--- a/toolsrc/src/vcpkg/commands.cpp
+++ b/toolsrc/src/vcpkg/commands.cpp
@@ -24,6 +24,7 @@ namespace vcpkg::Commands
{"env", &Env::perform_and_exit},
{"build-external", &BuildExternal::perform_and_exit},
{"export", &Export::perform_and_exit},
+ {"depend-info", &DependInfo::perform_and_exit},
};
return t;
}
@@ -38,7 +39,6 @@ namespace vcpkg::Commands
{"integrate", &Integrate::perform_and_exit},
{"owns", &Owns::perform_and_exit},
{"update", &Update::perform_and_exit},
- {"depend-info", &DependInfo::perform_and_exit},
{"edit", &Edit::perform_and_exit},
{"create", &Create::perform_and_exit},
{"import", &Import::perform_and_exit},
diff --git a/toolsrc/src/vcpkg/commands.dependinfo.cpp b/toolsrc/src/vcpkg/commands.dependinfo.cpp
index 8394e0166..8ca88dd56 100644
--- a/toolsrc/src/vcpkg/commands.dependinfo.cpp
+++ b/toolsrc/src/vcpkg/commands.dependinfo.cpp
@@ -4,65 +4,129 @@
#include <vcpkg/base/system.print.h>
#include <vcpkg/base/util.h>
#include <vcpkg/commands.h>
+#include <vcpkg/dependencies.h>
#include <vcpkg/help.h>
-#include <vcpkg/paragraphs.h>
+#include <vcpkg/input.h>
+#include <vcpkg/install.h>
#include <vcpkg/packagespec.h>
-
#include <vector>
-#include <memory>
-#include <vcpkg/dependencies.h>
+using vcpkg::Dependencies::AnyAction;
+using vcpkg::Dependencies::create_feature_install_plan;
+using vcpkg::Dependencies::InstallPlanAction;
using vcpkg::Dependencies::PathsPortFileProvider;
namespace vcpkg::Commands::DependInfo
{
constexpr StringLiteral OPTION_DOT = "--dot";
constexpr StringLiteral OPTION_DGML = "--dgml";
- constexpr StringLiteral OPTION_NO_RECURSE = "--no-recurse";
+ constexpr StringLiteral OPTION_SHOW_DEPTH = "--show-depth";
+ constexpr StringLiteral OPTION_MAX_RECURSE = "--max-recurse";
+ constexpr StringLiteral OPTION_SORT = "--sort";
+
+ constexpr int NO_RECURSE_LIMIT_VALUE = -1;
+
+ constexpr std::array<CommandSwitch, 3> DEPEND_SWITCHES = {{{OPTION_DOT, "Creates graph on basis of dot"},
+ {OPTION_DGML, "Creates graph on basis of dgml"},
+ {OPTION_SHOW_DEPTH, "Show recursion depth in output"}}};
- constexpr std::array<CommandSwitch, 3> DEPEND_SWITCHES = {{
- {OPTION_DOT, "Creates graph on basis of dot"},
- {OPTION_DGML, "Creates graph on basis of dgml"},
- {OPTION_NO_RECURSE,
- "Computes only immediate dependencies of packages explicitly specified on the command-line"},
- }};
+ constexpr std::array<CommandSetting, 2> DEPEND_SETTINGS = {
+ {{OPTION_MAX_RECURSE, "Set max recursion depth, a value of -1 indicates no limit"},
+ {OPTION_SORT,
+ "Set sort order for the list of dependencies, accepted values are: lexicographical, topological (default), "
+ "reverse"}}};
const CommandStructure COMMAND_STRUCTURE = {
- Help::create_example_string(R"###(depend-info [pat])###"),
+ Help::create_example_string("depend-info sqlite3"),
0,
SIZE_MAX,
- {DEPEND_SWITCHES, {}},
+ {DEPEND_SWITCHES, DEPEND_SETTINGS},
nullptr,
};
- std::string replace_dashes_with_underscore(const std::string& input)
+ struct PackageDependInfo
{
- std::string output = input;
- std::replace(output.begin(), output.end(), '-', '_');
- return output;
+ std::string package;
+ int depth;
+ std::set<std::string> features;
+ std::vector<std::string> dependencies;
+ };
+
+ enum SortMode
+ {
+ Lexicographical = 0,
+ Topological,
+ ReverseTopological,
+ Default = Topological
+ };
+
+ int get_max_depth(const ParsedArguments& options)
+ {
+ auto iter = options.settings.find(OPTION_MAX_RECURSE);
+ if (iter != options.settings.end())
+ {
+ std::string value = iter->second;
+ try
+ {
+ return std::stoi(value);
+ }
+ catch (std::exception&)
+ {
+ Checks::exit_with_message(VCPKG_LINE_INFO, "Value of --max-depth must be an integer");
+ }
+ }
+ // No --max-depth set, default to no limit.
+ return NO_RECURSE_LIMIT_VALUE;
}
- std::string create_dot_as_string(const std::vector<const SourceControlFile*>& source_control_files)
+ SortMode get_sort_mode(const ParsedArguments& options)
+ {
+ constexpr StringLiteral OPTION_SORT_LEXICOGRAPHICAL = "lexicographical";
+ constexpr StringLiteral OPTION_SORT_TOPOLOGICAL = "topological";
+ constexpr StringLiteral OPTION_SORT_REVERSE = "reverse";
+
+ static const std::map<std::string, SortMode> sortModesMap{{OPTION_SORT_LEXICOGRAPHICAL, Lexicographical},
+ {OPTION_SORT_TOPOLOGICAL, Topological},
+ {OPTION_SORT_REVERSE, ReverseTopological}};
+
+ auto iter = options.settings.find(OPTION_SORT);
+ if (iter != options.settings.end())
+ {
+ const std::string value = Strings::ascii_to_lowercase(std::string{iter->second});
+ auto it = sortModesMap.find(value);
+ if (it != sortModesMap.end())
+ {
+ return it->second;
+ }
+ Checks::exit_with_message(VCPKG_LINE_INFO,
+ "Value of --sort must be one of `%s`, `%s`, or `%s`",
+ OPTION_SORT_LEXICOGRAPHICAL,
+ OPTION_SORT_TOPOLOGICAL,
+ OPTION_SORT_REVERSE);
+ }
+ return Default;
+ }
+
+ std::string create_dot_as_string(const std::vector<PackageDependInfo>& depend_info)
{
int empty_node_count = 0;
std::string s;
s.append("digraph G{ rankdir=LR; edge [minlen=3]; overlap=false;");
- for (const auto& source_control_file : source_control_files)
+ for (const auto& package : depend_info)
{
- const SourceParagraph& source_paragraph = *source_control_file->core_paragraph;
- if (source_paragraph.depends.empty())
+ if (package.dependencies.empty())
{
empty_node_count++;
continue;
}
- const std::string name = replace_dashes_with_underscore(source_paragraph.name);
+ const std::string name = Strings::replace_all(std::string{ package.package }, "-", "_");
s.append(Strings::format("%s;", name));
- for (const Dependency& d : source_paragraph.depends)
+ for (const auto &d : package.dependencies)
{
- const std::string dependency_name = replace_dashes_with_underscore(d.depend.name);
+ const std::string dependency_name = Strings::replace_all(std::string{ d }, "-", "_");
s.append(Strings::format("%s -> %s;", name, dependency_name));
}
}
@@ -71,39 +135,22 @@ namespace vcpkg::Commands::DependInfo
return s;
}
- std::string create_dgml_as_string(const std::vector<const SourceControlFile*>& source_control_files)
+ std::string create_dgml_as_string(const std::vector<PackageDependInfo>& depend_info)
{
std::string s;
s.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
s.append("<DirectedGraph xmlns=\"http://schemas.microsoft.com/vs/2009/dgml\">");
std::string nodes, links;
- for (const auto& source_control_file : source_control_files)
+ for (const auto& package : depend_info)
{
- const SourceParagraph& source_paragraph = *source_control_file->core_paragraph;
- const std::string name = source_paragraph.name;
+ const std::string name = package.package;
nodes.append(Strings::format("<Node Id=\"%s\" />", name));
// Iterate over dependencies.
- for (const Dependency& d : source_paragraph.depends)
- {
- if (d.qualifier.empty())
- links.append(Strings::format("<Link Source=\"%s\" Target=\"%s\" />", name, d.depend.name));
- else
- links.append(Strings::format(
- "<Link Source=\"%s\" Target=\"%s\" StrokeDashArray=\"4\" />", name, d.depend.name));
- }
-
- // Iterate over feature dependencies.
- const std::vector<std::unique_ptr<FeatureParagraph>>& feature_paragraphs =
- source_control_file->feature_paragraphs;
- for (const auto& feature_paragraph : feature_paragraphs)
+ for (const auto& d : package.dependencies)
{
- for (const Dependency& d : feature_paragraph->depends)
- {
- links.append(Strings::format(
- "<Link Source=\"%s\" Target=\"%s\" StrokeDashArray=\"4\" />", name, d.depend.name));
- }
+ links.append(Strings::format("<Link Source=\"%s\" Target=\"%s\" />", name, d));
}
}
@@ -116,135 +163,162 @@ namespace vcpkg::Commands::DependInfo
}
std::string create_graph_as_string(const std::unordered_set<std::string>& switches,
- const std::vector<const SourceControlFile*>& source_control_files)
+ const std::vector<PackageDependInfo>& depend_info)
{
if (Util::Sets::contains(switches, OPTION_DOT))
{
- return create_dot_as_string(source_control_files);
+ return create_dot_as_string(depend_info);
}
else if (Util::Sets::contains(switches, OPTION_DGML))
{
- return create_dgml_as_string(source_control_files);
+ return create_dgml_as_string(depend_info);
}
return "";
}
- void build_dependencies_list(std::set<std::string>& packages_to_keep,
- const std::string& requested_package,
- const std::vector<const SourceControlFile*>& source_control_files,
- const std::unordered_set<std::string>& switches)
+ void assign_depth_to_dependencies(const std::string& package,
+ const int depth,
+ const int max_depth,
+ std::map<std::string, PackageDependInfo>& dependencies_map)
{
- auto maybe_requested_spec = ParsedSpecifier::from_string(requested_package);
- // TODO: move this check to the top-level invocation of this function since
- // argument `requested_package` shall always be valid in inner-level invocation.
- if (!maybe_requested_spec.has_value())
- {
- System::print2(System::Color::warning,
- "'",
- requested_package,
- "' is not a valid package specifier: ",
- vcpkg::to_string(maybe_requested_spec.error()),
- "\n");
- return;
- }
- auto requested_spec = maybe_requested_spec.get();
+ auto iter = dependencies_map.find(package);
+ Checks::check_exit(VCPKG_LINE_INFO, iter != dependencies_map.end(), "Package not found in dependency graph");
- const auto source_control_file =
- Util::find_if(source_control_files, [&requested_spec](const auto& source_control_file) {
- return source_control_file->core_paragraph->name == requested_spec->name;
- });
+ PackageDependInfo& info = iter->second;
- if (source_control_file != source_control_files.end())
+ if (depth > info.depth)
{
- const auto new_package = packages_to_keep.insert(requested_spec->name).second;
-
- if (new_package && !Util::Sets::contains(switches, OPTION_NO_RECURSE))
+ info.depth = depth;
+ if (depth < max_depth || max_depth == NO_RECURSE_LIMIT_VALUE)
{
- for (const auto& dependency : (*source_control_file)->core_paragraph->depends)
+ for (auto&& dependency : info.dependencies)
{
- build_dependencies_list(packages_to_keep, dependency.depend.name, source_control_files, switches);
- }
-
- // Collect features with `*` considered
- std::set<const FeatureParagraph*> collected_features;
- for (const auto& requested_feature_name : requested_spec->features)
- {
- if (requested_feature_name == "*")
- {
- for (auto &&feature_paragraph : (*source_control_file)->feature_paragraphs)
- {
- collected_features.insert(std::addressof(Util::as_const(*feature_paragraph)));
- }
- continue;
- }
- auto maybe_feature = (*source_control_file)->find_feature(requested_feature_name);
- if (auto &&feature_paragraph = maybe_feature.get())
- {
- collected_features.insert(std::addressof(Util::as_const(*feature_paragraph)));
- }
- else
- {
- System::print2(System::Color::warning,
- "dependency '",
- requested_feature_name,
- "' of package '",
- requested_spec->name,
- "' does not exist\n");
- continue;
- }
- }
- for (auto feature_paragraph : collected_features)
- {
- for (const auto& dependency : feature_paragraph->depends)
- {
- build_dependencies_list(packages_to_keep, dependency.depend.name, source_control_files, switches);
- }
+ assign_depth_to_dependencies(dependency, depth + 1, max_depth, dependencies_map);
}
}
}
- else
+ };
+
+ std::vector<PackageDependInfo> extract_depend_info(const std::vector<const InstallPlanAction*>& install_actions,
+ const int max_depth)
+ {
+ std::map<std::string, PackageDependInfo> package_dependencies;
+ for (const InstallPlanAction* pia : install_actions)
{
- System::print2(System::Color::warning, "package '", requested_package, "' does not exist\n");
+ const InstallPlanAction& install_action = *pia;
+
+ const std::vector<std::string> dependencies =
+ Util::fmap(install_action.computed_dependencies, [](const PackageSpec& spec) { return spec.name(); });
+
+ std::set<std::string> features{install_action.feature_list};
+ features.erase("core");
+
+ std::string port_name = install_action.spec.name();
+
+ PackageDependInfo info {port_name, -1, features, dependencies};
+ package_dependencies.emplace(port_name, std::move(info));
}
+
+ const InstallPlanAction& init = *install_actions.back();
+ assign_depth_to_dependencies(init.spec.name(), 0, max_depth, package_dependencies);
+
+ std::vector<PackageDependInfo> out =
+ Util::fmap(package_dependencies, [](auto&& kvpair) -> PackageDependInfo { return kvpair.second; });
+ Util::erase_remove_if(out, [](auto&& info) { return info.depth < 0; });
+ return out;
}
- void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
+ void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet)
{
const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
+ const int max_depth = get_max_depth(options);
+ const SortMode sort_mode = get_sort_mode(options);
+ const bool show_depth = Util::Sets::contains(options.switches, OPTION_SHOW_DEPTH);
- // TODO: Optimize implementation, current implementation needs to load all ports from disk which is too slow.
- PathsPortFileProvider provider(paths, args.overlay_ports.get());
- auto source_control_files = Util::fmap(provider.load_all_control_files(), [](auto&& scfl) -> const SourceControlFile * {
- return scfl->source_control_file.get();
+ const std::vector<FullPackageSpec> specs = Util::fmap(args.command_arguments, [&](auto&& arg) {
+ return Input::check_and_get_full_package_spec(
+ std::string{arg}, default_triplet, COMMAND_STRUCTURE.example_text);
});
- if (args.command_arguments.size() >= 1)
+ for (auto&& spec : specs)
{
- std::set<std::string> packages_to_keep;
- for (const auto& requested_package : args.command_arguments)
+ Input::check_triplet(spec.package_spec.triplet(), paths);
+ }
+
+ PathsPortFileProvider provider(paths, args.overlay_ports.get());
+
+ // 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())
{
- build_dependencies_list(packages_to_keep, requested_package, source_control_files, options.switches);
+ return install_action;
}
+ Checks::exit_with_message(VCPKG_LINE_INFO, "Only install actions should exist in the plan");
+ });
- Util::erase_remove_if(source_control_files, [&packages_to_keep](const auto& source_control_file) {
- return !Util::Sets::contains(packages_to_keep, source_control_file->core_paragraph->name);
- });
- }
+ std::vector<PackageDependInfo> depend_info = extract_depend_info(install_actions, max_depth);
if (Util::Sets::contains(options.switches, OPTION_DOT) || Util::Sets::contains(options.switches, OPTION_DGML))
{
- const std::string graph_as_string = create_graph_as_string(options.switches, source_control_files);
+ const std::vector<const SourceControlFile*> source_control_files =
+ Util::fmap(install_actions, [](const InstallPlanAction* install_action) {
+ const SourceControlFileLocation& scfl =
+ install_action->source_control_file_location.value_or_exit(VCPKG_LINE_INFO);
+ return const_cast<const SourceControlFile*>(scfl.source_control_file.get());
+ });
+
+ const std::string graph_as_string = create_graph_as_string(options.switches, depend_info);
System::print2(graph_as_string, '\n');
Checks::exit_success(VCPKG_LINE_INFO);
}
- for (auto&& source_control_file : source_control_files)
+
+ // TODO: Improve this code
+ auto lex = [](const PackageDependInfo& lhs, const PackageDependInfo& rhs) -> bool {
+ return lhs.package < rhs.package;
+ };
+ auto topo = [](const PackageDependInfo& lhs, const PackageDependInfo& rhs) -> bool {
+ return lhs.depth > rhs.depth;
+ };
+ auto reverse = [topo](const PackageDependInfo& lhs, const PackageDependInfo& rhs) -> bool {
+ return lhs.depth < rhs.depth;
+ };
+
+ switch (sort_mode)
{
- const SourceParagraph& source_paragraph = *source_control_file->core_paragraph.get();
- const auto s = Strings::join(", ", source_paragraph.depends, [](const Dependency& d) { return d.name(); });
- System::print2(source_paragraph.name, ": ", s, "\n");
+ case SortMode::Lexicographical: std::sort(std::begin(depend_info), std::end(depend_info), lex); break;
+ case SortMode::ReverseTopological:
+ std::sort(std::begin(depend_info), std::end(depend_info), reverse);
+ break;
+ case SortMode::Topological: std::sort(std::begin(depend_info), std::end(depend_info), topo); break;
+ default: Checks::unreachable(VCPKG_LINE_INFO);
}
+ for (auto&& info : depend_info)
+ {
+ if (info.depth >= 0)
+ {
+ std::string features = Strings::join(", ", info.features);
+ const std::string dependencies = Strings::join(", ", info.dependencies);
+
+ if (show_depth)
+ {
+ System::print2(System::Color::error, "(", info.depth, ") ");
+ }
+ System::print2(System::Color::success, info.package);
+ if (!features.empty())
+ {
+ System::print2("[");
+ System::print2(System::Color::warning, features);
+ System::print2("]");
+ }
+ System::print2(": ", dependencies, "\n");
+ }
+ }
Checks::exit_success(VCPKG_LINE_INFO);
}
}
diff --git a/toolsrc/src/vcpkg/commands.exportifw.cpp b/toolsrc/src/vcpkg/commands.exportifw.cpp
index f0946110c..3d963a297 100644
--- a/toolsrc/src/vcpkg/commands.exportifw.cpp
+++ b/toolsrc/src/vcpkg/commands.exportifw.cpp
@@ -352,13 +352,15 @@ namespace vcpkg::Export::IFW
System::print2("Generating repository ", repository_dir.generic_u8string(), "...\n");
std::error_code ec;
+ fs::path failure_point;
Files::Filesystem& fs = paths.get_filesystem();
- fs.remove_all(repository_dir, ec);
+ fs.remove_all(repository_dir, ec, failure_point);
Checks::check_exit(VCPKG_LINE_INFO,
!ec,
- "Could not remove outdated repository directory %s",
- repository_dir.generic_u8string());
+ "Could not remove outdated repository directory %s due to file %s",
+ repository_dir.generic_u8string(),
+ failure_point.string());
const auto cmd_line = Strings::format(R"("%s" --packages "%s" "%s" > nul)",
repogen_exe.u8string(),
@@ -414,16 +416,18 @@ namespace vcpkg::Export::IFW
const VcpkgPaths& paths)
{
std::error_code ec;
+ fs::path failure_point;
Files::Filesystem& fs = paths.get_filesystem();
// Prepare packages directory
const fs::path ifw_packages_dir_path = get_packages_dir_path(export_id, ifw_options, paths);
- fs.remove_all(ifw_packages_dir_path, ec);
+ fs.remove_all(ifw_packages_dir_path, ec, failure_point);
Checks::check_exit(VCPKG_LINE_INFO,
!ec,
- "Could not remove outdated packages directory %s",
- ifw_packages_dir_path.generic_u8string());
+ "Could not remove outdated packages directory %s due to file %s",
+ ifw_packages_dir_path.generic_u8string(),
+ failure_point.string());
fs.create_directory(ifw_packages_dir_path, ec);
Checks::check_exit(
diff --git a/toolsrc/src/vcpkg/commands.import.cpp b/toolsrc/src/vcpkg/commands.import.cpp
index 40f5a434c..c18d788c5 100644
--- a/toolsrc/src/vcpkg/commands.import.cpp
+++ b/toolsrc/src/vcpkg/commands.import.cpp
@@ -108,7 +108,7 @@ namespace vcpkg::Commands::Import
const fs::path include_directory(args.command_arguments[1]);
const fs::path project_directory(args.command_arguments[2]);
- const Expected<std::unordered_map<std::string, std::string>> pghs =
+ const Expected<Parse::RawParagraph> pghs =
Paragraphs::get_single_paragraph(paths.get_filesystem(), control_file_path);
Checks::check_exit(VCPKG_LINE_INFO,
pghs.get() != nullptr,
diff --git a/toolsrc/src/vcpkg/commands.portsdiff.cpp b/toolsrc/src/vcpkg/commands.portsdiff.cpp
index b30c38f43..cddc274b8 100644
--- a/toolsrc/src/vcpkg/commands.portsdiff.cpp
+++ b/toolsrc/src/vcpkg/commands.portsdiff.cpp
@@ -105,7 +105,7 @@ namespace vcpkg::Commands::PortsDiff
std::map<std::string, VersionT> names_and_versions;
for (auto&& port : all_ports)
names_and_versions.emplace(port->core_paragraph->name, port->core_paragraph->version);
- fs.remove_all(temp_checkout_path, ec);
+ fs.remove_all(temp_checkout_path, VCPKG_LINE_INFO);
return names_and_versions;
}
diff --git a/toolsrc/src/vcpkg/commands.search.cpp b/toolsrc/src/vcpkg/commands.search.cpp
index 3d8387ee1..943233502 100644
--- a/toolsrc/src/vcpkg/commands.search.cpp
+++ b/toolsrc/src/vcpkg/commands.search.cpp
@@ -2,12 +2,12 @@
#include <vcpkg/base/system.print.h>
#include <vcpkg/commands.h>
+#include <vcpkg/dependencies.h>
#include <vcpkg/globalstate.h>
#include <vcpkg/help.h>
#include <vcpkg/paragraphs.h>
#include <vcpkg/sourceparagraph.h>
#include <vcpkg/vcpkglib.h>
-#include <vcpkg/dependencies.h>
using vcpkg::Dependencies::PathsPortFileProvider;
@@ -67,9 +67,9 @@ namespace vcpkg::Commands::Search
const bool full_description = Util::Sets::contains(options.switches, OPTION_FULLDESC);
PathsPortFileProvider provider(paths, args.overlay_ports.get());
- auto source_paragraphs = Util::fmap(provider.load_all_control_files(), [](auto&& port) -> const SourceControlFile * {
- return port->source_control_file.get();
- });
+ auto source_paragraphs =
+ Util::fmap(provider.load_all_control_files(),
+ [](auto&& port) -> const SourceControlFile* { return port->source_control_file.get(); });
if (args.command_arguments.empty())
{
diff --git a/toolsrc/src/vcpkg/dependencies.cpp b/toolsrc/src/vcpkg/dependencies.cpp
index b604c9acf..9fccf950e 100644
--- a/toolsrc/src/vcpkg/dependencies.cpp
+++ b/toolsrc/src/vcpkg/dependencies.cpp
@@ -108,9 +108,8 @@ namespace vcpkg::Dependencies
static ClusterSource cluster_from_scf(const SourceControlFileLocation& scfl, Triplet t)
{
ClusterSource ret;
- ret.build_edges.emplace("core",
- filter_dependencies_to_specs(scfl.source_control_file->core_paragraph->depends,
- t));
+ ret.build_edges.emplace("core",
+ filter_dependencies_to_specs(scfl.source_control_file->core_paragraph->depends, t));
for (const auto& feature : scfl.source_control_file->feature_paragraphs)
ret.build_edges.emplace(feature->name, filter_dependencies_to_specs(feature->depends, t));
@@ -123,22 +122,23 @@ namespace vcpkg::Dependencies
const PortFileProvider& m_provider;
};
- std::string to_output_string(RequestType request_type,
- const CStringView s,
- const Build::BuildPackageOptions& options,
+ std::string to_output_string(RequestType request_type,
+ const CStringView s,
+ const Build::BuildPackageOptions& options,
const fs::path& install_port_path,
const fs::path& default_port_path)
{
- if (!default_port_path.empty()
- && !Strings::case_insensitive_ascii_starts_with(install_port_path.u8string(),
- default_port_path.u8string()))
+ if (!default_port_path.empty() &&
+ !Strings::case_insensitive_ascii_starts_with(install_port_path.u8string(), default_port_path.u8string()))
{
const char* const from_head = options.use_head_version == Build::UseHeadVersion::YES ? " (from HEAD)" : "";
switch (request_type)
{
- case RequestType::AUTO_SELECTED: return Strings::format(" * %s%s -- %s", s, from_head, install_port_path.u8string());
- case RequestType::USER_REQUESTED: return Strings::format(" %s%s -- %s", s, from_head, install_port_path.u8string());
- default: Checks::unreachable(VCPKG_LINE_INFO);
+ case RequestType::AUTO_SELECTED:
+ return Strings::format(" * %s%s -- %s", s, from_head, install_port_path.u8string());
+ case RequestType::USER_REQUESTED:
+ return Strings::format(" %s%s -- %s", s, from_head, install_port_path.u8string());
+ default: Checks::unreachable(VCPKG_LINE_INFO);
}
}
return to_output_string(request_type, s, options);
@@ -152,7 +152,7 @@ namespace vcpkg::Dependencies
switch (request_type)
{
- case RequestType::AUTO_SELECTED: return Strings::format(" * %s%s", s, from_head);
+ case RequestType::AUTO_SELECTED: return Strings::format(" * %s%s", s, from_head);
case RequestType::USER_REQUESTED: return Strings::format(" %s%s", s, from_head);
default: Checks::unreachable(VCPKG_LINE_INFO);
}
@@ -162,7 +162,7 @@ namespace vcpkg::Dependencies
{
switch (request_type)
{
- case RequestType::AUTO_SELECTED: return Strings::format(" * %s", s);
+ case RequestType::AUTO_SELECTED: return Strings::format(" * %s", s);
case RequestType::USER_REQUESTED: return Strings::format(" %s", s);
default: Checks::unreachable(VCPKG_LINE_INFO);
}
@@ -293,7 +293,8 @@ namespace vcpkg::Dependencies
MapPortFileProvider::MapPortFileProvider(const std::unordered_map<std::string, SourceControlFileLocation>& map)
: ports(map)
- {}
+ {
+ }
Optional<const SourceControlFileLocation&> MapPortFileProvider::get_control_file(const std::string& spec) const
{
@@ -304,13 +305,14 @@ namespace vcpkg::Dependencies
std::vector<const SourceControlFileLocation*> MapPortFileProvider::load_all_control_files() const
{
- return Util::fmap(ports, [](auto&& kvpair) -> const SourceControlFileLocation * { return &kvpair.second; });
+ 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)
+ 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)
@@ -325,7 +327,7 @@ namespace vcpkg::Dependencies
overlay.string());
Checks::check_exit(VCPKG_LINE_INFO,
- fs::stdfs::is_directory(overlay),
+ fs::is_directory(fs.status(VCPKG_LINE_INFO, overlay)),
"Error: Path \"%s\" must be a directory",
overlay.string());
@@ -354,7 +356,7 @@ namespace vcpkg::Dependencies
{
if (scf->get()->core_paragraph->name == spec)
{
- SourceControlFileLocation scfl{ std::move(*scf), ports_dir };
+ SourceControlFileLocation scfl{std::move(*scf), ports_dir};
auto it = cache.emplace(spec, std::move(scfl));
return it.first->second;
}
@@ -362,9 +364,8 @@ namespace vcpkg::Dependencies
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());
+ Checks::exit_with_message(
+ VCPKG_LINE_INFO, "Error: Failed to load port from %s", spec, ports_dir.u8string());
}
}
@@ -373,7 +374,7 @@ namespace vcpkg::Dependencies
{
if (scf->get()->core_paragraph->name == spec)
{
- SourceControlFileLocation scfl{ std::move(*scf), ports_dir / spec };
+ SourceControlFileLocation scfl{std::move(*scf), ports_dir / spec};
auto it = cache.emplace(spec, std::move(scfl));
return it.first->second;
}
@@ -399,7 +400,7 @@ namespace vcpkg::Dependencies
auto port_name = scf->get()->core_paragraph->name;
if (cache.find(port_name) == cache.end())
{
- SourceControlFileLocation scfl{ std::move(*scf), ports_dir };
+ SourceControlFileLocation scfl{std::move(*scf), ports_dir};
auto it = cache.emplace(port_name, std::move(scfl));
ret.emplace_back(&it.first->second);
}
@@ -407,9 +408,8 @@ namespace vcpkg::Dependencies
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());
+ Checks::exit_with_message(
+ VCPKG_LINE_INFO, "Error: Failed to load port from %s", ports_dir.u8string());
}
continue;
}
@@ -421,7 +421,7 @@ namespace vcpkg::Dependencies
auto port_name = scf->core_paragraph->name;
if (cache.find(port_name) == cache.end())
{
- SourceControlFileLocation scfl{ std::move(scf), ports_dir / port_name };
+ SourceControlFileLocation scfl{std::move(scf), ports_dir / port_name};
auto it = cache.emplace(port_name, std::move(scfl));
ret.emplace_back(&it.first->second);
}
@@ -768,9 +768,10 @@ namespace vcpkg::Dependencies
/// <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)
+ std::vector<AnyAction> create_feature_install_plan(
+ const std::unordered_map<std::string, SourceControlFileLocation>& map,
+ const std::vector<FeatureSpec>& specs,
+ const StatusParagraphs& status_db)
{
MapPortFileProvider provider(map);
return create_feature_install_plan(provider, specs, status_db);
@@ -832,9 +833,8 @@ namespace vcpkg::Dependencies
{
// 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");
+ 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),
@@ -914,7 +914,9 @@ namespace vcpkg::Dependencies
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 std::vector<AnyAction>& action_plan,
+ const bool is_recursive,
+ const fs::path& default_ports_dir)
{
std::vector<const RemovePlanAction*> remove_plans;
std::vector<const InstallPlanAction*> rebuilt_plans;
@@ -971,13 +973,10 @@ namespace vcpkg::Dependencies
static auto actions_to_output_string = [&](const std::vector<const InstallPlanAction*>& v) {
return Strings::join("\n", v, [&](const InstallPlanAction* p) {
- if (auto * pscfl = p->source_control_file_location.get())
+ if (auto* pscfl = p->source_control_file_location.get())
{
- return to_output_string(p->request_type,
- p->displayname(),
- p->build_options,
- pscfl->source_location,
- default_ports_dir);
+ return to_output_string(
+ p->request_type, p->displayname(), p->build_options, pscfl->source_location, default_ports_dir);
}
return to_output_string(p->request_type, p->displayname(), p->build_options);
diff --git a/toolsrc/src/vcpkg/export.cpp b/toolsrc/src/vcpkg/export.cpp
index 88c1526c5..f306bf4e6 100644
--- a/toolsrc/src/vcpkg/export.cpp
+++ b/toolsrc/src/vcpkg/export.cpp
@@ -400,8 +400,10 @@ namespace vcpkg::Export
Files::Filesystem& fs = paths.get_filesystem();
const fs::path export_to_path = paths.root;
const fs::path raw_exported_dir_path = export_to_path / export_id;
+ fs.remove_all(raw_exported_dir_path, VCPKG_LINE_INFO);
+
+ // TODO: error handling
std::error_code ec;
- fs.remove_all(raw_exported_dir_path, ec);
fs.create_directory(raw_exported_dir_path, ec);
// execute the plan
@@ -476,7 +478,7 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console
if (!opts.raw)
{
- fs.remove_all(raw_exported_dir_path, ec);
+ fs.remove_all(raw_exported_dir_path, VCPKG_LINE_INFO);
}
}
diff --git a/toolsrc/src/vcpkg/install.cpp b/toolsrc/src/vcpkg/install.cpp
index de19c360a..f6330e408 100644
--- a/toolsrc/src/vcpkg/install.cpp
+++ b/toolsrc/src/vcpkg/install.cpp
@@ -174,7 +174,7 @@ namespace vcpkg::Install
const std::vector<fs::path> package_file_paths = fs.get_files_recursive(package_dir);
const size_t package_remove_char_count = package_dir.generic_string().size() + 1; // +1 for the slash
auto package_files = Util::fmap(package_file_paths, [package_remove_char_count](const fs::path& path) {
- return std::move(std::string(path.generic_string(), package_remove_char_count));
+ return std::move(std::string(path.generic_string(), package_remove_char_count));
});
return SortedVector<std::string>(std::move(package_files));
@@ -202,6 +202,12 @@ namespace vcpkg::Install
struct intersection_compare
{
+ // The VS2015 standard library requires comparison operators of T and U
+ // to also support comparison of T and T, and of U and U, due to debug checks.
+#if _MSC_VER < 1910
+ bool operator()(const std::string& lhs, const std::string& rhs) { return lhs < rhs; }
+ bool operator()(const file_pack& lhs, const file_pack& rhs) { return lhs.first < rhs.first; }
+#endif
bool operator()(const std::string& lhs, const file_pack& rhs) { return lhs < rhs.first; }
bool operator()(const file_pack& lhs, const std::string& rhs) { return lhs.first < rhs; }
};
@@ -331,11 +337,8 @@ 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.source_control_file,
- action.spec.triplet(),
- static_cast<fs::path>(scfl.source_location),
- action.build_options,
- action.feature_list};
+ const Build::BuildPackageConfig build_config{
+ scfl, action.spec.triplet(), action.build_options, action.feature_list};
return Build::build_package(paths, build_config, status_db);
}();
@@ -355,8 +358,7 @@ namespace vcpkg::Install
{
auto& fs = paths.get_filesystem();
const fs::path package_dir = paths.package_dir(action.spec);
- std::error_code ec;
- fs.remove_all(package_dir, ec);
+ fs.remove_all(package_dir, VCPKG_LINE_INFO);
}
if (action.build_options.clean_downloads == Build::CleanDownloads::YES)
diff --git a/toolsrc/src/vcpkg/logicexpression.cpp b/toolsrc/src/vcpkg/logicexpression.cpp
new file mode 100644
index 000000000..0cf08ee03
--- /dev/null
+++ b/toolsrc/src/vcpkg/logicexpression.cpp
@@ -0,0 +1,268 @@
+
+#include "pch.h"
+
+#include <vcpkg/base/checks.h>
+#include <vcpkg/base/system.print.h>
+#include <vcpkg/logicexpression.h>
+
+#include <string>
+#include <vector>
+
+namespace vcpkg
+{
+ struct ParseError
+ {
+ ParseError(int column, std::string line, std::string message) : column(column), line(line), message(message) {}
+
+ const int column;
+ const std::string line;
+ const std::string message;
+
+ void print_error() const
+ {
+ System::print2(System::Color::error,
+ "Error: ",
+ message,
+ "\n"
+ " on expression: \"",
+ line,
+ "\"\n",
+ " ",
+ std::string(column, ' '),
+ "^\n");
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+ };
+
+ // logic expression supports the following :
+ // primary-expression:
+ // ( logic-expression )
+ // identifier
+ // identifier:
+ // alpha-numeric string of characters
+ // logic-expression: <- this is the entry point
+ // not-expression
+ // not-expression | logic-expression
+ // not-expression & logic-expression
+ // not-expression:
+ // ! primary-expression
+ // primary-expression
+ //
+ // | and & have equal precidence and cannot be used together at the same nesting level
+ // for example a|b&c is not allowd but (a|b)&c and a|(b&c) are allowed.
+ class ExpressionParser
+ {
+ public:
+ ExpressionParser(const std::string& str, const std::string& evaluation_context)
+ : raw_text(str), evaluation_context(evaluation_context)
+ {
+ go_to_begin();
+
+ final_result = logic_expression();
+
+ if (current_iter != raw_text.end())
+ {
+ 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; }
+
+ private:
+ bool final_result;
+
+ std::string::const_iterator current_iter;
+ const std::string& raw_text;
+ char current_char;
+
+ const std::string& evaluation_context;
+
+ std::unique_ptr<ParseError> err;
+
+ void add_error(std::string message, int column = -1)
+ {
+ // avoid castcading errors by only saving the first
+ if (!err)
+ {
+ if (column < 0)
+ {
+ column = current_column();
+ }
+ err = std::make_unique<ParseError>(column, raw_text, message);
+ }
+
+ // Avoid error loops by skipping to the end
+ skip_to_end();
+ }
+
+ int current_column() const { return static_cast<int>(current_iter - raw_text.begin()); }
+
+ void go_to_begin()
+ {
+ current_iter = raw_text.begin();
+ current_char = (current_iter != raw_text.end() ? *current_iter : current_char);
+
+ if (current_char == ' ' || current_char == '\t')
+ {
+ next_skip_whitespace();
+ }
+ }
+ void skip_to_end()
+ {
+ current_iter = raw_text.end();
+ current_char = '\0';
+ }
+ char current() const { return current_char; }
+ char next()
+ {
+ if (current_char != '\0')
+ {
+ current_iter++;
+ current_char = (current_iter != raw_text.end() ? *current_iter : '\0');
+ }
+ return current();
+ }
+ void skip_whitespace()
+ {
+ while (current_char == ' ' || current_char == '\t')
+ {
+ current_char = next();
+ }
+ }
+ char next_skip_whitespace()
+ {
+ next();
+ skip_whitespace();
+ return current_char;
+ }
+
+ static bool is_alphanum(char ch)
+ {
+ return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || (ch == '-');
+ }
+
+ bool evaluate_identifier(const std::string name) const
+ {
+ return evaluation_context.find(name) != std::string::npos;
+ }
+
+ // identifier:
+ // alpha-numeric string of characters
+ bool identifier_expression()
+ {
+ auto curr = current();
+ std::string name;
+
+ for (curr = current(); is_alphanum(curr); curr = next())
+ {
+ name += curr;
+ }
+
+ if (name.empty())
+ {
+ add_error("Invalid logic expression, unexpected character");
+ return false;
+ }
+
+ bool result = evaluate_identifier(name);
+ skip_whitespace();
+ return result;
+ }
+
+ // not-expression:
+ // ! primary-expression
+ // primary-expression
+ bool not_expression()
+ {
+ if (current() == '!')
+ {
+ next_skip_whitespace();
+ return !primary_expression();
+ }
+
+ return primary_expression();
+ }
+
+ template<char oper, char other, bool operation(bool, bool)>
+ bool logic_expression_helper(bool seed)
+ {
+ do
+ {
+ // Support chains of the operator to avoid breaking backwards compatability
+ while (next() == oper)
+ {
+ };
+ seed = operation(not_expression(), seed);
+
+ } while (current() == oper);
+
+ if (current() == other)
+ {
+ add_error("Mixing & and | is not allowed, Use () to specify order of operations.");
+ }
+
+ skip_whitespace();
+ return seed;
+ }
+ static bool and_helper(bool left, bool right) { return left && right; }
+ static bool or_helper(bool left, bool right) { return left || right; }
+
+ // logic-expression: <- entry point
+ // not-expression
+ // not-expression | logic-expression
+ // not-expression & logic-expression
+ bool logic_expression()
+ {
+ auto result = not_expression();
+
+ switch (current())
+ {
+ case '|':
+ {
+ return logic_expression_helper<'|', '&', or_helper>(result);
+ }
+ case '&':
+ {
+ return logic_expression_helper<'&', '|', and_helper>(result);
+ }
+ default: return result;
+ }
+ }
+
+ // primary-expression:
+ // ( logic-expression )
+ // identifier
+ bool primary_expression()
+ {
+ if (current() == '(')
+ {
+ next_skip_whitespace();
+ bool result = logic_expression();
+ if (current() != ')')
+ {
+ add_error("Error: missing closing )");
+ return result;
+ }
+ next_skip_whitespace();
+ return result;
+ }
+
+ return identifier_expression();
+ }
+ };
+
+ bool evaluate_expression(const std::string& expression, const std::string& evaluation_context)
+ {
+ ExpressionParser parser(expression, evaluation_context);
+
+ return parser.get_result();
+ }
+}
diff --git a/toolsrc/src/vcpkg/paragraphs.cpp b/toolsrc/src/vcpkg/paragraphs.cpp
index 21ef2c4d9..1232b940a 100644
--- a/toolsrc/src/vcpkg/paragraphs.cpp
+++ b/toolsrc/src/vcpkg/paragraphs.cpp
@@ -116,7 +116,7 @@ namespace vcpkg::Paragraphs
skip_spaces(ch);
}
- void get_paragraph(char& ch, std::unordered_map<std::string, std::string>& fields)
+ void get_paragraph(char& ch, RawParagraph& fields)
{
fields.clear();
std::string fieldname;
@@ -141,9 +141,9 @@ namespace vcpkg::Paragraphs
}
public:
- std::vector<std::unordered_map<std::string, std::string>> get_paragraphs()
+ std::vector<RawParagraph> get_paragraphs()
{
- std::vector<std::unordered_map<std::string, std::string>> paragraphs;
+ std::vector<RawParagraph> paragraphs;
char ch;
peek(ch);
@@ -164,51 +164,48 @@ namespace vcpkg::Paragraphs
}
};
- Expected<std::unordered_map<std::string, std::string>> get_single_paragraph(const Files::Filesystem& fs,
- const fs::path& control_path)
+ Expected<RawParagraph> parse_single_paragraph(const std::string& str)
{
- const Expected<std::string> contents = fs.read_contents(control_path);
- if (auto spgh = contents.get())
+ const std::vector<RawParagraph> p = Parser(str.c_str(), str.c_str() + str.size()).get_paragraphs();
+
+ if (p.size() == 1)
{
- return parse_single_paragraph(*spgh);
+ return p.at(0);
}
- return contents.error();
+ return std::error_code(ParagraphParseResult::EXPECTED_ONE_PARAGRAPH);
}
- Expected<std::vector<std::unordered_map<std::string, std::string>>> get_paragraphs(const Files::Filesystem& fs,
- const fs::path& control_path)
+ Expected<RawParagraph> get_single_paragraph(const Files::Filesystem& fs, const fs::path& control_path)
{
const Expected<std::string> contents = fs.read_contents(control_path);
if (auto spgh = contents.get())
{
- return parse_paragraphs(*spgh);
+ return parse_single_paragraph(*spgh);
}
return contents.error();
}
- Expected<std::unordered_map<std::string, std::string>> parse_single_paragraph(const std::string& str)
+ Expected<std::vector<RawParagraph>> get_paragraphs(const Files::Filesystem& fs, const fs::path& control_path)
{
- const std::vector<std::unordered_map<std::string, std::string>> p =
- Parser(str.c_str(), str.c_str() + str.size()).get_paragraphs();
-
- if (p.size() == 1)
+ const Expected<std::string> contents = fs.read_contents(control_path);
+ if (auto spgh = contents.get())
{
- return p.at(0);
+ return parse_paragraphs(*spgh);
}
- return std::error_code(ParagraphParseResult::EXPECTED_ONE_PARAGRAPH);
+ return contents.error();
}
- Expected<std::vector<std::unordered_map<std::string, std::string>>> parse_paragraphs(const std::string& str)
+ Expected<std::vector<RawParagraph>> parse_paragraphs(const std::string& str)
{
return Parser(str.c_str(), str.c_str() + str.size()).get_paragraphs();
}
ParseExpected<SourceControlFile> try_load_port(const Files::Filesystem& fs, const fs::path& path)
{
- Expected<std::vector<std::unordered_map<std::string, std::string>>> pghs = get_paragraphs(fs, path / "CONTROL");
+ Expected<std::vector<RawParagraph>> pghs = get_paragraphs(fs, path / "CONTROL");
if (auto vector_pghs = pghs.get())
{
return SourceControlFile::parse_control_file(std::move(*vector_pghs));
@@ -221,7 +218,7 @@ namespace vcpkg::Paragraphs
Expected<BinaryControlFile> try_load_cached_package(const VcpkgPaths& paths, const PackageSpec& spec)
{
- Expected<std::vector<std::unordered_map<std::string, std::string>>> pghs =
+ Expected<std::vector<RawParagraph>> pghs =
get_paragraphs(paths.get_filesystem(), paths.package_dir(spec) / "CONTROL");
if (auto p = pghs.get())
diff --git a/toolsrc/src/vcpkg/parse.cpp b/toolsrc/src/vcpkg/parse.cpp
index d50296cf8..9c9968249 100644
--- a/toolsrc/src/vcpkg/parse.cpp
+++ b/toolsrc/src/vcpkg/parse.cpp
@@ -6,8 +6,7 @@
namespace vcpkg::Parse
{
- static Optional<std::string> remove_field(std::unordered_map<std::string, std::string>* fields,
- const std::string& fieldname)
+ static Optional<std::string> remove_field(RawParagraph* fields, const std::string& fieldname)
{
auto it = fields->find(fieldname);
if (it == fields->end())
diff --git a/toolsrc/src/vcpkg/remove.cpp b/toolsrc/src/vcpkg/remove.cpp
index a40b27bd7..65e00668a 100644
--- a/toolsrc/src/vcpkg/remove.cpp
+++ b/toolsrc/src/vcpkg/remove.cpp
@@ -73,7 +73,7 @@ namespace vcpkg::Remove
if (ec)
{
#if defined(_WIN32)
- fs::stdfs::permissions(target, fs::stdfs::perms::owner_all | fs::stdfs::perms::group_all, ec);
+ fs::stdfs::permissions(target, fs::perms::owner_all | fs::perms::group_all, ec);
fs.remove(target, ec);
if (ec)
{
@@ -86,7 +86,7 @@ namespace vcpkg::Remove
#endif
}
}
- else if (!fs::stdfs::exists(status))
+ else if (!fs::exists(status))
{
System::printf(System::Color::warning, "Warning: %s: file not found\n", target.u8string());
}
@@ -179,8 +179,7 @@ namespace vcpkg::Remove
{
System::printf("Purging package %s...\n", display_name);
Files::Filesystem& fs = paths.get_filesystem();
- std::error_code ec;
- fs.remove_all(paths.packages / action.spec.dir(), ec);
+ fs.remove_all(paths.packages / action.spec.dir(), VCPKG_LINE_INFO);
System::printf(System::Color::success, "Purging package %s... done\n", display_name);
}
}
diff --git a/toolsrc/src/vcpkg/sourceparagraph.cpp b/toolsrc/src/vcpkg/sourceparagraph.cpp
index 9bc59cbe7..298095df6 100644
--- a/toolsrc/src/vcpkg/sourceparagraph.cpp
+++ b/toolsrc/src/vcpkg/sourceparagraph.cpp
@@ -1,5 +1,6 @@
#include "pch.h"
+#include <vcpkg/logicexpression.h>
#include <vcpkg/packagespec.h>
#include <vcpkg/sourceparagraph.h>
#include <vcpkg/triplet.h>
@@ -142,7 +143,7 @@ namespace vcpkg
}
ParseExpected<SourceControlFile> SourceControlFile::parse_control_file(
- std::vector<std::unordered_map<std::string, std::string>>&& control_paragraphs)
+ std::vector<Parse::RawParagraph>&& control_paragraphs)
{
if (control_paragraphs.size() == 0)
{
@@ -222,15 +223,8 @@ namespace vcpkg
std::vector<std::string> ret;
for (auto&& dep : deps)
{
- auto qualifiers = Strings::split(dep.qualifier, "&");
- if (std::all_of(qualifiers.begin(), qualifiers.end(), [&](const std::string& qualifier) {
- if (qualifier.empty()) return true;
- if (qualifier[0] == '!')
- {
- return t.canonical_name().find(qualifier.substr(1)) == std::string::npos;
- }
- return t.canonical_name().find(qualifier) != std::string::npos;
- }))
+ const auto& qualifier = dep.qualifier;
+ if (qualifier.empty() || evaluate_expression(qualifier, t.canonical_name()))
{
ret.emplace_back(dep.name());
}
@@ -238,6 +232,20 @@ namespace vcpkg
return ret;
}
+ std::vector<Features> filter_dependencies_to_features(const std::vector<vcpkg::Dependency>& deps, const Triplet& t)
+ {
+ std::vector<Features> ret;
+ for (auto&& dep : deps)
+ {
+ const auto& qualifier = dep.qualifier;
+ if (qualifier.empty() || evaluate_expression(qualifier, t.canonical_name()))
+ {
+ ret.emplace_back(dep.depend);
+ }
+ }
+ 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);
diff --git a/toolsrc/src/vcpkg/statusparagraph.cpp b/toolsrc/src/vcpkg/statusparagraph.cpp
index 86946a31a..f7e00f21c 100644
--- a/toolsrc/src/vcpkg/statusparagraph.cpp
+++ b/toolsrc/src/vcpkg/statusparagraph.cpp
@@ -24,7 +24,7 @@ namespace vcpkg
.push_back('\n');
}
- StatusParagraph::StatusParagraph(std::unordered_map<std::string, std::string>&& fields)
+ StatusParagraph::StatusParagraph(Parse::RawParagraph&& fields)
: want(Want::ERROR_STATE), state(InstallState::ERROR_STATE)
{
auto status_it = fields.find(BinaryParagraphRequiredField::STATUS);
diff --git a/toolsrc/src/vcpkg/tools.cpp b/toolsrc/src/vcpkg/tools.cpp
index 4f4b23055..7d56854c6 100644
--- a/toolsrc/src/vcpkg/tools.cpp
+++ b/toolsrc/src/vcpkg/tools.cpp
@@ -294,20 +294,6 @@ CMake suite maintained and supported by Kitware (kitware.com/cmake).
}
};
- static fs::path get_7za_path(const VcpkgPaths& paths)
- {
-#if defined(_WIN32)
- static const ToolData TOOL_DATA = parse_tool_data_from_xml(paths, "7zip").value_or_exit(VCPKG_LINE_INFO);
- if (!paths.get_filesystem().exists(TOOL_DATA.exe_path))
- {
- return fetch_tool(paths, "7zip", TOOL_DATA);
- }
- return TOOL_DATA.exe_path;
-#else
- Checks::exit_with_message(VCPKG_LINE_INFO, "Cannot download 7zip for non-Windows platforms.");
-#endif
- }
-
struct NinjaProvider : ToolProvider
{
std::string m_exe = "ninja";
@@ -443,31 +429,21 @@ git version 2.17.1.windows.2
virtual const fs::path& get_tool_path(const VcpkgPaths& paths, const std::string& tool) const override
{
return path_only_cache.get_lazy(tool, [&]() {
- // First deal with specially handled tools.
- // For these we may look in locations like Program Files, the PATH etc as well as the auto-downloaded
- // location.
- if (tool == Tools::SEVEN_ZIP) return get_7za_path(paths);
- if (tool == Tools::CMAKE || tool == Tools::GIT || tool == Tools::NINJA || tool == Tools::NUGET ||
- tool == Tools::IFW_INSTALLER_BASE)
- return get_tool_pathversion(paths, tool).path;
if (tool == Tools::IFW_BINARYCREATOR)
return get_tool_path(paths, Tools::IFW_INSTALLER_BASE).parent_path() / "binarycreator.exe";
if (tool == Tools::IFW_REPOGEN)
return get_tool_path(paths, Tools::IFW_INSTALLER_BASE).parent_path() / "repogen.exe";
- // For other tools, we simply always auto-download them.
- const ToolData tool_data = parse_tool_data_from_xml(paths, tool).value_or_exit(VCPKG_LINE_INFO);
- if (paths.get_filesystem().exists(tool_data.exe_path))
- {
- return tool_data.exe_path;
- }
- return fetch_tool(paths, tool, tool_data);
+ return get_tool_pathversion(paths, tool).path;
});
}
const PathAndVersion& get_tool_pathversion(const VcpkgPaths& paths, const std::string& tool) const
{
return path_version_cache.get_lazy(tool, [&]() -> PathAndVersion {
+ // First deal with specially handled tools.
+ // For these we may look in locations like Program Files, the PATH etc as well as the auto-downloaded
+ // location.
if (tool == Tools::CMAKE)
{
if (System::get_environment_variable("VCPKG_FORCE_SYSTEM_BINARIES").has_value())
@@ -495,7 +471,18 @@ git version 2.17.1.windows.2
if (tool == Tools::NUGET) return get_path(paths, NuGetProvider());
if (tool == Tools::IFW_INSTALLER_BASE) return get_path(paths, IfwInstallerBaseProvider());
- Checks::exit_with_message(VCPKG_LINE_INFO, "Finding version for %s is not implemented yet.", tool);
+ // For other tools, we simply always auto-download them.
+ auto maybe_tool_data = parse_tool_data_from_xml(paths, tool);
+ if (auto p_tool_data = maybe_tool_data.get())
+ {
+ if (paths.get_filesystem().exists(p_tool_data->exe_path))
+ {
+ return {p_tool_data->exe_path, p_tool_data->sha512};
+ }
+ return {fetch_tool(paths, tool, *p_tool_data), p_tool_data->sha512};
+ }
+
+ Checks::exit_with_message(VCPKG_LINE_INFO, "Unknown or unavailable tool: %s", tool);
});
}
diff --git a/toolsrc/src/vcpkg/userconfig.cpp b/toolsrc/src/vcpkg/userconfig.cpp
index a7c4e2765..a3c019be7 100644
--- a/toolsrc/src/vcpkg/userconfig.cpp
+++ b/toolsrc/src/vcpkg/userconfig.cpp
@@ -51,7 +51,7 @@ namespace vcpkg
{
const auto& pghs = *p_pghs;
- std::unordered_map<std::string, std::string> keys;
+ Parse::RawParagraph keys;
if (pghs.size() > 0) keys = pghs[0];
for (size_t x = 1; x < pghs.size(); ++x)
diff --git a/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp b/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp
index 7a28fb571..e48340df7 100644
--- a/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp
+++ b/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp
@@ -148,9 +148,8 @@ namespace vcpkg
}
if (Strings::starts_with(arg, "--scripts-root="))
{
- parse_cojoined_value(arg.substr(sizeof("--scripts-root=") - 1),
- "--scripts-root",
- args.scripts_root_dir);
+ parse_cojoined_value(
+ arg.substr(sizeof("--scripts-root=") - 1), "--scripts-root", args.scripts_root_dir);
continue;
}
if (arg == "--triplet")
@@ -161,16 +160,14 @@ namespace vcpkg
}
if (Strings::starts_with(arg, "--overlay-ports="))
{
- parse_cojoined_multivalue(arg.substr(sizeof("--overlay-ports=") - 1),
- "--overlay-ports",
- args.overlay_ports);
+ parse_cojoined_multivalue(
+ arg.substr(sizeof("--overlay-ports=") - 1), "--overlay-ports", args.overlay_ports);
continue;
}
if (Strings::starts_with(arg, "--overlay-triplets="))
{
- parse_cojoined_multivalue(arg.substr(sizeof("--overlay-triplets=") - 1),
- "--overlay-triplets",
- args.overlay_triplets);
+ parse_cojoined_multivalue(
+ arg.substr(sizeof("--overlay-triplets=") - 1), "--overlay-triplets", args.overlay_triplets);
continue;
}
if (arg == "--debug")
@@ -224,11 +221,11 @@ namespace vcpkg
{
const auto& key = arg.substr(0, eq_pos);
const auto& value = arg.substr(eq_pos + 1);
-
+
auto it = args.optional_command_arguments.find(key);
if (args.optional_command_arguments.end() == it)
{
- args.optional_command_arguments.emplace(key, std::vector<std::string> { value });
+ args.optional_command_arguments.emplace(key, std::vector<std::string>{value});
}
else
{
@@ -370,8 +367,9 @@ namespace vcpkg
{
if (v.empty())
{
- System::printf(
- System::Color::error, "Error: The option '%s' must be passed an argument.\n", option.name);
+ System::printf(System::Color::error,
+ "Error: The option '%s' must be passed an argument.\n",
+ option.name);
failed = true;
}
else
@@ -389,7 +387,7 @@ namespace vcpkg
System::printf(System::Color::error, "Unknown option(s) for command '%s':\n", this->command);
for (auto&& option : options_copy)
{
- System::print2(" ", option.first, "\n");
+ System::print2(" '", option.first, "'\n");
}
System::print2("\n");
failed = true;
@@ -425,12 +423,9 @@ namespace vcpkg
System::printf(" %-40s %s\n", (option.name + "=..."), option.short_help_text);
}
System::printf(" %-40s %s\n", "--triplet <t>", "Set the default triplet for unqualified packages");
- System::printf(" %-40s %s\n",
- "--overlay-ports=<path>",
- "Specify directories to be used when searching for ports");
- System::printf(" %-40s %s\n",
- "--overlay-triplets=<path>",
- "Specify directories containing triplets files");
+ System::printf(
+ " %-40s %s\n", "--overlay-ports=<path>", "Specify directories to be used when searching for ports");
+ System::printf(" %-40s %s\n", "--overlay-triplets=<path>", "Specify directories containing triplets files");
System::printf(" %-40s %s\n",
"--vcpkg-root <path>",
"Specify the vcpkg directory to use instead of current directory or tool directory");
diff --git a/toolsrc/src/vcpkg/vcpkgpaths.cpp b/toolsrc/src/vcpkg/vcpkgpaths.cpp
index d16acf2e8..bc46d2cfc 100644
--- a/toolsrc/src/vcpkg/vcpkgpaths.cpp
+++ b/toolsrc/src/vcpkg/vcpkgpaths.cpp
@@ -18,8 +18,9 @@ namespace vcpkg
const std::string& default_vs_path,
const std::vector<std::string>* triplets_dirs)
{
+ auto& fs = Files::get_real_filesystem();
std::error_code ec;
- const fs::path canonical_vcpkg_root_dir = fs::stdfs::canonical(vcpkg_root_dir, ec);
+ const fs::path canonical_vcpkg_root_dir = fs.canonical(vcpkg_root_dir, ec);
if (ec)
{
return ec;
@@ -42,7 +43,7 @@ namespace vcpkg
if (auto odp = overriddenDownloadsPath.get())
{
auto asPath = fs::u8path(*odp);
- if (!fs::stdfs::is_directory(asPath))
+ if (!fs::is_directory(fs.status(VCPKG_LINE_INFO, asPath)))
{
Metrics::g_metrics.lock()->track_property("error", "Invalid VCPKG_DOWNLOADS override directory.");
Checks::exit_with_message(
@@ -71,12 +72,12 @@ namespace vcpkg
{
if (scripts_dir->empty() || !fs::stdfs::is_directory(*scripts_dir))
{
- Metrics::g_metrics.lock()->track_property("error", "Invalid scripts override directory.");
- Checks::exit_with_message(
- VCPKG_LINE_INFO,
- "Invalid scripts override directory: %s; "
- "create that directory or unset --scripts-root to use the default scripts location.",
- scripts_dir->u8string());
+ Metrics::g_metrics.lock()->track_property("error", "Invalid scripts override directory.");
+ Checks::exit_with_message(
+ VCPKG_LINE_INFO,
+ "Invalid scripts override directory: %s; "
+ "create that directory or unset --scripts-root to use the default scripts location.",
+ scripts_dir->u8string());
}
paths.scripts = *scripts_dir;
@@ -147,26 +148,25 @@ namespace vcpkg
}
Util::sort_unique_erase(output);
return output;
- });
+ });
}
const fs::path VcpkgPaths::get_triplet_file_path(const Triplet& triplet) const
{
- return m_triplets_cache.get_lazy(triplet, [&]()-> auto {
- for (auto&& triplet_dir : triplets_dirs)
- {
- auto&& path = triplet_dir / (triplet.canonical_name() + ".cmake");
- if (this->get_filesystem().exists(path))
+ return m_triplets_cache.get_lazy(
+ triplet, [&]() -> auto {
+ for (auto&& triplet_dir : triplets_dirs)
{
- return path;
+ auto&& path = triplet_dir / (triplet.canonical_name() + ".cmake");
+ if (this->get_filesystem().exists(path))
+ {
+ return path;
+ }
}
- }
- Checks::exit_with_message(VCPKG_LINE_INFO,
- "Error: Triplet file %s.cmake not found",
- triplet.canonical_name());
- });
-
+ Checks::exit_with_message(
+ VCPKG_LINE_INFO, "Error: Triplet file %s.cmake not found", triplet.canonical_name());
+ });
}
const fs::path& VcpkgPaths::get_tool_exe(const std::string& tool) const