aboutsummaryrefslogtreecommitdiff
path: root/toolsrc/src
diff options
context:
space:
mode:
authorAlexander Saprykin <xelfium@gmail.com>2018-05-26 13:27:14 +0200
committerGitHub <noreply@github.com>2018-05-26 13:27:14 +0200
commit4ce5f064282c3a8d8d710aa82af7aa346b0c6dd5 (patch)
treed95c9490352eb73f078d34a33bc4bb44ac9fa48b /toolsrc/src
parentfb689bd13dd6ba563a885d71fff1dd2b32a615db (diff)
parent2ac7527b40b1dbeb7856b9f763362c1e139e2ca9 (diff)
downloadvcpkg-4ce5f064282c3a8d8d710aa82af7aa346b0c6dd5.tar.gz
vcpkg-4ce5f064282c3a8d8d710aa82af7aa346b0c6dd5.zip
Merge pull request #1 from Microsoft/master
Update vcpkg from upstream
Diffstat (limited to 'toolsrc/src')
-rw-r--r--toolsrc/src/BinaryParagraph.cpp83
-rw-r--r--toolsrc/src/PackageSpec.cpp60
-rw-r--r--toolsrc/src/PackageSpecParseResult.cpp40
-rw-r--r--toolsrc/src/PostBuildLint_BuildPolicies.cpp71
-rw-r--r--toolsrc/src/PostBuildLint_ConfigurationType.cpp24
-rw-r--r--toolsrc/src/PostBuildLint_LinkageType.cpp39
-rw-r--r--toolsrc/src/SourceParagraph.cpp140
-rw-r--r--toolsrc/src/StatusParagraph.cpp82
-rw-r--r--toolsrc/src/StatusParagraphs.cpp66
-rw-r--r--toolsrc/src/VcpkgCmdArguments.cpp226
-rw-r--r--toolsrc/src/VcpkgPaths.cpp343
-rw-r--r--toolsrc/src/VersionT.cpp19
-rw-r--r--toolsrc/src/commands_build.cpp90
-rw-r--r--toolsrc/src/commands_build_external.cpp22
-rw-r--r--toolsrc/src/commands_ci.cpp141
-rw-r--r--toolsrc/src/commands_contact.cpp22
-rw-r--r--toolsrc/src/commands_depends.cpp27
-rw-r--r--toolsrc/src/commands_edit.cpp82
-rw-r--r--toolsrc/src/commands_env.cpp20
-rw-r--r--toolsrc/src/commands_export.cpp392
-rw-r--r--toolsrc/src/commands_hash.cpp55
-rw-r--r--toolsrc/src/commands_help.cpp92
-rw-r--r--toolsrc/src/commands_install.cpp398
-rw-r--r--toolsrc/src/commands_list.cpp65
-rw-r--r--toolsrc/src/commands_remove.cpp229
-rw-r--r--toolsrc/src/commands_search.cpp101
-rw-r--r--toolsrc/src/commands_update.cpp97
-rw-r--r--toolsrc/src/commands_version.cpp38
-rw-r--r--toolsrc/src/metrics.cpp393
-rw-r--r--toolsrc/src/tests.arguments.cpp64
-rw-r--r--toolsrc/src/tests.chrono.cpp41
-rw-r--r--toolsrc/src/tests.dependencies.cpp108
-rw-r--r--toolsrc/src/tests.packagespec.cpp134
-rw-r--r--toolsrc/src/tests.paragraph.cpp (renamed from toolsrc/src/tests_paragraph.cpp)269
-rw-r--r--toolsrc/src/tests.pch.cpp1
-rw-r--r--toolsrc/src/tests.plan.cpp1258
-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/tests_arguments.cpp35
-rw-r--r--toolsrc/src/tests_dependencies.cpp38
-rw-r--r--toolsrc/src/triplet.cpp36
-rw-r--r--toolsrc/src/vcpkg.cpp282
-rw-r--r--toolsrc/src/vcpkg/base/checks.cpp96
-rw-r--r--toolsrc/src/vcpkg/base/chrono.cpp129
-rw-r--r--toolsrc/src/vcpkg/base/cofffilereader.cpp (renamed from toolsrc/src/coff_file_reader.cpp)122
-rw-r--r--toolsrc/src/vcpkg/base/enums.cpp (renamed from toolsrc/src/vcpkg_Enums.cpp)4
-rw-r--r--toolsrc/src/vcpkg/base/files.cpp (renamed from toolsrc/src/vcpkg_Files.cpp)83
-rw-r--r--toolsrc/src/vcpkg/base/lineinfo.cpp (renamed from toolsrc/src/LineInfo.cpp)4
-rw-r--r--toolsrc/src/vcpkg/base/machinetype.cpp (renamed from toolsrc/src/MachineType.cpp)8
-rw-r--r--toolsrc/src/vcpkg/base/strings.cpp192
-rw-r--r--toolsrc/src/vcpkg/base/system.cpp504
-rw-r--r--toolsrc/src/vcpkg/binaryparagraph.cpp123
-rw-r--r--toolsrc/src/vcpkg/build.cpp892
-rw-r--r--toolsrc/src/vcpkg/commands.autocomplete.cpp170
-rw-r--r--toolsrc/src/vcpkg/commands.buildexternal.cpp29
-rw-r--r--toolsrc/src/vcpkg/commands.cache.cpp (renamed from toolsrc/src/commands_cache.cpp)33
-rw-r--r--toolsrc/src/vcpkg/commands.ci.cpp264
-rw-r--r--toolsrc/src/vcpkg/commands.contact.cpp60
-rw-r--r--toolsrc/src/vcpkg/commands.cpp (renamed from toolsrc/src/commands_available_commands.cpp)21
-rw-r--r--toolsrc/src/vcpkg/commands.create.cpp (renamed from toolsrc/src/commands_create.cpp)32
-rw-r--r--toolsrc/src/vcpkg/commands.dependinfo.cpp61
-rw-r--r--toolsrc/src/vcpkg/commands.edit.cpp152
-rw-r--r--toolsrc/src/vcpkg/commands.env.cpp74
-rw-r--r--toolsrc/src/vcpkg/commands.exportifw.cpp487
-rw-r--r--toolsrc/src/vcpkg/commands.fetch.cpp965
-rw-r--r--toolsrc/src/vcpkg/commands.hash.cpp244
-rw-r--r--toolsrc/src/vcpkg/commands.import.cpp (renamed from toolsrc/src/commands_import.cpp)37
-rw-r--r--toolsrc/src/vcpkg/commands.integrate.cpp (renamed from toolsrc/src/commands_integrate.cpp)307
-rw-r--r--toolsrc/src/vcpkg/commands.list.cpp90
-rw-r--r--toolsrc/src/vcpkg/commands.owns.cpp (renamed from toolsrc/src/commands_owns.cpp)22
-rw-r--r--toolsrc/src/vcpkg/commands.portsdiff.cpp (renamed from toolsrc/src/commands_portsdiff.cpp)101
-rw-r--r--toolsrc/src/vcpkg/commands.search.cpp156
-rw-r--r--toolsrc/src/vcpkg/commands.upgrade.cpp180
-rw-r--r--toolsrc/src/vcpkg/commands.version.cpp94
-rw-r--r--toolsrc/src/vcpkg/dependencies.cpp885
-rw-r--r--toolsrc/src/vcpkg/export.cpp543
-rw-r--r--toolsrc/src/vcpkg/globalstate.cpp16
-rw-r--r--toolsrc/src/vcpkg/help.cpp151
-rw-r--r--toolsrc/src/vcpkg/input.cpp55
-rw-r--r--toolsrc/src/vcpkg/install.cpp741
-rw-r--r--toolsrc/src/vcpkg/metrics.cpp449
-rw-r--r--toolsrc/src/vcpkg/packagespec.cpp195
-rw-r--r--toolsrc/src/vcpkg/packagespecparseresult.cpp21
-rw-r--r--toolsrc/src/vcpkg/paragraphparseresult.cpp (renamed from toolsrc/src/ParagraphParseResult.cpp)4
-rw-r--r--toolsrc/src/vcpkg/paragraphs.cpp (renamed from toolsrc/src/Paragraphs.cpp)106
-rw-r--r--toolsrc/src/vcpkg/parse.cpp80
-rw-r--r--toolsrc/src/vcpkg/postbuildlint.buildtype.cpp (renamed from toolsrc/src/PostBuildLint_BuildType.cpp)18
-rw-r--r--toolsrc/src/vcpkg/postbuildlint.cpp (renamed from toolsrc/src/PostBuildLint.cpp)252
-rw-r--r--toolsrc/src/vcpkg/remove.cpp306
-rw-r--r--toolsrc/src/vcpkg/sourceparagraph.cpp282
-rw-r--r--toolsrc/src/vcpkg/statusparagraph.cpp128
-rw-r--r--toolsrc/src/vcpkg/statusparagraphs.cpp145
-rw-r--r--toolsrc/src/vcpkg/triplet.cpp57
-rw-r--r--toolsrc/src/vcpkg/update.cpp86
-rw-r--r--toolsrc/src/vcpkg/userconfig.cpp102
-rw-r--r--toolsrc/src/vcpkg/vcpkgcmdarguments.cpp296
-rw-r--r--toolsrc/src/vcpkg/vcpkglib.cpp (renamed from toolsrc/src/vcpkglib.cpp)84
-rw-r--r--toolsrc/src/vcpkg/vcpkgpaths.cpp170
-rw-r--r--toolsrc/src/vcpkg/versiont.cpp23
-rw-r--r--toolsrc/src/vcpkg_Build.cpp337
-rw-r--r--toolsrc/src/vcpkg_Checks.cpp47
-rw-r--r--toolsrc/src/vcpkg_Chrono.cpp61
-rw-r--r--toolsrc/src/vcpkg_Dependencies.cpp300
-rw-r--r--toolsrc/src/vcpkg_Input.cpp37
-rw-r--r--toolsrc/src/vcpkg_Strings.cpp125
-rw-r--r--toolsrc/src/vcpkg_System.cpp290
-rw-r--r--toolsrc/src/vcpkglib_helpers.cpp56
-rw-r--r--toolsrc/src/vcpkgmetricsuploader.cpp (renamed from toolsrc/src/vcpkg_metrics_uploader.cpp)13
109 files changed, 12679 insertions, 5504 deletions
diff --git a/toolsrc/src/BinaryParagraph.cpp b/toolsrc/src/BinaryParagraph.cpp
deleted file mode 100644
index d545eee2a..000000000
--- a/toolsrc/src/BinaryParagraph.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-#include "pch.h"
-
-#include "BinaryParagraph.h"
-#include "vcpkg_Checks.h"
-#include "vcpkglib_helpers.h"
-
-using namespace vcpkg::details;
-
-namespace vcpkg
-{
- namespace BinaryParagraphRequiredField
- {
- static const std::string PACKAGE = "Package";
- static const std::string VERSION = "Version";
- static const std::string ARCHITECTURE = "Architecture";
- static const std::string MULTI_ARCH = "Multi-Arch";
- }
-
- namespace BinaryParagraphOptionalField
- {
- static const std::string DESCRIPTION = "Description";
- static const std::string MAINTAINER = "Maintainer";
- static const std::string DEPENDS = "Depends";
- }
-
- BinaryParagraph::BinaryParagraph() = default;
-
- BinaryParagraph::BinaryParagraph(std::unordered_map<std::string, std::string> fields)
- {
- const std::string name = details::remove_required_field(&fields, BinaryParagraphRequiredField::PACKAGE);
- const std::string architecture =
- details::remove_required_field(&fields, BinaryParagraphRequiredField::ARCHITECTURE);
- const Triplet triplet = Triplet::from_canonical_name(architecture);
-
- this->spec = PackageSpec::from_name_and_triplet(name, triplet).value_or_exit(VCPKG_LINE_INFO);
- this->version = details::remove_required_field(&fields, BinaryParagraphRequiredField::VERSION);
-
- this->description = details::remove_optional_field(&fields, BinaryParagraphOptionalField::DESCRIPTION);
- this->maintainer = details::remove_optional_field(&fields, BinaryParagraphOptionalField::MAINTAINER);
-
- std::string multi_arch = details::remove_required_field(&fields, BinaryParagraphRequiredField::MULTI_ARCH);
- Checks::check_exit(VCPKG_LINE_INFO, multi_arch == "same", "Multi-Arch must be 'same' but was %s", multi_arch);
-
- std::string deps = details::remove_optional_field(&fields, BinaryParagraphOptionalField::DEPENDS);
- this->depends = parse_depends(deps);
- }
-
- BinaryParagraph::BinaryParagraph(const SourceParagraph& spgh, const Triplet& triplet)
- {
- this->spec = PackageSpec::from_name_and_triplet(spgh.name, triplet).value_or_exit(VCPKG_LINE_INFO);
- this->version = spgh.version;
- this->description = spgh.description;
- this->maintainer = spgh.maintainer;
- this->depends = filter_dependencies(spgh.depends, triplet);
- }
-
- std::string BinaryParagraph::displayname() const { return this->spec.to_string(); }
-
- std::string BinaryParagraph::dir() const { return this->spec.dir(); }
-
- std::string BinaryParagraph::fullstem() const
- {
- return Strings::format("%s_%s_%s", this->spec.name(), this->version, this->spec.triplet());
- }
-
- void serialize(const BinaryParagraph& pgh, std::string& out_str)
- {
- out_str.append("Package: ").append(pgh.spec.name()).push_back('\n');
- out_str.append("Version: ").append(pgh.version).push_back('\n');
- if (!pgh.depends.empty())
- {
- out_str.append("Depends: ");
- out_str.append(Strings::join(", ", pgh.depends));
- out_str.push_back('\n');
- }
-
- out_str.append("Architecture: ").append(pgh.spec.triplet().to_string()).push_back('\n');
- out_str.append("Multi-Arch: same\n");
-
- if (!pgh.maintainer.empty()) out_str.append("Maintainer: ").append(pgh.maintainer).push_back('\n');
- if (!pgh.description.empty()) out_str.append("Description: ").append(pgh.description).push_back('\n');
- }
-}
diff --git a/toolsrc/src/PackageSpec.cpp b/toolsrc/src/PackageSpec.cpp
deleted file mode 100644
index 69883a030..000000000
--- a/toolsrc/src/PackageSpec.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-#include "pch.h"
-
-#include "PackageSpec.h"
-#include "vcpkg_Util.h"
-
-namespace vcpkg
-{
- static bool is_valid_package_spec_char(char c) { return (c == '-') || isdigit(c) || (isalpha(c) && islower(c)); }
-
- Expected<PackageSpec> PackageSpec::from_string(const std::string& spec_as_string, const Triplet& default_triplet)
- {
- auto pos = spec_as_string.find(':');
- if (pos == std::string::npos)
- {
- return from_name_and_triplet(spec_as_string, default_triplet);
- }
-
- auto pos2 = spec_as_string.find(':', pos + 1);
- if (pos2 != std::string::npos)
- {
- return std::error_code(PackageSpecParseResult::TOO_MANY_COLONS);
- }
-
- const std::string name = spec_as_string.substr(0, pos);
- const Triplet triplet = Triplet::from_canonical_name(spec_as_string.substr(pos + 1));
- return from_name_and_triplet(name, triplet);
- }
-
- Expected<PackageSpec> PackageSpec::from_name_and_triplet(const std::string& name, const Triplet& triplet)
- {
- if (Util::find_if_not(name, is_valid_package_spec_char) != name.end())
- {
- return std::error_code(PackageSpecParseResult::INVALID_CHARACTERS);
- }
-
- PackageSpec p;
- p.m_name = name;
- p.m_triplet = triplet;
- return p;
- }
-
- const std::string& PackageSpec::name() const { return this->m_name; }
-
- const Triplet& PackageSpec::triplet() const { return this->m_triplet; }
-
- std::string PackageSpec::dir() const { return Strings::format("%s_%s", this->m_name, this->m_triplet); }
-
- std::string PackageSpec::to_string(const std::string& name, const Triplet& triplet)
- {
- return Strings::format("%s:%s", name, triplet);
- }
- std::string PackageSpec::to_string() const { return to_string(this->name(), this->triplet()); }
-
- bool operator==(const PackageSpec& left, const PackageSpec& right)
- {
- return left.name() == right.name() && left.triplet() == right.triplet();
- }
-
- bool operator!=(const PackageSpec& left, const PackageSpec& right) { return !(left == right); }
-}
diff --git a/toolsrc/src/PackageSpecParseResult.cpp b/toolsrc/src/PackageSpecParseResult.cpp
deleted file mode 100644
index 487c3aa1d..000000000
--- a/toolsrc/src/PackageSpecParseResult.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-#include "pch.h"
-
-#include "PackageSpecParseResult.h"
-#include "vcpkg_Checks.h"
-
-namespace vcpkg
-{
- const char* PackageSpecParseResultCategoryImpl::name() const noexcept { return "PackageSpecParseResult"; }
-
- std::string PackageSpecParseResultCategoryImpl::message(int ev) const noexcept
- {
- switch (static_cast<PackageSpecParseResult>(ev))
- {
- case PackageSpecParseResult::SUCCESS: return "OK";
- case PackageSpecParseResult::TOO_MANY_COLONS: return "Too many colons";
- case PackageSpecParseResult::INVALID_CHARACTERS:
- return "Contains invalid characters. Only alphanumeric lowercase ASCII characters and dashes are "
- "allowed";
- default: Checks::unreachable(VCPKG_LINE_INFO);
- }
- }
-
- const std::error_category& package_spec_parse_result_category()
- {
- static PackageSpecParseResultCategoryImpl instance;
- return instance;
- }
-
- std::error_code make_error_code(PackageSpecParseResult e)
- {
- return std::error_code(static_cast<int>(e), package_spec_parse_result_category());
- }
-
- PackageSpecParseResult to_package_spec_parse_result(int i) { return static_cast<PackageSpecParseResult>(i); }
-
- PackageSpecParseResult to_package_spec_parse_result(std::error_code ec)
- {
- return to_package_spec_parse_result(ec.value());
- }
-}
diff --git a/toolsrc/src/PostBuildLint_BuildPolicies.cpp b/toolsrc/src/PostBuildLint_BuildPolicies.cpp
deleted file mode 100644
index 001ba31e7..000000000
--- a/toolsrc/src/PostBuildLint_BuildPolicies.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-#include "pch.h"
-
-#include "PostBuildLint_BuildPolicies.h"
-#include "vcpkg_Checks.h"
-#include "vcpkg_Enums.h"
-
-namespace vcpkg::PostBuildLint
-{
- static const std::string NULLVALUE_STRING = Enums::nullvalue_to_string(BuildPoliciesC::ENUM_NAME);
-
- static const std::string NAME_EMPTY_PACKAGE = "PolicyEmptyPackage";
- static const std::string NAME_DLLS_WITHOUT_LIBS = "PolicyDLLsWithoutLIBs";
- static const std::string NAME_ONLY_RELEASE_CRT = "PolicyOnlyReleaseCRT";
- static const std::string NAME_EMPTY_INCLUDE_FOLDER = "PolicyEmptyIncludeFolder";
-
- BuildPolicies BuildPolicies::parse(const std::string& s)
- {
- if (s == NAME_EMPTY_PACKAGE)
- {
- return BuildPoliciesC::EMPTY_PACKAGE;
- }
-
- if (s == NAME_DLLS_WITHOUT_LIBS)
- {
- return BuildPoliciesC::DLLS_WITHOUT_LIBS;
- }
-
- if (s == NAME_ONLY_RELEASE_CRT)
- {
- return BuildPoliciesC::ONLY_RELEASE_CRT;
- }
-
- if (s == NAME_EMPTY_INCLUDE_FOLDER)
- {
- return BuildPoliciesC::EMPTY_INCLUDE_FOLDER;
- }
-
- return BuildPoliciesC::NULLVALUE;
- }
-
- const std::string& BuildPolicies::to_string() const
- {
- switch (this->backing_enum)
- {
- case BuildPoliciesC::EMPTY_PACKAGE: return NAME_EMPTY_PACKAGE;
- case BuildPoliciesC::DLLS_WITHOUT_LIBS: return NAME_DLLS_WITHOUT_LIBS;
- case BuildPoliciesC::ONLY_RELEASE_CRT: return NAME_ONLY_RELEASE_CRT;
- case BuildPoliciesC::EMPTY_INCLUDE_FOLDER: return NAME_EMPTY_INCLUDE_FOLDER;
- case BuildPoliciesC::NULLVALUE: return NULLVALUE_STRING;
- default: Checks::unreachable(VCPKG_LINE_INFO);
- }
- }
-
- const std::string& BuildPolicies::cmake_variable() const
- {
- static const std::string CMAKE_VARIABLE_EMPTY_PACKAGE = "VCPKG_POLICY_EMPTY_PACKAGE";
- static const std::string CMAKE_VARIABLE_DLLS_WITHOUT_LIBS = "VCPKG_POLICY_DLLS_WITHOUT_LIBS";
- static const std::string CMAKE_VARIABLE_ONLY_RELEASE_CRT = "VCPKG_POLICY_ONLY_RELEASE_CRT";
- static const std::string CMAKE_VARIABLE_EMPTY_INCLUDE_FOLDER = "VCPKG_POLICY_EMPTY_INCLUDE_FOLDER";
-
- switch (this->backing_enum)
- {
- case BuildPoliciesC::EMPTY_PACKAGE: return CMAKE_VARIABLE_EMPTY_PACKAGE;
- case BuildPoliciesC::DLLS_WITHOUT_LIBS: return CMAKE_VARIABLE_DLLS_WITHOUT_LIBS;
- case BuildPoliciesC::ONLY_RELEASE_CRT: return CMAKE_VARIABLE_ONLY_RELEASE_CRT;
- case BuildPoliciesC::EMPTY_INCLUDE_FOLDER: return CMAKE_VARIABLE_EMPTY_INCLUDE_FOLDER;
- case BuildPoliciesC::NULLVALUE: Enums::nullvalue_used(VCPKG_LINE_INFO, BuildPoliciesC::ENUM_NAME);
- default: Checks::unreachable(VCPKG_LINE_INFO);
- }
- }
-}
diff --git a/toolsrc/src/PostBuildLint_ConfigurationType.cpp b/toolsrc/src/PostBuildLint_ConfigurationType.cpp
deleted file mode 100644
index eeccb1804..000000000
--- a/toolsrc/src/PostBuildLint_ConfigurationType.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#include "pch.h"
-
-#include "PackageSpec.h"
-#include "PostBuildLint_ConfigurationType.h"
-#include "vcpkg_Enums.h"
-
-namespace vcpkg::PostBuildLint
-{
- static const std::string NULLVALUE_STRING = Enums::nullvalue_to_string(ConfigurationTypeC::ENUM_NAME);
-
- static const std::string NAME_DEBUG = "Debug";
- static const std::string NAME_RELEASE = "Release";
-
- const std::string& ConfigurationType::to_string() const
- {
- switch (this->backing_enum)
- {
- case ConfigurationTypeC::DEBUG: return NAME_DEBUG;
- case ConfigurationTypeC::RELEASE: return NAME_RELEASE;
- case ConfigurationTypeC::NULLVALUE: return NULLVALUE_STRING;
- default: Checks::unreachable(VCPKG_LINE_INFO);
- }
- }
-}
diff --git a/toolsrc/src/PostBuildLint_LinkageType.cpp b/toolsrc/src/PostBuildLint_LinkageType.cpp
deleted file mode 100644
index 43bdbed7b..000000000
--- a/toolsrc/src/PostBuildLint_LinkageType.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-#include "pch.h"
-
-#include "PostBuildLint_LinkageType.h"
-#include "vcpkg_Checks.h"
-#include "vcpkg_Enums.h"
-
-namespace vcpkg::PostBuildLint
-{
- static const std::string NULLVALUE_STRING = Enums::nullvalue_to_string(LinkageTypeC::ENUM_NAME);
-
- static const std::string NAME_DYNAMIC = "dynamic";
- static const std::string NAME_STATIC = "static";
-
- LinkageType LinkageType::value_of(const std::string& as_string)
- {
- if (as_string == NAME_DYNAMIC)
- {
- return LinkageTypeC::DYNAMIC;
- }
-
- if (as_string == NAME_STATIC)
- {
- return LinkageTypeC::STATIC;
- }
-
- return LinkageTypeC::NULLVALUE;
- }
-
- const std::string& LinkageType::to_string() const
- {
- switch (this->backing_enum)
- {
- case LinkageTypeC::DYNAMIC: return NAME_DYNAMIC;
- case LinkageTypeC::STATIC: return NAME_STATIC;
- case LinkageTypeC::NULLVALUE: return NULLVALUE_STRING;
- default: Checks::unreachable(VCPKG_LINE_INFO);
- }
- }
-}
diff --git a/toolsrc/src/SourceParagraph.cpp b/toolsrc/src/SourceParagraph.cpp
deleted file mode 100644
index d140ce72b..000000000
--- a/toolsrc/src/SourceParagraph.cpp
+++ /dev/null
@@ -1,140 +0,0 @@
-#include "pch.h"
-
-#include "SourceParagraph.h"
-#include "Triplet.h"
-#include "vcpkg_Checks.h"
-#include "vcpkg_Maps.h"
-#include "vcpkg_System.h"
-#include "vcpkglib_helpers.h"
-
-namespace vcpkg
-{
- //
- namespace SourceParagraphRequiredField
- {
- static const std::string SOURCE = "Source";
- static const std::string VERSION = "Version";
- }
-
- namespace SourceParagraphOptionalField
- {
- static const std::string DESCRIPTION = "Description";
- static const std::string MAINTAINER = "Maintainer";
- static const std::string BUILD_DEPENDS = "Build-Depends";
- }
-
- static const std::vector<std::string>& get_list_of_valid_fields()
- {
- static const std::vector<std::string> valid_fields = {SourceParagraphRequiredField::SOURCE,
- SourceParagraphRequiredField::VERSION,
-
- SourceParagraphOptionalField::DESCRIPTION,
- SourceParagraphOptionalField::MAINTAINER,
- SourceParagraphOptionalField::BUILD_DEPENDS};
-
- return valid_fields;
- }
-
- SourceParagraph::SourceParagraph() = default;
-
- SourceParagraph::SourceParagraph(std::unordered_map<std::string, std::string> fields)
- {
- this->name = details::remove_required_field(&fields, SourceParagraphRequiredField::SOURCE);
- this->version = details::remove_required_field(&fields, SourceParagraphRequiredField::VERSION);
- this->description = details::remove_optional_field(&fields, SourceParagraphOptionalField::DESCRIPTION);
- this->maintainer = details::remove_optional_field(&fields, SourceParagraphOptionalField::MAINTAINER);
-
- std::string deps = details::remove_optional_field(&fields, SourceParagraphOptionalField::BUILD_DEPENDS);
- this->depends = expand_qualified_dependencies(parse_depends(deps));
-
- if (!fields.empty())
- {
- const std::vector<std::string> remaining_fields = Maps::extract_keys(fields);
- const std::vector<std::string>& valid_fields = get_list_of_valid_fields();
-
- const std::string remaining_fields_as_string = Strings::join("\n ", remaining_fields);
- const std::string valid_fields_as_string = Strings::join("\n ", valid_fields);
-
- System::println(
- System::Color::error, "Error: There are invalid fields in the Source Paragraph of %s", this->name);
- System::println("The following fields were not expected:\n\n %s\n\n", remaining_fields_as_string);
- System::println("This is the list of valid fields (case-sensitive): \n\n %s\n", valid_fields_as_string);
- Checks::exit_fail(VCPKG_LINE_INFO);
- }
- }
-
- std::vector<Dependency> vcpkg::expand_qualified_dependencies(const std::vector<std::string>& depends)
- {
- auto convert = [&](const std::string& depend_string) -> Dependency {
- auto pos = depend_string.find(' ');
- if (pos == std::string::npos) return {depend_string, ""};
- // expect of the form "\w+ \[\w+\]"
- Dependency dep;
- dep.name = depend_string.substr(0, pos);
- if (depend_string.c_str()[pos + 1] != '[' || depend_string[depend_string.size() - 1] != ']')
- {
- // Error, but for now just slurp the entire string.
- return {depend_string, ""};
- }
- dep.qualifier = depend_string.substr(pos + 2, depend_string.size() - pos - 3);
- return dep;
- };
-
- std::vector<vcpkg::Dependency> ret;
-
- for (auto&& depend_string : depends)
- {
- ret.push_back(convert(depend_string));
- }
-
- return ret;
- }
-
- std::vector<std::string> parse_depends(const std::string& depends_string)
- {
- if (depends_string.empty())
- {
- return {};
- }
-
- std::vector<std::string> out;
-
- size_t cur = 0;
- do
- {
- auto pos = depends_string.find(',', cur);
- if (pos == std::string::npos)
- {
- out.push_back(depends_string.substr(cur));
- break;
- }
- out.push_back(depends_string.substr(cur, pos - cur));
-
- // skip comma and space
- ++pos;
- if (depends_string[pos] == ' ')
- {
- ++pos;
- }
-
- cur = pos;
- } while (cur != std::string::npos);
-
- return out;
- }
-
- std::vector<std::string> filter_dependencies(const std::vector<vcpkg::Dependency>& deps, const Triplet& t)
- {
- std::vector<std::string> ret;
- for (auto&& dep : deps)
- {
- if (dep.qualifier.empty() || t.canonical_name().find(dep.qualifier) != std::string::npos)
- {
- ret.push_back(dep.name);
- }
- }
- return ret;
- }
-
- const std::string& to_string(const Dependency& dep) { return dep.name; }
-}
diff --git a/toolsrc/src/StatusParagraph.cpp b/toolsrc/src/StatusParagraph.cpp
deleted file mode 100644
index b9ceed278..000000000
--- a/toolsrc/src/StatusParagraph.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-#include "pch.h"
-
-#include "StatusParagraph.h"
-#include "vcpkglib_helpers.h"
-
-using namespace vcpkg::details;
-
-namespace vcpkg
-{
- namespace BinaryParagraphRequiredField
- {
- static const std::string STATUS = "Status";
- }
-
- StatusParagraph::StatusParagraph() : want(Want::ERROR_STATE), state(InstallState::ERROR_STATE) {}
-
- void serialize(const StatusParagraph& pgh, std::string& out_str)
- {
- serialize(pgh.package, out_str);
- out_str.append("Status: ")
- .append(to_string(pgh.want))
- .append(" ok ")
- .append(to_string(pgh.state))
- .push_back('\n');
- }
-
- StatusParagraph::StatusParagraph(const std::unordered_map<std::string, std::string>& fields) : package(fields)
- {
- std::string status_field = required_field(fields, BinaryParagraphRequiredField::STATUS);
-
- auto b = status_field.begin();
- auto mark = b;
- auto e = status_field.end();
-
- // Todo: improve error handling
- while (b != e && *b != ' ')
- ++b;
-
- want = [](const std::string& text) {
- if (text == "unknown") return Want::UNKNOWN;
- if (text == "install") return Want::INSTALL;
- if (text == "hold") return Want::HOLD;
- if (text == "deinstall") return Want::DEINSTALL;
- if (text == "purge") return Want::PURGE;
- return Want::ERROR_STATE;
- }(std::string(mark, b));
-
- if (std::distance(b, e) < 4) return;
- b += 4;
-
- state = [](const std::string& text) {
- if (text == "not-installed") return InstallState::NOT_INSTALLED;
- if (text == "installed") return InstallState::INSTALLED;
- if (text == "half-installed") return InstallState::HALF_INSTALLED;
- return InstallState::ERROR_STATE;
- }(std::string(b, e));
- }
-
- std::string to_string(InstallState f)
- {
- switch (f)
- {
- case InstallState::HALF_INSTALLED: return "half-installed";
- case InstallState::INSTALLED: return "installed";
- case InstallState::NOT_INSTALLED: return "not-installed";
- default: return "error";
- }
- }
-
- std::string to_string(Want f)
- {
- switch (f)
- {
- case Want::DEINSTALL: return "deinstall";
- case Want::HOLD: return "hold";
- case Want::INSTALL: return "install";
- case Want::PURGE: return "purge";
- case Want::UNKNOWN: return "unknown";
- default: return "error";
- }
- }
-}
diff --git a/toolsrc/src/StatusParagraphs.cpp b/toolsrc/src/StatusParagraphs.cpp
deleted file mode 100644
index 27f3c30a2..000000000
--- a/toolsrc/src/StatusParagraphs.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-#include "pch.h"
-
-#include "StatusParagraphs.h"
-#include "vcpkg_Checks.h"
-#include <algorithm>
-
-namespace vcpkg
-{
- StatusParagraphs::StatusParagraphs() = default;
-
- StatusParagraphs::StatusParagraphs(std::vector<std::unique_ptr<StatusParagraph>>&& ps)
- : paragraphs(std::move(ps)){};
-
- StatusParagraphs::const_iterator StatusParagraphs::find(const std::string& name, const Triplet& triplet) const
- {
- return std::find_if(begin(), end(), [&](const std::unique_ptr<StatusParagraph>& pgh) {
- const PackageSpec& spec = pgh->package.spec;
- return spec.name() == name && spec.triplet() == triplet;
- });
- }
-
- StatusParagraphs::iterator StatusParagraphs::find(const std::string& name, const Triplet& triplet)
- {
- return std::find_if(begin(), end(), [&](const std::unique_ptr<StatusParagraph>& pgh) {
- const PackageSpec& spec = pgh->package.spec;
- return spec.name() == name && spec.triplet() == triplet;
- });
- }
-
- StatusParagraphs::const_iterator StatusParagraphs::find_installed(const std::string& name,
- const Triplet& triplet) const
- {
- const const_iterator it = find(name, triplet);
- if (it != end() && (*it)->want == Want::INSTALL && (*it)->state == InstallState::INSTALLED)
- {
- return it;
- }
-
- return end();
- }
-
- StatusParagraphs::iterator StatusParagraphs::insert(std::unique_ptr<StatusParagraph> pgh)
- {
- Checks::check_exit(VCPKG_LINE_INFO, pgh != nullptr, "Inserted null paragraph");
- const PackageSpec& spec = pgh->package.spec;
- auto ptr = find(spec.name(), spec.triplet());
- if (ptr == end())
- {
- paragraphs.push_back(std::move(pgh));
- return paragraphs.rbegin();
- }
-
- // consume data from provided pgh.
- **ptr = std::move(*pgh);
- return ptr;
- }
-
- void serialize(const StatusParagraphs& pghs, std::string& out_str)
- {
- for (auto& pgh : pghs.paragraphs)
- {
- serialize(*pgh, out_str);
- out_str.push_back('\n');
- }
- }
-}
diff --git a/toolsrc/src/VcpkgCmdArguments.cpp b/toolsrc/src/VcpkgCmdArguments.cpp
deleted file mode 100644
index 733308cb0..000000000
--- a/toolsrc/src/VcpkgCmdArguments.cpp
+++ /dev/null
@@ -1,226 +0,0 @@
-#include "pch.h"
-
-#include "VcpkgCmdArguments.h"
-#include "metrics.h"
-#include "vcpkg_Commands.h"
-#include "vcpkg_System.h"
-
-namespace vcpkg
-{
- static void parse_value(const std::string* arg_begin,
- const std::string* arg_end,
- const std::string& option_name,
- std::unique_ptr<std::string>& option_field)
- {
- if (arg_begin == arg_end)
- {
- System::println(System::Color::error, "Error: expected value after %s", option_name);
- Metrics::track_property("error", "error option name");
- Commands::Help::print_usage();
- Checks::exit_fail(VCPKG_LINE_INFO);
- }
-
- if (option_field != nullptr)
- {
- System::println(System::Color::error, "Error: %s specified multiple times", option_name);
- Metrics::track_property("error", "error option specified multiple times");
- Commands::Help::print_usage();
- Checks::exit_fail(VCPKG_LINE_INFO);
- }
-
- option_field = std::make_unique<std::string>(*arg_begin);
- }
-
- static void parse_switch(bool new_setting, const std::string& option_name, Optional<bool>& option_field)
- {
- if (option_field && option_field != new_setting)
- {
- System::println(System::Color::error, "Error: conflicting values specified for --%s", option_name);
- Metrics::track_property("error", "error conflicting switches");
- Commands::Help::print_usage();
- Checks::exit_fail(VCPKG_LINE_INFO);
- }
- option_field = new_setting;
- }
-
- VcpkgCmdArguments VcpkgCmdArguments::create_from_command_line(const int argc, const wchar_t* const* const argv)
- {
- std::vector<std::string> v;
- for (int i = 1; i < argc; ++i)
- {
- v.push_back(Strings::to_utf8(argv[i]));
- }
-
- return VcpkgCmdArguments::create_from_arg_sequence(v.data(), v.data() + v.size());
- }
-
- VcpkgCmdArguments VcpkgCmdArguments::create_from_arg_sequence(const std::string* arg_begin,
- const std::string* arg_end)
- {
- VcpkgCmdArguments args;
-
- for (; arg_begin != arg_end; ++arg_begin)
- {
- std::string arg = *arg_begin;
-
- if (arg.empty())
- {
- continue;
- }
-
- if (arg[0] == '-' && arg[1] != '-')
- {
- Metrics::track_property("error", "error short options are not supported");
- Checks::exit_with_message(VCPKG_LINE_INFO, "Error: short options are not supported: %s", arg);
- }
-
- if (arg[0] == '-' && arg[1] == '-')
- {
- // make argument case insensitive
- auto& f = std::use_facet<std::ctype<char>>(std::locale());
- f.tolower(&arg[0], &arg[0] + arg.size());
- // command switch
- if (arg == "--vcpkg-root")
- {
- ++arg_begin;
- parse_value(arg_begin, arg_end, "--vcpkg-root", args.vcpkg_root_dir);
- continue;
- }
- if (arg == "--triplet")
- {
- ++arg_begin;
- parse_value(arg_begin, arg_end, "--triplet", args.triplet);
- continue;
- }
- if (arg == "--debug")
- {
- parse_switch(true, "debug", args.debug);
- continue;
- }
- if (arg == "--sendmetrics")
- {
- parse_switch(true, "sendmetrics", args.sendmetrics);
- continue;
- }
- if (arg == "--printmetrics")
- {
- parse_switch(true, "printmetrics", args.printmetrics);
- continue;
- }
- if (arg == "--no-sendmetrics")
- {
- parse_switch(false, "sendmetrics", args.sendmetrics);
- continue;
- }
- if (arg == "--no-printmetrics")
- {
- parse_switch(false, "printmetrics", args.printmetrics);
- continue;
- }
-
- args.optional_command_arguments.insert(arg);
- continue;
- }
-
- if (args.command.empty())
- {
- args.command = arg;
- }
- else
- {
- args.command_arguments.push_back(arg);
- }
- }
-
- return args;
- }
-
- std::unordered_set<std::string> VcpkgCmdArguments::check_and_get_optional_command_arguments(
- const std::vector<std::string>& valid_options) const
- {
- std::unordered_set<std::string> output;
- auto options_copy = this->optional_command_arguments;
- for (const std::string& option : valid_options)
- {
- auto it = options_copy.find(option);
- if (it != options_copy.end())
- {
- output.insert(option);
- options_copy.erase(it);
- }
- }
-
- if (!options_copy.empty())
- {
- System::println(System::Color::error, "Unknown option(s) for command '%s':", this->command);
- for (const std::string& option : options_copy)
- {
- System::println(option);
- }
- Checks::exit_fail(VCPKG_LINE_INFO);
- }
-
- return output;
- }
-
- void VcpkgCmdArguments::check_max_arg_count(const size_t expected_arg_count) const
- {
- return check_max_arg_count(expected_arg_count, "");
- }
-
- void VcpkgCmdArguments::check_min_arg_count(const size_t expected_arg_count) const
- {
- return check_min_arg_count(expected_arg_count, "");
- }
-
- void VcpkgCmdArguments::check_exact_arg_count(const size_t expected_arg_count) const
- {
- return check_exact_arg_count(expected_arg_count, "");
- }
-
- void VcpkgCmdArguments::check_max_arg_count(const size_t expected_arg_count, const std::string& example_text) const
- {
- const size_t actual_arg_count = command_arguments.size();
- if (actual_arg_count > expected_arg_count)
- {
- System::println(System::Color::error,
- "Error: `%s` requires at most %u arguments, but %u were provided",
- this->command,
- expected_arg_count,
- actual_arg_count);
- System::print(example_text);
- Checks::exit_fail(VCPKG_LINE_INFO);
- }
- }
-
- void VcpkgCmdArguments::check_min_arg_count(const size_t expected_arg_count, const std::string& example_text) const
- {
- const size_t actual_arg_count = command_arguments.size();
- if (actual_arg_count < expected_arg_count)
- {
- System::println(System::Color::error,
- "Error: `%s` requires at least %u arguments, but %u were provided",
- this->command,
- expected_arg_count,
- actual_arg_count);
- System::print(example_text);
- Checks::exit_fail(VCPKG_LINE_INFO);
- }
- }
-
- void VcpkgCmdArguments::check_exact_arg_count(const size_t expected_arg_count,
- const std::string& example_text) const
- {
- const size_t actual_arg_count = command_arguments.size();
- if (actual_arg_count != expected_arg_count)
- {
- System::println(System::Color::error,
- "Error: `%s` requires %u arguments, but %u were provided",
- this->command,
- expected_arg_count,
- actual_arg_count);
- System::print(example_text);
- Checks::exit_fail(VCPKG_LINE_INFO);
- }
- }
-}
diff --git a/toolsrc/src/VcpkgPaths.cpp b/toolsrc/src/VcpkgPaths.cpp
deleted file mode 100644
index 3dd32de01..000000000
--- a/toolsrc/src/VcpkgPaths.cpp
+++ /dev/null
@@ -1,343 +0,0 @@
-#include "pch.h"
-
-#include "PackageSpec.h"
-#include "VcpkgPaths.h"
-#include "metrics.h"
-#include "vcpkg_Files.h"
-#include "vcpkg_System.h"
-#include "vcpkg_Util.h"
-#include "vcpkg_expected.h"
-
-namespace vcpkg
-{
- static bool exists_and_has_equal_or_greater_version(const std::wstring& version_cmd,
- const std::array<int, 3>& expected_version)
- {
- static const std::regex re(R"###((\d+)\.(\d+)\.(\d+))###");
-
- auto rc = System::cmd_execute_and_capture_output(Strings::wformat(LR"(%s)", version_cmd));
- if (rc.exit_code != 0)
- {
- return false;
- }
-
- std::match_results<std::string::const_iterator> match;
- auto found = std::regex_search(rc.output, match, re);
- if (!found)
- {
- return false;
- }
-
- int d1 = atoi(match[1].str().c_str());
- int d2 = atoi(match[2].str().c_str());
- int d3 = atoi(match[3].str().c_str());
- if (d1 > expected_version[0] || (d1 == expected_version[0] && d2 > expected_version[1]) ||
- (d1 == expected_version[0] && d2 == expected_version[1] && d3 >= expected_version[2]))
- {
- // satisfactory version found
- return true;
- }
-
- return false;
- }
-
- static Optional<fs::path> find_if_has_equal_or_greater_version(const std::vector<fs::path>& candidate_paths,
- const std::wstring& version_check_arguments,
- const std::array<int, 3>& expected_version)
- {
- auto it = Util::find_if(candidate_paths, [&](const fs::path& p) {
- const std::wstring cmd = Strings::wformat(LR"("%s" %s)", p.native(), version_check_arguments);
- return exists_and_has_equal_or_greater_version(cmd, expected_version);
- });
-
- if (it != candidate_paths.cend())
- {
- return std::move(*it);
- }
-
- return nullopt;
- }
-
- static std::vector<fs::path> find_from_PATH(const std::wstring& name)
- {
- const std::wstring cmd = Strings::wformat(L"where.exe %s", name);
- auto out = System::cmd_execute_and_capture_output(cmd);
- if (out.exit_code != 0)
- {
- return {};
- }
-
- return Util::fmap(Strings::split(out.output, "\n"), [](auto&& s) { return fs::path(s); });
- }
-
- static fs::path fetch_dependency(const fs::path scripts_folder,
- const std::wstring& tool_name,
- const fs::path& expected_downloaded_path)
- {
- const fs::path script = scripts_folder / "fetchDependency.ps1";
- auto install_cmd = System::create_powershell_script_cmd(script, Strings::wformat(L"-Dependency %s", tool_name));
- System::ExitCodeAndOutput rc = System::cmd_execute_and_capture_output(install_cmd);
- if (rc.exit_code)
- {
- System::println(System::Color::error, "Launching powershell failed or was denied");
- Metrics::track_property("error", "powershell install failed");
- Metrics::track_property("installcmd", install_cmd);
- Checks::exit_with_code(VCPKG_LINE_INFO, rc.exit_code);
- }
-
- const fs::path actual_downloaded_path = Strings::trimmed(rc.output);
- std::error_code ec;
- auto eq = fs::stdfs::equivalent(expected_downloaded_path, actual_downloaded_path, ec);
- Checks::check_exit(VCPKG_LINE_INFO,
- eq && !ec,
- "Expected dependency downloaded path to be %s, but was %s",
- expected_downloaded_path.u8string(),
- actual_downloaded_path.u8string());
- return actual_downloaded_path;
- }
-
- static fs::path get_cmake_path(const fs::path& downloads_folder, const fs::path scripts_folder)
- {
- static constexpr std::array<int, 3> expected_version = {3, 8, 0};
- static const std::wstring version_check_arguments = L"--version";
-
- const fs::path downloaded_copy = downloads_folder / "cmake-3.8.0-win32-x86" / "bin" / "cmake.exe";
- const std::vector<fs::path> from_path = find_from_PATH(L"cmake");
-
- std::vector<fs::path> candidate_paths;
- candidate_paths.push_back(downloaded_copy);
- candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend());
- candidate_paths.push_back(System::get_ProgramFiles_platform_bitness() / "CMake" / "bin" / "cmake.exe");
- candidate_paths.push_back(System::get_ProgramFiles_32_bit() / "CMake" / "bin");
-
- const Optional<fs::path> path =
- find_if_has_equal_or_greater_version(candidate_paths, version_check_arguments, expected_version);
- if (auto p = path.get())
- {
- return *p;
- }
-
- return fetch_dependency(scripts_folder, L"cmake", downloaded_copy);
- }
-
- fs::path get_nuget_path(const fs::path& downloads_folder, const fs::path scripts_folder)
- {
- static constexpr std::array<int, 3> expected_version = {3, 3, 0};
- static const std::wstring version_check_arguments = L"";
-
- const fs::path downloaded_copy = downloads_folder / "nuget-3.5.0" / "nuget.exe";
- const std::vector<fs::path> from_path = find_from_PATH(L"nuget");
-
- std::vector<fs::path> candidate_paths;
- candidate_paths.push_back(downloaded_copy);
- candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend());
-
- auto path = find_if_has_equal_or_greater_version(candidate_paths, version_check_arguments, expected_version);
- if (auto p = path.get())
- {
- return *p;
- }
-
- return fetch_dependency(scripts_folder, L"nuget", downloaded_copy);
- }
-
- fs::path get_git_path(const fs::path& downloads_folder, const fs::path scripts_folder)
- {
- static constexpr std::array<int, 3> expected_version = {2, 0, 0};
- static const std::wstring version_check_arguments = L"--version";
-
- const fs::path downloaded_copy = downloads_folder / "MinGit-2.11.1-32-bit" / "cmd" / "git.exe";
- const std::vector<fs::path> from_path = find_from_PATH(L"git");
-
- std::vector<fs::path> candidate_paths;
- candidate_paths.push_back(downloaded_copy);
- candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend());
- candidate_paths.push_back(System::get_ProgramFiles_platform_bitness() / "git" / "cmd" / "git.exe");
- candidate_paths.push_back(System::get_ProgramFiles_32_bit() / "git" / "cmd" / "git.exe");
-
- const Optional<fs::path> path =
- find_if_has_equal_or_greater_version(candidate_paths, version_check_arguments, expected_version);
- if (auto p = path.get())
- {
- return *p;
- }
-
- return fetch_dependency(scripts_folder, L"git", downloaded_copy);
- }
-
- Expected<VcpkgPaths> VcpkgPaths::create(const fs::path& vcpkg_root_dir)
- {
- std::error_code ec;
- const fs::path canonical_vcpkg_root_dir = fs::stdfs::canonical(vcpkg_root_dir, ec);
- if (ec)
- {
- return ec;
- }
-
- VcpkgPaths paths;
- paths.root = canonical_vcpkg_root_dir;
-
- if (paths.root.empty())
- {
- Metrics::track_property("error", "Invalid vcpkg root directory");
- Checks::exit_with_message(VCPKG_LINE_INFO, "Invalid vcpkg root directory: %s", paths.root.string());
- }
-
- paths.packages = paths.root / "packages";
- paths.buildtrees = paths.root / "buildtrees";
- paths.downloads = paths.root / "downloads";
- paths.ports = paths.root / "ports";
- paths.installed = paths.root / "installed";
- paths.triplets = paths.root / "triplets";
- paths.scripts = paths.root / "scripts";
-
- paths.buildsystems = paths.scripts / "buildsystems";
- paths.buildsystems_msbuild_targets = paths.buildsystems / "msbuild" / "vcpkg.targets";
-
- paths.vcpkg_dir = paths.installed / "vcpkg";
- paths.vcpkg_dir_status_file = paths.vcpkg_dir / "status";
- paths.vcpkg_dir_info = paths.vcpkg_dir / "info";
- paths.vcpkg_dir_updates = paths.vcpkg_dir / "updates";
-
- paths.ports_cmake = paths.scripts / "ports.cmake";
-
- return paths;
- }
-
- fs::path VcpkgPaths::package_dir(const PackageSpec& spec) const { return this->packages / spec.dir(); }
-
- fs::path VcpkgPaths::port_dir(const PackageSpec& spec) const { return this->ports / spec.name(); }
-
- fs::path VcpkgPaths::build_info_file_path(const PackageSpec& spec) const
- {
- return this->package_dir(spec) / "BUILD_INFO";
- }
-
- fs::path VcpkgPaths::listfile_path(const BinaryParagraph& pgh) const
- {
- return this->vcpkg_dir_info / (pgh.fullstem() + ".list");
- }
-
- bool VcpkgPaths::is_valid_triplet(const Triplet& t) const
- {
- for (auto&& path : get_filesystem().get_files_non_recursive(this->triplets))
- {
- std::string triplet_file_name = path.stem().generic_u8string();
- if (t.canonical_name() == triplet_file_name) // TODO: fuzzy compare
- {
- // t.value = triplet_file_name; // NOTE: uncomment when implementing fuzzy compare
- return true;
- }
- }
- return false;
- }
-
- const fs::path& VcpkgPaths::get_cmake_exe() const
- {
- return this->cmake_exe.get_lazy([this]() { return get_cmake_path(this->downloads, this->scripts); });
- }
-
- const fs::path& VcpkgPaths::get_git_exe() const
- {
- return this->git_exe.get_lazy([this]() { return get_git_path(this->downloads, this->scripts); });
- }
-
- const fs::path& VcpkgPaths::get_nuget_exe() const
- {
- return this->nuget_exe.get_lazy([this]() { return get_nuget_path(this->downloads, this->scripts); });
- }
-
- static std::vector<std::string> get_VS2017_installation_instances(const VcpkgPaths& paths)
- {
- const fs::path script = paths.scripts / "findVisualStudioInstallationInstances.ps1";
- const std::wstring cmd = System::create_powershell_script_cmd(script);
- System::ExitCodeAndOutput ec_data = System::cmd_execute_and_capture_output(cmd);
- Checks::check_exit(VCPKG_LINE_INFO, ec_data.exit_code == 0, "Could not run script to detect VS 2017 instances");
- return Strings::split(ec_data.output, "\n");
- }
-
- static Optional<fs::path> get_VS2015_installation_instance()
- {
- const Optional<std::wstring> vs2015_cmntools_optional = System::get_environment_variable(L"VS140COMNTOOLS");
- if (auto v = vs2015_cmntools_optional.get())
- {
- const fs::path vs2015_cmntools = fs::path(*v).parent_path(); // The call to parent_path() is needed because
- // the env variable has a trailing backslash
- return vs2015_cmntools.parent_path().parent_path();
- }
-
- return nullopt;
- }
-
- static Toolset find_toolset_instance(const VcpkgPaths& paths)
- {
- const auto& fs = paths.get_filesystem();
-
- const std::vector<std::string> vs2017_installation_instances = get_VS2017_installation_instances(paths);
- // Note: this will contain a mix of vcvarsall.bat locations and dumpbin.exe locations.
- std::vector<fs::path> paths_examined;
-
- // VS2017
- for (const fs::path& instance : vs2017_installation_instances)
- {
- const fs::path vc_dir = instance / "VC";
-
- // Skip any instances that do not have vcvarsall.
- const fs::path vcvarsall_bat = vc_dir / "Auxiliary" / "Build" / "vcvarsall.bat";
- paths_examined.push_back(vcvarsall_bat);
- if (!fs.exists(vcvarsall_bat)) continue;
-
- // Locate the "best" MSVC toolchain version
- const fs::path msvc_path = vc_dir / "Tools" / "MSVC";
- std::vector<fs::path> msvc_subdirectories = fs.get_files_non_recursive(msvc_path);
- Util::unstable_keep_if(msvc_subdirectories, [&fs](const fs::path& path) { return fs.is_directory(path); });
-
- // Sort them so that latest comes first
- std::sort(msvc_subdirectories.begin(),
- msvc_subdirectories.end(),
- [](const fs::path& left, const fs::path& right) { return left.filename() > right.filename(); });
-
- for (const fs::path& subdir : msvc_subdirectories)
- {
- const fs::path dumpbin_path = subdir / "bin" / "HostX86" / "x86" / "dumpbin.exe";
- paths_examined.push_back(dumpbin_path);
- if (fs.exists(dumpbin_path))
- {
- return {dumpbin_path, vcvarsall_bat, L"v141"};
- }
- }
- }
-
- // VS2015
- const Optional<fs::path> vs_2015_installation_instance = get_VS2015_installation_instance();
- if (auto v = vs_2015_installation_instance.get())
- {
- const fs::path vs2015_vcvarsall_bat = *v / "VC" / "vcvarsall.bat";
-
- paths_examined.push_back(vs2015_vcvarsall_bat);
- if (fs.exists(vs2015_vcvarsall_bat))
- {
- const fs::path vs2015_dumpbin_exe = *v / "VC" / "bin" / "dumpbin.exe";
- paths_examined.push_back(vs2015_dumpbin_exe);
- if (fs.exists(vs2015_dumpbin_exe))
- {
- return {vs2015_dumpbin_exe, vs2015_vcvarsall_bat, L"v140"};
- }
- }
- }
-
- System::println(System::Color::error, "Could not locate a complete toolset.");
- System::println("The following paths were examined:");
- for (const fs::path& path : paths_examined)
- {
- System::println(" %s", path.u8string());
- }
- Checks::exit_fail(VCPKG_LINE_INFO);
- }
-
- const Toolset& VcpkgPaths::get_toolset() const
- {
- return this->toolset.get_lazy([this]() { return find_toolset_instance(*this); });
- }
- Files::Filesystem& VcpkgPaths::get_filesystem() const { return Files::get_real_filesystem(); }
-}
diff --git a/toolsrc/src/VersionT.cpp b/toolsrc/src/VersionT.cpp
deleted file mode 100644
index 738d2ce88..000000000
--- a/toolsrc/src/VersionT.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-#include "pch.h"
-
-#include "VersionT.h"
-#include "vcpkg_Strings.h"
-
-namespace vcpkg
-{
- VersionT::VersionT() : value("0.0.0") {}
- VersionT::VersionT(const std::string& value) : value(value) {}
- std::string VersionT::to_string() const { return value; }
- bool operator==(const VersionT& left, const VersionT& right) { return left.value == right.value; }
- bool operator!=(const VersionT& left, const VersionT& right) { return left.value != right.value; }
- std::string to_printf_arg(const VersionT& version) { return version.value; }
-
- VersionDiff::VersionDiff() : left(), right() {}
- VersionDiff::VersionDiff(const VersionT& left, const VersionT& right) : left(left), right(right) {}
-
- std::string VersionDiff::to_string() const { return Strings::format("%s -> %s", left.value, right.value); }
-}
diff --git a/toolsrc/src/commands_build.cpp b/toolsrc/src/commands_build.cpp
deleted file mode 100644
index ec6586fa3..000000000
--- a/toolsrc/src/commands_build.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-#include "pch.h"
-
-#include "Paragraphs.h"
-#include "PostBuildLint.h"
-#include "StatusParagraphs.h"
-#include "vcpkg_Commands.h"
-#include "vcpkg_Dependencies.h"
-#include "vcpkg_Enums.h"
-#include "vcpkg_Input.h"
-#include "vcpkg_System.h"
-#include "vcpkglib.h"
-
-using vcpkg::Build::BuildResult;
-
-namespace vcpkg::Commands::BuildCommand
-{
- using Dependencies::InstallPlanAction;
- using Dependencies::InstallPlanType;
-
- static const std::string OPTION_CHECKS_ONLY = "--checks-only";
-
- void perform_and_exit(const PackageSpec& spec,
- const fs::path& port_dir,
- const std::unordered_set<std::string>& options,
- const VcpkgPaths& paths)
- {
- if (options.find(OPTION_CHECKS_ONLY) != options.end())
- {
- auto pre_build_info = Build::PreBuildInfo::from_triplet_file(paths, spec.triplet());
- 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);
- Checks::check_exit(VCPKG_LINE_INFO, error_count == 0);
- Checks::exit_success(VCPKG_LINE_INFO);
- }
-
- const Expected<SourceParagraph> maybe_spgh = Paragraphs::try_load_port(paths.get_filesystem(), port_dir);
- Checks::check_exit(VCPKG_LINE_INFO,
- !maybe_spgh.error_code(),
- "Could not find package %s: %s",
- spec,
- maybe_spgh.error_code().message());
- const SourceParagraph& spgh = *maybe_spgh.get();
- Checks::check_exit(VCPKG_LINE_INFO,
- spec.name() == spgh.name,
- "The Name: field inside the CONTROL does not match the port directory: '%s' != '%s'",
- spgh.name,
- spec.name());
-
- StatusParagraphs status_db = database_load_check(paths);
- const Build::BuildPackageConfig build_config{
- spgh, spec.triplet(), paths.port_dir(spec),
- };
- const auto result = Build::build_package(paths, build_config, status_db);
- if (result.code == BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES)
- {
- System::println(System::Color::error,
- "The build command requires all dependencies to be already installed.");
- System::println("The following dependencies are missing:");
- System::println("");
- for (const auto& p : result.unmet_dependencies)
- {
- System::println(" %s", p);
- }
- System::println("");
- Checks::exit_fail(VCPKG_LINE_INFO);
- }
-
- if (result.code != BuildResult::SUCCEEDED)
- {
- System::println(System::Color::error, Build::create_error_message(result.code, spec));
- System::println(Build::create_user_troubleshooting_message(spec));
- Checks::exit_fail(VCPKG_LINE_INFO);
- }
-
- Checks::exit_success(VCPKG_LINE_INFO);
- }
-
- void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet)
- {
- static const std::string example = Commands::Help::create_example_string("build zlib:x64-windows");
- args.check_exact_arg_count(
- 1, example); // Build only takes a single package and all dependencies must already be installed
- const PackageSpec spec =
- Input::check_and_get_package_spec(args.command_arguments.at(0), default_triplet, example);
- Input::check_triplet(spec.triplet(), paths);
- const std::unordered_set<std::string> options =
- args.check_and_get_optional_command_arguments({OPTION_CHECKS_ONLY});
- perform_and_exit(spec, paths.port_dir(spec), options, paths);
- }
-}
diff --git a/toolsrc/src/commands_build_external.cpp b/toolsrc/src/commands_build_external.cpp
deleted file mode 100644
index 2712ba0cf..000000000
--- a/toolsrc/src/commands_build_external.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-#include "pch.h"
-
-#include "vcpkg_Commands.h"
-#include "vcpkg_Input.h"
-#include "vcpkg_System.h"
-
-namespace vcpkg::Commands::BuildExternal
-{
- void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet)
- {
- static const std::string example =
- Commands::Help::create_example_string(R"(build_external zlib2 C:\path\to\dir\with\controlfile\)");
- args.check_exact_arg_count(2, example);
- const PackageSpec spec =
- Input::check_and_get_package_spec(args.command_arguments.at(0), default_triplet, example);
- Input::check_triplet(spec.triplet(), paths);
- const std::unordered_set<std::string> options = args.check_and_get_optional_command_arguments({});
-
- const fs::path port_dir = args.command_arguments.at(1);
- BuildCommand::perform_and_exit(spec, port_dir, options, paths);
- }
-}
diff --git a/toolsrc/src/commands_ci.cpp b/toolsrc/src/commands_ci.cpp
deleted file mode 100644
index e9755335c..000000000
--- a/toolsrc/src/commands_ci.cpp
+++ /dev/null
@@ -1,141 +0,0 @@
-#include "pch.h"
-
-#include "Paragraphs.h"
-#include "vcpkg_Build.h"
-#include "vcpkg_Chrono.h"
-#include "vcpkg_Commands.h"
-#include "vcpkg_Dependencies.h"
-#include "vcpkg_Files.h"
-#include "vcpkg_Input.h"
-#include "vcpkg_System.h"
-#include "vcpkglib.h"
-
-namespace vcpkg::Commands::CI
-{
- using Dependencies::InstallPlanAction;
- using Dependencies::InstallPlanType;
- using Build::BuildResult;
-
- static std::vector<PackageSpec> load_all_package_specs(Files::Filesystem& fs,
- const fs::path& ports_directory,
- const Triplet& triplet)
- {
- std::vector<SourceParagraph> ports = Paragraphs::load_all_ports(fs, ports_directory);
- std::vector<PackageSpec> specs;
- for (const SourceParagraph& p : ports)
- {
- specs.push_back(PackageSpec::from_name_and_triplet(p.name, triplet).value_or_exit(VCPKG_LINE_INFO));
- }
-
- return specs;
- }
-
- void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet)
- {
- static const std::string example = Commands::Help::create_example_string("ci x64-windows");
- args.check_max_arg_count(1, example);
- const Triplet triplet = args.command_arguments.size() == 1
- ? Triplet::from_canonical_name(args.command_arguments.at(0))
- : default_triplet;
- Input::check_triplet(triplet, paths);
- args.check_and_get_optional_command_arguments({});
- const std::vector<PackageSpec> specs = load_all_package_specs(paths.get_filesystem(), paths.ports, triplet);
-
- StatusParagraphs status_db = database_load_check(paths);
- const std::vector<InstallPlanAction> install_plan = Dependencies::create_install_plan(paths, specs, status_db);
- Checks::check_exit(VCPKG_LINE_INFO, !install_plan.empty(), "Install plan cannot be empty");
-
- std::vector<BuildResult> results;
- std::vector<std::string> timing;
- const ElapsedTime timer = ElapsedTime::create_started();
- size_t counter = 0;
- const size_t package_count = install_plan.size();
- for (const InstallPlanAction& action : install_plan)
- {
- const ElapsedTime build_timer = ElapsedTime::create_started();
- counter++;
- const std::string display_name = action.spec.to_string();
- System::println("Starting package %d/%d: %s", counter, package_count, display_name);
-
- timing.push_back("0");
- results.push_back(BuildResult::NULLVALUE);
-
- try
- {
- switch (action.plan_type)
- {
- case InstallPlanType::ALREADY_INSTALLED:
- results.back() = BuildResult::SUCCEEDED;
- System::println(System::Color::success, "Package %s is already installed", display_name);
- break;
- case InstallPlanType::BUILD_AND_INSTALL:
- {
- System::println("Building package %s... ", display_name);
- auto&& source_paragraph = action.any_paragraph.source_paragraph.value_or_exit(VCPKG_LINE_INFO);
- const Build::BuildPackageConfig build_config{
- source_paragraph, action.spec.triplet(), paths.port_dir(action.spec),
- };
- const auto result_ex = Build::build_package(paths, build_config, status_db);
- const auto result = result_ex.code;
-
- timing.back() = build_timer.to_string();
- results.back() = result;
- if (result != BuildResult::SUCCEEDED)
- {
- System::println(System::Color::error, Build::create_error_message(result, action.spec));
- continue;
- }
- System::println(System::Color::success, "Building package %s... done", display_name);
-
- const BinaryParagraph bpgh =
- Paragraphs::try_load_cached_package(paths, action.spec).value_or_exit(VCPKG_LINE_INFO);
- System::println("Installing package %s... ", display_name);
- Install::install_package(paths, bpgh, &status_db);
- System::println(System::Color::success, "Installing package %s... done", display_name);
- break;
- }
- case InstallPlanType::INSTALL:
- results.back() = BuildResult::SUCCEEDED;
- System::println("Installing package %s... ", display_name);
- Install::install_package(
- paths, action.any_paragraph.binary_paragraph.value_or_exit(VCPKG_LINE_INFO), &status_db);
- System::println(System::Color::success, "Installing package %s... done", display_name);
- break;
- default: Checks::unreachable(VCPKG_LINE_INFO);
- }
- }
- catch (const std::exception& e)
- {
- System::println(System::Color::error, "Error: Could not install package %s: %s", action.spec, e.what());
- results.back() = BuildResult::NULLVALUE;
- }
- System::println("Elapsed time for package %s: %s", action.spec, build_timer.to_string());
- }
-
- System::println("Total time taken: %s", timer.to_string());
-
- for (size_t i = 0; i < results.size(); i++)
- {
- System::println("%s: %s: %s", install_plan[i].spec, Build::to_string(results[i]), timing[i]);
- }
-
- std::map<BuildResult, int> summary;
- for (const BuildResult& v : Build::BuildResult_values)
- {
- summary[v] = 0;
- }
-
- for (const BuildResult& r : results)
- {
- summary[r]++;
- }
-
- System::println("\n\nSUMMARY");
- for (const std::pair<const BuildResult, int>& entry : summary)
- {
- System::println(" %s: %d", Build::to_string(entry.first), entry.second);
- }
-
- Checks::exit_success(VCPKG_LINE_INFO);
- }
-}
diff --git a/toolsrc/src/commands_contact.cpp b/toolsrc/src/commands_contact.cpp
deleted file mode 100644
index 7f4161802..000000000
--- a/toolsrc/src/commands_contact.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-#include "pch.h"
-
-#include "vcpkg_Commands.h"
-#include "vcpkg_System.h"
-
-namespace vcpkg::Commands::Contact
-{
- const std::string& email()
- {
- static const std::string s_email = R"(vcpkg@microsoft.com)";
- return s_email;
- }
-
- void perform_and_exit(const VcpkgCmdArguments& args)
- {
- args.check_exact_arg_count(0);
- args.check_and_get_optional_command_arguments({});
-
- System::println("Send an email to %s with any feedback.", email());
- Checks::exit_success(VCPKG_LINE_INFO);
- }
-}
diff --git a/toolsrc/src/commands_depends.cpp b/toolsrc/src/commands_depends.cpp
deleted file mode 100644
index ad33bbad4..000000000
--- a/toolsrc/src/commands_depends.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-#include "pch.h"
-
-#include "Paragraphs.h"
-#include "vcpkg_Commands.h"
-#include "vcpkg_Strings.h"
-#include "vcpkg_System.h"
-
-namespace vcpkg::Commands::DependInfo
-{
- void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
- {
- static const std::string example = Commands::Help::create_example_string(R"###(depend-info)###");
- args.check_exact_arg_count(0, example);
- args.check_and_get_optional_command_arguments({});
-
- const std::vector<SourceParagraph> source_paragraphs =
- Paragraphs::load_all_ports(paths.get_filesystem(), paths.ports);
-
- for (const SourceParagraph& source_paragraph : source_paragraphs)
- {
- auto s = Strings::join(", ", source_paragraph.depends, [](const Dependency& d) { return d.name; });
- System::println("%s: %s", source_paragraph.name, s);
- }
-
- Checks::exit_success(VCPKG_LINE_INFO);
- }
-}
diff --git a/toolsrc/src/commands_edit.cpp b/toolsrc/src/commands_edit.cpp
deleted file mode 100644
index 4e83fcca8..000000000
--- a/toolsrc/src/commands_edit.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-#include "pch.h"
-
-#include "vcpkg_Commands.h"
-#include "vcpkg_Input.h"
-#include "vcpkg_System.h"
-
-namespace vcpkg::Commands::Edit
-{
- void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
- {
- auto& fs = paths.get_filesystem();
-
- static const std::string example = Commands::Help::create_example_string("edit zlib");
- args.check_exact_arg_count(1, example);
- args.check_and_get_optional_command_arguments({});
- const std::string port_name = args.command_arguments.at(0);
-
- const fs::path portpath = paths.ports / port_name;
- Checks::check_exit(VCPKG_LINE_INFO, fs.is_directory(portpath), R"(Could not find port named "%s")", port_name);
-
- // Find the user's selected editor
- std::wstring env_EDITOR;
-
- if (env_EDITOR.empty())
- {
- const Optional<std::wstring> env_EDITOR_optional = System::get_environment_variable(L"EDITOR");
- if (auto e = env_EDITOR_optional.get())
- {
- env_EDITOR = *e;
- }
- }
-
- if (env_EDITOR.empty())
- {
- const fs::path CODE_EXE_PATH = System::get_ProgramFiles_32_bit() / "Microsoft VS Code/Code.exe";
- if (fs.exists(CODE_EXE_PATH))
- {
- env_EDITOR = CODE_EXE_PATH;
- }
- }
-
- if (env_EDITOR.empty())
- {
- static const std::array<const wchar_t*, 4> regkeys = {
- LR"(SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{C26E74D1-022E-4238-8B9D-1E7564A36CC9}_is1)",
- LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{C26E74D1-022E-4238-8B9D-1E7564A36CC9}_is1)",
- LR"(SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{F8A2A208-72B3-4D61-95FC-8A65D340689B}_is1)",
- LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{F8A2A208-72B3-4D61-95FC-8A65D340689B}_is1)",
- };
- for (auto&& keypath : regkeys)
- {
- const Optional<std::wstring> code_installpath =
- System::get_registry_string(HKEY_LOCAL_MACHINE, keypath, L"InstallLocation");
- if (auto c = code_installpath.get())
- {
- auto p = fs::path(*c) / "Code.exe";
- if (fs.exists(p))
- {
- env_EDITOR = p.native();
- break;
- }
- auto p_insiders = fs::path(*c) / "Code - Insiders.exe";
- if (fs.exists(p_insiders))
- {
- env_EDITOR = p_insiders.native();
- break;
- }
- }
- }
- }
-
- if (env_EDITOR.empty())
- {
- Checks::exit_with_message(
- VCPKG_LINE_INFO, "Visual Studio Code was not found and the environment variable EDITOR is not set");
- }
-
- std::wstring cmdLine = Strings::wformat(
- LR"("%s" "%s" "%s" -n)", env_EDITOR, portpath.native(), (portpath / "portfile.cmake").native());
- Checks::exit_with_code(VCPKG_LINE_INFO, System::cmd_execute(cmdLine));
- }
-}
diff --git a/toolsrc/src/commands_env.cpp b/toolsrc/src/commands_env.cpp
deleted file mode 100644
index 5e1ecc5e7..000000000
--- a/toolsrc/src/commands_env.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-#include "pch.h"
-
-#include "vcpkg_Build.h"
-#include "vcpkg_Commands.h"
-#include "vcpkg_System.h"
-
-namespace vcpkg::Commands::Env
-{
- void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet)
- {
- static const std::string example = Commands::Help::create_example_string(R"(env --Triplet x64-windows)");
- args.check_exact_arg_count(0, example);
- args.check_and_get_optional_command_arguments({});
-
- auto pre_build_info = Build::PreBuildInfo::from_triplet_file(paths, default_triplet);
- System::cmd_execute_clean(Build::make_build_env_cmd(pre_build_info, paths.get_toolset()) + L" && cmd");
-
- Checks::exit_success(VCPKG_LINE_INFO);
- }
-}
diff --git a/toolsrc/src/commands_export.cpp b/toolsrc/src/commands_export.cpp
deleted file mode 100644
index e46001b06..000000000
--- a/toolsrc/src/commands_export.cpp
+++ /dev/null
@@ -1,392 +0,0 @@
-#include "pch.h"
-
-#include "Paragraphs.h"
-#include "vcpkg_Commands.h"
-#include "vcpkg_Dependencies.h"
-#include "vcpkg_Input.h"
-#include "vcpkg_System.h"
-#include "vcpkg_Util.h"
-#include "vcpkglib.h"
-#include <regex>
-
-namespace vcpkg::Commands::Export
-{
- using Install::InstallDir;
- using Dependencies::ExportPlanAction;
- using Dependencies::RequestType;
- using Dependencies::ExportPlanType;
-
- static std::string create_nuspec_file_contents(const std::string& raw_exported_dir,
- const std::string& targets_redirect_path,
- const std::string& nuget_id,
- const std::string& nupkg_version)
- {
- static constexpr auto content_template = R"(
-<package>
- <metadata>
- <id>@NUGET_ID@</id>
- <version>@VERSION@</version>
- <authors>vcpkg</authors>
- <description>
- Vcpkg NuGet export
- </description>
- </metadata>
- <files>
- <file src="@RAW_EXPORTED_DIR@\installed\**" target="installed" />
- <file src="@RAW_EXPORTED_DIR@\scripts\**" target="scripts" />
- <file src="@RAW_EXPORTED_DIR@\.vcpkg-root" target="" />
- <file src="@TARGETS_REDIRECT_PATH@" target="build\native\@NUGET_ID@.targets" />
- </files>
-</package>
-)";
-
- std::string nuspec_file_content = std::regex_replace(content_template, std::regex("@NUGET_ID@"), nuget_id);
- nuspec_file_content = std::regex_replace(nuspec_file_content, std::regex("@VERSION@"), nupkg_version);
- nuspec_file_content =
- std::regex_replace(nuspec_file_content, std::regex("@RAW_EXPORTED_DIR@"), raw_exported_dir);
- nuspec_file_content =
- std::regex_replace(nuspec_file_content, std::regex("@TARGETS_REDIRECT_PATH@"), targets_redirect_path);
- return nuspec_file_content;
- }
-
- static std::string create_targets_redirect(const std::string& target_path) noexcept
- {
- return Strings::format(R"###(
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <Import Condition="Exists('%s')" Project="%s" />
-</Project>
-)###",
- target_path,
- target_path);
- }
-
- static void print_plan(const std::map<ExportPlanType, std::vector<const ExportPlanAction*>>& group_by_plan_type)
- {
- static constexpr std::array<ExportPlanType, 2> order = {ExportPlanType::ALREADY_BUILT,
- ExportPlanType::PORT_AVAILABLE_BUT_NOT_BUILT};
-
- for (const ExportPlanType plan_type : order)
- {
- auto it = group_by_plan_type.find(plan_type);
- if (it == group_by_plan_type.cend())
- {
- continue;
- }
-
- std::vector<const ExportPlanAction*> cont = it->second;
- std::sort(cont.begin(), cont.end(), &ExportPlanAction::compare_by_name);
- const std::string as_string = Strings::join("\n", cont, [](const ExportPlanAction* p) {
- return Dependencies::to_output_string(p->request_type, p->spec.to_string());
- });
-
- switch (plan_type)
- {
- case ExportPlanType::ALREADY_BUILT:
- System::println("The following packages are already built and will be exported:\n%s", as_string);
- continue;
- case ExportPlanType::PORT_AVAILABLE_BUT_NOT_BUILT:
- System::println("The following packages need to be built:\n%s", as_string);
- continue;
- default: Checks::unreachable(VCPKG_LINE_INFO);
- }
- }
- }
-
- static std::string create_export_id()
- {
- const tm date_time = System::get_current_date_time();
-
- // Format is: YYYYmmdd-HHMMSS
- // 15 characters + 1 null terminating character will be written for a total of 16 chars
- char mbstr[16];
- const size_t bytes_written = std::strftime(mbstr, sizeof(mbstr), "%Y%m%d-%H%M%S", &date_time);
- Checks::check_exit(VCPKG_LINE_INFO,
- bytes_written == 15,
- "Expected 15 bytes to be written, but %u were written",
- bytes_written);
- const std::string date_time_as_string(mbstr);
- return ("vcpkg-export-" + date_time_as_string);
- }
-
- static fs::path do_nuget_export(const VcpkgPaths& paths,
- const std::string& nuget_id,
- const fs::path& raw_exported_dir,
- const fs::path& output_dir)
- {
- static const std::string NUPKG_VERSION = "1.0.0";
-
- Files::Filesystem& fs = paths.get_filesystem();
- const fs::path& nuget_exe = paths.get_nuget_exe();
-
- // This file will be placed in "build\native" in the nuget package. Therefore, go up two dirs.
- const std::string targets_redirect_content =
- create_targets_redirect("../../scripts/buildsystems/msbuild/vcpkg.targets");
- const fs::path targets_redirect = paths.buildsystems / "tmp" / "vcpkg.export.nuget.targets";
- fs.write_contents(targets_redirect, targets_redirect_content);
-
- const std::string nuspec_file_content =
- create_nuspec_file_contents(raw_exported_dir.string(), targets_redirect.string(), nuget_id, NUPKG_VERSION);
- const fs::path nuspec_file_path = paths.buildsystems / "tmp" / "vcpkg.export.nuspec";
- fs.write_contents(nuspec_file_path, nuspec_file_content);
-
- // -NoDefaultExcludes is needed for ".vcpkg-root"
- const std::wstring cmd_line =
- Strings::wformat(LR"("%s" pack -OutputDirectory "%s" "%s" -NoDefaultExcludes > nul)",
- nuget_exe.native(),
- output_dir.native(),
- nuspec_file_path.native());
-
- const int exit_code = System::cmd_execute_clean(cmd_line);
- Checks::check_exit(VCPKG_LINE_INFO, exit_code == 0, "Error: NuGet package creation failed");
-
- const fs::path output_path = output_dir / (nuget_id + ".nupkg");
- return output_path;
- }
-
- struct ArchiveFormat final
- {
- enum class BackingEnum
- {
- ZIP = 1,
- _7ZIP,
- };
-
- constexpr ArchiveFormat() = delete;
-
- constexpr ArchiveFormat(BackingEnum backing_enum, const wchar_t* extension, const wchar_t* cmake_option)
- : backing_enum(backing_enum), m_extension(extension), m_cmake_option(cmake_option)
- {
- }
-
- constexpr operator BackingEnum() const { return backing_enum; }
- constexpr CWStringView extension() const { return this->m_extension; }
- constexpr CWStringView cmake_option() const { return this->m_cmake_option; }
-
- private:
- BackingEnum backing_enum;
- const wchar_t* m_extension;
- const wchar_t* m_cmake_option;
- };
-
- namespace ArchiveFormatC
- {
- constexpr const ArchiveFormat ZIP(ArchiveFormat::BackingEnum::ZIP, L"zip", L"zip");
- constexpr const ArchiveFormat _7ZIP(ArchiveFormat::BackingEnum::_7ZIP, L"7z", L"7zip");
- }
-
- static fs::path do_archive_export(const VcpkgPaths& paths,
- const fs::path& raw_exported_dir,
- const fs::path& output_dir,
- const ArchiveFormat& format)
- {
- const fs::path& cmake_exe = paths.get_cmake_exe();
-
- const std::wstring exported_dir_filename = raw_exported_dir.filename().native();
- const std::wstring exported_archive_filename =
- Strings::wformat(L"%s.%s", exported_dir_filename, format.extension());
- const fs::path exported_archive_path = (output_dir / exported_archive_filename);
-
- // -NoDefaultExcludes is needed for ".vcpkg-root"
- const std::wstring cmd_line = Strings::wformat(LR"("%s" -E tar "cf" "%s" --format=%s -- "%s")",
- cmake_exe.native(),
- exported_archive_path.native(),
- format.cmake_option(),
- raw_exported_dir.native());
-
- const int exit_code = System::cmd_execute_clean(cmd_line);
- Checks::check_exit(
- VCPKG_LINE_INFO, exit_code == 0, "Error: %s creation failed", exported_archive_path.generic_string());
- return exported_archive_path;
- }
-
- void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet)
- {
- static const std::string OPTION_DRY_RUN = "--dry-run";
- static const std::string OPTION_RAW = "--raw";
- static const std::string OPTION_NUGET = "--nuget";
- static const std::string OPTION_ZIP = "--zip";
- static const std::string OPTION_7ZIP = "--7zip";
-
- // input sanitization
- static const std::string example =
- Commands::Help::create_example_string("export zlib zlib:x64-windows boost --nuget");
- args.check_min_arg_count(1, example);
-
- const std::vector<PackageSpec> specs = Util::fmap(args.command_arguments, [&](auto&& arg) {
- return Input::check_and_get_package_spec(arg, default_triplet, example);
- });
- for (auto&& spec : specs)
- Input::check_triplet(spec.triplet(), paths);
-
- const std::unordered_set<std::string> options = args.check_and_get_optional_command_arguments(
- {OPTION_DRY_RUN, OPTION_RAW, OPTION_NUGET, OPTION_ZIP, OPTION_7ZIP});
- const bool dryRun = options.find(OPTION_DRY_RUN) != options.cend();
- const bool raw = options.find(OPTION_RAW) != options.cend();
- const bool nuget = options.find(OPTION_NUGET) != options.cend();
- const bool zip = options.find(OPTION_ZIP) != options.cend();
- const bool _7zip = options.find(OPTION_7ZIP) != options.cend();
-
- Checks::check_exit(VCPKG_LINE_INFO,
- raw || nuget || zip || _7zip,
- "Must provide at least one of the following options: --raw --nuget --zip --7zip");
-
- // create the plan
- StatusParagraphs status_db = database_load_check(paths);
- std::vector<ExportPlanAction> export_plan = Dependencies::create_export_plan(paths, specs, status_db);
- Checks::check_exit(VCPKG_LINE_INFO, !export_plan.empty(), "Export plan cannot be empty");
-
- std::map<ExportPlanType, std::vector<const ExportPlanAction*>> group_by_plan_type;
- Util::group_by(export_plan, &group_by_plan_type, [](const ExportPlanAction& p) { return p.plan_type; });
- print_plan(group_by_plan_type);
-
- const bool has_non_user_requested_packages =
- Util::find_if(export_plan, [](const ExportPlanAction& package) -> bool {
- return package.request_type != RequestType::USER_REQUESTED;
- }) != export_plan.cend();
-
- if (has_non_user_requested_packages)
- {
- System::println(System::Color::warning,
- "Additional packages (*) need to be exported to complete this operation.");
- }
-
- auto it = group_by_plan_type.find(ExportPlanType::PORT_AVAILABLE_BUT_NOT_BUILT);
- if (it != group_by_plan_type.cend() && !it->second.empty())
- {
- System::println(System::Color::error, "There are packages that have not been built.");
-
- // No need to show all of them, just the user-requested ones. Dependency resolution will handle the rest.
- std::vector<const ExportPlanAction*> unbuilt = it->second;
- Util::erase_remove_if(
- unbuilt, [](const ExportPlanAction* a) { return a->request_type != RequestType::USER_REQUESTED; });
-
- auto s = Strings::join(" ", unbuilt, [](const ExportPlanAction* a) { return a->spec.to_string(); });
- System::println("To build them, run:\n"
- " vcpkg install %s",
- s);
- Checks::exit_fail(VCPKG_LINE_INFO);
- }
-
- if (dryRun)
- {
- Checks::exit_success(VCPKG_LINE_INFO);
- }
-
- const std::string export_id = create_export_id();
-
- 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;
- std::error_code ec;
- fs.remove_all(raw_exported_dir_path, ec);
- fs.create_directory(raw_exported_dir_path, ec);
-
- // execute the plan
- for (const ExportPlanAction& action : export_plan)
- {
- if (action.plan_type != ExportPlanType::ALREADY_BUILT)
- {
- Checks::unreachable(VCPKG_LINE_INFO);
- }
-
- const std::string display_name = action.spec.to_string();
- System::println("Exporting package %s... ", display_name);
-
- const BinaryParagraph& binary_paragraph =
- action.any_paragraph.binary_paragraph.value_or_exit(VCPKG_LINE_INFO);
- const InstallDir dirs = InstallDir::from_destination_root(
- raw_exported_dir_path / "installed",
- action.spec.triplet().to_string(),
- raw_exported_dir_path / "installed" / "vcpkg" / "info" / (binary_paragraph.fullstem() + ".list"));
-
- Install::install_files_and_write_listfile(paths.get_filesystem(), paths.package_dir(action.spec), dirs);
- System::println(System::Color::success, "Exporting package %s... done", display_name);
- }
-
- // Copy files needed for integration
- const std::vector<fs::path> integration_files_relative_to_root = {
- {".vcpkg-root"},
- {fs::path{"scripts"} / "buildsystems" / "msbuild" / "applocal.ps1"},
- {fs::path{"scripts"} / "buildsystems" / "msbuild" / "vcpkg.targets"},
- {fs::path{"scripts"} / "buildsystems" / "vcpkg.cmake"},
- {fs::path{"scripts"} / "cmake" / "vcpkg_get_windows_sdk.cmake"},
- {fs::path{"scripts"} / "getWindowsSDK.ps1"},
- {fs::path{"scripts"} / "getProgramFilesPlatformBitness.ps1"},
- {fs::path{"scripts"} / "getProgramFiles32bit.ps1"},
- };
-
- for (const fs::path& file : integration_files_relative_to_root)
- {
- const fs::path source = paths.root / file;
- const fs::path destination = raw_exported_dir_path / file;
- fs.create_directories(destination.parent_path(), ec);
- Checks::check_exit(VCPKG_LINE_INFO, !ec);
- fs.copy_file(source, destination, fs::copy_options::overwrite_existing, ec);
- Checks::check_exit(VCPKG_LINE_INFO, !ec);
- }
-
- auto print_next_step_info = [](const fs::path& prefix) {
- const fs::path cmake_toolchain = prefix / "scripts" / "buildsystems" / "vcpkg.cmake";
- const CMakeVariable cmake_variable =
- CMakeVariable(L"CMAKE_TOOLCHAIN_FILE", cmake_toolchain.generic_string());
- System::println("\n"
- "To use the exported libraries in CMake projects use:"
- "\n"
- " %s"
- "\n",
- Strings::to_utf8(cmake_variable.s));
- };
-
- if (raw)
- {
- System::println(
- System::Color::success, R"(Files exported at: "%s")", raw_exported_dir_path.generic_string());
- print_next_step_info(export_to_path);
- }
-
- if (nuget)
- {
- System::println("Creating nuget package... ");
-
- const std::string nuget_id = raw_exported_dir_path.filename().string();
- const fs::path output_path = do_nuget_export(paths, nuget_id, raw_exported_dir_path, export_to_path);
- System::println(System::Color::success, "Creating nuget package... done");
- System::println(System::Color::success, "NuGet package exported at: %s", output_path.generic_string());
-
- System::println(R"(
-With a project open, go to Tools->NuGet Package Manager->Package Manager Console and paste:
- Install-Package %s -Source "%s"
-)"
- "\n",
- nuget_id,
- output_path.parent_path().u8string());
- }
-
- if (zip)
- {
- System::println("Creating zip archive... ");
- const fs::path output_path =
- do_archive_export(paths, raw_exported_dir_path, export_to_path, ArchiveFormatC::ZIP);
- System::println(System::Color::success, "Creating zip archive... done");
- System::println(System::Color::success, "Zip archive exported at: %s", output_path.generic_string());
- print_next_step_info("[...]");
- }
-
- if (_7zip)
- {
- System::println("Creating 7zip archive... ");
- const fs::path output_path =
- do_archive_export(paths, raw_exported_dir_path, export_to_path, ArchiveFormatC::_7ZIP);
- System::println(System::Color::success, "Creating 7zip archive... done");
- System::println(System::Color::success, "7zip archive exported at: %s", output_path.generic_string());
- print_next_step_info("[...]");
- }
-
- if (!raw)
- {
- fs.remove_all(raw_exported_dir_path, ec);
- }
-
- Checks::exit_success(VCPKG_LINE_INFO);
- }
-}
diff --git a/toolsrc/src/commands_hash.cpp b/toolsrc/src/commands_hash.cpp
deleted file mode 100644
index 0da2b031f..000000000
--- a/toolsrc/src/commands_hash.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-#include "pch.h"
-
-#include "vcpkg_Commands.h"
-#include "vcpkg_System.h"
-#include "vcpkg_Util.h"
-
-namespace vcpkg::Commands::Hash
-{
- static void do_file_hash(fs::path const& path, std::wstring const& hashType)
- {
- auto cmd_line = Strings::wformat(LR"(CertUtil.exe -hashfile "%s" %s)", path.c_str(), hashType);
- auto ec_data = System::cmd_execute_and_capture_output(cmd_line);
- Checks::check_exit(
- VCPKG_LINE_INFO, ec_data.exit_code == 0, "Running command:\n %s\n failed", Strings::to_utf8(cmd_line));
-
- std::string const& output = ec_data.output;
-
- auto start = output.find_first_of("\r\n");
- Checks::check_exit(VCPKG_LINE_INFO,
- start != std::string::npos,
- "Unexpected output format from command: %s",
- Strings::to_utf8(cmd_line));
-
- auto end = output.find_first_of("\r\n", start + 1);
- Checks::check_exit(VCPKG_LINE_INFO,
- end != std::string::npos,
- "Unexpected output format from command: %s",
- Strings::to_utf8(cmd_line));
-
- auto hash = output.substr(start, end - start);
- Util::erase_remove_if(hash, isspace);
- System::println(hash);
- }
-
- void perform_and_exit(const VcpkgCmdArguments& args)
- {
- static const std::string example =
- Strings::format("The argument should be a file path\n%s",
- Commands::Help::create_example_string("hash boost_1_62_0.tar.bz2"));
- args.check_min_arg_count(1, example);
- args.check_max_arg_count(2, example);
- args.check_and_get_optional_command_arguments({});
-
- if (args.command_arguments.size() == 1)
- {
- do_file_hash(args.command_arguments[0], L"SHA512");
- }
- if (args.command_arguments.size() == 2)
- {
- do_file_hash(args.command_arguments[0], Strings::to_utf16(args.command_arguments[1]));
- }
-
- Checks::exit_success(VCPKG_LINE_INFO);
- }
-}
diff --git a/toolsrc/src/commands_help.cpp b/toolsrc/src/commands_help.cpp
deleted file mode 100644
index a9f9956fe..000000000
--- a/toolsrc/src/commands_help.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-#include "pch.h"
-
-#include "vcpkg_Commands.h"
-#include "vcpkg_System.h"
-
-namespace vcpkg::Commands::Help
-{
- void help_topic_valid_triplet(const VcpkgPaths& paths)
- {
- System::println("Available architecture triplets:");
- for (auto&& path : paths.get_filesystem().get_files_non_recursive(paths.triplets))
- {
- System::println(" %s", path.stem().filename().string());
- }
- }
-
- void print_usage()
- {
- System::println(
- "Commands:\n"
- " vcpkg search [pat] Search for packages available to be built\n"
- " vcpkg install <pkg>... Install a package\n"
- " vcpkg remove <pkg>... Uninstall a package\n"
- " vcpkg remove --outdated Uninstall all out-of-date packages\n"
- " vcpkg list List installed packages\n"
- " vcpkg update Display list of packages for updating\n"
- " vcpkg hash <file> [alg] Hash a file by specific algorithm, default SHA512\n"
- "\n"
- "%s" // Integration help
- "\n"
- " vcpkg export <pkg>... [opt]... Exports a package\n"
- " vcpkg edit <pkg> Open up a port for editing (uses %%EDITOR%%, default 'code')\n"
- " vcpkg import <pkg> Import a pre-built library\n"
- " vcpkg create <pkg> <url>\n"
- " [archivename] Create a new package\n"
- " vcpkg owns <pat> Search for files in installed packages\n"
- " vcpkg cache List cached compiled packages\n"
- " vcpkg version Display version information\n"
- " vcpkg contact Display contact information to send feedback\n"
- "\n"
- //"internal commands:\n"
- //" --check-build-deps <controlfile>\n"
- //" --create-binary-control <controlfile>\n"
- //"\n"
- "Options:\n"
- " --triplet <t> Specify the target architecture triplet.\n"
- " (default: %%VCPKG_DEFAULT_TRIPLET%%, see 'vcpkg help triplet')\n"
- "\n"
- " --vcpkg-root <path> Specify the vcpkg root directory\n"
- " (default: %%VCPKG_ROOT%%)\n"
- "\n"
- "For more help (including examples) see the accompanying README.md.",
- Integrate::INTEGRATE_COMMAND_HELPSTRING);
- }
-
- std::string create_example_string(const std::string& command_and_arguments)
- {
- std::string cs = Strings::format("Example:\n"
- " vcpkg %s",
- command_and_arguments);
- return cs;
- }
-
- void print_example(const std::string& command_and_arguments)
- {
- System::println(create_example_string(command_and_arguments));
- }
-
- void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
- {
- args.check_max_arg_count(1);
- args.check_and_get_optional_command_arguments({});
-
- if (args.command_arguments.empty())
- {
- print_usage();
- Checks::exit_success(VCPKG_LINE_INFO);
- }
- const auto& topic = args.command_arguments[0];
- if (topic == "triplet")
- {
- help_topic_valid_triplet(paths);
- }
- else
- {
- System::println(System::Color::error, "Error: unknown topic %s", topic);
- print_usage();
- Checks::exit_fail(VCPKG_LINE_INFO);
- }
- Checks::exit_success(VCPKG_LINE_INFO);
- }
-}
diff --git a/toolsrc/src/commands_install.cpp b/toolsrc/src/commands_install.cpp
deleted file mode 100644
index bebe6a3a2..000000000
--- a/toolsrc/src/commands_install.cpp
+++ /dev/null
@@ -1,398 +0,0 @@
-#include "pch.h"
-
-#include "Paragraphs.h"
-#include "metrics.h"
-#include "vcpkg_Build.h"
-#include "vcpkg_Commands.h"
-#include "vcpkg_Dependencies.h"
-#include "vcpkg_Files.h"
-#include "vcpkg_Input.h"
-#include "vcpkg_System.h"
-#include "vcpkg_Util.h"
-#include "vcpkglib.h"
-
-namespace vcpkg::Commands::Install
-{
- using Dependencies::InstallPlanAction;
- using Dependencies::RequestType;
- using Dependencies::InstallPlanType;
-
- InstallDir InstallDir::from_destination_root(const fs::path& destination_root,
- const std::string& destination_subdirectory,
- const fs::path& listfile)
- {
- InstallDir dirs;
- dirs.m_destination = destination_root / destination_subdirectory;
- dirs.m_destination_subdirectory = destination_subdirectory;
- dirs.m_listfile = listfile;
- return dirs;
- }
-
- const fs::path& InstallDir::destination() const { return this->m_destination; }
-
- const std::string& InstallDir::destination_subdirectory() const { return this->m_destination_subdirectory; }
-
- const fs::path& InstallDir::listfile() const { return this->m_listfile; }
-
- void install_files_and_write_listfile(Files::Filesystem& fs,
- const fs::path& source_dir,
- const InstallDir& destination_dir)
- {
- std::vector<std::string> output;
- std::error_code ec;
-
- const size_t prefix_length = source_dir.native().size();
- const fs::path& destination = destination_dir.destination();
- const std::string& destination_subdirectory = destination_dir.destination_subdirectory();
- const fs::path& listfile = destination_dir.listfile();
-
- Checks::check_exit(
- VCPKG_LINE_INFO, fs.exists(source_dir), "Source directory %s does not exist", source_dir.generic_string());
- fs.create_directories(destination, ec);
- Checks::check_exit(
- VCPKG_LINE_INFO, !ec, "Could not create destination directory %s", destination.generic_string());
- const fs::path listfile_parent = listfile.parent_path();
- fs.create_directories(listfile_parent, ec);
- Checks::check_exit(
- VCPKG_LINE_INFO, !ec, "Could not create directory for listfile %s", listfile.generic_string());
-
- output.push_back(Strings::format(R"(%s/)", destination_subdirectory));
- auto files = fs.get_files_recursive(source_dir);
- for (auto&& file : files)
- {
- auto status = fs.status(file, ec);
- if (ec)
- {
- System::println(System::Color::error, "failed: %s: %s", file.u8string(), ec.message());
- continue;
- }
-
- const std::string filename = file.filename().generic_string();
- if (fs::is_regular_file(status) &&
- (Strings::case_insensitive_ascii_compare(filename.c_str(), "CONTROL") == 0 ||
- Strings::case_insensitive_ascii_compare(filename.c_str(), "BUILD_INFO") == 0))
- {
- // Do not copy the control file
- continue;
- }
-
- const std::string suffix = file.generic_u8string().substr(prefix_length + 1);
- const fs::path target = destination / suffix;
-
- if (fs::is_directory(status))
- {
- fs.create_directory(target, ec);
- if (ec)
- {
- System::println(System::Color::error, "failed: %s: %s", target.u8string(), ec.message());
- }
-
- // Trailing backslash for directories
- output.push_back(Strings::format(R"(%s/%s/)", destination_subdirectory, suffix));
- continue;
- }
-
- if (fs::is_regular_file(status))
- {
- if (fs.exists(target))
- {
- System::println(System::Color::warning,
- "File %s was already present and will be overwritten",
- target.u8string(),
- ec.message());
- }
- fs.copy_file(file, target, fs::copy_options::overwrite_existing, ec);
- if (ec)
- {
- System::println(System::Color::error, "failed: %s: %s", target.u8string(), ec.message());
- }
- output.push_back(Strings::format(R"(%s/%s)", destination_subdirectory, suffix));
- continue;
- }
-
- if (!fs::status_known(status))
- {
- System::println(System::Color::error, "failed: %s: unknown status", file.u8string());
- continue;
- }
-
- System::println(System::Color::error, "failed: %s: cannot handle file type", file.u8string());
- }
-
- std::sort(output.begin(), output.end());
-
- fs.write_lines(listfile, output);
- }
-
- static void remove_first_n_chars(std::vector<std::string>* strings, const size_t n)
- {
- for (std::string& s : *strings)
- {
- s.erase(0, n);
- }
- };
-
- static std::vector<std::string> extract_files_in_triplet(
- const std::vector<StatusParagraphAndAssociatedFiles>& pgh_and_files, const Triplet& triplet)
- {
- std::vector<std::string> output;
- for (const StatusParagraphAndAssociatedFiles& t : pgh_and_files)
- {
- if (t.pgh.package.spec.triplet() != triplet)
- {
- continue;
- }
-
- output.insert(output.end(), t.files.begin(), t.files.end());
- }
-
- std::sort(output.begin(), output.end());
- return output;
- }
-
- static SortedVector<std::string> build_list_of_package_files(const Files::Filesystem& fs,
- const fs::path& package_dir)
- {
- 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) {
- std::string as_string = path.generic_string();
- as_string.erase(0, package_remove_char_count);
- return std::move(as_string);
- });
-
- return SortedVector<std::string>(std::move(package_files));
- }
-
- static SortedVector<std::string> build_list_of_installed_files(
- const std::vector<StatusParagraphAndAssociatedFiles>& pgh_and_files, const Triplet& triplet)
- {
- std::vector<std::string> installed_files = extract_files_in_triplet(pgh_and_files, triplet);
- const size_t installed_remove_char_count = triplet.canonical_name().size() + 1; // +1 for the slash
- remove_first_n_chars(&installed_files, installed_remove_char_count);
-
- return SortedVector<std::string>(std::move(installed_files));
- }
-
- static void print_plan(const std::map<InstallPlanType, std::vector<const InstallPlanAction*>>& group_by_plan_type)
- {
- static constexpr std::array<InstallPlanType, 3> order = {
- InstallPlanType::ALREADY_INSTALLED, InstallPlanType::BUILD_AND_INSTALL, InstallPlanType::INSTALL};
-
- for (const InstallPlanType plan_type : order)
- {
- auto it = group_by_plan_type.find(plan_type);
- if (it == group_by_plan_type.cend())
- {
- continue;
- }
-
- std::vector<const InstallPlanAction*> cont = it->second;
- std::sort(cont.begin(), cont.end(), &InstallPlanAction::compare_by_name);
- const std::string as_string = Strings::join("\n", cont, [](const InstallPlanAction* p) {
- return Dependencies::to_output_string(p->request_type, p->spec.to_string());
- });
-
- switch (plan_type)
- {
- case InstallPlanType::ALREADY_INSTALLED:
- System::println("The following packages are already installed:\n%s", as_string);
- continue;
- case InstallPlanType::BUILD_AND_INSTALL:
- System::println("The following packages will be built and installed:\n%s", as_string);
- continue;
- case InstallPlanType::INSTALL:
- System::println("The following packages will be installed:\n%s", as_string);
- continue;
- default: Checks::unreachable(VCPKG_LINE_INFO);
- }
- }
- }
-
- void install_package(const VcpkgPaths& paths, const BinaryParagraph& binary_paragraph, StatusParagraphs* status_db)
- {
- const fs::path package_dir = paths.package_dir(binary_paragraph.spec);
- const Triplet& triplet = binary_paragraph.spec.triplet();
- const std::vector<StatusParagraphAndAssociatedFiles> pgh_and_files = get_installed_files(paths, *status_db);
-
- const SortedVector<std::string> package_files =
- build_list_of_package_files(paths.get_filesystem(), package_dir);
- const SortedVector<std::string> installed_files = build_list_of_installed_files(pgh_and_files, triplet);
-
- std::vector<std::string> intersection;
- std::set_intersection(package_files.begin(),
- package_files.end(),
- installed_files.begin(),
- installed_files.end(),
- std::back_inserter(intersection));
-
- if (!intersection.empty())
- {
- const fs::path triplet_install_path = paths.installed / triplet.canonical_name();
- System::println(System::Color::error,
- "The following files are already installed in %s and are in conflict with %s",
- triplet_install_path.generic_string(),
- binary_paragraph.spec);
- System::print("\n ");
- System::println(Strings::join("\n ", intersection));
- System::println("");
- Checks::exit_fail(VCPKG_LINE_INFO);
- }
-
- StatusParagraph source_paragraph;
- source_paragraph.package = binary_paragraph;
- source_paragraph.want = Want::INSTALL;
- source_paragraph.state = InstallState::HALF_INSTALLED;
- for (auto&& dep : source_paragraph.package.depends)
- {
- if (status_db->find_installed(dep, source_paragraph.package.spec.triplet()) == status_db->end())
- {
- Checks::unreachable(VCPKG_LINE_INFO);
- }
- }
- write_update(paths, source_paragraph);
- status_db->insert(std::make_unique<StatusParagraph>(source_paragraph));
-
- const InstallDir install_dir = InstallDir::from_destination_root(
- paths.installed, triplet.to_string(), paths.listfile_path(binary_paragraph));
-
- install_files_and_write_listfile(paths.get_filesystem(), package_dir, install_dir);
-
- source_paragraph.state = InstallState::INSTALLED;
- write_update(paths, source_paragraph);
- status_db->insert(std::make_unique<StatusParagraph>(source_paragraph));
- }
-
- void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet)
- {
- static const std::string OPTION_DRY_RUN = "--dry-run";
- static const std::string OPTION_USE_HEAD_VERSION = "--head";
- static const std::string OPTION_NO_DOWNLOADS = "--no-downloads";
-
- // input sanitization
- static const std::string example =
- Commands::Help::create_example_string("install zlib zlib:x64-windows curl boost");
- args.check_min_arg_count(1, example);
-
- const std::vector<PackageSpec> specs = Util::fmap(args.command_arguments, [&](auto&& arg) {
- return Input::check_and_get_package_spec(arg, default_triplet, example);
- });
- for (auto&& spec : specs)
- Input::check_triplet(spec.triplet(), paths);
-
- const std::unordered_set<std::string> options = args.check_and_get_optional_command_arguments(
- {OPTION_DRY_RUN, OPTION_USE_HEAD_VERSION, OPTION_NO_DOWNLOADS});
- const bool dryRun = options.find(OPTION_DRY_RUN) != options.cend();
- const bool use_head_version = options.find(OPTION_USE_HEAD_VERSION) != options.cend();
- const bool no_downloads = options.find(OPTION_NO_DOWNLOADS) != options.cend();
-
- // create the plan
- StatusParagraphs status_db = database_load_check(paths);
- std::vector<InstallPlanAction> install_plan = Dependencies::create_install_plan(paths, specs, status_db);
- Checks::check_exit(VCPKG_LINE_INFO, !install_plan.empty(), "Install plan cannot be empty");
-
- // log the plan
- const std::string specs_string =
- Strings::join(",", install_plan, [](const InstallPlanAction& plan) { return plan.spec.to_string(); });
- Metrics::track_property("installplan", specs_string);
-
- std::map<InstallPlanType, std::vector<const InstallPlanAction*>> group_by_plan_type;
- Util::group_by(install_plan, &group_by_plan_type, [](const InstallPlanAction& p) { return p.plan_type; });
- print_plan(group_by_plan_type);
-
- const bool has_non_user_requested_packages =
- Util::find_if(install_plan, [](const InstallPlanAction& package) -> bool {
- return package.request_type != RequestType::USER_REQUESTED;
- }) != install_plan.cend();
-
- if (has_non_user_requested_packages)
- {
- System::println("Additional packages (*) will be installed to complete this operation.");
- }
-
- if (dryRun)
- {
- Checks::exit_success(VCPKG_LINE_INFO);
- }
-
- // execute the plan
- for (const InstallPlanAction& action : install_plan)
- {
- const std::string display_name = action.spec.to_string();
-
- try
- {
- switch (action.plan_type)
- {
- case InstallPlanType::ALREADY_INSTALLED:
- if (use_head_version && action.request_type == RequestType::USER_REQUESTED)
- {
- System::println(System::Color::warning,
- "Package %s is already installed -- not building from HEAD",
- display_name);
- }
- else
- {
- System::println(System::Color::success, "Package %s is already installed", display_name);
- }
- break;
- case InstallPlanType::BUILD_AND_INSTALL:
- {
- Build::BuildPackageConfig build_config{
- action.any_paragraph.source_paragraph.value_or_exit(VCPKG_LINE_INFO),
- action.spec.triplet(),
- paths.port_dir(action.spec),
- };
-
- build_config.use_head_version =
- use_head_version && action.request_type == RequestType::USER_REQUESTED;
- build_config.no_downloads = no_downloads;
-
- if (build_config.use_head_version)
- System::println("Building package %s from HEAD... ", display_name);
- else
- System::println("Building package %s... ", display_name);
-
- const auto result = Build::build_package(paths, build_config, status_db);
- if (result.code != Build::BuildResult::SUCCEEDED)
- {
- System::println(System::Color::error,
- Build::create_error_message(result.code, action.spec));
- System::println(Build::create_user_troubleshooting_message(action.spec));
- Checks::exit_fail(VCPKG_LINE_INFO);
- }
- System::println("Building package %s... done", display_name);
-
- const BinaryParagraph bpgh =
- Paragraphs::try_load_cached_package(paths, action.spec).value_or_exit(VCPKG_LINE_INFO);
- System::println("Installing package %s... ", display_name);
- install_package(paths, bpgh, &status_db);
- System::println(System::Color::success, "Installing package %s... done", display_name);
- break;
- }
- case InstallPlanType::INSTALL:
- if (use_head_version && action.request_type == RequestType::USER_REQUESTED)
- {
- System::println(System::Color::warning,
- "Package %s is already built -- not building from HEAD",
- display_name);
- }
- System::println("Installing package %s... ", display_name);
- install_package(
- paths, action.any_paragraph.binary_paragraph.value_or_exit(VCPKG_LINE_INFO), &status_db);
- System::println(System::Color::success, "Installing package %s... done", display_name);
- break;
- case InstallPlanType::UNKNOWN:
- default: Checks::unreachable(VCPKG_LINE_INFO);
- }
- }
- catch (const std::exception& e)
- {
- System::println(System::Color::error, "Error: Could not install package %s: %s", action.spec, e.what());
- Checks::exit_fail(VCPKG_LINE_INFO);
- }
- }
-
- Checks::exit_success(VCPKG_LINE_INFO);
- }
-}
diff --git a/toolsrc/src/commands_list.cpp b/toolsrc/src/commands_list.cpp
deleted file mode 100644
index 6451ac5eb..000000000
--- a/toolsrc/src/commands_list.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-#include "pch.h"
-
-#include "vcpkg_Commands.h"
-#include "vcpkg_System.h"
-#include "vcpkglib.h"
-#include "vcpkglib_helpers.h"
-
-namespace vcpkg::Commands::List
-{
- static void do_print(const StatusParagraph& pgh)
- {
- System::println("%-27s %-16s %s",
- pgh.package.displayname(),
- pgh.package.version,
- details::shorten_description(pgh.package.description));
- }
-
- void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
- {
- static const std::string example = Strings::format(
- "The argument should be a substring to search for, or no argument to display all installed libraries.\n%s",
- Commands::Help::create_example_string("list png"));
- args.check_max_arg_count(1, example);
- args.check_and_get_optional_command_arguments({});
-
- const StatusParagraphs status_paragraphs = database_load_check(paths);
- std::vector<StatusParagraph*> installed_packages = get_installed_ports(status_paragraphs);
-
- if (installed_packages.empty())
- {
- System::println("No packages are installed. Did you mean `search`?");
- Checks::exit_success(VCPKG_LINE_INFO);
- }
-
- std::sort(installed_packages.begin(),
- installed_packages.end(),
- [](const StatusParagraph* lhs, const StatusParagraph* rhs) -> bool {
- return lhs->package.displayname() < rhs->package.displayname();
- });
-
- if (args.command_arguments.size() == 0)
- {
- for (const StatusParagraph* status_paragraph : installed_packages)
- {
- do_print(*status_paragraph);
- }
- }
- else
- {
- // At this point there is 1 argument
- for (const StatusParagraph* status_paragraph : installed_packages)
- {
- const std::string displayname = status_paragraph->package.displayname();
- if (Strings::case_insensitive_ascii_find(displayname, args.command_arguments[0]) == displayname.end())
- {
- continue;
- }
-
- do_print(*status_paragraph);
- }
- }
-
- Checks::exit_success(VCPKG_LINE_INFO);
- }
-}
diff --git a/toolsrc/src/commands_remove.cpp b/toolsrc/src/commands_remove.cpp
deleted file mode 100644
index e2b5d12a1..000000000
--- a/toolsrc/src/commands_remove.cpp
+++ /dev/null
@@ -1,229 +0,0 @@
-#include "pch.h"
-
-#include "vcpkg_Commands.h"
-#include "vcpkg_Dependencies.h"
-#include "vcpkg_Input.h"
-#include "vcpkg_System.h"
-#include "vcpkg_Util.h"
-#include "vcpkglib.h"
-
-namespace vcpkg::Commands::Remove
-{
- using Dependencies::RemovePlanAction;
- using Dependencies::RemovePlanType;
- using Dependencies::RequestType;
- using Update::OutdatedPackage;
-
- static void remove_package(const VcpkgPaths& paths, const PackageSpec& spec, StatusParagraphs* status_db)
- {
- auto& fs = paths.get_filesystem();
- StatusParagraph& pkg = **status_db->find(spec.name(), spec.triplet());
-
- pkg.want = Want::PURGE;
- pkg.state = InstallState::HALF_INSTALLED;
- write_update(paths, pkg);
-
- auto maybe_lines = fs.read_lines(paths.listfile_path(pkg.package));
-
- if (auto lines = maybe_lines.get())
- {
- std::vector<fs::path> dirs_touched;
- for (auto&& suffix : *lines)
- {
- if (!suffix.empty() && suffix.back() == '\r') suffix.pop_back();
-
- std::error_code ec;
-
- auto target = paths.installed / suffix;
-
- auto status = fs.status(target, ec);
- if (ec)
- {
- System::println(System::Color::error, "failed: %s", ec.message());
- continue;
- }
-
- if (fs::is_directory(status))
- {
- dirs_touched.push_back(target);
- }
- else if (fs::is_regular_file(status))
- {
- fs.remove(target, ec);
- if (ec)
- {
- System::println(System::Color::error, "failed: %s: %s", target.u8string(), ec.message());
- }
- }
- else if (!fs::status_known(status))
- {
- System::println(System::Color::warning, "Warning: unknown status: %s", target.u8string());
- }
- else
- {
- System::println(System::Color::warning, "Warning: %s: cannot handle file type", target.u8string());
- }
- }
-
- auto b = dirs_touched.rbegin();
- auto e = dirs_touched.rend();
- for (; b != e; ++b)
- {
- if (fs.is_empty(*b))
- {
- std::error_code ec;
- fs.remove(*b, ec);
- if (ec)
- {
- System::println(System::Color::error, "failed: %s", ec.message());
- }
- }
- }
-
- fs.remove(paths.listfile_path(pkg.package));
- }
-
- pkg.state = InstallState::NOT_INSTALLED;
- write_update(paths, pkg);
- }
-
- static void print_plan(const std::map<RemovePlanType, std::vector<const RemovePlanAction*>>& group_by_plan_type)
- {
- static constexpr std::array<RemovePlanType, 2> order = {RemovePlanType::NOT_INSTALLED, RemovePlanType::REMOVE};
-
- for (const RemovePlanType plan_type : order)
- {
- auto it = group_by_plan_type.find(plan_type);
- if (it == group_by_plan_type.cend())
- {
- continue;
- }
-
- std::vector<const RemovePlanAction*> cont = it->second;
- std::sort(cont.begin(), cont.end(), &RemovePlanAction::compare_by_name);
- const std::string as_string = Strings::join("\n", cont, [](const RemovePlanAction* p) {
- return Dependencies::to_output_string(p->request_type, p->spec.to_string());
- });
-
- switch (plan_type)
- {
- case RemovePlanType::NOT_INSTALLED:
- System::println("The following packages are not installed, so not removed:\n%s", as_string);
- continue;
- case RemovePlanType::REMOVE:
- System::println("The following packages will be removed:\n%s", as_string);
- continue;
- default: Checks::unreachable(VCPKG_LINE_INFO);
- }
- }
- }
-
- void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet)
- {
- static const std::string OPTION_PURGE = "--purge";
- static const std::string OPTION_NO_PURGE = "--no-purge";
- static const std::string OPTION_RECURSE = "--recurse";
- static const std::string OPTION_DRY_RUN = "--dry-run";
- static const std::string OPTION_OUTDATED = "--outdated";
- static const std::string example =
- Commands::Help::create_example_string("remove zlib zlib:x64-windows curl boost");
- const std::unordered_set<std::string> options = args.check_and_get_optional_command_arguments(
- {OPTION_PURGE, OPTION_NO_PURGE, OPTION_RECURSE, OPTION_DRY_RUN, OPTION_OUTDATED});
-
- StatusParagraphs status_db = database_load_check(paths);
- std::vector<PackageSpec> specs;
- if (options.find(OPTION_OUTDATED) != options.cend())
- {
- args.check_exact_arg_count(0, example);
- specs = Util::fmap(Update::find_outdated_packages(paths, status_db),
- [](auto&& outdated) { return outdated.spec; });
-
- if (specs.empty())
- {
- System::println(System::Color::success, "There are no oudated packages.");
- Checks::exit_success(VCPKG_LINE_INFO);
- }
- }
- else
- {
- args.check_min_arg_count(1, example);
- specs = Util::fmap(args.command_arguments, [&](auto&& arg) {
- return Input::check_and_get_package_spec(arg, default_triplet, example);
- });
-
- for (auto&& spec : specs)
- Input::check_triplet(spec.triplet(), paths);
- }
-
- const bool alsoRemoveFolderFromPackages = options.find(OPTION_NO_PURGE) == options.end();
- if (options.find(OPTION_PURGE) != options.end() && !alsoRemoveFolderFromPackages)
- {
- // User specified --purge and --no-purge
- System::println(System::Color::error, "Error: cannot specify both --no-purge and --purge.");
- System::print(example);
- Checks::exit_fail(VCPKG_LINE_INFO);
- }
- const bool isRecursive = options.find(OPTION_RECURSE) != options.cend();
- const bool dryRun = options.find(OPTION_DRY_RUN) != options.cend();
-
- const std::vector<RemovePlanAction> remove_plan = Dependencies::create_remove_plan(specs, status_db);
- Checks::check_exit(VCPKG_LINE_INFO, !remove_plan.empty(), "Remove plan cannot be empty");
-
- std::map<RemovePlanType, std::vector<const RemovePlanAction*>> group_by_plan_type;
- Util::group_by(remove_plan, &group_by_plan_type, [](const RemovePlanAction& p) { return p.plan_type; });
- print_plan(group_by_plan_type);
-
- const bool has_non_user_requested_packages =
- Util::find_if(remove_plan, [](const RemovePlanAction& package) -> bool {
- return package.request_type != RequestType::USER_REQUESTED;
- }) != remove_plan.cend();
-
- if (has_non_user_requested_packages)
- {
- System::println(System::Color::warning,
- "Additional packages (*) need to be removed to complete this operation.");
-
- if (!isRecursive)
- {
- System::println(System::Color::warning,
- "If you are sure you want to remove them, run the command with the --recurse option");
- Checks::exit_fail(VCPKG_LINE_INFO);
- }
- }
-
- if (dryRun)
- {
- Checks::exit_success(VCPKG_LINE_INFO);
- }
-
- for (const RemovePlanAction& action : remove_plan)
- {
- const std::string display_name = action.spec.to_string();
-
- switch (action.plan_type)
- {
- case RemovePlanType::NOT_INSTALLED:
- System::println(System::Color::success, "Package %s is not installed", display_name);
- break;
- case RemovePlanType::REMOVE:
- System::println("Removing package %s... ", display_name);
- remove_package(paths, action.spec, &status_db);
- System::println(System::Color::success, "Removing package %s... done", display_name);
- break;
- case RemovePlanType::UNKNOWN:
- default: Checks::unreachable(VCPKG_LINE_INFO);
- }
-
- if (alsoRemoveFolderFromPackages)
- {
- System::println("Purging package %s... ", display_name);
- Files::Filesystem& fs = paths.get_filesystem();
- std::error_code ec;
- fs.remove_all(paths.packages / action.spec.dir(), ec);
- System::println(System::Color::success, "Purging package %s... done", display_name);
- }
- }
-
- Checks::exit_success(VCPKG_LINE_INFO);
- }
-}
diff --git a/toolsrc/src/commands_search.cpp b/toolsrc/src/commands_search.cpp
deleted file mode 100644
index 0ba7305e9..000000000
--- a/toolsrc/src/commands_search.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-#include "pch.h"
-
-#include "Paragraphs.h"
-#include "SourceParagraph.h"
-#include "vcpkg_Commands.h"
-#include "vcpkg_System.h"
-#include "vcpkglib_helpers.h"
-
-namespace vcpkg::Commands::Search
-{
- static const std::string OPTION_GRAPH = "--graph"; // TODO: This should find a better home, eventually
-
- static std::string replace_dashes_with_underscore(const std::string& input)
- {
- std::string output = input;
- std::replace(output.begin(), output.end(), '-', '_');
- return output;
- }
-
- static std::string create_graph_as_string(const std::vector<SourceParagraph>& source_paragraphs)
- {
- int empty_node_count = 0;
-
- std::string s;
- s.append("digraph G{ rankdir=LR; edge [minlen=3]; overlap=false;");
-
- for (const SourceParagraph& source_paragraph : source_paragraphs)
- {
- if (source_paragraph.depends.empty())
- {
- empty_node_count++;
- continue;
- }
-
- const std::string name = replace_dashes_with_underscore(source_paragraph.name);
- s.append(Strings::format("%s;", name));
- for (const Dependency& d : source_paragraph.depends)
- {
- const std::string dependency_name = replace_dashes_with_underscore(d.name);
- s.append(Strings::format("%s -> %s;", name, dependency_name));
- }
- }
-
- s.append(Strings::format("empty [label=\"%d singletons...\"]; }", empty_node_count));
- return s;
- }
-
- static void do_print(const SourceParagraph& source_paragraph)
- {
- System::println("%-20s %-16s %s",
- source_paragraph.name,
- source_paragraph.version,
- details::shorten_description(source_paragraph.description));
- }
-
- void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
- {
- static const std::string example = Strings::format(
- "The argument should be a substring to search for, or no argument to display all libraries.\n%s",
- Commands::Help::create_example_string("search png"));
- args.check_max_arg_count(1, example);
- const std::unordered_set<std::string> options = args.check_and_get_optional_command_arguments({OPTION_GRAPH});
-
- const std::vector<SourceParagraph> source_paragraphs =
- Paragraphs::load_all_ports(paths.get_filesystem(), paths.ports);
- if (options.find(OPTION_GRAPH) != options.cend())
- {
- const std::string graph_as_string = create_graph_as_string(source_paragraphs);
- System::println(graph_as_string);
- Checks::exit_success(VCPKG_LINE_INFO);
- }
-
- if (args.command_arguments.empty())
- {
- for (const SourceParagraph& source_paragraph : source_paragraphs)
- {
- do_print(source_paragraph);
- }
- }
- else
- {
- // At this point there is 1 argument
- for (const SourceParagraph& source_paragraph : source_paragraphs)
- {
- if (Strings::case_insensitive_ascii_find(source_paragraph.name, args.command_arguments[0]) ==
- source_paragraph.name.end())
- {
- continue;
- }
-
- do_print(source_paragraph);
- }
- }
-
- System::println(
- "\nIf your library is not listed, please open an issue at and/or consider making a pull request:\n"
- " https://github.com/Microsoft/vcpkg/issues");
-
- Checks::exit_success(VCPKG_LINE_INFO);
- }
-}
diff --git a/toolsrc/src/commands_update.cpp b/toolsrc/src/commands_update.cpp
deleted file mode 100644
index e39c7dd35..000000000
--- a/toolsrc/src/commands_update.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-#include "pch.h"
-
-#include "Paragraphs.h"
-#include "vcpkg_Commands.h"
-#include "vcpkg_Files.h"
-#include "vcpkg_System.h"
-#include "vcpkglib.h"
-
-namespace vcpkg::Commands::Update
-{
- bool OutdatedPackage::compare_by_name(const OutdatedPackage& left, const OutdatedPackage& right)
- {
- return left.spec.name() < right.spec.name();
- }
-
- std::vector<OutdatedPackage> find_outdated_packages(const VcpkgPaths& paths, const StatusParagraphs& status_db)
- {
- const std::vector<SourceParagraph> source_paragraphs =
- Paragraphs::load_all_ports(paths.get_filesystem(), paths.ports);
- const std::map<std::string, VersionT> src_names_to_versions =
- Paragraphs::extract_port_names_and_versions(source_paragraphs);
- const std::vector<StatusParagraph*> installed_packages = get_installed_ports(status_db);
-
- std::vector<OutdatedPackage> output;
- for (const StatusParagraph* pgh : installed_packages)
- {
- auto it = src_names_to_versions.find(pgh->package.spec.name());
- if (it == src_names_to_versions.end())
- {
- // Package was not installed from portfile
- continue;
- }
- if (it->second != pgh->package.version)
- {
- output.push_back({pgh->package.spec, VersionDiff(pgh->package.version, it->second)});
- }
- }
-
- return output;
- }
-
- void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
- {
- args.check_exact_arg_count(0);
- args.check_and_get_optional_command_arguments({});
- System::println("Using local portfile versions. To update the local portfiles, use `git pull`.");
-
- const StatusParagraphs status_db = database_load_check(paths);
-
- const auto outdated_packages =
- SortedVector<OutdatedPackage>(find_outdated_packages(paths, status_db), &OutdatedPackage::compare_by_name);
-
- if (outdated_packages.empty())
- {
- System::println("No packages need updating.");
- }
- else
- {
- System::println("The following packages differ from their port versions:");
- for (auto&& package : outdated_packages)
- {
- System::println(" %-32s %s", package.spec, package.version_diff.to_string());
- }
- System::println("\n"
- "To update these packages, run\n"
- " .\\vcpkg remove --outdated\n"
- " .\\vcpkg install <pkgs>...");
- }
-
- auto version_file = paths.get_filesystem().read_contents(paths.root / "toolsrc" / "VERSION.txt");
- if (auto version_contents = version_file.get())
- {
- int maj1, min1, rev1;
- auto num1 = sscanf_s(version_contents->c_str(), "\"%d.%d.%d\"", &maj1, &min1, &rev1);
-
- int maj2, min2, rev2;
- auto num2 = sscanf_s(Version::version().c_str(), "%d.%d.%d-", &maj2, &min2, &rev2);
-
- if (num1 == 3 && num2 == 3)
- {
- if (maj1 != maj2 || min1 != min2 || rev1 != rev2)
- {
- System::println("Different source is available for vcpkg (%d.%d.%d -> %d.%d.%d). Use "
- ".\\bootstrap-vcpkg.bat to update.",
- maj2,
- min2,
- rev2,
- maj1,
- min1,
- rev1);
- }
- }
- }
-
- Checks::exit_success(VCPKG_LINE_INFO);
- }
-}
diff --git a/toolsrc/src/commands_version.cpp b/toolsrc/src/commands_version.cpp
deleted file mode 100644
index cd21f2561..000000000
--- a/toolsrc/src/commands_version.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-#include "pch.h"
-
-#include "metrics.h"
-#include "vcpkg_Commands.h"
-#include "vcpkg_System.h"
-
-#define STRINGIFY(...) #__VA_ARGS__
-#define MACRO_TO_STRING(X) STRINGIFY(X)
-
-#define VCPKG_VERSION_AS_STRING MACRO_TO_STRING(VCPKG_VERSION)
-
-namespace vcpkg::Commands::Version
-{
- const std::string& version()
- {
- static const std::string s_version =
-#include "../VERSION.txt"
-
- +std::string(VCPKG_VERSION_AS_STRING)
-#ifndef NDEBUG
- + std::string("-debug")
-#endif
- + std::string(Metrics::get_compiled_metrics_enabled() ? "" : "-external");
- return s_version;
- }
-
- void perform_and_exit(const VcpkgCmdArguments& args)
- {
- args.check_exact_arg_count(0);
- args.check_and_get_optional_command_arguments({});
-
- System::println("Vcpkg package management program version %s\n"
- "\n"
- "See LICENSE.txt for license information.",
- version());
- Checks::exit_success(VCPKG_LINE_INFO);
- }
-}
diff --git a/toolsrc/src/metrics.cpp b/toolsrc/src/metrics.cpp
deleted file mode 100644
index 7992f3e51..000000000
--- a/toolsrc/src/metrics.cpp
+++ /dev/null
@@ -1,393 +0,0 @@
-#include "pch.h"
-
-#include "filesystem_fs.h"
-#include "metrics.h"
-#include "vcpkg_Files.h"
-#include "vcpkg_Strings.h"
-#include "vcpkg_System.h"
-
-namespace vcpkg::Metrics
-{
- static std::string get_current_date_time()
- {
- struct tm newtime;
- std::array<char, 80> date;
- date.fill(0);
-
- struct _timeb timebuffer;
-
- _ftime_s(&timebuffer);
- time_t now = timebuffer.time;
- int milli = timebuffer.millitm;
-
- errno_t err = gmtime_s(&newtime, &now);
- if (err)
- {
- return "";
- }
-
- strftime(&date[0], date.size(), "%Y-%m-%dT%H:%M:%S", &newtime);
- return std::string(&date[0]) + "." + std::to_string(milli) + "Z";
- }
-
- static std::string generate_random_UUID()
- {
- int partSizes[] = {8, 4, 4, 4, 12};
- char uuid[37];
- memset(uuid, 0, sizeof(uuid));
- int num;
- srand(static_cast<int>(time(nullptr)));
- int index = 0;
- for (int part = 0; part < 5; part++)
- {
- if (part > 0)
- {
- uuid[index] = '-';
- index++;
- }
-
- // Generating UUID format version 4
- // http://en.wikipedia.org/wiki/Universally_unique_identifier
- for (int i = 0; i < partSizes[part]; i++, index++)
- {
- if (part == 2 && i == 0)
- {
- num = 4;
- }
- else if (part == 4 && i == 0)
- {
- num = (rand() % 4) + 8;
- }
- else
- {
- num = rand() % 16;
- }
-
- if (num < 10)
- {
- uuid[index] = static_cast<char>('0' + num);
- }
- else
- {
- uuid[index] = static_cast<char>('a' + (num - 10));
- }
- }
- }
-
- return uuid;
- }
-
- static const std::string& get_session_id()
- {
- static const std::string id = generate_random_UUID();
- return id;
- }
-
- static std::string to_json_string(const std::string& str)
- {
- std::string encoded = "\"";
- for (auto&& ch : str)
- {
- if (ch == '\\')
- {
- encoded.append("\\\\");
- }
- else if (ch == '"')
- {
- encoded.append("\\\"");
- }
- else if (ch < 0x20 || ch >= 0x80)
- {
- // Note: this treats incoming Strings as Latin-1
- static constexpr const char hex[16] = {
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
- encoded.append("\\u00");
- encoded.push_back(hex[ch / 16]);
- encoded.push_back(hex[ch % 16]);
- }
- else
- {
- encoded.push_back(ch);
- }
- }
- encoded.push_back('"');
- return encoded;
- }
-
- static std::string get_os_version_string()
- {
- std::wstring path;
- path.resize(MAX_PATH);
- auto n = GetSystemDirectoryW(&path[0], static_cast<UINT>(path.size()));
- path.resize(n);
- path += L"\\kernel32.dll";
-
- auto versz = GetFileVersionInfoSizeW(path.c_str(), nullptr);
- if (versz == 0) return "";
-
- std::vector<char> verbuf;
- verbuf.resize(versz);
-
- if (!GetFileVersionInfoW(path.c_str(), 0, static_cast<DWORD>(verbuf.size()), &verbuf[0])) return "";
-
- void* rootblock;
- UINT rootblocksize;
- if (!VerQueryValueW(&verbuf[0], L"\\", &rootblock, &rootblocksize)) return "";
-
- auto rootblock_ffi = static_cast<VS_FIXEDFILEINFO*>(rootblock);
-
- return Strings::format("%d.%d.%d",
- static_cast<int>(HIWORD(rootblock_ffi->dwProductVersionMS)),
- static_cast<int>(LOWORD(rootblock_ffi->dwProductVersionMS)),
- static_cast<int>(HIWORD(rootblock_ffi->dwProductVersionLS)));
- }
-
- struct MetricMessage
- {
- std::string user_id = generate_random_UUID();
- std::string user_timestamp;
- std::string timestamp = get_current_date_time();
- std::string properties;
- std::string measurements;
-
- void TrackProperty(const std::string& name, const std::string& value)
- {
- if (properties.size() != 0) properties.push_back(',');
- properties.append(to_json_string(name));
- properties.push_back(':');
- properties.append(to_json_string(value));
- }
-
- void TrackMetric(const std::string& name, double value)
- {
- if (measurements.size() != 0) measurements.push_back(',');
- measurements.append(to_json_string(name));
- measurements.push_back(':');
- measurements.append(std::to_string(value));
- }
-
- std::string format_event_data_template() const
- {
- const std::string& session_id = get_session_id();
- return Strings::format(R"([{
- "ver": 1,
- "name": "Microsoft.ApplicationInsights.Event",
- "time": "%s",
- "sampleRate": 100.000000,
- "seq": "0:0",
- "iKey": "b4e88960-4393-4dd9-ab8e-97e8fe6d7603",
- "flags": 0.000000,
- "tags": {
- "ai.device.os": "Windows",
- "ai.device.osVersion": "%s",
- "ai.session.id": "%s",
- "ai.user.id": "%s",
- "ai.user.accountAcquisitionDate": "%s"
- },
- "data": {
- "baseType": "EventData",
- "baseData": {
- "ver": 2,
- "name": "commandline_test7",
- "properties": { %s },
- "measurements": { %s }
- }
- }
-}])",
- timestamp,
- get_os_version_string(),
- session_id,
- user_id,
- user_timestamp,
- properties,
- measurements);
- }
- };
-
- static MetricMessage g_metricmessage;
- static bool g_should_send_metrics =
-#if defined(NDEBUG) && (DISABLE_METRICS == 0)
- true
-#else
- false
-#endif
- ;
- static bool g_should_print_metrics = false;
-
- bool get_compiled_metrics_enabled() { return DISABLE_METRICS == 0; }
-
- std::wstring get_SQM_user()
- {
- auto hkcu_sqmclient =
- System::get_registry_string(HKEY_CURRENT_USER, LR"(Software\Microsoft\SQMClient)", L"UserId");
- return hkcu_sqmclient.value_or(L"{}");
- }
-
- void set_user_information(const std::string& user_id, const std::string& first_use_time)
- {
- g_metricmessage.user_id = user_id;
- g_metricmessage.user_timestamp = first_use_time;
- }
-
- void init_user_information(std::string& user_id, std::string& first_use_time)
- {
- user_id = generate_random_UUID();
- first_use_time = get_current_date_time();
- }
-
- void set_send_metrics(bool should_send_metrics) { g_should_send_metrics = should_send_metrics; }
-
- void set_print_metrics(bool should_print_metrics) { g_should_print_metrics = should_print_metrics; }
-
- void track_metric(const std::string& name, double value) { g_metricmessage.TrackMetric(name, value); }
-
- void track_property(const std::string& name, const std::wstring& value)
- {
- // Note: this is not valid UTF-16 -> UTF-8, it just yields a close enough approximation for our purposes.
- std::string converted_value;
- converted_value.resize(value.size());
- std::transform(
- value.begin(), value.end(), converted_value.begin(), [](wchar_t ch) { return static_cast<char>(ch); });
-
- g_metricmessage.TrackProperty(name, converted_value);
- }
-
- void track_property(const std::string& name, const std::string& value)
- {
- g_metricmessage.TrackProperty(name, value);
- }
-
- void upload(const std::string& payload)
- {
- HINTERNET hSession = nullptr, hConnect = nullptr, hRequest = nullptr;
- BOOL bResults = FALSE;
-
- hSession = WinHttpOpen(
- L"vcpkg/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
- if (hSession)
- hConnect = WinHttpConnect(hSession, L"dc.services.visualstudio.com", INTERNET_DEFAULT_HTTPS_PORT, 0);
-
- if (hConnect)
- hRequest = WinHttpOpenRequest(hConnect,
- L"POST",
- L"/v2/track",
- nullptr,
- WINHTTP_NO_REFERER,
- WINHTTP_DEFAULT_ACCEPT_TYPES,
- WINHTTP_FLAG_SECURE);
-
- if (hRequest)
- {
- if (MAXDWORD <= payload.size()) abort();
- std::wstring hdrs = L"Content-Type: application/json\r\n";
- bResults = WinHttpSendRequest(hRequest,
- hdrs.c_str(),
- static_cast<DWORD>(hdrs.size()),
- (void*)&payload[0],
- static_cast<DWORD>(payload.size()),
- static_cast<DWORD>(payload.size()),
- 0);
- }
-
- if (bResults)
- {
- bResults = WinHttpReceiveResponse(hRequest, nullptr);
- }
-
- DWORD http_code = 0, junk = sizeof(DWORD);
-
- if (bResults)
- {
- bResults = WinHttpQueryHeaders(hRequest,
- WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
- nullptr,
- &http_code,
- &junk,
- WINHTTP_NO_HEADER_INDEX);
- }
-
- std::vector<char> responseBuffer;
- if (bResults)
- {
- DWORD availableData = 0, readData = 0, totalData = 0;
- while ((bResults = WinHttpQueryDataAvailable(hRequest, &availableData)) == TRUE && availableData > 0)
- {
- responseBuffer.resize(responseBuffer.size() + availableData);
-
- bResults = WinHttpReadData(hRequest, &responseBuffer.data()[totalData], availableData, &readData);
-
- if (!bResults)
- {
- break;
- }
-
- totalData += readData;
-
- responseBuffer.resize(totalData);
- }
- }
-
- if (!bResults)
- {
-#ifndef NDEBUG
- __debugbreak();
- auto err = GetLastError();
- std::cerr << "[DEBUG] failed to connect to server: " << err << "\n";
-#endif
- }
-
- if (hRequest) WinHttpCloseHandle(hRequest);
- if (hConnect) WinHttpCloseHandle(hConnect);
- if (hSession) WinHttpCloseHandle(hSession);
- }
-
- static fs::path get_bindir()
- {
- wchar_t buf[_MAX_PATH];
- int bytes = GetModuleFileNameW(nullptr, buf, _MAX_PATH);
- if (bytes == 0) std::abort();
- return fs::path(buf, buf + bytes);
- }
-
- void flush()
- {
- std::string payload = g_metricmessage.format_event_data_template();
- if (g_should_print_metrics) std::cerr << payload << "\n";
- if (!g_should_send_metrics) return;
-
- // upload(payload);
-
- wchar_t temp_folder[MAX_PATH];
- GetTempPathW(MAX_PATH, temp_folder);
-
- const fs::path temp_folder_path = temp_folder;
- const fs::path temp_folder_path_exe = temp_folder_path / "vcpkgmetricsuploader.exe";
-
- auto& fs = Files::get_real_filesystem();
-
- if (true)
- {
- const fs::path exe_path = [&fs]() -> fs::path {
- auto vcpkgdir = get_bindir().parent_path();
- auto path = vcpkgdir / "vcpkgmetricsuploader.exe";
- if (fs.exists(path)) return path;
-
- path = vcpkgdir / "scripts" / "vcpkgmetricsuploader.exe";
- if (fs.exists(path)) return path;
-
- return L"";
- }();
-
- std::error_code ec;
- fs.copy_file(exe_path, temp_folder_path_exe, fs::copy_options::skip_existing, ec);
- if (ec) return;
- }
-
- const fs::path vcpkg_metrics_txt_path = temp_folder_path / ("vcpkg" + generate_random_UUID() + ".txt");
- fs.write_contents(vcpkg_metrics_txt_path, payload);
-
- const std::wstring cmdLine =
- Strings::wformat(L"start %s %s", temp_folder_path_exe.native(), vcpkg_metrics_txt_path.native());
- System::cmd_execute_clean(cmdLine);
- }
-}
diff --git a/toolsrc/src/tests.arguments.cpp b/toolsrc/src/tests.arguments.cpp
new file mode 100644
index 000000000..c87281fa8
--- /dev/null
+++ b/toolsrc/src/tests.arguments.cpp
@@ -0,0 +1,64 @@
+#include "tests.pch.h"
+
+#pragma comment(lib, "version")
+#pragma comment(lib, "winhttp")
+
+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", "--debug", "--sendmetrics", "--printmetrics"};
+ 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::IsTrue(v.debug && *v.debug.get());
+ Assert::IsTrue(v.sendmetrics && v.sendmetrics.get());
+ Assert::IsTrue(v.printmetrics && *v.printmetrics.get());
+ }
+
+ TEST_METHOD(create_from_arg_sequence_options_upper)
+ {
+ std::vector<std::string> t = {"--VCPKG-ROOT", "C:\\vcpkg", "--DEBUG", "--SENDMETRICS", "--PRINTMETRICS"};
+ 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::IsTrue(v.debug && *v.debug.get());
+ Assert::IsTrue(v.sendmetrics && v.sendmetrics.get());
+ Assert::IsTrue(v.printmetrics && *v.printmetrics.get());
+ }
+
+ 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());
+ }
+ };
+} \ No newline at end of file
diff --git a/toolsrc/src/tests.chrono.cpp b/toolsrc/src/tests.chrono.cpp
new file mode 100644
index 000000000..269cdca58
--- /dev/null
+++ b/toolsrc/src/tests.chrono.cpp
@@ -0,0 +1,41 @@
+#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
new file mode 100644
index 000000000..f82fad4e4
--- /dev/null
+++ b/toolsrc/src/tests.dependencies.cpp
@@ -0,0 +1,108 @@
+#include "tests.pch.h"
+
+#pragma comment(lib, "version")
+#pragma comment(lib, "winhttp")
+
+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
new file mode 100644
index 000000000..32ad81227
--- /dev/null
+++ b/toolsrc/src/tests.packagespec.cpp
@@ -0,0 +1,134 @@
+#include "tests.pch.h"
+
+#include <tests.utils.h>
+
+#pragma comment(lib, "version")
+#pragma comment(lib, "winhttp")
+
+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
index b66adc816..9a56ad9ee 100644
--- a/toolsrc/src/tests_paragraph.cpp
+++ b/toolsrc/src/tests.paragraph.cpp
@@ -1,22 +1,10 @@
-#include "BinaryParagraph.h"
-#include "CppUnitTest.h"
-#include "Paragraphs.h"
-#include "vcpkg_Strings.h"
+#include "tests.pch.h"
#pragma comment(lib, "version")
#pragma comment(lib, "winhttp")
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
-namespace Microsoft::VisualStudio::CppUnitTestFramework
-{
- template<>
- inline std::wstring ToString<vcpkg::PackageSpecParseResult>(const vcpkg::PackageSpecParseResult& t)
- {
- return ToString(static_cast<uint32_t>(t));
- }
-}
-
namespace Strings = vcpkg::Strings;
namespace UnitTest1
@@ -25,70 +13,143 @@ namespace UnitTest1
{
TEST_METHOD(SourceParagraph_Construct_Minimum)
{
- vcpkg::SourceParagraph pgh({{"Source", "zlib"}, {"Version", "1.2.8"}});
-
- Assert::AreEqual("zlib", pgh.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(size_t(0), pgh.depends.size());
+ 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)
{
- vcpkg::SourceParagraph pgh({{"Source", "s"},
- {"Version", "v"},
- {"Maintainer", "m"},
- {"Description", "d"},
- {"Build-Depends", "bd"}});
- Assert::AreEqual("s", pgh.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].name.c_str());
+ 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)
{
- vcpkg::SourceParagraph pgh({{"Source", "zlib"}, {"Version", "1.2.8"}, {"Build-Depends", "z, openssl"}});
-
- Assert::AreEqual(size_t(2), pgh.depends.size());
- Assert::AreEqual("z", pgh.depends[0].name.c_str());
- Assert::AreEqual("openssl", pgh.depends[1].name.c_str());
+ 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)
{
- vcpkg::SourceParagraph pgh(
- {{"Source", "zlib"}, {"Version", "1.2.8"}, {"Build-Depends", "z, openssl, xyz"}});
+ 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());
+ }
- Assert::AreEqual(size_t(3), pgh.depends.size());
- Assert::AreEqual("z", pgh.depends[0].name.c_str());
- Assert::AreEqual("openssl", pgh.depends[1].name.c_str());
- Assert::AreEqual("xyz", pgh.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)
{
- vcpkg::SourceParagraph pgh(
- {{"Source", "zlib"}, {"Version", "1.2.8"}, {"Build-Depends", "libA [windows], libB [uwp]"}});
+ 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());
+ }
- Assert::AreEqual("zlib", pgh.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(size_t(2), pgh.depends.size());
- Assert::AreEqual("libA", pgh.depends[0].name.c_str());
- Assert::AreEqual("windows", pgh.depends[0].qualifier.c_str());
- Assert::AreEqual("libB", pgh.depends[1].name.c_str());
- Assert::AreEqual("uwp", pgh.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"},
+ {"Package", "zlib"},
+ {"Version", "1.2.8"},
+ {"Architecture", "x86-windows"},
+ {"Multi-Arch", "same"},
});
Assert::AreEqual("zlib", pgh.spec.name().c_str());
@@ -101,13 +162,15 @@ namespace UnitTest1
TEST_METHOD(BinaryParagraph_Construct_Maximum)
{
- vcpkg::BinaryParagraph pgh({{"Package", "s"},
- {"Version", "v"},
- {"Architecture", "x86-windows"},
- {"Multi-Arch", "same"},
- {"Maintainer", "m"},
- {"Description", "d"},
- {"Depends", "bd"}});
+ 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());
@@ -132,6 +195,35 @@ namespace UnitTest1
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 = "";
@@ -275,7 +367,10 @@ namespace UnitTest1
TEST_METHOD(BinaryParagraph_serialize_min)
{
vcpkg::BinaryParagraph pgh({
- {"Package", "zlib"}, {"Version", "1.2.8"}, {"Architecture", "x86-windows"}, {"Multi-Arch", "same"},
+ {"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);
@@ -325,44 +420,20 @@ namespace UnitTest1
Assert::AreEqual("a, b, c", pghs[0]["Depends"].c_str());
}
- TEST_METHOD(package_spec_parse)
- {
- vcpkg::Expected<vcpkg::PackageSpec> spec =
- vcpkg::PackageSpec::from_string("zlib", vcpkg::Triplet::X86_WINDOWS);
- Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS,
- vcpkg::to_package_spec_parse_result(spec.error_code()));
- Assert::AreEqual("zlib", spec.get()->name().c_str());
- Assert::AreEqual(vcpkg::Triplet::X86_WINDOWS.canonical_name(), spec.get()->triplet().canonical_name());
- }
-
- TEST_METHOD(package_spec_parse_with_arch)
- {
- vcpkg::Expected<vcpkg::PackageSpec> spec =
- vcpkg::PackageSpec::from_string("zlib:x64-uwp", vcpkg::Triplet::X86_WINDOWS);
- Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS,
- vcpkg::to_package_spec_parse_result(spec.error_code()));
- Assert::AreEqual("zlib", spec.get()->name().c_str());
- Assert::AreEqual(vcpkg::Triplet::X64_UWP.canonical_name(), spec.get()->triplet().canonical_name());
- }
-
- TEST_METHOD(package_spec_parse_with_multiple_colon)
- {
- auto ec = vcpkg::PackageSpec::from_string("zlib:x86-uwp:", vcpkg::Triplet::X86_WINDOWS).error_code();
- Assert::AreEqual(vcpkg::PackageSpecParseResult::TOO_MANY_COLONS, vcpkg::to_package_spec_parse_result(ec));
- }
-
- TEST_METHOD(utf8_to_utf16)
+ TEST_METHOD(BinaryParagraph_serialize_abi)
{
- 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());
+ 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());
}
};
-
- TEST_CLASS(Metrics){};
}
diff --git a/toolsrc/src/tests.pch.cpp b/toolsrc/src/tests.pch.cpp
new file mode 100644
index 000000000..bdddab76a
--- /dev/null
+++ b/toolsrc/src/tests.pch.cpp
@@ -0,0 +1 @@
+#include "tests.pch.h"
diff --git a/toolsrc/src/tests.plan.cpp b/toolsrc/src/tests.plan.cpp
new file mode 100644
index 000000000..238aa7032
--- /dev/null
+++ b/toolsrc/src/tests.plan.cpp
@@ -0,0 +1,1258 @@
+#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());
+
+ Assert::AreEqual(pkg_name.c_str(), plan.source_control_file.get()->core_paragraph->name.c_str());
+ Assert::AreEqual(size_t(vec.size()), feature_list.size());
+
+ for (auto&& feature_name : vec)
+ {
+ if (feature_name == "core" || feature_name == "")
+ {
+ Assert::IsTrue(Util::find(feature_list, "core") != feature_list.end() ||
+ Util::find(feature_list, "") != feature_list.end());
+ continue;
+ }
+ Assert::IsTrue(Util::find(feature_list, feature_name) != feature_list.end());
+ }
+ }
+
+ /// <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, SourceControlFile> 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 = {})
+ {
+ return emplace(std::move(*make_control_file(name, depends, features, default_features)));
+ }
+ PackageSpec emplace(vcpkg::SourceControlFile&& scf)
+ {
+ auto spec = PackageSpec::from_name_and_triplet(scf.core_paragraph->name, triplet);
+ Assert::IsTrue(spec.has_value());
+ map.emplace(scf.core_paragraph->name, std::move(scf));
+ return PackageSpec{*spec.get()};
+ }
+ };
+
+ 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
new file mode 100644
index 000000000..fa0d54fac
--- /dev/null
+++ b/toolsrc/src/tests.statusparagraphs.cpp
@@ -0,0 +1,115 @@
+#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
new file mode 100644
index 000000000..b6e487c17
--- /dev/null
+++ b/toolsrc/src/tests.update.cpp
@@ -0,0 +1,106 @@
+#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, SourceControlFile> map;
+ auto scf = unwrap(SourceControlFile::parse_control_file(Pgh{{{"Source", "a"}, {"Version", "0"}}}));
+ map.emplace("a", 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, SourceControlFile> map;
+ auto scf = unwrap(SourceControlFile::parse_control_file(Pgh{{{"Source", "a"}, {"Version", "0"}}}));
+ map.emplace("a", 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, SourceControlFile> map;
+ auto scf = unwrap(SourceControlFile::parse_control_file(Pgh{{{"Source", "a"}, {"Version", "0"}}}));
+ map.emplace("a", 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, SourceControlFile> map;
+ auto scf = unwrap(SourceControlFile::parse_control_file(Pgh{{{"Source", "a"}, {"Version", "2"}}}));
+ map.emplace("a", 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
new file mode 100644
index 000000000..ac391f559
--- /dev/null
+++ b/toolsrc/src/tests.utils.cpp
@@ -0,0 +1,42 @@
+#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/tests_arguments.cpp b/toolsrc/src/tests_arguments.cpp
deleted file mode 100644
index 624fbb910..000000000
--- a/toolsrc/src/tests_arguments.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-#include "CppUnitTest.h"
-#include "VcpkgCmdArguments.h"
-
-#pragma comment(lib, "version")
-#pragma comment(lib, "winhttp")
-
-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", "--debug", "--sendmetrics", "--printmetrics"};
- 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::IsTrue(v.debug && *v.debug.get());
- Assert::IsTrue(v.sendmetrics && v.sendmetrics.get());
- Assert::IsTrue(v.printmetrics && *v.printmetrics.get());
- }
-
- TEST_METHOD(create_from_arg_sequence_options_upper)
- {
- std::vector<std::string> t = {"--VCPKG-ROOT", "C:\\vcpkg", "--DEBUG", "--SENDMETRICS", "--PRINTMETRICS"};
- 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::IsTrue(v.debug && *v.debug.get());
- Assert::IsTrue(v.sendmetrics && v.sendmetrics.get());
- Assert::IsTrue(v.printmetrics && *v.printmetrics.get());
- }
- };
-} \ No newline at end of file
diff --git a/toolsrc/src/tests_dependencies.cpp b/toolsrc/src/tests_dependencies.cpp
deleted file mode 100644
index 3aabed80e..000000000
--- a/toolsrc/src/tests_dependencies.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-#include "CppUnitTest.h"
-#include "SourceParagraph.h"
-#include "Triplet.h"
-
-#pragma comment(lib, "version")
-#pragma comment(lib, "winhttp")
-
-using namespace Microsoft::VisualStudio::CppUnitTestFramework;
-
-using namespace vcpkg;
-
-namespace UnitTest1
-{
- class DependencyTests : public TestClass<DependencyTests>
- {
- TEST_METHOD(parse_depends_one)
- {
- auto v = expand_qualified_dependencies(parse_depends("libA [windows]"));
- Assert::AreEqual(size_t(1), v.size());
- Assert::AreEqual("libA", v[0].name.c_str());
- Assert::AreEqual("windows", v[0].qualifier.c_str());
- }
-
- TEST_METHOD(filter_depends)
- {
- auto deps = expand_qualified_dependencies(parse_depends("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());
- }
- };
-}
diff --git a/toolsrc/src/triplet.cpp b/toolsrc/src/triplet.cpp
deleted file mode 100644
index ff41ce77d..000000000
--- a/toolsrc/src/triplet.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-#include "pch.h"
-
-#include "Triplet.h"
-#include "vcpkg_Checks.h"
-#include "vcpkg_Strings.h"
-
-namespace vcpkg
-{
- const Triplet Triplet::X86_WINDOWS = from_canonical_name("x86-windows");
- const Triplet Triplet::X64_WINDOWS = from_canonical_name("x64-windows");
- const Triplet Triplet::X86_UWP = from_canonical_name("x86-uwp");
- const Triplet Triplet::X64_UWP = from_canonical_name("x64-uwp");
- const Triplet Triplet::ARM_UWP = from_canonical_name("arm-uwp");
-
- bool operator==(const Triplet& left, const Triplet& right)
- {
- return left.canonical_name() == right.canonical_name();
- }
-
- bool operator!=(const Triplet& left, const Triplet& right) { return !(left == right); }
-
- Triplet Triplet::from_canonical_name(const std::string& triplet_as_string)
- {
- const std::string s(Strings::ascii_to_lowercase(triplet_as_string));
- auto it = std::find(s.cbegin(), s.cend(), '-');
- Checks::check_exit(VCPKG_LINE_INFO, it != s.cend(), "Invalid triplet: %s", triplet_as_string);
-
- Triplet t;
- t.m_canonical_name = s;
- return t;
- }
-
- const std::string& Triplet::canonical_name() const { return this->m_canonical_name; }
-
- const std::string& Triplet::to_string() const { return this->m_canonical_name; }
-}
diff --git a/toolsrc/src/vcpkg.cpp b/toolsrc/src/vcpkg.cpp
index 154aefd0a..ac2eec876 100644
--- a/toolsrc/src/vcpkg.cpp
+++ b/toolsrc/src/vcpkg.cpp
@@ -1,77 +1,148 @@
+#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
-#include "Paragraphs.h"
-#include "metrics.h"
-#include "vcpkg_Chrono.h"
-#include "vcpkg_Commands.h"
-#include "vcpkg_Files.h"
-#include "vcpkg_Input.h"
-#include "vcpkg_Strings.h"
-#include "vcpkg_System.h"
-#include "vcpkglib.h"
-#include <Shlobj.h>
+#pragma warning(push)
+#pragma warning(disable : 4768)
+#include <ShlObj.h>
+#pragma warning(pop)
+#else
+#include <unistd.h>
+#endif
+
+#include <vcpkg/base/chrono.h>
+#include <vcpkg/base/files.h>
+#include <vcpkg/base/strings.h>
+#include <vcpkg/base/system.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/globalstate.h>
+#include <vcpkg/help.h>
+#include <vcpkg/input.h>
+#include <vcpkg/metrics.h>
+#include <vcpkg/paragraphs.h>
+#include <vcpkg/userconfig.h>
+#include <vcpkg/vcpkglib.h>
+
#include <cassert>
#include <fstream>
#include <memory>
+#include <random>
+
+#pragma comment(lib, "ole32")
+#pragma comment(lib, "shell32")
using namespace vcpkg;
void invalid_command(const std::string& cmd)
{
System::println(System::Color::error, "invalid command: %s", cmd);
- Commands::Help::print_usage();
+ Help::print_usage();
Checks::exit_fail(VCPKG_LINE_INFO);
}
static void inner(const VcpkgCmdArguments& args)
{
- Metrics::track_property("command", args.command);
+ Metrics::g_metrics.lock()->track_property("command", args.command);
if (args.command.empty())
{
- Commands::Help::print_usage();
+ Help::print_usage();
Checks::exit_fail(VCPKG_LINE_INFO);
}
- if (auto command_function = Commands::find(args.command, Commands::get_available_commands_type_c()))
+ static const auto find_command = [&](auto&& commands) {
+ auto it = Util::find_if(commands, [&](auto&& commandc) {
+ return Strings::case_insensitive_ascii_equals(commandc.name, args.command);
+ });
+ using std::end;
+ if (it != end(commands))
+ {
+ return &*it;
+ }
+ else
+ return static_cast<decltype(&*it)>(nullptr);
+ };
+
+ if (const auto command_function = find_command(Commands::get_available_commands_type_c()))
{
- return command_function(args);
+ return command_function->function(args);
}
fs::path vcpkg_root_dir;
if (args.vcpkg_root_dir != nullptr)
{
- vcpkg_root_dir = fs::stdfs::absolute(Strings::to_utf16(*args.vcpkg_root_dir));
+ vcpkg_root_dir = fs::stdfs::absolute(fs::u8path(*args.vcpkg_root_dir));
}
else
{
- const Optional<std::wstring> vcpkg_root_dir_env = System::get_environment_variable(L"VCPKG_ROOT");
- if (auto v = vcpkg_root_dir_env.get())
+ const auto vcpkg_root_dir_env = System::get_environment_variable("VCPKG_ROOT");
+ if (const auto v = vcpkg_root_dir_env.get())
{
vcpkg_root_dir = fs::stdfs::absolute(*v);
}
else
{
- vcpkg_root_dir = Files::get_real_filesystem().find_file_recursively_up(
- fs::stdfs::absolute(System::get_exe_path_of_current_process()), ".vcpkg-root");
+ const fs::path current_path = fs::stdfs::current_path();
+ vcpkg_root_dir = Files::get_real_filesystem().find_file_recursively_up(current_path, ".vcpkg-root");
+
+ if (vcpkg_root_dir.empty())
+ {
+ vcpkg_root_dir = Files::get_real_filesystem().find_file_recursively_up(
+ fs::stdfs::absolute(System::get_exe_path_of_current_process()), ".vcpkg-root");
+ }
}
}
Checks::check_exit(VCPKG_LINE_INFO, !vcpkg_root_dir.empty(), "Error: Could not detect vcpkg-root.");
- const Expected<VcpkgPaths> expected_paths = VcpkgPaths::create(vcpkg_root_dir);
+ Debug::println("Using vcpkg-root: %s", vcpkg_root_dir.u8string());
+
+ auto default_vs_path = System::get_environment_variable("VCPKG_DEFAULT_VS_PATH").value_or("");
+
+ const Expected<VcpkgPaths> expected_paths = VcpkgPaths::create(vcpkg_root_dir, default_vs_path);
Checks::check_exit(VCPKG_LINE_INFO,
- !expected_paths.error_code(),
+ !expected_paths.error(),
"Error: Invalid vcpkg root directory %s: %s",
vcpkg_root_dir.string(),
- expected_paths.error_code().message());
+ expected_paths.error().message());
const VcpkgPaths paths = expected_paths.value_or_exit(VCPKG_LINE_INFO);
- int exit_code = _wchdir(paths.root.c_str());
+
+#if defined(_WIN32)
+ const int exit_code = _wchdir(paths.root.c_str());
+#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");
- if (auto command_function = Commands::find(args.command, Commands::get_available_commands_type_b()))
+ if (args.command != "autocomplete")
+ {
+ Commands::Version::warn_if_vcpkg_version_mismatch(paths);
+ std::string surveydate = *GlobalState::g_surveydate.lock();
+ auto maybe_surveydate = Chrono::CTime::parse(surveydate);
+ if (auto p_surveydate = maybe_surveydate.get())
+ {
+ auto delta = std::chrono::system_clock::now() - p_surveydate->to_time_point();
+ // 24 hours/day * 30 days/month
+ if (std::chrono::duration_cast<std::chrono::hours>(delta).count() > 24 * 30)
+ {
+ std::default_random_engine generator(
+ static_cast<unsigned int>(std::chrono::system_clock::now().time_since_epoch().count()));
+ std::uniform_int_distribution<int> distribution(1, 4);
+
+ if (distribution(generator) == 1)
+ {
+ Metrics::g_metrics.lock()->track_property("surveyprompt", "true");
+ System::println(
+ System::Color::success,
+ "Your feedback is important to improve Vcpkg! Please take 3 minutes to complete our survey "
+ "by running: vcpkg contact --survey");
+ }
+ }
+ }
+ }
+
+ if (const auto command_function = find_command(Commands::get_available_commands_type_b()))
{
- return command_function(args, paths);
+ return command_function->function(args, paths);
}
Triplet default_triplet;
@@ -81,92 +152,84 @@ static void inner(const VcpkgCmdArguments& args)
}
else
{
- const Optional<std::wstring> vcpkg_default_triplet_env =
- System::get_environment_variable(L"VCPKG_DEFAULT_TRIPLET");
- if (auto v = vcpkg_default_triplet_env.get())
+ const auto vcpkg_default_triplet_env = System::get_environment_variable("VCPKG_DEFAULT_TRIPLET");
+ if (const auto v = vcpkg_default_triplet_env.get())
{
- default_triplet = Triplet::from_canonical_name(Strings::to_utf8(*v));
+ default_triplet = Triplet::from_canonical_name(*v);
}
else
{
+#if defined(_WIN32)
default_triplet = Triplet::X86_WINDOWS;
+#elif defined(__APPLE__)
+ default_triplet = Triplet::from_canonical_name("x64-osx");
+#elif defined(__FreeBSD__)
+ default_triplet = Triplet::from_canonical_name("x64-freebsd");
+#else
+ default_triplet = Triplet::from_canonical_name("x64-linux");
+#endif
}
}
Input::check_triplet(default_triplet, paths);
- if (auto command_function = Commands::find(args.command, Commands::get_available_commands_type_a()))
+ if (const auto command_function = find_command(Commands::get_available_commands_type_a()))
{
- return command_function(args, paths, default_triplet);
+ return command_function->function(args, paths, default_triplet);
}
return invalid_command(args.command);
}
-static void loadConfig()
+static void load_config()
{
- fs::path localappdata;
- {
- // Config path in AppDataLocal
- wchar_t* localappdatapath = nullptr;
- if (S_OK != SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &localappdatapath)) __fastfail(1);
- localappdata = localappdatapath;
- CoTaskMemFree(localappdatapath);
- }
+ auto& fs = Files::get_real_filesystem();
- try
- {
- auto maybe_pghs = Paragraphs::get_paragraphs(Files::get_real_filesystem(), localappdata / "vcpkg" / "config");
- if (auto p_pghs = maybe_pghs.get())
- {
- const auto& pghs = *p_pghs;
+ auto config = UserConfig::try_read_data(fs);
- std::unordered_map<std::string, std::string> keys;
- if (pghs.size() > 0) keys = pghs[0];
+ bool write_config = false;
- for (size_t x = 1; x < pghs.size(); ++x)
- {
- for (auto&& p : pghs[x])
- keys.insert(p);
- }
+ // config file not found, could not be read, or invalid
+ if (config.user_id.empty() || config.user_time.empty())
+ {
+ ::vcpkg::Metrics::Metrics::init_user_information(config.user_id, config.user_time);
+ write_config = true;
+ }
- auto user_id = keys["User-Id"];
- auto user_time = keys["User-Since"];
- if (!user_id.empty() && !user_time.empty())
- {
- Metrics::set_user_information(user_id, user_time);
- return;
- }
- }
+#if defined(_WIN32)
+ if (config.user_mac.empty())
+ {
+ config.user_mac = Metrics::get_MAC_user();
+ write_config = true;
}
- catch (...)
+#endif
+
{
+ auto locked_metrics = Metrics::g_metrics.lock();
+ locked_metrics->set_user_information(config.user_id, config.user_time);
+#if defined(_WIN32)
+ locked_metrics->track_property("user_mac", config.user_mac);
+#endif
}
- // config file not found, could not be read, or invalid
- std::string user_id, user_time;
- Metrics::init_user_information(user_id, user_time);
- Metrics::set_user_information(user_id, user_time);
- try
+ if (config.last_completed_survey.empty())
{
- std::error_code ec;
- auto& fs = Files::get_real_filesystem();
- fs.create_directory(localappdata / "vcpkg", ec);
- fs.write_contents(localappdata / "vcpkg" / "config",
- Strings::format("User-Id: %s\n"
- "User-Since: %s\n",
- user_id,
- user_time));
+ config.last_completed_survey = config.user_time;
}
- catch (...)
+
+ GlobalState::g_surveydate.lock()->assign(config.last_completed_survey);
+
+ if (write_config)
{
+ config.try_write_data(fs);
}
}
+#if defined(_WIN32)
static std::string trim_path_from_command_line(const std::string& full_command_line)
{
Checks::check_exit(
- VCPKG_LINE_INFO, full_command_line.size() > 0, "Internal failure - cannot have empty command line");
+ VCPKG_LINE_INFO, !full_command_line.empty(), "Internal failure - cannot have empty command line");
if (full_command_line[0] == '"')
{
@@ -183,35 +246,56 @@ static std::string trim_path_from_command_line(const std::string& full_command_l
++it;
return std::string(it, full_command_line.cend());
}
+#endif
-static ElapsedTime g_timer;
-
+#if defined(_WIN32)
int wmain(const int argc, const wchar_t* const* const argv)
+#else
+int main(const int argc, const char* const* const argv)
+#endif
{
if (argc == 0) std::abort();
- g_timer = ElapsedTime::create_started();
- atexit([]() {
- auto elapsed_us = g_timer.microseconds();
- Metrics::track_metric("elapsed_us", elapsed_us);
- g_debugging = false;
- Metrics::flush();
- });
+ *GlobalState::timer.lock() = Chrono::ElapsedTimer::create_started();
- Metrics::track_property("version", Commands::Version::version());
+#if defined(_WIN32)
+ GlobalState::g_init_console_cp = GetConsoleCP();
+ GlobalState::g_init_console_output_cp = GetConsoleOutputCP();
+
+ SetConsoleCP(CP_UTF8);
+ SetConsoleOutputCP(CP_UTF8);
const std::string trimmed_command_line = trim_path_from_command_line(Strings::to_utf8(GetCommandLineW()));
- Metrics::track_property("cmdline", trimmed_command_line);
- loadConfig();
- Metrics::track_property("sqmuser", Metrics::get_SQM_user());
+#endif
+
+ {
+ auto locked_metrics = Metrics::g_metrics.lock();
+ locked_metrics->track_property("version", Commands::Version::version());
+#if defined(_WIN32)
+ locked_metrics->track_property("cmdline", trimmed_command_line);
+#endif
+ }
+ load_config();
+
+ const auto vcpkg_feature_flags_env = System::get_environment_variable("VCPKG_FEATURE_FLAGS");
+ if (const auto v = vcpkg_feature_flags_env.get())
+ {
+ auto flags = Strings::split(*v, ",");
+ if (std::find(flags.begin(), flags.end(), "binarycaching") != flags.end()) GlobalState::g_binary_caching = true;
+ }
const VcpkgCmdArguments args = VcpkgCmdArguments::create_from_command_line(argc, argv);
- if (auto p = args.printmetrics.get()) Metrics::set_print_metrics(*p);
- if (auto p = args.sendmetrics.get()) Metrics::set_send_metrics(*p);
- if (auto p = args.debug.get()) g_debugging = *p;
+ if (const auto p = args.featurepackages.get()) GlobalState::feature_packages = *p;
+ if (const auto p = args.binarycaching.get()) GlobalState::g_binary_caching = *p;
+
+ if (const auto p = args.printmetrics.get()) Metrics::g_metrics.lock()->set_print_metrics(*p);
+ if (const auto p = args.sendmetrics.get()) Metrics::g_metrics.lock()->set_send_metrics(*p);
+ if (const auto p = args.debug.get()) GlobalState::debugging = *p;
- if (g_debugging)
+ Checks::register_console_ctrl_handler();
+
+ if (GlobalState::debugging)
{
inner(args);
Checks::exit_fail(VCPKG_LINE_INFO);
@@ -231,7 +315,7 @@ int wmain(const int argc, const wchar_t* const* const argv)
{
exc_msg = "unknown error(...)";
}
- Metrics::track_property("error", exc_msg);
+ Metrics::g_metrics.lock()->track_property("error", exc_msg);
fflush(stdout);
System::print("vcpkg.exe has crashed.\n"
@@ -247,6 +331,12 @@ int wmain(const int argc, const wchar_t* const* const argv)
exc_msg);
fflush(stdout);
for (int x = 0; x < argc; ++x)
+ {
+#if defined(_WIN32)
System::println("%s|", Strings::to_utf8(argv[x]));
+#else
+ System::println("%s|", argv[x]);
+#endif
+ }
fflush(stdout);
}
diff --git a/toolsrc/src/vcpkg/base/checks.cpp b/toolsrc/src/vcpkg/base/checks.cpp
new file mode 100644
index 000000000..2ac5f9a15
--- /dev/null
+++ b/toolsrc/src/vcpkg/base/checks.cpp
@@ -0,0 +1,96 @@
+#include "pch.h"
+
+#include <vcpkg/globalstate.h>
+#include <vcpkg/metrics.h>
+
+#include <vcpkg/base/checks.h>
+#include <vcpkg/base/system.h>
+
+namespace vcpkg::Checks
+{
+ [[noreturn]] static void cleanup_and_exit(const int exit_code)
+ {
+ static std::atomic<bool> have_entered{false};
+ if (have_entered) std::terminate();
+ have_entered = true;
+
+ const auto elapsed_us = GlobalState::timer.lock()->microseconds();
+
+ Debug::println("Exiting after %d us", static_cast<int>(elapsed_us));
+
+ auto metrics = Metrics::g_metrics.lock();
+ metrics->track_metric("elapsed_us", elapsed_us);
+ GlobalState::debugging = false;
+ metrics->flush();
+
+#if defined(_WIN32)
+ SetConsoleCP(GlobalState::g_init_console_cp);
+ SetConsoleOutputCP(GlobalState::g_init_console_output_cp);
+#endif
+
+ fflush(nullptr);
+
+#if defined(_WIN32)
+ ::TerminateProcess(::GetCurrentProcess(), exit_code);
+#else
+ std::exit(exit_code);
+#endif
+ }
+
+#if defined(_WIN32)
+ static BOOL ctrl_handler(DWORD fdw_ctrl_type)
+ {
+ {
+ auto locked_metrics = Metrics::g_metrics.lock();
+ locked_metrics->track_property("CtrlHandler", std::to_string(fdw_ctrl_type));
+ locked_metrics->track_property("error", "CtrlHandler was fired.");
+ }
+ cleanup_and_exit(EXIT_FAILURE);
+ }
+
+ void register_console_ctrl_handler()
+ {
+ SetConsoleCtrlHandler(reinterpret_cast<PHANDLER_ROUTINE>(ctrl_handler), TRUE);
+ }
+#else
+ void register_console_ctrl_handler() {}
+#endif
+ void unreachable(const LineInfo& line_info)
+ {
+ System::println(System::Color::error, "Error: Unreachable code was reached");
+ System::println(System::Color::error, line_info.to_string()); // Always print line_info here
+#ifndef NDEBUG
+ std::abort();
+#else
+ cleanup_and_exit(EXIT_FAILURE);
+#endif
+ }
+
+ void exit_with_code(const LineInfo& line_info, const int exit_code)
+ {
+ Debug::println(System::Color::error, line_info.to_string());
+ cleanup_and_exit(exit_code);
+ }
+
+ void exit_with_message(const LineInfo& line_info, const CStringView error_message)
+ {
+ System::println(System::Color::error, error_message);
+ exit_fail(line_info);
+ }
+
+ void check_exit(const LineInfo& line_info, bool expression)
+ {
+ if (!expression)
+ {
+ exit_fail(line_info);
+ }
+ }
+
+ void check_exit(const LineInfo& line_info, bool expression, const CStringView error_message)
+ {
+ if (!expression)
+ {
+ exit_with_message(line_info, error_message);
+ }
+ }
+}
diff --git a/toolsrc/src/vcpkg/base/chrono.cpp b/toolsrc/src/vcpkg/base/chrono.cpp
new file mode 100644
index 000000000..2a76f5df0
--- /dev/null
+++ b/toolsrc/src/vcpkg/base/chrono.cpp
@@ -0,0 +1,129 @@
+#include "pch.h"
+
+#include <vcpkg/base/checks.h>
+#include <vcpkg/base/chrono.h>
+
+namespace vcpkg::Chrono
+{
+ static std::string format_time_userfriendly(const std::chrono::nanoseconds& nanos)
+ {
+ using std::chrono::duration_cast;
+ using std::chrono::hours;
+ using std::chrono::microseconds;
+ using std::chrono::milliseconds;
+ using std::chrono::minutes;
+ using std::chrono::nanoseconds;
+ using std::chrono::seconds;
+
+ const auto nanos_as_double = static_cast<double>(nanos.count());
+
+ if (duration_cast<hours>(nanos) > hours())
+ {
+ const auto t = nanos_as_double / duration_cast<nanoseconds>(hours(1)).count();
+ return Strings::format("%.4g h", t);
+ }
+
+ if (duration_cast<minutes>(nanos) > minutes())
+ {
+ const auto t = nanos_as_double / duration_cast<nanoseconds>(minutes(1)).count();
+ return Strings::format("%.4g min", t);
+ }
+
+ if (duration_cast<seconds>(nanos) > seconds())
+ {
+ const auto t = nanos_as_double / duration_cast<nanoseconds>(seconds(1)).count();
+ return Strings::format("%.4g s", t);
+ }
+
+ if (duration_cast<milliseconds>(nanos) > milliseconds())
+ {
+ const auto t = nanos_as_double / duration_cast<nanoseconds>(milliseconds(1)).count();
+ return Strings::format("%.4g ms", t);
+ }
+
+ if (duration_cast<microseconds>(nanos) > microseconds())
+ {
+ const auto t = nanos_as_double / duration_cast<nanoseconds>(microseconds(1)).count();
+ return Strings::format("%.4g us", t);
+ }
+
+ return Strings::format("%.4g ns", nanos_as_double);
+ }
+
+ ElapsedTimer ElapsedTimer::create_started()
+ {
+ ElapsedTimer t;
+ t.m_start_tick = std::chrono::high_resolution_clock::now();
+ return t;
+ }
+
+ std::string ElapsedTime::to_string() const { return format_time_userfriendly(as<std::chrono::nanoseconds>()); }
+
+ std::string ElapsedTimer::to_string() const { return elapsed().to_string(); }
+
+ Optional<CTime> CTime::get_current_date_time()
+ {
+ CTime ret;
+
+#if defined(_WIN32)
+ struct _timeb timebuffer;
+
+ _ftime_s(&timebuffer);
+
+ const errno_t err = gmtime_s(&ret.m_tm, &timebuffer.time);
+
+ if (err)
+ {
+ return nullopt;
+ }
+#else
+ time_t now = {0};
+ time(&now);
+ auto null_if_failed = gmtime_r(&now, &ret.m_tm);
+ if (null_if_failed == nullptr)
+ {
+ return nullopt;
+ }
+#endif
+
+ return ret;
+ }
+
+ Optional<CTime> CTime::parse(CStringView str)
+ {
+ CTime ret;
+ const auto assigned =
+#if defined(_WIN32)
+ sscanf_s
+#else
+ sscanf
+#endif
+ (str.c_str(),
+ "%d-%d-%dT%d:%d:%d.",
+ &ret.m_tm.tm_year,
+ &ret.m_tm.tm_mon,
+ &ret.m_tm.tm_mday,
+ &ret.m_tm.tm_hour,
+ &ret.m_tm.tm_min,
+ &ret.m_tm.tm_sec);
+ if (assigned != 6) return nullopt;
+ if (ret.m_tm.tm_year < 1900) return nullopt;
+ ret.m_tm.tm_year -= 1900;
+ if (ret.m_tm.tm_mon < 1) return nullopt;
+ ret.m_tm.tm_mon -= 1;
+ mktime(&ret.m_tm);
+ return ret;
+ }
+
+ std::string CTime::to_string() const
+ {
+ std::array<char, 80> date{};
+ strftime(&date[0], date.size(), "%Y-%m-%dT%H:%M:%S.0Z", &m_tm);
+ return &date[0];
+ }
+ std::chrono::system_clock::time_point CTime::to_time_point() const
+ {
+ const time_t t = mktime(&m_tm);
+ return std::chrono::system_clock::from_time_t(t);
+ }
+}
diff --git a/toolsrc/src/coff_file_reader.cpp b/toolsrc/src/vcpkg/base/cofffilereader.cpp
index 467fc9a64..2c09e2c19 100644
--- a/toolsrc/src/coff_file_reader.cpp
+++ b/toolsrc/src/vcpkg/base/cofffilereader.cpp
@@ -1,12 +1,14 @@
#include "pch.h"
-#include "coff_file_reader.h"
-#include "vcpkg_Checks.h"
+#include <vcpkg/base/checks.h>
+#include <vcpkg/base/cofffilereader.h>
+#include <vcpkg/base/stringliteral.h>
using namespace std;
-namespace vcpkg::COFFFileReader
+namespace vcpkg::CoffFileReader
{
+#if defined(_WIN32)
template<class T>
static T reinterpret_bytes(const char* data)
{
@@ -24,7 +26,7 @@ namespace vcpkg::COFFFileReader
template<class T>
static T peek_value_from_stream(fstream& fs)
{
- fpos_t original_pos = fs.tellg().seekpos();
+ const std::streampos original_pos = fs.tellg();
T data;
fs.read(reinterpret_cast<char*>(&data), sizeof data);
fs.seekg(original_pos);
@@ -42,21 +44,21 @@ namespace vcpkg::COFFFileReader
actual);
}
- static void read_and_verify_PE_signature(fstream& fs)
+ static void read_and_verify_pe_signature(fstream& fs)
{
- static const size_t OFFSET_TO_PE_SIGNATURE_OFFSET = 0x3c;
+ static constexpr size_t OFFSET_TO_PE_SIGNATURE_OFFSET = 0x3c;
- static const char* PE_SIGNATURE = "PE\0\0";
- static const size_t PE_SIGNATURE_SIZE = 4;
+ static constexpr StringLiteral PE_SIGNATURE = "PE\0\0";
+ static constexpr size_t PE_SIGNATURE_SIZE = 4;
fs.seekg(OFFSET_TO_PE_SIGNATURE_OFFSET, ios_base::beg);
- const int32_t offset_to_PE_signature = read_value_from_stream<int32_t>(fs);
+ const auto offset_to_pe_signature = read_value_from_stream<int32_t>(fs);
- fs.seekg(offset_to_PE_signature);
+ fs.seekg(offset_to_pe_signature);
char signature[PE_SIGNATURE_SIZE];
fs.read(signature, PE_SIGNATURE_SIZE);
- verify_equal_strings(VCPKG_LINE_INFO, PE_SIGNATURE, signature, PE_SIGNATURE_SIZE, "PE_SIGNATURE");
- fs.seekg(offset_to_PE_signature + PE_SIGNATURE_SIZE, ios_base::beg);
+ verify_equal_strings(VCPKG_LINE_INFO, PE_SIGNATURE.c_str(), signature, PE_SIGNATURE_SIZE, "PE_SIGNATURE");
+ fs.seekg(offset_to_pe_signature + PE_SIGNATURE_SIZE, ios_base::beg);
}
static fpos_t align_to_size(const uint64_t unaligned, const uint64_t alignment_size)
@@ -70,7 +72,7 @@ namespace vcpkg::COFFFileReader
struct CoffFileHeader
{
- static const size_t HEADER_SIZE = 20;
+ static constexpr size_t HEADER_SIZE = 20;
static CoffFileHeader read(fstream& fs)
{
@@ -80,14 +82,14 @@ namespace vcpkg::COFFFileReader
return ret;
}
- MachineType machineType() const
+ MachineType machine_type() const
{
- static const size_t MACHINE_TYPE_OFFSET = 0;
- static const size_t MACHINE_TYPE_SIZE = 2;
+ static constexpr size_t MACHINE_TYPE_OFFSET = 0;
+ static constexpr size_t MACHINE_TYPE_SIZE = 2;
std::string machine_field_as_string = data.substr(MACHINE_TYPE_OFFSET, MACHINE_TYPE_SIZE);
- const uint16_t machine = reinterpret_bytes<uint16_t>(machine_field_as_string.c_str());
- return getMachineType(machine);
+ const auto machine = reinterpret_bytes<uint16_t>(machine_field_as_string.c_str());
+ return to_machine_type(machine);
}
private:
@@ -96,13 +98,13 @@ namespace vcpkg::COFFFileReader
struct ArchiveMemberHeader
{
- static const size_t HEADER_SIZE = 60;
+ static constexpr size_t HEADER_SIZE = 60;
static ArchiveMemberHeader read(fstream& fs)
{
- static const size_t HEADER_END_OFFSET = 58;
- static const char* HEADER_END = "`\n";
- static const size_t HEADER_END_SIZE = 2;
+ static constexpr size_t HEADER_END_OFFSET = 58;
+ static constexpr StringLiteral HEADER_END = "`\n";
+ static constexpr size_t HEADER_END_SIZE = 2;
ArchiveMemberHeader ret;
ret.data.resize(HEADER_SIZE);
@@ -112,7 +114,7 @@ namespace vcpkg::COFFFileReader
{
const std::string header_end = ret.data.substr(HEADER_END_OFFSET, HEADER_END_SIZE);
verify_equal_strings(
- VCPKG_LINE_INFO, HEADER_END, header_end.c_str(), HEADER_END_SIZE, "LIB HEADER_END");
+ VCPKG_LINE_INFO, HEADER_END.c_str(), header_end.c_str(), HEADER_END_SIZE, "LIB HEADER_END");
}
return ret;
@@ -120,17 +122,17 @@ namespace vcpkg::COFFFileReader
std::string name() const
{
- static const size_t HEADER_NAME_OFFSET = 0;
- static const size_t HEADER_NAME_SIZE = 16;
+ static constexpr size_t HEADER_NAME_OFFSET = 0;
+ static constexpr size_t HEADER_NAME_SIZE = 16;
return data.substr(HEADER_NAME_OFFSET, HEADER_NAME_SIZE);
}
uint64_t member_size() const
{
- static const size_t ALIGNMENT_SIZE = 2;
+ static constexpr size_t ALIGNMENT_SIZE = 2;
- static const size_t HEADER_SIZE_OFFSET = 48;
- static const size_t HEADER_SIZE_FIELD_SIZE = 10;
+ static constexpr size_t HEADER_SIZE_OFFSET = 48;
+ static constexpr size_t HEADER_SIZE_FIELD_SIZE = 10;
const std::string as_string = data.substr(HEADER_SIZE_OFFSET, HEADER_SIZE_FIELD_SIZE);
// This is in ASCII decimal representation
const uint64_t value = std::strtoull(as_string.c_str(), nullptr, 10);
@@ -146,18 +148,19 @@ namespace vcpkg::COFFFileReader
{
static OffsetsArray read(fstream& fs, const uint32_t offset_count)
{
- static const size_t OFFSET_WIDTH = 4;
+ static constexpr uint32_t OFFSET_WIDTH = 4;
std::string raw_offsets;
- const size_t raw_offset_size = offset_count * OFFSET_WIDTH;
+ const uint32_t raw_offset_size = offset_count * OFFSET_WIDTH;
raw_offsets.resize(raw_offset_size);
fs.read(&raw_offsets[0], raw_offset_size);
OffsetsArray ret;
for (uint32_t i = 0; i < offset_count; ++i)
{
- const std::string value_as_string = raw_offsets.substr(OFFSET_WIDTH * i, OFFSET_WIDTH * (i + 1));
- const uint32_t value = reinterpret_bytes<uint32_t>(value_as_string.c_str());
+ const std::string value_as_string = raw_offsets.substr(OFFSET_WIDTH * static_cast<size_t>(i),
+ OFFSET_WIDTH * (static_cast<size_t>(i) + 1));
+ const auto value = reinterpret_bytes<uint32_t>(value_as_string.c_str());
// Ignore offsets that point to offset 0. See vcpkg github #223 #288 #292
if (value != 0)
@@ -176,41 +179,41 @@ namespace vcpkg::COFFFileReader
struct ImportHeader
{
- static const size_t HEADER_SIZE = 20;
+ static constexpr size_t HEADER_SIZE = 20;
static ImportHeader read(fstream& fs)
{
- static const size_t SIG1_OFFSET = 0;
- static const uint16_t SIG1 = static_cast<uint16_t>(MachineType::UNKNOWN);
- static const size_t SIG1_SIZE = 2;
+ static constexpr size_t SIG1_OFFSET = 0;
+ static constexpr auto SIG1 = static_cast<uint16_t>(MachineType::UNKNOWN);
+ static constexpr size_t SIG1_SIZE = 2;
- static const size_t SIG2_OFFSET = 2;
- static const uint16_t SIG2 = 0xFFFF;
- static const size_t SIG2_SIZE = 2;
+ static constexpr size_t SIG2_OFFSET = 2;
+ static constexpr uint16_t SIG2 = 0xFFFF;
+ static constexpr size_t SIG2_SIZE = 2;
ImportHeader ret;
ret.data.resize(HEADER_SIZE);
fs.read(&ret.data[0], HEADER_SIZE);
const std::string sig1_as_string = ret.data.substr(SIG1_OFFSET, SIG1_SIZE);
- const uint16_t sig1 = reinterpret_bytes<uint16_t>(sig1_as_string.c_str());
+ const auto sig1 = reinterpret_bytes<uint16_t>(sig1_as_string.c_str());
Checks::check_exit(VCPKG_LINE_INFO, sig1 == SIG1, "Sig1 was incorrect. Expected %s but got %s", SIG1, sig1);
const std::string sig2_as_string = ret.data.substr(SIG2_OFFSET, SIG2_SIZE);
- const uint16_t sig2 = reinterpret_bytes<uint16_t>(sig2_as_string.c_str());
+ const auto sig2 = reinterpret_bytes<uint16_t>(sig2_as_string.c_str());
Checks::check_exit(VCPKG_LINE_INFO, sig2 == SIG2, "Sig2 was incorrect. Expected %s but got %s", SIG2, sig2);
return ret;
}
- MachineType machineType() const
+ MachineType machine_type() const
{
- static const size_t MACHINE_TYPE_OFFSET = 6;
- static const size_t MACHINE_TYPE_SIZE = 2;
+ static constexpr size_t MACHINE_TYPE_OFFSET = 6;
+ static constexpr size_t MACHINE_TYPE_SIZE = 2;
std::string machine_field_as_string = data.substr(MACHINE_TYPE_OFFSET, MACHINE_TYPE_SIZE);
- const uint16_t machine = reinterpret_bytes<uint16_t>(machine_field_as_string.c_str());
- return getMachineType(machine);
+ const auto machine = reinterpret_bytes<uint16_t>(machine_field_as_string.c_str());
+ return to_machine_type(machine);
}
private:
@@ -219,14 +222,14 @@ namespace vcpkg::COFFFileReader
static void read_and_verify_archive_file_signature(fstream& fs)
{
- static const char* FILE_START = "!<arch>\n";
- static const size_t FILE_START_SIZE = 8;
+ static constexpr StringLiteral FILE_START = "!<arch>\n";
+ static constexpr size_t FILE_START_SIZE = 8;
- fs.seekg(fs.beg);
+ fs.seekg(std::fstream::beg);
char file_start[FILE_START_SIZE];
fs.read(file_start, FILE_START_SIZE);
- verify_equal_strings(VCPKG_LINE_INFO, FILE_START, file_start, FILE_START_SIZE, "LIB FILE_START");
+ verify_equal_strings(VCPKG_LINE_INFO, FILE_START.c_str(), file_start, FILE_START_SIZE, "LIB FILE_START");
}
DllInfo read_dll(const fs::path& path)
@@ -234,9 +237,9 @@ namespace vcpkg::COFFFileReader
std::fstream fs(path, std::ios::in | std::ios::binary | std::ios::ate);
Checks::check_exit(VCPKG_LINE_INFO, fs.is_open(), "Could not open file %s for reading", path.generic_string());
- read_and_verify_PE_signature(fs);
+ read_and_verify_pe_signature(fs);
CoffFileHeader header = CoffFileHeader::read(fs);
- MachineType machine = header.machineType();
+ const MachineType machine = header.machine_type();
return {machine};
}
@@ -244,7 +247,7 @@ namespace vcpkg::COFFFileReader
{
void set_to_offset(const fpos_t position) { this->m_absolute_position = position; }
- void set_to_current_pos(fstream& fs) { this->m_absolute_position = fs.tellg().seekpos(); }
+ void set_to_current_pos(fstream& fs) { this->m_absolute_position = fs.tellg(); }
void seek_to_marker(fstream& fs) const { fs.seekg(this->m_absolute_position, ios_base::beg); }
@@ -277,13 +280,13 @@ namespace vcpkg::COFFFileReader
second_linker_member_header.name().substr(0, 2) == "/ ",
"Could not find proper second linker member");
// The first 4 bytes contains the number of archive members
- const uint32_t archive_member_count = read_value_from_stream<uint32_t>(fs);
+ const auto archive_member_count = read_value_from_stream<uint32_t>(fs);
const OffsetsArray offsets = OffsetsArray::read(fs, archive_member_count);
marker.advance_by(ArchiveMemberHeader::HEADER_SIZE + second_linker_member_header.member_size());
marker.seek_to_marker(fs);
- bool hasLongnameMemberHeader = peek_value_from_stream<uint16_t>(fs) == 0x2F2F;
- if (hasLongnameMemberHeader)
+ const bool has_longname_member_header = peek_value_from_stream<uint16_t>(fs) == 0x2F2F;
+ if (has_longname_member_header)
{
const ArchiveMemberHeader longnames_member_header = ArchiveMemberHeader::read(fs);
marker.advance_by(ArchiveMemberHeader::HEADER_SIZE + longnames_member_header.member_size());
@@ -296,13 +299,14 @@ namespace vcpkg::COFFFileReader
{
marker.set_to_offset(offset + ArchiveMemberHeader::HEADER_SIZE); // Skip the header, no need to read it.
marker.seek_to_marker(fs);
- const uint16_t first_two_bytes = peek_value_from_stream<uint16_t>(fs);
- const bool isImportHeader = getMachineType(first_two_bytes) == MachineType::UNKNOWN;
+ const auto first_two_bytes = peek_value_from_stream<uint16_t>(fs);
+ const bool is_import_header = to_machine_type(first_two_bytes) == MachineType::UNKNOWN;
const MachineType machine =
- isImportHeader ? ImportHeader::read(fs).machineType() : CoffFileHeader::read(fs).machineType();
+ is_import_header ? ImportHeader::read(fs).machine_type() : CoffFileHeader::read(fs).machine_type();
machine_types.insert(machine);
}
return {std::vector<MachineType>(machine_types.cbegin(), machine_types.cend())};
}
+#endif
}
diff --git a/toolsrc/src/vcpkg_Enums.cpp b/toolsrc/src/vcpkg/base/enums.cpp
index 51ba9d5dc..aa124f3aa 100644
--- a/toolsrc/src/vcpkg_Enums.cpp
+++ b/toolsrc/src/vcpkg/base/enums.cpp
@@ -1,7 +1,7 @@
#include "pch.h"
-#include "vcpkg_Checks.h"
-#include "vcpkg_Enums.h"
+#include <vcpkg/base/checks.h>
+#include <vcpkg/base/enums.h>
namespace vcpkg::Enums
{
diff --git a/toolsrc/src/vcpkg_Files.cpp b/toolsrc/src/vcpkg/base/files.cpp
index 17aa2997a..3d96e834b 100644
--- a/toolsrc/src/vcpkg_Files.cpp
+++ b/toolsrc/src/vcpkg/base/files.cpp
@@ -1,8 +1,8 @@
#include "pch.h"
-#include "vcpkg_Files.h"
-#include "vcpkg_System.h"
-#include <thread>
+#include <vcpkg/base/files.h>
+#include <vcpkg/base/system.h>
+#include <vcpkg/base/util.h>
namespace vcpkg::Files
{
@@ -15,16 +15,16 @@ namespace vcpkg::Files
std::fstream file_stream(file_path, std::ios_base::in | std::ios_base::binary);
if (file_stream.fail())
{
- return std::errc::no_such_file_or_directory;
+ return std::make_error_code(std::errc::no_such_file_or_directory);
}
file_stream.seekg(0, file_stream.end);
auto length = file_stream.tellg();
file_stream.seekg(0, file_stream.beg);
- if (length > SIZE_MAX)
+ if (length == std::streampos(-1))
{
- return std::errc::file_too_large;
+ return std::make_error_code(std::errc::io_error);
}
std::string output;
@@ -39,7 +39,7 @@ namespace vcpkg::Files
std::fstream file_stream(file_path, std::ios_base::in | std::ios_base::binary);
if (file_stream.fail())
{
- return std::errc::no_such_file_or_directory;
+ return std::make_error_code(std::errc::no_such_file_or_directory);
}
std::vector<std::string> output;
@@ -55,24 +55,27 @@ namespace vcpkg::Files
virtual fs::path find_file_recursively_up(const fs::path& starting_dir,
const std::string& filename) const override
{
+ static const fs::path UNIX_ROOT = "/";
fs::path current_dir = starting_dir;
- for (; !current_dir.empty(); current_dir = current_dir.parent_path())
+ for (; !current_dir.empty() && current_dir != UNIX_ROOT; current_dir = current_dir.parent_path())
{
const fs::path candidate = current_dir / filename;
if (exists(candidate))
{
- break;
+ return current_dir;
}
}
- return current_dir;
+ return fs::path();
}
virtual std::vector<fs::path> get_files_recursive(const fs::path& dir) const override
{
std::vector<fs::path> ret;
- fs::stdfs::recursive_directory_iterator b(dir), e{};
+ std::error_code ec;
+ fs::stdfs::recursive_directory_iterator b(dir, ec), e{};
+ if (ec) return ret;
for (; b != e; ++b)
{
ret.push_back(b->path());
@@ -85,7 +88,9 @@ namespace vcpkg::Files
{
std::vector<fs::path> ret;
- fs::stdfs::directory_iterator b(dir), e{};
+ std::error_code ec;
+ fs::stdfs::directory_iterator b(dir, ec), e{};
+ if (ec) return ret;
for (; b != e; ++b)
{
ret.push_back(b->path());
@@ -104,6 +109,10 @@ namespace vcpkg::Files
output.close();
}
+ virtual void rename(const fs::path& oldpath, const fs::path& newpath, std::error_code& ec) override
+ {
+ fs::stdfs::rename(oldpath, newpath, ec);
+ }
virtual void rename(const fs::path& oldpath, const fs::path& newpath) override
{
fs::stdfs::rename(oldpath, newpath);
@@ -160,15 +169,33 @@ namespace vcpkg::Files
{
return fs::stdfs::status(path, ec);
}
- virtual void write_contents(const fs::path& file_path, const std::string& data) override
+ virtual void write_contents(const fs::path& file_path, const std::string& data, std::error_code& ec) override
{
+ ec.clear();
+
FILE* f = nullptr;
- auto ec = _wfopen_s(&f, file_path.native().c_str(), L"wb");
- Checks::check_exit(VCPKG_LINE_INFO, ec == 0);
- auto count = fwrite(data.data(), sizeof(data[0]), data.size(), f);
- fclose(f);
+#if defined(_WIN32)
+ auto err = _wfopen_s(&f, file_path.native().c_str(), L"wb");
+#else
+ f = fopen(file_path.native().c_str(), "wb");
+ int err = f != nullptr ? 0 : 1;
+#endif
+ if (err != 0)
+ {
+ ec.assign(err, std::system_category());
+ return;
+ }
+
+ if (f != nullptr)
+ {
+ auto count = fwrite(data.data(), sizeof(data[0]), data.size(), f);
+ fclose(f);
- Checks::check_exit(VCPKG_LINE_INFO, count == data.size());
+ if (count != data.size())
+ {
+ ec = std::make_error_code(std::errc::no_space_on_device);
+ }
+ }
}
};
@@ -185,11 +212,27 @@ namespace vcpkg::Files
void print_paths(const std::vector<fs::path>& paths)
{
- System::println("");
+ System::println();
for (const fs::path& p : paths)
{
System::println(" %s", p.generic_string());
}
- System::println("");
+ System::println();
+ }
+
+ std::vector<fs::path> find_from_PATH(const std::string& name)
+ {
+#if defined(_WIN32)
+ const std::string cmd = Strings::format("where.exe %s", name);
+#else
+ const std::string cmd = Strings::format("which %s", name);
+#endif
+ auto out = System::cmd_execute_and_capture_output(cmd);
+ if (out.exit_code != 0)
+ {
+ return {};
+ }
+
+ return Util::fmap(Strings::split(out.output, "\n"), [](auto&& s) { return fs::path(s); });
}
}
diff --git a/toolsrc/src/LineInfo.cpp b/toolsrc/src/vcpkg/base/lineinfo.cpp
index d1bf9a4b1..7435ed666 100644
--- a/toolsrc/src/LineInfo.cpp
+++ b/toolsrc/src/vcpkg/base/lineinfo.cpp
@@ -1,7 +1,7 @@
#include "pch.h"
-#include "LineInfo.h"
-#include "vcpkg_Strings.h"
+#include <vcpkg/base/lineinfo.h>
+#include <vcpkg/base/strings.h>
namespace vcpkg
{
diff --git a/toolsrc/src/MachineType.cpp b/toolsrc/src/vcpkg/base/machinetype.cpp
index f288855e6..2b7bd5e3a 100644
--- a/toolsrc/src/MachineType.cpp
+++ b/toolsrc/src/vcpkg/base/machinetype.cpp
@@ -1,13 +1,13 @@
#include "pch.h"
-#include "MachineType.h"
-#include "vcpkg_Checks.h"
+#include <vcpkg/base/checks.h>
+#include <vcpkg/base/machinetype.h>
namespace vcpkg
{
- MachineType getMachineType(const uint16_t value)
+ MachineType to_machine_type(const uint16_t value)
{
- MachineType t = static_cast<MachineType>(value);
+ const MachineType t = static_cast<MachineType>(value);
switch (t)
{
case MachineType::UNKNOWN:
diff --git a/toolsrc/src/vcpkg/base/strings.cpp b/toolsrc/src/vcpkg/base/strings.cpp
new file mode 100644
index 000000000..8d43e7af7
--- /dev/null
+++ b/toolsrc/src/vcpkg/base/strings.cpp
@@ -0,0 +1,192 @@
+#include "pch.h"
+
+#include <vcpkg/base/checks.h>
+#include <vcpkg/base/strings.h>
+#include <vcpkg/base/util.h>
+
+namespace vcpkg::Strings::details
+{
+ // To disambiguate between two overloads
+ static bool is_space(const char c) { return std::isspace(c) != 0; }
+
+ // Avoids C4244 warnings because of char<->int conversion that occur when using std::tolower()
+ static char tolower_char(const char c) { return static_cast<char>(std::tolower(c)); }
+ static char toupper_char(const char c) { return static_cast<char>(std::toupper(c)); }
+
+#if defined(_WIN32)
+ static _locale_t& c_locale()
+ {
+ static _locale_t c_locale_impl = _create_locale(LC_ALL, "C");
+ return c_locale_impl;
+ }
+#endif
+
+ std::string format_internal(const char* fmtstr, ...)
+ {
+ va_list args;
+ va_start(args, fmtstr);
+
+#if defined(_WIN32)
+ const int sz = _vscprintf_l(fmtstr, c_locale(), args);
+#else
+ const int sz = vsnprintf(nullptr, 0, fmtstr, args);
+#endif
+ Checks::check_exit(VCPKG_LINE_INFO, sz > 0);
+
+ std::string output(sz, '\0');
+
+#if defined(_WIN32)
+ _vsnprintf_s_l(&output.at(0), output.size() + 1, output.size(), fmtstr, c_locale(), args);
+#else
+ va_start(args, fmtstr);
+ vsnprintf(&output.at(0), output.size() + 1, fmtstr, args);
+#endif
+ va_end(args);
+
+ return output;
+ }
+}
+
+namespace vcpkg::Strings
+{
+#if defined(_WIN32)
+ std::wstring to_utf16(const CStringView& s)
+ {
+ std::wstring output;
+ const size_t size = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, nullptr, 0);
+ if (size == 0) return output;
+ output.resize(size - 1);
+ MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, output.data(), static_cast<int>(size) - 1);
+ return output;
+ }
+#endif
+
+#if defined(_WIN32)
+ std::string to_utf8(const wchar_t* w)
+ {
+ std::string output;
+ const size_t size = WideCharToMultiByte(CP_UTF8, 0, w, -1, nullptr, 0, nullptr, nullptr);
+ if (size == 0) return output;
+ output.resize(size - 1);
+ WideCharToMultiByte(CP_UTF8, 0, w, -1, output.data(), static_cast<int>(size) - 1, nullptr, nullptr);
+ return output;
+ }
+#endif
+
+ std::string escape_string(const CStringView& s, char char_to_escape, char escape_char)
+ {
+ std::string ret = s.c_str();
+ // Replace '\' with '\\' or '`' with '``'
+ ret = Strings::replace_all(std::move(ret), {escape_char}, {escape_char, escape_char});
+ // Replace '"' with '\"' or '`"'
+ ret = Strings::replace_all(std::move(ret), {char_to_escape}, {escape_char, char_to_escape});
+ return ret;
+ }
+
+ std::string::const_iterator case_insensitive_ascii_find(const std::string& s, const std::string& pattern)
+ {
+ const std::string pattern_as_lower_case(ascii_to_lowercase(pattern));
+ return search(s.begin(),
+ s.end(),
+ pattern_as_lower_case.begin(),
+ pattern_as_lower_case.end(),
+ [](const char a, const char b) { return details::tolower_char(a) == b; });
+ }
+
+ bool case_insensitive_ascii_contains(const std::string& s, const std::string& pattern)
+ {
+ return case_insensitive_ascii_find(s, pattern) != s.end();
+ }
+
+ bool case_insensitive_ascii_equals(const CStringView left, const CStringView right)
+ {
+#if defined(_WIN32)
+ return _stricmp(left.c_str(), right.c_str()) == 0;
+#else
+ return strcasecmp(left.c_str(), right.c_str()) == 0;
+#endif
+ }
+
+ std::string ascii_to_lowercase(std::string s)
+ {
+ std::transform(s.begin(), s.end(), s.begin(), &details::tolower_char);
+ return s;
+ }
+
+ std::string ascii_to_uppercase(std::string s)
+ {
+ std::transform(s.begin(), s.end(), s.begin(), &details::toupper_char);
+ return s;
+ }
+
+ bool case_insensitive_ascii_starts_with(const std::string& s, const std::string& pattern)
+ {
+#if defined(_WIN32)
+ return _strnicmp(s.c_str(), pattern.c_str(), pattern.size()) == 0;
+#else
+ return strncasecmp(s.c_str(), pattern.c_str(), pattern.size()) == 0;
+#endif
+ }
+
+ bool ends_with(const std::string& s, StringLiteral pattern)
+ {
+ if (s.size() < pattern.size()) return false;
+ return std::equal(s.end() - pattern.size(), s.end(), pattern.c_str(), pattern.c_str() + pattern.size());
+ }
+
+ std::string replace_all(std::string&& s, const std::string& search, const std::string& rep)
+ {
+ size_t pos = 0;
+ while ((pos = s.find(search, pos)) != std::string::npos)
+ {
+ s.replace(pos, search.size(), rep);
+ pos += rep.size();
+ }
+ return std::move(s);
+ }
+
+ std::string trim(std::string&& s)
+ {
+ s.erase(std::find_if_not(s.rbegin(), s.rend(), details::is_space).base(), s.end());
+ s.erase(s.begin(), std::find_if_not(s.begin(), s.end(), details::is_space));
+ return std::move(s);
+ }
+
+ void trim_all_and_remove_whitespace_strings(std::vector<std::string>* strings)
+ {
+ for (std::string& s : *strings)
+ {
+ s = trim(std::move(s));
+ }
+
+ Util::erase_remove_if(*strings, [](const std::string& s) { return s.empty(); });
+ }
+
+ std::vector<std::string> split(const std::string& s, const std::string& delimiter)
+ {
+ std::vector<std::string> output;
+
+ if (delimiter.empty())
+ {
+ output.push_back(s);
+ return output;
+ }
+
+ const size_t delimiter_length = delimiter.length();
+ size_t i = 0;
+ for (size_t pos = s.find(delimiter); pos != std::string::npos; pos = s.find(delimiter, pos))
+ {
+ output.push_back(s.substr(i, pos - i));
+ pos += delimiter_length;
+ i = pos;
+ }
+
+ // Add the rest of the string after the last delimiter, unless there is nothing after it
+ if (i != s.length())
+ {
+ output.push_back(s.substr(i, s.length()));
+ }
+
+ return output;
+ }
+}
diff --git a/toolsrc/src/vcpkg/base/system.cpp b/toolsrc/src/vcpkg/base/system.cpp
new file mode 100644
index 000000000..1aa12d0a4
--- /dev/null
+++ b/toolsrc/src/vcpkg/base/system.cpp
@@ -0,0 +1,504 @@
+#include "pch.h"
+
+#include <vcpkg/base/checks.h>
+#include <vcpkg/base/system.h>
+#include <vcpkg/globalstate.h>
+#include <vcpkg/metrics.h>
+
+#include <ctime>
+
+#if defined(__APPLE__)
+#include <mach-o/dyld.h>
+#endif
+
+#if defined(__FreeBSD__)
+#include <sys/sysctl.h>
+#endif
+
+#pragma comment(lib, "Advapi32")
+
+namespace vcpkg::System
+{
+ tm get_current_date_time()
+ {
+ using std::chrono::system_clock;
+ std::time_t now_time = system_clock::to_time_t(system_clock::now());
+ tm parts{};
+#if defined(_WIN32)
+ localtime_s(&parts, &now_time);
+#else
+ parts = *localtime(&now_time);
+#endif
+ return parts;
+ }
+
+ fs::path get_exe_path_of_current_process()
+ {
+#if defined(_WIN32)
+ wchar_t buf[_MAX_PATH];
+ const int bytes = GetModuleFileNameW(nullptr, buf, _MAX_PATH);
+ if (bytes == 0) std::abort();
+ return fs::path(buf, buf + bytes);
+#elif defined(__APPLE__)
+ static constexpr const uint32_t buff_size = 1024 * 32;
+ uint32_t size = buff_size;
+ char buf[buff_size] = {};
+ bool result = _NSGetExecutablePath(buf, &size);
+ Checks::check_exit(VCPKG_LINE_INFO, result != -1, "Could not determine current executable path.");
+ std::unique_ptr<char> canonicalPath(realpath(buf, NULL));
+ Checks::check_exit(VCPKG_LINE_INFO, result != -1, "Could not determine current executable path.");
+ return fs::path(std::string(canonicalPath.get()));
+#elif defined(__FreeBSD__)
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
+ char exePath[2048];
+ size_t len = sizeof(exePath);
+ auto rcode = sysctl(mib, 4, exePath, &len, NULL, 0);
+ Checks::check_exit(VCPKG_LINE_INFO, rcode == 0, "Could not determine current executable path.");
+ Checks::check_exit(VCPKG_LINE_INFO, len > 0, "Could not determine current executable path.");
+ return fs::path(exePath, exePath + len - 1);
+#else /* LINUX */
+ std::array<char, 1024 * 4> buf;
+ auto written = readlink("/proc/self/exe", buf.data(), buf.size());
+ Checks::check_exit(VCPKG_LINE_INFO, written != -1, "Could not determine current executable path.");
+ return fs::path(buf.data(), buf.data() + written);
+#endif
+ }
+
+ Optional<CPUArchitecture> to_cpu_architecture(const CStringView& arch)
+ {
+ if (Strings::case_insensitive_ascii_equals(arch, "x86")) return CPUArchitecture::X86;
+ if (Strings::case_insensitive_ascii_equals(arch, "x64")) return CPUArchitecture::X64;
+ if (Strings::case_insensitive_ascii_equals(arch, "amd64")) return CPUArchitecture::X64;
+ if (Strings::case_insensitive_ascii_equals(arch, "arm")) return CPUArchitecture::ARM;
+ if (Strings::case_insensitive_ascii_equals(arch, "arm64")) return CPUArchitecture::ARM64;
+ return nullopt;
+ }
+
+ CPUArchitecture get_host_processor()
+ {
+#if defined(_WIN32)
+ auto w6432 = get_environment_variable("PROCESSOR_ARCHITEW6432");
+ if (const auto p = w6432.get()) return to_cpu_architecture(*p).value_or_exit(VCPKG_LINE_INFO);
+
+ const auto procarch = get_environment_variable("PROCESSOR_ARCHITECTURE").value_or_exit(VCPKG_LINE_INFO);
+ return to_cpu_architecture(procarch).value_or_exit(VCPKG_LINE_INFO);
+#else
+#if defined(__x86_64__) || defined(_M_X64)
+ return CPUArchitecture::X64;
+#elif defined(__x86__) || defined(_M_X86)
+ return CPUArchitecture::X86;
+#else
+#error "Unknown host architecture"
+#endif
+#endif
+ }
+
+ std::vector<CPUArchitecture> get_supported_host_architectures()
+ {
+ std::vector<CPUArchitecture> supported_architectures;
+ supported_architectures.push_back(get_host_processor());
+
+ // AMD64 machines support to run x86 applications
+ if (supported_architectures.back() == CPUArchitecture::X64)
+ {
+ supported_architectures.push_back(CPUArchitecture::X86);
+ }
+
+ return supported_architectures;
+ }
+
+ CMakeVariable::CMakeVariable(const CStringView varname, const char* varvalue)
+ : s(Strings::format(R"("-D%s=%s")", varname, varvalue))
+ {
+ }
+ CMakeVariable::CMakeVariable(const CStringView varname, const std::string& varvalue)
+ : CMakeVariable(varname, varvalue.c_str())
+ {
+ }
+ CMakeVariable::CMakeVariable(const CStringView varname, const fs::path& path)
+ : CMakeVariable(varname, path.generic_u8string())
+ {
+ }
+
+ std::string make_cmake_cmd(const fs::path& cmake_exe,
+ const fs::path& cmake_script,
+ const std::vector<CMakeVariable>& pass_variables)
+ {
+ const std::string cmd_cmake_pass_variables = Strings::join(" ", pass_variables, [](auto&& v) { return v.s; });
+ return Strings::format(
+ R"("%s" %s -P "%s")", cmake_exe.u8string(), cmd_cmake_pass_variables, cmake_script.generic_u8string());
+ }
+
+ int cmd_execute_clean(const CStringView cmd_line, const std::unordered_map<std::string, std::string>& extra_env)
+ {
+ auto timer = Chrono::ElapsedTimer::create_started();
+#if defined(_WIN32)
+ static const std::string SYSTEM_ROOT = get_environment_variable("SystemRoot").value_or_exit(VCPKG_LINE_INFO);
+ static const std::string SYSTEM_32 = SYSTEM_ROOT + R"(\system32)";
+ std::string new_path = Strings::format(
+ R"(Path=%s;%s;%s\Wbem;%s\WindowsPowerShell\v1.0\)", SYSTEM_32, SYSTEM_ROOT, SYSTEM_32, SYSTEM_32);
+
+ std::vector<std::wstring> env_wstrings = {
+ L"ALLUSERSPROFILE",
+ L"APPDATA",
+ L"CommonProgramFiles",
+ L"CommonProgramFiles(x86)",
+ L"CommonProgramW6432",
+ L"COMPUTERNAME",
+ L"ComSpec",
+ L"HOMEDRIVE",
+ L"HOMEPATH",
+ L"LOCALAPPDATA",
+ L"LOGONSERVER",
+ L"NUMBER_OF_PROCESSORS",
+ L"OS",
+ L"PATHEXT",
+ L"PROCESSOR_ARCHITECTURE",
+ L"PROCESSOR_ARCHITEW6432",
+ L"PROCESSOR_IDENTIFIER",
+ L"PROCESSOR_LEVEL",
+ L"PROCESSOR_REVISION",
+ L"ProgramData",
+ L"ProgramFiles",
+ L"ProgramFiles(x86)",
+ L"ProgramW6432",
+ L"PROMPT",
+ L"PSModulePath",
+ L"PUBLIC",
+ L"SystemDrive",
+ L"SystemRoot",
+ L"TEMP",
+ L"TMP",
+ L"USERDNSDOMAIN",
+ L"USERDOMAIN",
+ L"USERDOMAIN_ROAMINGPROFILE",
+ L"USERNAME",
+ L"USERPROFILE",
+ L"windir",
+ // Enables proxy information to be passed to Curl, the underlying download library in cmake.exe
+ L"http_proxy",
+ L"https_proxy",
+ // Enables find_package(CUDA) in CMake
+ L"CUDA_PATH",
+ // Environmental variable generated automatically by CUDA after installation
+ L"NVCUDASAMPLES_ROOT",
+ };
+
+ // Flush stdout before launching external process
+ fflush(nullptr);
+
+ std::wstring env_cstr;
+
+ for (auto&& env_wstring : env_wstrings)
+ {
+ const Optional<std::string> value = System::get_environment_variable(Strings::to_utf8(env_wstring.c_str()));
+ const auto v = value.get();
+ if (!v || v->empty()) continue;
+
+ env_cstr.append(env_wstring);
+ env_cstr.push_back(L'=');
+ env_cstr.append(Strings::to_utf16(*v));
+ env_cstr.push_back(L'\0');
+ }
+
+ if (extra_env.find("PATH") != extra_env.end())
+ new_path += Strings::format(";%s", extra_env.find("PATH")->second);
+ env_cstr.append(Strings::to_utf16(new_path));
+ env_cstr.push_back(L'\0');
+ env_cstr.append(L"VSLANG=1033");
+ env_cstr.push_back(L'\0');
+
+ for (const auto& item : extra_env)
+ {
+ if (item.first == "PATH") continue;
+ env_cstr.append(Strings::to_utf16(item.first));
+ env_cstr.push_back(L'=');
+ env_cstr.append(Strings::to_utf16(item.second));
+ env_cstr.push_back(L'\0');
+ }
+
+ STARTUPINFOW startup_info;
+ memset(&startup_info, 0, sizeof(STARTUPINFOW));
+ startup_info.cb = sizeof(STARTUPINFOW);
+
+ PROCESS_INFORMATION process_info;
+ memset(&process_info, 0, sizeof(PROCESS_INFORMATION));
+
+ // Basically we are wrapping it in quotes
+ const std::string actual_cmd_line = Strings::format(R"###(cmd.exe /c "%s")###", cmd_line);
+ Debug::println("CreateProcessW(%s)", actual_cmd_line);
+ bool succeeded = TRUE == CreateProcessW(nullptr,
+ Strings::to_utf16(actual_cmd_line).data(),
+ nullptr,
+ nullptr,
+ FALSE,
+ IDLE_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT,
+ env_cstr.data(),
+ nullptr,
+ &startup_info,
+ &process_info);
+
+ Checks::check_exit(VCPKG_LINE_INFO, succeeded, "Process creation failed with error code: %lu", GetLastError());
+
+ CloseHandle(process_info.hThread);
+
+ const DWORD result = WaitForSingleObject(process_info.hProcess, INFINITE);
+ Checks::check_exit(VCPKG_LINE_INFO, result != WAIT_FAILED, "WaitForSingleObject failed");
+
+ DWORD exit_code = 0;
+ GetExitCodeProcess(process_info.hProcess, &exit_code);
+
+ Debug::println("CreateProcessW() returned %lu after %d us", exit_code, static_cast<int>(timer.microseconds()));
+ return static_cast<int>(exit_code);
+#else
+ Debug::println("system(%s)", cmd_line.c_str());
+ fflush(nullptr);
+ int rc = system(cmd_line.c_str());
+ Debug::println("system() returned %d", rc);
+ return rc;
+#endif
+ }
+
+ int cmd_execute(const CStringView cmd_line)
+ {
+ // Flush stdout before launching external process
+ fflush(nullptr);
+
+ // Basically we are wrapping it in quotes
+#if defined(_WIN32)
+ const std::string& actual_cmd_line = Strings::format(R"###("%s")###", cmd_line);
+ Debug::println("_wsystem(%s)", actual_cmd_line);
+ const int exit_code = _wsystem(Strings::to_utf16(actual_cmd_line).c_str());
+ Debug::println("_wsystem() returned %d", exit_code);
+#else
+ Debug::println("_system(%s)", cmd_line);
+ const int exit_code = system(cmd_line.c_str());
+ Debug::println("_system() returned %d", exit_code);
+#endif
+ return exit_code;
+ }
+
+ ExitCodeAndOutput cmd_execute_and_capture_output(const CStringView cmd_line)
+ {
+ // Flush stdout before launching external process
+ fflush(stdout);
+
+ auto timer = Chrono::ElapsedTimer::create_started();
+
+#if defined(_WIN32)
+ const auto actual_cmd_line = Strings::format(R"###("%s 2>&1")###", cmd_line);
+
+ Debug::println("_wpopen(%s)", actual_cmd_line);
+ std::wstring output;
+ wchar_t buf[1024];
+ const auto pipe = _wpopen(Strings::to_utf16(actual_cmd_line).c_str(), L"r");
+ if (pipe == nullptr)
+ {
+ return {1, Strings::to_utf8(output.c_str())};
+ }
+ while (fgetws(buf, 1024, pipe))
+ {
+ output.append(buf);
+ }
+ if (!feof(pipe))
+ {
+ return {1, Strings::to_utf8(output.c_str())};
+ }
+
+ const auto ec = _pclose(pipe);
+
+ // On Win7, output from powershell calls contain a utf-8 byte order mark in the utf-16 stream, so we strip it
+ // out if it is present. 0xEF,0xBB,0xBF is the UTF-8 byte-order mark
+ const wchar_t* a = output.c_str();
+ while (output.size() >= 3 && a[0] == 0xEF && a[1] == 0xBB && a[2] == 0xBF)
+ {
+ output.erase(0, 3);
+ }
+
+ Debug::println("_pclose() returned %d after %8d us", ec, static_cast<int>(timer.microseconds()));
+
+ return {ec, Strings::to_utf8(output.c_str())};
+#else
+ const auto actual_cmd_line = Strings::format(R"###(%s 2>&1)###", cmd_line);
+
+ Debug::println("popen(%s)", actual_cmd_line);
+ std::string output;
+ char buf[1024];
+ const auto pipe = popen(actual_cmd_line.c_str(), "r");
+ if (pipe == nullptr)
+ {
+ return {1, output};
+ }
+ while (fgets(buf, 1024, pipe))
+ {
+ output.append(buf);
+ }
+ if (!feof(pipe))
+ {
+ return {1, output};
+ }
+
+ const auto ec = pclose(pipe);
+
+ Debug::println("_pclose() returned %d after %8d us", ec, (int)timer.microseconds());
+
+ return {ec, output};
+#endif
+ }
+
+ void println() { putchar('\n'); }
+
+ void print(const CStringView message) { fputs(message.c_str(), stdout); }
+
+ void println(const CStringView message)
+ {
+ print(message);
+ println();
+ }
+
+ void print(const Color c, const CStringView message)
+ {
+#if defined(_WIN32)
+ const HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ CONSOLE_SCREEN_BUFFER_INFO console_screen_buffer_info{};
+ GetConsoleScreenBufferInfo(console_handle, &console_screen_buffer_info);
+ const auto original_color = console_screen_buffer_info.wAttributes;
+
+ SetConsoleTextAttribute(console_handle, static_cast<WORD>(c) | (original_color & 0xF0));
+ print(message);
+ SetConsoleTextAttribute(console_handle, original_color);
+#else
+ print(message);
+#endif
+ }
+
+ void println(const Color c, const CStringView message)
+ {
+ print(c, message);
+ println();
+ }
+
+ Optional<std::string> get_environment_variable(const CStringView varname) noexcept
+ {
+#if defined(_WIN32)
+ const auto w_varname = Strings::to_utf16(varname);
+ const auto sz = GetEnvironmentVariableW(w_varname.c_str(), nullptr, 0);
+ if (sz == 0) return nullopt;
+
+ std::wstring ret(sz, L'\0');
+
+ Checks::check_exit(VCPKG_LINE_INFO, MAXDWORD >= ret.size());
+ const auto sz2 = GetEnvironmentVariableW(w_varname.c_str(), ret.data(), static_cast<DWORD>(ret.size()));
+ Checks::check_exit(VCPKG_LINE_INFO, sz2 + 1 == sz);
+ ret.pop_back();
+ return Strings::to_utf8(ret.c_str());
+#else
+ auto v = getenv(varname.c_str());
+ if (!v) return nullopt;
+ return std::string(v);
+#endif
+ }
+
+#if defined(_WIN32)
+ static bool is_string_keytype(const DWORD hkey_type)
+ {
+ return hkey_type == REG_SZ || hkey_type == REG_MULTI_SZ || hkey_type == REG_EXPAND_SZ;
+ }
+
+ Optional<std::string> get_registry_string(void* base_hkey, const CStringView sub_key, const CStringView valuename)
+ {
+ HKEY k = nullptr;
+ const LSTATUS ec =
+ RegOpenKeyExW(reinterpret_cast<HKEY>(base_hkey), Strings::to_utf16(sub_key).c_str(), NULL, KEY_READ, &k);
+ if (ec != ERROR_SUCCESS) return nullopt;
+
+ DWORD dw_buffer_size = 0;
+ DWORD dw_type = 0;
+ auto rc =
+ RegQueryValueExW(k, Strings::to_utf16(valuename).c_str(), nullptr, &dw_type, nullptr, &dw_buffer_size);
+ if (rc != ERROR_SUCCESS || !is_string_keytype(dw_type) || dw_buffer_size == 0 ||
+ dw_buffer_size % sizeof(wchar_t) != 0)
+ return nullopt;
+ std::wstring ret;
+ ret.resize(dw_buffer_size / sizeof(wchar_t));
+
+ rc = RegQueryValueExW(k,
+ Strings::to_utf16(valuename).c_str(),
+ nullptr,
+ &dw_type,
+ reinterpret_cast<LPBYTE>(ret.data()),
+ &dw_buffer_size);
+ if (rc != ERROR_SUCCESS || !is_string_keytype(dw_type) || dw_buffer_size != sizeof(wchar_t) * ret.size())
+ return nullopt;
+
+ ret.pop_back(); // remove extra trailing null byte
+ return Strings::to_utf8(ret.c_str());
+ }
+#else
+ Optional<std::string> get_registry_string(void* base_hkey, const CStringView sub_key, const CStringView valuename)
+ {
+ return nullopt;
+ }
+#endif
+
+ static const Optional<fs::path>& get_program_files()
+ {
+ static const auto PATH = []() -> Optional<fs::path> {
+ auto value = System::get_environment_variable("PROGRAMFILES");
+ if (auto v = value.get())
+ {
+ return *v;
+ }
+
+ return nullopt;
+ }();
+
+ return PATH;
+ }
+
+ const Optional<fs::path>& get_program_files_32_bit()
+ {
+ static const auto PATH = []() -> Optional<fs::path> {
+ auto value = System::get_environment_variable("ProgramFiles(x86)");
+ if (auto v = value.get())
+ {
+ return *v;
+ }
+ return get_program_files();
+ }();
+ return PATH;
+ }
+
+ const Optional<fs::path>& get_program_files_platform_bitness()
+ {
+ static const auto PATH = []() -> Optional<fs::path> {
+ auto value = System::get_environment_variable("ProgramW6432");
+ if (auto v = value.get())
+ {
+ return *v;
+ }
+ return get_program_files();
+ }();
+ return PATH;
+ }
+}
+
+namespace vcpkg::Debug
+{
+ void println(const CStringView message)
+ {
+ if (GlobalState::debugging)
+ {
+ System::println("[DEBUG] %s", message);
+ }
+ }
+
+ void println(const System::Color c, const CStringView message)
+ {
+ if (GlobalState::debugging)
+ {
+ System::println(c, "[DEBUG] %s", message);
+ }
+ }
+}
diff --git a/toolsrc/src/vcpkg/binaryparagraph.cpp b/toolsrc/src/vcpkg/binaryparagraph.cpp
new file mode 100644
index 000000000..73ca23df1
--- /dev/null
+++ b/toolsrc/src/vcpkg/binaryparagraph.cpp
@@ -0,0 +1,123 @@
+#include "pch.h"
+
+#include <vcpkg/base/checks.h>
+#include <vcpkg/binaryparagraph.h>
+#include <vcpkg/parse.h>
+
+namespace vcpkg
+{
+ namespace Fields
+ {
+ static const std::string PACKAGE = "Package";
+ static const std::string VERSION = "Version";
+ static const std::string ARCHITECTURE = "Architecture";
+ static const std::string MULTI_ARCH = "Multi-Arch";
+ }
+
+ namespace Fields
+ {
+ static const std::string ABI = "Abi";
+ static const std::string FEATURE = "Feature";
+ static const std::string DESCRIPTION = "Description";
+ static const std::string MAINTAINER = "Maintainer";
+ static const std::string DEPENDS = "Depends";
+ static const std::string DEFAULTFEATURES = "Default-Features";
+ }
+
+ BinaryParagraph::BinaryParagraph() = default;
+
+ BinaryParagraph::BinaryParagraph(std::unordered_map<std::string, std::string> fields)
+ {
+ using namespace vcpkg::Parse;
+
+ ParagraphParser parser(std::move(fields));
+
+ {
+ std::string name;
+ parser.required_field(Fields::PACKAGE, name);
+ std::string architecture;
+ parser.required_field(Fields::ARCHITECTURE, architecture);
+ this->spec = PackageSpec::from_name_and_triplet(name, Triplet::from_canonical_name(architecture))
+ .value_or_exit(VCPKG_LINE_INFO);
+ }
+
+ // one or the other
+ this->version = parser.optional_field(Fields::VERSION);
+ this->feature = parser.optional_field(Fields::FEATURE);
+
+ this->description = parser.optional_field(Fields::DESCRIPTION);
+ this->maintainer = parser.optional_field(Fields::MAINTAINER);
+
+ this->abi = parser.optional_field(Fields::ABI);
+
+ std::string multi_arch;
+ parser.required_field(Fields::MULTI_ARCH, multi_arch);
+
+ this->depends = parse_comma_list(parser.optional_field(Fields::DEPENDS));
+ if (this->feature.empty())
+ {
+ this->default_features = parse_comma_list(parser.optional_field(Fields::DEFAULTFEATURES));
+ }
+
+ if (const auto err = parser.error_info(this->spec.to_string()))
+ {
+ System::println(
+ System::Color::error, "Error: while parsing the Binary Paragraph for %s", this->spec.to_string());
+ print_error_message(err);
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+
+ // prefer failing above when possible because it gives better information
+ Checks::check_exit(VCPKG_LINE_INFO, multi_arch == "same", "Multi-Arch must be 'same' but was %s", multi_arch);
+ }
+
+ BinaryParagraph::BinaryParagraph(const SourceParagraph& spgh, const Triplet& triplet, const std::string& abi_tag)
+ : version(spgh.version), description(spgh.description), maintainer(spgh.maintainer), abi(abi_tag)
+ {
+ this->spec = PackageSpec::from_name_and_triplet(spgh.name, triplet).value_or_exit(VCPKG_LINE_INFO);
+ this->depends = filter_dependencies(spgh.depends, triplet);
+ }
+
+ BinaryParagraph::BinaryParagraph(const SourceParagraph& spgh, const FeatureParagraph& fpgh, const Triplet& triplet)
+ : version(), description(fpgh.description), maintainer(), feature(fpgh.name)
+ {
+ this->spec = PackageSpec::from_name_and_triplet(spgh.name, triplet).value_or_exit(VCPKG_LINE_INFO);
+ this->depends = filter_dependencies(fpgh.depends, triplet);
+ }
+
+ std::string BinaryParagraph::displayname() const
+ {
+ if (this->feature.empty() || this->feature == "core")
+ return Strings::format("%s:%s", this->spec.name(), this->spec.triplet());
+ return Strings::format("%s[%s]:%s", this->spec.name(), this->feature, this->spec.triplet());
+ }
+
+ std::string BinaryParagraph::dir() const { return this->spec.dir(); }
+
+ std::string BinaryParagraph::fullstem() const
+ {
+ return Strings::format("%s_%s_%s", this->spec.name(), this->version, this->spec.triplet());
+ }
+
+ void serialize(const BinaryParagraph& pgh, std::string& out_str)
+ {
+ out_str.append("Package: ").append(pgh.spec.name()).push_back('\n');
+ if (!pgh.version.empty())
+ out_str.append("Version: ").append(pgh.version).push_back('\n');
+ else if (!pgh.feature.empty())
+ out_str.append("Feature: ").append(pgh.feature).push_back('\n');
+ if (!pgh.depends.empty())
+ {
+ out_str.append("Depends: ");
+ out_str.append(Strings::join(", ", pgh.depends));
+ out_str.push_back('\n');
+ }
+
+ out_str.append("Architecture: ").append(pgh.spec.triplet().to_string()).push_back('\n');
+ out_str.append("Multi-Arch: same\n");
+
+ if (!pgh.maintainer.empty()) out_str.append("Maintainer: ").append(pgh.maintainer).push_back('\n');
+ if (!pgh.abi.empty()) out_str.append("Abi: ").append(pgh.abi).push_back('\n');
+ if (!pgh.description.empty()) out_str.append("Description: ").append(pgh.description).push_back('\n');
+ }
+}
diff --git a/toolsrc/src/vcpkg/build.cpp b/toolsrc/src/vcpkg/build.cpp
new file mode 100644
index 000000000..b8ccb15bf
--- /dev/null
+++ b/toolsrc/src/vcpkg/build.cpp
@@ -0,0 +1,892 @@
+#include "pch.h"
+
+#include <vcpkg/base/checks.h>
+#include <vcpkg/base/chrono.h>
+#include <vcpkg/base/enums.h>
+#include <vcpkg/base/optional.h>
+#include <vcpkg/base/stringliteral.h>
+#include <vcpkg/base/system.h>
+#include <vcpkg/build.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/dependencies.h>
+#include <vcpkg/globalstate.h>
+#include <vcpkg/help.h>
+#include <vcpkg/input.h>
+#include <vcpkg/metrics.h>
+#include <vcpkg/paragraphs.h>
+#include <vcpkg/postbuildlint.h>
+#include <vcpkg/statusparagraphs.h>
+#include <vcpkg/vcpkglib.h>
+
+using vcpkg::Build::BuildResult;
+using vcpkg::Parse::ParseControlErrorInfo;
+using vcpkg::Parse::ParseExpected;
+
+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 fs::path& port_dir,
+ const ParsedArguments& options,
+ const VcpkgPaths& paths)
+ {
+ const PackageSpec& spec = full_spec.package_spec;
+ 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);
+ Checks::check_exit(VCPKG_LINE_INFO, error_count == 0);
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+
+ const ParseExpected<SourceControlFile> source_control_file =
+ Paragraphs::try_load_port(paths.get_filesystem(), port_dir);
+
+ if (!source_control_file.has_value())
+ {
+ print_error_message(source_control_file.error());
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+
+ const auto& scf = source_control_file.value_or_exit(VCPKG_LINE_INFO);
+ Checks::check_exit(VCPKG_LINE_INFO,
+ spec.name() == scf->core_paragraph->name,
+ "The Source field inside the CONTROL file does not match the port directory: '%s' != '%s'",
+ 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,
+ Build::CleanBuildtrees::NO,
+ Build::CleanPackages::NO,
+ Build::DownloadTool::BUILT_IN};
+
+ 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{port_dir}, 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);
+ System::println("Elapsed time for package %s: %s", spec.to_string(), build_timer.to_string());
+
+ if (result.code == BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES)
+ {
+ System::println(System::Color::error,
+ "The build command requires all dependencies to be already installed.");
+ System::println("The following dependencies are missing:");
+ System::println();
+ for (const auto& p : result.unmet_dependencies)
+ {
+ System::println(" %s", p);
+ }
+ System::println();
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+
+ Checks::check_exit(VCPKG_LINE_INFO, result.code != BuildResult::EXCLUDED);
+
+ if (result.code != BuildResult::SUCCEEDED)
+ {
+ System::println(System::Color::error, Build::create_error_message(result.code, spec));
+ System::println(Build::create_user_troubleshooting_message(spec));
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+
+ 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,
+ };
+
+ void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet)
+ {
+ // Build only takes a single package and all dependencies must already be installed
+ const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
+ const std::string command_argument = args.command_arguments.at(0);
+ const FullPackageSpec spec =
+ Input::check_and_get_full_package_spec(command_argument, default_triplet, COMMAND_STRUCTURE.example_text);
+ Input::check_triplet(spec.package_spec.triplet(), paths);
+ if (!spec.features.empty() && !GlobalState::feature_packages)
+ {
+ Checks::exit_with_message(
+ VCPKG_LINE_INFO, "Feature packages are experimentally available under the --featurepackages flag.");
+ }
+ perform_and_exit_ex(spec, paths.port_dir(spec.package_spec), options, paths);
+ }
+}
+
+namespace vcpkg::Build
+{
+ static const std::string NAME_EMPTY_PACKAGE = "PolicyEmptyPackage";
+ static const std::string NAME_DLLS_WITHOUT_LIBS = "PolicyDLLsWithoutLIBs";
+ static const std::string NAME_ONLY_RELEASE_CRT = "PolicyOnlyReleaseCRT";
+ static const std::string NAME_EMPTY_INCLUDE_FOLDER = "PolicyEmptyIncludeFolder";
+ static const std::string NAME_ALLOW_OBSOLETE_MSVCRT = "PolicyAllowObsoleteMsvcrt";
+
+ const std::string& to_string(BuildPolicy policy)
+ {
+ switch (policy)
+ {
+ case BuildPolicy::EMPTY_PACKAGE: return NAME_EMPTY_PACKAGE;
+ case BuildPolicy::DLLS_WITHOUT_LIBS: return NAME_DLLS_WITHOUT_LIBS;
+ case BuildPolicy::ONLY_RELEASE_CRT: return NAME_ONLY_RELEASE_CRT;
+ case BuildPolicy::EMPTY_INCLUDE_FOLDER: return NAME_EMPTY_INCLUDE_FOLDER;
+ case BuildPolicy::ALLOW_OBSOLETE_MSVCRT: return NAME_ALLOW_OBSOLETE_MSVCRT;
+ default: Checks::unreachable(VCPKG_LINE_INFO);
+ }
+ }
+
+ CStringView to_cmake_variable(BuildPolicy policy)
+ {
+ switch (policy)
+ {
+ case BuildPolicy::EMPTY_PACKAGE: return "VCPKG_POLICY_EMPTY_PACKAGE";
+ case BuildPolicy::DLLS_WITHOUT_LIBS: return "VCPKG_POLICY_DLLS_WITHOUT_LIBS";
+ case BuildPolicy::ONLY_RELEASE_CRT: return "VCPKG_POLICY_ONLY_RELEASE_CRT";
+ case BuildPolicy::EMPTY_INCLUDE_FOLDER: return "VCPKG_POLICY_EMPTY_INCLUDE_FOLDER";
+ case BuildPolicy::ALLOW_OBSOLETE_MSVCRT: return "VCPKG_POLICY_ALLOW_OBSOLETE_MSVCRT";
+ default: Checks::unreachable(VCPKG_LINE_INFO);
+ }
+ }
+
+ static const std::string NAME_BUILD_IN_DOWNLOAD = "BUILT_IN";
+ static const std::string NAME_ARIA2_DOWNLOAD = "ARIA2";
+
+ const std::string& to_string(DownloadTool tool)
+ {
+ switch (tool)
+ {
+ case DownloadTool::BUILT_IN: return NAME_BUILD_IN_DOWNLOAD;
+ case DownloadTool::ARIA2: return NAME_ARIA2_DOWNLOAD;
+ default: Checks::unreachable(VCPKG_LINE_INFO);
+ }
+ }
+
+ Optional<LinkageType> to_linkage_type(const std::string& str)
+ {
+ if (str == "dynamic") return LinkageType::DYNAMIC;
+ if (str == "static") return LinkageType::STATIC;
+ return nullopt;
+ }
+
+ namespace BuildInfoRequiredField
+ {
+ static const std::string CRT_LINKAGE = "CRTLinkage";
+ static const std::string LIBRARY_LINKAGE = "LibraryLinkage";
+ }
+
+ CStringView to_vcvarsall_target(const std::string& cmake_system_name)
+ {
+ if (cmake_system_name.empty()) return "";
+ if (cmake_system_name == "Windows") return "";
+ if (cmake_system_name == "WindowsStore") return "store";
+
+ Checks::exit_with_message(VCPKG_LINE_INFO, "Unsupported vcvarsall target %s", cmake_system_name);
+ }
+
+ CStringView to_vcvarsall_toolchain(const std::string& target_architecture, const Toolset& toolset)
+ {
+ auto maybe_target_arch = System::to_cpu_architecture(target_architecture);
+ Checks::check_exit(
+ VCPKG_LINE_INFO, maybe_target_arch.has_value(), "Invalid architecture string: %s", target_architecture);
+ auto target_arch = maybe_target_arch.value_or_exit(VCPKG_LINE_INFO);
+ auto host_architectures = System::get_supported_host_architectures();
+
+ for (auto&& host : host_architectures)
+ {
+ const auto it = Util::find_if(toolset.supported_architectures, [&](const ToolsetArchOption& opt) {
+ return host == opt.host_arch && target_arch == opt.target_arch;
+ });
+ if (it != toolset.supported_architectures.end()) return it->name;
+ }
+
+ Checks::exit_with_message(VCPKG_LINE_INFO,
+ "Unsupported toolchain combination. Target was: %s but supported ones were:\n%s",
+ target_architecture,
+ Strings::join(",", toolset.supported_architectures, [](const ToolsetArchOption& t) {
+ return t.name.c_str();
+ }));
+ }
+
+ std::string make_build_env_cmd(const PreBuildInfo& pre_build_info, const Toolset& toolset)
+ {
+ if (pre_build_info.external_toolchain_file.has_value()) return "";
+ if (!pre_build_info.cmake_system_name.empty() && pre_build_info.cmake_system_name != "WindowsStore") return "";
+
+ const char* tonull = " >nul";
+ if (GlobalState::debugging)
+ {
+ tonull = "";
+ }
+
+ const auto arch = to_vcvarsall_toolchain(pre_build_info.target_architecture, toolset);
+ const auto target = to_vcvarsall_target(pre_build_info.cmake_system_name);
+
+ return Strings::format(R"("%s" %s %s %s %s 2>&1)",
+ toolset.vcvarsall.u8string(),
+ Strings::join(" ", toolset.vcvarsall_options),
+ arch,
+ target,
+ tonull);
+ }
+
+ static BinaryParagraph create_binary_feature_control_file(const SourceParagraph& source_paragraph,
+ const FeatureParagraph& feature_paragraph,
+ const Triplet& triplet)
+ {
+ return BinaryParagraph(source_paragraph, feature_paragraph, triplet);
+ }
+
+ static std::unique_ptr<BinaryControlFile> create_binary_control_file(const SourceParagraph& source_paragraph,
+ const Triplet& triplet,
+ const BuildInfo& build_info,
+ const std::string& abi_tag)
+ {
+ auto bcf = std::make_unique<BinaryControlFile>();
+ BinaryParagraph bpgh(source_paragraph, triplet, abi_tag);
+ if (const auto p_ver = build_info.version.get())
+ {
+ bpgh.version = *p_ver;
+ }
+ bcf->core_paragraph = std::move(bpgh);
+ return bcf;
+ }
+
+ static void write_binary_control_file(const VcpkgPaths& paths, BinaryControlFile bcf)
+ {
+ std::string start = Strings::serialize(bcf.core_paragraph);
+ for (auto&& feature : bcf.features)
+ {
+ start += "\n" + Strings::serialize(feature);
+ }
+ const fs::path binary_control_file = paths.packages / bcf.core_paragraph.dir() / "CONTROL";
+ paths.get_filesystem().write_contents(binary_control_file, start);
+ }
+
+ 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);
+ });
+
+ auto dep_fspecs = FeatureSpec::from_strings_and_triplet(dep_strings, triplet);
+ Util::sort_unique_erase(dep_fspecs);
+
+ // expand defaults
+ std::vector<FeatureSpec> ret;
+ for (auto&& fspec : dep_fspecs)
+ {
+ if (fspec.feature().empty())
+ {
+ // reference to default features
+ const auto it = status_db.find_installed(fspec.spec());
+ if (it == status_db.end())
+ {
+ // not currently installed, so just leave the default reference so it will fail later
+ ret.push_back(fspec);
+ }
+ else
+ {
+ ret.emplace_back(fspec.spec(), "core");
+ for (auto&& default_feature : it->get()->package.default_features)
+ ret.emplace_back(fspec.spec(), default_feature);
+ }
+ }
+ else
+ {
+ ret.push_back(fspec);
+ }
+ }
+ Util::sort_unique_erase(ret);
+
+ return ret;
+ }
+
+ static 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();
+ const Triplet& triplet = spec.triplet();
+
+#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);
+
+ std::string all_features;
+ for (auto& feature : config.scf.feature_paragraphs)
+ {
+ all_features.append(feature->name + ";");
+ }
+
+ const Toolset& toolset = paths.get_toolset(pre_build_info);
+ const std::string cmd_launch_cmake = System::make_cmake_cmd(
+ cmake_exe_path,
+ paths.ports_cmake,
+ {
+ {"CMD", "BUILD"},
+ {"PORT", config.scf.core_paragraph->name},
+ {"CURRENT_PORT_DIR", config.port_dir},
+ {"TARGET_TRIPLET", spec.triplet().canonical_name()},
+ {"VCPKG_PLATFORM_TOOLSET", toolset.version.c_str()},
+ {"VCPKG_USE_HEAD_VERSION",
+ Util::Enum::to_bool(config.build_package_options.use_head_version) ? "1" : "0"},
+ {"_VCPKG_NO_DOWNLOADS", !Util::Enum::to_bool(config.build_package_options.allow_downloads) ? "1" : "0"},
+ {"_VCPKG_DOWNLOAD_TOOL", to_string(config.build_package_options.download_tool)},
+ {"GIT", git_exe_path},
+ {"FEATURES", Strings::join(";", config.feature_list)},
+ {"ALL_FEATURES", all_features},
+ });
+
+ auto command = make_build_env_cmd(pre_build_info, toolset);
+ if (!command.empty()) command.append(" && ");
+ command.append(cmd_launch_cmake);
+
+ const auto timer = Chrono::ElapsedTimer::create_started();
+
+ const int return_code = System::cmd_execute_clean(command);
+ const auto buildtimeus = timer.microseconds();
+ const auto spec_string = spec.to_string();
+
+ {
+ auto locked_metrics = Metrics::g_metrics.lock();
+ locked_metrics->track_buildtime(spec.to_string() + ":[" + Strings::join(",", config.feature_list) + "]",
+ buildtimeus);
+ if (return_code != 0)
+ {
+ locked_metrics->track_property("error", "build failed");
+ locked_metrics->track_property("build_error", spec_string);
+ return BuildResult::BUILD_FAILED;
+ }
+ }
+
+ const BuildInfo build_info = read_build_info(fs, paths.build_info_file_path(spec));
+ const size_t error_count = PostBuildLint::perform_all_checks(spec, paths, pre_build_info, build_info);
+
+ auto bcf = create_binary_control_file(*config.scf.core_paragraph, triplet, build_info, abi_tag);
+
+ if (error_count != 0)
+ {
+ return BuildResult::POST_BUILD_CHECKS_FAILED;
+ }
+ for (auto&& feature : config.feature_list)
+ {
+ for (auto&& f_pgh : config.scf.feature_paragraphs)
+ {
+ if (f_pgh->name == feature)
+ bcf->features.push_back(
+ create_binary_feature_control_file(*config.scf.core_paragraph, *f_pgh, triplet));
+ }
+ }
+
+ write_binary_control_file(paths, *bcf);
+ return {BuildResult::SUCCEEDED, std::move(bcf)};
+ }
+
+ static ExtendedBuildResult do_build_package_and_clean_buildtrees(const VcpkgPaths& paths,
+ const PreBuildInfo& pre_build_info,
+ const PackageSpec& spec,
+ const std::string& abi_tag,
+ const BuildPackageConfig& config)
+ {
+ auto result = do_build_package(paths, pre_build_info, spec, abi_tag, config);
+
+ if (config.build_package_options.clean_buildtrees == CleanBuildtrees::YES)
+ {
+ auto& fs = paths.get_filesystem();
+ const fs::path buildtrees_dir = paths.buildtrees / config.scf.core_paragraph->name;
+ auto buildtree_files = fs.get_files_non_recursive(buildtrees_dir);
+ for (auto&& file : buildtree_files)
+ {
+ if (fs.is_directory(file) && file.filename() != "src")
+ {
+ std::error_code ec;
+ fs.remove_all(file, ec);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ Optional<AbiTagAndFile> compute_abi_tag(const VcpkgPaths& paths,
+ const BuildPackageConfig& config,
+ const PreBuildInfo& pre_build_info,
+ Span<const AbiEntry> dependency_abis)
+ {
+ if (!GlobalState::g_binary_caching) return nullopt;
+
+ auto& fs = paths.get_filesystem();
+ const Triplet& triplet = config.triplet;
+ const std::string& name = config.scf.core_paragraph->name;
+
+ std::vector<AbiEntry> abi_tag_entries;
+
+ abi_tag_entries.insert(abi_tag_entries.end(), dependency_abis.begin(), dependency_abis.end());
+
+ abi_tag_entries.emplace_back(
+ AbiEntry{"portfile", Commands::Hash::get_file_hash(fs, config.port_dir / "portfile.cmake", "SHA1")});
+ abi_tag_entries.emplace_back(
+ AbiEntry{"control", Commands::Hash::get_file_hash(fs, config.port_dir / "CONTROL", "SHA1")});
+
+ abi_tag_entries.emplace_back(AbiEntry{"triplet", pre_build_info.triplet_abi_tag});
+
+ const std::string features = Strings::join(";", config.feature_list);
+ abi_tag_entries.emplace_back(AbiEntry{"features", features});
+
+ if (config.build_package_options.use_head_version == UseHeadVersion::YES)
+ abi_tag_entries.emplace_back(AbiEntry{"head", ""});
+
+ Util::sort(abi_tag_entries);
+
+ const std::string full_abi_info =
+ Strings::join("", abi_tag_entries, [](const AbiEntry& p) { return p.key + " " + p.value + "\n"; });
+
+ if (GlobalState::debugging)
+ {
+ System::println("[DEBUG] <abientries>");
+ for (auto&& entry : abi_tag_entries)
+ {
+ System::println("[DEBUG] %s|%s", entry.key, entry.value);
+ }
+ System::println("[DEBUG] </abientries>");
+ }
+
+ auto abi_tag_entries_missing = abi_tag_entries;
+ Util::stable_keep_if(abi_tag_entries_missing, [](const AbiEntry& p) { return p.value.empty(); });
+
+ if (abi_tag_entries_missing.empty())
+ {
+ std::error_code ec;
+ fs.create_directories(paths.buildtrees / name, ec);
+ const auto abi_file_path = paths.buildtrees / name / (triplet.canonical_name() + ".vcpkg_abi_info.txt");
+ fs.write_contents(abi_file_path, full_abi_info);
+
+ return AbiTagAndFile{Commands::Hash::get_file_hash(fs, abi_file_path, "SHA1"), abi_file_path};
+ }
+
+ System::println(
+ "Warning: binary caching disabled because abi keys are missing values:\n%s",
+ Strings::join("", abi_tag_entries_missing, [](const AbiEntry& e) { return " " + e.key + "\n"; }));
+
+ return nullopt;
+ }
+
+ static void decompress_archive(const VcpkgPaths& paths, const PackageSpec& spec, const fs::path& archive_path)
+ {
+ auto& fs = paths.get_filesystem();
+
+ auto pkg_path = paths.package_dir(spec);
+ 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());
+
+#if defined(_WIN32)
+ auto&& seven_zip_exe = paths.get_tool_exe(Tools::SEVEN_ZIP);
+
+ System::cmd_execute_clean(Strings::format(
+ R"("%s" x "%s" -o"%s" -y >nul)", seven_zip_exe.u8string(), archive_path.u8string(), pkg_path.u8string()));
+#else
+ System::cmd_execute_clean(
+ Strings::format(R"(unzip -qq "%s" "-d%s")", archive_path.u8string(), pkg_path.u8string()));
+#endif
+ }
+
+ static void compress_archive(const VcpkgPaths& paths, const PackageSpec& spec, const fs::path& tmp_archive_path)
+ {
+ auto& fs = paths.get_filesystem();
+
+ std::error_code ec;
+
+ fs.remove(tmp_archive_path, ec);
+ Checks::check_exit(
+ VCPKG_LINE_INFO, !fs.exists(tmp_archive_path), "Could not remove file: %s", tmp_archive_path.u8string());
+#if defined(_WIN32)
+ auto&& seven_zip_exe = paths.get_tool_exe(Tools::SEVEN_ZIP);
+
+ System::cmd_execute_clean(Strings::format(R"("%s" a "%s" "%s\*" >nul)",
+ seven_zip_exe.u8string(),
+ tmp_archive_path.u8string(),
+ paths.package_dir(spec).u8string()));
+#else
+ System::cmd_execute_clean(Strings::format(
+ R"(cd '%s' && zip --quiet -r '%s' *)", paths.package_dir(spec).u8string(), tmp_archive_path.u8string()));
+#endif
+ }
+
+ ExtendedBuildResult build_package(const VcpkgPaths& paths,
+ const BuildPackageConfig& config,
+ const StatusParagraphs& status_db)
+ {
+ auto& fs = paths.get_filesystem();
+ const Triplet& triplet = config.triplet;
+ const std::string& name = config.scf.core_paragraph->name;
+
+ std::vector<FeatureSpec> required_fspecs = compute_required_feature_specs(config, status_db);
+
+ // extract out the actual package ids
+ auto dep_pspecs = Util::fmap(required_fspecs, [](FeatureSpec const& fspec) { return fspec.spec(); });
+ Util::sort_unique_erase(dep_pspecs);
+
+ // Find all features that aren't installed. This mutates required_fspecs.
+ Util::unstable_keep_if(required_fspecs, [&](FeatureSpec const& fspec) {
+ return !status_db.is_installed(fspec) && fspec.name() != name;
+ });
+
+ if (!required_fspecs.empty())
+ {
+ return {BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES, std::move(required_fspecs)};
+ }
+
+ const PackageSpec spec =
+ PackageSpec::from_name_and_triplet(config.scf.core_paragraph->name, triplet).value_or_exit(VCPKG_LINE_INFO);
+
+ std::vector<AbiEntry> dependency_abis;
+
+ // dep_pspecs was not destroyed
+ for (auto&& pspec : dep_pspecs)
+ {
+ if (pspec == spec) continue;
+ const auto status_it = status_db.find_installed(pspec);
+ Checks::check_exit(VCPKG_LINE_INFO, status_it != status_db.end());
+ dependency_abis.emplace_back(
+ AbiEntry{status_it->get()->package.spec.name(), status_it->get()->package.abi});
+ }
+
+ const auto pre_build_info = PreBuildInfo::from_triplet_file(paths, triplet);
+
+ auto maybe_abi_tag_and_file = compute_abi_tag(paths, config, pre_build_info, dependency_abis);
+
+ const auto abi_tag_and_file = maybe_abi_tag_and_file.get();
+
+ if (GlobalState::g_binary_caching && abi_tag_and_file)
+ {
+ const fs::path archives_root_dir = paths.root / "archives";
+ const std::string archive_name = abi_tag_and_file->tag + ".zip";
+ const fs::path archive_subpath = fs::u8path(abi_tag_and_file->tag.substr(0, 2)) / archive_name;
+ const fs::path archive_path = archives_root_dir / archive_subpath;
+ const fs::path archive_tombstone_path = archives_root_dir / "fail" / archive_subpath;
+
+ if (fs.exists(archive_path))
+ {
+ System::println("Using cached binary package: %s", archive_path.u8string());
+
+ decompress_archive(paths, spec, archive_path);
+
+ auto maybe_bcf = Paragraphs::try_load_cached_package(paths, spec);
+ std::unique_ptr<BinaryControlFile> bcf =
+ std::make_unique<BinaryControlFile>(std::move(maybe_bcf).value_or_exit(VCPKG_LINE_INFO));
+ return {BuildResult::SUCCEEDED, std::move(bcf)};
+ }
+
+ if (fs.exists(archive_tombstone_path))
+ {
+ System::println("Found failure tombstone: %s", archive_tombstone_path.u8string());
+ return BuildResult::BUILD_FAILED;
+ }
+
+ System::println("Could not locate cached archive: %s", archive_path.u8string());
+
+ ExtendedBuildResult result = do_build_package_and_clean_buildtrees(
+ paths, pre_build_info, spec, maybe_abi_tag_and_file.value_or(AbiTagAndFile{}).tag, config);
+
+ std::error_code ec;
+ fs.create_directories(paths.package_dir(spec) / "share" / spec.name(), ec);
+ auto abi_file_in_package = paths.package_dir(spec) / "share" / spec.name() / "vcpkg_abi_info.txt";
+ fs.copy_file(abi_tag_and_file->tag_file, abi_file_in_package, fs::stdfs::copy_options::none, ec);
+ Checks::check_exit(VCPKG_LINE_INFO, !ec, "Could not copy into file: %s", abi_file_in_package.u8string());
+
+ if (result.code == BuildResult::SUCCEEDED)
+ {
+ const auto tmp_archive_path = paths.buildtrees / spec.name() / (spec.triplet().to_string() + ".zip");
+
+ compress_archive(paths, spec, tmp_archive_path);
+
+ fs.create_directories(archive_path.parent_path(), ec);
+ fs.rename(tmp_archive_path, archive_path, ec);
+ if (ec)
+ System::println(
+ System::Color::warning, "Failed to store binary cache: %s", archive_path.u8string());
+ else
+ System::println("Stored binary cache: %s", archive_path.u8string());
+ }
+ else if (result.code == BuildResult::BUILD_FAILED || result.code == BuildResult::POST_BUILD_CHECKS_FAILED)
+ {
+ // Build failed, so store tombstone archive
+ fs.create_directories(archive_tombstone_path.parent_path(), ec);
+ fs.write_contents(archive_tombstone_path, "", ec);
+ }
+
+ return result;
+ }
+
+ return do_build_package_and_clean_buildtrees(
+ paths, pre_build_info, spec, maybe_abi_tag_and_file.value_or(AbiTagAndFile{}).tag, config);
+ }
+
+ const std::string& to_string(const BuildResult build_result)
+ {
+ static const std::string NULLVALUE_STRING = Enums::nullvalue_to_string("vcpkg::Commands::Build::BuildResult");
+ static const std::string SUCCEEDED_STRING = "SUCCEEDED";
+ static const std::string BUILD_FAILED_STRING = "BUILD_FAILED";
+ static const std::string FILE_CONFLICTS_STRING = "FILE_CONFLICTS";
+ static const std::string POST_BUILD_CHECKS_FAILED_STRING = "POST_BUILD_CHECKS_FAILED";
+ static const std::string CASCADED_DUE_TO_MISSING_DEPENDENCIES_STRING = "CASCADED_DUE_TO_MISSING_DEPENDENCIES";
+ static const std::string EXCLUDED_STRING = "EXCLUDED";
+
+ switch (build_result)
+ {
+ case BuildResult::NULLVALUE: return NULLVALUE_STRING;
+ case BuildResult::SUCCEEDED: return SUCCEEDED_STRING;
+ case BuildResult::BUILD_FAILED: return BUILD_FAILED_STRING;
+ case BuildResult::POST_BUILD_CHECKS_FAILED: return POST_BUILD_CHECKS_FAILED_STRING;
+ case BuildResult::FILE_CONFLICTS: return FILE_CONFLICTS_STRING;
+ case BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES: return CASCADED_DUE_TO_MISSING_DEPENDENCIES_STRING;
+ case BuildResult::EXCLUDED: return EXCLUDED_STRING;
+ default: Checks::unreachable(VCPKG_LINE_INFO);
+ }
+ }
+
+ std::string create_error_message(const BuildResult build_result, const PackageSpec& spec)
+ {
+ return Strings::format("Error: Building package %s failed with: %s", spec, Build::to_string(build_result));
+ }
+
+ std::string create_user_troubleshooting_message(const PackageSpec& spec)
+ {
+ return Strings::format("Please ensure you're using the latest portfiles with `.\\vcpkg update`, then\n"
+ "submit an issue at https://github.com/Microsoft/vcpkg/issues including:\n"
+ " Package: %s\n"
+ " Vcpkg version: %s\n"
+ "\n"
+ "Additionally, attach any relevant sections from the log files above.",
+ spec,
+ Commands::Version::version());
+ }
+
+ static BuildInfo inner_create_buildinfo(std::unordered_map<std::string, std::string> pgh)
+ {
+ Parse::ParagraphParser parser(std::move(pgh));
+
+ BuildInfo build_info;
+
+ {
+ std::string crt_linkage_as_string;
+ parser.required_field(BuildInfoRequiredField::CRT_LINKAGE, crt_linkage_as_string);
+
+ auto crtlinkage = to_linkage_type(crt_linkage_as_string);
+ if (const auto p = crtlinkage.get())
+ build_info.crt_linkage = *p;
+ else
+ Checks::exit_with_message(VCPKG_LINE_INFO, "Invalid crt linkage type: [%s]", crt_linkage_as_string);
+ }
+
+ {
+ std::string library_linkage_as_string;
+ parser.required_field(BuildInfoRequiredField::LIBRARY_LINKAGE, library_linkage_as_string);
+ auto liblinkage = to_linkage_type(library_linkage_as_string);
+ if (const auto p = liblinkage.get())
+ build_info.library_linkage = *p;
+ else
+ Checks::exit_with_message(
+ VCPKG_LINE_INFO, "Invalid library linkage type: [%s]", library_linkage_as_string);
+ }
+ std::string version = parser.optional_field("Version");
+ if (!version.empty()) build_info.version = std::move(version);
+
+ std::map<BuildPolicy, bool> policies;
+ for (auto policy : G_ALL_POLICIES)
+ {
+ const auto setting = parser.optional_field(to_string(policy));
+ if (setting.empty()) continue;
+ if (setting == "enabled")
+ policies.emplace(policy, true);
+ else if (setting == "disabled")
+ policies.emplace(policy, false);
+ else
+ Checks::exit_with_message(
+ VCPKG_LINE_INFO, "Unknown setting for policy '%s': %s", to_string(policy), setting);
+ }
+
+ if (const auto err = parser.error_info("PostBuildInformation"))
+ {
+ print_error_message(err);
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+
+ build_info.policies = BuildPolicies(std::move(policies));
+
+ return build_info;
+ }
+
+ 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);
+ 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)
+ {
+ static constexpr CStringView FLAG_GUID = "c35112b6-d1ba-415b-aa5d-81de856ef8eb";
+
+ const fs::path& cmake_exe_path = paths.get_tool_exe(Tools::CMAKE);
+ const fs::path ports_cmake_script_path = paths.scripts / "get_triplet_environment.cmake";
+ const fs::path triplet_file_path = paths.triplets / (triplet.canonical_name() + ".cmake");
+
+ const std::string triplet_abi_tag = [&]() {
+ static std::map<fs::path, std::string> s_hash_cache;
+
+ if (GlobalState::g_binary_caching)
+ {
+ auto it_hash = s_hash_cache.find(triplet_file_path);
+ if (it_hash != s_hash_cache.end())
+ {
+ return it_hash->second;
+ }
+ auto hash = Commands::Hash::get_file_hash(paths.get_filesystem(), triplet_file_path, "SHA1");
+ s_hash_cache.emplace(triplet_file_path, hash);
+ return hash;
+ }
+
+ return std::string();
+ }();
+
+ const auto cmd_launch_cmake = System::make_cmake_cmd(cmake_exe_path,
+ ports_cmake_script_path,
+ {
+ {"CMAKE_TRIPLET_FILE", triplet_file_path},
+ });
+ const auto ec_data = System::cmd_execute_and_capture_output(cmd_launch_cmake);
+ Checks::check_exit(VCPKG_LINE_INFO, ec_data.exit_code == 0, ec_data.output);
+
+ const std::vector<std::string> lines = Strings::split(ec_data.output, "\n");
+
+ PreBuildInfo pre_build_info;
+ pre_build_info.triplet_abi_tag = triplet_abi_tag;
+
+ const auto e = lines.cend();
+ auto cur = std::find(lines.cbegin(), e, FLAG_GUID);
+ if (cur != e) ++cur;
+
+ for (; cur != e; ++cur)
+ {
+ auto&& line = *cur;
+
+ const std::vector<std::string> s = Strings::split(line, "=");
+ Checks::check_exit(VCPKG_LINE_INFO,
+ s.size() == 1 || s.size() == 2,
+ "Expected format is [VARIABLE_NAME=VARIABLE_VALUE], but was [%s]",
+ line);
+
+ const bool variable_with_no_value = s.size() == 1;
+ const std::string variable_name = s.at(0);
+ const std::string variable_value = variable_with_no_value ? "" : s.at(1);
+
+ 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")
+ {
+ 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;
+ }
+
+ if (variable_name == "VCPKG_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);
+ continue;
+ }
+
+ Checks::exit_with_message(VCPKG_LINE_INFO, "Unknown variable name %s", line);
+ }
+
+ 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))
+ {
+ }
+ ExtendedBuildResult::ExtendedBuildResult(BuildResult code, std::vector<FeatureSpec>&& unmet_deps)
+ : code(code), unmet_dependencies(std::move(unmet_deps))
+ {
+ }
+}
diff --git a/toolsrc/src/vcpkg/commands.autocomplete.cpp b/toolsrc/src/vcpkg/commands.autocomplete.cpp
new file mode 100644
index 000000000..564404421
--- /dev/null
+++ b/toolsrc/src/vcpkg/commands.autocomplete.cpp
@@ -0,0 +1,170 @@
+#include "pch.h"
+
+#include <vcpkg/base/system.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/install.h>
+#include <vcpkg/metrics.h>
+#include <vcpkg/paragraphs.h>
+#include <vcpkg/remove.h>
+#include <vcpkg/vcpkglib.h>
+
+namespace vcpkg::Commands::Autocomplete
+{
+ [[noreturn]] static void output_sorted_results_and_exit(const LineInfo& line_info,
+ std::vector<std::string>&& results)
+ {
+ const SortedVector<std::string> sorted_results(results);
+ System::println(Strings::join("\n", sorted_results));
+
+ Checks::exit_success(line_info);
+ }
+
+ std::vector<std::string> combine_port_with_triplets(const std::string& port,
+ const std::vector<std::string>& triplets)
+ {
+ return Util::fmap(triplets,
+ [&](const std::string& triplet) { return Strings::format("%s:%s", port, triplet); });
+ }
+
+ void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
+ {
+ Metrics::g_metrics.lock()->set_send_metrics(false);
+ const std::string to_autocomplete = Strings::join(" ", args.command_arguments);
+ const std::vector<std::string> tokens = Strings::split(to_autocomplete, " ");
+
+ std::smatch match;
+
+ // Handles vcpkg <command>
+ if (std::regex_match(to_autocomplete, match, std::regex{R"###(^(\S*)$)###"}))
+ {
+ const std::string requested_command = match[1].str();
+
+ // First try public commands
+ std::vector<std::string> public_commands = {
+ "install",
+ "search",
+ "remove",
+ "list",
+ "update",
+ "hash",
+ "help",
+ "integrate",
+ "export",
+ "edit",
+ "create",
+ "owns",
+ "cache",
+ "version",
+ "contact",
+ };
+
+ Util::unstable_keep_if(public_commands, [&](const std::string& s) {
+ return Strings::case_insensitive_ascii_starts_with(s, requested_command);
+ });
+
+ if (!public_commands.empty())
+ {
+ output_sorted_results_and_exit(VCPKG_LINE_INFO, std::move(public_commands));
+ }
+
+ // If no public commands match, try private commands
+ std::vector<std::string> private_commands = {
+ "build",
+ "buildexternal",
+ "ci",
+ "depend-info",
+ "env",
+ "import",
+ "portsdiff",
+ };
+
+ Util::unstable_keep_if(private_commands, [&](const std::string& s) {
+ return Strings::case_insensitive_ascii_starts_with(s, requested_command);
+ });
+
+ output_sorted_results_and_exit(VCPKG_LINE_INFO, std::move(private_commands));
+ }
+
+ // Handles vcpkg install package:<triplet>
+ if (std::regex_match(to_autocomplete, match, std::regex{R"###(^install(.*|)\s([^:]+):(\S*)$)###"}))
+ {
+ const auto port_name = match[2].str();
+ const auto triplet_prefix = match[3].str();
+
+ auto maybe_port = Paragraphs::try_load_port(paths.get_filesystem(), paths.port_dir(port_name));
+ if (maybe_port.error())
+ {
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+
+ std::vector<std::string> triplets = paths.get_available_triplets();
+ Util::unstable_keep_if(triplets, [&](const std::string& s) {
+ return Strings::case_insensitive_ascii_starts_with(s, triplet_prefix);
+ });
+
+ auto result = combine_port_with_triplets(port_name, triplets);
+
+ output_sorted_results_and_exit(VCPKG_LINE_INFO, std::move(result));
+ }
+
+ struct CommandEntry
+ {
+ constexpr CommandEntry(const CStringView& name, const CStringView& regex, const CommandStructure& structure)
+ : name(name), regex(regex), structure(structure)
+ {
+ }
+
+ CStringView name;
+ CStringView regex;
+ const CommandStructure& structure;
+ };
+
+ static constexpr CommandEntry COMMANDS[] = {
+ CommandEntry{"install", R"###(^install\s(.*\s|)(\S*)$)###", Install::COMMAND_STRUCTURE},
+ CommandEntry{"edit", R"###(^edit\s(.*\s|)(\S*)$)###", Edit::COMMAND_STRUCTURE},
+ CommandEntry{"remove", R"###(^remove\s(.*\s|)(\S*)$)###", Remove::COMMAND_STRUCTURE},
+ CommandEntry{"integrate", R"###(^integrate(\s+)(\S*)$)###", Integrate::COMMAND_STRUCTURE},
+ };
+
+ for (auto&& command : COMMANDS)
+ {
+ if (std::regex_match(to_autocomplete, match, std::regex{command.regex.c_str()}))
+ {
+ const auto prefix = match[2].str();
+ std::vector<std::string> results;
+
+ const bool is_option = Strings::case_insensitive_ascii_starts_with(prefix, "-");
+ if (is_option)
+ {
+ results = Util::fmap(command.structure.options.switches,
+ [](const CommandSwitch& s) -> std::string { return s.name; });
+
+ auto settings = Util::fmap(command.structure.options.settings, [](auto&& s) { return s.name; });
+ results.insert(results.end(), settings.begin(), settings.end());
+ }
+ else
+ {
+ if (command.structure.valid_arguments != nullptr)
+ {
+ results = command.structure.valid_arguments(paths);
+ }
+ }
+
+ Util::unstable_keep_if(results, [&](const std::string& s) {
+ return Strings::case_insensitive_ascii_starts_with(s, prefix);
+ });
+
+ if (command.name == "install" && results.size() == 1 && !is_option)
+ {
+ const auto port_at_each_triplet =
+ combine_port_with_triplets(results[0], paths.get_available_triplets());
+ Util::Vectors::concatenate(&results, port_at_each_triplet);
+ }
+
+ output_sorted_results_and_exit(VCPKG_LINE_INFO, std::move(results));
+ }
+ }
+
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+}
diff --git a/toolsrc/src/vcpkg/commands.buildexternal.cpp b/toolsrc/src/vcpkg/commands.buildexternal.cpp
new file mode 100644
index 000000000..82d03db48
--- /dev/null
+++ b/toolsrc/src/vcpkg/commands.buildexternal.cpp
@@ -0,0 +1,29 @@
+#include "pch.h"
+
+#include <vcpkg/build.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/help.h>
+#include <vcpkg/input.h>
+
+namespace vcpkg::Commands::BuildExternal
+{
+ const CommandStructure COMMAND_STRUCTURE = {
+ Help::create_example_string(R"(build_external zlib2 C:\path\to\dir\with\controlfile\)"),
+ 2,
+ 2,
+ {},
+ nullptr,
+ };
+
+ void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet)
+ {
+ const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
+
+ const FullPackageSpec spec = Input::check_and_get_full_package_spec(
+ args.command_arguments.at(0), default_triplet, COMMAND_STRUCTURE.example_text);
+ Input::check_triplet(spec.package_spec.triplet(), paths);
+
+ const fs::path port_dir = args.command_arguments.at(1);
+ Build::Command::perform_and_exit_ex(spec, port_dir, options, paths);
+ }
+}
diff --git a/toolsrc/src/commands_cache.cpp b/toolsrc/src/vcpkg/commands.cache.cpp
index 75de78461..464f4f9ee 100644
--- a/toolsrc/src/commands_cache.cpp
+++ b/toolsrc/src/vcpkg/commands.cache.cpp
@@ -1,10 +1,11 @@
#include "pch.h"
-#include "BinaryParagraph.h"
-#include "Paragraphs.h"
-#include "vcpkg_Commands.h"
-#include "vcpkg_Files.h"
-#include "vcpkg_System.h"
+#include <vcpkg/base/files.h>
+#include <vcpkg/base/system.h>
+#include <vcpkg/binaryparagraph.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/help.h>
+#include <vcpkg/paragraphs.h>
namespace vcpkg::Commands::Cache
{
@@ -15,7 +16,7 @@ namespace vcpkg::Commands::Cache
{
const Expected<std::unordered_map<std::string, std::string>> pghs =
Paragraphs::get_single_paragraph(paths.get_filesystem(), path / "CONTROL");
- if (auto p = pghs.get())
+ if (const auto p = pghs.get())
{
const BinaryParagraph binary_paragraph = BinaryParagraph(*p);
output.push_back(binary_paragraph);
@@ -25,13 +26,19 @@ namespace vcpkg::Commands::Cache
return output;
}
+ const CommandStructure COMMAND_STRUCTURE = {
+ Strings::format(
+ "The argument should be a substring to search for, or no argument to display all cached libraries.\n%s",
+ Help::create_example_string("cache png")),
+ 0,
+ 1,
+ {},
+ nullptr,
+ };
+
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
{
- static const std::string example = Strings::format(
- "The argument should be a substring to search for, or no argument to display all cached libraries.\n%s",
- Commands::Help::create_example_string("cache png"));
- args.check_max_arg_count(1, example);
- args.check_and_get_optional_command_arguments({});
+ Util::unused(args.parse_arguments(COMMAND_STRUCTURE));
const std::vector<BinaryParagraph> binary_paragraphs = read_all_binary_paragraphs(paths);
if (binary_paragraphs.empty())
@@ -40,7 +47,7 @@ namespace vcpkg::Commands::Cache
Checks::exit_success(VCPKG_LINE_INFO);
}
- if (args.command_arguments.size() == 0)
+ if (args.command_arguments.empty())
{
for (const BinaryParagraph& binary_paragraph : binary_paragraphs)
{
@@ -54,7 +61,7 @@ namespace vcpkg::Commands::Cache
for (const BinaryParagraph& binary_paragraph : binary_paragraphs)
{
const std::string displayname = binary_paragraph.displayname();
- if (Strings::case_insensitive_ascii_find(displayname, args.command_arguments[0]) == displayname.end())
+ if (!Strings::case_insensitive_ascii_contains(displayname, args.command_arguments[0]))
{
continue;
}
diff --git a/toolsrc/src/vcpkg/commands.ci.cpp b/toolsrc/src/vcpkg/commands.ci.cpp
new file mode 100644
index 000000000..e2b93dc7e
--- /dev/null
+++ b/toolsrc/src/vcpkg/commands.ci.cpp
@@ -0,0 +1,264 @@
+#include "pch.h"
+
+#include <vcpkg/base/cache.h>
+#include <vcpkg/base/files.h>
+#include <vcpkg/base/stringliteral.h>
+#include <vcpkg/base/system.h>
+#include <vcpkg/base/util.h>
+#include <vcpkg/build.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/dependencies.h>
+#include <vcpkg/globalstate.h>
+#include <vcpkg/help.h>
+#include <vcpkg/input.h>
+#include <vcpkg/install.h>
+#include <vcpkg/vcpkglib.h>
+
+namespace vcpkg::Commands::CI
+{
+ using Build::BuildResult;
+ using Dependencies::InstallPlanAction;
+ using Dependencies::InstallPlanType;
+
+ struct TripletAndSummary
+ {
+ Triplet triplet;
+ Install::InstallSummary summary;
+ };
+
+ static constexpr StringLiteral OPTION_DRY_RUN = "--dry-run";
+ static constexpr StringLiteral OPTION_EXCLUDE = "--exclude";
+ static constexpr StringLiteral OPTION_XUNIT = "--x-xunit";
+
+ static constexpr std::array<CommandSetting, 2> CI_SETTINGS = {{
+ {OPTION_EXCLUDE, "Comma separated list of ports to skip"},
+ {OPTION_XUNIT, "File to output results in XUnit format (internal)"},
+ }};
+
+ static constexpr std::array<CommandSwitch, 1> CI_SWITCHES = {
+ {{OPTION_DRY_RUN, "Print out plan without execution"}}};
+
+ const CommandStructure COMMAND_STRUCTURE = {
+ Help::create_example_string("ci x64-windows"),
+ 1,
+ SIZE_MAX,
+ {CI_SWITCHES, CI_SETTINGS},
+ nullptr,
+ };
+
+ UnknownCIPortsResults find_unknown_ports_for_ci(const VcpkgPaths& paths,
+ const std::set<std::string>& exclusions,
+ const Dependencies::PortFileProvider& provider,
+ const std::vector<FeatureSpec>& fspecs)
+ {
+ UnknownCIPortsResults ret;
+
+ auto& fs = paths.get_filesystem();
+
+ std::map<PackageSpec, std::string> abi_tag_map;
+ std::set<PackageSpec> will_fail;
+
+ const Build::BuildPackageOptions install_plan_options = {Build::UseHeadVersion::NO,
+ Build::AllowDownloads::YES,
+ Build::CleanBuildtrees::YES,
+ Build::CleanPackages::YES};
+
+ vcpkg::Cache<Triplet, Build::PreBuildInfo> pre_build_info_cache;
+
+ auto action_plan = Dependencies::create_feature_install_plan(provider, fspecs, StatusParagraphs{});
+
+ for (auto&& action : action_plan)
+ {
+ if (auto p = action.install_action.get())
+ {
+ // determine abi tag
+ std::string abi;
+ if (auto scf = p->source_control_file.get())
+ {
+ auto triplet = p->spec.triplet();
+
+ const Build::BuildPackageConfig build_config{
+ *scf, triplet, paths.port_dir(p->spec), install_plan_options, p->feature_list};
+
+ auto dependency_abis =
+ Util::fmap(p->computed_dependencies, [&](const PackageSpec& spec) -> Build::AbiEntry {
+ auto it = abi_tag_map.find(spec);
+
+ if (it == abi_tag_map.end())
+ return {spec.name(), ""};
+ else
+ return {spec.name(), it->second};
+ });
+ const auto& pre_build_info = pre_build_info_cache.get_lazy(
+ triplet, [&]() { return Build::PreBuildInfo::from_triplet_file(paths, triplet); });
+
+ auto maybe_tag_and_file =
+ Build::compute_abi_tag(paths, build_config, pre_build_info, dependency_abis);
+ if (auto tag_and_file = maybe_tag_and_file.get())
+ {
+ abi = tag_and_file->tag;
+ abi_tag_map.emplace(p->spec, abi);
+ }
+ }
+ else if (auto ipv = p->installed_package.get())
+ {
+ abi = ipv->core->package.abi;
+ if (!abi.empty()) abi_tag_map.emplace(p->spec, abi);
+ }
+
+ std::string state;
+
+ auto archives_root_dir = paths.root / "archives";
+ auto archive_name = abi + ".zip";
+ auto archive_subpath = fs::u8path(abi.substr(0, 2)) / archive_name;
+ auto archive_path = archives_root_dir / archive_subpath;
+ auto archive_tombstone_path = archives_root_dir / "fail" / archive_subpath;
+
+ bool b_will_build = false;
+
+ if (Util::Sets::contains(exclusions, p->spec.name()))
+ {
+ ret.known.emplace(p->spec, BuildResult::EXCLUDED);
+ will_fail.emplace(p->spec);
+ }
+ else if (std::any_of(p->computed_dependencies.begin(),
+ p->computed_dependencies.end(),
+ [&](const PackageSpec& spec) { return Util::Sets::contains(will_fail, spec); }))
+ {
+ ret.known.emplace(p->spec, BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES);
+ will_fail.emplace(p->spec);
+ }
+ else if (fs.exists(archive_path))
+ {
+ state += "pass";
+ ret.known.emplace(p->spec, BuildResult::SUCCEEDED);
+ }
+ else if (fs.exists(archive_tombstone_path))
+ {
+ state += "fail";
+ ret.known.emplace(p->spec, BuildResult::BUILD_FAILED);
+ will_fail.emplace(p->spec);
+ }
+ else
+ {
+ ret.unknown.push_back(p->spec);
+ b_will_build = true;
+ }
+
+ System::println("%40s: %1s %8s: %s", p->spec, (b_will_build ? "*" : " "), state, abi);
+ }
+ }
+
+ return ret;
+ }
+
+ void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet)
+ {
+ if (!GlobalState::g_binary_caching)
+ {
+ System::println(System::Color::warning, "Warning: Running ci without binary caching!");
+ }
+
+ const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
+
+ std::set<std::string> exclusions_set;
+ auto it_exclusions = options.settings.find(OPTION_EXCLUDE);
+ if (it_exclusions != options.settings.end())
+ {
+ auto exclusions = Strings::split(it_exclusions->second, ",");
+ exclusions_set.insert(exclusions.begin(), exclusions.end());
+ }
+
+ auto is_dry_run = Util::Sets::contains(options.switches, OPTION_DRY_RUN);
+
+ std::vector<Triplet> triplets;
+ for (const std::string& triplet : args.command_arguments)
+ {
+ triplets.push_back(Triplet::from_canonical_name(triplet));
+ }
+
+ if (triplets.empty())
+ {
+ triplets.push_back(default_triplet);
+ }
+
+ StatusParagraphs status_db = database_load_check(paths);
+ const auto& paths_port_file = Dependencies::PathsPortFileProvider(paths);
+
+ const Build::BuildPackageOptions install_plan_options = {Build::UseHeadVersion::NO,
+ Build::AllowDownloads::YES,
+ Build::CleanBuildtrees::YES,
+ Build::CleanPackages::YES};
+
+ std::vector<std::map<PackageSpec, BuildResult>> all_known_results;
+
+ std::vector<std::string> all_ports = Install::get_all_port_names(paths);
+ std::vector<TripletAndSummary> results;
+ for (const Triplet& triplet : triplets)
+ {
+ Input::check_triplet(triplet, paths);
+
+ std::vector<PackageSpec> specs = PackageSpec::to_package_specs(all_ports, triplet);
+ // Install the default features for every package
+ auto all_fspecs = Util::fmap(specs, [](auto& spec) { return FeatureSpec(spec, ""); });
+ auto split_specs = find_unknown_ports_for_ci(paths, exclusions_set, paths_port_file, all_fspecs);
+ auto fspecs = Util::fmap(split_specs.unknown, [](auto& spec) { return FeatureSpec(spec, ""); });
+
+ auto action_plan = Dependencies::create_feature_install_plan(paths_port_file, fspecs, status_db);
+
+ for (auto&& action : action_plan)
+ {
+ if (auto p = action.install_action.get())
+ {
+ p->build_options = install_plan_options;
+ if (Util::Sets::contains(exclusions_set, p->spec.name()))
+ {
+ p->plan_type = InstallPlanType::EXCLUDED;
+ }
+ }
+ }
+
+ if (is_dry_run)
+ {
+ Dependencies::print_plan(action_plan);
+ }
+ else
+ {
+ auto summary = Install::perform(action_plan, Install::KeepGoing::YES, paths, status_db);
+ for (auto&& result : summary.results)
+ split_specs.known.erase(result.spec);
+ results.push_back({triplet, std::move(summary)});
+ all_known_results.emplace_back(std::move(split_specs.known));
+ }
+ }
+
+ for (auto&& result : results)
+ {
+ System::println("\nTriplet: %s", result.triplet);
+ System::println("Total elapsed time: %s", result.summary.total_elapsed_time);
+ result.summary.print();
+ }
+
+ auto it_xunit = options.settings.find(OPTION_XUNIT);
+ if (it_xunit != options.settings.end())
+ {
+ std::string xunit_doc = "<assemblies><assembly><collection>\n";
+
+ for (auto&& result : results)
+ xunit_doc += result.summary.xunit_results();
+ for (auto&& known_result : all_known_results)
+ {
+ for (auto&& result : known_result)
+ {
+ xunit_doc +=
+ Install::InstallSummary::xunit_result(result.first, Chrono::ElapsedTime{}, result.second);
+ }
+ }
+
+ xunit_doc += "</collection></assembly></assemblies>\n";
+ paths.get_filesystem().write_contents(fs::u8path(it_xunit->second), xunit_doc);
+ }
+
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+}
diff --git a/toolsrc/src/vcpkg/commands.contact.cpp b/toolsrc/src/vcpkg/commands.contact.cpp
new file mode 100644
index 000000000..9f86a67dc
--- /dev/null
+++ b/toolsrc/src/vcpkg/commands.contact.cpp
@@ -0,0 +1,60 @@
+#include "pch.h"
+
+#include <vcpkg/base/chrono.h>
+#include <vcpkg/base/system.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/help.h>
+#include <vcpkg/userconfig.h>
+
+namespace vcpkg::Commands::Contact
+{
+ const std::string& email()
+ {
+ static const std::string S_EMAIL = R"(vcpkg@microsoft.com)";
+ return S_EMAIL;
+ }
+
+ static constexpr StringLiteral OPTION_SURVEY = "--survey";
+
+ static constexpr std::array<CommandSwitch, 1> SWITCHES = {{
+ {OPTION_SURVEY, "Launch default browser to the current vcpkg survey"},
+ }};
+
+ const CommandStructure COMMAND_STRUCTURE = {
+ Help::create_example_string("contact"),
+ 0,
+ 0,
+ {SWITCHES, {}},
+ nullptr,
+ };
+
+ void perform_and_exit(const VcpkgCmdArguments& args)
+ {
+ const ParsedArguments parsed_args = args.parse_arguments(COMMAND_STRUCTURE);
+
+ if (Util::Sets::contains(parsed_args.switches, SWITCHES[0].name))
+ {
+ auto maybe_now = Chrono::CTime::get_current_date_time();
+ if (const auto p_now = maybe_now.get())
+ {
+ auto& fs = Files::get_real_filesystem();
+ auto config = UserConfig::try_read_data(fs);
+ config.last_completed_survey = p_now->to_string();
+ config.try_write_data(fs);
+ }
+
+#if defined(_WIN32)
+ System::cmd_execute("start https://aka.ms/NPS_vcpkg");
+ System::println("Default browser launched to https://aka.ms/NPS_vcpkg; thank you for your feedback!");
+#else
+ System::println(
+ "Please navigate to https://aka.ms/NPS_vcpkg in your preferred browser. Thank you for your feedback!");
+#endif
+ }
+ else
+ {
+ System::println("Send an email to %s with any feedback.", email());
+ }
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+}
diff --git a/toolsrc/src/commands_available_commands.cpp b/toolsrc/src/vcpkg/commands.cpp
index 87cc43dca..09da57705 100644
--- a/toolsrc/src/commands_available_commands.cpp
+++ b/toolsrc/src/vcpkg/commands.cpp
@@ -1,16 +1,23 @@
#include "pch.h"
-#include "vcpkg_Commands.h"
+#include <vcpkg/build.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/export.h>
+#include <vcpkg/help.h>
+#include <vcpkg/install.h>
+#include <vcpkg/remove.h>
+#include <vcpkg/update.h>
namespace vcpkg::Commands
{
- const std::vector<PackageNameAndFunction<CommandTypeA>>& get_available_commands_type_a()
+ Span<const PackageNameAndFunction<CommandTypeA>> get_available_commands_type_a()
{
static std::vector<PackageNameAndFunction<CommandTypeA>> t = {
{"install", &Install::perform_and_exit},
{"ci", &CI::perform_and_exit},
{"remove", &Remove::perform_and_exit},
- {"build", &BuildCommand::perform_and_exit},
+ {"upgrade", &Upgrade::perform_and_exit},
+ {"build", &Build::Command::perform_and_exit},
{"env", &Env::perform_and_exit},
{"build-external", &BuildExternal::perform_and_exit},
{"export", &Export::perform_and_exit},
@@ -18,7 +25,7 @@ namespace vcpkg::Commands
return t;
}
- const std::vector<PackageNameAndFunction<CommandTypeB>>& get_available_commands_type_b()
+ Span<const PackageNameAndFunction<CommandTypeB>> get_available_commands_type_b()
{
static std::vector<PackageNameAndFunction<CommandTypeB>> t = {
{"/?", &Help::perform_and_exit},
@@ -34,16 +41,18 @@ namespace vcpkg::Commands
{"import", &Import::perform_and_exit},
{"cache", &Cache::perform_and_exit},
{"portsdiff", &PortsDiff::perform_and_exit},
+ {"autocomplete", &Autocomplete::perform_and_exit},
+ {"hash", &Hash::perform_and_exit},
+ {"fetch", &Fetch::perform_and_exit},
};
return t;
}
- const std::vector<PackageNameAndFunction<CommandTypeC>>& get_available_commands_type_c()
+ Span<const PackageNameAndFunction<CommandTypeC>> get_available_commands_type_c()
{
static std::vector<PackageNameAndFunction<CommandTypeC>> t = {
{"version", &Version::perform_and_exit},
{"contact", &Contact::perform_and_exit},
- {"hash", &Hash::perform_and_exit},
};
return t;
}
diff --git a/toolsrc/src/commands_create.cpp b/toolsrc/src/vcpkg/commands.create.cpp
index 7f85b776a..dfb3ab784 100644
--- a/toolsrc/src/commands_create.cpp
+++ b/toolsrc/src/vcpkg/commands.create.cpp
@@ -1,26 +1,30 @@
#include "pch.h"
-#include "vcpkg_Commands.h"
-#include "vcpkg_Files.h"
-#include "vcpkg_Input.h"
-#include "vcpkg_System.h"
-#include "vcpkglib.h"
+#include <vcpkg/base/files.h>
+#include <vcpkg/base/system.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/help.h>
namespace vcpkg::Commands::Create
{
+ const CommandStructure COMMAND_STRUCTURE = {
+ Help::create_example_string(
+ R"###(create zlib2 http://zlib.net/zlib1211.zip "zlib1211-2.zip")###"),
+ 2,
+ 3,
+ {},
+ nullptr,
+ };
+
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
{
- static const std::string example = Commands::Help::create_example_string(
- R"###(create zlib2 http://zlib.net/zlib1211.zip "zlib1211-2.zip")###");
- args.check_max_arg_count(3, example);
- args.check_min_arg_count(2, example);
- args.check_and_get_optional_command_arguments({});
+ Util::unused(args.parse_arguments(COMMAND_STRUCTURE));
const std::string port_name = args.command_arguments.at(0);
const std::string url = args.command_arguments.at(1);
- const fs::path& cmake_exe = paths.get_cmake_exe();
+ const fs::path& cmake_exe = paths.get_tool_exe(Tools::CMAKE);
- std::vector<CMakeVariable> cmake_args{{L"CMD", L"CREATE"}, {L"PORT", port_name}, {L"URL", url}};
+ std::vector<System::CMakeVariable> cmake_args{{"CMD", "CREATE"}, {"PORT", port_name}, {"URL", url}};
if (args.command_arguments.size() >= 3)
{
@@ -30,10 +34,10 @@ namespace vcpkg::Commands::Create
R"(Filename cannot contain invalid chars %s, but was %s)",
Files::FILESYSTEM_INVALID_CHARACTERS,
zip_file_name);
- cmake_args.push_back({L"FILENAME", zip_file_name});
+ cmake_args.emplace_back("FILENAME", zip_file_name);
}
- const std::wstring cmd_launch_cmake = make_cmake_cmd(cmake_exe, paths.ports_cmake, cmake_args);
+ const std::string cmd_launch_cmake = make_cmake_cmd(cmake_exe, paths.ports_cmake, cmake_args);
Checks::exit_with_code(VCPKG_LINE_INFO, System::cmd_execute_clean(cmd_launch_cmake));
}
}
diff --git a/toolsrc/src/vcpkg/commands.dependinfo.cpp b/toolsrc/src/vcpkg/commands.dependinfo.cpp
new file mode 100644
index 000000000..1ca658216
--- /dev/null
+++ b/toolsrc/src/vcpkg/commands.dependinfo.cpp
@@ -0,0 +1,61 @@
+#include "pch.h"
+
+#include <vcpkg/base/strings.h>
+#include <vcpkg/base/system.h>
+#include <vcpkg/base/util.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/help.h>
+#include <vcpkg/paragraphs.h>
+
+namespace vcpkg::Commands::DependInfo
+{
+ const CommandStructure COMMAND_STRUCTURE = {
+ Help::create_example_string(R"###(depend-info [pat])###"),
+ 0,
+ 1,
+ {},
+ nullptr,
+ };
+
+ void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
+ {
+ Util::unused(args.parse_arguments(COMMAND_STRUCTURE));
+
+ std::vector<std::unique_ptr<SourceControlFile>> source_control_files =
+ Paragraphs::load_all_ports(paths.get_filesystem(), paths.ports);
+
+ if (args.command_arguments.size() == 1)
+ {
+ const std::string filter = args.command_arguments.at(0);
+
+ Util::erase_remove_if(source_control_files,
+ [&](const std::unique_ptr<SourceControlFile>& source_control_file) {
+ const SourceParagraph& source_paragraph = *source_control_file->core_paragraph;
+
+ if (Strings::case_insensitive_ascii_contains(source_paragraph.name, filter))
+ {
+ return false;
+ }
+
+ for (const Dependency& dependency : source_paragraph.depends)
+ {
+ if (Strings::case_insensitive_ascii_contains(dependency.name(), filter))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ });
+ }
+
+ for (auto&& source_control_file : source_control_files)
+ {
+ const SourceParagraph& source_paragraph = *source_control_file->core_paragraph;
+ const auto s = Strings::join(", ", source_paragraph.depends, [](const Dependency& d) { return d.name(); });
+ System::println("%s: %s", source_paragraph.name, s);
+ }
+
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+}
diff --git a/toolsrc/src/vcpkg/commands.edit.cpp b/toolsrc/src/vcpkg/commands.edit.cpp
new file mode 100644
index 000000000..2569c2cea
--- /dev/null
+++ b/toolsrc/src/vcpkg/commands.edit.cpp
@@ -0,0 +1,152 @@
+#include "pch.h"
+
+#include <vcpkg/base/strings.h>
+#include <vcpkg/base/system.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/help.h>
+#include <vcpkg/paragraphs.h>
+
+namespace vcpkg::Commands::Edit
+{
+ static std::vector<fs::path> find_from_registry()
+ {
+ std::vector<fs::path> output;
+
+#if defined(_WIN32)
+ static const std::array<const char*, 3> REGKEYS = {
+ R"(SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{C26E74D1-022E-4238-8B9D-1E7564A36CC9}_is1)",
+ R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{1287CAD5-7C8D-410D-88B9-0D1EE4A83FF2}_is1)",
+ R"(SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{F8A2A208-72B3-4D61-95FC-8A65D340689B}_is1)",
+ };
+
+ for (auto&& keypath : REGKEYS)
+ {
+ const Optional<std::string> code_installpath =
+ System::get_registry_string(HKEY_LOCAL_MACHINE, keypath, "InstallLocation");
+ if (const auto c = code_installpath.get())
+ {
+ const fs::path install_path = fs::path(*c);
+ output.push_back(install_path / "Code - Insiders.exe");
+ output.push_back(install_path / "Code.exe");
+ }
+ }
+#endif
+ return output;
+ }
+
+ static constexpr StringLiteral OPTION_BUILDTREES = "--buildtrees";
+
+ static constexpr StringLiteral OPTION_ALL = "--all";
+
+ static std::vector<std::string> valid_arguments(const VcpkgPaths& paths)
+ {
+ auto sources_and_errors = Paragraphs::try_load_all_ports(paths.get_filesystem(), paths.ports);
+
+ return Util::fmap(sources_and_errors.paragraphs,
+ [](auto&& pgh) -> std::string { return pgh->core_paragraph->name; });
+ }
+
+ static constexpr std::array<CommandSwitch, 2> EDIT_SWITCHES = {
+ {{OPTION_BUILDTREES, "Open editor into the port-specific buildtree subfolder"},
+ {OPTION_ALL, "Open editor into the port as well as the port-specific buildtree subfolder"}}};
+
+ const CommandStructure COMMAND_STRUCTURE = {
+ Help::create_example_string("edit zlib"),
+ 1,
+ 10,
+ {EDIT_SWITCHES, {}},
+ &valid_arguments,
+ };
+
+ static std::vector<std::string> create_editor_arguments(const VcpkgPaths& paths,
+ const ParsedArguments& options,
+ const std::vector<std::string>& ports)
+ {
+ if (Util::Sets::contains(options.switches, OPTION_ALL))
+ {
+ return Util::fmap(ports, [&](const std::string& port_name) -> std::string {
+ const auto portpath = paths.ports / port_name;
+ const auto portfile = portpath / "portfile.cmake";
+ const auto buildtrees_current_dir = paths.buildtrees / port_name;
+ return Strings::format(R"###("%s" "%s" "%s")###",
+ portpath.u8string(),
+ portfile.u8string(),
+ buildtrees_current_dir.u8string());
+ });
+ }
+
+ if (Util::Sets::contains(options.switches, OPTION_BUILDTREES))
+ {
+ return Util::fmap(ports, [&](const std::string& port_name) -> std::string {
+ const auto buildtrees_current_dir = paths.buildtrees / port_name;
+ return Strings::format(R"###("%s")###", buildtrees_current_dir.u8string());
+ });
+ }
+
+ return Util::fmap(ports, [&](const std::string& port_name) -> std::string {
+ const auto portpath = paths.ports / port_name;
+ const auto portfile = portpath / "portfile.cmake";
+ return Strings::format(R"###("%s" "%s")###", portpath.u8string(), portfile.u8string());
+ });
+ }
+
+ void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
+ {
+ static const fs::path VS_CODE_INSIDERS = fs::path{"Microsoft VS Code Insiders"} / "Code - Insiders.exe";
+ static const fs::path VS_CODE = fs::path{"Microsoft VS Code"} / "Code.exe";
+
+ auto& fs = paths.get_filesystem();
+
+ const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
+
+ const std::vector<std::string>& ports = args.command_arguments;
+ for (auto&& port_name : ports)
+ {
+ const fs::path portpath = paths.ports / port_name;
+ Checks::check_exit(
+ VCPKG_LINE_INFO, fs.is_directory(portpath), R"(Could not find port named "%s")", port_name);
+ }
+
+ std::vector<fs::path> candidate_paths;
+ auto maybe_editor_path = System::get_environment_variable("EDITOR");
+ if (const std::string* editor_path = maybe_editor_path.get())
+ {
+ candidate_paths.emplace_back(*editor_path);
+ }
+
+ const auto& program_files = System::get_program_files_platform_bitness();
+ if (const fs::path* pf = program_files.get())
+ {
+ candidate_paths.push_back(*pf / VS_CODE_INSIDERS);
+ candidate_paths.push_back(*pf / VS_CODE);
+ }
+
+ const auto& program_files_32_bit = System::get_program_files_32_bit();
+ if (const fs::path* pf = program_files_32_bit.get())
+ {
+ candidate_paths.push_back(*pf / VS_CODE_INSIDERS);
+ candidate_paths.push_back(*pf / VS_CODE);
+ }
+
+ const std::vector<fs::path> from_registry = find_from_registry();
+ candidate_paths.insert(candidate_paths.end(), from_registry.cbegin(), from_registry.cend());
+
+ const auto it = Util::find_if(candidate_paths, [&](const fs::path& p) { return fs.exists(p); });
+ if (it == candidate_paths.cend())
+ {
+ System::println(
+ System::Color::error,
+ "Error: Visual Studio Code was not found and the environment variable EDITOR is not set or invalid.");
+ System::println("The following paths were examined:");
+ Files::print_paths(candidate_paths);
+ System::println("You can also set the environmental variable EDITOR to your editor of choice.");
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+
+ const fs::path env_editor = *it;
+ const std::vector<std::string> arguments = create_editor_arguments(paths, options, ports);
+ const auto args_as_string = Strings::join(" ", arguments);
+ const auto cmd_line = Strings::format(R"("%s" %s -n)", env_editor.u8string(), args_as_string);
+ Checks::exit_with_code(VCPKG_LINE_INFO, System::cmd_execute(cmd_line));
+ }
+}
diff --git a/toolsrc/src/vcpkg/commands.env.cpp b/toolsrc/src/vcpkg/commands.env.cpp
new file mode 100644
index 000000000..d078baedb
--- /dev/null
+++ b/toolsrc/src/vcpkg/commands.env.cpp
@@ -0,0 +1,74 @@
+#include "pch.h"
+
+#include <vcpkg/base/strings.h>
+#include <vcpkg/base/system.h>
+#include <vcpkg/build.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/help.h>
+
+namespace vcpkg::Commands::Env
+{
+ static constexpr StringLiteral OPTION_BIN = "--bin";
+ static constexpr StringLiteral OPTION_INCLUDE = "--include";
+ static constexpr StringLiteral OPTION_DEBUG_BIN = "--debug-bin";
+ static constexpr StringLiteral OPTION_TOOLS = "--tools";
+ static constexpr StringLiteral OPTION_PYTHON = "--python";
+
+ static constexpr std::array<CommandSwitch, 5> SWITCHES = {{
+ {OPTION_BIN, "Add installed bin/ to PATH"},
+ {OPTION_INCLUDE, "Add installed include/ to INCLUDE"},
+ {OPTION_DEBUG_BIN, "Add installed debug/bin/ to PATH"},
+ {OPTION_TOOLS, "Add installed tools/*/ to PATH"},
+ {OPTION_PYTHON, "Add installed python/ to PYTHONPATH"},
+ }};
+
+ const CommandStructure COMMAND_STRUCTURE = {
+ Help::create_example_string("env --triplet x64-windows"),
+ 0,
+ 0,
+ {SWITCHES, {}},
+ nullptr,
+ };
+
+ void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& triplet)
+ {
+ const auto& fs = paths.get_filesystem();
+
+ const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
+
+ const auto pre_build_info = Build::PreBuildInfo::from_triplet_file(paths, triplet);
+ const Toolset& toolset = paths.get_toolset(pre_build_info);
+ auto env_cmd = Build::make_build_env_cmd(pre_build_info, toolset);
+
+ std::unordered_map<std::string, std::string> extra_env = {};
+ const bool add_bin = Util::Sets::contains(options.switches, OPTION_BIN);
+ const bool add_include = Util::Sets::contains(options.switches, OPTION_INCLUDE);
+ const bool add_debug_bin = Util::Sets::contains(options.switches, OPTION_DEBUG_BIN);
+ const bool add_tools = Util::Sets::contains(options.switches, OPTION_TOOLS);
+ const bool add_python = Util::Sets::contains(options.switches, OPTION_PYTHON);
+
+ std::vector<std::string> path_vars;
+ if (add_bin) path_vars.push_back((paths.installed / triplet.to_string() / "bin").u8string());
+ if (add_debug_bin) path_vars.push_back((paths.installed / triplet.to_string() / "debug" / "bin").u8string());
+ if (add_include) extra_env.emplace("INCLUDE", (paths.installed / triplet.to_string() / "include").u8string());
+ if (add_tools)
+ {
+ auto tools_dir = paths.installed / triplet.to_string() / "tools";
+ auto tool_files = fs.get_files_non_recursive(tools_dir);
+ path_vars.push_back(tools_dir.u8string());
+ for (auto&& tool_dir : tool_files)
+ {
+ if (fs.is_directory(tool_dir)) path_vars.push_back(tool_dir.u8string());
+ }
+ }
+ if (add_python) extra_env.emplace("PYTHONPATH", (paths.installed / triplet.to_string() / "python").u8string());
+ if (path_vars.size() > 0) extra_env.emplace("PATH", Strings::join(";", path_vars));
+
+ if (env_cmd.empty())
+ System::cmd_execute_clean("cmd", extra_env);
+ else
+ System::cmd_execute_clean(env_cmd + " && cmd", extra_env);
+
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+}
diff --git a/toolsrc/src/vcpkg/commands.exportifw.cpp b/toolsrc/src/vcpkg/commands.exportifw.cpp
new file mode 100644
index 000000000..ae106196a
--- /dev/null
+++ b/toolsrc/src/vcpkg/commands.exportifw.cpp
@@ -0,0 +1,487 @@
+#include "pch.h"
+
+#include <vcpkg/commands.h>
+#include <vcpkg/export.h>
+#include <vcpkg/export.ifw.h>
+#include <vcpkg/install.h>
+
+namespace vcpkg::Export::IFW
+{
+ using Dependencies::ExportPlanAction;
+ using Dependencies::ExportPlanType;
+ using Install::InstallDir;
+
+ static std::string create_release_date()
+ {
+ const tm date_time = System::get_current_date_time();
+
+ // Format is: YYYY-mm-dd
+ // 10 characters + 1 null terminating character will be written for a total of 11 chars
+ char mbstr[11];
+ const size_t bytes_written = std::strftime(mbstr, sizeof(mbstr), "%Y-%m-%d", &date_time);
+ Checks::check_exit(VCPKG_LINE_INFO,
+ bytes_written == 10,
+ "Expected 10 bytes to be written, but %u were written",
+ bytes_written);
+ const std::string date_time_as_string(mbstr);
+ return date_time_as_string;
+ }
+
+ std::string safe_rich_from_plain_text(const std::string& text)
+ {
+ // match standalone ampersand, no HTML number or name
+ std::regex standalone_ampersand(R"###(&(?!(#[0-9]+|\w+);))###");
+
+ return std::regex_replace(text, standalone_ampersand, "&amp;");
+ }
+
+ fs::path get_packages_dir_path(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths)
+ {
+ return ifw_options.maybe_packages_dir_path.has_value()
+ ? fs::path(ifw_options.maybe_packages_dir_path.value_or_exit(VCPKG_LINE_INFO))
+ : paths.root / (export_id + "-ifw-packages");
+ }
+
+ fs::path get_repository_dir_path(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths)
+ {
+ return ifw_options.maybe_repository_dir_path.has_value()
+ ? fs::path(ifw_options.maybe_repository_dir_path.value_or_exit(VCPKG_LINE_INFO))
+ : paths.root / (export_id + "-ifw-repository");
+ }
+
+ fs::path get_config_file_path(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths)
+ {
+ return ifw_options.maybe_config_file_path.has_value()
+ ? fs::path(ifw_options.maybe_config_file_path.value_or_exit(VCPKG_LINE_INFO))
+ : paths.root / (export_id + "-ifw-configuration.xml");
+ }
+
+ fs::path get_installer_file_path(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths)
+ {
+ return ifw_options.maybe_installer_file_path.has_value()
+ ? fs::path(ifw_options.maybe_installer_file_path.value_or_exit(VCPKG_LINE_INFO))
+ : paths.root / (export_id + "-ifw-installer.exe");
+ }
+
+ fs::path export_real_package(const fs::path& ifw_packages_dir_path,
+ const ExportPlanAction& action,
+ Files::Filesystem& fs)
+ {
+ std::error_code ec;
+
+ const BinaryParagraph& binary_paragraph = action.core_paragraph().value_or_exit(VCPKG_LINE_INFO);
+
+ // Prepare meta dir
+ const fs::path package_xml_file_path =
+ ifw_packages_dir_path /
+ Strings::format("packages.%s.%s", action.spec.name(), action.spec.triplet().canonical_name()) / "meta" /
+ "package.xml";
+ const fs::path package_xml_dir_path = package_xml_file_path.parent_path();
+ fs.create_directories(package_xml_dir_path, ec);
+ Checks::check_exit(VCPKG_LINE_INFO,
+ !ec,
+ "Could not create directory for package file %s",
+ package_xml_file_path.generic_string());
+
+ auto deps = Strings::join(
+ ",", binary_paragraph.depends, [](const std::string& dep) { return "packages." + dep + ":"; });
+
+ if (!deps.empty()) deps = "\n <Dependencies>" + deps + "</Dependencies>";
+
+ fs.write_contents(package_xml_file_path,
+ Strings::format(
+ R"###(<?xml version="1.0"?>
+<Package>
+ <DisplayName>%s</DisplayName>
+ <Version>%s</Version>
+ <ReleaseDate>%s</ReleaseDate>
+ <AutoDependOn>packages.%s:,triplets.%s:</AutoDependOn>%s
+ <Virtual>true</Virtual>
+</Package>
+)###",
+ action.spec.to_string(),
+ binary_paragraph.version,
+ create_release_date(),
+ action.spec.name(),
+ action.spec.triplet().canonical_name(),
+ deps));
+
+ // Return dir path for export package data
+ return ifw_packages_dir_path /
+ Strings::format("packages.%s.%s", action.spec.name(), action.spec.triplet().canonical_name()) / "data" /
+ "installed";
+ }
+
+ void export_unique_packages(const fs::path& raw_exported_dir_path,
+ std::map<std::string, const ExportPlanAction*> unique_packages,
+ Files::Filesystem& fs)
+ {
+ std::error_code ec;
+
+ // packages
+
+ fs::path package_xml_file_path = raw_exported_dir_path / "packages" / "meta" / "package.xml";
+ fs::path package_xml_dir_path = package_xml_file_path.parent_path();
+ fs.create_directories(package_xml_dir_path, ec);
+ Checks::check_exit(VCPKG_LINE_INFO,
+ !ec,
+ "Could not create directory for package file %s",
+ package_xml_file_path.generic_string());
+ fs.write_contents(package_xml_file_path,
+ Strings::format(
+ R"###(<?xml version="1.0"?>
+<Package>
+ <DisplayName>Packages</DisplayName>
+ <Version>1.0.0</Version>
+ <ReleaseDate>%s</ReleaseDate>
+</Package>
+)###",
+ create_release_date()));
+
+ for (const auto& unique_package : unique_packages)
+ {
+ const ExportPlanAction& action = *(unique_package.second);
+ const BinaryParagraph& binary_paragraph = action.core_paragraph().value_or_exit(VCPKG_LINE_INFO);
+
+ package_xml_file_path =
+ raw_exported_dir_path / Strings::format("packages.%s", unique_package.first) / "meta" / "package.xml";
+ package_xml_dir_path = package_xml_file_path.parent_path();
+ fs.create_directories(package_xml_dir_path, ec);
+ Checks::check_exit(VCPKG_LINE_INFO,
+ !ec,
+ "Could not create directory for package file %s",
+ package_xml_file_path.generic_string());
+
+ fs.write_contents(package_xml_file_path,
+ Strings::format(
+ R"###(<?xml version="1.0"?>
+<Package>
+ <DisplayName>%s</DisplayName>
+ <Description>%s</Description>
+ <Version>%s</Version>
+ <ReleaseDate>%s</ReleaseDate>
+</Package>
+)###",
+ action.spec.name(),
+ safe_rich_from_plain_text(binary_paragraph.description),
+ binary_paragraph.version,
+ create_release_date()));
+ }
+ }
+
+ void export_unique_triplets(const fs::path& raw_exported_dir_path,
+ std::set<std::string> unique_triplets,
+ Files::Filesystem& fs)
+ {
+ std::error_code ec;
+
+ // triplets
+
+ fs::path package_xml_file_path = raw_exported_dir_path / "triplets" / "meta" / "package.xml";
+ fs::path package_xml_dir_path = package_xml_file_path.parent_path();
+ fs.create_directories(package_xml_dir_path, ec);
+ Checks::check_exit(VCPKG_LINE_INFO,
+ !ec,
+ "Could not create directory for package file %s",
+ package_xml_file_path.generic_string());
+ fs.write_contents(package_xml_file_path,
+ Strings::format(
+ R"###(<?xml version="1.0"?>
+<Package>
+ <DisplayName>Triplets</DisplayName>
+ <Version>1.0.0</Version>
+ <ReleaseDate>%s</ReleaseDate>
+</Package>
+)###",
+ create_release_date()));
+
+ for (const std::string& triplet : unique_triplets)
+ {
+ package_xml_file_path =
+ raw_exported_dir_path / Strings::format("triplets.%s", triplet) / "meta" / "package.xml";
+ package_xml_dir_path = package_xml_file_path.parent_path();
+ fs.create_directories(package_xml_dir_path, ec);
+ Checks::check_exit(VCPKG_LINE_INFO,
+ !ec,
+ "Could not create directory for package file %s",
+ package_xml_file_path.generic_string());
+ fs.write_contents(package_xml_file_path,
+ Strings::format(
+ R"###(<?xml version="1.0"?>
+<Package>
+ <DisplayName>%s</DisplayName>
+ <Version>1.0.0</Version>
+ <ReleaseDate>%s</ReleaseDate>
+</Package>
+)###",
+ triplet,
+ create_release_date()));
+ }
+ }
+
+ void export_integration(const fs::path& raw_exported_dir_path, Files::Filesystem& fs)
+ {
+ std::error_code ec;
+
+ // integration
+ fs::path package_xml_file_path = raw_exported_dir_path / "integration" / "meta" / "package.xml";
+ fs::path package_xml_dir_path = package_xml_file_path.parent_path();
+ fs.create_directories(package_xml_dir_path, ec);
+ Checks::check_exit(VCPKG_LINE_INFO,
+ !ec,
+ "Could not create directory for package file %s",
+ package_xml_file_path.generic_string());
+
+ fs.write_contents(package_xml_file_path,
+ Strings::format(
+ R"###(<?xml version="1.0"?>
+<Package>
+ <DisplayName>Integration</DisplayName>
+ <Version>1.0.0</Version>
+ <ReleaseDate>%s</ReleaseDate>
+</Package>
+)###",
+ create_release_date()));
+ }
+
+ void export_config(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths)
+ {
+ std::error_code ec;
+ Files::Filesystem& fs = paths.get_filesystem();
+
+ const fs::path config_xml_file_path = get_config_file_path(export_id, ifw_options, paths);
+
+ fs::path config_xml_dir_path = config_xml_file_path.parent_path();
+ fs.create_directories(config_xml_dir_path, ec);
+ Checks::check_exit(VCPKG_LINE_INFO,
+ !ec,
+ "Could not create directory for configuration file %s",
+ config_xml_file_path.generic_string());
+
+ std::string formatted_repo_url;
+ std::string ifw_repo_url = ifw_options.maybe_repository_url.value_or("");
+ if (!ifw_repo_url.empty())
+ {
+ formatted_repo_url = Strings::format(R"###(
+ <RemoteRepositories>
+ <Repository>
+ <Url>%s</Url>
+ </Repository>
+ </RemoteRepositories>)###",
+ ifw_repo_url);
+ }
+
+ fs.write_contents(config_xml_file_path,
+ Strings::format(
+ R"###(<?xml version="1.0"?>
+<Installer>
+ <Name>vcpkg</Name>
+ <Version>1.0.0</Version>
+ <StartMenuDir>vcpkg</StartMenuDir>
+ <TargetDir>@RootDir@/src/vcpkg</TargetDir>%s
+</Installer>
+)###",
+ formatted_repo_url));
+ }
+
+ void export_maintenance_tool(const fs::path& ifw_packages_dir_path, const VcpkgPaths& paths)
+ {
+ System::println("Exporting maintenance tool... ");
+
+ std::error_code ec;
+ Files::Filesystem& fs = paths.get_filesystem();
+
+ const fs::path& installerbase_exe = paths.get_tool_exe(Tools::IFW_INSTALLER_BASE);
+ fs::path tempmaintenancetool = ifw_packages_dir_path / "maintenance" / "data" / "tempmaintenancetool.exe";
+ fs.create_directories(tempmaintenancetool.parent_path(), ec);
+ Checks::check_exit(VCPKG_LINE_INFO,
+ !ec,
+ "Could not create directory for package file %s",
+ tempmaintenancetool.generic_string());
+ fs.copy_file(installerbase_exe, tempmaintenancetool, fs::copy_options::overwrite_existing, ec);
+ Checks::check_exit(
+ VCPKG_LINE_INFO, !ec, "Could not write package file %s", tempmaintenancetool.generic_string());
+
+ fs::path package_xml_file_path = ifw_packages_dir_path / "maintenance" / "meta" / "package.xml";
+ fs::path package_xml_dir_path = package_xml_file_path.parent_path();
+ fs.create_directories(package_xml_dir_path, ec);
+ Checks::check_exit(VCPKG_LINE_INFO,
+ !ec,
+ "Could not create directory for package file %s",
+ package_xml_file_path.generic_string());
+ fs.write_contents(package_xml_file_path,
+ Strings::format(
+ R"###(<?xml version="1.0"?>
+<Package>
+ <DisplayName>Maintenance Tool</DisplayName>
+ <Description>Maintenance Tool</Description>
+ <Version>1.0.0</Version>
+ <ReleaseDate>%s</ReleaseDate>
+ <Script>maintenance.qs</Script>
+ <Essential>true</Essential>
+ <Virtual>true</Virtual>
+ <ForcedInstallation>true</ForcedInstallation>
+</Package>
+)###",
+ create_release_date()));
+ const fs::path script_source = paths.root / "scripts" / "ifw" / "maintenance.qs";
+ const fs::path script_destination = ifw_packages_dir_path / "maintenance" / "meta" / "maintenance.qs";
+ fs.copy_file(script_source, script_destination, fs::copy_options::overwrite_existing, ec);
+ Checks::check_exit(
+ VCPKG_LINE_INFO, !ec, "Could not write package file %s", script_destination.generic_string());
+
+ System::println("Exporting maintenance tool... done");
+ }
+
+ void do_repository(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths)
+ {
+ const fs::path& repogen_exe = paths.get_tool_exe(Tools::IFW_REPOGEN);
+ const fs::path packages_dir = get_packages_dir_path(export_id, ifw_options, paths);
+ const fs::path repository_dir = get_repository_dir_path(export_id, ifw_options, paths);
+
+ System::println("Generating repository %s...", repository_dir.generic_string());
+
+ std::error_code ec;
+ Files::Filesystem& fs = paths.get_filesystem();
+
+ fs.remove_all(repository_dir, ec);
+ Checks::check_exit(
+ VCPKG_LINE_INFO, !ec, "Could not remove outdated repository directory %s", repository_dir.generic_string());
+
+ const auto cmd_line = Strings::format(R"("%s" --packages "%s" "%s" > nul)",
+ repogen_exe.u8string(),
+ packages_dir.u8string(),
+ repository_dir.u8string());
+
+ const int exit_code = System::cmd_execute_clean(cmd_line);
+ Checks::check_exit(VCPKG_LINE_INFO, exit_code == 0, "Error: IFW repository generating failed");
+
+ System::println(System::Color::success, "Generating repository %s... done.", repository_dir.generic_string());
+ }
+
+ void do_installer(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths)
+ {
+ const fs::path& binarycreator_exe = paths.get_tool_exe(Tools::IFW_BINARYCREATOR);
+ const fs::path config_file = get_config_file_path(export_id, ifw_options, paths);
+ const fs::path packages_dir = get_packages_dir_path(export_id, ifw_options, paths);
+ const fs::path repository_dir = get_repository_dir_path(export_id, ifw_options, paths);
+ const fs::path installer_file = get_installer_file_path(export_id, ifw_options, paths);
+
+ System::println("Generating installer %s...", installer_file.generic_string());
+
+ std::string cmd_line;
+
+ std::string ifw_repo_url = ifw_options.maybe_repository_url.value_or("");
+ if (!ifw_repo_url.empty())
+ {
+ cmd_line = Strings::format(R"("%s" --online-only --config "%s" --repository "%s" "%s" > nul)",
+ binarycreator_exe.u8string(),
+ config_file.u8string(),
+ repository_dir.u8string(),
+ installer_file.u8string());
+ }
+ else
+ {
+ cmd_line = Strings::format(R"("%s" --config "%s" --packages "%s" "%s" > nul)",
+ binarycreator_exe.u8string(),
+ config_file.u8string(),
+ packages_dir.u8string(),
+ installer_file.u8string());
+ }
+
+ const int exit_code = System::cmd_execute_clean(cmd_line);
+ Checks::check_exit(VCPKG_LINE_INFO, exit_code == 0, "Error: IFW installer generating failed");
+
+ System::println(System::Color::success, "Generating installer %s... done.", installer_file.generic_string());
+ }
+
+ void do_export(const std::vector<ExportPlanAction>& export_plan,
+ const std::string& export_id,
+ const Options& ifw_options,
+ const VcpkgPaths& paths)
+ {
+ std::error_code ec;
+ 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);
+ Checks::check_exit(VCPKG_LINE_INFO,
+ !ec,
+ "Could not remove outdated packages directory %s",
+ ifw_packages_dir_path.generic_string());
+
+ fs.create_directory(ifw_packages_dir_path, ec);
+ Checks::check_exit(
+ VCPKG_LINE_INFO, !ec, "Could not create packages directory %s", ifw_packages_dir_path.generic_string());
+
+ // Export maintenance tool
+ export_maintenance_tool(ifw_packages_dir_path, paths);
+
+ System::println("Exporting packages %s... ", ifw_packages_dir_path.generic_string());
+
+ // execute the plan
+ std::map<std::string, const ExportPlanAction*> unique_packages;
+ std::set<std::string> unique_triplets;
+ for (const ExportPlanAction& action : export_plan)
+ {
+ if (action.plan_type != ExportPlanType::ALREADY_BUILT)
+ {
+ Checks::unreachable(VCPKG_LINE_INFO);
+ }
+
+ const std::string display_name = action.spec.to_string();
+ System::println("Exporting package %s... ", display_name);
+
+ const BinaryParagraph& binary_paragraph = action.core_paragraph().value_or_exit(VCPKG_LINE_INFO);
+
+ unique_packages[action.spec.name()] = &action;
+ unique_triplets.insert(action.spec.triplet().canonical_name());
+
+ // Export real package and return data dir for installation
+ fs::path ifw_package_dir_path = export_real_package(ifw_packages_dir_path, action, fs);
+
+ // Copy package data
+ const InstallDir dirs = InstallDir::from_destination_root(ifw_package_dir_path,
+ action.spec.triplet().to_string(),
+ ifw_package_dir_path / "vcpkg" / "info" /
+ (binary_paragraph.fullstem() + ".list"));
+
+ Install::install_files_and_write_listfile(paths.get_filesystem(), paths.package_dir(action.spec), dirs);
+ System::println("Exporting package %s... done", display_name);
+ }
+
+ System::println("Exporting packages %s... done", ifw_packages_dir_path.generic_string());
+
+ const fs::path config_file = get_config_file_path(export_id, ifw_options, paths);
+
+ System::println("Generating configuration %s...", config_file.generic_string());
+
+ // Unique packages
+ export_unique_packages(ifw_packages_dir_path, unique_packages, fs);
+
+ // Unique triplets
+ export_unique_triplets(ifw_packages_dir_path, unique_triplets, fs);
+
+ // Copy files needed for integration
+ export_integration_files(ifw_packages_dir_path / "integration" / "data", paths);
+ // Integration
+ export_integration(ifw_packages_dir_path, fs);
+
+ // Configuration
+ export_config(export_id, ifw_options, paths);
+
+ System::println("Generating configuration %s... done.", config_file.generic_string());
+
+ // Do repository (optional)
+ std::string ifw_repo_url = ifw_options.maybe_repository_url.value_or("");
+ if (!ifw_repo_url.empty())
+ {
+ do_repository(export_id, ifw_options, paths);
+ }
+
+ // Do installer
+ do_installer(export_id, ifw_options, paths);
+ }
+}
diff --git a/toolsrc/src/vcpkg/commands.fetch.cpp b/toolsrc/src/vcpkg/commands.fetch.cpp
new file mode 100644
index 000000000..33c5c1dcc
--- /dev/null
+++ b/toolsrc/src/vcpkg/commands.fetch.cpp
@@ -0,0 +1,965 @@
+#include "pch.h"
+
+#include <vcpkg/base/checks.h>
+#include <vcpkg/base/sortedvector.h>
+#include <vcpkg/base/strings.h>
+#include <vcpkg/base/system.h>
+#include <vcpkg/base/util.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/help.h>
+
+namespace vcpkg::Commands::Fetch
+{
+ static constexpr CStringView V_120 = "v120";
+ static constexpr CStringView V_140 = "v140";
+ static constexpr CStringView V_141 = "v141";
+
+ struct ToolData
+ {
+ std::array<int, 3> version;
+ fs::path exe_path;
+ std::string url;
+ fs::path download_path;
+ bool is_archive;
+ fs::path tool_dir_path;
+ std::string sha512;
+ };
+
+ static Optional<std::array<int, 3>> parse_version_string(const std::string& version_as_string)
+ {
+ static const std::regex RE(R"###((\d+)\.(\d+)\.(\d+))###");
+
+ std::match_results<std::string::const_iterator> match;
+ const auto found = std::regex_search(version_as_string, match, RE);
+ if (!found)
+ {
+ return {};
+ }
+
+ const int d1 = atoi(match[1].str().c_str());
+ const int d2 = atoi(match[2].str().c_str());
+ const int d3 = atoi(match[3].str().c_str());
+ const std::array<int, 3> result = {d1, d2, d3};
+ return result;
+ }
+
+ struct VcpkgStringRange
+ {
+ VcpkgStringRange() = default;
+
+ // Implicit by design
+ VcpkgStringRange(const std::string& s) : begin(s.cbegin()), end(s.cend()) {}
+
+ VcpkgStringRange(const std::string::const_iterator begin, const std::string::const_iterator end)
+ : begin(begin), end(end)
+ {
+ }
+
+ std::string::const_iterator begin;
+ std::string::const_iterator end;
+
+ std::string to_string() const { return std::string(this->begin, this->end); }
+ };
+
+ static std::vector<VcpkgStringRange> find_all_enclosed(const VcpkgStringRange& input,
+ const std::string& left_delim,
+ const std::string& right_delim)
+ {
+ std::string::const_iterator it_left = input.begin;
+ std::string::const_iterator it_right = input.begin;
+
+ std::vector<VcpkgStringRange> output;
+
+ while (true)
+ {
+ it_left = std::search(it_right, input.end, left_delim.cbegin(), left_delim.cend());
+ if (it_left == input.end) break;
+
+ it_left += left_delim.length();
+
+ it_right = std::search(it_left, input.end, right_delim.cbegin(), right_delim.cend());
+ if (it_right == input.end) break;
+
+ output.emplace_back(it_left, it_right);
+
+ ++it_right;
+ }
+
+ return output;
+ }
+
+ static VcpkgStringRange find_exactly_one_enclosed(const VcpkgStringRange& input,
+ const std::string& left_tag,
+ const std::string& right_tag)
+ {
+ std::vector<VcpkgStringRange> result = find_all_enclosed(input, left_tag, right_tag);
+ Checks::check_exit(VCPKG_LINE_INFO,
+ result.size() == 1,
+ "Found %d sets of %s.*%s but expected exactly 1, in block:\n%s",
+ result.size(),
+ left_tag,
+ right_tag,
+ input);
+ return std::move(result.front());
+ }
+
+ static Optional<VcpkgStringRange> find_at_most_one_enclosed(const VcpkgStringRange& input,
+ const std::string& left_tag,
+ const std::string& right_tag)
+ {
+ std::vector<VcpkgStringRange> result = find_all_enclosed(input, left_tag, right_tag);
+ Checks::check_exit(VCPKG_LINE_INFO,
+ result.size() <= 1,
+ "Found %d sets of %s.*%s but expected at most 1, in block:\n%s",
+ result.size(),
+ left_tag,
+ right_tag,
+ input);
+
+ if (result.empty())
+ {
+ return nullopt;
+ }
+
+ return result.front();
+ }
+
+ static ToolData parse_tool_data_from_xml(const VcpkgPaths& paths, const std::string& tool)
+ {
+#if defined(_WIN32)
+ static constexpr StringLiteral OS_STRING = "windows";
+#elif defined(__APPLE__)
+ static constexpr StringLiteral OS_STRING = "osx";
+#elif defined(__linux__)
+ static constexpr StringLiteral OS_STRING = "linux";
+#else
+ return ToolData{};
+#endif
+
+#if defined(_WIN32) || defined(__APPLE__) || defined(__linux__)
+ static const std::string XML_VERSION = "2";
+ static const fs::path XML_PATH = paths.scripts / "vcpkgTools.xml";
+ static const std::regex XML_VERSION_REGEX{R"###(<tools[\s]+version="([^"]+)">)###"};
+ static const std::string XML = paths.get_filesystem().read_contents(XML_PATH).value_or_exit(VCPKG_LINE_INFO);
+ std::smatch match_xml_version;
+ const bool has_xml_version = std::regex_search(XML.cbegin(), XML.cend(), match_xml_version, XML_VERSION_REGEX);
+ Checks::check_exit(VCPKG_LINE_INFO,
+ has_xml_version,
+ R"(Could not find <tools version="%s"> in %s)",
+ XML_VERSION,
+ XML_PATH.generic_string());
+ Checks::check_exit(VCPKG_LINE_INFO,
+ XML_VERSION == match_xml_version[1],
+ "Expected %s version: [%s], but was [%s]. Please re-run bootstrap-vcpkg.",
+ XML_PATH.generic_string(),
+ XML_VERSION,
+ match_xml_version[1]);
+
+ const std::regex tool_regex{Strings::format(R"###(<tool[\s]+name="%s"[\s]+os="%s">)###", tool, OS_STRING)};
+ std::smatch match_tool_entry;
+ const bool has_tool_entry = std::regex_search(XML.cbegin(), XML.cend(), match_tool_entry, tool_regex);
+ Checks::check_exit(VCPKG_LINE_INFO,
+ has_tool_entry,
+ "Could not find entry for tool [%s] in %s",
+ tool,
+ XML_PATH.generic_string());
+
+ const std::string tool_data = find_exactly_one_enclosed(XML, match_tool_entry[0], "</tool>").to_string();
+ const std::string version_as_string =
+ find_exactly_one_enclosed(tool_data, "<version>", "</version>").to_string();
+ const std::string exe_relative_path =
+ find_exactly_one_enclosed(tool_data, "<exeRelativePath>", "</exeRelativePath>").to_string();
+ const std::string url = find_exactly_one_enclosed(tool_data, "<url>", "</url>").to_string();
+ const std::string sha512 = find_exactly_one_enclosed(tool_data, "<sha512>", "</sha512>").to_string();
+ auto archive_name = find_at_most_one_enclosed(tool_data, "<archiveName>", "</archiveName>");
+
+ const Optional<std::array<int, 3>> version = parse_version_string(version_as_string);
+ Checks::check_exit(VCPKG_LINE_INFO,
+ version.has_value(),
+ "Could not parse version for tool %s. Version string was: %s",
+ tool,
+ version_as_string);
+
+ const std::string tool_dir_name = Strings::format("%s-%s-%s", tool, version_as_string, OS_STRING);
+ const fs::path tool_dir_path = paths.tools / tool_dir_name;
+ const fs::path exe_path = tool_dir_path / exe_relative_path;
+
+ return ToolData{*version.get(),
+ exe_path,
+ url,
+ paths.downloads / archive_name.value_or(exe_relative_path).to_string(),
+ archive_name.has_value(),
+ tool_dir_path,
+ sha512};
+#endif
+ }
+
+ static bool exists_and_has_equal_or_greater_version(const std::string& version_cmd,
+ const std::array<int, 3>& expected_version)
+ {
+ const auto rc = System::cmd_execute_and_capture_output(Strings::format(R"(%s)", version_cmd));
+ if (rc.exit_code != 0)
+ {
+ return false;
+ }
+
+ const Optional<std::array<int, 3>> v = parse_version_string(rc.output);
+ if (!v.has_value())
+ {
+ return false;
+ }
+
+ const std::array<int, 3> actual_version = *v.get();
+ return (actual_version[0] > expected_version[0] ||
+ (actual_version[0] == expected_version[0] && actual_version[1] > expected_version[1]) ||
+ (actual_version[0] == expected_version[0] && actual_version[1] == expected_version[1] &&
+ actual_version[2] >= expected_version[2]));
+ }
+
+ static Optional<fs::path> find_if_has_equal_or_greater_version(Files::Filesystem& fs,
+ const std::vector<fs::path>& candidate_paths,
+ const std::string& version_check_arguments,
+ const std::array<int, 3>& expected_version)
+ {
+ const auto it = Util::find_if(candidate_paths, [&](const fs::path& p) {
+ if (!fs.exists(p)) return false;
+ const std::string cmd = Strings::format(R"("%s" %s)", p.u8string(), version_check_arguments);
+ return exists_and_has_equal_or_greater_version(cmd, expected_version);
+ });
+
+ if (it != candidate_paths.cend())
+ {
+ return *it;
+ }
+
+ return nullopt;
+ }
+
+ static void extract_archive(const VcpkgPaths& paths, const fs::path& archive, const fs::path& to_path)
+ {
+ Files::Filesystem& fs = paths.get_filesystem();
+ const fs::path to_path_partial = to_path.u8string() + ".partial";
+
+ 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)
+ if (ext == ".nupkg")
+ {
+ static bool recursion_limiter_sevenzip_old = false;
+ Checks::check_exit(VCPKG_LINE_INFO, !recursion_limiter_sevenzip_old);
+ recursion_limiter_sevenzip_old = true;
+ const auto nuget_exe = get_tool_path(paths, Tools::NUGET);
+
+ const std::string stem = archive.stem().u8string();
+ // assuming format of [name].[version in the form d.d.d]
+ // This assumption may not always hold
+ std::smatch match;
+ const bool has_match = std::regex_match(stem, match, std::regex{R"###(^(.+)\.(\d+\.\d+\.\d+)$)###"});
+ Checks::check_exit(VCPKG_LINE_INFO,
+ has_match,
+ "Could not deduce nuget id and version from filename: %s",
+ archive.u8string());
+
+ const std::string nugetid = match[1];
+ const std::string version = match[2];
+
+ const auto code_and_output = System::cmd_execute_and_capture_output(Strings::format(
+ R"("%s" install %s -Version %s -OutputDirectory "%s" -Source "%s" -nocache -DirectDownload -NonInteractive -ForceEnglishOutput -PackageSaveMode nuspec)",
+ nuget_exe.u8string(),
+ nugetid,
+ version,
+ to_path_partial.u8string(),
+ paths.downloads.u8string()));
+
+ Checks::check_exit(VCPKG_LINE_INFO,
+ code_and_output.exit_code == 0,
+ "Failed to extract '%s' with message:\n%s",
+ archive.u8string(),
+ code_and_output.output);
+ recursion_limiter_sevenzip_old = false;
+ }
+ else
+ {
+ static bool recursion_limiter_sevenzip = false;
+ Checks::check_exit(VCPKG_LINE_INFO, !recursion_limiter_sevenzip);
+ recursion_limiter_sevenzip = true;
+ const auto seven_zip = get_tool_path(paths, Tools::SEVEN_ZIP);
+ const auto code_and_output = System::cmd_execute_and_capture_output(Strings::format(
+ R"("%s" x "%s" -o"%s" -y)", seven_zip.u8string(), archive.u8string(), to_path_partial.u8string()));
+ Checks::check_exit(VCPKG_LINE_INFO,
+ code_and_output.exit_code == 0,
+ "7zip failed while extracting '%s' with message:\n%s",
+ archive.u8string(),
+ code_and_output.output);
+ recursion_limiter_sevenzip = false;
+ }
+#else
+ if (ext == ".gz" && ext.extension() != ".tar")
+ {
+ const auto code = System::cmd_execute(
+ Strings::format(R"(cd '%s' && tar xzf '%s')", to_path_partial.u8string(), archive.u8string()));
+ Checks::check_exit(VCPKG_LINE_INFO, code == 0, "tar failed while extracting %s", archive.u8string());
+ }
+ else if (ext == ".zip")
+ {
+ const auto code = System::cmd_execute(
+ Strings::format(R"(cd '%s' && unzip -qqo '%s')", to_path_partial.u8string(), archive.u8string()));
+ Checks::check_exit(VCPKG_LINE_INFO, code == 0, "unzip failed while extracting %s", archive.u8string());
+ }
+ else
+ {
+ Checks::exit_with_message(VCPKG_LINE_INFO, "Unexpected archive extension: %s", ext.u8string());
+ }
+#endif
+
+ fs.rename(to_path_partial, to_path, ec);
+ Checks::check_exit(VCPKG_LINE_INFO,
+ !ec,
+ "Failed to do post-extract rename-in-place.\nfs.rename(%s, %s, %s)",
+ to_path_partial.u8string(),
+ to_path.u8string(),
+ ec.message());
+ }
+
+ static void verify_hash(const Files::Filesystem& fs,
+ const std::string& url,
+ const fs::path& path,
+ const std::string& sha512)
+ {
+ const std::string actual_hash = Hash::get_file_hash(fs, path, "SHA512");
+ Checks::check_exit(VCPKG_LINE_INFO,
+ sha512 == actual_hash,
+ "File does not have the expected hash:\n"
+ " url : [ %s ]\n"
+ " File path : [ %s ]\n"
+ " Expected hash : [ %s ]\n"
+ " Actual hash : [ %s ]\n",
+ url,
+ path.u8string(),
+ sha512,
+ actual_hash);
+ }
+
+#if defined(_WIN32)
+ static void winhttp_download_file(Files::Filesystem& fs,
+ CStringView target_file_path,
+ CStringView hostname,
+ CStringView url_path)
+ {
+ // Make sure the directories are present, otherwise fopen_s fails
+ const auto dir = fs::path(target_file_path.c_str()).parent_path();
+ std::error_code ec;
+ fs.create_directories(dir, ec);
+ Checks::check_exit(VCPKG_LINE_INFO, !ec, "Could not create directories %s", dir.u8string());
+
+ FILE* f = nullptr;
+ const errno_t err = fopen_s(&f, target_file_path.c_str(), "wb");
+ Checks::check_exit(VCPKG_LINE_INFO,
+ !err,
+ "Could not download https://%s%s. Failed to open file %s. Error code was %s",
+ hostname,
+ url_path,
+ target_file_path,
+ std::to_string(err));
+
+ auto hSession = WinHttpOpen(
+ L"vcpkg/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
+ Checks::check_exit(VCPKG_LINE_INFO, hSession, "WinHttpOpen() failed: %d", GetLastError());
+
+ // Use Windows 10 defaults on Windows 7
+ DWORD secure_protocols(WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 |
+ WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2);
+ WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &secure_protocols, sizeof(secure_protocols));
+
+ // Specify an HTTP server.
+ auto hConnect = WinHttpConnect(hSession, Strings::to_utf16(hostname).c_str(), INTERNET_DEFAULT_HTTPS_PORT, 0);
+ Checks::check_exit(VCPKG_LINE_INFO, hConnect, "WinHttpConnect() failed: %d", GetLastError());
+
+ // Create an HTTP request handle.
+ auto hRequest = WinHttpOpenRequest(hConnect,
+ L"GET",
+ Strings::to_utf16(url_path).c_str(),
+ nullptr,
+ WINHTTP_NO_REFERER,
+ WINHTTP_DEFAULT_ACCEPT_TYPES,
+ WINHTTP_FLAG_SECURE);
+ Checks::check_exit(VCPKG_LINE_INFO, hRequest, "WinHttpOpenRequest() failed: %d", GetLastError());
+
+ // Send a request.
+ auto bResults =
+ WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
+ Checks::check_exit(VCPKG_LINE_INFO, bResults, "WinHttpSendRequest() failed: %d", GetLastError());
+
+ // End the request.
+ bResults = WinHttpReceiveResponse(hRequest, NULL);
+ Checks::check_exit(VCPKG_LINE_INFO, bResults, "WinHttpReceiveResponse() failed: %d", GetLastError());
+
+ std::vector<char> buf;
+
+ size_t total_downloaded_size = 0;
+ DWORD dwSize = 0;
+ do
+ {
+ DWORD downloaded_size = 0;
+ bResults = WinHttpQueryDataAvailable(hRequest, &dwSize);
+ Checks::check_exit(VCPKG_LINE_INFO, bResults, "WinHttpQueryDataAvailable() failed: %d", GetLastError());
+
+ if (buf.size() < dwSize) buf.resize(dwSize * 2);
+
+ bResults = WinHttpReadData(hRequest, (LPVOID)buf.data(), dwSize, &downloaded_size);
+ Checks::check_exit(VCPKG_LINE_INFO, bResults, "WinHttpReadData() failed: %d", GetLastError());
+ fwrite(buf.data(), 1, downloaded_size, f);
+
+ total_downloaded_size += downloaded_size;
+ } while (dwSize > 0);
+
+ WinHttpCloseHandle(hSession);
+ WinHttpCloseHandle(hConnect);
+ WinHttpCloseHandle(hRequest);
+ fflush(f);
+ fclose(f);
+ }
+#endif
+
+ static void download_file(Files::Filesystem& fs,
+ const std::string& url,
+ const fs::path& download_path,
+ const std::string& sha512)
+ {
+ const std::string download_path_part = download_path.u8string() + ".part";
+ std::error_code ec;
+ fs.remove(download_path, ec);
+ fs.remove(download_path_part, ec);
+#if defined(_WIN32)
+ auto url_no_proto = url.substr(8); // drop https://
+ auto path_begin = Util::find(url_no_proto, '/');
+ std::string hostname(url_no_proto.begin(), path_begin);
+ std::string path(path_begin, url_no_proto.end());
+
+ winhttp_download_file(fs, download_path_part.c_str(), hostname, path);
+#else
+ const auto code = System::cmd_execute(
+ Strings::format(R"(curl -L '%s' --create-dirs --output '%s')", url, download_path_part));
+ Checks::check_exit(VCPKG_LINE_INFO, code == 0, "Could not download %s", url);
+#endif
+
+ verify_hash(fs, url, download_path_part, sha512);
+ fs.rename(download_path_part, download_path, ec);
+ Checks::check_exit(VCPKG_LINE_INFO,
+ !ec,
+ "Failed to do post-download rename-in-place.\nfs.rename(%s, %s, %s)",
+ download_path_part,
+ download_path.u8string(),
+ ec.message());
+ }
+
+ static fs::path fetch_tool(const VcpkgPaths& paths, const std::string& tool_name, const ToolData& tool_data)
+ {
+ const std::array<int, 3>& version = tool_data.version;
+ const std::string version_as_string = Strings::format("%d.%d.%d", version[0], version[1], version[2]);
+ Checks::check_exit(VCPKG_LINE_INFO,
+ !tool_data.url.empty(),
+ "A suitable version of %s was not found (required v%s) and unable to automatically "
+ "download a portable one. Please install a newer version of git.",
+ tool_name,
+ version_as_string);
+ System::println("A suitable version of %s was not found (required v%s). Downloading portable %s v%s...",
+ tool_name,
+ version_as_string,
+ tool_name,
+ version_as_string);
+ auto& fs = paths.get_filesystem();
+ if (!fs.exists(tool_data.download_path))
+ {
+ System::println("Downloading %s...", tool_name);
+ download_file(fs, tool_data.url, tool_data.download_path, tool_data.sha512);
+ System::println("Downloading %s... done.", tool_name);
+ }
+ else
+ {
+ verify_hash(fs, tool_data.url, tool_data.download_path, tool_data.sha512);
+ }
+
+ if (tool_data.is_archive)
+ {
+ System::println("Extracting %s...", tool_name);
+ extract_archive(paths, tool_data.download_path, tool_data.tool_dir_path);
+ System::println("Extracting %s... done.", tool_name);
+ }
+ else
+ {
+ std::error_code ec;
+ fs.create_directories(tool_data.exe_path.parent_path(), ec);
+ fs.rename(tool_data.download_path, tool_data.exe_path, ec);
+ }
+
+ Checks::check_exit(VCPKG_LINE_INFO,
+ fs.exists(tool_data.exe_path),
+ "Expected %s to exist after fetching",
+ tool_data.exe_path.u8string());
+
+ return tool_data.exe_path;
+ }
+
+ static fs::path get_cmake_path(const VcpkgPaths& paths)
+ {
+ std::vector<fs::path> candidate_paths;
+#if defined(_WIN32) || defined(__APPLE__) || defined(__linux__)
+ static const ToolData TOOL_DATA = parse_tool_data_from_xml(paths, "cmake");
+ candidate_paths.push_back(TOOL_DATA.exe_path);
+#else
+ static const ToolData TOOL_DATA = ToolData{{3, 5, 1}, ""};
+#endif
+ static const std::string VERSION_CHECK_ARGUMENTS = "--version";
+
+ const std::vector<fs::path> from_path = Files::find_from_PATH("cmake");
+ candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend());
+
+ const auto& program_files = System::get_program_files_platform_bitness();
+ if (const auto pf = program_files.get()) candidate_paths.push_back(*pf / "CMake" / "bin" / "cmake.exe");
+ const auto& program_files_32_bit = System::get_program_files_32_bit();
+ if (const auto pf = program_files_32_bit.get()) candidate_paths.push_back(*pf / "CMake" / "bin" / "cmake.exe");
+
+ const Optional<fs::path> path = find_if_has_equal_or_greater_version(
+ paths.get_filesystem(), candidate_paths, VERSION_CHECK_ARGUMENTS, TOOL_DATA.version);
+ if (const auto p = path.get())
+ {
+ return *p;
+ }
+
+ return fetch_tool(paths, "cmake", TOOL_DATA);
+ }
+
+ static fs::path get_7za_path(const VcpkgPaths& paths)
+ {
+#if defined(_WIN32)
+ static const ToolData TOOL_DATA = parse_tool_data_from_xml(paths, "7zip");
+ 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
+ }
+
+ static fs::path get_ninja_path(const VcpkgPaths& paths)
+ {
+ static const ToolData TOOL_DATA = parse_tool_data_from_xml(paths, "ninja");
+
+ std::vector<fs::path> candidate_paths;
+ candidate_paths.push_back(TOOL_DATA.exe_path);
+ const std::vector<fs::path> from_path = Files::find_from_PATH("ninja");
+ candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend());
+
+ auto path = find_if_has_equal_or_greater_version(
+ paths.get_filesystem(), candidate_paths, "--version", TOOL_DATA.version);
+ if (const auto p = path.get())
+ {
+ return *p;
+ }
+
+ return fetch_tool(paths, "ninja", TOOL_DATA);
+ }
+
+ static fs::path get_nuget_path(const VcpkgPaths& paths)
+ {
+ static const ToolData TOOL_DATA = parse_tool_data_from_xml(paths, "nuget");
+
+ std::vector<fs::path> candidate_paths;
+ candidate_paths.push_back(TOOL_DATA.exe_path);
+ const std::vector<fs::path> from_path = Files::find_from_PATH("nuget");
+ candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend());
+
+ auto path =
+ find_if_has_equal_or_greater_version(paths.get_filesystem(), candidate_paths, "", TOOL_DATA.version);
+ if (const auto p = path.get())
+ {
+ return *p;
+ }
+
+ return fetch_tool(paths, "nuget", TOOL_DATA);
+ }
+
+ static fs::path get_git_path(const VcpkgPaths& paths)
+ {
+ static const ToolData TOOL_DATA = parse_tool_data_from_xml(paths, "git");
+ static const std::string VERSION_CHECK_ARGUMENTS = "--version";
+
+ std::vector<fs::path> candidate_paths;
+#if defined(_WIN32)
+ candidate_paths.push_back(TOOL_DATA.exe_path);
+#endif
+ const std::vector<fs::path> from_path = Files::find_from_PATH("git");
+ candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend());
+
+ const auto& program_files = System::get_program_files_platform_bitness();
+ if (const auto pf = program_files.get()) candidate_paths.push_back(*pf / "git" / "cmd" / "git.exe");
+ const auto& program_files_32_bit = System::get_program_files_32_bit();
+ if (const auto pf = program_files_32_bit.get()) candidate_paths.push_back(*pf / "git" / "cmd" / "git.exe");
+
+ const Optional<fs::path> path = find_if_has_equal_or_greater_version(
+ paths.get_filesystem(), candidate_paths, VERSION_CHECK_ARGUMENTS, TOOL_DATA.version);
+ if (const auto p = path.get())
+ {
+ return *p;
+ }
+
+ return fetch_tool(paths, "git", TOOL_DATA);
+ }
+
+ static fs::path get_ifw_installerbase_path(const VcpkgPaths& paths)
+ {
+ static const ToolData TOOL_DATA = parse_tool_data_from_xml(paths, "installerbase");
+
+ static const std::string VERSION_CHECK_ARGUMENTS = "--framework-version";
+
+ std::vector<fs::path> candidate_paths;
+ candidate_paths.push_back(TOOL_DATA.exe_path);
+ // TODO: Uncomment later
+ // const std::vector<fs::path> from_path = Files::find_from_PATH("installerbase");
+ // candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend());
+ // candidate_paths.push_back(fs::path(System::get_environment_variable("HOMEDRIVE").value_or("C:")) / "Qt" /
+ // "Tools" / "QtInstallerFramework" / "3.1" / "bin" / "installerbase.exe");
+ // candidate_paths.push_back(fs::path(System::get_environment_variable("HOMEDRIVE").value_or("C:")) / "Qt" /
+ // "QtIFW-3.1.0" / "bin" / "installerbase.exe");
+
+ const Optional<fs::path> path = find_if_has_equal_or_greater_version(
+ paths.get_filesystem(), candidate_paths, VERSION_CHECK_ARGUMENTS, TOOL_DATA.version);
+ if (const auto p = path.get())
+ {
+ return *p;
+ }
+
+ return fetch_tool(paths, "installerbase", TOOL_DATA);
+ }
+
+ struct VisualStudioInstance
+ {
+ enum class ReleaseType
+ {
+ STABLE,
+ PRERELEASE,
+ LEGACY
+ };
+
+ static bool preferred_first_comparator(const VisualStudioInstance& left, const VisualStudioInstance& right)
+ {
+ const auto get_preference_weight = [](const ReleaseType& type) -> int {
+ switch (type)
+ {
+ case ReleaseType::STABLE: return 3;
+ case ReleaseType::PRERELEASE: return 2;
+ case ReleaseType::LEGACY: return 1;
+ default: Checks::unreachable(VCPKG_LINE_INFO);
+ }
+ };
+
+ if (left.release_type != right.release_type)
+ {
+ return get_preference_weight(left.release_type) > get_preference_weight(right.release_type);
+ }
+
+ return left.version > right.version;
+ }
+
+ VisualStudioInstance(fs::path&& root_path, std::string&& version, const ReleaseType& release_type)
+ : root_path(std::move(root_path)), version(std::move(version)), release_type(release_type)
+ {
+ }
+
+ fs::path root_path;
+ std::string version;
+ ReleaseType release_type;
+
+ std::string major_version() const { return version.substr(0, 2); }
+ };
+
+ static std::vector<VisualStudioInstance> get_visual_studio_instances(const VcpkgPaths& paths)
+ {
+ const auto& fs = paths.get_filesystem();
+ std::vector<VisualStudioInstance> instances;
+
+ const auto& program_files_32_bit = System::get_program_files_32_bit().value_or_exit(VCPKG_LINE_INFO);
+
+ // Instances from vswhere
+ const fs::path vswhere_exe = program_files_32_bit / "Microsoft Visual Studio" / "Installer" / "vswhere.exe";
+ if (fs.exists(vswhere_exe))
+ {
+ const auto code_and_output = System::cmd_execute_and_capture_output(
+ Strings::format(R"("%s" -prerelease -legacy -products * -format xml)", vswhere_exe.u8string()));
+ Checks::check_exit(VCPKG_LINE_INFO,
+ code_and_output.exit_code == 0,
+ "Running vswhere.exe failed with message:\n%s",
+ code_and_output.output);
+
+ const auto instance_entries = find_all_enclosed(code_and_output.output, "<instance>", "</instance>");
+ for (const VcpkgStringRange& instance : instance_entries)
+ {
+ auto maybe_is_prerelease = find_at_most_one_enclosed(instance, "<isPrerelease>", "</isPrerelease>");
+
+ VisualStudioInstance::ReleaseType release_type = VisualStudioInstance::ReleaseType::LEGACY;
+ if (const auto p = maybe_is_prerelease.get())
+ {
+ const auto s = p->to_string();
+ if (s == "0")
+ release_type = VisualStudioInstance::ReleaseType::STABLE;
+ else if (s == "1")
+ release_type = VisualStudioInstance::ReleaseType::PRERELEASE;
+ else
+ Checks::unreachable(VCPKG_LINE_INFO);
+ }
+
+ instances.emplace_back(
+ find_exactly_one_enclosed(instance, "<installationPath>", "</installationPath>").to_string(),
+ find_exactly_one_enclosed(instance, "<installationVersion>", "</installationVersion>").to_string(),
+ release_type);
+ }
+ }
+
+ const auto append_if_has_cl = [&](fs::path&& path_root) {
+ const auto cl_exe = path_root / "VC" / "bin" / "cl.exe";
+ const auto vcvarsall_bat = path_root / "VC" / "vcvarsall.bat";
+
+ if (fs.exists(cl_exe) && fs.exists(vcvarsall_bat))
+ instances.emplace_back(std::move(path_root), "14.0", VisualStudioInstance::ReleaseType::LEGACY);
+ };
+
+ // VS2015 instance from environment variable
+ auto maybe_vs140_comntools = System::get_environment_variable("vs140comntools");
+ if (const auto path_as_string = maybe_vs140_comntools.get())
+ {
+ // We want lexically_normal(), but it is not available
+ // Correct root path might be 2 or 3 levels up, depending on if the path has trailing backslash. Try both.
+ auto common7_tools = fs::path{*path_as_string};
+ append_if_has_cl(fs::path{*path_as_string}.parent_path().parent_path());
+ append_if_has_cl(fs::path{*path_as_string}.parent_path().parent_path().parent_path());
+ }
+
+ // VS2015 instance from Program Files
+ append_if_has_cl(program_files_32_bit / "Microsoft Visual Studio 14.0");
+
+ return instances;
+ }
+
+#if defined(_WIN32)
+ std::vector<Toolset> find_toolset_instances_preferred_first(const VcpkgPaths& paths)
+ {
+ using CPU = System::CPUArchitecture;
+
+ const auto& fs = paths.get_filesystem();
+
+ // Note: this will contain a mix of vcvarsall.bat locations and dumpbin.exe locations.
+ std::vector<fs::path> paths_examined;
+
+ std::vector<Toolset> found_toolsets;
+ std::vector<Toolset> excluded_toolsets;
+
+ const SortedVector<VisualStudioInstance> sorted{get_visual_studio_instances(paths),
+ VisualStudioInstance::preferred_first_comparator};
+
+ const bool v140_is_available = Util::find_if(sorted, [&](const VisualStudioInstance& vs_instance) {
+ return vs_instance.major_version() == "14";
+ }) != sorted.end();
+
+ for (const VisualStudioInstance& vs_instance : sorted)
+ {
+ const std::string major_version = vs_instance.major_version();
+ if (major_version == "15")
+ {
+ const fs::path vc_dir = vs_instance.root_path / "VC";
+
+ // Skip any instances that do not have vcvarsall.
+ const fs::path vcvarsall_dir = vc_dir / "Auxiliary" / "Build";
+ const fs::path vcvarsall_bat = vcvarsall_dir / "vcvarsall.bat";
+ paths_examined.push_back(vcvarsall_bat);
+ if (!fs.exists(vcvarsall_bat)) continue;
+
+ // Get all supported architectures
+ std::vector<ToolsetArchOption> supported_architectures;
+ if (fs.exists(vcvarsall_dir / "vcvars32.bat"))
+ supported_architectures.push_back({"x86", CPU::X86, CPU::X86});
+ if (fs.exists(vcvarsall_dir / "vcvars64.bat"))
+ supported_architectures.push_back({"amd64", CPU::X64, CPU::X64});
+ if (fs.exists(vcvarsall_dir / "vcvarsx86_amd64.bat"))
+ supported_architectures.push_back({"x86_amd64", CPU::X86, CPU::X64});
+ if (fs.exists(vcvarsall_dir / "vcvarsx86_arm.bat"))
+ supported_architectures.push_back({"x86_arm", CPU::X86, CPU::ARM});
+ if (fs.exists(vcvarsall_dir / "vcvarsx86_arm64.bat"))
+ supported_architectures.push_back({"x86_arm64", CPU::X86, CPU::ARM64});
+ if (fs.exists(vcvarsall_dir / "vcvarsamd64_x86.bat"))
+ supported_architectures.push_back({"amd64_x86", CPU::X64, CPU::X86});
+ if (fs.exists(vcvarsall_dir / "vcvarsamd64_arm.bat"))
+ supported_architectures.push_back({"amd64_arm", CPU::X64, CPU::ARM});
+ if (fs.exists(vcvarsall_dir / "vcvarsamd64_arm64.bat"))
+ supported_architectures.push_back({"amd64_arm64", CPU::X64, CPU::ARM64});
+
+ // Locate the "best" MSVC toolchain version
+ const fs::path msvc_path = vc_dir / "Tools" / "MSVC";
+ std::vector<fs::path> msvc_subdirectories = fs.get_files_non_recursive(msvc_path);
+ Util::unstable_keep_if(msvc_subdirectories,
+ [&fs](const fs::path& path) { return fs.is_directory(path); });
+
+ // Sort them so that latest comes first
+ std::sort(
+ msvc_subdirectories.begin(),
+ msvc_subdirectories.end(),
+ [](const fs::path& left, const fs::path& right) { return left.filename() > right.filename(); });
+
+ for (const fs::path& subdir : msvc_subdirectories)
+ {
+ const fs::path dumpbin_path = subdir / "bin" / "HostX86" / "x86" / "dumpbin.exe";
+ paths_examined.push_back(dumpbin_path);
+ if (fs.exists(dumpbin_path))
+ {
+ const Toolset v141toolset{
+ vs_instance.root_path, dumpbin_path, vcvarsall_bat, {}, V_141, supported_architectures};
+
+ auto english_language_pack = dumpbin_path.parent_path() / "1033";
+
+ if (!fs.exists(english_language_pack))
+ {
+ excluded_toolsets.push_back(v141toolset);
+ break;
+ }
+
+ found_toolsets.push_back(v141toolset);
+
+ if (v140_is_available)
+ {
+ const Toolset v140toolset{vs_instance.root_path,
+ dumpbin_path,
+ vcvarsall_bat,
+ {"-vcvars_ver=14.0"},
+ V_140,
+ supported_architectures};
+ found_toolsets.push_back(v140toolset);
+ }
+
+ break;
+ }
+ }
+
+ continue;
+ }
+
+ if (major_version == "14" || major_version == "12")
+ {
+ const fs::path vcvarsall_bat = vs_instance.root_path / "VC" / "vcvarsall.bat";
+
+ paths_examined.push_back(vcvarsall_bat);
+ if (fs.exists(vcvarsall_bat))
+ {
+ const fs::path vs_dumpbin_exe = vs_instance.root_path / "VC" / "bin" / "dumpbin.exe";
+ paths_examined.push_back(vs_dumpbin_exe);
+
+ const fs::path vs_bin_dir = vcvarsall_bat.parent_path() / "bin";
+ std::vector<ToolsetArchOption> supported_architectures;
+ if (fs.exists(vs_bin_dir / "vcvars32.bat"))
+ supported_architectures.push_back({"x86", CPU::X86, CPU::X86});
+ if (fs.exists(vs_bin_dir / "amd64\\vcvars64.bat"))
+ supported_architectures.push_back({"x64", CPU::X64, CPU::X64});
+ if (fs.exists(vs_bin_dir / "x86_amd64\\vcvarsx86_amd64.bat"))
+ supported_architectures.push_back({"x86_amd64", CPU::X86, CPU::X64});
+ if (fs.exists(vs_bin_dir / "x86_arm\\vcvarsx86_arm.bat"))
+ supported_architectures.push_back({"x86_arm", CPU::X86, CPU::ARM});
+ if (fs.exists(vs_bin_dir / "amd64_x86\\vcvarsamd64_x86.bat"))
+ supported_architectures.push_back({"amd64_x86", CPU::X64, CPU::X86});
+ if (fs.exists(vs_bin_dir / "amd64_arm\\vcvarsamd64_arm.bat"))
+ supported_architectures.push_back({"amd64_arm", CPU::X64, CPU::ARM});
+
+ if (fs.exists(vs_dumpbin_exe))
+ {
+ const Toolset toolset = {vs_instance.root_path,
+ vs_dumpbin_exe,
+ vcvarsall_bat,
+ {},
+ major_version == "14" ? V_140 : V_120,
+ supported_architectures};
+
+ auto english_language_pack = vs_dumpbin_exe.parent_path() / "1033";
+
+ if (!fs.exists(english_language_pack))
+ {
+ excluded_toolsets.push_back(toolset);
+ break;
+ }
+
+ found_toolsets.push_back(toolset);
+ }
+ }
+ }
+ }
+
+ if (!excluded_toolsets.empty())
+ {
+ System::println(
+ System::Color::warning,
+ "Warning: The following VS instances are excluded because the English language pack is unavailable.");
+ for (const Toolset& toolset : excluded_toolsets)
+ {
+ System::println(" %s", toolset.visual_studio_root_path.u8string());
+ }
+ System::println(System::Color::warning, "Please install the English language pack.");
+ }
+
+ if (found_toolsets.empty())
+ {
+ System::println(System::Color::error, "Could not locate a complete toolset.");
+ System::println("The following paths were examined:");
+ for (const fs::path& path : paths_examined)
+ {
+ System::println(" %s", path.u8string());
+ }
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+
+ return found_toolsets;
+ }
+#endif
+
+ fs::path get_tool_path(const VcpkgPaths& paths, const std::string& 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) return get_cmake_path(paths);
+ if (tool == Tools::GIT) return get_git_path(paths);
+ if (tool == Tools::NINJA) return get_ninja_path(paths);
+ if (tool == Tools::NUGET) return get_nuget_path(paths);
+ if (tool == Tools::IFW_INSTALLER_BASE) return get_ifw_installerbase_path(paths);
+ if (tool == Tools::IFW_BINARYCREATOR)
+ return get_ifw_installerbase_path(paths).parent_path() / "binarycreator.exe";
+ if (tool == Tools::IFW_REPOGEN) return get_ifw_installerbase_path(paths).parent_path() / "repogen.exe";
+
+ // For other tools, we simply always auto-download them.
+ const ToolData tool_data = parse_tool_data_from_xml(paths, tool);
+ if (paths.get_filesystem().exists(tool_data.exe_path))
+ {
+ return tool_data.exe_path;
+ }
+ return fetch_tool(paths, tool, tool_data);
+ }
+
+ const CommandStructure COMMAND_STRUCTURE = {
+ Strings::format("The argument should be tool name\n%s", Help::create_example_string("fetch cmake")),
+ 1,
+ 1,
+ {},
+ nullptr,
+ };
+
+ void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
+ {
+ Util::unused(args.parse_arguments(COMMAND_STRUCTURE));
+
+ const std::string tool = args.command_arguments[0];
+ const fs::path tool_path = get_tool_path(paths, tool);
+ System::println(tool_path.u8string());
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+}
diff --git a/toolsrc/src/vcpkg/commands.hash.cpp b/toolsrc/src/vcpkg/commands.hash.cpp
new file mode 100644
index 000000000..0c7aa72c4
--- /dev/null
+++ b/toolsrc/src/vcpkg/commands.hash.cpp
@@ -0,0 +1,244 @@
+#include "pch.h"
+
+#include <vcpkg/base/checks.h>
+#include <vcpkg/base/strings.h>
+#include <vcpkg/base/system.h>
+#include <vcpkg/base/util.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/help.h>
+
+namespace vcpkg::Commands::Hash
+{
+ static void verify_has_only_allowed_chars(const std::string& s)
+ {
+ static const std::regex ALLOWED_CHARS{"^[a-zA-Z0-9-]*$"};
+ Checks::check_exit(VCPKG_LINE_INFO,
+ std::regex_match(s, ALLOWED_CHARS),
+ "Only alphanumeric chars and dashes are currently allowed. String was:\n"
+ " % s",
+ s);
+ }
+}
+
+#if defined(_WIN32)
+#include <bcrypt.h>
+
+#ifndef NT_SUCCESS
+#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
+#endif
+
+namespace vcpkg::Commands::Hash
+{
+ namespace
+ {
+ std::string to_hex(const unsigned char* string, const size_t bytes)
+ {
+ static constexpr char HEX_MAP[] = "0123456789abcdef";
+
+ std::string output;
+ output.resize(2 * bytes);
+
+ size_t current_char = 0;
+ for (size_t i = 0; i < bytes; i++)
+ {
+ // high
+ output[current_char] = HEX_MAP[(string[i] & 0xF0) >> 4];
+ ++current_char;
+ // low
+ output[current_char] = HEX_MAP[(string[i] & 0x0F)];
+ ++current_char;
+ }
+
+ return output;
+ }
+
+ class BCryptHasher
+ {
+ struct BCryptAlgorithmHandle : Util::ResourceBase
+ {
+ BCRYPT_ALG_HANDLE handle = nullptr;
+
+ ~BCryptAlgorithmHandle()
+ {
+ if (handle) BCryptCloseAlgorithmProvider(handle, 0);
+ }
+ };
+
+ struct BCryptHashHandle : Util::ResourceBase
+ {
+ BCRYPT_HASH_HANDLE handle = nullptr;
+
+ ~BCryptHashHandle()
+ {
+ if (handle) BCryptDestroyHash(handle);
+ }
+ };
+
+ static void initialize_hash_handle(BCryptHashHandle& hash_handle,
+ const BCryptAlgorithmHandle& algorithm_handle)
+ {
+ const NTSTATUS error_code =
+ BCryptCreateHash(algorithm_handle.handle, &hash_handle.handle, nullptr, 0, nullptr, 0, 0);
+ Checks::check_exit(VCPKG_LINE_INFO, NT_SUCCESS(error_code), "Failed to initialize the hasher");
+ }
+
+ static void hash_data(BCryptHashHandle& hash_handle, const unsigned char* buffer, const size_t& data_size)
+ {
+ const NTSTATUS error_code = BCryptHashData(
+ hash_handle.handle, const_cast<unsigned char*>(buffer), static_cast<ULONG>(data_size), 0);
+ Checks::check_exit(VCPKG_LINE_INFO, NT_SUCCESS(error_code), "Failed to hash data");
+ }
+
+ static std::string finalize_hash_handle(const BCryptHashHandle& hash_handle, const ULONG length_in_bytes)
+ {
+ std::unique_ptr<unsigned char[]> hash_buffer = std::make_unique<UCHAR[]>(length_in_bytes);
+ const NTSTATUS error_code = BCryptFinishHash(hash_handle.handle, hash_buffer.get(), length_in_bytes, 0);
+ Checks::check_exit(VCPKG_LINE_INFO, NT_SUCCESS(error_code), "Failed to finalize the hash");
+ return to_hex(hash_buffer.get(), length_in_bytes);
+ }
+
+ public:
+ explicit BCryptHasher(const std::string& hash_type)
+ {
+ NTSTATUS error_code =
+ BCryptOpenAlgorithmProvider(&this->algorithm_handle.handle,
+ Strings::to_utf16(Strings::ascii_to_uppercase(hash_type)).c_str(),
+ nullptr,
+ 0);
+ Checks::check_exit(VCPKG_LINE_INFO, NT_SUCCESS(error_code), "Failed to open the algorithm provider");
+
+ DWORD hash_buffer_bytes;
+ DWORD cb_data;
+ error_code = BCryptGetProperty(this->algorithm_handle.handle,
+ BCRYPT_HASH_LENGTH,
+ reinterpret_cast<PUCHAR>(&hash_buffer_bytes),
+ sizeof(DWORD),
+ &cb_data,
+ 0);
+ Checks::check_exit(VCPKG_LINE_INFO, NT_SUCCESS(error_code), "Failed to get hash length");
+ this->length_in_bytes = hash_buffer_bytes;
+ }
+
+ std::string hash_file(const fs::path& path) const
+ {
+ BCryptHashHandle hash_handle;
+ initialize_hash_handle(hash_handle, this->algorithm_handle);
+
+ FILE* file = nullptr;
+ const auto ec = _wfopen_s(&file, path.c_str(), L"rb");
+ Checks::check_exit(VCPKG_LINE_INFO, ec == 0, "Failed to open file: %s", path.u8string());
+ if (file != nullptr)
+ {
+ unsigned char buffer[4096];
+ while (const auto actual_size = fread(buffer, 1, sizeof(buffer), file))
+ {
+ hash_data(hash_handle, buffer, actual_size);
+ }
+ fclose(file);
+ }
+
+ return finalize_hash_handle(hash_handle, length_in_bytes);
+ }
+
+ std::string hash_string(const std::string& s) const
+ {
+ BCryptHashHandle hash_handle;
+ initialize_hash_handle(hash_handle, this->algorithm_handle);
+ hash_data(hash_handle, reinterpret_cast<const unsigned char*>(s.c_str()), s.size());
+ return finalize_hash_handle(hash_handle, length_in_bytes);
+ }
+
+ private:
+ BCryptAlgorithmHandle algorithm_handle;
+ ULONG length_in_bytes;
+ };
+ }
+
+ std::string get_file_hash(const Files::Filesystem& fs, const fs::path& path, const std::string& hash_type)
+ {
+ Checks::check_exit(VCPKG_LINE_INFO, fs.exists(path), "File %s does not exist", path.u8string());
+ return BCryptHasher{hash_type}.hash_file(path);
+ }
+
+ std::string get_string_hash(const std::string& s, const std::string& hash_type)
+ {
+ verify_has_only_allowed_chars(s);
+ return BCryptHasher{hash_type}.hash_string(s);
+ }
+}
+
+#else
+namespace vcpkg::Commands::Hash
+{
+ static std::string get_digest_size(const std::string& hash_type)
+ {
+ if (!Strings::case_insensitive_ascii_starts_with(hash_type, "SHA"))
+ {
+ Checks::exit_with_message(
+ VCPKG_LINE_INFO, "shasum only supports SHA hashes, but %s was provided", hash_type);
+ }
+
+ return hash_type.substr(3, hash_type.length() - 3);
+ }
+
+ static std::string run_shasum_and_post_process(const std::string& cmd_line)
+ {
+ const auto ec_data = System::cmd_execute_and_capture_output(cmd_line);
+ Checks::check_exit(VCPKG_LINE_INFO,
+ ec_data.exit_code == 0,
+ "Failed to run:\n"
+ " %s",
+ cmd_line);
+
+ std::vector<std::string> split = Strings::split(ec_data.output, " ");
+ Checks::check_exit(VCPKG_LINE_INFO,
+ split.size() == 3,
+ "Expected output of the form [hash filename\n] (3 tokens), but got\n"
+ "[%s] (%s tokens)",
+ ec_data.output,
+ std::to_string(split.size()));
+
+ return split[0];
+ }
+
+ std::string get_file_hash(const Files::Filesystem& fs, const fs::path& path, const std::string& hash_type)
+ {
+ const std::string digest_size = get_digest_size(hash_type);
+ Checks::check_exit(VCPKG_LINE_INFO, fs.exists(path), "File %s does not exist", path.u8string());
+ const std::string cmd_line = Strings::format(R"(shasum -a %s "%s")", digest_size, path.u8string());
+ return run_shasum_and_post_process(cmd_line);
+ }
+
+ std::string get_string_hash(const std::string& s, const std::string& hash_type)
+ {
+ const std::string digest_size = get_digest_size(hash_type);
+ verify_has_only_allowed_chars(s);
+
+ const std::string cmd_line = Strings::format(R"(echo -n "%s" | shasum -a %s)", s, digest_size);
+ return run_shasum_and_post_process(cmd_line);
+ }
+}
+#endif
+
+namespace vcpkg::Commands::Hash
+{
+ const CommandStructure COMMAND_STRUCTURE = {
+ Strings::format("The argument should be a file path\n%s",
+ Help::create_example_string("hash boost_1_62_0.tar.bz2")),
+ 1,
+ 2,
+ {},
+ nullptr,
+ };
+
+ void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
+ {
+ Util::unused(args.parse_arguments(COMMAND_STRUCTURE));
+
+ const fs::path file_to_hash = args.command_arguments[0];
+ const std::string algorithm = args.command_arguments.size() == 2 ? args.command_arguments[1] : "SHA512";
+ const std::string hash = get_file_hash(paths.get_filesystem(), file_to_hash, algorithm);
+ System::println(hash);
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+}
diff --git a/toolsrc/src/commands_import.cpp b/toolsrc/src/vcpkg/commands.import.cpp
index 5db1885b2..4b595697a 100644
--- a/toolsrc/src/commands_import.cpp
+++ b/toolsrc/src/vcpkg/commands.import.cpp
@@ -1,9 +1,10 @@
#include "pch.h"
-#include "Paragraphs.h"
-#include "StatusParagraph.h"
-#include "vcpkg_Commands.h"
-#include "vcpkg_Files.h"
+#include <vcpkg/base/files.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/help.h>
+#include <vcpkg/paragraphs.h>
+#include <vcpkg/statusparagraph.h>
namespace vcpkg::Commands::Import
{
@@ -28,7 +29,7 @@ namespace vcpkg::Commands::Import
for (auto&& file : files)
{
if (fs.is_directory(file)) continue;
- auto ext = file.extension();
+ const auto ext = file.extension();
if (ext == ".dll")
binaries.dlls.push_back(std::move(file));
else if (ext == ".lib")
@@ -46,7 +47,7 @@ namespace vcpkg::Commands::Import
for (auto const& src_path : files)
{
- fs::path dest_path = destination_folder / src_path.filename();
+ const fs::path dest_path = destination_folder / src_path.filename();
fs.copy(src_path, dest_path, fs::copy_options::overwrite_existing);
}
}
@@ -59,10 +60,10 @@ namespace vcpkg::Commands::Import
check_is_directory(VCPKG_LINE_INFO, fs, include_directory);
check_is_directory(VCPKG_LINE_INFO, fs, project_directory);
check_is_directory(VCPKG_LINE_INFO, fs, destination_path);
- Binaries debug_binaries = find_binaries_in_dir(fs, project_directory / "Debug");
- Binaries release_binaries = find_binaries_in_dir(fs, project_directory / "Release");
+ const Binaries debug_binaries = find_binaries_in_dir(fs, project_directory / "Debug");
+ const Binaries release_binaries = find_binaries_in_dir(fs, project_directory / "Release");
- fs::path destination_include_directory = destination_path / "include";
+ const fs::path destination_include_directory = destination_path / "include";
fs.copy(include_directory,
destination_include_directory,
fs::copy_options::recursive | fs::copy_options::overwrite_existing);
@@ -82,21 +83,27 @@ namespace vcpkg::Commands::Import
const BinaryParagraph& control_file_data)
{
auto& fs = paths.get_filesystem();
- fs::path library_destination_path = paths.package_dir(control_file_data.spec);
+ const fs::path library_destination_path = paths.package_dir(control_file_data.spec);
std::error_code ec;
fs.create_directory(library_destination_path, ec);
place_library_files_in(paths.get_filesystem(), include_directory, project_directory, library_destination_path);
- fs::path control_file_path = library_destination_path / "CONTROL";
+ const fs::path control_file_path = library_destination_path / "CONTROL";
fs.write_contents(control_file_path, Strings::serialize(control_file_data));
}
+ const CommandStructure COMMAND_STRUCTURE = {
+ Help::create_example_string(
+ R"(import C:\path\to\CONTROLfile C:\path\to\includedir C:\path\to\projectdir)"),
+ 3,
+ 3,
+ {},
+ nullptr,
+ };
+
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
{
- static const std::string example = Commands::Help::create_example_string(
- R"(import C:\path\to\CONTROLfile C:\path\to\includedir C:\path\to\projectdir)");
- args.check_exact_arg_count(3, example);
- args.check_and_get_optional_command_arguments({});
+ Util::unused(args.parse_arguments(COMMAND_STRUCTURE));
const fs::path control_file_path(args.command_arguments[0]);
const fs::path include_directory(args.command_arguments[1]);
diff --git a/toolsrc/src/commands_integrate.cpp b/toolsrc/src/vcpkg/commands.integrate.cpp
index c5942f9fc..82172e363 100644
--- a/toolsrc/src/commands_integrate.cpp
+++ b/toolsrc/src/vcpkg/commands.integrate.cpp
@@ -1,19 +1,16 @@
#include "pch.h"
-#include "vcpkg_Checks.h"
-#include "vcpkg_Commands.h"
-#include "vcpkg_Files.h"
-#include "vcpkg_System.h"
-#include "vcpkg_Util.h"
+#include <vcpkg/base/checks.h>
+#include <vcpkg/base/files.h>
+#include <vcpkg/base/system.h>
+#include <vcpkg/base/util.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/metrics.h>
+#include <vcpkg/userconfig.h>
namespace vcpkg::Commands::Integrate
{
- static const std::array<fs::path, 2> old_system_target_files = {
- System::get_ProgramFiles_32_bit() / "MSBuild/14.0/Microsoft.Common.Targets/ImportBefore/vcpkg.nuget.targets",
- System::get_ProgramFiles_32_bit() / "MSBuild/14.0/Microsoft.Common.Targets/ImportBefore/vcpkg.system.targets"};
- static const fs::path system_wide_targets_file =
- System::get_ProgramFiles_32_bit() / "MSBuild/Microsoft.Cpp/v4.0/V140/ImportBefore/Default/vcpkg.system.props";
-
+#if defined(_WIN32)
static std::string create_appdata_targets_shortcut(const std::string& target_path) noexcept
{
return Strings::format(R"###(
@@ -24,7 +21,9 @@ namespace vcpkg::Commands::Integrate
target_path,
target_path);
}
+#endif
+#if defined(_WIN32)
static std::string create_system_targets_shortcut() noexcept
{
return R"###(
@@ -37,7 +36,9 @@ namespace vcpkg::Commands::Integrate
</Project>
)###";
}
+#endif
+#if defined(_WIN32)
static std::string create_nuget_targets_file_contents(const fs::path& msbuild_vcpkg_targets_file) noexcept
{
const std::string as_string = msbuild_vcpkg_targets_file.string();
@@ -53,7 +54,9 @@ namespace vcpkg::Commands::Integrate
as_string,
as_string);
}
+#endif
+#if defined(_WIN32)
static std::string create_nuget_props_file_contents() noexcept
{
return R"###(
@@ -64,7 +67,9 @@ namespace vcpkg::Commands::Integrate
</Project>
)###";
}
+#endif
+#if defined(_WIN32)
static std::string get_nuget_id(const fs::path& vcpkg_root_dir)
{
std::string dir_id = vcpkg_root_dir.generic_string();
@@ -77,12 +82,14 @@ namespace vcpkg::Commands::Integrate
const std::string nuget_id = "vcpkg." + dir_id;
return nuget_id;
}
+#endif
+#if defined(_WIN32)
static std::string create_nuspec_file_contents(const fs::path& vcpkg_root_dir,
const std::string& nuget_id,
const std::string& nupkg_version)
{
- static constexpr auto content_template = R"(
+ static constexpr auto CONTENT_TEMPLATE = R"(
<package>
<metadata>
<id>@NUGET_ID@</id>
@@ -99,12 +106,14 @@ namespace vcpkg::Commands::Integrate
</package>
)";
- std::string content = std::regex_replace(content_template, std::regex("@NUGET_ID@"), nuget_id);
- content = std::regex_replace(content, std::regex("@VCPKG_DIR@"), vcpkg_root_dir.string());
- content = std::regex_replace(content, std::regex("@VERSION@"), nupkg_version);
+ std::string content = Strings::replace_all(CONTENT_TEMPLATE, "@NUGET_ID@", nuget_id);
+ content = Strings::replace_all(std::move(content), "@VCPKG_DIR@", vcpkg_root_dir.string());
+ content = Strings::replace_all(std::move(content), "@VERSION@", nupkg_version);
return content;
}
+#endif
+#if defined(_WIN32)
enum class ElevationPromptChoice
{
YES,
@@ -113,50 +122,64 @@ namespace vcpkg::Commands::Integrate
static ElevationPromptChoice elevated_cmd_execute(const std::string& param)
{
- SHELLEXECUTEINFO shExInfo = {0};
- shExInfo.cbSize = sizeof(shExInfo);
- shExInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
- shExInfo.hwnd = nullptr;
- shExInfo.lpVerb = "runas";
- shExInfo.lpFile = "cmd"; // Application to start
-
- shExInfo.lpParameters = param.c_str(); // Additional parameters
- shExInfo.lpDirectory = nullptr;
- shExInfo.nShow = SW_HIDE;
- shExInfo.hInstApp = nullptr;
-
- if (!ShellExecuteExA(&shExInfo))
+ SHELLEXECUTEINFOW sh_ex_info{};
+ sh_ex_info.cbSize = sizeof(sh_ex_info);
+ sh_ex_info.fMask = SEE_MASK_NOCLOSEPROCESS;
+ sh_ex_info.hwnd = nullptr;
+ sh_ex_info.lpVerb = L"runas";
+ sh_ex_info.lpFile = L"cmd"; // Application to start
+
+ auto wparam = Strings::to_utf16(param);
+ sh_ex_info.lpParameters = wparam.c_str(); // Additional parameters
+ sh_ex_info.lpDirectory = nullptr;
+ sh_ex_info.nShow = SW_HIDE;
+ sh_ex_info.hInstApp = nullptr;
+
+ if (!ShellExecuteExW(&sh_ex_info))
{
return ElevationPromptChoice::NO;
}
- if (shExInfo.hProcess == nullptr)
+ if (sh_ex_info.hProcess == nullptr)
{
return ElevationPromptChoice::NO;
}
- WaitForSingleObject(shExInfo.hProcess, INFINITE);
- CloseHandle(shExInfo.hProcess);
+ WaitForSingleObject(sh_ex_info.hProcess, INFINITE);
+ CloseHandle(sh_ex_info.hProcess);
return ElevationPromptChoice::YES;
}
+#endif
+#if defined(_WIN32)
static fs::path get_appdata_targets_path()
{
- static const fs::path local_app_data =
- fs::path(System::get_environment_variable(L"LOCALAPPDATA").value_or_exit(VCPKG_LINE_INFO));
- return local_app_data / "vcpkg" / "vcpkg.user.targets";
+ static const fs::path LOCAL_APP_DATA =
+ fs::u8path(System::get_environment_variable("LOCALAPPDATA").value_or_exit(VCPKG_LINE_INFO));
+ return LOCAL_APP_DATA / "vcpkg" / "vcpkg.user.targets";
}
+#endif
- static void integrate_install(const VcpkgPaths& paths)
+ static fs::path get_path_txt_path() { return get_user_dir() / "vcpkg.path.txt"; }
+
+#if defined(_WIN32)
+ static void integrate_install_msbuild14(Files::Filesystem& fs, const fs::path& tmp_dir)
{
- auto& fs = paths.get_filesystem();
+ static const std::array<fs::path, 2> OLD_SYSTEM_TARGET_FILES = {
+ System::get_program_files_32_bit().value_or_exit(VCPKG_LINE_INFO) /
+ "MSBuild/14.0/Microsoft.Common.Targets/ImportBefore/vcpkg.nuget.targets",
+ System::get_program_files_32_bit().value_or_exit(VCPKG_LINE_INFO) /
+ "MSBuild/14.0/Microsoft.Common.Targets/ImportBefore/vcpkg.system.targets"};
+ static const fs::path SYSTEM_WIDE_TARGETS_FILE =
+ System::get_program_files_32_bit().value_or_exit(VCPKG_LINE_INFO) /
+ "MSBuild/Microsoft.Cpp/v4.0/V140/ImportBefore/Default/vcpkg.system.props";
// TODO: This block of code should eventually be removed
- for (auto&& old_system_wide_targets_file : old_system_target_files)
+ for (auto&& old_system_wide_targets_file : OLD_SYSTEM_TARGET_FILES)
{
if (fs.exists(old_system_wide_targets_file))
{
const std::string param =
Strings::format(R"(/c DEL "%s" /Q > nul)", old_system_wide_targets_file.string());
- ElevationPromptChoice user_choice = elevated_cmd_execute(param);
+ const ElevationPromptChoice user_choice = elevated_cmd_execute(param);
switch (user_choice)
{
case ElevationPromptChoice::YES: break;
@@ -167,22 +190,16 @@ namespace vcpkg::Commands::Integrate
}
}
}
-
- std::error_code ec;
- const fs::path tmp_dir = paths.buildsystems / "tmp";
- fs.create_directory(paths.buildsystems, ec);
- fs.create_directory(tmp_dir, ec);
-
bool should_install_system = true;
- const Expected<std::string> system_wide_file_contents = fs.read_contents(system_wide_targets_file);
- if (auto contents_data = system_wide_file_contents.get())
+ const Expected<std::string> system_wide_file_contents = fs.read_contents(SYSTEM_WIDE_TARGETS_FILE);
+ static const std::regex RE(R"###(<!-- version (\d+) -->)###");
+ if (const auto contents_data = system_wide_file_contents.get())
{
- std::regex re(R"###(<!-- version (\d+) -->)###");
std::match_results<std::string::const_iterator> match;
- auto found = std::regex_search(*contents_data, match, re);
+ const auto found = std::regex_search(*contents_data, match, RE);
if (found)
{
- int ver = atoi(match[1].str().c_str());
+ const int ver = atoi(match[1].str().c_str());
if (ver >= 1) should_install_system = false;
}
}
@@ -193,10 +210,10 @@ namespace vcpkg::Commands::Integrate
fs.write_contents(sys_src_path, create_system_targets_shortcut());
const std::string param = Strings::format(R"(/c mkdir "%s" & copy "%s" "%s" /Y > nul)",
- system_wide_targets_file.parent_path().string(),
+ SYSTEM_WIDE_TARGETS_FILE.parent_path().string(),
sys_src_path.string(),
- system_wide_targets_file.string());
- ElevationPromptChoice user_choice = elevated_cmd_execute(param);
+ SYSTEM_WIDE_TARGETS_FILE.string());
+ const ElevationPromptChoice user_choice = elevated_cmd_execute(param);
switch (user_choice)
{
case ElevationPromptChoice::YES: break;
@@ -207,47 +224,88 @@ namespace vcpkg::Commands::Integrate
}
Checks::check_exit(VCPKG_LINE_INFO,
- fs.exists(system_wide_targets_file),
+ fs.exists(SYSTEM_WIDE_TARGETS_FILE),
"Error: failed to copy targets file to %s",
- system_wide_targets_file.string());
+ SYSTEM_WIDE_TARGETS_FILE.string());
}
+ }
+#endif
+
+ static void integrate_install(const VcpkgPaths& paths)
+ {
+ auto& fs = paths.get_filesystem();
- const fs::path appdata_src_path = tmp_dir / "vcpkg.user.targets";
- fs.write_contents(appdata_src_path,
- create_appdata_targets_shortcut(paths.buildsystems_msbuild_targets.string()));
- auto appdata_dst_path = get_appdata_targets_path();
+#if defined(_WIN32)
+ {
+ std::error_code ec;
+ const fs::path tmp_dir = paths.buildsystems / "tmp";
+ fs.create_directory(paths.buildsystems, ec);
+ fs.create_directory(tmp_dir, ec);
+
+ integrate_install_msbuild14(fs, tmp_dir);
+
+ const fs::path appdata_src_path = tmp_dir / "vcpkg.user.targets";
+ fs.write_contents(appdata_src_path,
+ create_appdata_targets_shortcut(paths.buildsystems_msbuild_targets.string()));
+ auto appdata_dst_path = get_appdata_targets_path();
+
+ const auto rc = fs.copy_file(appdata_src_path, appdata_dst_path, fs::copy_options::overwrite_existing, ec);
- auto rc = fs.copy_file(appdata_src_path, appdata_dst_path, fs::copy_options::overwrite_existing, ec);
+ if (!rc || ec)
+ {
+ System::println(System::Color::error,
+ "Error: Failed to copy file: %s -> %s",
+ appdata_src_path.string(),
+ appdata_dst_path.string());
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+ }
+#endif
- if (!rc || ec)
+ const auto pathtxt = get_path_txt_path();
+ std::error_code ec;
+ fs.write_contents(pathtxt, paths.root.generic_u8string(), ec);
+ if (ec)
{
- System::println(System::Color::error,
- "Error: Failed to copy file: %s -> %s",
- appdata_src_path.string(),
- appdata_dst_path.string());
+ System::println(System::Color::error, "Error: Failed to write file: %s", pathtxt.string());
Checks::exit_fail(VCPKG_LINE_INFO);
}
+
System::println(System::Color::success, "Applied user-wide integration for this vcpkg root.");
const fs::path cmake_toolchain = paths.buildsystems / "vcpkg.cmake";
- System::println("\n"
- "All MSBuild C++ projects can now #include any installed libraries.\n"
- "Linking will be handled automatically.\n"
- "Installing new libraries will make them instantly available.\n"
- "\n"
- "CMake projects should use -DCMAKE_TOOLCHAIN_FILE=%s",
- cmake_toolchain.generic_string());
+#if defined(_WIN32)
+ System::println(
+ R"(
+All MSBuild C++ projects can now #include any installed libraries.
+Linking will be handled automatically.
+Installing new libraries will make them instantly available.
+
+CMake projects should use: "-DCMAKE_TOOLCHAIN_FILE=%s")",
+ cmake_toolchain.generic_string());
+#else
+ System::println(
+ R"(
+CMake projects should use: "-DCMAKE_TOOLCHAIN_FILE=%s")",
+ cmake_toolchain.generic_string());
+#endif
Checks::exit_success(VCPKG_LINE_INFO);
}
static void integrate_remove(Files::Filesystem& fs)
{
+ std::error_code ec;
+ bool was_deleted = false;
+
+#if defined(_WIN32)
const fs::path path = get_appdata_targets_path();
- std::error_code ec;
- bool was_deleted = fs.remove(path, ec);
+ was_deleted |= fs.remove(path, ec);
+ Checks::check_exit(VCPKG_LINE_INFO, !ec, "Error: Unable to remove user-wide integration: %s", ec.message());
+#endif
- Checks::check_exit(VCPKG_LINE_INFO, !ec, "Error: Unable to remove user-wide integration: %d", ec.message());
+ was_deleted |= fs.remove(get_path_txt_path(), ec);
+ Checks::check_exit(VCPKG_LINE_INFO, !ec, "Error: Unable to remove user-wide integration: %s", ec.message());
if (was_deleted)
{
@@ -261,11 +319,12 @@ namespace vcpkg::Commands::Integrate
Checks::exit_success(VCPKG_LINE_INFO);
}
+#if defined(WIN32)
static void integrate_project(const VcpkgPaths& paths)
{
auto& fs = paths.get_filesystem();
- const fs::path& nuget_exe = paths.get_nuget_exe();
+ const fs::path& nuget_exe = paths.get_tool_exe(Tools::NUGET);
const fs::path& buildsystems_dir = paths.buildsystems;
const fs::path tmp_dir = buildsystems_dir / "tmp";
@@ -284,10 +343,10 @@ namespace vcpkg::Commands::Integrate
fs.write_contents(nuspec_file_path, create_nuspec_file_contents(paths.root, nuget_id, nupkg_version));
// Using all forward slashes for the command line
- const std::wstring cmd_line = Strings::wformat(LR"("%s" pack -OutputDirectory "%s" "%s" > nul)",
- nuget_exe.native(),
- buildsystems_dir.native(),
- nuspec_file_path.native());
+ const std::string cmd_line = Strings::format(R"("%s" pack -OutputDirectory "%s" "%s" > nul)",
+ nuget_exe.u8string(),
+ buildsystems_dir.u8string(),
+ nuspec_file_path.u8string());
const int exit_code = System::cmd_execute_clean(cmd_line);
@@ -297,7 +356,7 @@ namespace vcpkg::Commands::Integrate
System::println(System::Color::success, "Created nupkg: %s", nuget_package.string());
auto source_path = buildsystems_dir.u8string();
- source_path = std::regex_replace(source_path, std::regex("`"), "``");
+ source_path = Strings::replace_all(std::move(source_path), "`", "``");
System::println(R"(
With a project open, go to Tools->NuGet Package Manager->Package Manager Console and paste:
@@ -308,33 +367,103 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console
Checks::exit_success(VCPKG_LINE_INFO);
}
+#endif
+#if defined(_WIN32)
+ static void integrate_powershell(const VcpkgPaths& paths)
+ {
+ static constexpr StringLiteral TITLE = "PowerShell Tab-Completion";
+ const fs::path script_path = paths.scripts / "addPoshVcpkgToPowershellProfile.ps1";
+
+ // Console font corruption workaround
+ SetConsoleCP(437);
+ SetConsoleOutputCP(437);
+
+ const std::string cmd = Strings::format(
+ R"(powershell -NoProfile -ExecutionPolicy Bypass -Command "& {& '%s' %s}")", script_path.u8string(), "");
+ const int rc = System::cmd_execute(cmd);
+
+ SetConsoleCP(CP_UTF8);
+ SetConsoleOutputCP(CP_UTF8);
+
+ if (rc)
+ {
+ System::println(System::Color::error,
+ "%s\n"
+ "Could not run:\n"
+ " '%s'",
+ TITLE,
+ script_path.generic_string());
+
+ {
+ auto locked_metrics = Metrics::g_metrics.lock();
+ locked_metrics->track_property("error", "powershell script failed");
+ locked_metrics->track_property("title", TITLE);
+ }
+ }
+
+ Checks::exit_with_code(VCPKG_LINE_INFO, rc);
+ }
+#endif
+
+#if defined(_WIN32)
const char* const INTEGRATE_COMMAND_HELPSTRING =
" vcpkg integrate install Make installed packages available user-wide. Requires admin privileges on "
"first use\n"
" vcpkg integrate remove Remove user-wide integration\n"
- " vcpkg integrate project Generate a referencing nuget package for individual VS project use\n";
+ " vcpkg integrate project Generate a referencing nuget package for individual VS project use\n"
+ " vcpkg integrate powershell Enable PowerShell Tab-Completion\n";
+#else
+ const char* const INTEGRATE_COMMAND_HELPSTRING =
+ " vcpkg integrate install Make installed packages available user-wide.\n"
+ " vcpkg integrate remove Remove user-wide integration\n";
+#endif
+
+ namespace Subcommand
+ {
+ static const std::string INSTALL = "install";
+ static const std::string REMOVE = "remove";
+ static const std::string PROJECT = "project";
+ static const std::string POWERSHELL = "powershell";
+ }
+
+ static std::vector<std::string> valid_arguments(const VcpkgPaths&)
+ {
+ return {Subcommand::INSTALL, Subcommand::REMOVE, Subcommand::PROJECT, Subcommand::POWERSHELL};
+ }
+
+ const CommandStructure COMMAND_STRUCTURE = {
+ Strings::format("Commands:\n"
+ "%s",
+ INTEGRATE_COMMAND_HELPSTRING),
+ 1,
+ 1,
+ {},
+ &valid_arguments,
+ };
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
{
- static const std::string example = Strings::format("Commands:\n"
- "%s",
- INTEGRATE_COMMAND_HELPSTRING);
- args.check_exact_arg_count(1, example);
- args.check_and_get_optional_command_arguments({});
+ Util::unused(args.parse_arguments(COMMAND_STRUCTURE));
- if (args.command_arguments[0] == "install")
+ if (args.command_arguments[0] == Subcommand::INSTALL)
{
return integrate_install(paths);
}
- if (args.command_arguments[0] == "remove")
+ if (args.command_arguments[0] == Subcommand::REMOVE)
{
return integrate_remove(paths.get_filesystem());
}
- if (args.command_arguments[0] == "project")
+#if defined(_WIN32)
+ if (args.command_arguments[0] == Subcommand::PROJECT)
{
return integrate_project(paths);
}
+ if (args.command_arguments[0] == Subcommand::POWERSHELL)
+ {
+ return integrate_powershell(paths);
+ }
+#endif
Checks::exit_with_message(VCPKG_LINE_INFO, "Unknown parameter %s for integrate", args.command_arguments[0]);
}
diff --git a/toolsrc/src/vcpkg/commands.list.cpp b/toolsrc/src/vcpkg/commands.list.cpp
new file mode 100644
index 000000000..cadc06ad3
--- /dev/null
+++ b/toolsrc/src/vcpkg/commands.list.cpp
@@ -0,0 +1,90 @@
+#include "pch.h"
+
+#include <vcpkg/base/system.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/help.h>
+#include <vcpkg/vcpkglib.h>
+
+namespace vcpkg::Commands::List
+{
+ static constexpr StringLiteral OPTION_FULLDESC =
+ "--x-full-desc"; // TODO: This should find a better home, eventually
+
+ static void do_print(const StatusParagraph& pgh, const bool full_desc)
+ {
+ if (full_desc)
+ {
+ System::println("%-50s %-16s %s", pgh.package.displayname(), pgh.package.version, pgh.package.description);
+ }
+ else
+ {
+ System::println("%-50s %-16s %s",
+ vcpkg::shorten_text(pgh.package.displayname(), 50),
+ vcpkg::shorten_text(pgh.package.version, 16),
+ vcpkg::shorten_text(pgh.package.description, 51));
+ }
+ }
+
+ static constexpr std::array<CommandSwitch, 1> LIST_SWITCHES = {{
+ {OPTION_FULLDESC, "Do not truncate long text"},
+ }};
+
+ const CommandStructure COMMAND_STRUCTURE = {
+ Strings::format(
+ "The argument should be a substring to search for, or no argument to display all installed libraries.\n%s",
+ Help::create_example_string("list png")),
+ 0,
+ 1,
+ {LIST_SWITCHES, {}},
+ nullptr,
+ };
+
+ void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
+ {
+ const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
+
+ const StatusParagraphs status_paragraphs = database_load_check(paths);
+ auto installed_ipv = get_installed_ports(status_paragraphs);
+
+ if (installed_ipv.empty())
+ {
+ System::println("No packages are installed. Did you mean `search`?");
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+
+ auto installed_packages = Util::fmap(installed_ipv, [](const InstalledPackageView& ipv) { return ipv.core; });
+ auto installed_features =
+ Util::fmap_flatten(installed_ipv, [](const InstalledPackageView& ipv) { return ipv.features; });
+ installed_packages.insert(installed_packages.end(), installed_features.begin(), installed_features.end());
+
+ std::sort(installed_packages.begin(),
+ installed_packages.end(),
+ [](const StatusParagraph* lhs, const StatusParagraph* rhs) -> bool {
+ return lhs->package.displayname() < rhs->package.displayname();
+ });
+
+ if (args.command_arguments.empty())
+ {
+ for (const StatusParagraph* status_paragraph : installed_packages)
+ {
+ do_print(*status_paragraph, Util::Sets::contains(options.switches, OPTION_FULLDESC));
+ }
+ }
+ else
+ {
+ // At this point there is 1 argument
+ for (const StatusParagraph* status_paragraph : installed_packages)
+ {
+ const std::string displayname = status_paragraph->package.displayname();
+ if (!Strings::case_insensitive_ascii_contains(displayname, args.command_arguments[0]))
+ {
+ continue;
+ }
+
+ do_print(*status_paragraph, Util::Sets::contains(options.switches, OPTION_FULLDESC));
+ }
+ }
+
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+}
diff --git a/toolsrc/src/commands_owns.cpp b/toolsrc/src/vcpkg/commands.owns.cpp
index 757292e96..ee9584651 100644
--- a/toolsrc/src/commands_owns.cpp
+++ b/toolsrc/src/vcpkg/commands.owns.cpp
@@ -1,8 +1,9 @@
#include "pch.h"
-#include "vcpkg_Commands.h"
-#include "vcpkg_System.h"
-#include "vcpkglib.h"
+#include <vcpkg/base/system.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/help.h>
+#include <vcpkg/vcpkglib.h>
namespace vcpkg::Commands::Owns
{
@@ -22,15 +23,20 @@ namespace vcpkg::Commands::Owns
}
}
}
+ const CommandStructure COMMAND_STRUCTURE = {
+ Strings::format("The argument should be a pattern to search for. %s",
+ Help::create_example_string("owns zlib.dll")),
+ 1,
+ 1,
+ {},
+ nullptr,
+ };
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
{
- static const std::string example = Strings::format("The argument should be a pattern to search for. %s",
- Commands::Help::create_example_string("owns zlib.dll"));
- args.check_exact_arg_count(1, example);
- args.check_and_get_optional_command_arguments({});
+ Util::unused(args.parse_arguments(COMMAND_STRUCTURE));
- StatusParagraphs status_db = database_load_check(paths);
+ const StatusParagraphs status_db = database_load_check(paths);
search_file(paths, args.command_arguments[0], status_db);
Checks::exit_success(VCPKG_LINE_INFO);
}
diff --git a/toolsrc/src/commands_portsdiff.cpp b/toolsrc/src/vcpkg/commands.portsdiff.cpp
index a614d654e..2d2b4bd5f 100644
--- a/toolsrc/src/commands_portsdiff.cpp
+++ b/toolsrc/src/vcpkg/commands.portsdiff.cpp
@@ -1,11 +1,13 @@
#include "pch.h"
-#include "Paragraphs.h"
-#include "SortedVector.h"
-#include "SourceParagraph.h"
-#include "vcpkg_Commands.h"
-#include "vcpkg_Maps.h"
-#include "vcpkg_System.h"
+#include <vcpkg/commands.h>
+#include <vcpkg/help.h>
+#include <vcpkg/paragraphs.h>
+#include <vcpkg/versiont.h>
+
+#include <vcpkg/base/sortedvector.h>
+#include <vcpkg/base/system.h>
+#include <vcpkg/base/util.h>
namespace vcpkg::Commands::PortsDiff
{
@@ -69,68 +71,71 @@ namespace vcpkg::Commands::PortsDiff
for (const std::string& name : ports_to_print)
{
const VersionT& version = names_and_versions.at(name);
- System::println("%-20s %-16s", name, version);
+ System::println(" - %-14s %-16s", name, version);
}
}
static std::map<std::string, VersionT> read_ports_from_commit(const VcpkgPaths& paths,
- const std::wstring& git_commit_id)
+ const std::string& git_commit_id)
{
std::error_code ec;
auto& fs = paths.get_filesystem();
- const fs::path& git_exe = paths.get_git_exe();
+ const fs::path& git_exe = paths.get_tool_exe(Tools::GIT);
const fs::path dot_git_dir = paths.root / ".git";
- const std::wstring ports_dir_name_as_string = paths.ports.filename().native();
+ const std::string ports_dir_name_as_string = paths.ports.filename().u8string();
const fs::path temp_checkout_path =
- paths.root / Strings::wformat(L"%s-%s", ports_dir_name_as_string, git_commit_id);
+ paths.root / Strings::format("%s-%s", ports_dir_name_as_string, git_commit_id);
fs.create_directory(temp_checkout_path, ec);
- const std::wstring checkout_this_dir =
- Strings::wformat(LR"(.\%s)", ports_dir_name_as_string); // Must be relative to the root of the repository
-
- const std::wstring cmd =
- Strings::wformat(LR"("%s" --git-dir="%s" --work-tree="%s" checkout %s -f -q -- %s %s & "%s" reset >NUL)",
- git_exe.native(),
- dot_git_dir.native(),
- temp_checkout_path.native(),
- git_commit_id,
- checkout_this_dir,
- L".vcpkg-root",
- git_exe.native());
+ const auto checkout_this_dir =
+ Strings::format(R"(.\%s)", ports_dir_name_as_string); // Must be relative to the root of the repository
+
+ const std::string cmd =
+ Strings::format(R"("%s" --git-dir="%s" --work-tree="%s" checkout %s -f -q -- %s %s & "%s" reset >NUL)",
+ git_exe.u8string(),
+ dot_git_dir.u8string(),
+ temp_checkout_path.u8string(),
+ git_commit_id,
+ checkout_this_dir,
+ ".vcpkg-root",
+ git_exe.u8string());
System::cmd_execute_clean(cmd);
- const std::vector<SourceParagraph> source_paragraphs =
+ const auto all_ports =
Paragraphs::load_all_ports(paths.get_filesystem(), temp_checkout_path / ports_dir_name_as_string);
- const std::map<std::string, VersionT> names_and_versions =
- Paragraphs::extract_port_names_and_versions(source_paragraphs);
+ 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);
return names_and_versions;
}
- static void check_commit_exists(const fs::path& git_exe, const std::wstring& git_commit_id)
+ static void check_commit_exists(const fs::path& git_exe, const std::string& git_commit_id)
{
static const std::string VALID_COMMIT_OUTPUT = "commit\n";
- const std::wstring cmd = Strings::wformat(LR"("%s" cat-file -t %s)", git_exe.native(), git_commit_id);
+ const auto cmd = Strings::format(R"("%s" cat-file -t %s)", git_exe.u8string(), git_commit_id);
const System::ExitCodeAndOutput output = System::cmd_execute_and_capture_output(cmd);
- Checks::check_exit(VCPKG_LINE_INFO,
- output.output == VALID_COMMIT_OUTPUT,
- "Invalid commit id %s",
- Strings::to_utf8(git_commit_id));
+ Checks::check_exit(
+ VCPKG_LINE_INFO, output.output == VALID_COMMIT_OUTPUT, "Invalid commit id %s", git_commit_id);
}
+ const CommandStructure COMMAND_STRUCTURE = {
+ Strings::format("The argument should be a branch/tag/hash to checkout.\n%s",
+ Help::create_example_string("portsdiff mybranchname")),
+ 1,
+ 2,
+ {},
+ nullptr,
+ };
+
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
{
- static const std::string example =
- Strings::format("The argument should be a branch/tag/hash to checkout.\n%s",
- Commands::Help::create_example_string("portsdiff mybranchname"));
- args.check_min_arg_count(1, example);
- args.check_max_arg_count(2, example);
- args.check_and_get_optional_command_arguments({});
+ Util::unused(args.parse_arguments(COMMAND_STRUCTURE));
- const fs::path& git_exe = paths.get_git_exe();
+ const fs::path& git_exe = paths.get_tool_exe(Tools::GIT);
- const std::wstring git_commit_id_for_previous_snapshot = Strings::to_utf16(args.command_arguments.at(0));
- const std::wstring git_commit_id_for_current_snapshot =
- args.command_arguments.size() < 2 ? L"HEAD" : Strings::to_utf16(args.command_arguments.at(1));
+ const std::string git_commit_id_for_previous_snapshot = args.command_arguments.at(0);
+ const std::string git_commit_id_for_current_snapshot =
+ args.command_arguments.size() < 2 ? "HEAD" : args.command_arguments.at(1);
check_commit_exists(git_exe, git_commit_id_for_current_snapshot);
check_commit_exists(git_exe, git_commit_id_for_previous_snapshot);
@@ -141,8 +146,8 @@ namespace vcpkg::Commands::PortsDiff
read_ports_from_commit(paths, git_commit_id_for_previous_snapshot);
// Already sorted, so set_difference can work on std::vector too
- std::vector<std::string> current_ports = Maps::extract_keys(current_names_and_versions);
- std::vector<std::string> previous_ports = Maps::extract_keys(previous_names_and_versions);
+ const std::vector<std::string> current_ports = Util::extract_keys(current_names_and_versions);
+ const std::vector<std::string> previous_ports = Util::extract_keys(previous_names_and_versions);
const SetElementPresence<std::string> setp =
SetElementPresence<std::string>::create(current_ports, previous_ports);
@@ -150,14 +155,14 @@ namespace vcpkg::Commands::PortsDiff
const std::vector<std::string>& added_ports = setp.only_left;
if (!added_ports.empty())
{
- System::println("\nThe following %d ports were added:\n", added_ports.size());
+ System::println("\nThe following %zd ports were added:", added_ports.size());
do_print_name_and_version(added_ports, current_names_and_versions);
}
const std::vector<std::string>& removed_ports = setp.only_right;
if (!removed_ports.empty())
{
- System::println("\nThe following %d ports were removed:\n", removed_ports.size());
+ System::println("\nThe following %zd ports were removed:", removed_ports.size());
do_print_name_and_version(removed_ports, previous_names_and_versions);
}
@@ -167,10 +172,10 @@ namespace vcpkg::Commands::PortsDiff
if (!updated_ports.empty())
{
- System::println("\nThe following %d ports were updated:\n", updated_ports.size());
+ System::println("\nThe following %zd ports were updated:", updated_ports.size());
for (const UpdatedPort& p : updated_ports)
{
- System::println("%-20s %-16s", p.port, p.version_diff.to_string());
+ System::println(" - %-14s %-16s", p.port, p.version_diff.to_string());
}
}
diff --git a/toolsrc/src/vcpkg/commands.search.cpp b/toolsrc/src/vcpkg/commands.search.cpp
new file mode 100644
index 000000000..33a642e40
--- /dev/null
+++ b/toolsrc/src/vcpkg/commands.search.cpp
@@ -0,0 +1,156 @@
+#include "pch.h"
+
+#include <vcpkg/base/system.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/globalstate.h>
+#include <vcpkg/help.h>
+#include <vcpkg/paragraphs.h>
+#include <vcpkg/sourceparagraph.h>
+#include <vcpkg/vcpkglib.h>
+
+namespace vcpkg::Commands::Search
+{
+ static constexpr StringLiteral OPTION_GRAPH = "--graph"; // TODO: This should find a better home, eventually
+ static constexpr StringLiteral OPTION_FULLDESC =
+ "--x-full-desc"; // TODO: This should find a better home, eventually
+
+ static std::string replace_dashes_with_underscore(const std::string& input)
+ {
+ std::string output = input;
+ std::replace(output.begin(), output.end(), '-', '_');
+ return output;
+ }
+
+ static std::string create_graph_as_string(
+ const std::vector<std::unique_ptr<SourceControlFile>>& source_control_files)
+ {
+ 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)
+ {
+ const SourceParagraph& source_paragraph = *source_control_file->core_paragraph;
+ if (source_paragraph.depends.empty())
+ {
+ empty_node_count++;
+ continue;
+ }
+
+ const std::string name = replace_dashes_with_underscore(source_paragraph.name);
+ s.append(Strings::format("%s;", name));
+ for (const Dependency& d : source_paragraph.depends)
+ {
+ const std::string dependency_name = replace_dashes_with_underscore(d.name());
+ s.append(Strings::format("%s -> %s;", name, dependency_name));
+ }
+ }
+
+ s.append(Strings::format("empty [label=\"%d singletons...\"]; }", empty_node_count));
+ return s;
+ }
+ static void do_print(const SourceParagraph& source_paragraph, bool full_desc)
+ {
+ if (full_desc)
+ {
+ System::println(
+ "%-20s %-16s %s", source_paragraph.name, source_paragraph.version, source_paragraph.description);
+ }
+ else
+ {
+ System::println("%-20s %-16s %s",
+ vcpkg::shorten_text(source_paragraph.name, 20),
+ vcpkg::shorten_text(source_paragraph.version, 16),
+ vcpkg::shorten_text(source_paragraph.description, 81));
+ }
+ }
+
+ static void do_print(const std::string& name, const FeatureParagraph& feature_paragraph, bool full_desc)
+ {
+ if (full_desc)
+ {
+ System::println("%-37s %s", name + "[" + feature_paragraph.name + "]", feature_paragraph.description);
+ }
+ else
+ {
+ System::println("%-37s %s",
+ vcpkg::shorten_text(name + "[" + feature_paragraph.name + "]", 37),
+ vcpkg::shorten_text(feature_paragraph.description, 81));
+ }
+ }
+
+ static constexpr std::array<CommandSwitch, 2> SEARCH_SWITCHES = {{
+ {OPTION_GRAPH, "Open editor into the port-specific buildtree subfolder"},
+ {OPTION_FULLDESC, "Do not truncate long text"},
+ }};
+
+ const CommandStructure COMMAND_STRUCTURE = {
+ Strings::format(
+ "The argument should be a substring to search for, or no argument to display all libraries.\n%s",
+ Help::create_example_string("search png")),
+ 0,
+ 1,
+ {SEARCH_SWITCHES, {}},
+ nullptr,
+ };
+
+ void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
+ {
+ const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
+ const bool full_description = Util::Sets::contains(options.switches, OPTION_FULLDESC);
+
+ auto source_paragraphs = Paragraphs::load_all_ports(paths.get_filesystem(), paths.ports);
+
+ if (Util::Sets::contains(options.switches, OPTION_GRAPH))
+ {
+ const std::string graph_as_string = create_graph_as_string(source_paragraphs);
+ System::println(graph_as_string);
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+
+ if (args.command_arguments.empty())
+ {
+ for (const auto& source_control_file : source_paragraphs)
+ {
+ do_print(*source_control_file->core_paragraph, full_description);
+ for (auto&& feature_paragraph : source_control_file->feature_paragraphs)
+ {
+ do_print(source_control_file->core_paragraph->name, *feature_paragraph, full_description);
+ }
+ }
+ }
+ else
+ {
+ const auto& icontains = Strings::case_insensitive_ascii_contains;
+
+ // At this point there is 1 argument
+ auto&& args_zero = args.command_arguments[0];
+ for (const auto& source_control_file : source_paragraphs)
+ {
+ auto&& sp = *source_control_file->core_paragraph;
+
+ const bool contains_name = icontains(sp.name, args_zero);
+ if (contains_name || icontains(sp.description, args_zero))
+ {
+ do_print(sp, full_description);
+ }
+
+ for (auto&& feature_paragraph : source_control_file->feature_paragraphs)
+ {
+ if (contains_name || icontains(feature_paragraph->name, args_zero) ||
+ icontains(feature_paragraph->description, args_zero))
+ {
+ do_print(sp.name, *feature_paragraph, full_description);
+ }
+ }
+ }
+ }
+
+ System::println(
+ "\nIf your library is not listed, please open an issue at and/or consider making a pull request:\n"
+ " https://github.com/Microsoft/vcpkg/issues");
+
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+}
diff --git a/toolsrc/src/vcpkg/commands.upgrade.cpp b/toolsrc/src/vcpkg/commands.upgrade.cpp
new file mode 100644
index 000000000..a902ddeaf
--- /dev/null
+++ b/toolsrc/src/vcpkg/commands.upgrade.cpp
@@ -0,0 +1,180 @@
+#include "pch.h"
+
+#include <vcpkg/base/util.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/dependencies.h>
+#include <vcpkg/help.h>
+#include <vcpkg/input.h>
+#include <vcpkg/install.h>
+#include <vcpkg/statusparagraphs.h>
+#include <vcpkg/update.h>
+#include <vcpkg/vcpkglib.h>
+
+namespace vcpkg::Commands::Upgrade
+{
+ using Install::KeepGoing;
+ using Install::to_keep_going;
+
+ static constexpr StringLiteral OPTION_NO_DRY_RUN = "--no-dry-run";
+ static constexpr StringLiteral OPTION_KEEP_GOING = "--keep-going";
+
+ static constexpr std::array<CommandSwitch, 2> INSTALL_SWITCHES = {{
+ {OPTION_NO_DRY_RUN, "Actually upgrade"},
+ {OPTION_KEEP_GOING, "Continue installing packages on failure"},
+ }};
+
+ const CommandStructure COMMAND_STRUCTURE = {
+ Help::create_example_string("upgrade --no-dry-run"),
+ 0,
+ SIZE_MAX,
+ {INSTALL_SWITCHES, {}},
+ nullptr,
+ };
+
+ void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet)
+ {
+ const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
+
+ const bool no_dry_run = Util::Sets::contains(options.switches, OPTION_NO_DRY_RUN);
+ const KeepGoing keep_going = to_keep_going(Util::Sets::contains(options.switches, OPTION_KEEP_GOING));
+
+ StatusParagraphs status_db = database_load_check(paths);
+
+ Dependencies::PathsPortFileProvider provider(paths);
+ Dependencies::PackageGraph graph(provider, status_db);
+
+ // input sanitization
+ const std::vector<PackageSpec> specs = Util::fmap(args.command_arguments, [&](auto&& arg) {
+ return Input::check_and_get_package_spec(arg, default_triplet, COMMAND_STRUCTURE.example_text);
+ });
+
+ for (auto&& spec : specs)
+ {
+ Input::check_triplet(spec.triplet(), paths);
+ }
+
+ if (specs.empty())
+ {
+ // If no packages specified, upgrade all outdated packages.
+ auto outdated_packages = Update::find_outdated_packages(provider, status_db);
+
+ if (outdated_packages.empty())
+ {
+ System::println("All installed packages are up-to-date with the local portfiles.");
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+
+ for (auto&& outdated_package : outdated_packages)
+ graph.upgrade(outdated_package.spec);
+ }
+ else
+ {
+ std::vector<PackageSpec> not_installed;
+ std::vector<PackageSpec> no_portfile;
+ std::vector<PackageSpec> to_upgrade;
+ std::vector<PackageSpec> up_to_date;
+
+ for (auto&& spec : specs)
+ {
+ auto it = status_db.find_installed(spec);
+ if (it == status_db.end())
+ {
+ not_installed.push_back(spec);
+ }
+
+ auto maybe_scf = provider.get_control_file(spec.name());
+ if (auto p_scf = maybe_scf.get())
+ {
+ if (it != status_db.end())
+ {
+ if (p_scf->core_paragraph->version != (*it)->package.version)
+ {
+ to_upgrade.push_back(spec);
+ }
+ else
+ {
+ up_to_date.push_back(spec);
+ }
+ }
+ }
+ else
+ {
+ no_portfile.push_back(spec);
+ }
+ }
+
+ Util::sort(not_installed);
+ Util::sort(no_portfile);
+ Util::sort(up_to_date);
+ Util::sort(to_upgrade);
+
+ if (!up_to_date.empty())
+ {
+ System::println(System::Color::success, "The following packages are up-to-date:");
+ System::println(Strings::join(
+ "", up_to_date, [](const PackageSpec& spec) { return " " + spec.to_string() + "\n"; }));
+ }
+
+ if (!not_installed.empty())
+ {
+ System::println(System::Color::error, "The following packages are not installed:");
+ System::println(Strings::join(
+ "", not_installed, [](const PackageSpec& spec) { return " " + spec.to_string() + "\n"; }));
+ }
+
+ if (!no_portfile.empty())
+ {
+ System::println(System::Color::error, "The following packages do not have a valid portfile:");
+ System::println(Strings::join(
+ "", no_portfile, [](const PackageSpec& spec) { return " " + spec.to_string() + "\n"; }));
+ }
+
+ Checks::check_exit(VCPKG_LINE_INFO, not_installed.empty() && no_portfile.empty());
+
+ if (to_upgrade.empty()) Checks::exit_success(VCPKG_LINE_INFO);
+
+ for (auto&& spec : to_upgrade)
+ graph.upgrade(spec);
+ }
+
+ auto plan = graph.serialize();
+
+ Checks::check_exit(VCPKG_LINE_INFO, !plan.empty());
+
+ const Build::BuildPackageOptions install_plan_options = {Build::UseHeadVersion::NO,
+ Build::AllowDownloads::YES,
+ Build::CleanBuildtrees::NO,
+ Build::CleanPackages::NO,
+ Build::DownloadTool::BUILT_IN};
+
+ // Set build settings for all install actions
+ for (auto&& action : plan)
+ {
+ if (auto p_install = action.install_action.get())
+ {
+ p_install->build_options = install_plan_options;
+ }
+ }
+
+ Dependencies::print_plan(plan, true);
+
+ if (!no_dry_run)
+ {
+ System::println(System::Color::warning,
+ "If you are sure you want to rebuild the above packages, run this command with the "
+ "--no-dry-run option.");
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+
+ const Install::InstallSummary summary = Install::perform(plan, keep_going, paths, status_db);
+
+ System::println("\nTotal elapsed time: %s\n", summary.total_elapsed_time);
+
+ if (keep_going == KeepGoing::YES)
+ {
+ summary.print();
+ }
+
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+}
diff --git a/toolsrc/src/vcpkg/commands.version.cpp b/toolsrc/src/vcpkg/commands.version.cpp
new file mode 100644
index 000000000..2ad91b57d
--- /dev/null
+++ b/toolsrc/src/vcpkg/commands.version.cpp
@@ -0,0 +1,94 @@
+#include "pch.h"
+
+#include <vcpkg/base/system.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/help.h>
+#include <vcpkg/metrics.h>
+
+#define STRINGIFY(...) #__VA_ARGS__
+#define MACRO_TO_STRING(X) STRINGIFY(X)
+
+#if defined(VCPKG_VERSION)
+#define VCPKG_VERSION_AS_STRING MACRO_TO_STRING(VCPKG_VERSION)
+#else
+#define VCPKG_VERSION_AS_STRING "-unknownhash"
+#endif
+
+namespace vcpkg::Commands::Version
+{
+ const char* base_version()
+ {
+ return
+#include "../VERSION.txt"
+ ;
+ }
+
+ const std::string& version()
+ {
+ static const std::string S_VERSION =
+#include "../VERSION.txt"
+
+ +std::string(VCPKG_VERSION_AS_STRING)
+#ifndef NDEBUG
+ + std::string("-debug")
+#endif
+ + std::string(Metrics::get_compiled_metrics_enabled() ? "" : "-external");
+ return S_VERSION;
+ }
+
+ static int scan3(const char* input, const char* pattern, int* a, int* b, int* c)
+ {
+#if defined(_WIN32)
+ return sscanf_s(input, pattern, a, b, c);
+#else
+ return sscanf(input, pattern, a, b, c);
+#endif
+ }
+
+ void warn_if_vcpkg_version_mismatch(const VcpkgPaths& paths)
+ {
+ auto version_file = paths.get_filesystem().read_contents(paths.root / "toolsrc" / "VERSION.txt");
+ if (const auto version_contents = version_file.get())
+ {
+ int maj1, min1, rev1;
+ const auto num1 = scan3(version_contents->c_str(), "\"%d.%d.%d\"", &maj1, &min1, &rev1);
+
+ int maj2, min2, rev2;
+ const auto num2 = scan3(Version::version().c_str(), "%d.%d.%d-", &maj2, &min2, &rev2);
+
+ if (num1 == 3 && num2 == 3)
+ {
+ if (maj1 != maj2 || min1 != min2 || rev1 != rev2)
+ {
+ System::println(System::Color::warning,
+ "Warning: Different source is available for vcpkg (%d.%d.%d -> %d.%d.%d). Use "
+ ".\\bootstrap-vcpkg.bat to update.",
+ maj2,
+ min2,
+ rev2,
+ maj1,
+ min1,
+ rev1);
+ }
+ }
+ }
+ }
+ const CommandStructure COMMAND_STRUCTURE = {
+ Help::create_example_string("version"),
+ 0,
+ 0,
+ {},
+ nullptr,
+ };
+
+ void perform_and_exit(const VcpkgCmdArguments& args)
+ {
+ Util::unused(args.parse_arguments(COMMAND_STRUCTURE));
+
+ System::println("Vcpkg package management program version %s\n"
+ "\n"
+ "See LICENSE.txt for license information.",
+ version());
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+}
diff --git a/toolsrc/src/vcpkg/dependencies.cpp b/toolsrc/src/vcpkg/dependencies.cpp
new file mode 100644
index 000000000..8fb35b0da
--- /dev/null
+++ b/toolsrc/src/vcpkg/dependencies.cpp
@@ -0,0 +1,885 @@
+#include "pch.h"
+
+#include <vcpkg/base/files.h>
+#include <vcpkg/base/graphs.h>
+#include <vcpkg/base/strings.h>
+#include <vcpkg/base/util.h>
+#include <vcpkg/dependencies.h>
+#include <vcpkg/packagespec.h>
+#include <vcpkg/paragraphs.h>
+#include <vcpkg/statusparagraphs.h>
+#include <vcpkg/vcpkglib.h>
+#include <vcpkg/vcpkgpaths.h>
+
+namespace vcpkg::Dependencies
+{
+ struct ClusterInstalled
+ {
+ InstalledPackageView ipv;
+ std::set<PackageSpec> remove_edges;
+ std::set<std::string> original_features;
+ };
+
+ struct ClusterSource
+ {
+ const SourceControlFile* scf = nullptr;
+ std::unordered_map<std::string, std::vector<FeatureSpec>> build_edges;
+ };
+
+ /// <summary>
+ /// Representation of a package and its features in a ClusterGraph.
+ /// </summary>
+ struct Cluster : Util::MoveOnlyBase
+ {
+ PackageSpec spec;
+
+ Optional<ClusterInstalled> installed;
+ Optional<ClusterSource> source;
+
+ // Note: this map can contain "special" strings such as "" and "*"
+ std::unordered_map<std::string, bool> plus;
+ std::set<std::string> to_install_features;
+ bool minus = false;
+ bool transient_uninstalled = true;
+ RequestType request_type = RequestType::AUTO_SELECTED;
+ };
+
+ struct ClusterPtr
+ {
+ Cluster* ptr;
+
+ Cluster* operator->() const { return ptr; }
+ };
+
+ bool operator==(const ClusterPtr& l, const ClusterPtr& r) { return l.ptr == r.ptr; }
+}
+
+namespace std
+{
+ template<>
+ struct hash<vcpkg::Dependencies::ClusterPtr>
+ {
+ size_t operator()(const vcpkg::Dependencies::ClusterPtr& value) const
+ {
+ return std::hash<vcpkg::PackageSpec>()(value.ptr->spec);
+ }
+ };
+}
+
+namespace vcpkg::Dependencies
+{
+ struct GraphPlan
+ {
+ Graphs::Graph<ClusterPtr> remove_graph;
+ Graphs::Graph<ClusterPtr> install_graph;
+ };
+
+ /// <summary>
+ /// Directional graph representing a collection of packages with their features connected by their dependencies.
+ /// </summary>
+ struct ClusterGraph : Util::MoveOnlyBase
+ {
+ explicit ClusterGraph(const PortFileProvider& provider) : m_provider(provider) {}
+
+ /// <summary>
+ /// Find the cluster associated with spec or if not found, create it from the PortFileProvider.
+ /// </summary>
+ /// <param name="spec">Package spec to get the cluster for.</param>
+ /// <returns>The cluster found or created for spec.</returns>
+ Cluster& get(const PackageSpec& spec)
+ {
+ auto it = m_graph.find(spec);
+ if (it == m_graph.end())
+ {
+ // Load on-demand from m_provider
+ auto maybe_scf = m_provider.get_control_file(spec.name());
+ auto& clust = m_graph[spec];
+ clust.spec = spec;
+ if (auto p_scf = maybe_scf.get())
+ {
+ clust.source = cluster_from_scf(*p_scf, clust.spec.triplet());
+ }
+ return clust;
+ }
+ return it->second;
+ }
+
+ private:
+ static ClusterSource cluster_from_scf(const SourceControlFile& scf, Triplet t)
+ {
+ ClusterSource ret;
+ ret.build_edges.emplace("core", filter_dependencies_to_specs(scf.core_paragraph->depends, t));
+
+ for (const auto& feature : scf.feature_paragraphs)
+ ret.build_edges.emplace(feature->name, filter_dependencies_to_specs(feature->depends, t));
+
+ ret.scf = &scf;
+ return ret;
+ }
+
+ std::unordered_map<PackageSpec, Cluster> m_graph;
+ const PortFileProvider& m_provider;
+ };
+
+ std::string to_output_string(RequestType request_type,
+ const CStringView s,
+ const Build::BuildPackageOptions& options)
+ {
+ 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, from_head);
+ case RequestType::USER_REQUESTED: return Strings::format(" %s%s", s, from_head);
+ default: Checks::unreachable(VCPKG_LINE_INFO);
+ }
+ }
+
+ std::string to_output_string(RequestType request_type, const CStringView s)
+ {
+ switch (request_type)
+ {
+ case RequestType::AUTO_SELECTED: return Strings::format(" * %s", s);
+ case RequestType::USER_REQUESTED: return Strings::format(" %s", s);
+ default: Checks::unreachable(VCPKG_LINE_INFO);
+ }
+ }
+
+ InstallPlanAction::InstallPlanAction() noexcept
+ : plan_type(InstallPlanType::UNKNOWN), request_type(RequestType::UNKNOWN), build_options{}
+ {
+ }
+
+ InstallPlanAction::InstallPlanAction(const PackageSpec& spec,
+ const SourceControlFile& scf,
+ const std::set<std::string>& features,
+ const RequestType& request_type,
+ std::vector<PackageSpec>&& dependencies)
+ : spec(spec)
+ , source_control_file(scf)
+ , plan_type(InstallPlanType::BUILD_AND_INSTALL)
+ , request_type(request_type)
+ , build_options{}
+ , feature_list(features)
+ , computed_dependencies(std::move(dependencies))
+ {
+ }
+
+ InstallPlanAction::InstallPlanAction(InstalledPackageView&& ipv,
+ const std::set<std::string>& features,
+ const RequestType& request_type)
+ : spec(ipv.spec())
+ , installed_package(std::move(ipv))
+ , plan_type(InstallPlanType::ALREADY_INSTALLED)
+ , request_type(request_type)
+ , build_options{}
+ , feature_list(features)
+ , computed_dependencies(installed_package.get()->dependencies())
+ {
+ }
+
+ std::string InstallPlanAction::displayname() const
+ {
+ if (this->feature_list.empty())
+ {
+ return this->spec.to_string();
+ }
+
+ const std::string features = Strings::join(",", this->feature_list);
+ return Strings::format("%s[%s]:%s", this->spec.name(), features, this->spec.triplet());
+ }
+
+ bool InstallPlanAction::compare_by_name(const InstallPlanAction* left, const InstallPlanAction* right)
+ {
+ return left->spec.name() < right->spec.name();
+ }
+
+ RemovePlanAction::RemovePlanAction() noexcept
+ : plan_type(RemovePlanType::UNKNOWN), request_type(RequestType::UNKNOWN)
+ {
+ }
+
+ RemovePlanAction::RemovePlanAction(const PackageSpec& spec,
+ const RemovePlanType& plan_type,
+ const RequestType& request_type)
+ : spec(spec), plan_type(plan_type), request_type(request_type)
+ {
+ }
+
+ const PackageSpec& AnyAction::spec() const
+ {
+ if (const auto p = install_action.get())
+ {
+ return p->spec;
+ }
+
+ if (const auto p = remove_action.get())
+ {
+ return p->spec;
+ }
+
+ Checks::exit_with_message(VCPKG_LINE_INFO, "Null action");
+ }
+
+ bool ExportPlanAction::compare_by_name(const ExportPlanAction* left, const ExportPlanAction* right)
+ {
+ return left->spec.name() < right->spec.name();
+ }
+
+ ExportPlanAction::ExportPlanAction() noexcept
+ : plan_type(ExportPlanType::UNKNOWN), request_type(RequestType::UNKNOWN)
+ {
+ }
+
+ ExportPlanAction::ExportPlanAction(const PackageSpec& spec,
+ InstalledPackageView&& installed_package,
+ const RequestType& request_type)
+ : spec(spec)
+ , plan_type(ExportPlanType::ALREADY_BUILT)
+ , request_type(request_type)
+ , m_installed_package(std::move(installed_package))
+ {
+ }
+
+ ExportPlanAction::ExportPlanAction(const PackageSpec& spec, const RequestType& request_type)
+ : spec(spec), plan_type(ExportPlanType::NOT_BUILT), request_type(request_type)
+ {
+ }
+
+ Optional<const BinaryParagraph&> ExportPlanAction::core_paragraph() const
+ {
+ if (auto p_ip = m_installed_package.get())
+ {
+ return p_ip->core->package;
+ }
+ return nullopt;
+ }
+
+ std::vector<PackageSpec> ExportPlanAction::dependencies(const Triplet&) const
+ {
+ if (auto p_ip = m_installed_package.get())
+ return p_ip->dependencies();
+ else
+ return {};
+ }
+
+ bool RemovePlanAction::compare_by_name(const RemovePlanAction* left, const RemovePlanAction* right)
+ {
+ return left->spec.name() < right->spec.name();
+ }
+
+ MapPortFileProvider::MapPortFileProvider(const std::unordered_map<std::string, SourceControlFile>& map) : ports(map)
+ {
+ }
+
+ Optional<const SourceControlFile&> MapPortFileProvider::get_control_file(const std::string& spec) const
+ {
+ auto scf = ports.find(spec);
+ if (scf == ports.end()) return nullopt;
+ return scf->second;
+ }
+
+ PathsPortFileProvider::PathsPortFileProvider(const VcpkgPaths& paths) : ports(paths) {}
+
+ Optional<const SourceControlFile&> PathsPortFileProvider::get_control_file(const std::string& spec) const
+ {
+ auto cache_it = cache.find(spec);
+ if (cache_it != cache.end())
+ {
+ return cache_it->second;
+ }
+ Parse::ParseExpected<SourceControlFile> source_control_file =
+ Paragraphs::try_load_port(ports.get_filesystem(), ports.port_dir(spec));
+
+ if (auto scf = source_control_file.get())
+ {
+ auto it = cache.emplace(spec, std::move(*scf->get()));
+ return it.first->second;
+ }
+ return nullopt;
+ }
+
+ std::vector<RemovePlanAction> create_remove_plan(const std::vector<PackageSpec>& specs,
+ const StatusParagraphs& status_db)
+ {
+ struct RemoveAdjacencyProvider final : Graphs::AdjacencyProvider<PackageSpec, RemovePlanAction>
+ {
+ const StatusParagraphs& status_db;
+ const std::vector<InstalledPackageView>& installed_ports;
+ const std::unordered_set<PackageSpec>& specs_as_set;
+
+ RemoveAdjacencyProvider(const StatusParagraphs& status_db,
+ const std::vector<InstalledPackageView>& installed_ports,
+ const std::unordered_set<PackageSpec>& specs_as_set)
+ : status_db(status_db), installed_ports(installed_ports), specs_as_set(specs_as_set)
+ {
+ }
+
+ std::vector<PackageSpec> adjacency_list(const RemovePlanAction& plan) const override
+ {
+ if (plan.plan_type == RemovePlanType::NOT_INSTALLED)
+ {
+ return {};
+ }
+
+ const PackageSpec& spec = plan.spec;
+ std::vector<PackageSpec> dependents;
+ for (auto&& ipv : installed_ports)
+ {
+ auto deps = ipv.dependencies();
+
+ if (std::find(deps.begin(), deps.end(), spec) == deps.end()) continue;
+
+ dependents.push_back(ipv.spec());
+ }
+
+ return dependents;
+ }
+
+ RemovePlanAction load_vertex_data(const PackageSpec& spec) const override
+ {
+ const RequestType request_type = specs_as_set.find(spec) != specs_as_set.end()
+ ? RequestType::USER_REQUESTED
+ : RequestType::AUTO_SELECTED;
+ const StatusParagraphs::const_iterator it = status_db.find_installed(spec);
+ if (it == status_db.end())
+ {
+ return RemovePlanAction{spec, RemovePlanType::NOT_INSTALLED, request_type};
+ }
+ return RemovePlanAction{spec, RemovePlanType::REMOVE, request_type};
+ }
+
+ std::string to_string(const PackageSpec& spec) const override { return spec.to_string(); }
+ };
+
+ auto installed_ports = get_installed_ports(status_db);
+ const std::unordered_set<PackageSpec> specs_as_set(specs.cbegin(), specs.cend());
+ return Graphs::topological_sort(specs, RemoveAdjacencyProvider{status_db, installed_ports, specs_as_set});
+ }
+
+ std::vector<ExportPlanAction> create_export_plan(const std::vector<PackageSpec>& specs,
+ const StatusParagraphs& status_db)
+ {
+ struct ExportAdjacencyProvider final : Graphs::AdjacencyProvider<PackageSpec, ExportPlanAction>
+ {
+ const StatusParagraphs& status_db;
+ const std::unordered_set<PackageSpec>& specs_as_set;
+
+ ExportAdjacencyProvider(const StatusParagraphs& s, const std::unordered_set<PackageSpec>& specs_as_set)
+ : status_db(s), specs_as_set(specs_as_set)
+ {
+ }
+
+ std::vector<PackageSpec> adjacency_list(const ExportPlanAction& plan) const override
+ {
+ return plan.dependencies(plan.spec.triplet());
+ }
+
+ ExportPlanAction load_vertex_data(const PackageSpec& spec) const override
+ {
+ const RequestType request_type = specs_as_set.find(spec) != specs_as_set.end()
+ ? RequestType::USER_REQUESTED
+ : RequestType::AUTO_SELECTED;
+
+ auto maybe_ipv = status_db.find_all_installed(spec);
+
+ if (auto p_ipv = maybe_ipv.get())
+ {
+ return ExportPlanAction{spec, std::move(*p_ipv), request_type};
+ }
+
+ return ExportPlanAction{spec, request_type};
+ }
+
+ std::string to_string(const PackageSpec& spec) const override { return spec.to_string(); }
+ };
+
+ const std::unordered_set<PackageSpec> specs_as_set(specs.cbegin(), specs.cend());
+ std::vector<ExportPlanAction> toposort =
+ Graphs::topological_sort(specs, ExportAdjacencyProvider{status_db, specs_as_set});
+ return toposort;
+ }
+
+ enum class MarkPlusResult
+ {
+ FEATURE_NOT_FOUND,
+ SUCCESS,
+ };
+
+ static MarkPlusResult mark_plus(const std::string& feature,
+ Cluster& cluster,
+ ClusterGraph& graph,
+ GraphPlan& graph_plan,
+ const std::unordered_set<std::string>& prevent_default_features);
+
+ static void mark_minus(Cluster& cluster,
+ ClusterGraph& graph,
+ GraphPlan& graph_plan,
+ const std::unordered_set<std::string>& prevent_default_features);
+
+ static MarkPlusResult follow_plus_dependencies(const std::string& feature,
+ Cluster& cluster,
+ ClusterGraph& graph,
+ GraphPlan& graph_plan,
+ const std::unordered_set<std::string>& prevent_default_features)
+ {
+ if (auto p_source = cluster.source.get())
+ {
+ auto it_build_edges = p_source->build_edges.find(feature);
+ if (it_build_edges != p_source->build_edges.end())
+ {
+ // mark this package for rebuilding if needed
+ mark_minus(cluster, graph, graph_plan, prevent_default_features);
+
+ graph_plan.install_graph.add_vertex({&cluster});
+ cluster.to_install_features.insert(feature);
+
+ if (feature != "core")
+ {
+ // All features implicitly depend on core
+ auto res = mark_plus("core", cluster, graph, graph_plan, prevent_default_features);
+
+ // Should be impossible for "core" to not exist
+ Checks::check_exit(VCPKG_LINE_INFO, res == MarkPlusResult::SUCCESS);
+ }
+
+ if (!cluster.installed.get() && !Util::Sets::contains(prevent_default_features, cluster.spec.name()))
+ {
+ // Add the default features of this package if it was not previously installed and it isn't being
+ // suppressed.
+ auto res = mark_plus("", cluster, graph, graph_plan, prevent_default_features);
+
+ Checks::check_exit(VCPKG_LINE_INFO,
+ res == MarkPlusResult::SUCCESS,
+ "Error: Unable to satisfy default dependencies of %s",
+ cluster.spec);
+ }
+
+ for (auto&& depend : it_build_edges->second)
+ {
+ auto& depend_cluster = graph.get(depend.spec());
+ auto res = mark_plus(depend.feature(), depend_cluster, graph, graph_plan, prevent_default_features);
+
+ Checks::check_exit(VCPKG_LINE_INFO,
+ res == MarkPlusResult::SUCCESS,
+ "Error: Unable to satisfy dependency %s of %s",
+ depend,
+ FeatureSpec(cluster.spec, feature));
+
+ if (&depend_cluster == &cluster) continue;
+ graph_plan.install_graph.add_edge({&cluster}, {&depend_cluster});
+ }
+
+ return MarkPlusResult::SUCCESS;
+ }
+ }
+
+ // The feature was not available in the installed package nor the source paragraph.
+ return MarkPlusResult::FEATURE_NOT_FOUND;
+ }
+
+ MarkPlusResult mark_plus(const std::string& feature,
+ Cluster& cluster,
+ ClusterGraph& graph,
+ GraphPlan& graph_plan,
+ const std::unordered_set<std::string>& prevent_default_features)
+ {
+ auto& plus = cluster.plus[feature];
+ if (plus) return MarkPlusResult::SUCCESS;
+ plus = true;
+
+ if (feature.empty())
+ {
+ // Add default features for this package. This is an exact reference, so ignore prevent_default_features.
+ if (auto p_source = cluster.source.get())
+ {
+ for (auto&& default_feature : p_source->scf->core_paragraph.get()->default_features)
+ {
+ auto res = mark_plus(default_feature, cluster, graph, graph_plan, prevent_default_features);
+ if (res != MarkPlusResult::SUCCESS)
+ {
+ return res;
+ }
+ }
+ }
+ else
+ {
+ Checks::exit_with_message(VCPKG_LINE_INFO,
+ "Error: Unable to install default features because can't find CONTROL for %s",
+ cluster.spec);
+ }
+
+ // "core" is always required.
+ return mark_plus("core", cluster, graph, graph_plan, prevent_default_features);
+ }
+
+ if (feature == "*")
+ {
+ if (auto p_source = cluster.source.get())
+ {
+ for (auto&& fpgh : p_source->scf->feature_paragraphs)
+ {
+ auto res = mark_plus(fpgh->name, cluster, graph, graph_plan, prevent_default_features);
+
+ Checks::check_exit(VCPKG_LINE_INFO,
+ res == MarkPlusResult::SUCCESS,
+ "Error: Unable to locate feature %s in %s",
+ fpgh->name,
+ cluster.spec);
+ }
+
+ auto res = mark_plus("core", cluster, graph, graph_plan, prevent_default_features);
+
+ Checks::check_exit(VCPKG_LINE_INFO, res == MarkPlusResult::SUCCESS);
+ }
+ else
+ {
+ Checks::exit_with_message(
+ VCPKG_LINE_INFO, "Error: Unable to handle '*' because can't find CONTROL for %s", cluster.spec);
+ }
+ return MarkPlusResult::SUCCESS;
+ }
+
+ if (auto p_installed = cluster.installed.get())
+ {
+ if (p_installed->original_features.find(feature) != p_installed->original_features.end())
+ {
+ return MarkPlusResult::SUCCESS;
+ }
+ }
+
+ // This feature was or will be uninstalled, therefore we need to rebuild
+ mark_minus(cluster, graph, graph_plan, prevent_default_features);
+
+ return follow_plus_dependencies(feature, cluster, graph, graph_plan, prevent_default_features);
+ }
+
+ void mark_minus(Cluster& cluster,
+ ClusterGraph& graph,
+ GraphPlan& graph_plan,
+ const std::unordered_set<std::string>& prevent_default_features)
+ {
+ if (cluster.minus) return;
+ cluster.minus = true;
+ cluster.transient_uninstalled = true;
+
+ auto p_installed = cluster.installed.get();
+ auto p_source = cluster.source.get();
+
+ Checks::check_exit(
+ VCPKG_LINE_INFO,
+ p_source,
+ "Error: cannot locate new portfile for %s. Please explicitly remove this package with `vcpkg remove %s`.",
+ cluster.spec,
+ cluster.spec);
+
+ if (p_installed)
+ {
+ graph_plan.remove_graph.add_vertex({&cluster});
+ for (auto&& edge : p_installed->remove_edges)
+ {
+ auto& depend_cluster = graph.get(edge);
+ Checks::check_exit(VCPKG_LINE_INFO, &cluster != &depend_cluster);
+ graph_plan.remove_graph.add_edge({&cluster}, {&depend_cluster});
+ mark_minus(depend_cluster, graph, graph_plan, prevent_default_features);
+ }
+
+ // Reinstall all original features. Don't use mark_plus because it will ignore them since they are
+ // "already installed".
+ for (auto&& f : p_installed->original_features)
+ {
+ auto res = follow_plus_dependencies(f, cluster, graph, graph_plan, prevent_default_features);
+ if (res != MarkPlusResult::SUCCESS)
+ {
+ System::println(System::Color::warning,
+ "Warning: could not reinstall feature %s",
+ FeatureSpec{cluster.spec, f});
+ }
+ }
+
+ // Check if any default features have been added
+ auto& previous_df = p_installed->ipv.core->package.default_features;
+ for (auto&& default_feature : p_source->scf->core_paragraph->default_features)
+ {
+ if (std::find(previous_df.begin(), previous_df.end(), default_feature) == previous_df.end())
+ {
+ // This is a new default feature, mark it for installation
+ auto res = mark_plus(default_feature, cluster, graph, graph_plan, prevent_default_features);
+ if (res != MarkPlusResult::SUCCESS)
+ {
+ System::println(System::Color::warning,
+ "Warning: could not install new default feature %s",
+ FeatureSpec{cluster.spec, default_feature});
+ }
+ }
+ }
+ }
+ }
+
+ /// <summary>Figure out which actions are required to install features specifications in `specs`.</summary>
+ /// <param name="provider">Contains the ports of 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 PortFileProvider& provider,
+ const std::vector<FeatureSpec>& specs,
+ const StatusParagraphs& status_db)
+ {
+ std::unordered_set<std::string> prevent_default_features;
+ for (auto&& spec : specs)
+ {
+ // When "core" is explicitly listed, default features should not be installed.
+ if (spec.feature() == "core") prevent_default_features.insert(spec.name());
+ }
+
+ PackageGraph pgraph(provider, status_db);
+ for (auto&& spec : specs)
+ {
+ // If preventing default features, ignore the automatically generated "" references
+ if (spec.feature().empty() && Util::Sets::contains(prevent_default_features, spec.name())) continue;
+ pgraph.install(spec, prevent_default_features);
+ }
+
+ return pgraph.serialize();
+ }
+
+ /// <summary>Figure out which actions are required to install features specifications in `specs`.</summary>
+ /// <param name="map">Map of all source 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, SourceControlFile>& map,
+ const std::vector<FeatureSpec>& specs,
+ const StatusParagraphs& status_db)
+ {
+ MapPortFileProvider provider(map);
+ return create_feature_install_plan(provider, specs, status_db);
+ }
+
+ /// <param name="prevent_default_features">
+ /// List of package names for which default features should not be installed instead of the core package (e.g. if
+ /// the user is currently installing specific features of that package).
+ /// </param>
+ void PackageGraph::install(const FeatureSpec& spec,
+ const std::unordered_set<std::string>& prevent_default_features) const
+ {
+ Cluster& spec_cluster = m_graph->get(spec.spec());
+ spec_cluster.request_type = RequestType::USER_REQUESTED;
+
+ auto res = mark_plus(spec.feature(), spec_cluster, *m_graph, *m_graph_plan, prevent_default_features);
+
+ Checks::check_exit(VCPKG_LINE_INFO, res == MarkPlusResult::SUCCESS, "Error: Unable to locate feature %s", spec);
+
+ m_graph_plan->install_graph.add_vertex(ClusterPtr{&spec_cluster});
+ }
+
+ void PackageGraph::upgrade(const PackageSpec& spec) const
+ {
+ Cluster& spec_cluster = m_graph->get(spec);
+ spec_cluster.request_type = RequestType::USER_REQUESTED;
+
+ mark_minus(spec_cluster, *m_graph, *m_graph_plan, {});
+ }
+
+ std::vector<AnyAction> PackageGraph::serialize() const
+ {
+ auto remove_vertex_list = m_graph_plan->remove_graph.vertex_list();
+ auto remove_toposort = Graphs::topological_sort(remove_vertex_list, m_graph_plan->remove_graph);
+
+ auto insert_vertex_list = m_graph_plan->install_graph.vertex_list();
+ auto insert_toposort = Graphs::topological_sort(insert_vertex_list, m_graph_plan->install_graph);
+
+ std::vector<AnyAction> plan;
+
+ for (auto&& p_cluster : remove_toposort)
+ {
+ plan.emplace_back(RemovePlanAction{
+ std::move(p_cluster->spec),
+ RemovePlanType::REMOVE,
+ p_cluster->request_type,
+ });
+ }
+
+ for (auto&& p_cluster : insert_toposort)
+ {
+ if (p_cluster->transient_uninstalled)
+ {
+ // If it will be transiently uninstalled, we need to issue a full installation command
+ auto pscf = p_cluster->source.value_or_exit(VCPKG_LINE_INFO).scf;
+
+ auto dep_specs = Util::fmap(m_graph_plan->install_graph.adjacency_list(p_cluster),
+ [](ClusterPtr const& p) { return p->spec; });
+ Util::sort_unique_erase(dep_specs);
+
+ plan.emplace_back(InstallPlanAction{
+ p_cluster->spec,
+ *pscf,
+ p_cluster->to_install_features,
+ p_cluster->request_type,
+ std::move(dep_specs),
+ });
+ }
+ else
+ {
+ // If the package isn't transitively installed, still include it if the user explicitly requested it
+ if (p_cluster->request_type != RequestType::USER_REQUESTED) continue;
+ auto&& installed = p_cluster->installed.value_or_exit(VCPKG_LINE_INFO);
+ plan.emplace_back(InstallPlanAction{
+ InstalledPackageView{installed.ipv},
+ installed.original_features,
+ p_cluster->request_type,
+ });
+ }
+ }
+
+ return plan;
+ }
+
+ static std::unique_ptr<ClusterGraph> create_feature_install_graph(const PortFileProvider& map,
+ const StatusParagraphs& status_db)
+ {
+ std::unique_ptr<ClusterGraph> graph = std::make_unique<ClusterGraph>(map);
+
+ auto installed_ports = get_installed_ports(status_db);
+
+ for (auto&& ipv : installed_ports)
+ {
+ Cluster& cluster = graph->get(ipv.spec());
+
+ cluster.transient_uninstalled = false;
+
+ cluster.installed = [](const InstalledPackageView& ipv) -> ClusterInstalled {
+ ClusterInstalled ret;
+ ret.ipv = ipv;
+ ret.original_features.emplace("core");
+ for (auto&& feature : ipv.features)
+ ret.original_features.emplace(feature->package.feature);
+ return ret;
+ }(ipv);
+ }
+
+ // Populate the graph with "remove edges", which are the reverse of the Build-Depends edges.
+ for (auto&& ipv : installed_ports)
+ {
+ auto deps = ipv.dependencies();
+
+ for (auto&& dep : deps)
+ {
+ auto p_installed = graph->get(dep).installed.get();
+ Checks::check_exit(VCPKG_LINE_INFO,
+ p_installed,
+ "Error: database corrupted. Package %s is installed but dependency %s is not.",
+ ipv.spec(),
+ dep);
+ p_installed->remove_edges.emplace(ipv.spec());
+ }
+ }
+ return graph;
+ }
+
+ PackageGraph::PackageGraph(const PortFileProvider& provider, const StatusParagraphs& status_db)
+ : m_graph_plan(std::make_unique<GraphPlan>()), m_graph(create_feature_install_graph(provider, status_db))
+ {
+ }
+
+ PackageGraph::~PackageGraph() = default;
+
+ void print_plan(const std::vector<AnyAction>& action_plan, const bool is_recursive)
+ {
+ std::vector<const RemovePlanAction*> remove_plans;
+ std::vector<const InstallPlanAction*> rebuilt_plans;
+ std::vector<const InstallPlanAction*> only_install_plans;
+ std::vector<const InstallPlanAction*> new_plans;
+ std::vector<const InstallPlanAction*> already_installed_plans;
+ std::vector<const InstallPlanAction*> excluded;
+
+ const bool has_non_user_requested_packages = Util::find_if(action_plan, [](const AnyAction& package) -> bool {
+ if (auto iplan = package.install_action.get())
+ return iplan->request_type != RequestType::USER_REQUESTED;
+ else
+ return false;
+ }) != action_plan.cend();
+
+ for (auto&& action : action_plan)
+ {
+ if (auto install_action = action.install_action.get())
+ {
+ // remove plans are guaranteed to come before install plans, so we know the plan will be contained if at
+ // all.
+ auto it = Util::find_if(
+ remove_plans, [&](const RemovePlanAction* plan) { return plan->spec == install_action->spec; });
+ if (it != remove_plans.end())
+ {
+ rebuilt_plans.emplace_back(install_action);
+ }
+ else
+ {
+ switch (install_action->plan_type)
+ {
+ case InstallPlanType::ALREADY_INSTALLED:
+ if (install_action->request_type == RequestType::USER_REQUESTED)
+ already_installed_plans.emplace_back(install_action);
+ break;
+ case InstallPlanType::BUILD_AND_INSTALL: new_plans.emplace_back(install_action); break;
+ case InstallPlanType::EXCLUDED: excluded.emplace_back(install_action); break;
+ default: Checks::unreachable(VCPKG_LINE_INFO);
+ }
+ }
+ }
+ else if (auto remove_action = action.remove_action.get())
+ {
+ remove_plans.emplace_back(remove_action);
+ }
+ }
+
+ std::sort(remove_plans.begin(), remove_plans.end(), &RemovePlanAction::compare_by_name);
+ std::sort(rebuilt_plans.begin(), rebuilt_plans.end(), &InstallPlanAction::compare_by_name);
+ std::sort(only_install_plans.begin(), only_install_plans.end(), &InstallPlanAction::compare_by_name);
+ std::sort(new_plans.begin(), new_plans.end(), &InstallPlanAction::compare_by_name);
+ std::sort(already_installed_plans.begin(), already_installed_plans.end(), &InstallPlanAction::compare_by_name);
+ std::sort(excluded.begin(), excluded.end(), &InstallPlanAction::compare_by_name);
+
+ static auto actions_to_output_string = [](const std::vector<const InstallPlanAction*>& v) {
+ return Strings::join("\n", v, [](const InstallPlanAction* p) {
+ return to_output_string(p->request_type, p->displayname(), p->build_options);
+ });
+ };
+
+ if (!excluded.empty())
+ {
+ System::println("The following packages are excluded:\n%s", actions_to_output_string(excluded));
+ }
+
+ if (!already_installed_plans.empty())
+ {
+ System::println("The following packages are already installed:\n%s",
+ actions_to_output_string(already_installed_plans));
+ }
+
+ if (!rebuilt_plans.empty())
+ {
+ System::println("The following packages will be rebuilt:\n%s", actions_to_output_string(rebuilt_plans));
+ }
+
+ if (!new_plans.empty())
+ {
+ System::println("The following packages will be built and installed:\n%s",
+ actions_to_output_string(new_plans));
+ }
+
+ if (!only_install_plans.empty())
+ {
+ System::println("The following packages will be directly installed:\n%s",
+ actions_to_output_string(only_install_plans));
+ }
+
+ if (has_non_user_requested_packages)
+ System::println("Additional packages (*) will be modified to complete this operation.");
+
+ if (!remove_plans.empty() && !is_recursive)
+ {
+ System::println(System::Color::warning,
+ "If you are sure you want to rebuild the above packages, run the command with the "
+ "--recurse option");
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+ }
+}
diff --git a/toolsrc/src/vcpkg/export.cpp b/toolsrc/src/vcpkg/export.cpp
new file mode 100644
index 000000000..c444cf3d0
--- /dev/null
+++ b/toolsrc/src/vcpkg/export.cpp
@@ -0,0 +1,543 @@
+#include "pch.h"
+
+#include <vcpkg/base/stringliteral.h>
+#include <vcpkg/base/system.h>
+#include <vcpkg/base/util.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/dependencies.h>
+#include <vcpkg/export.h>
+#include <vcpkg/export.ifw.h>
+#include <vcpkg/help.h>
+#include <vcpkg/input.h>
+#include <vcpkg/install.h>
+#include <vcpkg/paragraphs.h>
+#include <vcpkg/vcpkglib.h>
+
+namespace vcpkg::Export
+{
+ using Dependencies::ExportPlanAction;
+ using Dependencies::ExportPlanType;
+ using Dependencies::RequestType;
+ using Install::InstallDir;
+
+ static std::string create_nuspec_file_contents(const std::string& raw_exported_dir,
+ const std::string& targets_redirect_path,
+ const std::string& nuget_id,
+ const std::string& nupkg_version)
+ {
+ static constexpr auto CONTENT_TEMPLATE = R"(
+<package>
+ <metadata>
+ <id>@NUGET_ID@</id>
+ <version>@VERSION@</version>
+ <authors>vcpkg</authors>
+ <description>
+ Vcpkg NuGet export
+ </description>
+ </metadata>
+ <files>
+ <file src="@RAW_EXPORTED_DIR@\installed\**" target="installed" />
+ <file src="@RAW_EXPORTED_DIR@\scripts\**" target="scripts" />
+ <file src="@RAW_EXPORTED_DIR@\.vcpkg-root" target="" />
+ <file src="@TARGETS_REDIRECT_PATH@" target="build\native\@NUGET_ID@.targets" />
+ </files>
+</package>
+)";
+
+ std::string nuspec_file_content = Strings::replace_all(CONTENT_TEMPLATE, "@NUGET_ID@", nuget_id);
+ nuspec_file_content = Strings::replace_all(std::move(nuspec_file_content), "@VERSION@", nupkg_version);
+ nuspec_file_content =
+ Strings::replace_all(std::move(nuspec_file_content), "@RAW_EXPORTED_DIR@", raw_exported_dir);
+ nuspec_file_content =
+ Strings::replace_all(std::move(nuspec_file_content), "@TARGETS_REDIRECT_PATH@", targets_redirect_path);
+ return nuspec_file_content;
+ }
+
+ static std::string create_targets_redirect(const std::string& target_path) noexcept
+ {
+ return Strings::format(R"###(
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Condition="Exists('%s')" Project="%s" />
+</Project>
+)###",
+ target_path,
+ target_path);
+ }
+
+ static void print_plan(const std::map<ExportPlanType, std::vector<const ExportPlanAction*>>& group_by_plan_type)
+ {
+ static constexpr std::array<ExportPlanType, 2> ORDER = {ExportPlanType::ALREADY_BUILT,
+ ExportPlanType::NOT_BUILT};
+ static constexpr Build::BuildPackageOptions BUILD_OPTIONS = {Build::UseHeadVersion::NO,
+ Build::AllowDownloads::YES,
+ Build::CleanBuildtrees::NO,
+ Build::CleanPackages::NO,
+ Build::DownloadTool::BUILT_IN};
+
+ for (const ExportPlanType plan_type : ORDER)
+ {
+ const auto it = group_by_plan_type.find(plan_type);
+ if (it == group_by_plan_type.cend())
+ {
+ continue;
+ }
+
+ std::vector<const ExportPlanAction*> cont = it->second;
+ std::sort(cont.begin(), cont.end(), &ExportPlanAction::compare_by_name);
+ const std::string as_string = Strings::join("\n", cont, [](const ExportPlanAction* p) {
+ return Dependencies::to_output_string(p->request_type, p->spec.to_string(), BUILD_OPTIONS);
+ });
+
+ switch (plan_type)
+ {
+ case ExportPlanType::ALREADY_BUILT:
+ System::println("The following packages are already built and will be exported:\n%s", as_string);
+ continue;
+ case ExportPlanType::NOT_BUILT:
+ System::println("The following packages need to be built:\n%s", as_string);
+ continue;
+ default: Checks::unreachable(VCPKG_LINE_INFO);
+ }
+ }
+ }
+
+ static std::string create_export_id()
+ {
+ const tm date_time = System::get_current_date_time();
+
+ // Format is: YYYYmmdd-HHMMSS
+ // 15 characters + 1 null terminating character will be written for a total of 16 chars
+ char mbstr[16];
+ const size_t bytes_written = std::strftime(mbstr, sizeof(mbstr), "%Y%m%d-%H%M%S", &date_time);
+ Checks::check_exit(VCPKG_LINE_INFO,
+ bytes_written == 15,
+ "Expected 15 bytes to be written, but %u were written",
+ bytes_written);
+ const std::string date_time_as_string(mbstr);
+ return ("vcpkg-export-" + date_time_as_string);
+ }
+
+ static fs::path do_nuget_export(const VcpkgPaths& paths,
+ const std::string& nuget_id,
+ const std::string& nuget_version,
+ const fs::path& raw_exported_dir,
+ const fs::path& output_dir)
+ {
+ Files::Filesystem& fs = paths.get_filesystem();
+ const fs::path& nuget_exe = paths.get_tool_exe(Tools::NUGET);
+
+ // This file will be placed in "build\native" in the nuget package. Therefore, go up two dirs.
+ const std::string targets_redirect_content =
+ create_targets_redirect("../../scripts/buildsystems/msbuild/vcpkg.targets");
+ const fs::path targets_redirect = paths.buildsystems / "tmp" / "vcpkg.export.nuget.targets";
+
+ std::error_code ec;
+ fs.create_directories(paths.buildsystems / "tmp", ec);
+
+ fs.write_contents(targets_redirect, targets_redirect_content);
+
+ const std::string nuspec_file_content =
+ create_nuspec_file_contents(raw_exported_dir.string(), targets_redirect.string(), nuget_id, nuget_version);
+ const fs::path nuspec_file_path = paths.buildsystems / "tmp" / "vcpkg.export.nuspec";
+ fs.write_contents(nuspec_file_path, nuspec_file_content);
+
+ // -NoDefaultExcludes is needed for ".vcpkg-root"
+ const auto cmd_line = Strings::format(R"("%s" pack -OutputDirectory "%s" "%s" -NoDefaultExcludes > nul)",
+ nuget_exe.u8string(),
+ output_dir.u8string(),
+ nuspec_file_path.u8string());
+
+ const int exit_code = System::cmd_execute_clean(cmd_line);
+ Checks::check_exit(VCPKG_LINE_INFO, exit_code == 0, "Error: NuGet package creation failed");
+
+ const fs::path output_path = output_dir / (nuget_id + ".nupkg");
+ return output_path;
+ }
+
+ struct ArchiveFormat final
+ {
+ enum class BackingEnum
+ {
+ ZIP = 1,
+ SEVEN_ZIP,
+ };
+
+ constexpr ArchiveFormat() = delete;
+
+ constexpr ArchiveFormat(BackingEnum backing_enum, const char* extension, const char* cmake_option)
+ : backing_enum(backing_enum), m_extension(extension), m_cmake_option(cmake_option)
+ {
+ }
+
+ constexpr operator BackingEnum() const { return backing_enum; }
+ constexpr CStringView extension() const { return this->m_extension; }
+ constexpr CStringView cmake_option() const { return this->m_cmake_option; }
+
+ private:
+ BackingEnum backing_enum;
+ const char* m_extension;
+ const char* m_cmake_option;
+ };
+
+ namespace ArchiveFormatC
+ {
+ constexpr const ArchiveFormat ZIP(ArchiveFormat::BackingEnum::ZIP, "zip", "zip");
+ constexpr const ArchiveFormat SEVEN_ZIP(ArchiveFormat::BackingEnum::SEVEN_ZIP, "7z", "7zip");
+ }
+
+ static fs::path do_archive_export(const VcpkgPaths& paths,
+ const fs::path& raw_exported_dir,
+ const fs::path& output_dir,
+ const ArchiveFormat& format)
+ {
+ const fs::path& cmake_exe = paths.get_tool_exe(Tools::CMAKE);
+
+ const std::string exported_dir_filename = raw_exported_dir.filename().u8string();
+ const std::string exported_archive_filename =
+ Strings::format("%s.%s", exported_dir_filename, format.extension());
+ const fs::path exported_archive_path = (output_dir / exported_archive_filename);
+
+ // -NoDefaultExcludes is needed for ".vcpkg-root"
+ const auto cmd_line = Strings::format(R"("%s" -E tar "cf" "%s" --format=%s -- "%s")",
+ cmake_exe.u8string(),
+ exported_archive_path.u8string(),
+ format.cmake_option(),
+ raw_exported_dir.u8string());
+
+ const int exit_code = System::cmd_execute_clean(cmd_line);
+ Checks::check_exit(
+ VCPKG_LINE_INFO, exit_code == 0, "Error: %s creation failed", exported_archive_path.generic_string());
+ return exported_archive_path;
+ }
+
+ static Optional<std::string> maybe_lookup(std::unordered_map<std::string, std::string> const& m,
+ std::string const& key)
+ {
+ const auto it = m.find(key);
+ if (it != m.end()) return it->second;
+ return nullopt;
+ }
+
+ void export_integration_files(const fs::path& raw_exported_dir_path, const VcpkgPaths& paths)
+ {
+ const std::vector<fs::path> integration_files_relative_to_root = {
+ {".vcpkg-root"},
+ {fs::path{"scripts"} / "buildsystems" / "msbuild" / "applocal.ps1"},
+ {fs::path{"scripts"} / "buildsystems" / "msbuild" / "vcpkg.targets"},
+ {fs::path{"scripts"} / "buildsystems" / "vcpkg.cmake"},
+ {fs::path{"scripts"} / "cmake" / "vcpkg_get_windows_sdk.cmake"},
+ };
+
+ for (const fs::path& file : integration_files_relative_to_root)
+ {
+ const fs::path source = paths.root / file;
+ fs::path destination = raw_exported_dir_path / file;
+ Files::Filesystem& fs = paths.get_filesystem();
+ std::error_code ec;
+ fs.create_directories(destination.parent_path(), ec);
+ Checks::check_exit(VCPKG_LINE_INFO, !ec);
+ fs.copy_file(source, destination, fs::copy_options::overwrite_existing, ec);
+ Checks::check_exit(VCPKG_LINE_INFO, !ec);
+ }
+ }
+
+ struct ExportArguments
+ {
+ bool dry_run = false;
+ bool raw = false;
+ bool nuget = false;
+ bool ifw = false;
+ bool zip = false;
+ bool seven_zip = false;
+
+ Optional<std::string> maybe_output;
+
+ Optional<std::string> maybe_nuget_id;
+ Optional<std::string> maybe_nuget_version;
+
+ IFW::Options ifw_options;
+ std::vector<PackageSpec> specs;
+ };
+
+ static constexpr StringLiteral OPTION_OUTPUT = "--output";
+ static constexpr StringLiteral OPTION_DRY_RUN = "--dry-run";
+ static constexpr StringLiteral OPTION_RAW = "--raw";
+ static constexpr StringLiteral OPTION_NUGET = "--nuget";
+ static constexpr StringLiteral OPTION_IFW = "--ifw";
+ static constexpr StringLiteral OPTION_ZIP = "--zip";
+ static constexpr StringLiteral OPTION_SEVEN_ZIP = "--7zip";
+ static constexpr StringLiteral OPTION_NUGET_ID = "--nuget-id";
+ static constexpr StringLiteral OPTION_NUGET_VERSION = "--nuget-version";
+ static constexpr StringLiteral OPTION_IFW_REPOSITORY_URL = "--ifw-repository-url";
+ static constexpr StringLiteral OPTION_IFW_PACKAGES_DIR_PATH = "--ifw-packages-directory-path";
+ static constexpr StringLiteral OPTION_IFW_REPOSITORY_DIR_PATH = "--ifw-repository-directory-path";
+ static constexpr StringLiteral OPTION_IFW_CONFIG_FILE_PATH = "--ifw-configuration-file-path";
+ static constexpr StringLiteral OPTION_IFW_INSTALLER_FILE_PATH = "--ifw-installer-file-path";
+
+ static constexpr std::array<CommandSwitch, 6> EXPORT_SWITCHES = {{
+ {OPTION_DRY_RUN, "Do not actually export"},
+ {OPTION_RAW, "Export to an uncompressed directory"},
+ {OPTION_NUGET, "Export a NuGet package"},
+ {OPTION_IFW, "Export to an IFW-based installer"},
+ {OPTION_ZIP, "Export to a zip file"},
+ {OPTION_SEVEN_ZIP, "Export to a 7zip (.7z) file"},
+ }};
+
+ static constexpr std::array<CommandSetting, 8> EXPORT_SETTINGS = {{
+ {OPTION_OUTPUT, "Specify the output name (used to construct filename)"},
+ {OPTION_NUGET_ID, "Specify the id for the exported NuGet package (overrides --output)"},
+ {OPTION_NUGET_VERSION, "Specify the version for the exported NuGet package"},
+ {OPTION_IFW_REPOSITORY_URL, "Specify the remote repository URL for the online installer"},
+ {OPTION_IFW_PACKAGES_DIR_PATH, "Specify the temporary directory path for the repacked packages"},
+ {OPTION_IFW_REPOSITORY_DIR_PATH, "Specify the directory path for the exported repository"},
+ {OPTION_IFW_CONFIG_FILE_PATH, "Specify the temporary file path for the installer configuration"},
+ {OPTION_IFW_INSTALLER_FILE_PATH, "Specify the file path for the exported installer"},
+ }};
+
+ const CommandStructure COMMAND_STRUCTURE = {
+ Help::create_example_string("export zlib zlib:x64-windows boost --nuget"),
+ 0,
+ SIZE_MAX,
+ {EXPORT_SWITCHES, EXPORT_SETTINGS},
+ nullptr,
+ };
+
+ static ExportArguments handle_export_command_arguments(const VcpkgCmdArguments& args,
+ const Triplet& default_triplet)
+ {
+ ExportArguments ret;
+
+ const auto options = args.parse_arguments(COMMAND_STRUCTURE);
+
+ // input sanitization
+ ret.specs = Util::fmap(args.command_arguments, [&](auto&& arg) {
+ return Input::check_and_get_package_spec(arg, default_triplet, COMMAND_STRUCTURE.example_text);
+ });
+ ret.dry_run = options.switches.find(OPTION_DRY_RUN) != options.switches.cend();
+ ret.raw = options.switches.find(OPTION_RAW) != options.switches.cend();
+ ret.nuget = options.switches.find(OPTION_NUGET) != options.switches.cend();
+ ret.ifw = options.switches.find(OPTION_IFW) != options.switches.cend();
+ ret.zip = options.switches.find(OPTION_ZIP) != options.switches.cend();
+ ret.seven_zip = options.switches.find(OPTION_SEVEN_ZIP) != options.switches.cend();
+
+ ret.maybe_output = maybe_lookup(options.settings, OPTION_OUTPUT);
+
+ if (!ret.raw && !ret.nuget && !ret.ifw && !ret.zip && !ret.seven_zip && !ret.dry_run)
+ {
+ System::println(System::Color::error,
+ "Must provide at least one export type: --raw --nuget --ifw --zip --7zip");
+ System::print(COMMAND_STRUCTURE.example_text);
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+
+ struct OptionPair
+ {
+ const std::string& name;
+ Optional<std::string>& out_opt;
+ };
+ const auto options_implies =
+ [&](const std::string& main_opt_name, bool main_opt, Span<const OptionPair> implying_opts) {
+ if (main_opt)
+ {
+ for (auto&& opt : implying_opts)
+ opt.out_opt = maybe_lookup(options.settings, opt.name);
+ }
+ else
+ {
+ for (auto&& opt : implying_opts)
+ Checks::check_exit(VCPKG_LINE_INFO,
+ !maybe_lookup(options.settings, opt.name),
+ "%s is only valid with %s",
+ opt.name,
+ main_opt_name);
+ }
+ };
+
+ options_implies(OPTION_NUGET,
+ ret.nuget,
+ {
+ {OPTION_NUGET_ID, ret.maybe_nuget_id},
+ {OPTION_NUGET_VERSION, ret.maybe_nuget_version},
+ });
+
+ options_implies(OPTION_IFW,
+ ret.ifw,
+ {
+ {OPTION_IFW_REPOSITORY_URL, ret.ifw_options.maybe_repository_url},
+ {OPTION_IFW_PACKAGES_DIR_PATH, ret.ifw_options.maybe_packages_dir_path},
+ {OPTION_IFW_REPOSITORY_DIR_PATH, ret.ifw_options.maybe_repository_dir_path},
+ {OPTION_IFW_CONFIG_FILE_PATH, ret.ifw_options.maybe_config_file_path},
+ {OPTION_IFW_INSTALLER_FILE_PATH, ret.ifw_options.maybe_installer_file_path},
+ });
+ return ret;
+ }
+
+ static void print_next_step_info(const fs::path& prefix)
+ {
+ const fs::path cmake_toolchain = prefix / "scripts" / "buildsystems" / "vcpkg.cmake";
+ const System::CMakeVariable cmake_variable =
+ System::CMakeVariable("CMAKE_TOOLCHAIN_FILE", cmake_toolchain.generic_string());
+ System::println("\n"
+ "To use the exported libraries in CMake projects use:"
+ "\n"
+ " %s"
+ "\n",
+ cmake_variable.s);
+ }
+
+ static void handle_raw_based_export(Span<const ExportPlanAction> export_plan,
+ const ExportArguments& opts,
+ const std::string& export_id,
+ const VcpkgPaths& paths)
+ {
+ 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;
+ std::error_code ec;
+ fs.remove_all(raw_exported_dir_path, ec);
+ fs.create_directory(raw_exported_dir_path, ec);
+
+ // execute the plan
+ for (const ExportPlanAction& action : export_plan)
+ {
+ if (action.plan_type != ExportPlanType::ALREADY_BUILT)
+ {
+ Checks::unreachable(VCPKG_LINE_INFO);
+ }
+
+ const std::string display_name = action.spec.to_string();
+ System::println("Exporting package %s... ", display_name);
+
+ const BinaryParagraph& binary_paragraph = action.core_paragraph().value_or_exit(VCPKG_LINE_INFO);
+
+ const InstallDir dirs = InstallDir::from_destination_root(
+ raw_exported_dir_path / "installed",
+ action.spec.triplet().to_string(),
+ raw_exported_dir_path / "installed" / "vcpkg" / "info" / (binary_paragraph.fullstem() + ".list"));
+
+ Install::install_files_and_write_listfile(paths.get_filesystem(), paths.package_dir(action.spec), dirs);
+ System::println(System::Color::success, "Exporting package %s... done", display_name);
+ }
+
+ // Copy files needed for integration
+ export_integration_files(raw_exported_dir_path, paths);
+
+ if (opts.raw)
+ {
+ System::println(
+ System::Color::success, R"(Files exported at: "%s")", raw_exported_dir_path.generic_string());
+ print_next_step_info(raw_exported_dir_path);
+ }
+
+ if (opts.nuget)
+ {
+ System::println("Creating nuget package... ");
+
+ const std::string nuget_id = opts.maybe_nuget_id.value_or(raw_exported_dir_path.filename().string());
+ const std::string nuget_version = opts.maybe_nuget_version.value_or("1.0.0");
+ const fs::path output_path =
+ do_nuget_export(paths, nuget_id, nuget_version, raw_exported_dir_path, export_to_path);
+ System::println(System::Color::success, "Creating nuget package... done");
+ System::println(System::Color::success, "NuGet package exported at: %s", output_path.generic_string());
+
+ System::println(R"(
+With a project open, go to Tools->NuGet Package Manager->Package Manager Console and paste:
+ Install-Package %s -Source "%s"
+)"
+ "\n",
+ nuget_id,
+ output_path.parent_path().u8string());
+ }
+
+ if (opts.zip)
+ {
+ System::println("Creating zip archive... ");
+ const fs::path output_path =
+ do_archive_export(paths, raw_exported_dir_path, export_to_path, ArchiveFormatC::ZIP);
+ System::println(System::Color::success, "Creating zip archive... done");
+ System::println(System::Color::success, "Zip archive exported at: %s", output_path.generic_string());
+ print_next_step_info("[...]");
+ }
+
+ if (opts.seven_zip)
+ {
+ System::println("Creating 7zip archive... ");
+ const fs::path output_path =
+ do_archive_export(paths, raw_exported_dir_path, export_to_path, ArchiveFormatC::SEVEN_ZIP);
+ System::println(System::Color::success, "Creating 7zip archive... done");
+ System::println(System::Color::success, "7zip archive exported at: %s", output_path.generic_string());
+ print_next_step_info("[...]");
+ }
+
+ if (!opts.raw)
+ {
+ fs.remove_all(raw_exported_dir_path, ec);
+ }
+ }
+
+ void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet)
+ {
+ const auto opts = handle_export_command_arguments(args, default_triplet);
+ for (auto&& spec : opts.specs)
+ Input::check_triplet(spec.triplet(), paths);
+
+ // create the plan
+ const StatusParagraphs status_db = database_load_check(paths);
+ Dependencies::PathsPortFileProvider provider(paths);
+ std::vector<ExportPlanAction> export_plan = Dependencies::create_export_plan(opts.specs, status_db);
+ Checks::check_exit(VCPKG_LINE_INFO, !export_plan.empty(), "Export plan cannot be empty");
+
+ std::map<ExportPlanType, std::vector<const ExportPlanAction*>> group_by_plan_type;
+ Util::group_by(export_plan, &group_by_plan_type, [](const ExportPlanAction& p) { return p.plan_type; });
+ print_plan(group_by_plan_type);
+
+ const bool has_non_user_requested_packages =
+ Util::find_if(export_plan, [](const ExportPlanAction& package) -> bool {
+ return package.request_type != RequestType::USER_REQUESTED;
+ }) != export_plan.cend();
+
+ if (has_non_user_requested_packages)
+ {
+ System::println(System::Color::warning,
+ "Additional packages (*) need to be exported to complete this operation.");
+ }
+
+ const auto it = group_by_plan_type.find(ExportPlanType::NOT_BUILT);
+ if (it != group_by_plan_type.cend() && !it->second.empty())
+ {
+ System::println(System::Color::error, "There are packages that have not been built.");
+
+ // No need to show all of them, just the user-requested ones. Dependency resolution will handle the rest.
+ std::vector<const ExportPlanAction*> unbuilt = it->second;
+ Util::erase_remove_if(
+ unbuilt, [](const ExportPlanAction* a) { return a->request_type != RequestType::USER_REQUESTED; });
+
+ const auto s = Strings::join(" ", unbuilt, [](const ExportPlanAction* a) { return a->spec.to_string(); });
+ System::println("To build them, run:\n"
+ " vcpkg install %s",
+ s);
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+
+ if (opts.dry_run)
+ {
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+
+ std::string export_id = opts.maybe_output.value_or(create_export_id());
+
+ if (opts.raw || opts.nuget || opts.zip || opts.seven_zip)
+ {
+ handle_raw_based_export(export_plan, opts, export_id, paths);
+ }
+
+ if (opts.ifw)
+ {
+ IFW::do_export(export_plan, export_id, opts.ifw_options, paths);
+
+ print_next_step_info("@RootDir@/src/vcpkg");
+ }
+
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+}
diff --git a/toolsrc/src/vcpkg/globalstate.cpp b/toolsrc/src/vcpkg/globalstate.cpp
new file mode 100644
index 000000000..a4100acf7
--- /dev/null
+++ b/toolsrc/src/vcpkg/globalstate.cpp
@@ -0,0 +1,16 @@
+#include "pch.h"
+
+#include <vcpkg/globalstate.h>
+
+namespace vcpkg
+{
+ Util::LockGuarded<Chrono::ElapsedTimer> GlobalState::timer;
+ Util::LockGuarded<std::string> GlobalState::g_surveydate;
+
+ std::atomic<bool> GlobalState::debugging(false);
+ std::atomic<bool> GlobalState::feature_packages(true);
+ std::atomic<bool> GlobalState::g_binary_caching(false);
+
+ std::atomic<int> GlobalState::g_init_console_cp(0);
+ std::atomic<int> GlobalState::g_init_console_output_cp(0);
+}
diff --git a/toolsrc/src/vcpkg/help.cpp b/toolsrc/src/vcpkg/help.cpp
new file mode 100644
index 000000000..743619937
--- /dev/null
+++ b/toolsrc/src/vcpkg/help.cpp
@@ -0,0 +1,151 @@
+#include "pch.h"
+
+#include <vcpkg/base/system.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/export.h>
+#include <vcpkg/help.h>
+#include <vcpkg/install.h>
+#include <vcpkg/remove.h>
+
+namespace vcpkg::Help
+{
+ struct Topic
+ {
+ using topic_function = void (*)(const VcpkgPaths& paths);
+
+ constexpr Topic(CStringView n, topic_function fn) : name(n), print(fn) {}
+
+ CStringView name;
+ topic_function print;
+ };
+
+ template<const CommandStructure& S>
+ static void command_topic_fn(const VcpkgPaths&)
+ {
+ display_usage(S);
+ }
+
+ static void integrate_topic_fn(const VcpkgPaths&)
+ {
+ System::print("Commands:\n"
+ "%s",
+ Commands::Integrate::INTEGRATE_COMMAND_HELPSTRING);
+ }
+
+ static void help_topics(const VcpkgPaths&);
+
+ const CommandStructure COMMAND_STRUCTURE = {
+ Help::create_example_string("help"),
+ 0,
+ 1,
+ {},
+ nullptr,
+ };
+
+ static constexpr std::array<Topic, 12> topics = {{
+ {"create", command_topic_fn<Commands::Create::COMMAND_STRUCTURE>},
+ {"edit", command_topic_fn<Commands::Edit::COMMAND_STRUCTURE>},
+ {"env", command_topic_fn<Commands::Env::COMMAND_STRUCTURE>},
+ {"export", command_topic_fn<Export::COMMAND_STRUCTURE>},
+ {"help", command_topic_fn<Help::COMMAND_STRUCTURE>},
+ {"install", command_topic_fn<Install::COMMAND_STRUCTURE>},
+ {"integrate", integrate_topic_fn},
+ {"list", command_topic_fn<Commands::List::COMMAND_STRUCTURE>},
+ {"owns", command_topic_fn<Commands::Owns::COMMAND_STRUCTURE>},
+ {"remove", command_topic_fn<Remove::COMMAND_STRUCTURE>},
+ {"search", command_topic_fn<Commands::Search::COMMAND_STRUCTURE>},
+ {"topics", help_topics},
+ }};
+
+ static void help_topics(const VcpkgPaths&)
+ {
+ System::println("Available help topics:\n"
+ " triplet\n"
+ " integrate"
+ "%s",
+ Strings::join("", topics, [](const Topic& topic) { return std::string("\n ") + topic.name; }));
+ }
+
+ void help_topic_valid_triplet(const VcpkgPaths& paths)
+ {
+ System::println("Available architecture triplets:");
+ for (auto&& triplet : paths.get_available_triplets())
+ {
+ System::println(" %s", triplet);
+ }
+ }
+
+ void print_usage()
+ {
+ System::println(
+ "Commands:\n"
+ " vcpkg search [pat] Search for packages available to be built\n"
+ " vcpkg install <pkg>... Install a package\n"
+ " vcpkg remove <pkg>... Uninstall a package\n"
+ " vcpkg remove --outdated Uninstall all out-of-date packages\n"
+ " vcpkg list List installed packages\n"
+ " vcpkg update Display list of packages for updating\n"
+ " vcpkg upgrade Rebuild all outdated packages\n"
+ " vcpkg hash <file> [alg] Hash a file by specific algorithm, default SHA512\n"
+ " vcpkg help topics Display the list of help topics\n"
+ " vcpkg help <topic> Display help for a specific topic\n"
+ "\n"
+ "%s" // Integration help
+ "\n"
+ " vcpkg export <pkg>... [opt]... Exports a package\n"
+ " vcpkg edit <pkg> Open up a port for editing (uses %%EDITOR%%, default 'code')\n"
+ " vcpkg import <pkg> Import a pre-built library\n"
+ " vcpkg create <pkg> <url>\n"
+ " [archivename] Create a new package\n"
+ " vcpkg owns <pat> Search for files in installed packages\n"
+ " vcpkg env Creates a clean shell environment for development or compiling.\n"
+ " vcpkg version Display version information\n"
+ " vcpkg contact Display contact information to send feedback\n"
+ "\n"
+ "Options:\n"
+ " --triplet <t> Specify the target architecture triplet.\n"
+ " (default: %%VCPKG_DEFAULT_TRIPLET%%, see 'vcpkg help triplet')\n"
+ "\n"
+ " --vcpkg-root <path> Specify the vcpkg root directory\n"
+ " (default: %%VCPKG_ROOT%%)\n"
+ "\n"
+ "For more help (including examples) see the accompanying README.md.",
+ Commands::Integrate::INTEGRATE_COMMAND_HELPSTRING);
+ }
+
+ std::string create_example_string(const std::string& command_and_arguments)
+ {
+ std::string cs = Strings::format("Example:\n"
+ " vcpkg %s\n",
+ command_and_arguments);
+ return cs;
+ }
+
+ void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
+ {
+ args.parse_arguments(COMMAND_STRUCTURE);
+
+ if (args.command_arguments.empty())
+ {
+ print_usage();
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+ const auto& topic = args.command_arguments[0];
+ if (topic == "triplet" || topic == "triplets" || topic == "triple")
+ {
+ help_topic_valid_triplet(paths);
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+
+ auto it_topic = Util::find_if(topics, [&](const Topic& t) { return t.name == topic; });
+ if (it_topic != topics.end())
+ {
+ it_topic->print(paths);
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+
+ System::println(System::Color::error, "Error: unknown topic %s", topic);
+ help_topics(paths);
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+}
diff --git a/toolsrc/src/vcpkg/input.cpp b/toolsrc/src/vcpkg/input.cpp
new file mode 100644
index 000000000..aee0fac7f
--- /dev/null
+++ b/toolsrc/src/vcpkg/input.cpp
@@ -0,0 +1,55 @@
+#include "pch.h"
+
+#include <vcpkg/base/system.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/help.h>
+#include <vcpkg/input.h>
+#include <vcpkg/metrics.h>
+
+namespace vcpkg::Input
+{
+ PackageSpec check_and_get_package_spec(const std::string& package_spec_as_string,
+ const Triplet& default_triplet,
+ CStringView example_text)
+ {
+ const std::string as_lowercase = Strings::ascii_to_lowercase(package_spec_as_string);
+ auto expected_spec = FullPackageSpec::from_string(as_lowercase, default_triplet);
+ if (const auto spec = expected_spec.get())
+ {
+ return PackageSpec{spec->package_spec};
+ }
+
+ // Intentionally show the lowercased string
+ System::println(System::Color::error, "Error: %s: %s", vcpkg::to_string(expected_spec.error()), as_lowercase);
+ System::print(example_text);
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+
+ void check_triplet(const Triplet& t, const VcpkgPaths& paths)
+ {
+ if (!paths.is_valid_triplet(t))
+ {
+ System::println(System::Color::error, "Error: invalid triplet: %s", t);
+ Metrics::g_metrics.lock()->track_property("error", "invalid triplet: " + t.to_string());
+ Help::help_topic_valid_triplet(paths);
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+ }
+
+ FullPackageSpec check_and_get_full_package_spec(const std::string& full_package_spec_as_string,
+ const Triplet& default_triplet,
+ CStringView example_text)
+ {
+ const std::string as_lowercase = Strings::ascii_to_lowercase(full_package_spec_as_string);
+ auto expected_spec = FullPackageSpec::from_string(as_lowercase, default_triplet);
+ if (const auto spec = expected_spec.get())
+ {
+ return *spec;
+ }
+
+ // Intentionally show the lowercased string
+ System::println(System::Color::error, "Error: %s: %s", vcpkg::to_string(expected_spec.error()), as_lowercase);
+ System::print(example_text);
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+}
diff --git a/toolsrc/src/vcpkg/install.cpp b/toolsrc/src/vcpkg/install.cpp
new file mode 100644
index 000000000..40e696fa0
--- /dev/null
+++ b/toolsrc/src/vcpkg/install.cpp
@@ -0,0 +1,741 @@
+#include "pch.h"
+
+#include <vcpkg/base/files.h>
+#include <vcpkg/base/system.h>
+#include <vcpkg/base/util.h>
+#include <vcpkg/build.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/dependencies.h>
+#include <vcpkg/globalstate.h>
+#include <vcpkg/help.h>
+#include <vcpkg/input.h>
+#include <vcpkg/install.h>
+#include <vcpkg/metrics.h>
+#include <vcpkg/paragraphs.h>
+#include <vcpkg/remove.h>
+#include <vcpkg/vcpkglib.h>
+
+namespace vcpkg::Install
+{
+ using namespace Dependencies;
+
+ InstallDir InstallDir::from_destination_root(const fs::path& destination_root,
+ const std::string& destination_subdirectory,
+ const fs::path& listfile)
+ {
+ InstallDir dirs;
+ dirs.m_destination = destination_root / destination_subdirectory;
+ dirs.m_destination_subdirectory = destination_subdirectory;
+ dirs.m_listfile = listfile;
+ return dirs;
+ }
+
+ const fs::path& InstallDir::destination() const { return this->m_destination; }
+
+ const std::string& InstallDir::destination_subdirectory() const { return this->m_destination_subdirectory; }
+
+ const fs::path& InstallDir::listfile() const { return this->m_listfile; }
+
+ void install_files_and_write_listfile(Files::Filesystem& fs,
+ const fs::path& source_dir,
+ const InstallDir& destination_dir)
+ {
+ std::vector<std::string> output;
+ std::error_code ec;
+
+ const size_t prefix_length = source_dir.native().size();
+ const fs::path& destination = destination_dir.destination();
+ const std::string& destination_subdirectory = destination_dir.destination_subdirectory();
+ const fs::path& listfile = destination_dir.listfile();
+
+ Checks::check_exit(
+ VCPKG_LINE_INFO, fs.exists(source_dir), "Source directory %s does not exist", source_dir.generic_string());
+ fs.create_directories(destination, ec);
+ Checks::check_exit(
+ VCPKG_LINE_INFO, !ec, "Could not create destination directory %s", destination.generic_string());
+ const fs::path listfile_parent = listfile.parent_path();
+ fs.create_directories(listfile_parent, ec);
+ Checks::check_exit(
+ VCPKG_LINE_INFO, !ec, "Could not create directory for listfile %s", listfile.generic_string());
+
+ output.push_back(Strings::format(R"(%s/)", destination_subdirectory));
+ auto files = fs.get_files_recursive(source_dir);
+ for (auto&& file : files)
+ {
+ const auto status = fs.status(file, ec);
+ if (ec)
+ {
+ System::println(System::Color::error, "failed: %s: %s", file.u8string(), ec.message());
+ continue;
+ }
+
+ const std::string filename = file.filename().u8string();
+ if (fs::is_regular_file(status) && (Strings::case_insensitive_ascii_equals(filename, "CONTROL") ||
+ Strings::case_insensitive_ascii_equals(filename, "BUILD_INFO")))
+ {
+ // Do not copy the control file
+ continue;
+ }
+
+ const std::string suffix = file.generic_u8string().substr(prefix_length + 1);
+ const fs::path target = destination / suffix;
+
+ switch (status.type())
+ {
+ case fs::file_type::directory:
+ {
+ fs.create_directory(target, ec);
+ if (ec)
+ {
+ System::println(System::Color::error, "failed: %s: %s", target.u8string(), ec.message());
+ }
+
+ // Trailing backslash for directories
+ output.push_back(Strings::format(R"(%s/%s/)", destination_subdirectory, suffix));
+ break;
+ }
+ case fs::file_type::regular:
+ {
+ if (fs.exists(target))
+ {
+ System::println(System::Color::warning,
+ "File %s was already present and will be overwritten",
+ target.u8string(),
+ ec.message());
+ }
+ fs.copy_file(file, target, fs::copy_options::overwrite_existing, ec);
+ if (ec)
+ {
+ System::println(System::Color::error, "failed: %s: %s", target.u8string(), ec.message());
+ }
+ output.push_back(Strings::format(R"(%s/%s)", destination_subdirectory, suffix));
+ break;
+ }
+ default:
+ System::println(System::Color::error, "failed: %s: cannot handle file type", file.u8string());
+ break;
+ }
+ }
+
+ std::sort(output.begin(), output.end());
+
+ fs.write_lines(listfile, output);
+ }
+
+ static void remove_first_n_chars(std::vector<std::string>* strings, const size_t n)
+ {
+ for (std::string& s : *strings)
+ {
+ s.erase(0, n);
+ }
+ };
+
+ static std::vector<std::string> extract_files_in_triplet(
+ const std::vector<StatusParagraphAndAssociatedFiles>& pgh_and_files, const Triplet& triplet)
+ {
+ std::vector<std::string> output;
+ for (const StatusParagraphAndAssociatedFiles& t : pgh_and_files)
+ {
+ if (t.pgh.package.spec.triplet() != triplet)
+ {
+ continue;
+ }
+
+ Util::Vectors::concatenate(&output, t.files);
+ }
+
+ std::sort(output.begin(), output.end());
+ return output;
+ }
+
+ static SortedVector<std::string> build_list_of_package_files(const Files::Filesystem& fs,
+ const fs::path& package_dir)
+ {
+ 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) {
+ std::string as_string = path.generic_string();
+ as_string.erase(0, package_remove_char_count);
+ return std::move(as_string);
+ });
+
+ return SortedVector<std::string>(std::move(package_files));
+ }
+
+ static SortedVector<std::string> build_list_of_installed_files(
+ const std::vector<StatusParagraphAndAssociatedFiles>& pgh_and_files, const Triplet& triplet)
+ {
+ std::vector<std::string> installed_files = extract_files_in_triplet(pgh_and_files, triplet);
+ const size_t installed_remove_char_count = triplet.canonical_name().size() + 1; // +1 for the slash
+ remove_first_n_chars(&installed_files, installed_remove_char_count);
+
+ return SortedVector<std::string>(std::move(installed_files));
+ }
+
+ InstallResult install_package(const VcpkgPaths& paths, const BinaryControlFile& bcf, StatusParagraphs* status_db)
+ {
+ const fs::path package_dir = paths.package_dir(bcf.core_paragraph.spec);
+ const Triplet& triplet = bcf.core_paragraph.spec.triplet();
+ const std::vector<StatusParagraphAndAssociatedFiles> pgh_and_files = get_installed_files(paths, *status_db);
+
+ const SortedVector<std::string> package_files =
+ build_list_of_package_files(paths.get_filesystem(), package_dir);
+ const SortedVector<std::string> installed_files = build_list_of_installed_files(pgh_and_files, triplet);
+
+ std::vector<std::string> intersection;
+ std::set_intersection(package_files.begin(),
+ package_files.end(),
+ installed_files.begin(),
+ installed_files.end(),
+ std::back_inserter(intersection));
+
+ if (!intersection.empty())
+ {
+ const fs::path triplet_install_path = paths.installed / triplet.canonical_name();
+ System::println(System::Color::error,
+ "The following files are already installed in %s and are in conflict with %s",
+ triplet_install_path.generic_string(),
+ bcf.core_paragraph.spec);
+ System::print("\n ");
+ System::println(Strings::join("\n ", intersection));
+ System::println();
+ return InstallResult::FILE_CONFLICTS;
+ }
+
+ StatusParagraph source_paragraph;
+ source_paragraph.package = bcf.core_paragraph;
+ source_paragraph.want = Want::INSTALL;
+ source_paragraph.state = InstallState::HALF_INSTALLED;
+
+ write_update(paths, source_paragraph);
+ status_db->insert(std::make_unique<StatusParagraph>(source_paragraph));
+
+ std::vector<StatusParagraph> features_spghs;
+ for (auto&& feature : bcf.features)
+ {
+ features_spghs.emplace_back();
+
+ StatusParagraph& feature_paragraph = features_spghs.back();
+ feature_paragraph.package = feature;
+ feature_paragraph.want = Want::INSTALL;
+ feature_paragraph.state = InstallState::HALF_INSTALLED;
+
+ write_update(paths, feature_paragraph);
+ status_db->insert(std::make_unique<StatusParagraph>(feature_paragraph));
+ }
+
+ const InstallDir install_dir = InstallDir::from_destination_root(
+ paths.installed, triplet.to_string(), paths.listfile_path(bcf.core_paragraph));
+
+ install_files_and_write_listfile(paths.get_filesystem(), package_dir, install_dir);
+
+ source_paragraph.state = InstallState::INSTALLED;
+ write_update(paths, source_paragraph);
+ status_db->insert(std::make_unique<StatusParagraph>(source_paragraph));
+
+ for (auto&& feature_paragraph : features_spghs)
+ {
+ feature_paragraph.state = InstallState::INSTALLED;
+ write_update(paths, feature_paragraph);
+ status_db->insert(std::make_unique<StatusParagraph>(feature_paragraph));
+ }
+
+ return InstallResult::SUCCESS;
+ }
+
+ using Build::BuildResult;
+ using Build::ExtendedBuildResult;
+
+ ExtendedBuildResult perform_install_plan_action(const VcpkgPaths& paths,
+ const InstallPlanAction& action,
+ StatusParagraphs& status_db)
+ {
+ const InstallPlanType& plan_type = action.plan_type;
+ const std::string display_name = action.spec.to_string();
+ const std::string display_name_with_features =
+ GlobalState::feature_packages ? action.displayname() : display_name;
+
+ const bool is_user_requested = action.request_type == RequestType::USER_REQUESTED;
+ const bool use_head_version = Util::Enum::to_bool(action.build_options.use_head_version);
+
+ if (plan_type == InstallPlanType::ALREADY_INSTALLED)
+ {
+ if (use_head_version && is_user_requested)
+ System::println(
+ System::Color::warning, "Package %s is already installed -- not building from HEAD", display_name);
+ else
+ System::println(System::Color::success, "Package %s is already installed", display_name);
+ return BuildResult::SUCCEEDED;
+ }
+
+ auto aux_install = [&](const std::string& name, const BinaryControlFile& bcf) -> BuildResult {
+ System::println("Installing package %s... ", name);
+ const auto install_result = install_package(paths, bcf, &status_db);
+ switch (install_result)
+ {
+ case InstallResult::SUCCESS:
+ System::println(System::Color::success, "Installing package %s... done", name);
+ return BuildResult::SUCCEEDED;
+ case InstallResult::FILE_CONFLICTS: return BuildResult::FILE_CONFLICTS;
+ default: Checks::unreachable(VCPKG_LINE_INFO);
+ }
+ };
+
+ if (plan_type == InstallPlanType::BUILD_AND_INSTALL)
+ {
+ if (use_head_version)
+ System::println("Building package %s from HEAD... ", display_name_with_features);
+ else
+ System::println("Building package %s... ", display_name_with_features);
+
+ auto result = [&]() -> Build::ExtendedBuildResult {
+ const Build::BuildPackageConfig build_config{action.source_control_file.value_or_exit(VCPKG_LINE_INFO),
+ action.spec.triplet(),
+ paths.port_dir(action.spec),
+ action.build_options,
+ action.feature_list};
+ return Build::build_package(paths, build_config, status_db);
+ }();
+
+ if (result.code != Build::BuildResult::SUCCEEDED)
+ {
+ System::println(System::Color::error, Build::create_error_message(result.code, action.spec));
+ return result;
+ }
+
+ System::println("Building package %s... done", display_name_with_features);
+
+ auto bcf = std::make_unique<BinaryControlFile>(
+ Paragraphs::try_load_cached_package(paths, action.spec).value_or_exit(VCPKG_LINE_INFO));
+ auto code = aux_install(display_name_with_features, *bcf);
+
+ if (action.build_options.clean_packages == Build::CleanPackages::YES)
+ {
+ 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);
+ }
+
+ return {code, std::move(bcf)};
+ }
+
+ if (plan_type == InstallPlanType::EXCLUDED)
+ {
+ System::println(System::Color::warning, "Package %s is excluded", display_name);
+ return BuildResult::EXCLUDED;
+ }
+
+ Checks::unreachable(VCPKG_LINE_INFO);
+ }
+
+ void InstallSummary::print() const
+ {
+ System::println("RESULTS");
+
+ for (const SpecSummary& result : this->results)
+ {
+ System::println(" %s: %s: %s", result.spec, Build::to_string(result.build_result.code), result.timing);
+ }
+
+ std::map<BuildResult, int> summary;
+ for (const BuildResult& v : Build::BUILD_RESULT_VALUES)
+ {
+ summary[v] = 0;
+ }
+
+ for (const SpecSummary& r : this->results)
+ {
+ summary[r.build_result.code]++;
+ }
+
+ System::println("\nSUMMARY");
+ for (const std::pair<const BuildResult, int>& entry : summary)
+ {
+ System::println(" %s: %d", Build::to_string(entry.first), entry.second);
+ }
+ }
+
+ InstallSummary perform(const std::vector<AnyAction>& action_plan,
+ const KeepGoing keep_going,
+ const VcpkgPaths& paths,
+ StatusParagraphs& status_db)
+ {
+ std::vector<SpecSummary> results;
+
+ const auto timer = Chrono::ElapsedTimer::create_started();
+ size_t counter = 0;
+ const size_t package_count = action_plan.size();
+
+ for (const auto& action : action_plan)
+ {
+ const auto build_timer = Chrono::ElapsedTimer::create_started();
+ counter++;
+
+ const PackageSpec& spec = action.spec();
+ const std::string display_name = spec.to_string();
+ System::println("Starting package %zd/%zd: %s", counter, package_count, display_name);
+
+ results.emplace_back(spec, &action);
+
+ if (const auto install_action = action.install_action.get())
+ {
+ auto result = perform_install_plan_action(paths, *install_action, status_db);
+
+ if (result.code != BuildResult::SUCCEEDED && keep_going == KeepGoing::NO)
+ {
+ System::println(Build::create_user_troubleshooting_message(install_action->spec));
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+
+ results.back().build_result = std::move(result);
+ }
+ else if (const auto remove_action = action.remove_action.get())
+ {
+ Remove::perform_remove_plan_action(paths, *remove_action, Remove::Purge::YES, &status_db);
+ }
+ else
+ {
+ Checks::unreachable(VCPKG_LINE_INFO);
+ }
+
+ results.back().timing = build_timer.elapsed();
+ System::println("Elapsed time for package %s: %s", display_name, results.back().timing.to_string());
+ }
+
+ return InstallSummary{std::move(results), timer.to_string()};
+ }
+
+ static constexpr StringLiteral OPTION_DRY_RUN = "--dry-run";
+ static constexpr StringLiteral OPTION_USE_HEAD_VERSION = "--head";
+ static constexpr StringLiteral OPTION_NO_DOWNLOADS = "--no-downloads";
+ static constexpr StringLiteral OPTION_RECURSE = "--recurse";
+ static constexpr StringLiteral OPTION_KEEP_GOING = "--keep-going";
+ static constexpr StringLiteral OPTION_XUNIT = "--x-xunit";
+ static constexpr StringLiteral OPTION_USE_ARIA2 = "--x-use-aria2";
+
+ static constexpr std::array<CommandSwitch, 6> INSTALL_SWITCHES = {{
+ {OPTION_DRY_RUN, "Do not actually build or install"},
+ {OPTION_USE_HEAD_VERSION, "Install the libraries on the command line using the latest upstream sources"},
+ {OPTION_NO_DOWNLOADS, "Do not download new sources"},
+ {OPTION_RECURSE, "Allow removal of packages as part of installation"},
+ {OPTION_KEEP_GOING, "Continue installing packages on failure"},
+ {OPTION_USE_ARIA2, "Use aria2 to perform download tasks"},
+ }};
+ static constexpr std::array<CommandSetting, 1> INSTALL_SETTINGS = {{
+ {OPTION_XUNIT, "File to output results in XUnit format (Internal use)"},
+ }};
+
+ std::vector<std::string> get_all_port_names(const VcpkgPaths& paths)
+ {
+ auto sources_and_errors = Paragraphs::try_load_all_ports(paths.get_filesystem(), paths.ports);
+
+ return Util::fmap(sources_and_errors.paragraphs,
+ [](auto&& pgh) -> std::string { return pgh->core_paragraph->name; });
+ }
+
+ const CommandStructure COMMAND_STRUCTURE = {
+ Help::create_example_string("install zlib zlib:x64-windows curl boost"),
+ 1,
+ SIZE_MAX,
+ {INSTALL_SWITCHES, INSTALL_SETTINGS},
+ &get_all_port_names,
+ };
+
+ static void print_cmake_information(const BinaryParagraph& bpgh, const VcpkgPaths& paths)
+ {
+ static const std::regex cmake_library_regex(R"(\badd_library\(([^\s\)]+)\s)", std::regex_constants::ECMAScript);
+
+ auto& fs = paths.get_filesystem();
+
+ auto usage_file = paths.installed / bpgh.spec.triplet().canonical_name() / "share" / bpgh.spec.name() / "usage";
+ if (fs.exists(usage_file))
+ {
+ auto maybe_contents = fs.read_contents(usage_file);
+ if (auto p_contents = maybe_contents.get())
+ {
+ System::println(*p_contents);
+ }
+ return;
+ }
+
+ auto files = fs.read_lines(paths.listfile_path(bpgh));
+ if (auto p_lines = files.get())
+ {
+ std::map<std::string, std::string> config_files;
+ std::map<std::string, std::vector<std::string>> library_targets;
+
+ for (auto&& suffix : *p_lines)
+ {
+ if (Strings::case_insensitive_ascii_contains(suffix, "/share/") && Strings::ends_with(suffix, ".cmake"))
+ {
+ // CMake file is inside the share folder
+ auto path = paths.installed / suffix;
+ auto maybe_contents = fs.read_contents(path);
+ auto find_package_name = path.parent_path().filename().u8string();
+ if (auto p_contents = maybe_contents.get())
+ {
+ std::sregex_iterator next(p_contents->begin(), p_contents->end(), cmake_library_regex);
+ std::sregex_iterator last;
+
+ while (next != last)
+ {
+ auto match = *next;
+ library_targets[find_package_name].push_back(match[1]);
+ ++next;
+ }
+ }
+
+ auto filename = fs::u8path(suffix).filename().u8string();
+
+ if (Strings::ends_with(filename, "Config.cmake"))
+ {
+ auto root = filename.substr(0, filename.size() - 12);
+ if (Strings::case_insensitive_ascii_equals(root, find_package_name))
+ config_files[find_package_name] = root;
+ }
+ else if (Strings::ends_with(filename, "-config.cmake"))
+ {
+ auto root = filename.substr(0, filename.size() - 13);
+ if (Strings::case_insensitive_ascii_equals(root, find_package_name))
+ config_files[find_package_name] = root;
+ }
+ }
+ }
+
+ if (library_targets.empty())
+ {
+ }
+ else
+ {
+ System::println("The package %s provides CMake targets:\n", bpgh.spec);
+
+ for (auto&& library_target_pair : library_targets)
+ {
+ auto config_it = config_files.find(library_target_pair.first);
+ if (config_it != config_files.end())
+ System::println(" find_package(%s CONFIG REQUIRED)", config_it->second);
+ else
+ System::println(" find_package(%s CONFIG REQUIRED)", library_target_pair.first);
+
+ std::sort(library_target_pair.second.begin(),
+ library_target_pair.second.end(),
+ [](const std::string& l, const std::string& r) {
+ if (l.size() < r.size()) return true;
+ if (l.size() > r.size()) return false;
+ return l < r;
+ });
+
+ if (library_target_pair.second.size() <= 4)
+ {
+ System::println(" target_link_libraries(main PRIVATE %s)\n",
+ Strings::join(" ", library_target_pair.second));
+ }
+ else
+ {
+ auto omitted = library_target_pair.second.size() - 4;
+ library_target_pair.second.erase(library_target_pair.second.begin() + 4,
+ library_target_pair.second.end());
+ System::println(" # Note: %zd target(s) were omitted.\n"
+ " target_link_libraries(main PRIVATE %s)\n",
+ omitted,
+ Strings::join(" ", library_target_pair.second));
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// <summary>
+ /// Run "install" command.
+ /// </summary>
+ ///
+ void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet)
+ {
+ // input sanitization
+ const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
+
+ const std::vector<FullPackageSpec> specs = Util::fmap(args.command_arguments, [&](auto&& arg) {
+ return Input::check_and_get_full_package_spec(arg, default_triplet, COMMAND_STRUCTURE.example_text);
+ });
+
+ for (auto&& spec : specs)
+ {
+ Input::check_triplet(spec.package_spec.triplet(), paths);
+ if (!spec.features.empty() && !GlobalState::feature_packages)
+ {
+ Checks::exit_with_message(
+ VCPKG_LINE_INFO, "Feature packages are experimentally available under the --featurepackages flag.");
+ }
+ }
+
+ const bool dry_run = Util::Sets::contains(options.switches, OPTION_DRY_RUN);
+ const bool use_head_version = Util::Sets::contains(options.switches, (OPTION_USE_HEAD_VERSION));
+ const bool no_downloads = Util::Sets::contains(options.switches, (OPTION_NO_DOWNLOADS));
+ const bool is_recursive = Util::Sets::contains(options.switches, (OPTION_RECURSE));
+ const bool use_aria2 = Util::Sets::contains(options.switches, (OPTION_USE_ARIA2));
+ const KeepGoing keep_going = to_keep_going(Util::Sets::contains(options.switches, OPTION_KEEP_GOING));
+
+ // create the plan
+ StatusParagraphs status_db = database_load_check(paths);
+
+ Build::DownloadTool download_tool = Build::DownloadTool::BUILT_IN;
+ if (use_aria2) download_tool = Build::DownloadTool::ARIA2;
+
+ const Build::BuildPackageOptions install_plan_options = {
+ Util::Enum::to_enum<Build::UseHeadVersion>(use_head_version),
+ Util::Enum::to_enum<Build::AllowDownloads>(!no_downloads),
+ Build::CleanBuildtrees::NO,
+ Build::CleanPackages::NO,
+ download_tool};
+
+ auto all_ports = Paragraphs::load_all_ports(paths.get_filesystem(), paths.ports);
+ std::unordered_map<std::string, SourceControlFile> scf_map;
+ for (auto&& port : all_ports)
+ scf_map[port->core_paragraph->name] = std::move(*port);
+ MapPortFileProvider provider(scf_map);
+
+ // Note: action_plan will hold raw pointers to SourceControlFiles from this map
+ std::vector<AnyAction> action_plan =
+ create_feature_install_plan(provider, FullPackageSpec::to_feature_specs(specs), status_db);
+
+ if (!GlobalState::feature_packages)
+ {
+ for (auto&& action : action_plan)
+ {
+ if (action.remove_action.has_value())
+ {
+ Checks::exit_with_message(
+ VCPKG_LINE_INFO,
+ "The installation plan requires feature packages support. Please re-run the "
+ "command with --featurepackages.");
+ }
+ }
+ }
+
+ for (auto&& action : action_plan)
+ {
+ if (auto p_install = action.install_action.get())
+ {
+ p_install->build_options = install_plan_options;
+ if (p_install->request_type != RequestType::USER_REQUESTED)
+ p_install->build_options.use_head_version = Build::UseHeadVersion::NO;
+ }
+ }
+
+ // install plan will be empty if it is already installed - need to change this at status paragraph part
+ Checks::check_exit(VCPKG_LINE_INFO, !action_plan.empty(), "Install plan cannot be empty");
+
+ // log the plan
+ const std::string specs_string = Strings::join(",", action_plan, [](const AnyAction& action) {
+ if (auto iaction = action.install_action.get())
+ return iaction->spec.to_string();
+ else if (auto raction = action.remove_action.get())
+ return "R$" + raction->spec.to_string();
+ Checks::unreachable(VCPKG_LINE_INFO);
+ });
+
+ Metrics::g_metrics.lock()->track_property("installplan", specs_string);
+
+ Dependencies::print_plan(action_plan, is_recursive);
+
+ if (dry_run)
+ {
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+
+ const InstallSummary summary = perform(action_plan, keep_going, paths, status_db);
+
+ System::println("\nTotal elapsed time: %s\n", summary.total_elapsed_time);
+
+ if (keep_going == KeepGoing::YES)
+ {
+ summary.print();
+ }
+
+ auto it_xunit = options.settings.find(OPTION_XUNIT);
+ if (it_xunit != options.settings.end())
+ {
+ std::string xunit_doc = "<assemblies><assembly><collection>\n";
+
+ xunit_doc += summary.xunit_results();
+
+ xunit_doc += "</collection></assembly></assemblies>\n";
+ paths.get_filesystem().write_contents(fs::u8path(it_xunit->second), xunit_doc);
+ }
+
+ for (auto&& result : summary.results)
+ {
+ if (!result.action) continue;
+ if (auto p_install_action = result.action->install_action.get())
+ {
+ if (p_install_action->request_type != RequestType::USER_REQUESTED) continue;
+ auto bpgh = result.get_binary_paragraph();
+ if (!bpgh) continue;
+ print_cmake_information(*bpgh, paths);
+ }
+ }
+
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+
+ SpecSummary::SpecSummary(const PackageSpec& spec, const Dependencies::AnyAction* action)
+ : spec(spec), build_result{BuildResult::NULLVALUE, nullptr}, action(action)
+ {
+ }
+
+ const BinaryParagraph* SpecSummary::get_binary_paragraph() const
+ {
+ if (build_result.binary_control_file) return &build_result.binary_control_file->core_paragraph;
+ if (action)
+ if (auto p_install_plan = action->install_action.get())
+ {
+ if (auto p_status = p_install_plan->installed_package.get())
+ {
+ return &p_status->core->package;
+ }
+ }
+ return nullptr;
+ }
+
+ std::string InstallSummary::xunit_result(const PackageSpec& spec, Chrono::ElapsedTime time, BuildResult code)
+ {
+ std::string inner_block;
+ const char* result_string = "";
+ switch (code)
+ {
+ case BuildResult::POST_BUILD_CHECKS_FAILED:
+ case BuildResult::FILE_CONFLICTS:
+ case BuildResult::BUILD_FAILED:
+ result_string = "Fail";
+ inner_block = Strings::format("<failure><message><![CDATA[%s]]></message></failure>", to_string(code));
+ break;
+ case BuildResult::EXCLUDED:
+ case BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES:
+ result_string = "Skip";
+ inner_block = Strings::format("<reason><![CDATA[%s]]></reason>", to_string(code));
+ break;
+ case BuildResult::SUCCEEDED: result_string = "Pass"; break;
+ default: Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+
+ return Strings::format(R"(<test name="%s" method="%s" time="%lld" result="%s">%s</test>)"
+ "\n",
+ spec,
+ spec,
+ time.as<std::chrono::seconds>().count(),
+ result_string,
+ inner_block);
+ }
+
+ std::string InstallSummary::xunit_results() const
+ {
+ std::string xunit_doc;
+ for (auto&& result : results)
+ {
+ xunit_doc += xunit_result(result.spec, result.timing, result.build_result.code);
+ }
+ return xunit_doc;
+ }
+}
diff --git a/toolsrc/src/vcpkg/metrics.cpp b/toolsrc/src/vcpkg/metrics.cpp
new file mode 100644
index 000000000..8890c067f
--- /dev/null
+++ b/toolsrc/src/vcpkg/metrics.cpp
@@ -0,0 +1,449 @@
+#include "pch.h"
+
+#include <vcpkg/commands.h>
+#include <vcpkg/metrics.h>
+
+#include <vcpkg/base/chrono.h>
+#include <vcpkg/base/files.h>
+#include <vcpkg/base/strings.h>
+#include <vcpkg/base/system.h>
+
+#pragma comment(lib, "version")
+#pragma comment(lib, "winhttp")
+
+namespace vcpkg::Metrics
+{
+ Util::LockGuarded<Metrics> g_metrics;
+
+ static std::string get_current_date_time()
+ {
+ auto maybe_time = Chrono::CTime::get_current_date_time();
+ if (auto ptime = maybe_time.get())
+ {
+ return ptime->to_string();
+ }
+
+ return "";
+ }
+
+ static std::string generate_random_UUID()
+ {
+ int part_sizes[] = {8, 4, 4, 4, 12};
+ char uuid[37];
+ memset(uuid, 0, sizeof(uuid));
+ int num;
+ srand(static_cast<int>(time(nullptr)));
+ int index = 0;
+ for (int part = 0; part < 5; part++)
+ {
+ if (part > 0)
+ {
+ uuid[index] = '-';
+ index++;
+ }
+
+ // Generating UUID format version 4
+ // http://en.wikipedia.org/wiki/Universally_unique_identifier
+ for (int i = 0; i < part_sizes[part]; i++, index++)
+ {
+ if (part == 2 && i == 0)
+ {
+ num = 4;
+ }
+ else if (part == 4 && i == 0)
+ {
+ num = (rand() % 4) + 8;
+ }
+ else
+ {
+ num = rand() % 16;
+ }
+
+ if (num < 10)
+ {
+ uuid[index] = static_cast<char>('0' + num);
+ }
+ else
+ {
+ uuid[index] = static_cast<char>('a' + (num - 10));
+ }
+ }
+ }
+
+ return uuid;
+ }
+
+ static const std::string& get_session_id()
+ {
+ static const std::string ID = generate_random_UUID();
+ return ID;
+ }
+
+ static std::string to_json_string(const std::string& str)
+ {
+ std::string encoded = "\"";
+ for (auto&& ch : str)
+ {
+ if (ch == '\\')
+ {
+ encoded.append("\\\\");
+ }
+ else if (ch == '"')
+ {
+ encoded.append("\\\"");
+ }
+ else if (ch < 0x20 || static_cast<unsigned char>(ch) >= 0x80)
+ {
+ // Note: this treats incoming Strings as Latin-1
+ static constexpr const char HEX[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+ encoded.append("\\u00");
+ encoded.push_back(HEX[ch / 16]);
+ encoded.push_back(HEX[ch % 16]);
+ }
+ else
+ {
+ encoded.push_back(ch);
+ }
+ }
+ encoded.push_back('"');
+ return encoded;
+ }
+
+ static std::string get_os_version_string()
+ {
+#if defined(_WIN32)
+ std::wstring path;
+ path.resize(MAX_PATH);
+ const auto n = GetSystemDirectoryW(&path[0], static_cast<UINT>(path.size()));
+ path.resize(n);
+ path += L"\\kernel32.dll";
+
+ const auto versz = GetFileVersionInfoSizeW(path.c_str(), nullptr);
+ if (versz == 0) return "";
+
+ std::vector<char> verbuf;
+ verbuf.resize(versz);
+
+ if (!GetFileVersionInfoW(path.c_str(), 0, static_cast<DWORD>(verbuf.size()), &verbuf[0])) return "";
+
+ void* rootblock;
+ UINT rootblocksize;
+ if (!VerQueryValueW(&verbuf[0], L"\\", &rootblock, &rootblocksize)) return "";
+
+ auto rootblock_ffi = static_cast<VS_FIXEDFILEINFO*>(rootblock);
+
+ return Strings::format("%d.%d.%d",
+ static_cast<int>(HIWORD(rootblock_ffi->dwProductVersionMS)),
+ static_cast<int>(LOWORD(rootblock_ffi->dwProductVersionMS)),
+ static_cast<int>(HIWORD(rootblock_ffi->dwProductVersionLS)));
+#else
+ return "unknown";
+#endif
+ }
+
+ struct MetricMessage
+ {
+ std::string user_id = generate_random_UUID();
+ std::string user_timestamp;
+ std::string timestamp = get_current_date_time();
+ std::string properties;
+ std::string measurements;
+
+ std::vector<std::string> buildtime_names;
+ std::vector<std::string> buildtime_times;
+
+ void track_property(const std::string& name, const std::string& value)
+ {
+ if (properties.size() != 0) properties.push_back(',');
+ properties.append(to_json_string(name));
+ properties.push_back(':');
+ properties.append(to_json_string(value));
+ }
+
+ void track_metric(const std::string& name, double value)
+ {
+ if (measurements.size() != 0) measurements.push_back(',');
+ measurements.append(to_json_string(name));
+ measurements.push_back(':');
+ measurements.append(std::to_string(value));
+ }
+
+ void track_buildtime(const std::string& name, double value)
+ {
+ buildtime_names.push_back(name);
+ buildtime_times.push_back(std::to_string(value));
+ }
+
+ std::string format_event_data_template() const
+ {
+ auto props_plus_buildtimes = properties;
+ if (buildtime_names.size() > 0)
+ {
+ if (props_plus_buildtimes.size() > 0) props_plus_buildtimes.push_back(',');
+ props_plus_buildtimes.append(Strings::format(R"("buildnames": [%s], "buildtimes": [%s])",
+ Strings::join(",", buildtime_names, to_json_string),
+ Strings::join(",", buildtime_times)));
+ }
+
+ const std::string& session_id = get_session_id();
+ return Strings::format(R"([{
+ "ver": 1,
+ "name": "Microsoft.ApplicationInsights.Event",
+ "time": "%s",
+ "sampleRate": 100.000000,
+ "seq": "0:0",
+ "iKey": "b4e88960-4393-4dd9-ab8e-97e8fe6d7603",
+ "flags": 0.000000,
+ "tags": {
+ "ai.device.os": "Other",
+ "ai.device.osVersion": "%s-%s",
+ "ai.session.id": "%s",
+ "ai.user.id": "%s",
+ "ai.user.accountAcquisitionDate": "%s"
+ },
+ "data": {
+ "baseType": "EventData",
+ "baseData": {
+ "ver": 2,
+ "name": "commandline_test7",
+ "properties": { %s },
+ "measurements": { %s }
+ }
+ }
+}])",
+ timestamp,
+#if defined(_WIN32)
+ "Windows",
+#elif defined(__APPLE__)
+ "OSX",
+#elif defined(__linux__)
+ "Linux",
+#elif defined(__unix__)
+ "Unix",
+#else
+ "Other",
+#endif
+ get_os_version_string(),
+ session_id,
+ user_id,
+ user_timestamp,
+ props_plus_buildtimes,
+ measurements);
+ }
+ };
+
+ static MetricMessage g_metricmessage;
+ static bool g_should_send_metrics =
+#if defined(NDEBUG) && (DISABLE_METRICS == 0)
+ true
+#else
+ false
+#endif
+ ;
+ static bool g_should_print_metrics = false;
+
+ bool get_compiled_metrics_enabled() { return DISABLE_METRICS == 0; }
+
+ std::string get_MAC_user()
+ {
+#if defined(_WIN32)
+ auto getmac = System::cmd_execute_and_capture_output("getmac");
+
+ if (getmac.exit_code != 0) return "0";
+
+ std::regex mac_regex("([a-fA-F0-9]{2}(-[a-fA-F0-9]{2}){5})");
+ std::sregex_iterator next(getmac.output.begin(), getmac.output.end(), mac_regex);
+ std::sregex_iterator last;
+
+ while (next != last)
+ {
+ const auto match = *next;
+ if (match[0] != "00-00-00-00-00-00")
+ {
+ return vcpkg::Commands::Hash::get_string_hash(match[0], "SHA256");
+ }
+ ++next;
+ }
+
+ return "0";
+#else
+ return "{}";
+#endif
+ }
+
+ void Metrics::set_user_information(const std::string& user_id, const std::string& first_use_time)
+ {
+ g_metricmessage.user_id = user_id;
+ g_metricmessage.user_timestamp = first_use_time;
+ }
+
+ void Metrics::init_user_information(std::string& user_id, std::string& first_use_time)
+ {
+ user_id = generate_random_UUID();
+ first_use_time = get_current_date_time();
+ }
+
+ void Metrics::set_send_metrics(bool should_send_metrics) { g_should_send_metrics = should_send_metrics; }
+
+ void Metrics::set_print_metrics(bool should_print_metrics) { g_should_print_metrics = should_print_metrics; }
+
+ void Metrics::track_metric(const std::string& name, double value) { g_metricmessage.track_metric(name, value); }
+
+ void Metrics::track_buildtime(const std::string& name, double value)
+ {
+ g_metricmessage.track_buildtime(name, value);
+ }
+
+ void Metrics::track_property(const std::string& name, const std::string& value)
+ {
+ g_metricmessage.track_property(name, value);
+ }
+
+ void Metrics::upload(const std::string& payload)
+ {
+#if defined(_WIN32)
+ HINTERNET connect = nullptr, request = nullptr;
+ BOOL results = FALSE;
+
+ const HINTERNET session = WinHttpOpen(
+ L"vcpkg/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
+ if (session) connect = WinHttpConnect(session, L"dc.services.visualstudio.com", INTERNET_DEFAULT_HTTPS_PORT, 0);
+
+ if (connect)
+ request = WinHttpOpenRequest(connect,
+ L"POST",
+ L"/v2/track",
+ nullptr,
+ WINHTTP_NO_REFERER,
+ WINHTTP_DEFAULT_ACCEPT_TYPES,
+ WINHTTP_FLAG_SECURE);
+
+ if (request)
+ {
+ if (MAXDWORD <= payload.size()) abort();
+ std::wstring hdrs = L"Content-Type: application/json\r\n";
+ std::string& p = const_cast<std::string&>(payload);
+ results = WinHttpSendRequest(request,
+ hdrs.c_str(),
+ static_cast<DWORD>(hdrs.size()),
+ static_cast<void*>(&p[0]),
+ static_cast<DWORD>(payload.size()),
+ static_cast<DWORD>(payload.size()),
+ 0);
+ }
+
+ if (results)
+ {
+ results = WinHttpReceiveResponse(request, nullptr);
+ }
+
+ DWORD http_code = 0, junk = sizeof(DWORD);
+
+ if (results)
+ {
+ results = WinHttpQueryHeaders(request,
+ WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
+ nullptr,
+ &http_code,
+ &junk,
+ WINHTTP_NO_HEADER_INDEX);
+ }
+
+ std::vector<char> response_buffer;
+ if (results)
+ {
+ DWORD available_data = 0, read_data = 0, total_data = 0;
+ while ((results = WinHttpQueryDataAvailable(request, &available_data)) == TRUE && available_data > 0)
+ {
+ response_buffer.resize(response_buffer.size() + available_data);
+
+ results = WinHttpReadData(request, &response_buffer.data()[total_data], available_data, &read_data);
+
+ if (!results)
+ {
+ break;
+ }
+
+ total_data += read_data;
+
+ response_buffer.resize(total_data);
+ }
+ }
+
+ if (!results)
+ {
+#ifndef NDEBUG
+ __debugbreak();
+ auto err = GetLastError();
+ std::cerr << "[DEBUG] failed to connect to server: " << err << "\n";
+#endif
+ }
+
+ if (request) WinHttpCloseHandle(request);
+ if (connect) WinHttpCloseHandle(connect);
+ if (session) WinHttpCloseHandle(session);
+#endif
+ }
+
+ void Metrics::flush()
+ {
+ const std::string payload = g_metricmessage.format_event_data_template();
+ if (g_should_print_metrics) std::cerr << payload << "\n";
+ if (!g_should_send_metrics) return;
+
+#if defined(_WIN32)
+ wchar_t temp_folder[MAX_PATH];
+ GetTempPathW(MAX_PATH, temp_folder);
+
+ const fs::path temp_folder_path = fs::path(temp_folder) / "vcpkg";
+ const fs::path temp_folder_path_exe =
+ temp_folder_path / Strings::format("vcpkgmetricsuploader-%s.exe", Commands::Version::base_version());
+#endif
+
+ auto& fs = Files::get_real_filesystem();
+
+#if defined(_WIN32)
+
+ const fs::path exe_path = [&fs]() -> fs::path {
+ auto vcpkgdir = System::get_exe_path_of_current_process().parent_path();
+ auto path = vcpkgdir / "vcpkgmetricsuploader.exe";
+ if (fs.exists(path)) return path;
+
+ path = vcpkgdir / "scripts" / "vcpkgmetricsuploader.exe";
+ if (fs.exists(path)) return path;
+
+ return "";
+ }();
+
+ std::error_code ec;
+ fs.create_directories(temp_folder_path, ec);
+ if (ec) return;
+ fs.copy_file(exe_path, temp_folder_path_exe, fs::copy_options::skip_existing, ec);
+ if (ec) return;
+#else
+ if (!fs.exists("/tmp")) return;
+ const fs::path temp_folder_path = "/tmp/vcpkg";
+ std::error_code ec;
+ fs.create_directory(temp_folder_path, ec);
+ // ignore error
+ ec.clear();
+#endif
+ const fs::path vcpkg_metrics_txt_path = temp_folder_path / ("vcpkg" + generate_random_UUID() + ".txt");
+ fs.write_contents(vcpkg_metrics_txt_path, payload, ec);
+ if (ec) return;
+
+#if defined(_WIN32)
+ const std::string cmd_line = Strings::format("start \"vcpkgmetricsuploader.exe\" \"%s\" \"%s\"",
+ temp_folder_path_exe.u8string(),
+ vcpkg_metrics_txt_path.u8string());
+#else
+ auto escaped_path = Strings::escape_string(vcpkg_metrics_txt_path.u8string(), '\'', '\\');
+ const std::string cmd_line = Strings::format(
+ R"((curl "https://dc.services.visualstudio.com/v2/track" -H "Content-Type: application/json" -X POST --data '@%s' >/dev/null 2>&1; rm '%s') &)",
+ escaped_path,
+ escaped_path);
+#endif
+ System::cmd_execute_clean(cmd_line);
+ }
+}
diff --git a/toolsrc/src/vcpkg/packagespec.cpp b/toolsrc/src/vcpkg/packagespec.cpp
new file mode 100644
index 000000000..789aaca80
--- /dev/null
+++ b/toolsrc/src/vcpkg/packagespec.cpp
@@ -0,0 +1,195 @@
+#include "pch.h"
+
+#include <vcpkg/base/util.h>
+#include <vcpkg/packagespec.h>
+#include <vcpkg/packagespecparseresult.h>
+#include <vcpkg/parse.h>
+
+using vcpkg::Parse::parse_comma_list;
+
+namespace vcpkg
+{
+ static bool is_valid_package_spec_char(char c)
+ {
+ return (c == '-') || isdigit(c) || (isalpha(c) && islower(c)) || (c == '[') || (c == ']');
+ }
+
+ std::string FeatureSpec::to_string() const
+ {
+ if (feature().empty()) return spec().to_string();
+ return Strings::format("%s[%s]:%s", name(), feature(), triplet());
+ }
+
+ std::vector<FeatureSpec> FeatureSpec::from_strings_and_triplet(const std::vector<std::string>& depends,
+ const Triplet& triplet)
+ {
+ std::vector<FeatureSpec> f_specs;
+ for (auto&& depend : depends)
+ {
+ auto maybe_spec = ParsedSpecifier::from_string(depend);
+ if (auto spec = maybe_spec.get())
+ {
+ Checks::check_exit(VCPKG_LINE_INFO,
+ spec->triplet.empty(),
+ "error: triplets cannot currently be specified in this context: %s",
+ depend);
+ PackageSpec pspec =
+ PackageSpec::from_name_and_triplet(spec->name, triplet).value_or_exit(VCPKG_LINE_INFO);
+
+ for (auto&& feature : spec->features)
+ f_specs.push_back(FeatureSpec{pspec, feature});
+
+ if (spec->features.empty()) f_specs.push_back(FeatureSpec{pspec, ""});
+ }
+ else
+ {
+ Checks::exit_with_message(VCPKG_LINE_INFO,
+ "error while parsing feature list: %s: %s",
+ vcpkg::to_string(maybe_spec.error()),
+ depend);
+ }
+ }
+ return f_specs;
+ }
+
+ std::vector<FeatureSpec> FullPackageSpec::to_feature_specs(const std::vector<FullPackageSpec>& specs)
+ {
+ std::vector<FeatureSpec> ret;
+ for (auto&& spec : specs)
+ {
+ ret.emplace_back(spec.package_spec, "");
+ for (auto&& feature : spec.features)
+ ret.emplace_back(spec.package_spec, feature);
+ }
+ return ret;
+ }
+
+ ExpectedT<FullPackageSpec, PackageSpecParseResult> FullPackageSpec::from_string(const std::string& spec_as_string,
+ const Triplet& default_triplet)
+ {
+ auto res = ParsedSpecifier::from_string(spec_as_string);
+ if (auto p = res.get())
+ {
+ FullPackageSpec fspec;
+ Triplet t = p->triplet.empty() ? default_triplet : Triplet::from_canonical_name(p->triplet);
+ fspec.package_spec = PackageSpec::from_name_and_triplet(p->name, t).value_or_exit(VCPKG_LINE_INFO);
+ fspec.features = std::move(p->features);
+ return fspec;
+ }
+ return res.error();
+ }
+
+ ExpectedT<PackageSpec, PackageSpecParseResult> PackageSpec::from_name_and_triplet(const std::string& name,
+ const Triplet& triplet)
+ {
+ if (Util::find_if_not(name, is_valid_package_spec_char) != name.end())
+ {
+ return PackageSpecParseResult::INVALID_CHARACTERS;
+ }
+
+ PackageSpec p;
+ p.m_name = name;
+ p.m_triplet = triplet;
+ return p;
+ }
+
+ std::vector<PackageSpec> PackageSpec::to_package_specs(const std::vector<std::string>& ports,
+ const Triplet& triplet)
+ {
+ return Util::fmap(ports, [&](const std::string& spec_as_string) -> PackageSpec {
+ auto maybe_spec = PackageSpec::from_name_and_triplet(spec_as_string, triplet);
+ if (auto spec = maybe_spec.get())
+ {
+ return std::move(*spec);
+ }
+
+ const PackageSpecParseResult error_type = maybe_spec.error();
+ Checks::exit_with_message(VCPKG_LINE_INFO,
+ "Invalid package: %s\n"
+ "%s",
+ spec_as_string,
+ vcpkg::to_string(error_type));
+ });
+ }
+
+ const std::string& PackageSpec::name() const { return this->m_name; }
+
+ const Triplet& PackageSpec::triplet() const { return this->m_triplet; }
+
+ std::string PackageSpec::dir() const { return Strings::format("%s_%s", this->m_name, this->m_triplet); }
+
+ std::string PackageSpec::to_string() const { return Strings::format("%s:%s", this->name(), this->triplet()); }
+
+ bool operator==(const PackageSpec& left, const PackageSpec& right)
+ {
+ return left.name() == right.name() && left.triplet() == right.triplet();
+ }
+
+ bool operator!=(const PackageSpec& left, const PackageSpec& right) { return !(left == right); }
+
+ ExpectedT<ParsedSpecifier, PackageSpecParseResult> ParsedSpecifier::from_string(const std::string& input)
+ {
+ auto pos = input.find(':');
+ auto pos_l_bracket = input.find('[');
+ auto pos_r_bracket = input.find(']');
+
+ ParsedSpecifier f;
+ if (pos == std::string::npos && pos_l_bracket == std::string::npos)
+ {
+ f.name = input;
+ return f;
+ }
+ else if (pos == std::string::npos)
+ {
+ if (pos_r_bracket == std::string::npos || pos_l_bracket >= pos_r_bracket)
+ {
+ return PackageSpecParseResult::INVALID_CHARACTERS;
+ }
+ const std::string name = input.substr(0, pos_l_bracket);
+ f.name = name;
+ f.features = parse_comma_list(input.substr(pos_l_bracket + 1, pos_r_bracket - pos_l_bracket - 1));
+ return f;
+ }
+ else if (pos_l_bracket == std::string::npos && pos_r_bracket == std::string::npos)
+ {
+ const std::string name = input.substr(0, pos);
+ f.triplet = input.substr(pos + 1);
+ f.name = name;
+ }
+ else
+ {
+ if (pos_r_bracket == std::string::npos || pos_l_bracket >= pos_r_bracket)
+ {
+ return PackageSpecParseResult::INVALID_CHARACTERS;
+ }
+ const std::string name = input.substr(0, pos_l_bracket);
+ f.features = parse_comma_list(input.substr(pos_l_bracket + 1, pos_r_bracket - pos_l_bracket - 1));
+ f.triplet = input.substr(pos + 1);
+ f.name = name;
+ }
+
+ auto pos2 = input.find(':', pos + 1);
+ if (pos2 != std::string::npos)
+ {
+ return PackageSpecParseResult::TOO_MANY_COLONS;
+ }
+ return f;
+ }
+
+ ExpectedT<Features, PackageSpecParseResult> Features::from_string(const std::string& name)
+ {
+ auto maybe_spec = ParsedSpecifier::from_string(name);
+ if (auto spec = maybe_spec.get())
+ {
+ Checks::check_exit(
+ VCPKG_LINE_INFO, spec->triplet.empty(), "error: triplet not allowed in specifier: %s", name);
+
+ Features f;
+ f.name = spec->name;
+ f.features = spec->features;
+ return f;
+ }
+
+ return maybe_spec.error();
+ }
+}
diff --git a/toolsrc/src/vcpkg/packagespecparseresult.cpp b/toolsrc/src/vcpkg/packagespecparseresult.cpp
new file mode 100644
index 000000000..b12bd12d0
--- /dev/null
+++ b/toolsrc/src/vcpkg/packagespecparseresult.cpp
@@ -0,0 +1,21 @@
+#include "pch.h"
+
+#include <vcpkg/packagespecparseresult.h>
+
+#include <vcpkg/base/checks.h>
+
+namespace vcpkg
+{
+ CStringView to_string(PackageSpecParseResult ev) noexcept
+ {
+ switch (ev)
+ {
+ case PackageSpecParseResult::SUCCESS: return "OK";
+ case PackageSpecParseResult::TOO_MANY_COLONS: return "Too many colons";
+ case PackageSpecParseResult::INVALID_CHARACTERS:
+ return "Contains invalid characters. Only alphanumeric lowercase ASCII characters and dashes are "
+ "allowed";
+ default: Checks::unreachable(VCPKG_LINE_INFO);
+ }
+ }
+}
diff --git a/toolsrc/src/ParagraphParseResult.cpp b/toolsrc/src/vcpkg/paragraphparseresult.cpp
index 5c8c1d59d..920a4b16b 100644
--- a/toolsrc/src/ParagraphParseResult.cpp
+++ b/toolsrc/src/vcpkg/paragraphparseresult.cpp
@@ -1,7 +1,7 @@
#include "pch.h"
-#include "ParagraphParseResult.h"
-#include "vcpkg_Checks.h"
+#include <vcpkg/base/checks.h>
+#include <vcpkg/paragraphparseresult.h>
namespace vcpkg
{
diff --git a/toolsrc/src/Paragraphs.cpp b/toolsrc/src/vcpkg/paragraphs.cpp
index 70d3db2b3..77c028937 100644
--- a/toolsrc/src/Paragraphs.cpp
+++ b/toolsrc/src/vcpkg/paragraphs.cpp
@@ -1,8 +1,12 @@
#include "pch.h"
-#include "ParagraphParseResult.h"
-#include "Paragraphs.h"
-#include "vcpkg_Files.h"
+#include <vcpkg/base/files.h>
+#include <vcpkg/base/util.h>
+#include <vcpkg/globalstate.h>
+#include <vcpkg/paragraphparseresult.h>
+#include <vcpkg/paragraphs.h>
+
+using namespace vcpkg::Parse;
namespace vcpkg::Paragraphs
{
@@ -168,7 +172,7 @@ namespace vcpkg::Paragraphs
return parse_single_paragraph(*spgh);
}
- return contents.error_code();
+ return contents.error();
}
Expected<std::vector<std::unordered_map<std::string, std::string>>> get_paragraphs(const Files::Filesystem& fs,
@@ -180,7 +184,7 @@ namespace vcpkg::Paragraphs
return parse_paragraphs(*spgh);
}
- return contents.error_code();
+ return contents.error();
}
Expected<std::unordered_map<std::string, std::string>> parse_single_paragraph(const std::string& str)
@@ -201,54 +205,94 @@ namespace vcpkg::Paragraphs
return Parser(str.c_str(), str.c_str() + str.size()).get_paragraphs();
}
- Expected<SourceParagraph> try_load_port(const Files::Filesystem& fs, const fs::path& path)
+ ParseExpected<SourceControlFile> try_load_port(const Files::Filesystem& fs, const fs::path& path)
{
- Expected<std::unordered_map<std::string, std::string>> pghs = get_single_paragraph(fs, path / "CONTROL");
- if (auto p = pghs.get())
+ Expected<std::vector<std::unordered_map<std::string, std::string>>> pghs = get_paragraphs(fs, path / "CONTROL");
+ if (auto vector_pghs = pghs.get())
{
- return SourceParagraph(*p);
+ auto csf = SourceControlFile::parse_control_file(std::move(*vector_pghs));
+ if (!GlobalState::feature_packages)
+ {
+ if (auto ptr = csf.get())
+ {
+ Checks::check_exit(VCPKG_LINE_INFO, ptr->get() != nullptr);
+ ptr->get()->core_paragraph->default_features.clear();
+ ptr->get()->feature_paragraphs.clear();
+ }
+ }
+ return csf;
}
-
- return pghs.error_code();
+ auto error_info = std::make_unique<ParseControlErrorInfo>();
+ error_info->name = path.filename().generic_u8string();
+ error_info->error = pghs.error();
+ return error_info;
}
- Expected<BinaryParagraph> try_load_cached_package(const VcpkgPaths& paths, const PackageSpec& spec)
+ Expected<BinaryControlFile> try_load_cached_package(const VcpkgPaths& paths, const PackageSpec& spec)
{
- Expected<std::unordered_map<std::string, std::string>> pghs =
- get_single_paragraph(paths.get_filesystem(), paths.package_dir(spec) / "CONTROL");
+ Expected<std::vector<std::unordered_map<std::string, std::string>>> pghs =
+ get_paragraphs(paths.get_filesystem(), paths.package_dir(spec) / "CONTROL");
if (auto p = pghs.get())
{
- return BinaryParagraph(*p);
+ BinaryControlFile bcf;
+ bcf.core_paragraph = BinaryParagraph(p->front());
+ p->erase(p->begin());
+
+ bcf.features =
+ Util::fmap(*p, [&](auto&& raw_feature) -> BinaryParagraph { return BinaryParagraph(raw_feature); });
+
+ return bcf;
}
- return pghs.error_code();
+ return pghs.error();
}
- std::vector<SourceParagraph> load_all_ports(const Files::Filesystem& fs, const fs::path& ports_dir)
+ LoadResults try_load_all_ports(const Files::Filesystem& fs, const fs::path& ports_dir)
{
- std::vector<SourceParagraph> output;
- for (auto&& path : fs.get_files_non_recursive(ports_dir))
+ LoadResults ret;
+ auto port_dirs = fs.get_files_non_recursive(ports_dir);
+ Util::sort(port_dirs);
+ Util::erase_remove_if(port_dirs, [&](auto&& port_dir_entry) {
+ return fs.is_regular_file(port_dir_entry) && port_dir_entry.filename() == ".DS_Store";
+ });
+
+ for (auto&& path : port_dirs)
{
- Expected<SourceParagraph> source_paragraph = try_load_port(fs, path);
- if (auto srcpgh = source_paragraph.get())
+ auto maybe_spgh = try_load_port(fs, path);
+ if (const auto spgh = maybe_spgh.get())
{
- output.emplace_back(std::move(*srcpgh));
+ ret.paragraphs.emplace_back(std::move(*spgh));
+ }
+ else
+ {
+ ret.errors.emplace_back(std::move(maybe_spgh).error());
}
}
-
- return output;
+ return ret;
}
- std::map<std::string, VersionT> extract_port_names_and_versions(
- const std::vector<SourceParagraph>& source_paragraphs)
+ std::vector<std::unique_ptr<SourceControlFile>> load_all_ports(const Files::Filesystem& fs,
+ const fs::path& ports_dir)
{
- std::map<std::string, VersionT> names_and_versions;
- for (const SourceParagraph& port : source_paragraphs)
+ auto results = try_load_all_ports(fs, ports_dir);
+ if (!results.errors.empty())
{
- names_and_versions.emplace(port.name, port.version);
+ if (GlobalState::debugging)
+ {
+ print_error_message(results.errors);
+ }
+ else
+ {
+ for (auto&& error : results.errors)
+ {
+ System::println(
+ System::Color::warning, "Warning: an error occurred while parsing '%s'", error->name);
+ }
+ System::println(System::Color::warning,
+ "Use '--debug' to get more information about the parse failures.\n");
+ }
}
-
- return names_and_versions;
+ return std::move(results.paragraphs);
}
}
diff --git a/toolsrc/src/vcpkg/parse.cpp b/toolsrc/src/vcpkg/parse.cpp
new file mode 100644
index 000000000..d50296cf8
--- /dev/null
+++ b/toolsrc/src/vcpkg/parse.cpp
@@ -0,0 +1,80 @@
+#include "pch.h"
+
+#include <vcpkg/parse.h>
+
+#include <vcpkg/base/util.h>
+
+namespace vcpkg::Parse
+{
+ static Optional<std::string> remove_field(std::unordered_map<std::string, std::string>* fields,
+ const std::string& fieldname)
+ {
+ auto it = fields->find(fieldname);
+ if (it == fields->end())
+ {
+ return nullopt;
+ }
+
+ const std::string value = std::move(it->second);
+ fields->erase(it);
+ return value;
+ }
+
+ void ParagraphParser::required_field(const std::string& fieldname, std::string& out)
+ {
+ auto maybe_field = remove_field(&fields, fieldname);
+ if (const auto field = maybe_field.get())
+ out = std::move(*field);
+ else
+ missing_fields.push_back(fieldname);
+ }
+ std::string ParagraphParser::optional_field(const std::string& fieldname) const
+ {
+ return remove_field(&fields, fieldname).value_or("");
+ }
+ std::unique_ptr<ParseControlErrorInfo> ParagraphParser::error_info(const std::string& name) const
+ {
+ if (!fields.empty() || !missing_fields.empty())
+ {
+ auto err = std::make_unique<ParseControlErrorInfo>();
+ err->name = name;
+ err->extra_fields = Util::extract_keys(fields);
+ err->missing_fields = std::move(missing_fields);
+ return err;
+ }
+ return nullptr;
+ }
+
+ std::vector<std::string> parse_comma_list(const std::string& str)
+ {
+ if (str.empty())
+ {
+ return {};
+ }
+
+ std::vector<std::string> out;
+
+ size_t cur = 0;
+ do
+ {
+ auto pos = str.find(',', cur);
+ if (pos == std::string::npos)
+ {
+ out.push_back(str.substr(cur));
+ break;
+ }
+ out.push_back(str.substr(cur, pos - cur));
+
+ // skip comma and space
+ ++pos;
+ if (str[pos] == ' ')
+ {
+ ++pos;
+ }
+
+ cur = pos;
+ } while (cur != std::string::npos);
+
+ return out;
+ }
+}
diff --git a/toolsrc/src/PostBuildLint_BuildType.cpp b/toolsrc/src/vcpkg/postbuildlint.buildtype.cpp
index e690036d2..e966ce78a 100644
--- a/toolsrc/src/PostBuildLint_BuildType.cpp
+++ b/toolsrc/src/vcpkg/postbuildlint.buildtype.cpp
@@ -1,28 +1,30 @@
#include "pch.h"
-#include "PostBuildLint_BuildType.h"
-#include "vcpkg_Checks.h"
+#include <vcpkg/base/checks.h>
+#include <vcpkg/postbuildlint.buildtype.h>
+
+using vcpkg::Build::ConfigurationType;
namespace vcpkg::PostBuildLint
{
- BuildType BuildType::value_of(const ConfigurationType& config, const LinkageType& linkage)
+ BuildType BuildType::value_of(const ConfigurationType& config, const Build::LinkageType& linkage)
{
- if (config == ConfigurationTypeC::DEBUG && linkage == LinkageTypeC::STATIC)
+ if (config == ConfigurationType::DEBUG && linkage == Build::LinkageType::STATIC)
{
return BuildTypeC::DEBUG_STATIC;
}
- if (config == ConfigurationTypeC::DEBUG && linkage == LinkageTypeC::DYNAMIC)
+ if (config == ConfigurationType::DEBUG && linkage == Build::LinkageType::DYNAMIC)
{
return BuildTypeC::DEBUG_DYNAMIC;
}
- if (config == ConfigurationTypeC::RELEASE && linkage == LinkageTypeC::STATIC)
+ if (config == ConfigurationType::RELEASE && linkage == Build::LinkageType::STATIC)
{
return BuildTypeC::RELEASE_STATIC;
}
- if (config == ConfigurationTypeC::RELEASE && linkage == LinkageTypeC::DYNAMIC)
+ if (config == ConfigurationType::RELEASE && linkage == Build::LinkageType::DYNAMIC)
{
return BuildTypeC::RELEASE_DYNAMIC;
}
@@ -32,7 +34,7 @@ namespace vcpkg::PostBuildLint
const ConfigurationType& BuildType::config() const { return this->m_config; }
- const LinkageType& BuildType::linkage() const { return this->m_linkage; }
+ const Build::LinkageType& BuildType::linkage() const { return this->m_linkage; }
const std::regex& BuildType::crt_regex() const
{
diff --git a/toolsrc/src/PostBuildLint.cpp b/toolsrc/src/vcpkg/postbuildlint.cpp
index 0d2f556c1..6fe11951f 100644
--- a/toolsrc/src/PostBuildLint.cpp
+++ b/toolsrc/src/vcpkg/postbuildlint.cpp
@@ -1,17 +1,18 @@
#include "pch.h"
-#include "PackageSpec.h"
-#include "PostBuildLint.h"
-#include "PostBuildLint_BuildType.h"
-#include "VcpkgPaths.h"
-#include "coff_file_reader.h"
-#include "vcpkg_Build.h"
-#include "vcpkg_Files.h"
-#include "vcpkg_System.h"
-#include "vcpkg_Util.h"
+#include <vcpkg/base/cofffilereader.h>
+#include <vcpkg/base/files.h>
+#include <vcpkg/base/system.h>
+#include <vcpkg/base/util.h>
+#include <vcpkg/build.h>
+#include <vcpkg/packagespec.h>
+#include <vcpkg/postbuildlint.buildtype.h>
+#include <vcpkg/postbuildlint.h>
+#include <vcpkg/vcpkgpaths.h>
-using vcpkg::Build::PreBuildInfo;
using vcpkg::Build::BuildInfo;
+using vcpkg::Build::BuildPolicy;
+using vcpkg::Build::PreBuildInfo;
namespace vcpkg::PostBuildLint
{
@@ -37,44 +38,49 @@ namespace vcpkg::PostBuildLint
}
};
- const std::vector<OutdatedDynamicCrt>& get_outdated_dynamic_crts()
- {
- static const std::vector<OutdatedDynamicCrt> v = {{"msvcp100.dll", R"(msvcp100\.dll)"},
- {"msvcp100d.dll", R"(msvcp100d\.dll)"},
- {"msvcp110.dll", R"(msvcp110\.dll)"},
- {"msvcp110_win.dll", R"(msvcp110_win\.dll)"},
- {"msvcp120.dll", R"(msvcp120\.dll)"},
- {"msvcp120_clr0400.dll", R"(msvcp120_clr0400\.dll)"},
- {"msvcp60.dll", R"(msvcp60\.dll)"},
- {"msvcp60.dll", R"(msvcp60\.dll)"},
-
- {"msvcr100.dll", R"(msvcr100\.dll)"},
- {"msvcr100d.dll", R"(msvcr100d\.dll)"},
- {"msvcr100_clr0400.dll", R"(msvcr100_clr0400\.dll)"},
- {"msvcr110.dll", R"(msvcr110\.dll)"},
- {"msvcr120.dll", R"(msvcr120\.dll)"},
- {"msvcr120_clr0400.dll", R"(msvcr120_clr0400\.dll)"},
- {"msvcrt.dll", R"(msvcrt\.dll)"},
- {"msvcrt20.dll", R"(msvcrt20\.dll)"},
- {"msvcrt40.dll", R"(msvcrt40\.dll)"}};
-
- return v;
- }
+ Span<const OutdatedDynamicCrt> get_outdated_dynamic_crts(const Optional<std::string>& toolset_version)
+ {
+ static const std::vector<OutdatedDynamicCrt> V_NO_120 = {
+ {"msvcp100.dll", R"(msvcp100\.dll)"},
+ {"msvcp100d.dll", R"(msvcp100d\.dll)"},
+ {"msvcp110.dll", R"(msvcp110\.dll)"},
+ {"msvcp110_win.dll", R"(msvcp110_win\.dll)"},
+ {"msvcp60.dll", R"(msvcp60\.dll)"},
+ {"msvcp60.dll", R"(msvcp60\.dll)"},
+
+ {"msvcrt.dll", R"(msvcrt\.dll)"},
+ {"msvcr100.dll", R"(msvcr100\.dll)"},
+ {"msvcr100d.dll", R"(msvcr100d\.dll)"},
+ {"msvcr100_clr0400.dll", R"(msvcr100_clr0400\.dll)"},
+ {"msvcr110.dll", R"(msvcr110\.dll)"},
+ {"msvcrt20.dll", R"(msvcrt20\.dll)"},
+ {"msvcrt40.dll", R"(msvcrt40\.dll)"},
+ };
- template<class T>
- static bool contains_and_enabled(const std::map<T, bool> map, const T& key)
- {
- auto it = map.find(key);
- if (it != map.cend()) return it->second;
+ static const std::vector<OutdatedDynamicCrt> V_NO_MSVCRT = [&]() {
+ auto ret = V_NO_120;
+ ret.push_back({"msvcp120.dll", R"(msvcp120\.dll)"});
+ ret.push_back({"msvcp120_clr0400.dll", R"(msvcp120_clr0400\.dll)"});
+ ret.push_back({"msvcr120.dll", R"(msvcr120\.dll)"});
+ ret.push_back({"msvcr120_clr0400.dll", R"(msvcr120_clr0400\.dll)"});
+ return ret;
+ }();
+
+ const auto tsv = toolset_version.get();
+ if (tsv && (*tsv) == "v120")
+ {
+ return V_NO_120;
+ }
- return false;
+ // Default case for all version >= VS 2015.
+ return V_NO_MSVCRT;
}
static LintStatus check_for_files_in_include_directory(const Files::Filesystem& fs,
- const std::map<BuildPolicies, bool>& policies,
+ const Build::BuildPolicies& policies,
const fs::path& package_dir)
{
- if (contains_and_enabled(policies, BuildPoliciesC::EMPTY_INCLUDE_FOLDER))
+ if (policies.is_enabled(BuildPolicy::EMPTY_INCLUDE_FOLDER))
{
return LintStatus::SUCCESS;
}
@@ -82,8 +88,9 @@ namespace vcpkg::PostBuildLint
const fs::path include_dir = package_dir / "include";
if (!fs.exists(include_dir) || fs.is_empty(include_dir))
{
- System::println(System::Color::warning,
- "The folder /include is empty. This indicates the library was not correctly installed.");
+ System::println(
+ System::Color::warning,
+ "The folder /include is empty or not present. This indicates the library was not correctly installed.");
return LintStatus::ERROR_DETECTED;
}
@@ -292,13 +299,10 @@ namespace vcpkg::PostBuildLint
std::vector<fs::path> dlls_with_no_exports;
for (const fs::path& dll : dlls)
{
- const std::wstring cmd_line =
- Strings::wformat(LR"("%s" /exports "%s")", dumpbin_exe.native(), dll.native());
+ const std::string cmd_line =
+ Strings::format(R"("%s" /exports "%s")", dumpbin_exe.u8string(), dll.u8string());
System::ExitCodeAndOutput ec_data = System::cmd_execute_and_capture_output(cmd_line);
- Checks::check_exit(VCPKG_LINE_INFO,
- ec_data.exit_code == 0,
- "Running command:\n %s\n failed",
- Strings::to_utf8(cmd_line));
+ Checks::check_exit(VCPKG_LINE_INFO, ec_data.exit_code == 0, "Running command:\n %s\n failed", cmd_line);
if (ec_data.output.find("ordinal hint RVA name") == std::string::npos)
{
@@ -329,13 +333,10 @@ namespace vcpkg::PostBuildLint
std::vector<fs::path> dlls_with_improper_uwp_bit;
for (const fs::path& dll : dlls)
{
- const std::wstring cmd_line =
- Strings::wformat(LR"("%s" /headers "%s")", dumpbin_exe.native(), dll.native());
+ const std::string cmd_line =
+ Strings::format(R"("%s" /headers "%s")", dumpbin_exe.u8string(), dll.u8string());
System::ExitCodeAndOutput ec_data = System::cmd_execute_and_capture_output(cmd_line);
- Checks::check_exit(VCPKG_LINE_INFO,
- ec_data.exit_code == 0,
- "Running command:\n %s\n failed",
- Strings::to_utf8(cmd_line));
+ Checks::check_exit(VCPKG_LINE_INFO, ec_data.exit_code == 0, "Running command:\n %s\n failed", cmd_line);
if (ec_data.output.find("App Container") == std::string::npos)
{
@@ -360,6 +361,7 @@ namespace vcpkg::PostBuildLint
std::string actual_arch;
};
+#if defined(_WIN32)
static std::string get_actual_architecture(const MachineType& machine_type)
{
switch (machine_type)
@@ -369,20 +371,23 @@ namespace vcpkg::PostBuildLint
case MachineType::I386: return "x86";
case MachineType::ARM:
case MachineType::ARMNT: return "arm";
+ case MachineType::ARM64: return "arm64";
default: return "Machine Type Code = " + std::to_string(static_cast<uint16_t>(machine_type));
}
}
+#endif
+#if defined(_WIN32)
static void print_invalid_architecture_files(const std::string& expected_architecture,
std::vector<FileAndArch> binaries_with_invalid_architecture)
{
System::println(System::Color::warning, "The following files were built for an incorrect architecture:");
- System::println("");
+ System::println();
for (const FileAndArch& b : binaries_with_invalid_architecture)
{
System::println(" %s", b.file.generic_string());
System::println("Expected %s, but was: %s", expected_architecture, b.actual_arch);
- System::println("");
+ System::println();
}
}
@@ -397,7 +402,7 @@ namespace vcpkg::PostBuildLint
file.extension() == ".dll",
"The file extension was not .dll: %s",
file.generic_string());
- COFFFileReader::DllInfo info = COFFFileReader::read_dll(file);
+ const CoffFileReader::DllInfo info = CoffFileReader::read_dll(file);
const std::string actual_architecture = get_actual_architecture(info.machine_type);
if (expected_architecture != actual_architecture)
@@ -414,10 +419,12 @@ namespace vcpkg::PostBuildLint
return LintStatus::SUCCESS;
}
+#endif
static LintStatus check_lib_architecture(const std::string& expected_architecture,
const std::vector<fs::path>& files)
{
+#if defined(_WIN32)
std::vector<FileAndArch> binaries_with_invalid_architecture;
for (const fs::path& file : files)
@@ -426,7 +433,7 @@ namespace vcpkg::PostBuildLint
file.extension() == ".lib",
"The file extension was not .lib: %s",
file.generic_string());
- COFFFileReader::LibInfo info = COFFFileReader::read_lib(file);
+ CoffFileReader::LibInfo info = CoffFileReader::read_lib(file);
// This is zero for folly's debug library
// TODO: Why?
@@ -449,6 +456,7 @@ namespace vcpkg::PostBuildLint
print_invalid_architecture_files(expected_architecture, binaries_with_invalid_architecture);
return LintStatus::ERROR_DETECTED;
}
+#endif
return LintStatus::SUCCESS;
}
@@ -477,7 +485,7 @@ namespace vcpkg::PostBuildLint
}
System::println(System::Color::warning,
- "Mismatching number of debug and release binaries. Found %d for debug but %d for release.",
+ "Mismatching number of debug and release binaries. Found %zd for debug but %zd for release.",
debug_count,
release_count);
System::println("Debug binaries");
@@ -495,21 +503,17 @@ namespace vcpkg::PostBuildLint
System::println(System::Color::warning, "Release binaries were not found");
}
- System::println("");
+ System::println();
return LintStatus::ERROR_DETECTED;
}
- static LintStatus check_lib_files_are_available_if_dlls_are_available(const std::map<BuildPolicies, bool>& policies,
+ static LintStatus check_lib_files_are_available_if_dlls_are_available(const Build::BuildPolicies& policies,
const size_t lib_count,
const size_t dll_count,
const fs::path& lib_dir)
{
- auto it = policies.find(BuildPoliciesC::DLLS_WITHOUT_LIBS);
- if (it != policies.cend() && it->second)
- {
- return LintStatus::SUCCESS;
- }
+ if (policies.is_enabled(BuildPolicy::DLLS_WITHOUT_LIBS)) return LintStatus::SUCCESS;
if (lib_count == 0 && dll_count != 0)
{
@@ -517,7 +521,7 @@ namespace vcpkg::PostBuildLint
System::println(System::Color::warning,
"If this is intended, add the following line in the portfile:\n"
" SET(%s enabled)",
- BuildPoliciesC::DLLS_WITHOUT_LIBS.cmake_variable());
+ to_cmake_variable(BuildPolicy::DLLS_WITHOUT_LIBS));
return LintStatus::ERROR_DETECTED;
}
@@ -592,7 +596,7 @@ namespace vcpkg::PostBuildLint
return LintStatus::SUCCESS;
}
- struct BuildType_and_file
+ struct BuildTypeAndFile
{
fs::path file;
BuildType build_type;
@@ -606,17 +610,14 @@ namespace vcpkg::PostBuildLint
bad_build_types.erase(std::remove(bad_build_types.begin(), bad_build_types.end(), expected_build_type),
bad_build_types.end());
- std::vector<BuildType_and_file> libs_with_invalid_crt;
+ std::vector<BuildTypeAndFile> libs_with_invalid_crt;
for (const fs::path& lib : libs)
{
- const std::wstring cmd_line =
- Strings::wformat(LR"("%s" /directives "%s")", dumpbin_exe.native(), lib.native());
+ const std::string cmd_line =
+ Strings::format(R"("%s" /directives "%s")", dumpbin_exe.u8string(), lib.u8string());
System::ExitCodeAndOutput ec_data = System::cmd_execute_and_capture_output(cmd_line);
- Checks::check_exit(VCPKG_LINE_INFO,
- ec_data.exit_code == 0,
- "Running command:\n %s\n failed",
- Strings::to_utf8(cmd_line));
+ Checks::check_exit(VCPKG_LINE_INFO, ec_data.exit_code == 0, "Running command:\n %s\n failed", cmd_line);
for (const BuildType& bad_build_type : bad_build_types)
{
@@ -633,12 +634,12 @@ namespace vcpkg::PostBuildLint
System::println(System::Color::warning,
"Expected %s crt linkage, but the following libs had invalid crt linkage:",
expected_build_type.to_string());
- System::println("");
- for (const BuildType_and_file btf : libs_with_invalid_crt)
+ System::println();
+ for (const BuildTypeAndFile btf : libs_with_invalid_crt)
{
System::println(" %s: %s", btf.file.generic_string(), btf.build_type.to_string());
}
- System::println("");
+ System::println();
System::println(System::Color::warning,
"To inspect the lib files, use:\n dumpbin.exe /directives mylibfile.lib");
@@ -648,31 +649,30 @@ namespace vcpkg::PostBuildLint
return LintStatus::SUCCESS;
}
- struct OutdatedDynamicCrt_and_file
+ struct OutdatedDynamicCrtAndFile
{
fs::path file;
OutdatedDynamicCrt outdated_crt;
- OutdatedDynamicCrt_and_file() = delete;
+ OutdatedDynamicCrtAndFile() = delete;
};
- static LintStatus check_outdated_crt_linkage_of_dlls(const std::vector<fs::path>& dlls, const fs::path dumpbin_exe)
+ static LintStatus check_outdated_crt_linkage_of_dlls(const std::vector<fs::path>& dlls,
+ const fs::path dumpbin_exe,
+ const BuildInfo& build_info,
+ const PreBuildInfo& pre_build_info)
{
- const std::vector<OutdatedDynamicCrt>& outdated_crts = get_outdated_dynamic_crts();
+ if (build_info.policies.is_enabled(BuildPolicy::ALLOW_OBSOLETE_MSVCRT)) return LintStatus::SUCCESS;
- std::vector<OutdatedDynamicCrt_and_file> dlls_with_outdated_crt;
+ std::vector<OutdatedDynamicCrtAndFile> dlls_with_outdated_crt;
for (const fs::path& dll : dlls)
{
- const std::wstring cmd_line =
- Strings::wformat(LR"("%s" /dependents "%s")", dumpbin_exe.native(), dll.native());
+ const auto cmd_line = Strings::format(R"("%s" /dependents "%s")", dumpbin_exe.u8string(), dll.u8string());
System::ExitCodeAndOutput ec_data = System::cmd_execute_and_capture_output(cmd_line);
- Checks::check_exit(VCPKG_LINE_INFO,
- ec_data.exit_code == 0,
- "Running command:\n %s\n failed",
- Strings::to_utf8(cmd_line));
+ Checks::check_exit(VCPKG_LINE_INFO, ec_data.exit_code == 0, "Running command:\n %s\n failed", cmd_line);
- for (const OutdatedDynamicCrt& outdated_crt : outdated_crts)
+ for (const OutdatedDynamicCrt& outdated_crt : get_outdated_dynamic_crts(pre_build_info.platform_toolset))
{
if (std::regex_search(ec_data.output.cbegin(), ec_data.output.cend(), outdated_crt.regex))
{
@@ -685,12 +685,12 @@ namespace vcpkg::PostBuildLint
if (!dlls_with_outdated_crt.empty())
{
System::println(System::Color::warning, "Detected outdated dynamic CRT in the following files:");
- System::println("");
- for (const OutdatedDynamicCrt_and_file btf : dlls_with_outdated_crt)
+ System::println();
+ for (const OutdatedDynamicCrtAndFile btf : dlls_with_outdated_crt)
{
System::println(" %s: %s", btf.file.generic_string(), btf.outdated_crt.name);
}
- System::println("");
+ System::println();
System::println(System::Color::warning,
"To inspect the dll files, use:\n dumpbin.exe /dependents mydllfile.dll");
@@ -705,8 +705,8 @@ namespace vcpkg::PostBuildLint
std::vector<fs::path> misplaced_files = fs.get_files_non_recursive(dir);
Util::unstable_keep_if(misplaced_files, [&fs](const fs::path& path) {
const std::string filename = path.filename().generic_string();
- if (Strings::case_insensitive_ascii_compare(filename.c_str(), "CONTROL") == 0 ||
- Strings::case_insensitive_ascii_compare(filename.c_str(), "BUILD_INFO") == 0)
+ if (Strings::case_insensitive_ascii_equals(filename.c_str(), "CONTROL") ||
+ Strings::case_insensitive_ascii_equals(filename.c_str(), "BUILD_INFO"))
return false;
return !fs.is_directory(path);
});
@@ -732,12 +732,12 @@ namespace vcpkg::PostBuildLint
const auto& fs = paths.get_filesystem();
// for dumpbin
- const Toolset& toolset = paths.get_toolset();
+ const Toolset& toolset = paths.get_toolset(pre_build_info);
const fs::path package_dir = paths.package_dir(spec);
size_t error_count = 0;
- if (contains_and_enabled(build_info.policies, BuildPoliciesC::EMPTY_PACKAGE))
+ if (build_info.policies.is_enabled(BuildPolicy::EMPTY_PACKAGE))
{
return error_count;
}
@@ -764,7 +764,8 @@ namespace vcpkg::PostBuildLint
std::vector<fs::path> release_libs = fs.get_files_recursive(release_lib_dir);
Util::unstable_keep_if(release_libs, has_extension_pred(fs, ".lib"));
- error_count += check_matching_debug_and_release_binaries(debug_libs, release_libs);
+ if (!pre_build_info.build_type)
+ error_count += check_matching_debug_and_release_binaries(debug_libs, release_libs);
{
std::vector<fs::path> libs;
@@ -774,16 +775,17 @@ namespace vcpkg::PostBuildLint
error_count += check_lib_architecture(pre_build_info.target_architecture, libs);
}
+ std::vector<fs::path> debug_dlls = fs.get_files_recursive(debug_bin_dir);
+ Util::unstable_keep_if(debug_dlls, has_extension_pred(fs, ".dll"));
+ std::vector<fs::path> release_dlls = fs.get_files_recursive(release_bin_dir);
+ Util::unstable_keep_if(release_dlls, has_extension_pred(fs, ".dll"));
+
switch (build_info.library_linkage)
{
- case LinkageType::BackingEnum::DYNAMIC:
+ case Build::LinkageType::DYNAMIC:
{
- std::vector<fs::path> debug_dlls = fs.get_files_recursive(debug_bin_dir);
- Util::unstable_keep_if(debug_dlls, has_extension_pred(fs, ".dll"));
- std::vector<fs::path> release_dlls = fs.get_files_recursive(release_bin_dir);
- Util::unstable_keep_if(release_dlls, has_extension_pred(fs, ".dll"));
-
- error_count += check_matching_debug_and_release_binaries(debug_dlls, release_dlls);
+ if (!pre_build_info.build_type)
+ error_count += check_matching_debug_and_release_binaries(debug_dlls, release_dlls);
error_count += check_lib_files_are_available_if_dlls_are_available(
build_info.policies, debug_libs.size(), debug_dlls.size(), debug_lib_dir);
@@ -794,35 +796,43 @@ namespace vcpkg::PostBuildLint
dlls.insert(dlls.cend(), debug_dlls.cbegin(), debug_dlls.cend());
dlls.insert(dlls.cend(), release_dlls.cbegin(), release_dlls.cend());
- error_count += check_exports_of_dlls(dlls, toolset.dumpbin);
- error_count += check_uwp_bit_of_dlls(pre_build_info.cmake_system_name, dlls, toolset.dumpbin);
- error_count += check_dll_architecture(pre_build_info.target_architecture, dlls);
+ if (!toolset.dumpbin.empty())
+ {
+ error_count += check_exports_of_dlls(dlls, toolset.dumpbin);
+ error_count += check_uwp_bit_of_dlls(pre_build_info.cmake_system_name, dlls, toolset.dumpbin);
+ error_count +=
+ check_outdated_crt_linkage_of_dlls(dlls, toolset.dumpbin, build_info, pre_build_info);
+ }
- error_count += check_outdated_crt_linkage_of_dlls(dlls, toolset.dumpbin);
+#if defined(_WIN32)
+ error_count += check_dll_architecture(pre_build_info.target_architecture, dlls);
+#endif
break;
}
- case LinkageType::BackingEnum::STATIC:
+ case Build::LinkageType::STATIC:
{
- std::vector<fs::path> dlls = fs.get_files_recursive(package_dir);
- Util::unstable_keep_if(dlls, has_extension_pred(fs, ".dll"));
+ auto dlls = release_dlls;
+ dlls.insert(dlls.end(), debug_dlls.begin(), debug_dlls.end());
error_count += check_no_dlls_present(dlls);
error_count += check_bin_folders_are_not_present_in_static_build(fs, package_dir);
- if (!contains_and_enabled(build_info.policies, BuildPoliciesC::ONLY_RELEASE_CRT))
+ if (!toolset.dumpbin.empty())
{
+ if (!build_info.policies.is_enabled(BuildPolicy::ONLY_RELEASE_CRT))
+ {
+ error_count += check_crt_linkage_of_libs(
+ BuildType::value_of(Build::ConfigurationType::DEBUG, build_info.crt_linkage),
+ debug_libs,
+ toolset.dumpbin);
+ }
error_count += check_crt_linkage_of_libs(
- BuildType::value_of(ConfigurationTypeC::DEBUG, build_info.crt_linkage),
- debug_libs,
+ BuildType::value_of(Build::ConfigurationType::RELEASE, build_info.crt_linkage),
+ release_libs,
toolset.dumpbin);
}
- error_count +=
- check_crt_linkage_of_libs(BuildType::value_of(ConfigurationTypeC::RELEASE, build_info.crt_linkage),
- release_libs,
- toolset.dumpbin);
break;
}
- case LinkageType::BackingEnum::NULLVALUE:
default: Checks::unreachable(VCPKG_LINE_INFO);
}
diff --git a/toolsrc/src/vcpkg/remove.cpp b/toolsrc/src/vcpkg/remove.cpp
new file mode 100644
index 000000000..13cc9325e
--- /dev/null
+++ b/toolsrc/src/vcpkg/remove.cpp
@@ -0,0 +1,306 @@
+#include "pch.h"
+
+#include <vcpkg/base/system.h>
+#include <vcpkg/base/util.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/dependencies.h>
+#include <vcpkg/help.h>
+#include <vcpkg/input.h>
+#include <vcpkg/paragraphs.h>
+#include <vcpkg/remove.h>
+#include <vcpkg/update.h>
+#include <vcpkg/vcpkglib.h>
+
+namespace vcpkg::Remove
+{
+ using Dependencies::RemovePlanAction;
+ using Dependencies::RemovePlanType;
+ using Dependencies::RequestType;
+ using Update::OutdatedPackage;
+
+ void remove_package(const VcpkgPaths& paths, const PackageSpec& spec, StatusParagraphs* status_db)
+ {
+ auto& fs = paths.get_filesystem();
+ auto maybe_ipv = status_db->find_all_installed(spec);
+
+ Checks::check_exit(
+ VCPKG_LINE_INFO, maybe_ipv.has_value(), "unable to remove package %s: already removed", spec);
+
+ auto&& ipv = maybe_ipv.value_or_exit(VCPKG_LINE_INFO);
+
+ std::vector<StatusParagraph> spghs;
+ spghs.emplace_back(*ipv.core);
+ for (auto&& feature : ipv.features)
+ {
+ spghs.emplace_back(*feature);
+ }
+
+ for (auto&& spgh : spghs)
+ {
+ spgh.want = Want::PURGE;
+ spgh.state = InstallState::HALF_INSTALLED;
+ write_update(paths, spgh);
+ }
+
+ auto maybe_lines = fs.read_lines(paths.listfile_path(ipv.core->package));
+
+ if (const auto lines = maybe_lines.get())
+ {
+ std::vector<fs::path> dirs_touched;
+ for (auto&& suffix : *lines)
+ {
+ if (!suffix.empty() && suffix.back() == '\r') suffix.pop_back();
+
+ std::error_code ec;
+
+ auto target = paths.installed / suffix;
+
+ const auto status = fs.status(target, ec);
+ if (ec)
+ {
+ System::println(System::Color::error, "failed: status(%s): %s", target.u8string(), ec.message());
+ continue;
+ }
+
+ if (fs::is_directory(status))
+ {
+ dirs_touched.push_back(target);
+ }
+ else if (fs::is_regular_file(status))
+ {
+ fs.remove(target, ec);
+ if (ec)
+ {
+#if defined(_WIN32)
+ fs::stdfs::permissions(target, fs::stdfs::perms::owner_all | fs::stdfs::perms::group_all, ec);
+ fs.remove(target, ec);
+ if (ec)
+ {
+ System::println(
+ System::Color::error, "failed: remove(%s): %s", target.u8string(), ec.message());
+ }
+#else
+ System::println(
+ System::Color::error, "failed: remove(%s): %s", target.u8string(), ec.message());
+#endif
+ }
+ }
+ else if (!fs::stdfs::exists(status))
+ {
+ System::println(System::Color::warning, "Warning: %s: file not found", target.u8string());
+ }
+ else
+ {
+ System::println(System::Color::warning, "Warning: %s: cannot handle file type", target.u8string());
+ }
+ }
+
+ auto b = dirs_touched.rbegin();
+ const auto e = dirs_touched.rend();
+ for (; b != e; ++b)
+ {
+ if (fs.is_empty(*b))
+ {
+ std::error_code ec;
+ fs.remove(*b, ec);
+ if (ec)
+ {
+ System::println(System::Color::error, "failed: %s", ec.message());
+ }
+ }
+ }
+
+ fs.remove(paths.listfile_path(ipv.core->package));
+ }
+
+ for (auto&& spgh : spghs)
+ {
+ spgh.state = InstallState::NOT_INSTALLED;
+ write_update(paths, spgh);
+
+ status_db->insert(std::make_unique<StatusParagraph>(std::move(spgh)));
+ }
+ }
+
+ static void print_plan(const std::map<RemovePlanType, std::vector<const RemovePlanAction*>>& group_by_plan_type)
+ {
+ static constexpr std::array<RemovePlanType, 2> ORDER = {RemovePlanType::NOT_INSTALLED, RemovePlanType::REMOVE};
+
+ for (const RemovePlanType plan_type : ORDER)
+ {
+ const auto it = group_by_plan_type.find(plan_type);
+ if (it == group_by_plan_type.cend())
+ {
+ continue;
+ }
+
+ std::vector<const RemovePlanAction*> cont = it->second;
+ std::sort(cont.begin(), cont.end(), &RemovePlanAction::compare_by_name);
+ const std::string as_string = Strings::join("\n", cont, [](const RemovePlanAction* p) {
+ return Dependencies::to_output_string(p->request_type, p->spec.to_string());
+ });
+
+ switch (plan_type)
+ {
+ case RemovePlanType::NOT_INSTALLED:
+ System::println("The following packages are not installed, so not removed:\n%s", as_string);
+ continue;
+ case RemovePlanType::REMOVE:
+ System::println("The following packages will be removed:\n%s", as_string);
+ continue;
+ default: Checks::unreachable(VCPKG_LINE_INFO);
+ }
+ }
+ }
+
+ void perform_remove_plan_action(const VcpkgPaths& paths,
+ const RemovePlanAction& action,
+ const Purge purge,
+ StatusParagraphs* status_db)
+ {
+ const std::string display_name = action.spec.to_string();
+
+ switch (action.plan_type)
+ {
+ case RemovePlanType::NOT_INSTALLED:
+ System::println(System::Color::success, "Package %s is not installed", display_name);
+ break;
+ case RemovePlanType::REMOVE:
+ System::println("Removing package %s... ", display_name);
+ remove_package(paths, action.spec, status_db);
+ System::println(System::Color::success, "Removing package %s... done", display_name);
+ break;
+ case RemovePlanType::UNKNOWN:
+ default: Checks::unreachable(VCPKG_LINE_INFO);
+ }
+
+ if (purge == Purge::YES)
+ {
+ System::println("Purging package %s... ", display_name);
+ Files::Filesystem& fs = paths.get_filesystem();
+ std::error_code ec;
+ fs.remove_all(paths.packages / action.spec.dir(), ec);
+ System::println(System::Color::success, "Purging package %s... done", display_name);
+ }
+ }
+
+ static constexpr StringLiteral OPTION_PURGE = "--purge";
+ static constexpr StringLiteral OPTION_NO_PURGE = "--no-purge";
+ static constexpr StringLiteral OPTION_RECURSE = "--recurse";
+ static constexpr StringLiteral OPTION_DRY_RUN = "--dry-run";
+ static constexpr StringLiteral OPTION_OUTDATED = "--outdated";
+
+ static constexpr std::array<CommandSwitch, 5> SWITCHES = {{
+ {OPTION_PURGE, "Remove the cached copy of the package (default)"},
+ {OPTION_NO_PURGE, "Do not remove the cached copy of the package"},
+ {OPTION_RECURSE, "Allow removal of packages not explicitly specified on the command line"},
+ {OPTION_DRY_RUN, "Print the packages to be removed, but do not remove them"},
+ {OPTION_OUTDATED, "Select all packages with versions that do not match the portfiles"},
+ }};
+
+ static std::vector<std::string> valid_arguments(const VcpkgPaths& paths)
+ {
+ const StatusParagraphs status_db = database_load_check(paths);
+ auto installed_packages = get_installed_ports(status_db);
+
+ return Util::fmap(installed_packages, [](auto&& pgh) -> std::string { return pgh.spec().to_string(); });
+ }
+
+ const CommandStructure COMMAND_STRUCTURE = {
+ Help::create_example_string("remove zlib zlib:x64-windows curl boost"),
+ 0,
+ SIZE_MAX,
+ {SWITCHES, {}},
+ &valid_arguments,
+ };
+
+ void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet)
+ {
+ const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
+
+ StatusParagraphs status_db = database_load_check(paths);
+ std::vector<PackageSpec> specs;
+ if (Util::Sets::contains(options.switches, OPTION_OUTDATED))
+ {
+ if (args.command_arguments.size() != 0)
+ {
+ System::println(System::Color::error, "Error: 'remove' accepts either libraries or '--outdated'");
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+
+ Dependencies::PathsPortFileProvider provider(paths);
+
+ specs = Util::fmap(Update::find_outdated_packages(provider, status_db),
+ [](auto&& outdated) { return outdated.spec; });
+
+ if (specs.empty())
+ {
+ System::println(System::Color::success, "There are no outdated packages.");
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+ }
+ else
+ {
+ if (args.command_arguments.size() < 1)
+ {
+ System::println(System::Color::error, "Error: 'remove' accepts either libraries or '--outdated'");
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+ specs = Util::fmap(args.command_arguments, [&](auto&& arg) {
+ return Input::check_and_get_package_spec(arg, default_triplet, COMMAND_STRUCTURE.example_text);
+ });
+
+ for (auto&& spec : specs)
+ Input::check_triplet(spec.triplet(), paths);
+ }
+
+ const bool no_purge_was_passed = Util::Sets::contains(options.switches, OPTION_NO_PURGE);
+ const bool purge_was_passed = Util::Sets::contains(options.switches, OPTION_PURGE);
+ if (purge_was_passed && no_purge_was_passed)
+ {
+ System::println(System::Color::error, "Error: cannot specify both --no-purge and --purge.");
+ System::print(COMMAND_STRUCTURE.example_text);
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+ const Purge purge = to_purge(purge_was_passed || !no_purge_was_passed);
+ const bool is_recursive = Util::Sets::contains(options.switches, OPTION_RECURSE);
+ const bool dry_run = Util::Sets::contains(options.switches, OPTION_DRY_RUN);
+
+ const std::vector<RemovePlanAction> remove_plan = Dependencies::create_remove_plan(specs, status_db);
+ Checks::check_exit(VCPKG_LINE_INFO, !remove_plan.empty(), "Remove plan cannot be empty");
+
+ std::map<RemovePlanType, std::vector<const RemovePlanAction*>> group_by_plan_type;
+ Util::group_by(remove_plan, &group_by_plan_type, [](const RemovePlanAction& p) { return p.plan_type; });
+ print_plan(group_by_plan_type);
+
+ const bool has_non_user_requested_packages =
+ Util::find_if(remove_plan, [](const RemovePlanAction& package) -> bool {
+ return package.request_type != RequestType::USER_REQUESTED;
+ }) != remove_plan.cend();
+
+ if (has_non_user_requested_packages)
+ {
+ System::println(System::Color::warning,
+ "Additional packages (*) need to be removed to complete this operation.");
+
+ if (!is_recursive)
+ {
+ System::println(System::Color::warning,
+ "If you are sure you want to remove them, run the command with the --recurse option");
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+ }
+
+ if (dry_run)
+ {
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+
+ for (const RemovePlanAction& action : remove_plan)
+ {
+ perform_remove_plan_action(paths, action, purge, &status_db);
+ }
+
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+}
diff --git a/toolsrc/src/vcpkg/sourceparagraph.cpp b/toolsrc/src/vcpkg/sourceparagraph.cpp
new file mode 100644
index 000000000..ed61cb42a
--- /dev/null
+++ b/toolsrc/src/vcpkg/sourceparagraph.cpp
@@ -0,0 +1,282 @@
+#include "pch.h"
+
+#include <vcpkg/packagespec.h>
+#include <vcpkg/sourceparagraph.h>
+#include <vcpkg/triplet.h>
+
+#include <vcpkg/base/checks.h>
+#include <vcpkg/base/expected.h>
+#include <vcpkg/base/system.h>
+#include <vcpkg/base/util.h>
+
+namespace vcpkg
+{
+ using namespace vcpkg::Parse;
+
+ namespace SourceParagraphFields
+ {
+ static const std::string BUILD_DEPENDS = "Build-Depends";
+ static const std::string DEFAULTFEATURES = "Default-Features";
+ static const std::string DESCRIPTION = "Description";
+ static const std::string FEATURE = "Feature";
+ static const std::string MAINTAINER = "Maintainer";
+ static const std::string SOURCE = "Source";
+ static const std::string SUPPORTS = "Supports";
+ static const std::string VERSION = "Version";
+ }
+
+ static Span<const std::string> get_list_of_valid_fields()
+ {
+ static const std::string valid_fields[] = {
+ SourceParagraphFields::SOURCE,
+ SourceParagraphFields::VERSION,
+ SourceParagraphFields::DESCRIPTION,
+ SourceParagraphFields::MAINTAINER,
+ SourceParagraphFields::BUILD_DEPENDS,
+ };
+
+ return valid_fields;
+ }
+
+ void print_error_message(Span<const std::unique_ptr<Parse::ParseControlErrorInfo>> error_info_list)
+ {
+ Checks::check_exit(VCPKG_LINE_INFO, error_info_list.size() > 0);
+
+ for (auto&& error_info : error_info_list)
+ {
+ Checks::check_exit(VCPKG_LINE_INFO, error_info != nullptr);
+ if (error_info->error)
+ {
+ System::println(
+ System::Color::error, "Error: while loading %s: %s", error_info->name, error_info->error.message());
+ }
+ }
+
+ bool have_remaining_fields = false;
+ for (auto&& error_info : error_info_list)
+ {
+ if (!error_info->extra_fields.empty())
+ {
+ System::println(System::Color::error,
+ "Error: There are invalid fields in the control file of %s",
+ error_info->name);
+ System::println("The following fields were not expected:\n\n %s\n",
+ Strings::join("\n ", error_info->extra_fields));
+ have_remaining_fields = true;
+ }
+ }
+
+ if (have_remaining_fields)
+ {
+ System::println("This is the list of valid fields (case-sensitive): \n\n %s\n",
+ Strings::join("\n ", get_list_of_valid_fields()));
+ System::println("Different source may be available for vcpkg. Use .\\bootstrap-vcpkg.bat to update.\n");
+ }
+
+ for (auto&& error_info : error_info_list)
+ {
+ if (!error_info->missing_fields.empty())
+ {
+ System::println(System::Color::error,
+ "Error: There are missing fields in the control file of %s",
+ error_info->name);
+ System::println("The following fields were missing:\n\n %s\n",
+ Strings::join("\n ", error_info->missing_fields));
+ }
+ }
+ }
+
+ static ParseExpected<SourceParagraph> parse_source_paragraph(RawParagraph&& fields)
+ {
+ ParagraphParser parser(std::move(fields));
+
+ auto spgh = std::make_unique<SourceParagraph>();
+
+ parser.required_field(SourceParagraphFields::SOURCE, spgh->name);
+ parser.required_field(SourceParagraphFields::VERSION, spgh->version);
+
+ spgh->description = parser.optional_field(SourceParagraphFields::DESCRIPTION);
+ spgh->maintainer = parser.optional_field(SourceParagraphFields::MAINTAINER);
+ spgh->depends = expand_qualified_dependencies(
+ parse_comma_list(parser.optional_field(SourceParagraphFields::BUILD_DEPENDS)));
+ spgh->supports = parse_comma_list(parser.optional_field(SourceParagraphFields::SUPPORTS));
+ spgh->default_features = parse_comma_list(parser.optional_field(SourceParagraphFields::DEFAULTFEATURES));
+
+ auto err = parser.error_info(spgh->name);
+ if (err)
+ return std::move(err);
+ else
+ return std::move(spgh);
+ }
+
+ static ParseExpected<FeatureParagraph> parse_feature_paragraph(RawParagraph&& fields)
+ {
+ ParagraphParser parser(std::move(fields));
+
+ auto fpgh = std::make_unique<FeatureParagraph>();
+
+ parser.required_field(SourceParagraphFields::FEATURE, fpgh->name);
+ parser.required_field(SourceParagraphFields::DESCRIPTION, fpgh->description);
+
+ fpgh->depends = expand_qualified_dependencies(
+ parse_comma_list(parser.optional_field(SourceParagraphFields::BUILD_DEPENDS)));
+
+ auto err = parser.error_info(fpgh->name);
+ if (err)
+ return std::move(err);
+ else
+ return std::move(fpgh);
+ }
+
+ ParseExpected<SourceControlFile> SourceControlFile::parse_control_file(
+ std::vector<std::unordered_map<std::string, std::string>>&& control_paragraphs)
+ {
+ if (control_paragraphs.size() == 0)
+ {
+ return std::make_unique<Parse::ParseControlErrorInfo>();
+ }
+
+ auto control_file = std::make_unique<SourceControlFile>();
+
+ auto maybe_source = parse_source_paragraph(std::move(control_paragraphs.front()));
+ if (const auto source = maybe_source.get())
+ control_file->core_paragraph = std::move(*source);
+ else
+ return std::move(maybe_source).error();
+
+ control_paragraphs.erase(control_paragraphs.begin());
+
+ for (auto&& feature_pgh : control_paragraphs)
+ {
+ auto maybe_feature = parse_feature_paragraph(std::move(feature_pgh));
+ if (const auto feature = maybe_feature.get())
+ control_file->feature_paragraphs.emplace_back(std::move(*feature));
+ else
+ return std::move(maybe_feature).error();
+ }
+
+ return std::move(control_file);
+ }
+
+ Optional<const FeatureParagraph&> SourceControlFile::find_feature(const std::string& featurename) const
+ {
+ auto it = Util::find_if(feature_paragraphs,
+ [&](const std::unique_ptr<FeatureParagraph>& p) { return p->name == featurename; });
+ if (it != feature_paragraphs.end())
+ return **it;
+ else
+ return nullopt;
+ }
+
+ Dependency Dependency::parse_dependency(std::string name, std::string qualifier)
+ {
+ Dependency dep;
+ dep.qualifier = qualifier;
+ if (auto maybe_features = Features::from_string(name))
+ dep.depend = *maybe_features.get();
+ else
+ Checks::exit_with_message(
+ VCPKG_LINE_INFO, "error while parsing dependency: %s: %s", to_string(maybe_features.error()), name);
+ return dep;
+ }
+
+ std::string Dependency::name() const
+ {
+ if (this->depend.features.empty()) return this->depend.name;
+
+ const std::string features = Strings::join(",", this->depend.features);
+ return Strings::format("%s[%s]", this->depend.name, features);
+ }
+
+ std::vector<Dependency> expand_qualified_dependencies(const std::vector<std::string>& depends)
+ {
+ return Util::fmap(depends, [&](const std::string& depend_string) -> Dependency {
+ auto pos = depend_string.find(' ');
+ if (pos == std::string::npos) return Dependency::parse_dependency(depend_string, "");
+ // expect of the form "\w+ \[\w+\]"
+ Dependency dep;
+
+ dep.depend.name = depend_string.substr(0, pos);
+ if (depend_string.c_str()[pos + 1] != '(' || depend_string[depend_string.size() - 1] != ')')
+ {
+ // Error, but for now just slurp the entire string.
+ return Dependency::parse_dependency(depend_string, "");
+ }
+ dep.qualifier = depend_string.substr(pos + 2, depend_string.size() - pos - 3);
+ return dep;
+ });
+ }
+
+ std::vector<std::string> filter_dependencies(const std::vector<vcpkg::Dependency>& deps, const Triplet& t)
+ {
+ 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;
+ }))
+ {
+ ret.emplace_back(dep.name());
+ }
+ }
+ return ret;
+ }
+
+ std::vector<FeatureSpec> filter_dependencies_to_specs(const std::vector<Dependency>& deps, const Triplet& t)
+ {
+ return FeatureSpec::from_strings_and_triplet(filter_dependencies(deps, t), t);
+ }
+
+ std::string to_string(const Dependency& dep) { return dep.name(); }
+
+ ExpectedT<Supports, std::vector<std::string>> Supports::parse(const std::vector<std::string>& strs)
+ {
+ Supports ret;
+ std::vector<std::string> unrecognized;
+
+ for (auto&& str : strs)
+ {
+ if (str == "x64")
+ ret.architectures.push_back(Architecture::X64);
+ else if (str == "x86")
+ ret.architectures.push_back(Architecture::X86);
+ else if (str == "arm")
+ ret.architectures.push_back(Architecture::ARM);
+ else if (str == "windows")
+ ret.platforms.push_back(Platform::WINDOWS);
+ else if (str == "uwp")
+ ret.platforms.push_back(Platform::UWP);
+ else if (str == "v140")
+ ret.toolsets.push_back(ToolsetVersion::V140);
+ else if (str == "v141")
+ ret.toolsets.push_back(ToolsetVersion::V141);
+ else if (str == "crt-static")
+ ret.crt_linkages.push_back(Linkage::STATIC);
+ else if (str == "crt-dynamic")
+ ret.crt_linkages.push_back(Linkage::DYNAMIC);
+ else
+ unrecognized.push_back(str);
+ }
+
+ if (unrecognized.empty())
+ return std::move(ret);
+ else
+ return std::move(unrecognized);
+ }
+
+ bool Supports::is_supported(Architecture arch, Platform plat, Linkage crt, ToolsetVersion tools)
+ {
+ const auto is_in_or_empty = [](auto v, auto&& c) -> bool { return c.empty() || c.end() != Util::find(c, v); };
+ if (!is_in_or_empty(arch, architectures)) return false;
+ if (!is_in_or_empty(plat, platforms)) return false;
+ if (!is_in_or_empty(crt, crt_linkages)) return false;
+ if (!is_in_or_empty(tools, toolsets)) return false;
+ return true;
+ }
+}
diff --git a/toolsrc/src/vcpkg/statusparagraph.cpp b/toolsrc/src/vcpkg/statusparagraph.cpp
new file mode 100644
index 000000000..462d8d8ed
--- /dev/null
+++ b/toolsrc/src/vcpkg/statusparagraph.cpp
@@ -0,0 +1,128 @@
+#include "pch.h"
+
+#include <vcpkg/base/util.h>
+#include <vcpkg/statusparagraph.h>
+
+using namespace vcpkg::Parse;
+
+namespace vcpkg
+{
+ namespace BinaryParagraphRequiredField
+ {
+ static const std::string STATUS = "Status";
+ }
+
+ StatusParagraph::StatusParagraph() noexcept : want(Want::ERROR_STATE), state(InstallState::ERROR_STATE) {}
+
+ void serialize(const StatusParagraph& pgh, std::string& out_str)
+ {
+ serialize(pgh.package, out_str);
+ out_str.append("Status: ")
+ .append(to_string(pgh.want))
+ .append(" ok ")
+ .append(to_string(pgh.state))
+ .push_back('\n');
+ }
+
+ StatusParagraph::StatusParagraph(std::unordered_map<std::string, std::string>&& fields)
+ : want(Want::ERROR_STATE), state(InstallState::ERROR_STATE)
+ {
+ auto status_it = fields.find(BinaryParagraphRequiredField::STATUS);
+ Checks::check_exit(VCPKG_LINE_INFO, status_it != fields.end(), "Expected 'Status' field in status paragraph");
+ std::string status_field = std::move(status_it->second);
+ fields.erase(status_it);
+
+ this->package = BinaryParagraph(std::move(fields));
+
+ auto b = status_field.begin();
+ const auto mark = b;
+ const auto e = status_field.end();
+
+ // Todo: improve error handling
+ while (b != e && *b != ' ')
+ ++b;
+
+ want = [](const std::string& text) {
+ if (text == "unknown") return Want::UNKNOWN;
+ if (text == "install") return Want::INSTALL;
+ if (text == "hold") return Want::HOLD;
+ if (text == "deinstall") return Want::DEINSTALL;
+ if (text == "purge") return Want::PURGE;
+ return Want::ERROR_STATE;
+ }(std::string(mark, b));
+
+ if (std::distance(b, e) < 4) return;
+ b += 4;
+
+ state = [](const std::string& text) {
+ if (text == "not-installed") return InstallState::NOT_INSTALLED;
+ if (text == "installed") return InstallState::INSTALLED;
+ if (text == "half-installed") return InstallState::HALF_INSTALLED;
+ return InstallState::ERROR_STATE;
+ }(std::string(b, e));
+ }
+
+ std::string to_string(InstallState f)
+ {
+ switch (f)
+ {
+ case InstallState::HALF_INSTALLED: return "half-installed";
+ case InstallState::INSTALLED: return "installed";
+ case InstallState::NOT_INSTALLED: return "not-installed";
+ default: return "error";
+ }
+ }
+
+ std::string to_string(Want f)
+ {
+ switch (f)
+ {
+ case Want::DEINSTALL: return "deinstall";
+ case Want::HOLD: return "hold";
+ case Want::INSTALL: return "install";
+ case Want::PURGE: return "purge";
+ case Want::UNKNOWN: return "unknown";
+ default: return "error";
+ }
+ }
+ std::vector<PackageSpec> InstalledPackageView::dependencies() const
+ {
+ // accumulate all features in installed dependencies
+ // Todo: make this unneeded by collapsing all package dependencies into the core package
+ auto deps = Util::fmap_flatten(features, [](const StatusParagraph* pgh) -> std::vector<std::string> const& {
+ return pgh->package.depends;
+ });
+
+ // Add the core paragraph dependencies to the list
+ deps.insert(deps.end(), core->package.depends.begin(), core->package.depends.end());
+
+ auto&& l_spec = spec();
+
+ // <hack>
+ // This is a hack to work around existing installations that put featurespecs into binary packages
+ // (example: curl[core])
+ for (auto&& dep : deps)
+ {
+ dep.erase(std::find(dep.begin(), dep.end(), '['), dep.end());
+ }
+ Util::unstable_keep_if(deps, [&](auto&& e) { return e != l_spec.name(); });
+ // </hack>
+ Util::sort_unique_erase(deps);
+
+ return Util::fmap(deps, [&](const std::string& dep) -> PackageSpec {
+ auto maybe_dependency_spec = PackageSpec::from_name_and_triplet(dep, l_spec.triplet());
+ if (auto dependency_spec = maybe_dependency_spec.get())
+ {
+ return std::move(*dependency_spec);
+ }
+
+ const PackageSpecParseResult error_type = maybe_dependency_spec.error();
+ Checks::exit_with_message(VCPKG_LINE_INFO,
+ "Invalid dependency [%s] in package [%s]\n"
+ "%s",
+ dep,
+ l_spec.name(),
+ vcpkg::to_string(error_type));
+ });
+ }
+}
diff --git a/toolsrc/src/vcpkg/statusparagraphs.cpp b/toolsrc/src/vcpkg/statusparagraphs.cpp
new file mode 100644
index 000000000..c642af59b
--- /dev/null
+++ b/toolsrc/src/vcpkg/statusparagraphs.cpp
@@ -0,0 +1,145 @@
+#include "pch.h"
+
+#include <vcpkg/base/checks.h>
+#include <vcpkg/statusparagraphs.h>
+
+namespace vcpkg
+{
+ StatusParagraphs::StatusParagraphs() = default;
+
+ StatusParagraphs::StatusParagraphs(std::vector<std::unique_ptr<StatusParagraph>>&& ps)
+ : paragraphs(std::move(ps)){};
+
+ std::vector<std::unique_ptr<StatusParagraph>*> StatusParagraphs::find_all(const std::string& name,
+ const Triplet& triplet)
+ {
+ std::vector<std::unique_ptr<StatusParagraph>*> spghs;
+ for (auto&& p : *this)
+ {
+ if (p->package.spec.name() == name && p->package.spec.triplet() == triplet)
+ {
+ if (p->package.feature.empty())
+ spghs.emplace(spghs.begin(), &p);
+ else
+ spghs.emplace_back(&p);
+ }
+ }
+ return spghs;
+ }
+
+ Optional<InstalledPackageView> StatusParagraphs::find_all_installed(const PackageSpec& spec) const
+ {
+ InstalledPackageView ipv;
+ for (auto&& p : *this)
+ {
+ if (p->package.spec.name() == spec.name() && p->package.spec.triplet() == spec.triplet() &&
+ p->is_installed())
+ {
+ if (p->package.feature.empty())
+ {
+ Checks::check_exit(VCPKG_LINE_INFO, ipv.core == nullptr);
+ ipv.core = p.get();
+ }
+ else
+ ipv.features.emplace_back(p.get());
+ }
+ }
+ if (ipv.core != nullptr)
+ return std::move(ipv);
+ else
+ return nullopt;
+ }
+
+ StatusParagraphs::iterator StatusParagraphs::find(const std::string& name,
+ const Triplet& triplet,
+ const std::string& feature)
+ {
+ if (feature == "core")
+ {
+ // The core feature maps to .feature == ""
+ return find(name, triplet, "");
+ }
+ return std::find_if(begin(), end(), [&](const std::unique_ptr<StatusParagraph>& pgh) {
+ const PackageSpec& spec = pgh->package.spec;
+ return spec.name() == name && spec.triplet() == triplet && pgh->package.feature == feature;
+ });
+ }
+
+ StatusParagraphs::const_iterator StatusParagraphs::find(const std::string& name,
+ const Triplet& triplet,
+ const std::string& feature) const
+ {
+ if (feature == "core")
+ {
+ // The core feature maps to .feature == ""
+ return find(name, triplet, "");
+ }
+ return std::find_if(begin(), end(), [&](const std::unique_ptr<StatusParagraph>& pgh) {
+ const PackageSpec& spec = pgh->package.spec;
+ return spec.name() == name && spec.triplet() == triplet && pgh->package.feature == feature;
+ });
+ }
+
+ StatusParagraphs::const_iterator StatusParagraphs::find_installed(const PackageSpec& spec) const
+ {
+ auto it = find(spec);
+ if (it != end() && (*it)->is_installed())
+ {
+ return it;
+ }
+ else
+ {
+ return end();
+ }
+ }
+
+ StatusParagraphs::const_iterator StatusParagraphs::find_installed(const FeatureSpec& spec) const
+ {
+ auto it = find(spec);
+ if (it != end() && (*it)->is_installed())
+ {
+ return it;
+ }
+ else
+ {
+ return end();
+ }
+ }
+
+ bool vcpkg::StatusParagraphs::is_installed(const PackageSpec& spec) const
+ {
+ auto it = find(spec);
+ return it != end() && (*it)->is_installed();
+ }
+
+ bool vcpkg::StatusParagraphs::is_installed(const FeatureSpec& spec) const
+ {
+ auto it = find(spec);
+ return it != end() && (*it)->is_installed();
+ }
+
+ StatusParagraphs::iterator StatusParagraphs::insert(std::unique_ptr<StatusParagraph> pgh)
+ {
+ Checks::check_exit(VCPKG_LINE_INFO, pgh != nullptr, "Inserted null paragraph");
+ const PackageSpec& spec = pgh->package.spec;
+ const auto ptr = find(spec.name(), spec.triplet(), pgh->package.feature);
+ if (ptr == end())
+ {
+ paragraphs.push_back(std::move(pgh));
+ return paragraphs.rbegin();
+ }
+
+ // consume data from provided pgh.
+ **ptr = std::move(*pgh);
+ return ptr;
+ }
+
+ void serialize(const StatusParagraphs& pghs, std::string& out_str)
+ {
+ for (auto& pgh : pghs.paragraphs)
+ {
+ serialize(*pgh, out_str);
+ out_str.push_back('\n');
+ }
+ }
+}
diff --git a/toolsrc/src/vcpkg/triplet.cpp b/toolsrc/src/vcpkg/triplet.cpp
new file mode 100644
index 000000000..c4ad3f690
--- /dev/null
+++ b/toolsrc/src/vcpkg/triplet.cpp
@@ -0,0 +1,57 @@
+#include "pch.h"
+
+#include <vcpkg/base/strings.h>
+#include <vcpkg/triplet.h>
+
+namespace vcpkg
+{
+ struct TripletInstance
+ {
+ TripletInstance(std::string&& s) : value(std::move(s)), hash(std::hash<std::string>()(value)) {}
+
+ const std::string value;
+ const size_t hash = 0;
+
+ bool operator==(const TripletInstance& o) const { return o.value == value; }
+ };
+ const TripletInstance Triplet::DEFAULT_INSTANCE({});
+}
+
+namespace std
+{
+ template<>
+ struct hash<vcpkg::TripletInstance>
+ {
+ size_t operator()(const vcpkg::TripletInstance& t) const { return t.hash; }
+ };
+}
+
+namespace vcpkg
+{
+ static std::unordered_set<TripletInstance> g_triplet_instances;
+
+ const Triplet Triplet::X86_WINDOWS = from_canonical_name("x86-windows");
+ const Triplet Triplet::X64_WINDOWS = from_canonical_name("x64-windows");
+ const Triplet Triplet::X86_UWP = from_canonical_name("x86-uwp");
+ const Triplet Triplet::X64_UWP = from_canonical_name("x64-uwp");
+ const Triplet Triplet::ARM_UWP = from_canonical_name("arm-uwp");
+ const Triplet Triplet::ARM64_UWP = from_canonical_name("arm64-uwp");
+ const Triplet Triplet::ARM_WINDOWS = from_canonical_name("arm-windows");
+ const Triplet Triplet::ARM64_WINDOWS = from_canonical_name("arm64-windows");
+
+ bool Triplet::operator==(const Triplet& other) const { return this->m_instance == other.m_instance; }
+
+ bool operator!=(const Triplet& left, const Triplet& right) { return !(left == right); }
+
+ Triplet Triplet::from_canonical_name(const std::string& triplet_as_string)
+ {
+ std::string s(Strings::ascii_to_lowercase(triplet_as_string));
+ const auto p = g_triplet_instances.emplace(std::move(s));
+ return &*p.first;
+ }
+
+ const std::string& Triplet::canonical_name() const { return this->m_instance->value; }
+
+ const std::string& Triplet::to_string() const { return this->canonical_name(); }
+ size_t Triplet::hash_code() const { return m_instance->hash; }
+}
diff --git a/toolsrc/src/vcpkg/update.cpp b/toolsrc/src/vcpkg/update.cpp
new file mode 100644
index 000000000..57259f952
--- /dev/null
+++ b/toolsrc/src/vcpkg/update.cpp
@@ -0,0 +1,86 @@
+#include "pch.h"
+
+#include <vcpkg/base/system.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/help.h>
+#include <vcpkg/paragraphs.h>
+#include <vcpkg/update.h>
+#include <vcpkg/vcpkglib.h>
+
+namespace vcpkg::Update
+{
+ bool OutdatedPackage::compare_by_name(const OutdatedPackage& left, const OutdatedPackage& right)
+ {
+ return left.spec.name() < right.spec.name();
+ }
+
+ std::vector<OutdatedPackage> find_outdated_packages(const Dependencies::PortFileProvider& provider,
+ const StatusParagraphs& status_db)
+ {
+ auto installed_packages = get_installed_ports(status_db);
+
+ std::vector<OutdatedPackage> output;
+ for (auto&& ipv : installed_packages)
+ {
+ const auto& pgh = ipv.core;
+ auto maybe_scf = provider.get_control_file(pgh->package.spec.name());
+ if (auto p_scf = maybe_scf.get())
+ {
+ auto&& port_version = p_scf->core_paragraph->version;
+ auto&& installed_version = pgh->package.version;
+ if (installed_version != port_version)
+ {
+ output.push_back({pgh->package.spec, VersionDiff(installed_version, port_version)});
+ }
+ }
+ else
+ {
+ // No portfile available
+ }
+ }
+
+ return output;
+ }
+
+ const CommandStructure COMMAND_STRUCTURE = {
+ Help::create_example_string("update"),
+ 0,
+ 0,
+ {},
+ nullptr,
+ };
+
+ void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
+ {
+ args.parse_arguments(COMMAND_STRUCTURE);
+ System::println("Using local portfile versions. To update the local portfiles, use `git pull`.");
+
+ const StatusParagraphs status_db = database_load_check(paths);
+
+ Dependencies::PathsPortFileProvider provider(paths);
+
+ const auto outdated_packages = SortedVector<OutdatedPackage>(find_outdated_packages(provider, status_db),
+ &OutdatedPackage::compare_by_name);
+
+ if (outdated_packages.empty())
+ {
+ System::println("No packages need updating.");
+ }
+ else
+ {
+ System::println("The following packages differ from their port versions:");
+ for (auto&& package : outdated_packages)
+ {
+ System::println(" %-32s %s", package.spec, package.version_diff.to_string());
+ }
+ System::println("\n"
+ "To update these packages and all dependencies, run\n"
+ " .\\vcpkg upgrade\n"
+ "\n"
+ "To only remove outdated packages, run\n"
+ " .\\vcpkg remove --outdated\n");
+ }
+
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+}
diff --git a/toolsrc/src/vcpkg/userconfig.cpp b/toolsrc/src/vcpkg/userconfig.cpp
new file mode 100644
index 000000000..4945fdaaa
--- /dev/null
+++ b/toolsrc/src/vcpkg/userconfig.cpp
@@ -0,0 +1,102 @@
+#include "pch.h"
+
+#include <vcpkg/base/files.h>
+#include <vcpkg/base/lazy.h>
+#include <vcpkg/paragraphs.h>
+#include <vcpkg/userconfig.h>
+
+#if defined(_WIN32)
+namespace
+{
+ static vcpkg::Lazy<fs::path> s_localappdata;
+
+ static const fs::path& get_localappdata()
+ {
+ return s_localappdata.get_lazy([]() {
+ fs::path localappdata;
+ {
+ // Config path in AppDataLocal
+ wchar_t* localappdatapath = nullptr;
+ if (S_OK != SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &localappdatapath)) __fastfail(1);
+ localappdata = localappdatapath;
+ CoTaskMemFree(localappdatapath);
+ }
+ return localappdata;
+ });
+ }
+}
+#endif
+
+namespace vcpkg
+{
+ fs::path get_user_dir()
+ {
+#if defined(_WIN32)
+ return get_localappdata() / "vcpkg";
+#else
+ auto maybe_home = System::get_environment_variable("HOME");
+ return fs::path(maybe_home.value_or("/var")) / ".vcpkg";
+#endif
+ }
+
+ static fs::path get_config_path()
+ {
+ return get_user_dir() / "config";
+ }
+
+ UserConfig UserConfig::try_read_data(const Files::Filesystem& fs)
+ {
+ UserConfig ret;
+ try
+ {
+ auto maybe_pghs = Paragraphs::get_paragraphs(fs, get_config_path());
+ if (const auto p_pghs = maybe_pghs.get())
+ {
+ const auto& pghs = *p_pghs;
+
+ std::unordered_map<std::string, std::string> keys;
+ if (pghs.size() > 0) keys = pghs[0];
+
+ for (size_t x = 1; x < pghs.size(); ++x)
+ {
+ for (auto&& p : pghs[x])
+ keys.insert(p);
+ }
+
+ ret.user_id = keys["User-Id"];
+ ret.user_time = keys["User-Since"];
+ ret.user_mac = keys["Mac-Hash"];
+ ret.last_completed_survey = keys["Survey-Completed"];
+ }
+ }
+ catch (...)
+ {
+ }
+
+ return ret;
+ }
+
+ void UserConfig::try_write_data(Files::Filesystem& fs) const
+ {
+ try
+ {
+ auto config_path = get_config_path();
+ auto config_dir = config_path.parent_path();
+ std::error_code ec;
+ fs.create_directory(config_dir, ec);
+ fs.write_contents(config_path,
+ Strings::format("User-Id: %s\n"
+ "User-Since: %s\n"
+ "Mac-Hash: %s\n"
+ "Survey-Completed: %s\n",
+ user_id,
+ user_time,
+ user_mac,
+ last_completed_survey),
+ ec);
+ }
+ catch (...)
+ {
+ }
+ }
+}
diff --git a/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp b/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp
new file mode 100644
index 000000000..8909e1552
--- /dev/null
+++ b/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp
@@ -0,0 +1,296 @@
+#include "pch.h"
+
+#include <vcpkg/base/system.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/globalstate.h>
+#include <vcpkg/help.h>
+#include <vcpkg/metrics.h>
+#include <vcpkg/vcpkgcmdarguments.h>
+
+namespace vcpkg
+{
+ static void parse_value(const std::string* arg_begin,
+ const std::string* arg_end,
+ const std::string& option_name,
+ std::unique_ptr<std::string>& option_field)
+ {
+ if (arg_begin == arg_end)
+ {
+ System::println(System::Color::error, "Error: expected value after %s", option_name);
+ Metrics::g_metrics.lock()->track_property("error", "error option name");
+ Help::print_usage();
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+
+ if (option_field != nullptr)
+ {
+ System::println(System::Color::error, "Error: %s specified multiple times", option_name);
+ Metrics::g_metrics.lock()->track_property("error", "error option specified multiple times");
+ Help::print_usage();
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+
+ option_field = std::make_unique<std::string>(*arg_begin);
+ }
+
+ static void parse_switch(bool new_setting, const std::string& option_name, Optional<bool>& option_field)
+ {
+ if (option_field && option_field != new_setting)
+ {
+ System::println(System::Color::error, "Error: conflicting values specified for --%s", option_name);
+ Metrics::g_metrics.lock()->track_property("error", "error conflicting switches");
+ Help::print_usage();
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+ option_field = new_setting;
+ }
+
+ VcpkgCmdArguments VcpkgCmdArguments::create_from_command_line(const int argc,
+ const CommandLineCharType* const* const argv)
+ {
+ std::vector<std::string> v;
+ for (int i = 1; i < argc; ++i)
+ {
+#if defined(_WIN32)
+ v.push_back(Strings::to_utf8(argv[i]));
+#else
+ v.push_back(argv[i]);
+#endif
+ }
+
+ return VcpkgCmdArguments::create_from_arg_sequence(v.data(), v.data() + v.size());
+ }
+
+ VcpkgCmdArguments VcpkgCmdArguments::create_from_arg_sequence(const std::string* arg_begin,
+ const std::string* arg_end)
+ {
+ VcpkgCmdArguments args;
+
+ for (; arg_begin != arg_end; ++arg_begin)
+ {
+ std::string arg = *arg_begin;
+
+ if (arg.empty())
+ {
+ continue;
+ }
+
+ if (arg[0] == '-' && arg[1] != '-')
+ {
+ Metrics::g_metrics.lock()->track_property("error", "error short options are not supported");
+ Checks::exit_with_message(VCPKG_LINE_INFO, "Error: short options are not supported: %s", arg);
+ }
+
+ if (arg[0] == '-' && arg[1] == '-')
+ {
+ // make argument case insensitive
+ auto& f = std::use_facet<std::ctype<char>>(std::locale());
+ f.tolower(&arg[0], &arg[0] + arg.size());
+ // command switch
+ if (arg == "--vcpkg-root")
+ {
+ ++arg_begin;
+ parse_value(arg_begin, arg_end, "--vcpkg-root", args.vcpkg_root_dir);
+ continue;
+ }
+ if (arg == "--triplet")
+ {
+ ++arg_begin;
+ parse_value(arg_begin, arg_end, "--triplet", args.triplet);
+ continue;
+ }
+ if (arg == "--debug")
+ {
+ parse_switch(true, "debug", args.debug);
+ continue;
+ }
+ if (arg == "--sendmetrics")
+ {
+ parse_switch(true, "sendmetrics", args.sendmetrics);
+ continue;
+ }
+ if (arg == "--printmetrics")
+ {
+ parse_switch(true, "printmetrics", args.printmetrics);
+ continue;
+ }
+ if (arg == "--no-sendmetrics")
+ {
+ parse_switch(false, "sendmetrics", args.sendmetrics);
+ continue;
+ }
+ if (arg == "--no-printmetrics")
+ {
+ parse_switch(false, "printmetrics", args.printmetrics);
+ continue;
+ }
+ if (arg == "--featurepackages")
+ {
+ parse_switch(true, "featurepackages", args.featurepackages);
+ continue;
+ }
+ if (arg == "--no-featurepackages")
+ {
+ parse_switch(false, "featurepackages", args.featurepackages);
+ continue;
+ }
+ if (arg == "--binarycaching")
+ {
+ parse_switch(true, "binarycaching", args.binarycaching);
+ continue;
+ }
+ if (arg == "--no-binarycaching")
+ {
+ parse_switch(false, "binarycaching", args.binarycaching);
+ continue;
+ }
+
+ const auto eq_pos = arg.find('=');
+ if (eq_pos != std::string::npos)
+ {
+ args.optional_command_arguments.emplace(arg.substr(0, eq_pos), arg.substr(eq_pos + 1));
+ }
+ else
+ {
+ args.optional_command_arguments.emplace(arg, nullopt);
+ }
+ continue;
+ }
+
+ if (args.command.empty())
+ {
+ args.command = arg;
+ }
+ else
+ {
+ args.command_arguments.push_back(arg);
+ }
+ }
+
+ return args;
+ }
+
+ ParsedArguments VcpkgCmdArguments::parse_arguments(const CommandStructure& command_structure) const
+ {
+ bool failed = false;
+ ParsedArguments output;
+
+ const size_t actual_arg_count = command_arguments.size();
+
+ if (command_structure.minimum_arity == command_structure.maximum_arity)
+ {
+ if (actual_arg_count != command_structure.minimum_arity)
+ {
+ System::println(System::Color::error,
+ "Error: '%s' requires %u arguments, but %u were provided.",
+ this->command,
+ command_structure.minimum_arity,
+ actual_arg_count);
+ failed = true;
+ }
+ }
+ else
+ {
+ if (actual_arg_count < command_structure.minimum_arity)
+ {
+ System::println(System::Color::error,
+ "Error: '%s' requires at least %u arguments, but %u were provided",
+ this->command,
+ command_structure.minimum_arity,
+ actual_arg_count);
+ failed = true;
+ }
+ if (actual_arg_count > command_structure.maximum_arity)
+ {
+ System::println(System::Color::error,
+ "Error: '%s' requires at most %u arguments, but %u were provided",
+ this->command,
+ command_structure.maximum_arity,
+ actual_arg_count);
+ failed = true;
+ }
+ }
+
+ auto options_copy = this->optional_command_arguments;
+ for (auto&& option : command_structure.options.switches)
+ {
+ const auto it = options_copy.find(option.name);
+ if (it != options_copy.end())
+ {
+ if (it->second.has_value())
+ {
+ // Having a string value indicates it was passed like '--a=xyz'
+ System::println(
+ System::Color::error, "Error: The option '%s' does not accept an argument.", option.name);
+ failed = true;
+ }
+ else
+ {
+ output.switches.insert(option.name);
+ options_copy.erase(it);
+ }
+ }
+ }
+
+ for (auto&& option : command_structure.options.settings)
+ {
+ const auto it = options_copy.find(option.name);
+ if (it != options_copy.end())
+ {
+ if (!it->second.has_value())
+ {
+ // Not having a string value indicates it was passed like '--a'
+ System::println(
+ System::Color::error, "Error: The option '%s' must be passed an argument.", option.name);
+ failed = true;
+ }
+ else
+ {
+ output.settings.emplace(option.name, it->second.value_or_exit(VCPKG_LINE_INFO));
+ options_copy.erase(it);
+ }
+ }
+ }
+
+ if (!options_copy.empty())
+ {
+ System::println(System::Color::error, "Unknown option(s) for command '%s':", this->command);
+ for (auto&& option : options_copy)
+ {
+ System::println(" %s", option.first);
+ }
+ System::println();
+ failed = true;
+ }
+
+ if (failed)
+ {
+ display_usage(command_structure);
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+
+ return output;
+ }
+
+ void display_usage(const CommandStructure& command_structure)
+ {
+ if (!command_structure.example_text.empty())
+ {
+ System::println("%s", command_structure.example_text);
+ }
+
+ System::println("Options:");
+ for (auto&& option : command_structure.options.switches)
+ {
+ System::println(" %-40s %s", option.name, option.short_help_text);
+ }
+ for (auto&& option : command_structure.options.settings)
+ {
+ System::println(" %-40s %s", (option.name + "=..."), option.short_help_text);
+ }
+ System::println(" %-40s %s", "--triplet <t>", "Set the default triplet for unqualified packages");
+ System::println(" %-40s %s",
+ "--vcpkg-root <path>",
+ "Specify the vcpkg directory to use instead of current directory or tool directory");
+ }
+}
diff --git a/toolsrc/src/vcpkglib.cpp b/toolsrc/src/vcpkg/vcpkglib.cpp
index 15130f77e..c8e95dab1 100644
--- a/toolsrc/src/vcpkglib.cpp
+++ b/toolsrc/src/vcpkg/vcpkglib.cpp
@@ -1,16 +1,14 @@
#include "pch.h"
-#include "Paragraphs.h"
-#include "metrics.h"
-#include "vcpkg_Files.h"
-#include "vcpkg_Strings.h"
-#include "vcpkg_Util.h"
-#include "vcpkglib.h"
+#include <vcpkg/base/files.h>
+#include <vcpkg/base/strings.h>
+#include <vcpkg/base/util.h>
+#include <vcpkg/metrics.h>
+#include <vcpkg/paragraphs.h>
+#include <vcpkg/vcpkglib.h>
namespace vcpkg
{
- bool g_debugging = false;
-
static StatusParagraphs load_current_database(Files::Filesystem& fs,
const fs::path& vcpkg_dir_status_file,
const fs::path& vcpkg_dir_status_file_old)
@@ -31,7 +29,7 @@ namespace vcpkg
std::vector<std::unique_ptr<StatusParagraph>> status_pghs;
for (auto&& p : pghs)
{
- status_pghs.push_back(std::make_unique<StatusParagraph>(p));
+ status_pghs.push_back(std::make_unique<StatusParagraph>(std::move(p)));
}
return StatusParagraphs(std::move(status_pghs));
@@ -41,7 +39,7 @@ namespace vcpkg
{
auto& fs = paths.get_filesystem();
- auto updates_dir = paths.vcpkg_dir_updates;
+ const auto updates_dir = paths.vcpkg_dir_updates;
std::error_code ec;
fs.create_directory(paths.installed, ec);
@@ -56,6 +54,7 @@ namespace vcpkg
StatusParagraphs current_status_db = load_current_database(fs, status_file, status_file_old);
auto update_files = fs.get_files_non_recursive(updates_dir);
+ Util::sort(update_files);
if (update_files.empty())
{
// updates directory is empty, control file is up-to-date.
@@ -69,7 +68,7 @@ namespace vcpkg
auto pghs = Paragraphs::get_paragraphs(fs, file).value_or_exit(VCPKG_LINE_INFO);
for (auto&& p : pghs)
{
- current_status_db.insert(std::make_unique<StatusParagraph>(p));
+ current_status_db.insert(std::make_unique<StatusParagraph>(std::move(p)));
}
}
@@ -92,9 +91,9 @@ namespace vcpkg
static int update_id = 0;
auto& fs = paths.get_filesystem();
- auto my_update_id = update_id++;
- auto tmp_update_filename = paths.vcpkg_dir_updates / "incomplete";
- auto update_filename = paths.vcpkg_dir_updates / std::to_string(my_update_id);
+ const auto my_update_id = update_id++;
+ const auto tmp_update_filename = paths.vcpkg_dir_updates / "incomplete";
+ const auto update_filename = paths.vcpkg_dir_updates / Strings::format("%010d", my_update_id);
fs.write_contents(tmp_update_filename, Strings::serialize(p));
fs.rename(tmp_update_filename, update_filename);
@@ -119,7 +118,7 @@ namespace vcpkg
if (!was_tracked)
{
was_tracked = true;
- Metrics::track_property("listfile", "update to new format");
+ Metrics::g_metrics.lock()->track_property("listfile", "update to new format");
}
// The files are sorted such that directories are placed just before the files they contain
@@ -170,16 +169,32 @@ namespace vcpkg
fs.rename(updated_listfile_path, listfile_path);
}
- std::vector<StatusParagraph*> get_installed_ports(const StatusParagraphs& status_db)
+ std::vector<InstalledPackageView> get_installed_ports(const StatusParagraphs& status_db)
{
- std::vector<StatusParagraph*> installed_packages;
+ std::map<PackageSpec, InstalledPackageView> ipv_map;
+
+ std::vector<InstalledPackageView> installed_packages;
for (auto&& pgh : status_db)
{
- if (pgh->state != InstallState::INSTALLED || pgh->want != Want::INSTALL) continue;
- installed_packages.push_back(pgh.get());
+ if (!pgh->is_installed()) continue;
+ auto& ipv = ipv_map[pgh->package.spec];
+ if (pgh->package.feature.empty())
+ {
+ ipv.core = pgh.get();
+ }
+ else
+ {
+ ipv.features.emplace_back(pgh.get());
+ }
}
- return installed_packages;
+ for (auto&& ipv : ipv_map)
+ Checks::check_exit(VCPKG_LINE_INFO,
+ ipv.second.core != nullptr,
+ "Database is corrupted: package %s has features but no core paragraph.",
+ ipv.first);
+
+ return Util::fmap(ipv_map, [](auto&& p) -> InstalledPackageView { return std::move(p.second); });
}
std::vector<StatusParagraphAndAssociatedFiles> get_installed_files(const VcpkgPaths& paths,
@@ -191,7 +206,7 @@ namespace vcpkg
for (const std::unique_ptr<StatusParagraph>& pgh : status_db)
{
- if (pgh->state != InstallState::INSTALLED)
+ if (!pgh->is_installed() || !pgh->package.feature.empty())
{
continue;
}
@@ -214,29 +229,10 @@ namespace vcpkg
return installed_files;
}
- CMakeVariable::CMakeVariable(const CWStringView varname, const wchar_t* varvalue)
- : s(Strings::wformat(LR"("-D%s=%s")", varname, varvalue))
- {
- }
- CMakeVariable::CMakeVariable(const CWStringView varname, const std::string& varvalue)
- : CMakeVariable(varname, Strings::to_utf16(varvalue).c_str())
- {
- }
- CMakeVariable::CMakeVariable(const CWStringView varname, const std::wstring& varvalue)
- : CMakeVariable(varname, varvalue.c_str())
- {
- }
- CMakeVariable::CMakeVariable(const CWStringView varname, const fs::path& path)
- : CMakeVariable(varname, path.generic_wstring())
- {
- }
-
- std::wstring make_cmake_cmd(const fs::path& cmake_exe,
- const fs::path& cmake_script,
- const std::vector<CMakeVariable>& pass_variables)
+ std::string shorten_text(const std::string& desc, const size_t length)
{
- std::wstring cmd_cmake_pass_variables = Strings::join(L" ", pass_variables, [](auto&& v) { return v.s; });
- return Strings::wformat(
- LR"("%s" %s -P "%s")", cmake_exe.native(), cmd_cmake_pass_variables, cmake_script.generic_wstring());
+ Checks::check_exit(VCPKG_LINE_INFO, length >= 3);
+ auto simple_desc = std::regex_replace(desc, std::regex("\\s+"), " ");
+ return simple_desc.size() <= length ? simple_desc : simple_desc.substr(0, length - 3) + "...";
}
}
diff --git a/toolsrc/src/vcpkg/vcpkgpaths.cpp b/toolsrc/src/vcpkg/vcpkgpaths.cpp
new file mode 100644
index 000000000..9b74bea74
--- /dev/null
+++ b/toolsrc/src/vcpkg/vcpkgpaths.cpp
@@ -0,0 +1,170 @@
+#include "pch.h"
+
+#include <vcpkg/base/expected.h>
+#include <vcpkg/base/files.h>
+#include <vcpkg/base/system.h>
+#include <vcpkg/base/util.h>
+#include <vcpkg/build.h>
+#include <vcpkg/commands.h>
+#include <vcpkg/metrics.h>
+#include <vcpkg/packagespec.h>
+#include <vcpkg/vcpkgpaths.h>
+
+namespace vcpkg
+{
+ Expected<VcpkgPaths> VcpkgPaths::create(const fs::path& vcpkg_root_dir, const std::string& default_vs_path)
+ {
+ std::error_code ec;
+ const fs::path canonical_vcpkg_root_dir = fs::stdfs::canonical(vcpkg_root_dir, ec);
+ if (ec)
+ {
+ return ec;
+ }
+
+ VcpkgPaths paths;
+ paths.root = canonical_vcpkg_root_dir;
+ paths.default_vs_path = default_vs_path;
+
+ if (paths.root.empty())
+ {
+ Metrics::g_metrics.lock()->track_property("error", "Invalid vcpkg root directory");
+ Checks::exit_with_message(VCPKG_LINE_INFO, "Invalid vcpkg root directory: %s", paths.root.string());
+ }
+
+ paths.packages = paths.root / "packages";
+ paths.buildtrees = paths.root / "buildtrees";
+ paths.downloads = paths.root / "downloads";
+ paths.ports = paths.root / "ports";
+ paths.installed = paths.root / "installed";
+ paths.triplets = paths.root / "triplets";
+ paths.scripts = paths.root / "scripts";
+
+ paths.tools = paths.downloads / "tools";
+ paths.buildsystems = paths.scripts / "buildsystems";
+ paths.buildsystems_msbuild_targets = paths.buildsystems / "msbuild" / "vcpkg.targets";
+
+ paths.vcpkg_dir = paths.installed / "vcpkg";
+ paths.vcpkg_dir_status_file = paths.vcpkg_dir / "status";
+ paths.vcpkg_dir_info = paths.vcpkg_dir / "info";
+ paths.vcpkg_dir_updates = paths.vcpkg_dir / "updates";
+
+ paths.ports_cmake = paths.scripts / "ports.cmake";
+
+ return paths;
+ }
+
+ fs::path VcpkgPaths::package_dir(const PackageSpec& spec) const { return this->packages / spec.dir(); }
+
+ fs::path VcpkgPaths::port_dir(const PackageSpec& spec) const { return this->ports / spec.name(); }
+ fs::path VcpkgPaths::port_dir(const std::string& name) const { return this->ports / name; }
+
+ fs::path VcpkgPaths::build_info_file_path(const PackageSpec& spec) const
+ {
+ return this->package_dir(spec) / "BUILD_INFO";
+ }
+
+ fs::path VcpkgPaths::listfile_path(const BinaryParagraph& pgh) const
+ {
+ return this->vcpkg_dir_info / (pgh.fullstem() + ".list");
+ }
+
+ const std::vector<std::string>& VcpkgPaths::get_available_triplets() const
+ {
+ return this->available_triplets.get_lazy([this]() -> std::vector<std::string> {
+ std::vector<std::string> output;
+ for (auto&& path : this->get_filesystem().get_files_non_recursive(this->triplets))
+ {
+ output.push_back(path.stem().filename().string());
+ }
+ Util::sort(output);
+
+ return output;
+ });
+ }
+
+ bool VcpkgPaths::is_valid_triplet(const Triplet& t) const
+ {
+ const auto it = Util::find_if(this->get_available_triplets(), [&](auto&& available_triplet) {
+ return t.canonical_name() == available_triplet;
+ });
+ return it != this->get_available_triplets().cend();
+ }
+
+ const fs::path& VcpkgPaths::get_tool_exe(const std::string& tool) const
+ {
+ return this->tool_paths.get_lazy(tool, [&]() { return Commands::Fetch::get_tool_path(*this, tool); });
+ }
+
+ const Toolset& VcpkgPaths::get_toolset(const Build::PreBuildInfo& prebuildinfo) const
+ {
+ if (prebuildinfo.external_toolchain_file ||
+ (!prebuildinfo.cmake_system_name.empty() && prebuildinfo.cmake_system_name != "WindowsStore"))
+ {
+ static Toolset external_toolset = []() -> Toolset {
+ Toolset ret;
+ ret.dumpbin = "";
+ ret.supported_architectures = {
+ ToolsetArchOption{"", System::get_host_processor(), System::get_host_processor()}};
+ ret.vcvarsall = "";
+ ret.vcvarsall_options = {};
+ ret.version = "external";
+ ret.visual_studio_root_path = "";
+ return ret;
+ }();
+ return external_toolset;
+ }
+
+#if !defined(_WIN32)
+ Checks::exit_with_message(VCPKG_LINE_INFO, "Cannot build windows triplets from non-windows.");
+#else
+ const std::vector<Toolset>& vs_toolsets = this->toolsets.get_lazy(
+ [this]() { return Commands::Fetch::find_toolset_instances_preferred_first(*this); });
+
+ std::vector<const Toolset*> candidates = Util::element_pointers(vs_toolsets);
+ const auto tsv = prebuildinfo.platform_toolset.get();
+ auto vsp = prebuildinfo.visual_studio_path.get();
+ if (!vsp && !default_vs_path.empty())
+ {
+ vsp = &default_vs_path;
+ }
+
+ if (tsv && vsp)
+ {
+ Util::stable_keep_if(
+ candidates, [&](const Toolset* t) { return *tsv == t->version && *vsp == t->visual_studio_root_path; });
+ Checks::check_exit(VCPKG_LINE_INFO,
+ !candidates.empty(),
+ "Could not find Visual Studio instance at %s with %s toolset.",
+ vsp->u8string(),
+ *tsv);
+
+ Checks::check_exit(VCPKG_LINE_INFO, candidates.size() == 1);
+ return *candidates.back();
+ }
+
+ if (tsv)
+ {
+ Util::stable_keep_if(candidates, [&](const Toolset* t) { return *tsv == t->version; });
+ Checks::check_exit(
+ VCPKG_LINE_INFO, !candidates.empty(), "Could not find Visual Studio instance with %s toolset.", *tsv);
+ }
+
+ if (vsp)
+ {
+ const fs::path vs_root_path = *vsp;
+ Util::stable_keep_if(candidates,
+ [&](const Toolset* t) { return vs_root_path == t->visual_studio_root_path; });
+ Checks::check_exit(VCPKG_LINE_INFO,
+ !candidates.empty(),
+ "Could not find Visual Studio instance at %s.",
+ vs_root_path.generic_string());
+ }
+
+ Checks::check_exit(VCPKG_LINE_INFO, !candidates.empty(), "No suitable Visual Studio instances were found");
+ return *candidates.front();
+
+#endif
+ }
+
+ Files::Filesystem& VcpkgPaths::get_filesystem() const { return Files::get_real_filesystem(); }
+}
diff --git a/toolsrc/src/vcpkg/versiont.cpp b/toolsrc/src/vcpkg/versiont.cpp
new file mode 100644
index 000000000..d20e6b577
--- /dev/null
+++ b/toolsrc/src/vcpkg/versiont.cpp
@@ -0,0 +1,23 @@
+#include "pch.h"
+
+#include <vcpkg/base/strings.h>
+#include <vcpkg/versiont.h>
+
+namespace vcpkg
+{
+ VersionT::VersionT() noexcept : value("0.0.0") {}
+ VersionT::VersionT(std::string&& value) : value(std::move(value)) {}
+ VersionT::VersionT(const std::string& value) : value(value) {}
+ const std::string& VersionT::to_string() const { return value; }
+ bool operator==(const VersionT& left, const VersionT& right) { return left.to_string() == right.to_string(); }
+ bool operator!=(const VersionT& left, const VersionT& right) { return left.to_string() != right.to_string(); }
+ std::string to_printf_arg(const VersionT& version) { return version.to_string(); }
+
+ VersionDiff::VersionDiff() noexcept : left(), right() {}
+ VersionDiff::VersionDiff(const VersionT& left, const VersionT& right) : left(left), right(right) {}
+
+ std::string VersionDiff::to_string() const
+ {
+ return Strings::format("%s -> %s", left.to_string(), right.to_string());
+ }
+}
diff --git a/toolsrc/src/vcpkg_Build.cpp b/toolsrc/src/vcpkg_Build.cpp
deleted file mode 100644
index 6605fa4fb..000000000
--- a/toolsrc/src/vcpkg_Build.cpp
+++ /dev/null
@@ -1,337 +0,0 @@
-#include "pch.h"
-
-#include "Paragraphs.h"
-#include "PostBuildLint.h"
-#include "metrics.h"
-#include "vcpkg_Build.h"
-#include "vcpkg_Checks.h"
-#include "vcpkg_Chrono.h"
-#include "vcpkg_Commands.h"
-#include "vcpkg_Enums.h"
-#include "vcpkg_System.h"
-#include "vcpkg_optional.h"
-#include "vcpkglib.h"
-#include "vcpkglib_helpers.h"
-
-using vcpkg::PostBuildLint::BuildPolicies;
-namespace BuildPoliciesC = vcpkg::PostBuildLint::BuildPoliciesC;
-using vcpkg::PostBuildLint::LinkageType;
-namespace LinkageTypeC = vcpkg::PostBuildLint::LinkageTypeC;
-
-namespace vcpkg::Build
-{
- namespace BuildInfoRequiredField
- {
- static const std::string CRT_LINKAGE = "CRTLinkage";
- static const std::string LIBRARY_LINKAGE = "LibraryLinkage";
- }
-
- CWStringView to_vcvarsall_target(const std::string& cmake_system_name)
- {
- if (cmake_system_name == "") return L"";
- if (cmake_system_name == "Windows") return L"";
- if (cmake_system_name == "WindowsStore") return L"store";
-
- Checks::exit_with_message(VCPKG_LINE_INFO, "Unsupported vcvarsall target %s", cmake_system_name);
- }
-
- CWStringView to_vcvarsall_toolchain(const std::string& target_architecture)
- {
- using CPU = System::CPUArchitecture;
-
- struct ArchOption
- {
- CWStringView name;
- CPU host_arch;
- CPU target_arch;
- };
-
- static constexpr ArchOption X86 = {L"x86", CPU::X86, CPU::X86};
- static constexpr ArchOption X86_X64 = {L"x86_x64", CPU::X86, CPU::X64};
- static constexpr ArchOption X86_ARM = {L"x86_arm", CPU::X86, CPU::ARM};
- static constexpr ArchOption X86_ARM64 = {L"x86_arm64", CPU::X86, CPU::ARM64};
-
- static constexpr ArchOption X64 = {L"amd64", CPU::X64, CPU::X64};
- static constexpr ArchOption X64_X86 = {L"amd64_x86", CPU::X64, CPU::X86};
- static constexpr ArchOption X64_ARM = {L"amd64_arm", CPU::X64, CPU::ARM};
- static constexpr ArchOption X64_ARM64 = {L"amd64_arm64", CPU::X64, CPU::ARM64};
-
- static constexpr std::array<ArchOption, 8> VALUES = {
- X86, X86_X64, X86_ARM, X86_ARM64, X64, X64_X86, X64_ARM, X64_ARM64};
-
- auto target_arch = System::to_cpu_architecture(target_architecture);
- auto host_arch = System::get_host_processor();
-
- for (auto&& value : VALUES)
- {
- if (target_arch == value.target_arch && host_arch == value.host_arch)
- {
- return value.name;
- }
- }
-
- Checks::exit_with_message(VCPKG_LINE_INFO, "Unsupported toolchain combination %s", target_architecture);
- }
-
- std::wstring make_build_env_cmd(const PreBuildInfo& pre_build_info, const Toolset& toolset)
- {
- const wchar_t* tonull = L" >nul";
- if (g_debugging)
- {
- tonull = L"";
- }
-
- auto arch = to_vcvarsall_toolchain(pre_build_info.target_architecture);
- auto target = to_vcvarsall_target(pre_build_info.cmake_system_name);
-
- return Strings::wformat(LR"("%s" %s %s %s 2>&1)", toolset.vcvarsall.native(), arch, target, tonull);
- }
-
- static void create_binary_control_file(const VcpkgPaths& paths,
- const SourceParagraph& source_paragraph,
- const Triplet& triplet,
- const BuildInfo& build_info)
- {
- BinaryParagraph bpgh = BinaryParagraph(source_paragraph, triplet);
- if (auto p_ver = build_info.version.get())
- {
- bpgh.version = *p_ver;
- }
- const fs::path binary_control_file = paths.packages / bpgh.dir() / "CONTROL";
- paths.get_filesystem().write_contents(binary_control_file, Strings::serialize(bpgh));
- }
-
- ExtendedBuildResult build_package(const VcpkgPaths& paths,
- const BuildPackageConfig& config,
- const StatusParagraphs& status_db)
- {
- const PackageSpec spec =
- PackageSpec::from_name_and_triplet(config.src.name, config.triplet).value_or_exit(VCPKG_LINE_INFO);
-
- const Triplet& triplet = config.triplet;
- {
- std::vector<PackageSpec> missing_specs;
- for (auto&& dep : filter_dependencies(config.src.depends, triplet))
- {
- if (status_db.find_installed(dep, triplet) == status_db.end())
- {
- missing_specs.push_back(
- PackageSpec::from_name_and_triplet(dep, triplet).value_or_exit(VCPKG_LINE_INFO));
- }
- }
- // Fail the build if any dependencies were missing
- if (!missing_specs.empty())
- {
- return {BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES, std::move(missing_specs)};
- }
- }
-
- const fs::path& cmake_exe_path = paths.get_cmake_exe();
- const fs::path& git_exe_path = paths.get_git_exe();
-
- const fs::path ports_cmake_script_path = paths.ports_cmake;
- const Toolset& toolset = paths.get_toolset();
- auto pre_build_info = PreBuildInfo::from_triplet_file(paths, triplet);
- const auto cmd_set_environment = make_build_env_cmd(pre_build_info, toolset);
-
- const std::wstring cmd_launch_cmake =
- make_cmake_cmd(cmake_exe_path,
- ports_cmake_script_path,
- {{L"CMD", L"BUILD"},
- {L"PORT", config.src.name},
- {L"CURRENT_PORT_DIR", config.port_dir / "/."},
- {L"TARGET_TRIPLET", triplet.canonical_name()},
- {L"VCPKG_PLATFORM_TOOLSET", toolset.version},
- {L"VCPKG_USE_HEAD_VERSION", config.use_head_version ? L"1" : L"0"},
- {L"_VCPKG_NO_DOWNLOADS", config.no_downloads ? L"1" : L"0"},
- {L"GIT", git_exe_path}});
-
- const std::wstring command = Strings::wformat(LR"(%s && %s)", cmd_set_environment, cmd_launch_cmake);
-
- const ElapsedTime timer = ElapsedTime::create_started();
-
- int return_code = System::cmd_execute_clean(command);
- auto buildtimeus = timer.microseconds();
- const auto spec_string = spec.to_string();
- Metrics::track_metric("buildtimeus-" + spec_string, buildtimeus);
-
- if (return_code != 0)
- {
- Metrics::track_property("error", "build failed");
- Metrics::track_property("build_error", spec_string);
- return {BuildResult::BUILD_FAILED, {}};
- }
-
- auto build_info = read_build_info(paths.get_filesystem(), paths.build_info_file_path(spec));
- const size_t error_count = PostBuildLint::perform_all_checks(spec, paths, pre_build_info, build_info);
-
- if (error_count != 0)
- {
- return {BuildResult::POST_BUILD_CHECKS_FAILED, {}};
- }
-
- create_binary_control_file(paths, config.src, triplet, build_info);
-
- // const fs::path port_buildtrees_dir = paths.buildtrees / spec.name;
- // delete_directory(port_buildtrees_dir);
-
- return {BuildResult::SUCCEEDED, {}};
- }
-
- const std::string& to_string(const BuildResult build_result)
- {
- static const std::string NULLVALUE_STRING = Enums::nullvalue_to_string("vcpkg::Commands::Build::BuildResult");
- static const std::string SUCCEEDED_STRING = "SUCCEEDED";
- static const std::string BUILD_FAILED_STRING = "BUILD_FAILED";
- static const std::string POST_BUILD_CHECKS_FAILED_STRING = "POST_BUILD_CHECKS_FAILED";
- static const std::string CASCADED_DUE_TO_MISSING_DEPENDENCIES_STRING = "CASCADED_DUE_TO_MISSING_DEPENDENCIES";
-
- switch (build_result)
- {
- case BuildResult::NULLVALUE: return NULLVALUE_STRING;
- case BuildResult::SUCCEEDED: return SUCCEEDED_STRING;
- case BuildResult::BUILD_FAILED: return BUILD_FAILED_STRING;
- case BuildResult::POST_BUILD_CHECKS_FAILED: return POST_BUILD_CHECKS_FAILED_STRING;
- case BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES: return CASCADED_DUE_TO_MISSING_DEPENDENCIES_STRING;
- default: Checks::unreachable(VCPKG_LINE_INFO);
- }
- }
-
- std::string create_error_message(const BuildResult build_result, const PackageSpec& spec)
- {
- return Strings::format("Error: Building package %s failed with: %s", spec, Build::to_string(build_result));
- }
-
- std::string create_user_troubleshooting_message(const PackageSpec& spec)
- {
- return Strings::format("Please ensure you're using the latest portfiles with `.\\vcpkg update`, then\n"
- "submit an issue at https://github.com/Microsoft/vcpkg/issues including:\n"
- " Package: %s\n"
- " Vcpkg version: %s\n"
- "\n"
- "Additionally, attach any relevant sections from the log files above.",
- spec,
- Commands::Version::version());
- }
-
- BuildInfo BuildInfo::create(std::unordered_map<std::string, std::string> pgh)
- {
- BuildInfo build_info;
- const std::string crt_linkage_as_string =
- details::remove_required_field(&pgh, BuildInfoRequiredField::CRT_LINKAGE);
- build_info.crt_linkage = LinkageType::value_of(crt_linkage_as_string);
- Checks::check_exit(VCPKG_LINE_INFO,
- build_info.crt_linkage != LinkageTypeC::NULLVALUE,
- "Invalid crt linkage type: [%s]",
- crt_linkage_as_string);
-
- const std::string library_linkage_as_string =
- details::remove_required_field(&pgh, BuildInfoRequiredField::LIBRARY_LINKAGE);
- build_info.library_linkage = LinkageType::value_of(library_linkage_as_string);
- Checks::check_exit(VCPKG_LINE_INFO,
- build_info.library_linkage != LinkageTypeC::NULLVALUE,
- "Invalid library linkage type: [%s]",
- library_linkage_as_string);
-
- auto it_version = pgh.find("Version");
- if (it_version != pgh.end())
- {
- build_info.version = it_version->second;
- pgh.erase(it_version);
- }
-
- // The remaining entries are policies
- for (const std::unordered_map<std::string, std::string>::value_type& p : pgh)
- {
- const BuildPolicies policy = BuildPolicies::parse(p.first);
- Checks::check_exit(
- VCPKG_LINE_INFO, policy != BuildPoliciesC::NULLVALUE, "Unknown policy found: %s", p.first);
- if (p.second == "enabled")
- build_info.policies.emplace(policy, true);
- else if (p.second == "disabled")
- build_info.policies.emplace(policy, false);
- else
- Checks::exit_with_message(VCPKG_LINE_INFO, "Unknown setting for policy '%s': %s", p.first, p.second);
- }
-
- return build_info;
- }
-
- 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);
- Checks::check_exit(VCPKG_LINE_INFO, pghs.get() != nullptr, "Invalid BUILD_INFO file for package");
- return BuildInfo::create(*pghs.get());
- }
-
- PreBuildInfo PreBuildInfo::from_triplet_file(const VcpkgPaths& paths, const Triplet& triplet)
- {
- static constexpr CStringView FLAG_GUID = "c35112b6-d1ba-415b-aa5d-81de856ef8eb";
-
- const fs::path& cmake_exe_path = paths.get_cmake_exe();
- const fs::path ports_cmake_script_path = paths.scripts / "get_triplet_environment.cmake";
- const fs::path triplet_file_path = paths.triplets / (triplet.canonical_name() + ".cmake");
-
- const std::wstring cmd_launch_cmake = make_cmake_cmd(cmake_exe_path,
- ports_cmake_script_path,
- {
- {L"CMAKE_TRIPLET_FILE", triplet_file_path},
- });
-
- const std::wstring command = Strings::wformat(LR"(%s)", cmd_launch_cmake);
- auto ec_data = System::cmd_execute_and_capture_output(command);
- Checks::check_exit(VCPKG_LINE_INFO, ec_data.exit_code == 0);
-
- const std::vector<std::string> lines = Strings::split(ec_data.output, "\n");
-
- PreBuildInfo pre_build_info;
-
- auto e = lines.cend();
- auto cur = std::find(lines.cbegin(), e, FLAG_GUID);
- if (cur != e) ++cur;
-
- for (; cur != e; ++cur)
- {
- auto&& line = *cur;
-
- const std::vector<std::string> s = Strings::split(line, "=");
- Checks::check_exit(VCPKG_LINE_INFO,
- s.size() == 1 || s.size() == 2,
- "Expected format is [VARIABLE_NAME=VARIABLE_VALUE], but was [%s]",
- line);
-
- const bool variable_with_no_value = s.size() == 1;
- const std::string variable_name = s.at(0);
- const std::string variable_value = variable_with_no_value ? "" : s.at(1);
-
- 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;
- continue;
- }
-
- Checks::exit_with_message(VCPKG_LINE_INFO, "Unknown variable name %s", line);
- }
-
- return pre_build_info;
- }
-}
diff --git a/toolsrc/src/vcpkg_Checks.cpp b/toolsrc/src/vcpkg_Checks.cpp
deleted file mode 100644
index 2674b889a..000000000
--- a/toolsrc/src/vcpkg_Checks.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-#include "pch.h"
-
-#include "vcpkg_Checks.h"
-#include "vcpkg_System.h"
-#include "vcpkglib.h"
-
-namespace vcpkg::Checks
-{
- [[noreturn]] void unreachable(const LineInfo& line_info)
- {
- System::println(System::Color::error, "Error: Unreachable code was reached");
- System::println(System::Color::error, line_info.to_string()); // Always print line_info here
-#ifndef NDEBUG
- std::abort();
-#else
- ::exit(EXIT_FAILURE);
-#endif
- }
-
- [[noreturn]] void exit_with_code(const LineInfo& line_info, const int exit_code)
- {
- Debug::println(System::Color::error, line_info.to_string());
- ::exit(exit_code);
- }
-
- [[noreturn]] void exit_with_message(const LineInfo& line_info, const CStringView errorMessage)
- {
- System::println(System::Color::error, errorMessage);
- exit_fail(line_info);
- }
-
- void check_exit(const LineInfo& line_info, bool expression)
- {
- if (!expression)
- {
- exit_with_message(line_info, "");
- }
- }
-
- void check_exit(const LineInfo& line_info, bool expression, const CStringView errorMessage)
- {
- if (!expression)
- {
- exit_with_message(line_info, errorMessage);
- }
- }
-}
diff --git a/toolsrc/src/vcpkg_Chrono.cpp b/toolsrc/src/vcpkg_Chrono.cpp
deleted file mode 100644
index d70f4a054..000000000
--- a/toolsrc/src/vcpkg_Chrono.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-#include "pch.h"
-
-#include "vcpkg_Checks.h"
-#include "vcpkg_Chrono.h"
-
-namespace vcpkg
-{
- static std::string format_time_userfriendly(const std::chrono::nanoseconds& nanos)
- {
- using std::chrono::hours;
- using std::chrono::minutes;
- using std::chrono::seconds;
- using std::chrono::milliseconds;
- using std::chrono::microseconds;
- using std::chrono::nanoseconds;
- using std::chrono::duration_cast;
-
- const double nanos_as_double = static_cast<double>(nanos.count());
-
- if (duration_cast<hours>(nanos) > hours())
- {
- auto t = nanos_as_double / duration_cast<nanoseconds>(hours(1)).count();
- return Strings::format("%.4g h", t);
- }
-
- if (duration_cast<minutes>(nanos) > minutes())
- {
- auto t = nanos_as_double / duration_cast<nanoseconds>(minutes(1)).count();
- return Strings::format("%.4g min", t);
- }
-
- if (duration_cast<seconds>(nanos) > seconds())
- {
- auto t = nanos_as_double / duration_cast<nanoseconds>(seconds(1)).count();
- return Strings::format("%.4g s", t);
- }
-
- if (duration_cast<milliseconds>(nanos) > milliseconds())
- {
- auto t = nanos_as_double / duration_cast<nanoseconds>(milliseconds(1)).count();
- return Strings::format("%.4g ms", t);
- }
-
- if (duration_cast<microseconds>(nanos) > microseconds())
- {
- auto t = nanos_as_double / duration_cast<nanoseconds>(microseconds(1)).count();
- return Strings::format("%.4g us", t);
- }
-
- return Strings::format("%.4g ns", nanos_as_double);
- }
-
- ElapsedTime ElapsedTime::create_started()
- {
- ElapsedTime t;
- t.m_start_tick = std::chrono::high_resolution_clock::now();
- return t;
- }
-
- std::string ElapsedTime::to_string() const { return format_time_userfriendly(elapsed<std::chrono::nanoseconds>()); }
-}
diff --git a/toolsrc/src/vcpkg_Dependencies.cpp b/toolsrc/src/vcpkg_Dependencies.cpp
deleted file mode 100644
index acfb55239..000000000
--- a/toolsrc/src/vcpkg_Dependencies.cpp
+++ /dev/null
@@ -1,300 +0,0 @@
-#include "pch.h"
-
-#include "PackageSpec.h"
-#include "Paragraphs.h"
-#include "StatusParagraphs.h"
-#include "VcpkgPaths.h"
-#include "vcpkg_Dependencies.h"
-#include "vcpkg_Files.h"
-#include "vcpkg_Graphs.h"
-#include "vcpkg_Util.h"
-#include "vcpkglib.h"
-
-namespace vcpkg::Dependencies
-{
- std::vector<PackageSpec> AnyParagraph::dependencies(const Triplet& triplet) const
- {
- auto to_package_specs = [&](const std::vector<std::string>& dependencies_as_string) {
- return Util::fmap(dependencies_as_string, [&](const std::string s) {
- return PackageSpec::from_name_and_triplet(s, triplet).value_or_exit(VCPKG_LINE_INFO);
- });
- };
-
- if (auto p = this->status_paragraph.get())
- {
- return to_package_specs(p->package.depends);
- }
-
- if (auto p = this->binary_paragraph.get())
- {
- return to_package_specs(p->depends);
- }
-
- if (auto p = this->source_paragraph.get())
- {
- return to_package_specs(filter_dependencies(p->depends, triplet));
- }
-
- Checks::exit_with_message(VCPKG_LINE_INFO,
- "Cannot get dependencies because there was none of: source/binary/status paragraphs");
- }
-
- std::string to_output_string(RequestType request_type, const CStringView s)
- {
- switch (request_type)
- {
- case RequestType::AUTO_SELECTED: return Strings::format(" * %s", s);
- case RequestType::USER_REQUESTED: return Strings::format(" %s", s);
- default: Checks::unreachable(VCPKG_LINE_INFO);
- }
- }
-
- InstallPlanAction::InstallPlanAction()
- : spec(), any_paragraph(), plan_type(InstallPlanType::UNKNOWN), request_type(RequestType::UNKNOWN)
- {
- }
-
- InstallPlanAction::InstallPlanAction(const PackageSpec& spec,
- const AnyParagraph& any_paragraph,
- const RequestType& request_type)
- : InstallPlanAction()
- {
- this->spec = spec;
- this->request_type = request_type;
- if (auto p = any_paragraph.status_paragraph.get())
- {
- this->plan_type = InstallPlanType::ALREADY_INSTALLED;
- this->any_paragraph.status_paragraph = *p;
- return;
- }
-
- if (auto p = any_paragraph.binary_paragraph.get())
- {
- this->plan_type = InstallPlanType::INSTALL;
- this->any_paragraph.binary_paragraph = *p;
- return;
- }
-
- if (auto p = any_paragraph.source_paragraph.get())
- {
- this->plan_type = InstallPlanType::BUILD_AND_INSTALL;
- this->any_paragraph.source_paragraph = *p;
- return;
- }
-
- this->plan_type = InstallPlanType::UNKNOWN;
- }
-
- bool InstallPlanAction::compare_by_name(const InstallPlanAction* left, const InstallPlanAction* right)
- {
- return left->spec.name() < right->spec.name();
- }
-
- RemovePlanAction::RemovePlanAction() : plan_type(RemovePlanType::UNKNOWN), request_type(RequestType::UNKNOWN) {}
-
- RemovePlanAction::RemovePlanAction(const PackageSpec& spec,
- const RemovePlanType& plan_type,
- const RequestType& request_type)
- : spec(spec), plan_type(plan_type), request_type(request_type)
- {
- }
-
- bool ExportPlanAction::compare_by_name(const ExportPlanAction* left, const ExportPlanAction* right)
- {
- return left->spec.name() < right->spec.name();
- }
-
- ExportPlanAction::ExportPlanAction()
- : spec(), any_paragraph(), plan_type(ExportPlanType::UNKNOWN), request_type(RequestType::UNKNOWN)
- {
- }
-
- ExportPlanAction::ExportPlanAction(const PackageSpec& spec,
- const AnyParagraph& any_paragraph,
- const RequestType& request_type)
- : ExportPlanAction()
- {
- this->spec = spec;
- this->request_type = request_type;
-
- if (auto p = any_paragraph.binary_paragraph.get())
- {
- this->plan_type = ExportPlanType::ALREADY_BUILT;
- this->any_paragraph.binary_paragraph = *p;
- return;
- }
-
- if (auto p = any_paragraph.source_paragraph.get())
- {
- this->plan_type = ExportPlanType::PORT_AVAILABLE_BUT_NOT_BUILT;
- this->any_paragraph.source_paragraph = *p;
- return;
- }
-
- this->plan_type = ExportPlanType::UNKNOWN;
- }
-
- bool RemovePlanAction::compare_by_name(const RemovePlanAction* left, const RemovePlanAction* right)
- {
- return left->spec.name() < right->spec.name();
- }
-
- std::vector<InstallPlanAction> create_install_plan(const VcpkgPaths& paths,
- const std::vector<PackageSpec>& specs,
- const StatusParagraphs& status_db)
- {
- struct InstallAdjacencyProvider final : Graphs::AdjacencyProvider<PackageSpec, InstallPlanAction>
- {
- const VcpkgPaths& paths;
- const StatusParagraphs& status_db;
- const std::unordered_set<PackageSpec>& specs_as_set;
-
- InstallAdjacencyProvider(const VcpkgPaths& p,
- const StatusParagraphs& s,
- const std::unordered_set<PackageSpec>& specs_as_set)
- : paths(p), status_db(s), specs_as_set(specs_as_set)
- {
- }
-
- std::vector<PackageSpec> adjacency_list(const InstallPlanAction& plan) const override
- {
- if (plan.any_paragraph.status_paragraph.get()) return std::vector<PackageSpec>{};
- return plan.any_paragraph.dependencies(plan.spec.triplet());
- }
-
- InstallPlanAction load_vertex_data(const PackageSpec& spec) const override
- {
- const RequestType request_type = specs_as_set.find(spec) != specs_as_set.end()
- ? RequestType::USER_REQUESTED
- : RequestType::AUTO_SELECTED;
- auto it = status_db.find_installed(spec);
- if (it != status_db.end()) return InstallPlanAction{spec, {*it->get(), nullopt, nullopt}, request_type};
-
- Expected<BinaryParagraph> maybe_bpgh = Paragraphs::try_load_cached_package(paths, spec);
- if (auto bpgh = maybe_bpgh.get())
- return InstallPlanAction{spec, {nullopt, *bpgh, nullopt}, request_type};
-
- Expected<SourceParagraph> maybe_spgh =
- Paragraphs::try_load_port(paths.get_filesystem(), paths.port_dir(spec));
- if (auto spgh = maybe_spgh.get())
- return InstallPlanAction{spec, {nullopt, nullopt, *spgh}, request_type};
-
- Checks::exit_with_message(VCPKG_LINE_INFO, "Could not find package %s", spec);
- }
- };
-
- const std::unordered_set<PackageSpec> specs_as_set(specs.cbegin(), specs.cend());
- std::vector<InstallPlanAction> toposort =
- Graphs::topological_sort(specs, InstallAdjacencyProvider{paths, status_db, specs_as_set});
- Util::erase_remove_if(toposort, [](const InstallPlanAction& plan) {
- return plan.request_type == RequestType::AUTO_SELECTED &&
- plan.plan_type == InstallPlanType::ALREADY_INSTALLED;
- });
-
- return toposort;
- }
-
- std::vector<RemovePlanAction> create_remove_plan(const std::vector<PackageSpec>& specs,
- const StatusParagraphs& status_db)
- {
- struct RemoveAdjacencyProvider final : Graphs::AdjacencyProvider<PackageSpec, RemovePlanAction>
- {
- const StatusParagraphs& status_db;
- const std::vector<StatusParagraph*>& installed_ports;
- const std::unordered_set<PackageSpec>& specs_as_set;
-
- RemoveAdjacencyProvider(const StatusParagraphs& status_db,
- const std::vector<StatusParagraph*>& installed_ports,
- const std::unordered_set<PackageSpec>& specs_as_set)
- : status_db(status_db), installed_ports(installed_ports), specs_as_set(specs_as_set)
- {
- }
-
- std::vector<PackageSpec> adjacency_list(const RemovePlanAction& plan) const override
- {
- if (plan.plan_type == RemovePlanType::NOT_INSTALLED)
- {
- return {};
- }
-
- const PackageSpec& spec = plan.spec;
- std::vector<PackageSpec> dependents;
- for (const StatusParagraph* an_installed_package : installed_ports)
- {
- if (an_installed_package->package.spec.triplet() != spec.triplet()) continue;
-
- const std::vector<std::string>& deps = an_installed_package->package.depends;
- if (std::find(deps.begin(), deps.end(), spec.name()) == deps.end()) continue;
-
- dependents.push_back(an_installed_package->package.spec);
- }
-
- return dependents;
- }
-
- RemovePlanAction load_vertex_data(const PackageSpec& spec) const override
- {
- const RequestType request_type = specs_as_set.find(spec) != specs_as_set.end()
- ? RequestType::USER_REQUESTED
- : RequestType::AUTO_SELECTED;
- const StatusParagraphs::const_iterator it = status_db.find_installed(spec);
- if (it == status_db.end())
- {
- return RemovePlanAction{spec, RemovePlanType::NOT_INSTALLED, request_type};
- }
- return RemovePlanAction{spec, RemovePlanType::REMOVE, request_type};
- }
- };
-
- const std::vector<StatusParagraph*>& installed_ports = get_installed_ports(status_db);
- const std::unordered_set<PackageSpec> specs_as_set(specs.cbegin(), specs.cend());
- return Graphs::topological_sort(specs, RemoveAdjacencyProvider{status_db, installed_ports, specs_as_set});
- }
-
- std::vector<ExportPlanAction> create_export_plan(const VcpkgPaths& paths,
- const std::vector<PackageSpec>& specs,
- const StatusParagraphs& status_db)
- {
- struct ExportAdjacencyProvider final : Graphs::AdjacencyProvider<PackageSpec, ExportPlanAction>
- {
- const VcpkgPaths& paths;
- const StatusParagraphs& status_db;
- const std::unordered_set<PackageSpec>& specs_as_set;
-
- ExportAdjacencyProvider(const VcpkgPaths& p,
- const StatusParagraphs& s,
- const std::unordered_set<PackageSpec>& specs_as_set)
- : paths(p), status_db(s), specs_as_set(specs_as_set)
- {
- }
-
- std::vector<PackageSpec> adjacency_list(const ExportPlanAction& plan) const override
- {
- return plan.any_paragraph.dependencies(plan.spec.triplet());
- }
-
- ExportPlanAction load_vertex_data(const PackageSpec& spec) const override
- {
- const RequestType request_type = specs_as_set.find(spec) != specs_as_set.end()
- ? RequestType::USER_REQUESTED
- : RequestType::AUTO_SELECTED;
-
- Expected<BinaryParagraph> maybe_bpgh = Paragraphs::try_load_cached_package(paths, spec);
- if (auto bpgh = maybe_bpgh.get())
- return ExportPlanAction{spec, {nullopt, *bpgh, nullopt}, request_type};
-
- Expected<SourceParagraph> maybe_spgh =
- Paragraphs::try_load_port(paths.get_filesystem(), paths.port_dir(spec));
- if (auto spgh = maybe_spgh.get())
- return ExportPlanAction{spec, {nullopt, nullopt, *spgh}, request_type};
-
- Checks::exit_with_message(VCPKG_LINE_INFO, "Could not find package %s", spec);
- }
- };
-
- const std::unordered_set<PackageSpec> specs_as_set(specs.cbegin(), specs.cend());
- std::vector<ExportPlanAction> toposort =
- Graphs::topological_sort(specs, ExportAdjacencyProvider{paths, status_db, specs_as_set});
- return toposort;
- }
-}
diff --git a/toolsrc/src/vcpkg_Input.cpp b/toolsrc/src/vcpkg_Input.cpp
deleted file mode 100644
index 7d8e4767e..000000000
--- a/toolsrc/src/vcpkg_Input.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-#include "pch.h"
-
-#include "metrics.h"
-#include "vcpkg_Commands.h"
-#include "vcpkg_Input.h"
-#include "vcpkg_System.h"
-
-namespace vcpkg::Input
-{
- PackageSpec check_and_get_package_spec(const std::string& package_spec_as_string,
- const Triplet& default_triplet,
- CStringView example_text)
- {
- const std::string as_lowercase = Strings::ascii_to_lowercase(package_spec_as_string);
- Expected<PackageSpec> expected_spec = PackageSpec::from_string(as_lowercase, default_triplet);
- if (auto spec = expected_spec.get())
- {
- return *spec;
- }
-
- // Intentionally show the lowercased string
- System::println(System::Color::error, "Error: %s: %s", expected_spec.error_code().message(), as_lowercase);
- System::print(example_text);
- Checks::exit_fail(VCPKG_LINE_INFO);
- }
-
- void check_triplet(const Triplet& t, const VcpkgPaths& paths)
- {
- if (!paths.is_valid_triplet(t))
- {
- System::println(System::Color::error, "Error: invalid triplet: %s", t);
- Metrics::track_property("error", "invalid triplet: " + t.to_string());
- Commands::Help::help_topic_valid_triplet(paths);
- Checks::exit_fail(VCPKG_LINE_INFO);
- }
- }
-}
diff --git a/toolsrc/src/vcpkg_Strings.cpp b/toolsrc/src/vcpkg_Strings.cpp
deleted file mode 100644
index 9ba9eb700..000000000
--- a/toolsrc/src/vcpkg_Strings.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-#include "pch.h"
-
-#include "vcpkg_Strings.h"
-#include "vcpkg_Util.h"
-
-namespace vcpkg::Strings::details
-{
- // To disambiguate between two overloads
- static const auto isspace = [](const char c) { return std::isspace(c); };
-
- // Avoids C4244 warnings because of char<->int conversion that occur when using std::tolower()
- static char tolower_char(const char c) { return static_cast<char>(std::tolower(c)); }
-
- static _locale_t& c_locale()
- {
- static _locale_t c_locale_impl = _create_locale(LC_ALL, "C");
- return c_locale_impl;
- }
-
- std::string format_internal(const char* fmtstr, ...)
- {
- va_list lst;
- va_start(lst, fmtstr);
-
- const int sz = _vscprintf_l(fmtstr, c_locale(), lst);
- std::string output(sz, '\0');
- _vsnprintf_s_l(&output[0], output.size() + 1, output.size() + 1, fmtstr, c_locale(), lst);
- va_end(lst);
-
- return output;
- }
-
- std::wstring wformat_internal(const wchar_t* fmtstr, ...)
- {
- va_list lst;
- va_start(lst, fmtstr);
-
- const int sz = _vscwprintf_l(fmtstr, c_locale(), lst);
- std::wstring output(sz, '\0');
- _vsnwprintf_s_l(&output[0], output.size() + 1, output.size() + 1, fmtstr, c_locale(), lst);
- va_end(lst);
-
- return output;
- }
-}
-
-namespace vcpkg::Strings
-{
- std::wstring to_utf16(const CStringView s)
- {
- std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> conversion;
- return conversion.from_bytes(s);
- }
-
- std::string to_utf8(const CWStringView w)
- {
- std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> conversion;
- return conversion.to_bytes(w);
- }
-
- std::string::const_iterator case_insensitive_ascii_find(const std::string& s, const std::string& pattern)
- {
- const std::string pattern_as_lower_case(ascii_to_lowercase(pattern));
- return search(s.begin(),
- s.end(),
- pattern_as_lower_case.begin(),
- pattern_as_lower_case.end(),
- [](const char a, const char b) { return details::tolower_char(a) == b; });
- }
-
- int case_insensitive_ascii_compare(const CStringView left, const CStringView right)
- {
- return _stricmp(left, right);
- }
-
- std::string ascii_to_lowercase(const std::string& input)
- {
- std::string output(input);
- std::transform(output.begin(), output.end(), output.begin(), &details::tolower_char);
- return output;
- }
-
- void trim(std::string* s)
- {
- s->erase(std::find_if_not(s->rbegin(), s->rend(), details::isspace).base(), s->end());
- s->erase(s->begin(), std::find_if_not(s->begin(), s->end(), details::isspace));
- }
-
- std::string trimmed(const std::string& s)
- {
- auto whitespace_back = std::find_if_not(s.rbegin(), s.rend(), details::isspace).base();
- auto whitespace_front = std::find_if_not(s.begin(), whitespace_back, details::isspace);
- return std::string(whitespace_front, whitespace_back);
- }
-
- void trim_all_and_remove_whitespace_strings(std::vector<std::string>* strings)
- {
- for (std::string& s : *strings)
- {
- trim(&s);
- }
-
- Util::erase_remove_if(*strings, [](const std::string& s) { return s == ""; });
- }
-
- std::vector<std::string> split(const std::string& s, const std::string& delimiter)
- {
- std::vector<std::string> output;
-
- size_t i = 0;
- for (size_t pos = s.find(delimiter); pos != std::string::npos; pos = s.find(delimiter, pos))
- {
- output.push_back(s.substr(i, pos - i));
- i = ++pos;
- }
-
- // Add the rest of the string after the last delimiter, unless there is nothing after it
- if (i != s.length())
- {
- output.push_back(s.substr(i, s.length()));
- }
-
- return output;
- }
-}
diff --git a/toolsrc/src/vcpkg_System.cpp b/toolsrc/src/vcpkg_System.cpp
deleted file mode 100644
index 21329e003..000000000
--- a/toolsrc/src/vcpkg_System.cpp
+++ /dev/null
@@ -1,290 +0,0 @@
-#include "pch.h"
-
-#include "vcpkg_Checks.h"
-#include "vcpkg_System.h"
-#include "vcpkglib.h"
-
-namespace vcpkg::System
-{
- tm get_current_date_time()
- {
- using std::chrono::system_clock;
- std::time_t now_time = system_clock::to_time_t(system_clock::now());
- tm parts;
- localtime_s(&parts, &now_time);
- return parts;
- }
-
- fs::path get_exe_path_of_current_process()
- {
- wchar_t buf[_MAX_PATH];
- int bytes = GetModuleFileNameW(nullptr, buf, _MAX_PATH);
- if (bytes == 0) std::abort();
- return fs::path(buf, buf + bytes);
- }
-
- Optional<CPUArchitecture> to_cpu_architecture(CStringView arch)
- {
- if (Strings::case_insensitive_ascii_compare(arch, "x86") == 0) return CPUArchitecture::X86;
- if (Strings::case_insensitive_ascii_compare(arch, "x64") == 0) return CPUArchitecture::X64;
- if (Strings::case_insensitive_ascii_compare(arch, "amd64") == 0) return CPUArchitecture::X64;
- if (Strings::case_insensitive_ascii_compare(arch, "arm") == 0) return CPUArchitecture::ARM;
- if (Strings::case_insensitive_ascii_compare(arch, "arm64") == 0) return CPUArchitecture::ARM64;
- return nullopt;
- }
-
- CPUArchitecture get_host_processor()
- {
- auto w6432 = get_environment_variable(L"PROCESSOR_ARCHITEW6432");
- if (auto p = w6432.get()) return to_cpu_architecture(Strings::to_utf8(*p)).value_or_exit(VCPKG_LINE_INFO);
-
- auto procarch = get_environment_variable(L"PROCESSOR_ARCHITECTURE").value_or_exit(VCPKG_LINE_INFO);
- return to_cpu_architecture(Strings::to_utf8(procarch)).value_or_exit(VCPKG_LINE_INFO);
- }
-
- int cmd_execute_clean(const CWStringView cmd_line)
- {
- static const std::wstring system_root = get_environment_variable(L"SystemRoot").value_or_exit(VCPKG_LINE_INFO);
- static const std::wstring system_32 = system_root + LR"(\system32)";
- static const std::wstring new_PATH = Strings::wformat(
- LR"(Path=%s;%s;%s\Wbem;%s\WindowsPowerShell\v1.0\)", system_32, system_root, system_32, system_32);
-
- std::vector<std::wstring> env_wstrings = {
- L"ALLUSERSPROFILE",
- L"APPDATA",
- L"CommonProgramFiles",
- L"CommonProgramFiles(x86)",
- L"CommonProgramW6432",
- L"COMPUTERNAME",
- L"ComSpec",
- L"HOMEDRIVE",
- L"HOMEPATH",
- L"LOCALAPPDATA",
- L"LOGONSERVER",
- L"NUMBER_OF_PROCESSORS",
- L"OS",
- L"PATHEXT",
- L"PROCESSOR_ARCHITECTURE",
- L"PROCESSOR_ARCHITEW6432",
- L"PROCESSOR_IDENTIFIER",
- L"PROCESSOR_LEVEL",
- L"PROCESSOR_REVISION",
- L"ProgramData",
- L"ProgramFiles",
- L"ProgramFiles(x86)",
- L"ProgramW6432",
- L"PROMPT",
- L"PSModulePath",
- L"PUBLIC",
- L"SystemDrive",
- L"SystemRoot",
- L"TEMP",
- L"TMP",
- L"USERDNSDOMAIN",
- L"USERDOMAIN",
- L"USERDOMAIN_ROAMINGPROFILE",
- L"USERNAME",
- L"USERPROFILE",
- L"windir",
- // Enables proxy information to be passed to Curl, the underlying download library in cmake.exe
- L"HTTP_PROXY",
- L"HTTPS_PROXY",
- // Enables find_package(CUDA) in CMake
- L"CUDA_PATH",
- };
-
- // Flush stdout before launching external process
- _flushall();
-
- std::vector<const wchar_t*> env_cstr;
- env_cstr.reserve(env_wstrings.size() + 2);
-
- for (auto&& env_wstring : env_wstrings)
- {
- const Optional<std::wstring> value = System::get_environment_variable(env_wstring);
- auto v = value.get();
- if (!v || v->empty()) continue;
-
- env_wstring.push_back(L'=');
- env_wstring.append(*v);
- env_cstr.push_back(env_wstring.c_str());
- }
-
- env_cstr.push_back(new_PATH.c_str());
- env_cstr.push_back(nullptr);
-
- // Basically we are wrapping it in quotes
- const std::wstring& actual_cmd_line = Strings::wformat(LR"###("%s")###", cmd_line);
- Debug::println("_wspawnlpe(cmd.exe /c %s)", Strings::to_utf8(actual_cmd_line));
- auto exit_code =
- _wspawnlpe(_P_WAIT, L"cmd.exe", L"cmd.exe", L"/c", actual_cmd_line.c_str(), nullptr, env_cstr.data());
- Debug::println("_wspawnlpe() returned %d", exit_code);
- return static_cast<int>(exit_code);
- }
-
- int cmd_execute(const CWStringView cmd_line)
- {
- // Flush stdout before launching external process
- _flushall();
-
- // Basically we are wrapping it in quotes
- const std::wstring& actual_cmd_line = Strings::wformat(LR"###("%s")###", cmd_line);
- Debug::println("_wsystem(%s)", Strings::to_utf8(actual_cmd_line));
- int exit_code = _wsystem(actual_cmd_line.c_str());
- Debug::println("_wsystem() returned %d", exit_code);
- return exit_code;
- }
-
- ExitCodeAndOutput cmd_execute_and_capture_output(const CWStringView cmd_line)
- {
- // Flush stdout before launching external process
- fflush(stdout);
-
- const std::wstring& actual_cmd_line = Strings::wformat(LR"###("%s 2>&1")###", cmd_line);
-
- std::string output;
- char buf[1024];
- auto pipe = _wpopen(actual_cmd_line.c_str(), L"r");
- if (pipe == nullptr)
- {
- return {1, output};
- }
- while (fgets(buf, 1024, pipe))
- {
- output.append(buf);
- }
- if (!feof(pipe))
- {
- return {1, output};
- }
- auto ec = _pclose(pipe);
- return {ec, output};
- }
-
- std::wstring create_powershell_script_cmd(const fs::path& script_path, const CWStringView args)
- {
- // TODO: switch out ExecutionPolicy Bypass with "Remove Mark Of The Web" code and restore RemoteSigned
- return Strings::wformat(
- LR"(powershell -NoProfile -ExecutionPolicy Bypass -Command "& {& '%s' %s}")", script_path.native(), args);
- }
-
- void print(const CStringView message) { fputs(message, stdout); }
-
- void println(const CStringView message)
- {
- print(message);
- putchar('\n');
- }
-
- void print(const Color c, const CStringView message)
- {
- HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
-
- CONSOLE_SCREEN_BUFFER_INFO consoleScreenBufferInfo{};
- GetConsoleScreenBufferInfo(hConsole, &consoleScreenBufferInfo);
- auto original_color = consoleScreenBufferInfo.wAttributes;
-
- SetConsoleTextAttribute(hConsole, static_cast<WORD>(c) | (original_color & 0xF0));
- print(message);
- SetConsoleTextAttribute(hConsole, original_color);
- }
-
- void println(const Color c, const CStringView message)
- {
- print(c, message);
- putchar('\n');
- }
-
- Optional<std::wstring> get_environment_variable(const CWStringView varname) noexcept
- {
- auto sz = GetEnvironmentVariableW(varname, nullptr, 0);
- if (sz == 0) return nullopt;
-
- std::wstring ret(sz, L'\0');
-
- Checks::check_exit(VCPKG_LINE_INFO, MAXDWORD >= ret.size());
- auto sz2 = GetEnvironmentVariableW(varname, ret.data(), static_cast<DWORD>(ret.size()));
- Checks::check_exit(VCPKG_LINE_INFO, sz2 + 1 == sz);
- ret.pop_back();
- return ret;
- }
-
- static bool is_string_keytype(DWORD hkey_type)
- {
- return hkey_type == REG_SZ || hkey_type == REG_MULTI_SZ || hkey_type == REG_EXPAND_SZ;
- }
-
- Optional<std::wstring> get_registry_string(HKEY base, const CWStringView subKey, const CWStringView valuename)
- {
- HKEY k = nullptr;
- LSTATUS ec = RegOpenKeyExW(base, subKey, NULL, KEY_READ, &k);
- if (ec != ERROR_SUCCESS) return nullopt;
-
- DWORD dwBufferSize = 0;
- DWORD dwType = 0;
- auto rc = RegQueryValueExW(k, valuename, nullptr, &dwType, nullptr, &dwBufferSize);
- if (rc != ERROR_SUCCESS || !is_string_keytype(dwType) || dwBufferSize == 0 ||
- dwBufferSize % sizeof(wchar_t) != 0)
- return nullopt;
- std::wstring ret;
- ret.resize(dwBufferSize / sizeof(wchar_t));
-
- rc = RegQueryValueExW(k, valuename, nullptr, &dwType, reinterpret_cast<LPBYTE>(ret.data()), &dwBufferSize);
- if (rc != ERROR_SUCCESS || !is_string_keytype(dwType) || dwBufferSize != sizeof(wchar_t) * ret.size())
- return nullopt;
-
- ret.pop_back(); // remove extra trailing null byte
- return ret;
- }
-
- static const fs::path& get_ProgramFiles()
- {
- static const fs::path p = System::get_environment_variable(L"PROGRAMFILES").value_or_exit(VCPKG_LINE_INFO);
- return p;
- }
-
- const fs::path& get_ProgramFiles_32_bit()
- {
- static const fs::path p = []() -> fs::path {
- auto value = System::get_environment_variable(L"ProgramFiles(x86)");
- if (auto v = value.get())
- {
- return std::move(*v);
- }
- return get_ProgramFiles();
- }();
- return p;
- }
-
- const fs::path& get_ProgramFiles_platform_bitness()
- {
- static const fs::path p = []() -> fs::path {
- auto value = System::get_environment_variable(L"ProgramW6432");
- if (auto v = value.get())
- {
- return std::move(*v);
- }
- return get_ProgramFiles();
- }();
- return p;
- }
-}
-
-namespace vcpkg::Debug
-{
- void println(const CStringView message)
- {
- if (g_debugging)
- {
- System::println("[DEBUG] %s", message);
- }
- }
-
- void println(const System::Color c, const CStringView message)
- {
- if (g_debugging)
- {
- System::println(c, "[DEBUG] %s", message);
- }
- }
-}
diff --git a/toolsrc/src/vcpkglib_helpers.cpp b/toolsrc/src/vcpkglib_helpers.cpp
deleted file mode 100644
index 9f992d320..000000000
--- a/toolsrc/src/vcpkglib_helpers.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-#include "pch.h"
-
-#include "vcpkg_Checks.h"
-#include "vcpkglib_helpers.h"
-
-namespace vcpkg::details
-{
- std::string optional_field(const std::unordered_map<std::string, std::string>& fields, const std::string& fieldname)
- {
- auto it = fields.find(fieldname);
- if (it == fields.end())
- {
- return std::string();
- }
-
- return it->second;
- }
-
- std::string remove_optional_field(std::unordered_map<std::string, std::string>* fields,
- const std::string& fieldname)
- {
- auto it = fields->find(fieldname);
- if (it == fields->end())
- {
- return std::string();
- }
-
- const std::string value = std::move(it->second);
- fields->erase(it);
- return value;
- }
-
- std::string required_field(const std::unordered_map<std::string, std::string>& fields, const std::string& fieldname)
- {
- auto it = fields.find(fieldname);
- Checks::check_exit(VCPKG_LINE_INFO, it != fields.end(), "Required field not present: %s", fieldname);
- return it->second;
- }
-
- std::string remove_required_field(std::unordered_map<std::string, std::string>* fields,
- const std::string& fieldname)
- {
- auto it = fields->find(fieldname);
- Checks::check_exit(VCPKG_LINE_INFO, it != fields->end(), "Required field not present: %s", fieldname);
-
- const std::string value = std::move(it->second);
- fields->erase(it);
- return value;
- }
-
- std::string shorten_description(const std::string& desc)
- {
- auto simple_desc = std::regex_replace(desc, std::regex("\\s+"), " ");
- return simple_desc.size() <= 52 ? simple_desc : simple_desc.substr(0, 49) + "...";
- }
-}
diff --git a/toolsrc/src/vcpkg_metrics_uploader.cpp b/toolsrc/src/vcpkgmetricsuploader.cpp
index 38bf7ff9c..2239fe750 100644
--- a/toolsrc/src/vcpkg_metrics_uploader.cpp
+++ b/toolsrc/src/vcpkgmetricsuploader.cpp
@@ -1,6 +1,8 @@
-#include "metrics.h"
-#include "vcpkg_Checks.h"
-#include "vcpkg_Files.h"
+#include <vcpkg/metrics.h>
+
+#include <vcpkg/base/checks.h>
+#include <vcpkg/base/files.h>
+
#include <Windows.h>
using namespace vcpkg;
@@ -11,5 +13,8 @@ int WINAPI WinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ LPSTR, _In_ int)
LPWSTR* szArgList = CommandLineToArgvW(GetCommandLineW(), &argCount);
Checks::check_exit(VCPKG_LINE_INFO, argCount == 2, "Requires exactly one argument, the path to the payload file");
- Metrics::upload(Files::get_real_filesystem().read_contents(szArgList[1]).value_or_exit(VCPKG_LINE_INFO));
+ auto v = Files::get_real_filesystem().read_contents(szArgList[1]).value_or_exit(VCPKG_LINE_INFO);
+ Metrics::g_metrics.lock()->upload(v);
+ LocalFree(szArgList);
+ return 0;
}