aboutsummaryrefslogtreecommitdiff
path: root/toolsrc/src
diff options
context:
space:
mode:
authorPhil Christensen <philc@microsoft.com>2018-12-06 15:06:28 -0800
committerPhil Christensen <philc@microsoft.com>2018-12-06 15:06:28 -0800
commit7347305e8459fcc78553a9f88196e0d93eb0a8fe (patch)
treeac9eee9ff267c6a71a83249bed7a94f02b00d9a5 /toolsrc/src
parented9357a5aafea7192932b5874264bd103fc61255 (diff)
parent63c1b2628e958f8e02356411f032941c0c2f3bbb (diff)
downloadvcpkg-7347305e8459fcc78553a9f88196e0d93eb0a8fe.tar.gz
vcpkg-7347305e8459fcc78553a9f88196e0d93eb0a8fe.zip
Merge branch 'master' of https://github.com/microsoft/vcpkg into dev/philc/3425
Diffstat (limited to 'toolsrc/src')
-rw-r--r--toolsrc/src/vcpkg.cpp32
-rw-r--r--toolsrc/src/vcpkg/archives.cpp106
-rw-r--r--toolsrc/src/vcpkg/base/checks.cpp26
-rw-r--r--toolsrc/src/vcpkg/base/chrono.cpp96
-rw-r--r--toolsrc/src/vcpkg/base/downloads.cpp183
-rw-r--r--toolsrc/src/vcpkg/base/files.cpp117
-rw-r--r--toolsrc/src/vcpkg/base/hash.cpp (renamed from toolsrc/src/vcpkg/commands.hash.cpp)116
-rw-r--r--toolsrc/src/vcpkg/base/stringrange.cpp79
-rw-r--r--toolsrc/src/vcpkg/base/strings.cpp18
-rw-r--r--toolsrc/src/vcpkg/base/system.cpp207
-rw-r--r--toolsrc/src/vcpkg/build.cpp158
-rw-r--r--toolsrc/src/vcpkg/commands.autocomplete.cpp52
-rw-r--r--toolsrc/src/vcpkg/commands.cache.cpp2
-rw-r--r--toolsrc/src/vcpkg/commands.ci.cpp114
-rw-r--r--toolsrc/src/vcpkg/commands.cpp49
-rw-r--r--toolsrc/src/vcpkg/commands.dependinfo.cpp227
-rw-r--r--toolsrc/src/vcpkg/commands.edit.cpp51
-rw-r--r--toolsrc/src/vcpkg/commands.env.cpp13
-rw-r--r--toolsrc/src/vcpkg/commands.exportifw.cpp2
-rw-r--r--toolsrc/src/vcpkg/commands.fetch.cpp714
-rw-r--r--toolsrc/src/vcpkg/commands.integrate.cpp42
-rw-r--r--toolsrc/src/vcpkg/commands.list.cpp2
-rw-r--r--toolsrc/src/vcpkg/commands.search.cpp49
-rw-r--r--toolsrc/src/vcpkg/commands.upgrade.cpp18
-rw-r--r--toolsrc/src/vcpkg/commands.xvsinstances.cpp33
-rw-r--r--toolsrc/src/vcpkg/dependencies.cpp59
-rw-r--r--toolsrc/src/vcpkg/export.cpp37
-rw-r--r--toolsrc/src/vcpkg/globalstate.cpp44
-rw-r--r--toolsrc/src/vcpkg/help.cpp15
-rw-r--r--toolsrc/src/vcpkg/install.cpp67
-rw-r--r--toolsrc/src/vcpkg/metrics.cpp6
-rw-r--r--toolsrc/src/vcpkg/postbuildlint.cpp39
-rw-r--r--toolsrc/src/vcpkg/remove.cpp4
-rw-r--r--toolsrc/src/vcpkg/statusparagraph.cpp2
-rw-r--r--toolsrc/src/vcpkg/tools.cpp533
-rw-r--r--toolsrc/src/vcpkg/vcpkgcmdarguments.cpp22
-rw-r--r--toolsrc/src/vcpkg/vcpkgpaths.cpp28
-rw-r--r--toolsrc/src/vcpkg/visualstudio.cpp326
38 files changed, 2383 insertions, 1305 deletions
diff --git a/toolsrc/src/vcpkg.cpp b/toolsrc/src/vcpkg.cpp
index 06c99e9a8..3589881a7 100644
--- a/toolsrc/src/vcpkg.cpp
+++ b/toolsrc/src/vcpkg.cpp
@@ -33,6 +33,12 @@
using namespace vcpkg;
+// 24 hours/day * 30 days/month * 6 months
+static constexpr int SURVEY_INTERVAL_IN_HOURS = 24 * 30 * 6;
+
+// Initial survey appears after 10 days. Therefore, subtract 24 hours/day * 10 days
+static constexpr int SURVEY_INITIAL_OFFSET_IN_HOURS = SURVEY_INTERVAL_IN_HOURS - 24 * 10;
+
void invalid_command(const std::string& cmd)
{
System::println(System::Color::error, "invalid command: %s", cmd);
@@ -70,7 +76,7 @@ static void inner(const VcpkgCmdArguments& 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
{
@@ -94,6 +100,8 @@ static void inner(const VcpkgCmdArguments& args)
Checks::check_exit(VCPKG_LINE_INFO, !vcpkg_root_dir.empty(), "Error: Could not detect vcpkg-root.");
+ 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);
@@ -102,7 +110,7 @@ static void inner(const VcpkgCmdArguments& args)
"Error: Invalid vcpkg root directory %s: %s",
vcpkg_root_dir.string(),
expected_paths.error().message());
- const VcpkgPaths paths = expected_paths.value_or_exit(VCPKG_LINE_INFO);
+ const VcpkgPaths& paths = expected_paths.value_or_exit(VCPKG_LINE_INFO);
#if defined(_WIN32)
const int exit_code = _wchdir(paths.root.c_str());
@@ -111,19 +119,19 @@ static void inner(const VcpkgCmdArguments& args)
#endif
Checks::check_exit(VCPKG_LINE_INFO, exit_code == 0, "Changing the working dir failed");
- if (args.command != "autocomplete")
+ if (args.command == "install" || args.command == "remove" || args.command == "export" || args.command == "update")
{
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)
+ const auto now = Chrono::CTime::get_current_date_time().value_or_exit(VCPKG_LINE_INFO);
+ const auto delta = now.to_time_point() - p_surveydate->to_time_point();
+ if (std::chrono::duration_cast<std::chrono::hours>(delta).count() > SURVEY_INTERVAL_IN_HOURS)
{
std::default_random_engine generator(
- static_cast<unsigned int>(std::chrono::system_clock::now().time_since_epoch().count()));
+ static_cast<unsigned int>(now.to_time_point().time_since_epoch().count()));
std::uniform_int_distribution<int> distribution(1, 4);
if (distribution(generator) == 1)
@@ -212,7 +220,9 @@ static void load_config()
if (config.last_completed_survey.empty())
{
- config.last_completed_survey = config.user_time;
+ const auto now = Chrono::CTime::parse(config.user_time).value_or_exit(VCPKG_LINE_INFO);
+ const Chrono::CTime offset = now.add_hours(-SURVEY_INITIAL_OFFSET_IN_HOURS);
+ config.last_completed_survey = offset.to_string();
}
GlobalState::g_surveydate.lock()->assign(config.last_completed_survey);
@@ -259,6 +269,7 @@ int main(const int argc, const char* const* const argv)
#if defined(_WIN32)
GlobalState::g_init_console_cp = GetConsoleCP();
GlobalState::g_init_console_output_cp = GetConsoleOutputCP();
+ GlobalState::g_init_console_initialized = true;
SetConsoleCP(CP_UTF8);
SetConsoleOutputCP(CP_UTF8);
@@ -273,6 +284,9 @@ int main(const int argc, const char* const* const argv)
locked_metrics->track_property("cmdline", trimmed_command_line);
#endif
}
+
+ Checks::register_console_ctrl_handler();
+
load_config();
const auto vcpkg_feature_flags_env = System::get_environment_variable("VCPKG_FEATURE_FLAGS");
@@ -291,8 +305,6 @@ int main(const int argc, const char* const* const argv)
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;
- Checks::register_console_ctrl_handler();
-
if (GlobalState::debugging)
{
inner(args);
diff --git a/toolsrc/src/vcpkg/archives.cpp b/toolsrc/src/vcpkg/archives.cpp
new file mode 100644
index 000000000..8943893d6
--- /dev/null
+++ b/toolsrc/src/vcpkg/archives.cpp
@@ -0,0 +1,106 @@
+#include "pch.h"
+
+#include <vcpkg/archives.h>
+#include <vcpkg/commands.h>
+
+namespace vcpkg::Archives
+{
+ 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 = paths.get_tool_exe(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 = paths.get_tool_exe(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);
+
+ for (int i = 0; i < 5 && ec; i++)
+ {
+ i++;
+ using namespace std::chrono_literals;
+ std::this_thread::sleep_for(i * 100ms);
+ fs.rename(to_path_partial, to_path, ec);
+ }
+
+ Checks::check_exit(VCPKG_LINE_INFO,
+ !ec,
+ "Failed to do post-extract rename-in-place.\n"
+ "fs.rename(%s, %s, %s)",
+ to_path_partial.u8string(),
+ to_path.u8string(),
+ ec.message());
+ }
+}
diff --git a/toolsrc/src/vcpkg/base/checks.cpp b/toolsrc/src/vcpkg/base/checks.cpp
index d96cb98ff..cc439adfe 100644
--- a/toolsrc/src/vcpkg/base/checks.cpp
+++ b/toolsrc/src/vcpkg/base/checks.cpp
@@ -14,18 +14,29 @@ namespace vcpkg::Checks
if (have_entered) std::terminate();
have_entered = true;
- const auto elapsed_us = GlobalState::timer.lock()->microseconds();
+ const auto elapsed_us_inner = GlobalState::timer.lock()->microseconds();
+
+ bool debugging = GlobalState::debugging;
auto metrics = Metrics::g_metrics.lock();
- metrics->track_metric("elapsed_us", elapsed_us);
+ metrics->track_metric("elapsed_us", elapsed_us_inner);
GlobalState::debugging = false;
metrics->flush();
#if defined(_WIN32)
- SetConsoleCP(GlobalState::g_init_console_cp);
- SetConsoleOutputCP(GlobalState::g_init_console_output_cp);
+ if (GlobalState::g_init_console_initialized)
+ {
+ SetConsoleCP(GlobalState::g_init_console_cp);
+ SetConsoleOutputCP(GlobalState::g_init_console_output_cp);
+ }
#endif
+ auto elapsed_us = GlobalState::timer.lock()->microseconds();
+ if (debugging)
+ System::println("[DEBUG] Exiting after %d us (%d us)",
+ static_cast<int>(elapsed_us),
+ static_cast<int>(elapsed_us_inner));
+
fflush(nullptr);
#if defined(_WIN32)
@@ -38,12 +49,11 @@ namespace vcpkg::Checks
#if defined(_WIN32)
static BOOL ctrl_handler(DWORD fdw_ctrl_type)
{
+ switch (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.");
+ case CTRL_C_EVENT: GlobalState::g_ctrl_c_state.transition_handle_ctrl_c(); return TRUE;
+ default: return FALSE;
}
- cleanup_and_exit(EXIT_FAILURE);
}
void register_console_ctrl_handler()
diff --git a/toolsrc/src/vcpkg/base/chrono.cpp b/toolsrc/src/vcpkg/base/chrono.cpp
index 2a76f5df0..405e76605 100644
--- a/toolsrc/src/vcpkg/base/chrono.cpp
+++ b/toolsrc/src/vcpkg/base/chrono.cpp
@@ -5,6 +5,61 @@
namespace vcpkg::Chrono
{
+ static std::time_t get_current_time_as_time_since_epoch()
+ {
+ using std::chrono::system_clock;
+ return system_clock::to_time_t(system_clock::now());
+ }
+
+ static std::time_t utc_mktime(tm* time_ptr)
+ {
+#if defined(_WIN32)
+ return _mkgmtime(time_ptr);
+#else
+ return timegm(time_ptr);
+#endif
+ }
+
+ static tm to_local_time(const std::time_t& t)
+ {
+ tm parts {};
+#if defined(_WIN32)
+ localtime_s(&parts, &t);
+#else
+ parts = *localtime(&t);
+#endif
+ return parts;
+ }
+
+ static Optional<tm> to_utc_time(const std::time_t& t)
+ {
+ tm parts {};
+#if defined(_WIN32)
+ const errno_t err = gmtime_s(&parts, &t);
+ if (err)
+ {
+ return nullopt;
+ }
+#else
+ auto null_if_failed = gmtime_r(&t, &parts);
+ if (null_if_failed == nullptr)
+ {
+ return nullopt;
+ }
+#endif
+ return parts;
+ }
+
+ static tm date_plus_hours(tm* date, const int hours)
+ {
+ using namespace std::chrono_literals;
+ static constexpr std::chrono::seconds SECONDS_IN_ONE_HOUR =
+ std::chrono::duration_cast<std::chrono::seconds>(1h);
+
+ const std::time_t date_in_seconds = utc_mktime(date) + (hours * SECONDS_IN_ONE_HOUR.count());
+ return to_utc_time(date_in_seconds).value_or_exit(VCPKG_LINE_INFO);
+ }
+
static std::string format_time_userfriendly(const std::chrono::nanoseconds& nanos)
{
using std::chrono::duration_cast;
@@ -63,30 +118,14 @@ namespace vcpkg::Chrono
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)
+ const std::time_t ct = get_current_time_as_time_since_epoch();
+ const Optional<tm> opt = to_utc_time(ct);
+ if (auto p_tm = opt.get())
{
- return nullopt;
+ return CTime {*p_tm};
}
-#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;
+ return nullopt;
}
Optional<CTime> CTime::parse(CStringView str)
@@ -111,19 +150,28 @@ namespace vcpkg::Chrono
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);
+ utc_mktime(&ret.m_tm);
+
return ret;
}
+ CTime CTime::add_hours(const int hours) const { return CTime {date_plus_hours(&this->m_tm, hours)}; }
+
std::string CTime::to_string() const
{
- std::array<char, 80> date{};
+ 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);
+ const time_t t = utc_mktime(&m_tm);
return std::chrono::system_clock::from_time_t(t);
}
+
+ tm get_current_date_time_local()
+ {
+ const std::time_t now_time = get_current_time_as_time_since_epoch();
+ return Chrono::to_local_time(now_time);
+ }
}
diff --git a/toolsrc/src/vcpkg/base/downloads.cpp b/toolsrc/src/vcpkg/base/downloads.cpp
new file mode 100644
index 000000000..571562244
--- /dev/null
+++ b/toolsrc/src/vcpkg/base/downloads.cpp
@@ -0,0 +1,183 @@
+#include "pch.h"
+
+#include <vcpkg/base/downloads.h>
+#include <vcpkg/base/hash.h>
+#include <vcpkg/base/util.h>
+
+#if defined(_WIN32)
+#include <VersionHelpers.h>
+#else
+#include <vcpkg/base/system.h>
+#endif
+
+namespace vcpkg::Downloads
+{
+#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",
+ IsWindows8Point1OrGreater() ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY
+ : 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());
+
+ // Win7 IE Proxy fallback
+ if (IsWindows7OrGreater() && !IsWindows8Point1OrGreater()) {
+ // First check if any proxy has been found automatically
+ WINHTTP_PROXY_INFO proxyInfo;
+ DWORD proxyInfoSize = sizeof(WINHTTP_PROXY_INFO);
+ auto noProxyFound =
+ !WinHttpQueryOption(hSession, WINHTTP_OPTION_PROXY, &proxyInfo, &proxyInfoSize)
+ || proxyInfo.dwAccessType == WINHTTP_ACCESS_TYPE_NO_PROXY;
+
+ // If no proxy was found automatically, use IE's proxy settings, if any
+ if (noProxyFound) {
+ WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ieProxy;
+ if (WinHttpGetIEProxyConfigForCurrentUser(&ieProxy) && ieProxy.lpszProxy != nullptr) {
+ WINHTTP_PROXY_INFO proxy;
+ proxy.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
+ proxy.lpszProxy = ieProxy.lpszProxy;
+ proxy.lpszProxyBypass = ieProxy.lpszProxyBypass;
+ WinHttpSetOption(hSession, WINHTTP_OPTION_PROXY, &proxy, sizeof(proxy));
+ }
+ }
+ }
+
+ // 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
+
+ void verify_downloaded_file_hash(const Files::Filesystem& fs,
+ const std::string& url,
+ const fs::path& path,
+ const std::string& sha512)
+ {
+ std::string actual_hash = vcpkg::Hash::get_file_hash(fs, path, "SHA512");
+
+ // <HACK to handle NuGet.org changing nupkg hashes.>
+ // This is the NEW hash for 7zip
+ if (actual_hash == "a9dfaaafd15d98a2ac83682867ec5766720acf6e99d40d1a00d480692752603bf3f3742623f0ea85647a92374df"
+ "405f331afd6021c5cf36af43ee8db198129c0")
+ // This is the OLD hash for 7zip
+ actual_hash = "8c75314102e68d2b2347d592f8e3eb05812e1ebb525decbac472231633753f1d4ca31c8e6881a36144a8da26b257"
+ "1305b3ae3f4e2b85fc4a290aeda63d1a13b8";
+ // </HACK>
+
+ 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);
+ }
+
+ void download_file(vcpkg::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_downloaded_file_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.\n"
+ "fs.rename(%s, %s, %s)",
+ download_path_part,
+ download_path.u8string(),
+ ec.message());
+ }
+}
diff --git a/toolsrc/src/vcpkg/base/files.cpp b/toolsrc/src/vcpkg/base/files.cpp
index 1723b467e..f9bce8631 100644
--- a/toolsrc/src/vcpkg/base/files.cpp
+++ b/toolsrc/src/vcpkg/base/files.cpp
@@ -4,10 +4,26 @@
#include <vcpkg/base/system.h>
#include <vcpkg/base/util.h>
+#if defined(__linux__)
+#include <fcntl.h>
+#include <sys/sendfile.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
namespace vcpkg::Files
{
static const std::regex FILESYSTEM_INVALID_CHARACTERS_REGEX = std::regex(R"([\/:*?"<>|])");
+ void Filesystem::write_contents(const fs::path& file_path, const std::string& data)
+ {
+ std::error_code ec;
+ write_contents(file_path, data, ec);
+ Checks::check_exit(
+ VCPKG_LINE_INFO, !ec, "error while writing file: %s: %s", file_path.u8string(), ec.message());
+ }
+
struct RealFilesystem final : Filesystem
{
virtual Expected<std::string> read_contents(const fs::path& file_path) const override
@@ -55,17 +71,18 @@ 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
@@ -116,6 +133,42 @@ namespace vcpkg::Files
{
fs::stdfs::rename(oldpath, newpath);
}
+ virtual void rename_or_copy(const fs::path& oldpath,
+ const fs::path& newpath,
+ StringLiteral temp_suffix,
+ std::error_code& ec) override
+ {
+ this->rename(oldpath, newpath, ec);
+#if defined(__linux__)
+ if (ec)
+ {
+ auto dst = newpath;
+ dst.replace_filename(dst.filename() + temp_suffix.c_str());
+
+ int i_fd = open(oldpath.c_str(), O_RDONLY);
+ if (i_fd == -1) return;
+
+ int o_fd = creat(dst.c_str(), 0664);
+ if (o_fd == -1)
+ {
+ close(i_fd);
+ return;
+ }
+
+ off_t bytes = 0;
+ struct stat info = {0};
+ fstat(i_fd, &info);
+ auto written_bytes = sendfile(o_fd, i_fd, &bytes, info.st_size);
+ close(i_fd);
+ close(o_fd);
+ if (written_bytes == -1) return;
+
+ this->rename(dst, newpath, ec);
+ if (ec) return;
+ this->remove(oldpath, ec);
+ }
+#endif
+ }
virtual bool remove(const fs::path& path) override { return fs::stdfs::remove(path); }
virtual bool remove(const fs::path& path, std::error_code& ec) override { return fs::stdfs::remove(path, ec); }
virtual std::uintmax_t remove_all(const fs::path& path, std::error_code& ec) override
@@ -163,11 +216,19 @@ namespace vcpkg::Files
{
return fs::stdfs::copy_file(oldpath, newpath, opts, ec);
}
+ virtual void copy_symlink(const fs::path& oldpath, const fs::path& newpath, std::error_code& ec)
+ {
+ return fs::stdfs::copy_symlink(oldpath, newpath, ec);
+ }
virtual fs::file_status status(const fs::path& path, std::error_code& ec) const override
{
return fs::stdfs::status(path, ec);
}
+ virtual fs::file_status symlink_status(const fs::path& path, std::error_code& ec) const override
+ {
+ return fs::stdfs::symlink_status(path, ec);
+ }
virtual void write_contents(const fs::path& file_path, const std::string& data, std::error_code& ec) override
{
ec.clear();
@@ -196,6 +257,40 @@ namespace vcpkg::Files
}
}
}
+
+ virtual std::vector<fs::path> find_from_PATH(const std::string& name) const override
+ {
+#if defined(_WIN32)
+ static constexpr StringLiteral EXTS[] = {".cmd", ".exe", ".bat"};
+ auto paths = Strings::split(System::get_environment_variable("PATH").value_or_exit(VCPKG_LINE_INFO), ";");
+
+ std::vector<fs::path> ret;
+ for (auto&& path : paths)
+ {
+ auto base = path + "/" + name;
+ for (auto&& ext : EXTS)
+ {
+ auto p = fs::u8path(base + ext.c_str());
+ if (Util::find(ret, p) == ret.end() && this->exists(p))
+ {
+ ret.push_back(p);
+ Debug::println("Found path: %s", p.u8string());
+ }
+ }
+ }
+
+ return ret;
+#else
+ const std::string cmd = Strings::format("which %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); });
+#endif
+ }
};
Filesystem& get_real_filesystem()
@@ -218,20 +313,4 @@ namespace vcpkg::Files
}
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/vcpkg/commands.hash.cpp b/toolsrc/src/vcpkg/base/hash.cpp
index 1f709f87b..7a74371db 100644
--- a/toolsrc/src/vcpkg/commands.hash.cpp
+++ b/toolsrc/src/vcpkg/base/hash.cpp
@@ -4,10 +4,16 @@
#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
+#if defined(_WIN32)
+#include <bcrypt.h>
+
+#ifndef NT_SUCCESS
+#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
+#endif
+#endif
+
+namespace vcpkg::Hash
{
static void verify_has_only_allowed_chars(const std::string& s)
{
@@ -18,17 +24,7 @@ namespace vcpkg::Commands::Hash
" % 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)
@@ -154,10 +150,9 @@ namespace vcpkg::Commands::Hash
};
}
- std::string get_file_hash(const VcpkgPaths& paths, const fs::path& path, const std::string& hash_type)
+ std::string get_file_hash(const Files::Filesystem& fs, const fs::path& path, const std::string& hash_type)
{
- Checks::check_exit(
- VCPKG_LINE_INFO, paths.get_filesystem().exists(path), "File %s does not exist", path.u8string());
+ Checks::check_exit(VCPKG_LINE_INFO, fs.exists(path), "File %s does not exist", path.u8string());
return BCryptHasher{hash_type}.hash_file(path);
}
@@ -166,11 +161,8 @@ namespace vcpkg::Commands::Hash
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"))
@@ -182,33 +174,45 @@ namespace vcpkg::Commands::Hash
return hash_type.substr(3, hash_type.length() - 3);
}
- static std::string run_shasum_and_post_process(const std::string& cmd_line)
+ static std::string parse_shasum_output(const std::string& shasum_output)
{
- 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, " ");
+ std::vector<std::string> split = Strings::split(shasum_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,
+ shasum_output,
std::to_string(split.size()));
return split[0];
}
- std::string get_file_hash(const VcpkgPaths& paths, const fs::path& path, const std::string& hash_type)
+ 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, paths.get_filesystem().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);
+ Checks::check_exit(VCPKG_LINE_INFO, fs.exists(path), "File %s does not exist", path.u8string());
+
+ // Try hash-specific tools, like sha512sum
+ {
+ const auto ec_data = System::cmd_execute_and_capture_output(
+ Strings::format(R"(sha%ssum "%s")", digest_size, path.u8string()));
+ if (ec_data.exit_code == 0)
+ {
+ return parse_shasum_output(ec_data.output);
+ }
+ }
+
+ // Try shasum
+ {
+ const auto ec_data = System::cmd_execute_and_capture_output(
+ Strings::format(R"(shasum -a %s "%s")", digest_size, path.u8string()));
+ if (ec_data.exit_code == 0)
+ {
+ return parse_shasum_output(ec_data.output);
+ }
+ }
+
+ Checks::exit_with_message(VCPKG_LINE_INFO, "Could not hash file %s with %s", path.u8string(), hash_type);
}
std::string get_string_hash(const std::string& s, const std::string& hash_type)
@@ -216,31 +220,27 @@ namespace vcpkg::Commands::Hash
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
+ // Try hash-specific tools, like sha512sum
+ {
+ const auto ec_data =
+ System::cmd_execute_and_capture_output(Strings::format(R"(echo -n "%s" | sha%ssum)", s, digest_size));
+ if (ec_data.exit_code == 0)
+ {
+ return parse_shasum_output(ec_data.output);
+ }
+ }
-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));
+ // Try shasum
+ {
+ const auto ec_data = System::cmd_execute_and_capture_output(
+ Strings::format(R"(echo -n "%s" | shasum -a %s)", s, digest_size));
+ if (ec_data.exit_code == 0)
+ {
+ return parse_shasum_output(ec_data.output);
+ }
+ }
- 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, file_to_hash, algorithm);
- System::println(hash);
- Checks::exit_success(VCPKG_LINE_INFO);
+ Checks::exit_with_message(VCPKG_LINE_INFO, "Could not hash input string with %s", hash_type);
}
+#endif
}
diff --git a/toolsrc/src/vcpkg/base/stringrange.cpp b/toolsrc/src/vcpkg/base/stringrange.cpp
new file mode 100644
index 000000000..f7e431c88
--- /dev/null
+++ b/toolsrc/src/vcpkg/base/stringrange.cpp
@@ -0,0 +1,79 @@
+#include "pch.h"
+
+#include <vcpkg/base/checks.h>
+#include <vcpkg/base/stringrange.h>
+
+namespace vcpkg
+{
+ std::vector<StringRange> StringRange::find_all_enclosed(const StringRange& 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<StringRange> 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;
+ }
+
+ StringRange StringRange::find_exactly_one_enclosed(const StringRange& input,
+ const std::string& left_tag,
+ const std::string& right_tag)
+ {
+ std::vector<StringRange> 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 result.front();
+ }
+
+ Optional<StringRange> StringRange::find_at_most_one_enclosed(const StringRange& input,
+ const std::string& left_tag,
+ const std::string& right_tag)
+ {
+ std::vector<StringRange> 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();
+ }
+
+ StringRange::StringRange(const std::string& s) : begin(s.cbegin()), end(s.cend()) {}
+
+ StringRange::StringRange(const std::string::const_iterator begin, const std::string::const_iterator end)
+ : begin(begin), end(end)
+ {
+ }
+
+ std::string StringRange::to_string() const { return std::string(this->begin, this->end); }
+}
diff --git a/toolsrc/src/vcpkg/base/strings.cpp b/toolsrc/src/vcpkg/base/strings.cpp
index fbc33ca42..8d43e7af7 100644
--- a/toolsrc/src/vcpkg/base/strings.cpp
+++ b/toolsrc/src/vcpkg/base/strings.cpp
@@ -49,33 +49,29 @@ namespace vcpkg::Strings::details
namespace vcpkg::Strings
{
+#if defined(_WIN32)
std::wstring to_utf16(const CStringView& s)
{
-#if defined(_WIN32)
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;
-#else
- Checks::unreachable(VCPKG_LINE_INFO);
-#endif
}
+#endif
+#if defined(_WIN32)
std::string to_utf8(const wchar_t* w)
{
-#if defined(_WIN32)
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;
-#else
- Checks::unreachable(VCPKG_LINE_INFO);
-#endif
}
+#endif
std::string escape_string(const CStringView& s, char char_to_escape, char escape_char)
{
@@ -132,6 +128,12 @@ namespace vcpkg::Strings
#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;
diff --git a/toolsrc/src/vcpkg/base/system.cpp b/toolsrc/src/vcpkg/base/system.cpp
index d4210fe6d..9c72f8401 100644
--- a/toolsrc/src/vcpkg/base/system.cpp
+++ b/toolsrc/src/vcpkg/base/system.cpp
@@ -19,19 +19,6 @@
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)
@@ -40,8 +27,9 @@ namespace vcpkg::System
if (bytes == 0) std::abort();
return fs::path(buf, buf + bytes);
#elif defined(__APPLE__)
- uint32_t size = 1024 * 32;
- char buf[size] = {};
+ 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));
@@ -128,34 +116,9 @@ namespace vcpkg::System
R"("%s" %s -P "%s")", cmake_exe.u8string(), cmd_cmake_pass_variables, cmake_script.generic_u8string());
}
- PowershellParameter::PowershellParameter(const CStringView varname, const char* varvalue)
- : s(Strings::format(R"(-%s '%s')", varname, varvalue))
- {
- }
-
- PowershellParameter::PowershellParameter(const CStringView varname, const std::string& varvalue)
- : PowershellParameter(varname, varvalue.c_str())
- {
- }
-
- PowershellParameter::PowershellParameter(const CStringView varname, const fs::path& path)
- : PowershellParameter(varname, path.generic_u8string())
- {
- }
-
- static std::string make_powershell_cmd(const fs::path& script_path,
- const std::vector<PowershellParameter>& parameters)
- {
- const std::string args = Strings::join(" ", parameters, [](auto&& v) { return v.s; });
-
- // TODO: switch out ExecutionPolicy Bypass with "Remove Mark Of The Web" code and restore RemoteSigned
- return Strings::format(
- R"(powershell -NoProfile -ExecutionPolicy Bypass -Command "& {& '%s' %s}")", script_path.u8string(), args);
- }
-
- int cmd_execute_clean(const CStringView cmd_line, const std::unordered_map<std::string, std::string>& extra_env)
- {
#if defined(_WIN32)
+ static std::wstring compute_clean_environment(const std::unordered_map<std::string, std::string>& extra_env)
+ {
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(
@@ -205,11 +168,10 @@ namespace vcpkg::System
L"CUDA_PATH",
// Environmental variable generated automatically by CUDA after installation
L"NVCUDASAMPLES_ROOT",
+ // Enables find_package(Vulkan) in CMake. Environmental variable generated by Vulkan SDK installer
+ L"VULKAN_SDK",
};
- // Flush stdout before launching external process
- fflush(nullptr);
-
std::wstring env_cstr;
for (auto&& env_wstring : env_wstrings)
@@ -240,14 +202,28 @@ namespace vcpkg::System
env_cstr.push_back(L'\0');
}
+ return env_cstr;
+ }
+#endif
+
+#if defined(_WIN32)
+ /// <param name="maybe_environment">If non-null, an environment block to use for the new process. If null, the new
+ /// process will inherit the current environment.</param>
+ static void windows_create_process(const CStringView cmd_line,
+ const wchar_t* maybe_environment,
+ DWORD dwCreationFlags,
+ PROCESS_INFORMATION* process_info) noexcept
+ {
+ Checks::check_exit(VCPKG_LINE_INFO, process_info != nullptr);
+
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));
+ // Flush stdout before launching external process
+ fflush(nullptr);
- // Basically we are wrapping it in quotes
+ // Wrapping the command in a single set of quotes causes cmd.exe to correctly execute
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,
@@ -255,40 +231,81 @@ namespace vcpkg::System
nullptr,
nullptr,
FALSE,
- IDLE_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT,
- env_cstr.data(),
+ IDLE_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT | dwCreationFlags,
+ (void*)maybe_environment,
nullptr,
&startup_info,
- &process_info);
+ process_info);
Checks::check_exit(VCPKG_LINE_INFO, succeeded, "Process creation failed with error code: %lu", GetLastError());
+ }
+#endif
+
+#if defined(_WIN32)
+ void cmd_execute_no_wait(const CStringView cmd_line) noexcept
+ {
+ auto timer = Chrono::ElapsedTimer::create_started();
+
+ PROCESS_INFORMATION process_info;
+ memset(&process_info, 0, sizeof(PROCESS_INFORMATION));
+
+ windows_create_process(cmd_line, nullptr, DETACHED_PROCESS, &process_info);
+
+ CloseHandle(process_info.hThread);
+ CloseHandle(process_info.hProcess);
+
+ Debug::println("CreateProcessW() took %d us", static_cast<int>(timer.microseconds()));
+ }
+#endif
+
+ int cmd_execute_clean(const CStringView cmd_line,
+ const std::unordered_map<std::string, std::string>& extra_env) noexcept
+ {
+ auto timer = Chrono::ElapsedTimer::create_started();
+#if defined(_WIN32)
+
+ PROCESS_INFORMATION process_info;
+ memset(&process_info, 0, sizeof(PROCESS_INFORMATION));
+
+ GlobalState::g_ctrl_c_state.transition_to_spawn_process();
+ auto clean_env = compute_clean_environment(extra_env);
+ windows_create_process(cmd_line, clean_env.c_str(), NULL, &process_info);
CloseHandle(process_info.hThread);
const DWORD result = WaitForSingleObject(process_info.hProcess, INFINITE);
+ GlobalState::g_ctrl_c_state.transition_from_spawn_process();
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", exit_code);
+ CloseHandle(process_info.hProcess);
+
+ 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);
- return system(cmd_line.c_str());
+ int rc = system(cmd_line.c_str());
+ Debug::println("system() returned %d after %d us", rc, static_cast<int>(timer.microseconds()));
+ return rc;
#endif
}
- int cmd_execute(const CStringView cmd_line)
+ int cmd_execute(const CStringView cmd_line) noexcept
{
// Flush stdout before launching external process
fflush(nullptr);
- // Basically we are wrapping it in quotes
#if defined(_WIN32)
+ // We are wrap the command line in quotes to cause cmd.exe to correctly process it
const std::string& actual_cmd_line = Strings::format(R"###("%s")###", cmd_line);
Debug::println("_wsystem(%s)", actual_cmd_line);
+ GlobalState::g_ctrl_c_state.transition_to_spawn_process();
const int exit_code = _wsystem(Strings::to_utf16(actual_cmd_line).c_str());
+ GlobalState::g_ctrl_c_state.transition_from_spawn_process();
Debug::println("_wsystem() returned %d", exit_code);
#else
Debug::println("_system(%s)", cmd_line);
@@ -298,11 +315,8 @@ namespace vcpkg::System
return exit_code;
}
- ExitCodeAndOutput cmd_execute_and_capture_output(const CStringView cmd_line)
+ ExitCodeAndOutput cmd_execute_and_capture_output(const CStringView cmd_line) noexcept
{
- // Flush stdout before launching external process
- fflush(stdout);
-
auto timer = Chrono::ElapsedTimer::create_started();
#if defined(_WIN32)
@@ -311,9 +325,13 @@ namespace vcpkg::System
Debug::println("_wpopen(%s)", actual_cmd_line);
std::wstring output;
wchar_t buf[1024];
+ GlobalState::g_ctrl_c_state.transition_to_spawn_process();
+ // Flush stdout before launching external process
+ fflush(stdout);
const auto pipe = _wpopen(Strings::to_utf16(actual_cmd_line).c_str(), L"r");
if (pipe == nullptr)
{
+ GlobalState::g_ctrl_c_state.transition_from_spawn_process();
return {1, Strings::to_utf8(output.c_str())};
}
while (fgetws(buf, 1024, pipe))
@@ -322,10 +340,12 @@ namespace vcpkg::System
}
if (!feof(pipe))
{
+ GlobalState::g_ctrl_c_state.transition_from_spawn_process();
return {1, Strings::to_utf8(output.c_str())};
}
const auto ec = _pclose(pipe);
+ GlobalState::g_ctrl_c_state.transition_from_spawn_process();
// 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
@@ -344,6 +364,8 @@ namespace vcpkg::System
Debug::println("popen(%s)", actual_cmd_line);
std::string output;
char buf[1024];
+ // Flush stdout before launching external process
+ fflush(stdout);
const auto pipe = popen(actual_cmd_line.c_str(), "r");
if (pipe == nullptr)
{
@@ -366,71 +388,6 @@ namespace vcpkg::System
#endif
}
- void powershell_execute(const std::string& title,
- const fs::path& script_path,
- const std::vector<PowershellParameter>& parameters)
- {
- const std::string cmd = make_powershell_cmd(script_path, parameters);
- const int rc = System::cmd_execute(cmd);
-
- if (rc)
- {
- System::println(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);
- }
- }
-
- std::string powershell_execute_and_capture_output(const std::string& title,
- const fs::path& script_path,
- const std::vector<PowershellParameter>& parameters)
- {
- const std::string cmd = make_powershell_cmd(script_path, parameters);
- auto rc = System::cmd_execute_and_capture_output(cmd);
-
- if (rc.exit_code)
- {
- System::println(Color::error,
- "%s\n"
- "Could not run:\n"
- " '%s'\n"
- "Error message was:\n"
- " %s",
- title,
- script_path.generic_string(),
- rc.output);
-
- {
- 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.exit_code);
- }
-
- // Remove newline from all output.
- // Powershell returns newlines when it hits the column count of the console.
- // For example, this is 80 in cmd on Windows 7. If the expected output is longer than 80 lines, we get
- // newlines in-between the data.
- // To solve this, we design our interaction with powershell to not depend on newlines,
- // and then strip all newlines here.
- rc.output = Strings::replace_all(std::move(rc.output), "\n", "");
-
- return rc.output;
- }
-
void println() { putchar('\n'); }
void print(const CStringView message) { fputs(message.c_str(), stdout); }
@@ -446,7 +403,7 @@ namespace vcpkg::System
#if defined(_WIN32)
const HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
- CONSOLE_SCREEN_BUFFER_INFO console_screen_buffer_info{};
+ CONSOLE_SCREEN_BUFFER_INFO console_screen_buffer_info {};
GetConsoleScreenBufferInfo(console_handle, &console_screen_buffer_info);
const auto original_color = console_screen_buffer_info.wAttributes;
diff --git a/toolsrc/src/vcpkg/build.cpp b/toolsrc/src/vcpkg/build.cpp
index 7a9d35667..0e60fd50e 100644
--- a/toolsrc/src/vcpkg/build.cpp
+++ b/toolsrc/src/vcpkg/build.cpp
@@ -3,9 +3,11 @@
#include <vcpkg/base/checks.h>
#include <vcpkg/base/chrono.h>
#include <vcpkg/base/enums.h>
+#include <vcpkg/base/hash.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>
@@ -61,11 +63,15 @@ namespace vcpkg::Build::Command
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};
+ const Build::BuildPackageOptions build_package_options{
+ Build::UseHeadVersion::NO,
+ Build::AllowDownloads::YES,
+ Build::CleanBuildtrees::NO,
+ Build::CleanPackages::NO,
+ Build::DownloadTool::BUILT_IN,
+ GlobalState::g_binary_caching ? Build::BinaryCaching::YES : Build::BinaryCaching::NO,
+ Build::FailOnTombstone::NO,
+ };
std::set<std::string> features_as_set(full_spec.features.begin(), full_spec.features.end());
features_as_set.emplace("core");
@@ -217,7 +223,12 @@ namespace vcpkg::Build
if (it != toolset.supported_architectures.end()) return it->name;
}
- Checks::exit_with_message(VCPKG_LINE_INFO, "Unsupported toolchain combination %s", target_architecture);
+ 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)
@@ -234,7 +245,7 @@ namespace vcpkg::Build
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)",
+ return Strings::format(R"("%s" %s %s %s %s 2>&1 <NUL)",
toolset.vcvarsall.u8string(),
Strings::join(" ", toolset.vcvarsall_options),
arch,
@@ -357,7 +368,7 @@ namespace vcpkg::Build
{
{"CMD", "BUILD"},
{"PORT", config.scf.core_paragraph->name},
- {"CURRENT_PORT_DIR", config.port_dir / "/."},
+ {"CURRENT_PORT_DIR", config.port_dir},
{"TARGET_TRIPLET", spec.triplet().canonical_name()},
{"VCPKG_PLATFORM_TOOLSET", toolset.version.c_str()},
{"VCPKG_USE_HEAD_VERSION",
@@ -370,9 +381,15 @@ namespace vcpkg::Build
});
auto command = make_build_env_cmd(pre_build_info, toolset);
- if (!command.empty()) command.append(" && ");
+ if (!command.empty())
+ {
+#ifdef _WIN32
+ command.append(" & ");
+#else
+ command.append(" && ");
+#endif
+ }
command.append(cmd_launch_cmake);
-
const auto timer = Chrono::ElapsedTimer::create_started();
const int return_code = System::cmd_execute_clean(command);
@@ -429,7 +446,7 @@ namespace vcpkg::Build
auto buildtree_files = fs.get_files_non_recursive(buildtrees_dir);
for (auto&& file : buildtree_files)
{
- if (fs.is_directory(file) && file.filename() != "src")
+ if (fs.is_directory(file)) // Will only keep the logs
{
std::error_code ec;
fs.remove_all(file, ec);
@@ -445,20 +462,22 @@ namespace vcpkg::Build
const PreBuildInfo& pre_build_info,
Span<const AbiEntry> dependency_abis)
{
- if (!GlobalState::g_binary_caching) return nullopt;
+ if (config.build_package_options.binary_caching == BinaryCaching::NO) 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;
+ std::vector<AbiEntry> abi_tag_entries(dependency_abis.begin(), dependency_abis.end());
- abi_tag_entries.insert(abi_tag_entries.end(), dependency_abis.begin(), dependency_abis.end());
+ abi_tag_entries.emplace_back(AbiEntry{"cmake", paths.get_tool_version(Tools::CMAKE)});
abi_tag_entries.emplace_back(
- AbiEntry{"portfile", Commands::Hash::get_file_hash(paths, config.port_dir / "portfile.cmake", "SHA1")});
+ AbiEntry{"portfile", vcpkg::Hash::get_file_hash(fs, config.port_dir / "portfile.cmake", "SHA1")});
abi_tag_entries.emplace_back(
- AbiEntry{"control", Commands::Hash::get_file_hash(paths, config.port_dir / "CONTROL", "SHA1")});
+ AbiEntry{"control", vcpkg::Hash::get_file_hash(fs, config.port_dir / "CONTROL", "SHA1")});
+
+ abi_tag_entries.emplace_back(AbiEntry{"vcpkg_fixup_cmake_targets", "1"});
abi_tag_entries.emplace_back(AbiEntry{"triplet", pre_build_info.triplet_abi_tag});
@@ -484,7 +503,7 @@ namespace vcpkg::Build
}
auto abi_tag_entries_missing = abi_tag_entries;
- Util::stable_keep_if(abi_tag_entries_missing, [](const AbiEntry& p) { return p.value.empty(); });
+ Util::erase_remove_if(abi_tag_entries_missing, [](const AbiEntry& p) { return !p.value.empty(); });
if (abi_tag_entries_missing.empty())
{
@@ -493,7 +512,7 @@ namespace vcpkg::Build
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(paths, abi_file_path, "SHA1"), abi_file_path};
+ return AbiTagAndFile{Hash::get_file_hash(fs, abi_file_path, "SHA1"), abi_file_path};
}
System::println(
@@ -520,8 +539,8 @@ namespace vcpkg::Build
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()));
+ System::cmd_execute_clean(
+ Strings::format(R"(unzip -qq "%s" "-d%s")", archive_path.u8string(), pkg_path.u8string()));
#endif
}
@@ -537,11 +556,10 @@ namespace vcpkg::Build
#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()));
+ 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()));
@@ -563,8 +581,8 @@ namespace vcpkg::Build
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;
+ Util::erase_remove_if(required_fspecs, [&](FeatureSpec const& fspec) {
+ return status_db.is_installed(fspec) || fspec.name() == name;
});
if (!required_fspecs.empty())
@@ -593,7 +611,7 @@ namespace vcpkg::Build
const auto abi_tag_and_file = maybe_abi_tag_and_file.get();
- if (GlobalState::g_binary_caching && abi_tag_and_file)
+ if (config.build_package_options.binary_caching == BinaryCaching::YES && abi_tag_and_file)
{
const fs::path archives_root_dir = paths.root / "archives";
const std::string archive_name = abi_tag_and_file->tag + ".zip";
@@ -615,8 +633,16 @@ namespace vcpkg::Build
if (fs.exists(archive_tombstone_path))
{
- System::println("Found failure tombstone: %s", archive_tombstone_path.u8string());
- return BuildResult::BUILD_FAILED;
+ if (config.build_package_options.fail_on_tombstone == FailOnTombstone::YES)
+ {
+ System::println("Found failure tombstone: %s", archive_tombstone_path.u8string());
+ return BuildResult::BUILD_FAILED;
+ }
+ else
+ {
+ System::println(
+ System::Color::warning, "Found failure tombstone: %s", archive_tombstone_path.u8string());
+ }
}
System::println("Could not locate cached archive: %s", archive_path.u8string());
@@ -637,10 +663,14 @@ namespace vcpkg::Build
compress_archive(paths, spec, tmp_archive_path);
fs.create_directories(archive_path.parent_path(), ec);
- fs.rename(tmp_archive_path, archive_path, ec);
+ fs.rename_or_copy(tmp_archive_path, archive_path, ".tmp", ec);
if (ec)
- System::println(
- System::Color::warning, "Failed to store binary cache: %s", archive_path.u8string());
+ {
+ System::println(System::Color::warning,
+ "Failed to store binary cache %s: %s",
+ archive_path.u8string(),
+ ec.message());
+ }
else
System::println("Stored binary cache: %s", archive_path.u8string());
}
@@ -769,26 +799,6 @@ namespace vcpkg::Build
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, triplet_file_path, "SHA1");
- s_hash_cache.emplace(triplet_file_path, hash);
- return hash;
- }
- else
- {
- return std::string();
- }
- }();
-
const auto cmd_launch_cmake = System::make_cmake_cmd(cmake_exe_path,
ports_cmake_script_path,
{
@@ -800,7 +810,6 @@ namespace vcpkg::Build
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);
@@ -876,6 +885,47 @@ namespace vcpkg::Build
Checks::exit_with_message(VCPKG_LINE_INFO, "Unknown variable name %s", line);
}
+ pre_build_info.triplet_abi_tag = [&]() {
+ const auto& fs = paths.get_filesystem();
+ static std::map<fs::path, std::string> s_hash_cache;
+
+ auto it_hash = s_hash_cache.find(triplet_file_path);
+ if (it_hash != s_hash_cache.end())
+ {
+ return it_hash->second;
+ }
+ auto hash = Hash::get_file_hash(fs, triplet_file_path, "SHA1");
+
+ if (auto p = pre_build_info.external_toolchain_file.get())
+ {
+ hash += "-";
+ hash += Hash::get_file_hash(fs, *p, "SHA1");
+ }
+ else if (pre_build_info.cmake_system_name == "Linux")
+ {
+ hash += "-";
+ hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "linux.cmake", "SHA1");
+ }
+ else if (pre_build_info.cmake_system_name == "Darwin")
+ {
+ hash += "-";
+ hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "osx.cmake", "SHA1");
+ }
+ else if (pre_build_info.cmake_system_name == "FreeBSD")
+ {
+ hash += "-";
+ hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "freebsd.cmake", "SHA1");
+ }
+ else if (pre_build_info.cmake_system_name == "Android")
+ {
+ hash += "-";
+ hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "android.cmake", "SHA1");
+ }
+
+ s_hash_cache.emplace(triplet_file_path, hash);
+ return hash;
+ }();
+
return pre_build_info;
}
ExtendedBuildResult::ExtendedBuildResult(BuildResult code) : code(code) {}
diff --git a/toolsrc/src/vcpkg/commands.autocomplete.cpp b/toolsrc/src/vcpkg/commands.autocomplete.cpp
index 564404421..3b353feec 100644
--- a/toolsrc/src/vcpkg/commands.autocomplete.cpp
+++ b/toolsrc/src/vcpkg/commands.autocomplete.cpp
@@ -40,26 +40,25 @@ namespace vcpkg::Commands::Autocomplete
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);
+ std::vector<std::string> public_commands = {"install",
+ "search",
+ "remove",
+ "list",
+ "update",
+ "hash",
+ "help",
+ "integrate",
+ "export",
+ "edit",
+ "create",
+ "owns",
+ "cache",
+ "version",
+ "contact",
+ "upgrade"};
+
+ Util::erase_remove_if(public_commands, [&](const std::string& s) {
+ return !Strings::case_insensitive_ascii_starts_with(s, requested_command);
});
if (!public_commands.empty())
@@ -78,8 +77,8 @@ namespace vcpkg::Commands::Autocomplete
"portsdiff",
};
- Util::unstable_keep_if(private_commands, [&](const std::string& s) {
- return Strings::case_insensitive_ascii_starts_with(s, requested_command);
+ Util::erase_remove_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));
@@ -98,8 +97,8 @@ namespace vcpkg::Commands::Autocomplete
}
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);
+ Util::erase_remove_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);
@@ -124,6 +123,7 @@ namespace vcpkg::Commands::Autocomplete
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},
+ CommandEntry{"upgrade", R"###(^upgrade(\s+)(\S*)$)###", Upgrade::COMMAND_STRUCTURE},
};
for (auto&& command : COMMANDS)
@@ -150,8 +150,8 @@ namespace vcpkg::Commands::Autocomplete
}
}
- Util::unstable_keep_if(results, [&](const std::string& s) {
- return Strings::case_insensitive_ascii_starts_with(s, prefix);
+ Util::erase_remove_if(results, [&](const std::string& s) {
+ return !Strings::case_insensitive_ascii_starts_with(s, prefix);
});
if (command.name == "install" && results.size() == 1 && !is_option)
diff --git a/toolsrc/src/vcpkg/commands.cache.cpp b/toolsrc/src/vcpkg/commands.cache.cpp
index a9d8ba03c..464f4f9ee 100644
--- a/toolsrc/src/vcpkg/commands.cache.cpp
+++ b/toolsrc/src/vcpkg/commands.cache.cpp
@@ -61,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
index e2b93dc7e..551eee27b 100644
--- a/toolsrc/src/vcpkg/commands.ci.cpp
+++ b/toolsrc/src/vcpkg/commands.ci.cpp
@@ -28,6 +28,7 @@ namespace vcpkg::Commands::CI
static constexpr StringLiteral OPTION_DRY_RUN = "--dry-run";
static constexpr StringLiteral OPTION_EXCLUDE = "--exclude";
+ static constexpr StringLiteral OPTION_PURGE_TOMBSTONES = "--purge-tombstones";
static constexpr StringLiteral OPTION_XUNIT = "--x-xunit";
static constexpr std::array<CommandSetting, 2> CI_SETTINGS = {{
@@ -35,8 +36,10 @@ namespace vcpkg::Commands::CI
{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"}}};
+ static constexpr std::array<CommandSwitch, 2> CI_SWITCHES = {{
+ {OPTION_DRY_RUN, "Print out plan without execution"},
+ {OPTION_PURGE_TOMBSTONES, "Purge failure tombstones and retry building the ports"},
+ }};
const CommandStructure COMMAND_STRUCTURE = {
Help::create_example_string("ci x64-windows"),
@@ -46,10 +49,18 @@ namespace vcpkg::Commands::CI
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)
+ struct UnknownCIPortsResults
+ {
+ std::vector<FullPackageSpec> unknown;
+ std::map<PackageSpec, Build::BuildResult> known;
+ std::map<PackageSpec, std::vector<std::string>> features;
+ };
+
+ static UnknownCIPortsResults find_unknown_ports_for_ci(const VcpkgPaths& paths,
+ const std::set<std::string>& exclusions,
+ const Dependencies::PortFileProvider& provider,
+ const std::vector<FeatureSpec>& fspecs,
+ const bool purge_tombstones)
{
UnknownCIPortsResults ret;
@@ -58,14 +69,19 @@ namespace vcpkg::Commands::CI
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};
+ const Build::BuildPackageOptions install_plan_options = {
+ Build::UseHeadVersion::NO,
+ Build::AllowDownloads::YES,
+ Build::CleanBuildtrees::YES,
+ Build::CleanPackages::YES,
+ Build::DownloadTool::BUILT_IN,
+ GlobalState::g_binary_caching ? Build::BinaryCaching::YES : Build::BinaryCaching::NO,
+ Build::FailOnTombstone::YES,
+ };
vcpkg::Cache<Triplet, Build::PreBuildInfo> pre_build_info_cache;
- auto action_plan = Dependencies::create_feature_install_plan(provider, fspecs, StatusParagraphs{});
+ auto action_plan = Dependencies::create_feature_install_plan(provider, fspecs, StatusParagraphs {});
for (auto&& action : action_plan)
{
@@ -77,7 +93,7 @@ namespace vcpkg::Commands::CI
{
auto triplet = p->spec.triplet();
- const Build::BuildPackageConfig build_config{
+ const Build::BuildPackageConfig build_config {
*scf, triplet, paths.port_dir(p->spec), install_plan_options, p->feature_list};
auto dependency_abis =
@@ -114,8 +130,17 @@ namespace vcpkg::Commands::CI
auto archive_path = archives_root_dir / archive_subpath;
auto archive_tombstone_path = archives_root_dir / "fail" / archive_subpath;
+ if (purge_tombstones)
+ {
+ std::error_code ec;
+ fs.remove(archive_tombstone_path, ec); // Ignore error
+ }
+
bool b_will_build = false;
+ ret.features.emplace(p->spec,
+ std::vector<std::string> {p->feature_list.begin(), p->feature_list.end()});
+
if (Util::Sets::contains(exclusions, p->spec.name()))
{
ret.known.emplace(p->spec, BuildResult::EXCLUDED);
@@ -141,7 +166,7 @@ namespace vcpkg::Commands::CI
}
else
{
- ret.unknown.push_back(p->spec);
+ ret.unknown.push_back({p->spec, {p->feature_list.begin(), p->feature_list.end()}});
b_will_build = true;
}
@@ -169,7 +194,8 @@ namespace vcpkg::Commands::CI
exclusions_set.insert(exclusions.begin(), exclusions.end());
}
- auto is_dry_run = Util::Sets::contains(options.switches, OPTION_DRY_RUN);
+ const auto is_dry_run = Util::Sets::contains(options.switches, OPTION_DRY_RUN);
+ const auto purge_tombstones = Util::Sets::contains(options.switches, OPTION_PURGE_TOMBSTONES);
std::vector<Triplet> triplets;
for (const std::string& triplet : args.command_arguments)
@@ -185,10 +211,15 @@ namespace vcpkg::Commands::CI
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};
+ const Build::BuildPackageOptions install_plan_options = {
+ Build::UseHeadVersion::NO,
+ Build::AllowDownloads::YES,
+ Build::CleanBuildtrees::YES,
+ Build::CleanPackages::YES,
+ Build::DownloadTool::BUILT_IN,
+ GlobalState::g_binary_caching ? Build::BinaryCaching::YES : Build::BinaryCaching::NO,
+ Build::FailOnTombstone::YES,
+ };
std::vector<std::map<PackageSpec, BuildResult>> all_known_results;
@@ -198,25 +229,48 @@ namespace vcpkg::Commands::CI
{
Input::check_triplet(triplet, paths);
+ Dependencies::PackageGraph pgraph(paths_port_file, status_db);
+
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 split_specs =
+ find_unknown_ports_for_ci(paths, exclusions_set, paths_port_file, all_fspecs, purge_tombstones);
+ auto fspecs = FullPackageSpec::to_feature_specs(split_specs.unknown);
- auto action_plan = Dependencies::create_feature_install_plan(paths_port_file, fspecs, status_db);
+ for (auto&& fspec : fspecs)
+ pgraph.install(fspec);
- for (auto&& action : action_plan)
- {
- if (auto p = action.install_action.get())
+ auto action_plan = [&]() {
+ int iterations = 0;
+ do
{
- p->build_options = install_plan_options;
- if (Util::Sets::contains(exclusions_set, p->spec.name()))
+ bool inconsistent = false;
+ auto action_plan = pgraph.serialize();
+
+ for (auto&& action : action_plan)
{
- p->plan_type = InstallPlanType::EXCLUDED;
+ if (auto p = action.install_action.get())
+ {
+ p->build_options = install_plan_options;
+ if (Util::Sets::contains(exclusions_set, p->spec.name()))
+ {
+ p->plan_type = InstallPlanType::EXCLUDED;
+ }
+
+ for (auto&& feature : split_specs.features[p->spec])
+ if (p->feature_list.find(feature) == p->feature_list.end())
+ {
+ pgraph.install({p->spec, feature});
+ inconsistent = true;
+ }
+ }
}
- }
- }
+
+ if (!inconsistent) return action_plan;
+ Checks::check_exit(VCPKG_LINE_INFO, ++iterations < 100);
+ } while (true);
+ }();
if (is_dry_run)
{
@@ -251,7 +305,7 @@ namespace vcpkg::Commands::CI
for (auto&& result : known_result)
{
xunit_doc +=
- Install::InstallSummary::xunit_result(result.first, Chrono::ElapsedTime{}, result.second);
+ Install::InstallSummary::xunit_result(result.first, Chrono::ElapsedTime {}, result.second);
}
}
diff --git a/toolsrc/src/vcpkg/commands.cpp b/toolsrc/src/vcpkg/commands.cpp
index 8b6ffb3d7..db265514f 100644
--- a/toolsrc/src/vcpkg/commands.cpp
+++ b/toolsrc/src/vcpkg/commands.cpp
@@ -1,5 +1,7 @@
#include "pch.h"
+#include <vcpkg/base/hash.h>
+
#include <vcpkg/build.h>
#include <vcpkg/commands.h>
#include <vcpkg/export.h>
@@ -43,7 +45,8 @@ namespace vcpkg::Commands
{"portsdiff", &PortsDiff::perform_and_exit},
{"autocomplete", &Autocomplete::perform_and_exit},
{"hash", &Hash::perform_and_exit},
- // {"fetch", &Fetch::perform_and_exit},
+ {"fetch", &Fetch::perform_and_exit},
+ {"x-vsinstances", &X_VSInstances::perform_and_exit},
};
return t;
}
@@ -57,3 +60,47 @@ namespace vcpkg::Commands
return t;
}
}
+
+namespace vcpkg::Commands::Fetch
+{
+ 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 = paths.get_tool_exe(tool);
+ System::println(tool_path.u8string());
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+}
+
+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 = vcpkg::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/vcpkg/commands.dependinfo.cpp b/toolsrc/src/vcpkg/commands.dependinfo.cpp
index 1ca658216..5f72e965b 100644
--- a/toolsrc/src/vcpkg/commands.dependinfo.cpp
+++ b/toolsrc/src/vcpkg/commands.dependinfo.cpp
@@ -1,61 +1,166 @@
-#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);
- }
-}
+#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
+{
+ constexpr StringLiteral OPTION_DOT = "--dot";
+ constexpr StringLiteral OPTION_DGML = "--dgml";
+
+ constexpr std::array<CommandSwitch, 2> DEPEND_SWITCHES = { {
+ { OPTION_DOT, "Creates graph on basis of dot" },
+ { OPTION_DGML, "Creates graph on basis of dgml" },
+ } };
+
+ const CommandStructure COMMAND_STRUCTURE = {
+ Help::create_example_string(R"###(depend-info [pat])###"),
+ 0,
+ 1,
+ { DEPEND_SWITCHES,{} },
+ nullptr,
+ };
+
+ std::string replace_dashes_with_underscore(const std::string& input)
+ {
+ std::string output = input;
+ std::replace(output.begin(), output.end(), '-', '_');
+ return output;
+ }
+
+ std::string create_dot_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;
+ }
+
+ std::string create_dgml_as_string(
+ const std::vector<std::unique_ptr<SourceControlFile>>& source_control_files)
+ {
+ std::string s;
+ s.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
+ s.append("<DirectedGraph xmlns=\"http://schemas.microsoft.com/vs/2009/dgml\">");
+
+ std::string nodes, links;
+ for (const auto& source_control_file : source_control_files)
+ {
+ const SourceParagraph& source_paragraph = *source_control_file->core_paragraph;
+ const std::string name = source_paragraph.name;
+ nodes.append(Strings::format("<Node Id=\"%s\" />", name));
+
+ // Iterate over dependencies.
+ for (const Dependency& d : source_paragraph.depends)
+ {
+ links.append(Strings::format("<Link Source=\"%s\" Target=\"%s\" />", name, d.name()));
+ }
+
+ // Iterate over feature dependencies.
+ const std::vector<std::unique_ptr<FeatureParagraph>>& feature_paragraphs = source_control_file->feature_paragraphs;
+ for (const auto& feature_paragraph : feature_paragraphs)
+ {
+ for (const Dependency& d : feature_paragraph->depends)
+ {
+ links.append(Strings::format("<Link Source=\"%s\" Target=\"%s\" />", name, d.name()));
+ }
+ }
+ }
+
+ s.append(Strings::format("<Nodes>%s</Nodes>", nodes));
+
+ s.append(Strings::format("<Links>%s</Links>", links));
+
+ s.append("</DirectedGraph>");
+ return s;
+ }
+
+ std::string create_graph_as_string(
+ const std::unordered_set<std::string>& switches,
+ const std::vector<std::unique_ptr<SourceControlFile>>& source_control_files)
+ {
+ if (Util::Sets::contains(switches, OPTION_DOT))
+ {
+ return create_dot_as_string(source_control_files);
+ }
+ else if (Util::Sets::contains(switches, OPTION_DGML))
+ {
+ return create_dgml_as_string(source_control_files);
+ }
+ return "";
+ }
+
+ void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
+ {
+ const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
+
+ auto 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;
+ });
+ }
+
+ if (!options.switches.empty())
+ {
+ const std::string graph_as_string = create_graph_as_string(options.switches, source_control_files);
+ System::println(graph_as_string);
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+
+ 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
index b6324565c..044ae1c47 100644
--- a/toolsrc/src/vcpkg/commands.edit.cpp
+++ b/toolsrc/src/vcpkg/commands.edit.cpp
@@ -64,21 +64,37 @@ namespace vcpkg::Commands::Edit
{
if (Util::Sets::contains(options.switches, OPTION_ALL))
{
+ const auto& fs = paths.get_filesystem();
+ auto packages = fs.get_files_non_recursive(paths.packages);
+
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")###",
+ const auto pattern = port_name + "_";
+
+ std::string package_paths;
+ for (auto&& package : packages)
+ {
+ if (Strings::case_insensitive_ascii_starts_with(package.filename().u8string(), pattern))
+ {
+ package_paths.append(Strings::format(" \"%s\"", package.u8string()));
+ }
+ }
+
+ return Strings::format(R"###("%s" "%s" "%s"%s)###",
portpath.u8string(),
portfile.u8string(),
- buildtrees_current_dir.u8string());
+ buildtrees_current_dir.u8string(),
+ package_paths);
});
}
if (Util::Sets::contains(options.switches, OPTION_BUILDTREES))
{
return Util::fmap(ports, [&](const std::string& port_name) -> std::string {
- return (paths.buildtrees / port_name).u8string();
+ const auto buildtrees_current_dir = paths.buildtrees / port_name;
+ return Strings::format(R"###("%s")###", buildtrees_current_dir.u8string());
});
}
@@ -91,9 +107,6 @@ namespace vcpkg::Commands::Edit
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);
@@ -113,6 +126,10 @@ namespace vcpkg::Commands::Edit
candidate_paths.emplace_back(*editor_path);
}
+#ifdef _WIN32
+ 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";
+
const auto& program_files = System::get_program_files_platform_bitness();
if (const fs::path* pf = program_files.get())
{
@@ -127,8 +144,20 @@ namespace vcpkg::Commands::Edit
candidate_paths.push_back(*pf / VS_CODE);
}
+ const auto& app_data = System::get_environment_variable("APPDATA");
+ if (const auto* ad = app_data.get())
+ {
+ const fs::path default_base = fs::path{*ad}.parent_path() / "Local" / "Programs";
+ candidate_paths.push_back(default_base / VS_CODE_INSIDERS);
+ candidate_paths.push_back(default_base / VS_CODE);
+ }
+
const std::vector<fs::path> from_registry = find_from_registry();
candidate_paths.insert(candidate_paths.end(), from_registry.cbegin(), from_registry.cend());
+#elif defined(__APPLE__)
+ candidate_paths.push_back(fs::path{"/Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/bin/code"});
+ candidate_paths.push_back(fs::path{"/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code"});
+#endif
const auto it = Util::find_if(candidate_paths, [&](const fs::path& p) { return fs.exists(p); });
if (it == candidate_paths.cend())
@@ -146,6 +175,16 @@ namespace vcpkg::Commands::Edit
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);
+
+ auto editor_exe = env_editor.filename().u8string();
+
+#ifdef _WIN32
+ if (editor_exe == "Code.exe" || editor_exe == "Code - Insiders.exe")
+ {
+ System::cmd_execute_no_wait(cmd_line + " <NUL");
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+#endif
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
index d078baedb..ea00617d4 100644
--- a/toolsrc/src/vcpkg/commands.env.cpp
+++ b/toolsrc/src/vcpkg/commands.env.cpp
@@ -23,9 +23,9 @@ namespace vcpkg::Commands::Env
}};
const CommandStructure COMMAND_STRUCTURE = {
- Help::create_example_string("env --triplet x64-windows"),
- 0,
+ Help::create_example_string("env <optional command> --triplet x64-windows"),
0,
+ 1,
{SWITCHES, {}},
nullptr,
};
@@ -64,11 +64,12 @@ namespace vcpkg::Commands::Env
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);
+ std::string env_cmd_prefix = env_cmd.empty() ? "" : Strings::format("%s && ", env_cmd);
+ std::string env_cmd_suffix =
+ args.command_arguments.empty() ? "cmd" : Strings::format("cmd /c %s", args.command_arguments.at(0));
+ const std::string cmd = Strings::format("%s%s", env_cmd_prefix, env_cmd_suffix);
+ System::cmd_execute_clean(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
index ae106196a..62725a90a 100644
--- a/toolsrc/src/vcpkg/commands.exportifw.cpp
+++ b/toolsrc/src/vcpkg/commands.exportifw.cpp
@@ -13,7 +13,7 @@ namespace vcpkg::Export::IFW
static std::string create_release_date()
{
- const tm date_time = System::get_current_date_time();
+ const tm date_time = Chrono::get_current_date_time_local();
// Format is: YYYY-mm-dd
// 10 characters + 1 null terminating character will be written for a total of 11 chars
diff --git a/toolsrc/src/vcpkg/commands.fetch.cpp b/toolsrc/src/vcpkg/commands.fetch.cpp
deleted file mode 100644
index 1e31e6bc4..000000000
--- a/toolsrc/src/vcpkg/commands.fetch.cpp
+++ /dev/null
@@ -1,714 +0,0 @@
-#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::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;
- 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;
- }
-
- static Optional<std::string> extract_string_between_delimiters(const std::string& input,
- const std::string& left_delim,
- const std::string& right_delim,
- const size_t& starting_offset = 0)
- {
- const size_t from = input.find(left_delim, starting_offset);
- if (from == std::string::npos) return nullopt;
-
- const size_t substring_start = from + left_delim.length();
-
- const size_t to = input.find(right_delim, substring_start);
- if (from == std::string::npos) return nullopt;
-
- return input.substr(substring_start, to - substring_start);
- }
-
- 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";
-
- const auto get_string_inside_tags =
- [](const std::string& input, const std::string& left_delim, const std::string& right_delim) -> std::string {
- Optional<std::string> result = extract_string_between_delimiters(input, left_delim, right_delim);
- Checks::check_exit(VCPKG_LINE_INFO,
- result.has_value(),
- "Could not find tag <%s>.*<%s> in %s",
- left_delim,
- right_delim,
- XML_PATH.generic_string());
-
- return *result.get();
- };
-
- 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 = get_string_inside_tags(XML, match_tool_entry[0], R"(</tool>)");
-
- const std::string version_as_string = get_string_inside_tags(tool_data, "<version>", R"(</version>)");
- const std::string exe_relative_path =
- get_string_inside_tags(tool_data, "<exeRelativePath>", R"(</exeRelativePath>)");
- const std::string url = get_string_inside_tags(tool_data, "<url>", R"(</url>)");
- const std::string sha512 = get_string_inside_tags(tool_data, "<sha512>", R"(</sha512>)");
- auto archive_name = extract_string_between_delimiters(tool_data, "<archiveName>", R"(</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.downloads / "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),
- 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(const std::vector<fs::path>& candidate_paths,
- const std::string& version_check_arguments,
- const std::array<int, 3>& expected_version)
- {
- auto it = Util::find_if(candidate_paths, [&](const fs::path& p) {
- 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 std::move(*it);
- }
-
- return nullopt;
- }
-
- static std::vector<std::string> keep_data_lines(const std::string& data_blob)
- {
- static const std::regex DATA_LINE_REGEX(R"(<sol>::(.+?)(?=::<eol>))");
-
- std::vector<std::string> data_lines;
-
- const std::sregex_iterator it(data_blob.cbegin(), data_blob.cend(), DATA_LINE_REGEX);
- const std::sregex_iterator end;
- for (std::sregex_iterator i = it; i != end; ++i)
- {
- const std::smatch match = *i;
- data_lines.push_back(match[1].str());
- }
-
- return data_lines;
- }
-
-#if !defined(_WIN32)
- 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_partial, ec);
- fs.create_directories(to_path_partial, ec);
-
- const auto ext = archive.extension();
- 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());
- }
-
- fs.rename(to_path_partial, to_path);
- }
-
- static void verify_hash(const VcpkgPaths& paths,
- const std::string& url,
- const fs::path& path,
- const std::string& sha512)
- {
- const std::string actual_hash = Hash::get_file_hash(paths, 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);
- }
-
- static void download_file(const VcpkgPaths& paths,
- const std::string& url,
- const fs::path& download_path,
- const std::string& sha512)
- {
- Files::Filesystem& fs = paths.get_filesystem();
- const std::string download_path_part = download_path.u8string() + ".part";
- std::error_code ec;
- fs.remove(download_path_part, ec);
- 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);
-
- verify_hash(paths, url, download_path_part, sha512);
- fs.rename(download_path_part, download_path);
- }
-
-#endif
- 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]);
- 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);
-#if defined(_WIN32)
- const fs::path script = paths.scripts / "fetchtool.ps1";
- const std::string title = Strings::format(
- "Fetching %s version %s (No sufficient installed version was found)", tool_name, version_as_string);
- const System::PowershellParameter tool_param("tool", tool_name);
- const std::string output = System::powershell_execute_and_capture_output(title, script, {tool_param});
-
- const std::vector<std::string> tool_path = keep_data_lines(output);
- Checks::check_exit(VCPKG_LINE_INFO, tool_path.size() == 1, "Expected tool path, but got %s", output);
-
- const fs::path actual_downloaded_path = Strings::trim(std::string{tool_path.at(0)});
- const fs::path& expected_downloaded_path = tool_data.exe_path;
- std::error_code ec;
- const auto eq = fs::stdfs::equivalent(expected_downloaded_path, actual_downloaded_path, ec);
- Checks::check_exit(VCPKG_LINE_INFO,
- eq && !ec,
- "Expected tool downloaded path to be %s, but was %s",
- expected_downloaded_path.u8string(),
- actual_downloaded_path.u8string());
- return actual_downloaded_path;
-#else
- const auto& fs = paths.get_filesystem();
- if (!fs.exists(tool_data.download_path))
- {
- System::println("Downloading %s...", tool_name);
- download_file(paths, tool_data.url, tool_data.download_path, tool_data.sha512);
- System::println("Downloading %s... done.", tool_name);
- }
- else
- {
- verify_hash(paths, tool_data.url, tool_data.download_path, tool_data.sha512);
- }
-
- 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);
-
- Checks::check_exit(VCPKG_LINE_INFO,
- fs.exists(tool_data.exe_path),
- "Expected %s to exist after extracting",
- tool_data.exe_path);
-
- return tool_data.exe_path;
-#endif
- }
-
- 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(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(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(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)
- {
-#if defined(_WIN32)
- static const ToolData TOOL_DATA = parse_tool_data_from_xml(paths, "git");
-#else
- static const ToolData TOOL_DATA = ToolData{{2, 7, 4}, ""};
-#endif
- 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(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(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
- {
- fs::path root_path;
- std::string version;
- std::string release_type;
- std::string preference_weight; // Mostly unused, just for verification that order is as intended
-
- std::string major_version() const { return version.substr(0, 2); }
- };
-
- static std::vector<VisualStudioInstance> get_visual_studio_instances(const VcpkgPaths& paths)
- {
- const fs::path script = paths.scripts / "findVisualStudioInstallationInstances.ps1";
- const std::string output =
- System::powershell_execute_and_capture_output("Detecting Visual Studio instances", script);
-
- const std::vector<std::string> instances_as_strings = keep_data_lines(output);
- Checks::check_exit(VCPKG_LINE_INFO,
- !instances_as_strings.empty(),
- "Could not detect any Visual Studio instances.\n"
- "Powershell script:\n"
- " %s\n"
- "returned:\n"
- "%s",
- script.generic_string(),
- output);
-
- std::vector<VisualStudioInstance> instances;
- for (const std::string& instance_as_string : instances_as_strings)
- {
- const std::vector<std::string> split = Strings::split(instance_as_string, "::");
- Checks::check_exit(VCPKG_LINE_INFO,
- split.size() == 4,
- "Invalid Visual Studio instance format.\n"
- "Expected: PreferenceWeight::ReleaseType::Version::PathToVisualStudio\n"
- "Actual : %s\n",
- instance_as_string);
- instances.push_back({split.at(3), split.at(2), split.at(1), split.at(0)});
- }
-
- return instances;
- }
-
- std::vector<Toolset> find_toolset_instances(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 std::vector<VisualStudioInstance> vs_instances = get_visual_studio_instances(paths);
- const bool v140_is_available = Util::find_if(vs_instances, [&](const VisualStudioInstance& vs_instance) {
- return vs_instance.major_version() == "14";
- }) != vs_instances.cend();
-
- for (const VisualStudioInstance& vs_instance : vs_instances)
- {
- 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 = Toolset{
- 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 = Toolset{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;
- }
-
- 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.integrate.cpp b/toolsrc/src/vcpkg/commands.integrate.cpp
index 8897ea138..82172e363 100644
--- a/toolsrc/src/vcpkg/commands.integrate.cpp
+++ b/toolsrc/src/vcpkg/commands.integrate.cpp
@@ -5,6 +5,7 @@
#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
@@ -369,6 +370,43 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console
#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"
@@ -423,9 +461,7 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console
}
if (args.command_arguments[0] == Subcommand::POWERSHELL)
{
- System::powershell_execute("PowerShell Tab-Completion",
- paths.scripts / "addPoshVcpkgToPowershellProfile.ps1");
- Checks::exit_success(VCPKG_LINE_INFO);
+ return integrate_powershell(paths);
}
#endif
diff --git a/toolsrc/src/vcpkg/commands.list.cpp b/toolsrc/src/vcpkg/commands.list.cpp
index 1bfbc4247..cadc06ad3 100644
--- a/toolsrc/src/vcpkg/commands.list.cpp
+++ b/toolsrc/src/vcpkg/commands.list.cpp
@@ -76,7 +76,7 @@ namespace vcpkg::Commands::List
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())
+ if (!Strings::case_insensitive_ascii_contains(displayname, args.command_arguments[0]))
{
continue;
}
diff --git a/toolsrc/src/vcpkg/commands.search.cpp b/toolsrc/src/vcpkg/commands.search.cpp
index 33a642e40..263c86698 100644
--- a/toolsrc/src/vcpkg/commands.search.cpp
+++ b/toolsrc/src/vcpkg/commands.search.cpp
@@ -10,46 +10,9 @@
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)
@@ -80,8 +43,7 @@ namespace vcpkg::Commands::Search
}
}
- static constexpr std::array<CommandSwitch, 2> SEARCH_SWITCHES = {{
- {OPTION_GRAPH, "Open editor into the port-specific buildtree subfolder"},
+ static constexpr std::array<CommandSwitch, 1> SEARCH_SWITCHES = {{
{OPTION_FULLDESC, "Do not truncate long text"},
}};
@@ -102,13 +64,6 @@ namespace vcpkg::Commands::Search
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)
diff --git a/toolsrc/src/vcpkg/commands.upgrade.cpp b/toolsrc/src/vcpkg/commands.upgrade.cpp
index a902ddeaf..0e58b012d 100644
--- a/toolsrc/src/vcpkg/commands.upgrade.cpp
+++ b/toolsrc/src/vcpkg/commands.upgrade.cpp
@@ -1,8 +1,8 @@
#include "pch.h"
-#include <vcpkg/base/util.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>
@@ -10,6 +10,8 @@
#include <vcpkg/update.h>
#include <vcpkg/vcpkglib.h>
+#include <vcpkg/base/util.h>
+
namespace vcpkg::Commands::Upgrade
{
using Install::KeepGoing;
@@ -141,11 +143,15 @@ namespace vcpkg::Commands::Upgrade
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};
+ const Build::BuildPackageOptions install_plan_options = {
+ Build::UseHeadVersion::NO,
+ Build::AllowDownloads::YES,
+ Build::CleanBuildtrees::NO,
+ Build::CleanPackages::NO,
+ Build::DownloadTool::BUILT_IN,
+ GlobalState::g_binary_caching ? Build::BinaryCaching::YES : Build::BinaryCaching::NO,
+ Build::FailOnTombstone::NO,
+ };
// Set build settings for all install actions
for (auto&& action : plan)
diff --git a/toolsrc/src/vcpkg/commands.xvsinstances.cpp b/toolsrc/src/vcpkg/commands.xvsinstances.cpp
new file mode 100644
index 000000000..d748b6b2f
--- /dev/null
+++ b/toolsrc/src/vcpkg/commands.xvsinstances.cpp
@@ -0,0 +1,33 @@
+#include "pch.h"
+
+#include <vcpkg/commands.h>
+#include <vcpkg/help.h>
+#include <vcpkg/visualstudio.h>
+
+namespace vcpkg::Commands::X_VSInstances
+{
+ const CommandStructure COMMAND_STRUCTURE = {
+ Help::create_example_string("x-vsinstances"),
+ 0,
+ 0,
+ {{}, {}},
+ nullptr,
+ };
+
+ void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
+ {
+#if defined(_WIN32)
+ const ParsedArguments parsed_args = args.parse_arguments(COMMAND_STRUCTURE);
+
+ const auto instances = vcpkg::VisualStudio::get_visual_studio_instances(paths);
+ for (const std::string& instance : instances)
+ {
+ System::println(instance);
+ }
+
+ Checks::exit_success(VCPKG_LINE_INFO);
+#else
+ Checks::exit_with_message(VCPKG_LINE_INFO, "This command is not supported on non-windows platforms.");
+#endif
+ }
+}
diff --git a/toolsrc/src/vcpkg/dependencies.cpp b/toolsrc/src/vcpkg/dependencies.cpp
index 8fb35b0da..60c43e4a8 100644
--- a/toolsrc/src/vcpkg/dependencies.cpp
+++ b/toolsrc/src/vcpkg/dependencies.cpp
@@ -488,26 +488,23 @@ namespace vcpkg::Dependencies
if (plus) return MarkPlusResult::SUCCESS;
plus = true;
+ auto p_source = cluster.source.get();
+ Checks::check_exit(VCPKG_LINE_INFO,
+ p_source != nullptr,
+ "Error: Cannot find definition for package `%s`.",
+ cluster.spec.name());
+
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)
{
- 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)
{
- auto res = mark_plus(default_feature, cluster, graph, graph_plan, prevent_default_features);
- if (res != MarkPlusResult::SUCCESS)
- {
- return res;
- }
+ 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);
@@ -515,28 +512,20 @@ namespace vcpkg::Dependencies
if (feature == "*")
{
- if (auto p_source = cluster.source.get())
+ for (auto&& fpgh : p_source->scf->feature_paragraphs)
{
- for (auto&& fpgh : p_source->scf->feature_paragraphs)
- {
- auto res = mark_plus(fpgh->name, cluster, graph, graph_plan, prevent_default_features);
+ 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);
- }
+ Checks::check_exit(VCPKG_LINE_INFO,
+ res == MarkPlusResult::SUCCESS,
+ "Error: Internal error while installing feature %s in %s",
+ fpgh->name,
+ cluster.spec);
+ }
- auto res = mark_plus("core", cluster, graph, graph_plan, prevent_default_features);
+ 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);
- }
+ Checks::check_exit(VCPKG_LINE_INFO, res == MarkPlusResult::SUCCESS);
return MarkPlusResult::SUCCESS;
}
@@ -643,7 +632,7 @@ namespace vcpkg::Dependencies
}
/// <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="map">Map of all source control files in the current environment.</param>
/// <param name="specs">Feature specifications to resolve dependencies for.</param>
/// <param name="status_db">Status of installed packages in the current environment.</param>
std::vector<AnyAction> create_feature_install_plan(const std::unordered_map<std::string, SourceControlFile>& map,
@@ -666,7 +655,11 @@ namespace vcpkg::Dependencies
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);
+ Checks::check_exit(VCPKG_LINE_INFO,
+ res == MarkPlusResult::SUCCESS,
+ "Error: `%s` is not a feature of package `%s`",
+ spec.feature(),
+ spec.name());
m_graph_plan->install_graph.add_vertex(ClusterPtr{&spec_cluster});
}
diff --git a/toolsrc/src/vcpkg/export.cpp b/toolsrc/src/vcpkg/export.cpp
index 152252018..eec9a39f2 100644
--- a/toolsrc/src/vcpkg/export.cpp
+++ b/toolsrc/src/vcpkg/export.cpp
@@ -1,8 +1,5 @@
#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>
@@ -13,6 +10,10 @@
#include <vcpkg/paragraphs.h>
#include <vcpkg/vcpkglib.h>
+#include <vcpkg/base/stringliteral.h>
+#include <vcpkg/base/system.h>
+#include <vcpkg/base/util.h>
+
namespace vcpkg::Export
{
using Dependencies::ExportPlanAction;
@@ -68,11 +69,15 @@ namespace vcpkg::Export
{
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};
+ static constexpr Build::BuildPackageOptions BUILD_OPTIONS = {
+ Build::UseHeadVersion::NO,
+ Build::AllowDownloads::YES,
+ Build::CleanBuildtrees::NO,
+ Build::CleanPackages::NO,
+ Build::DownloadTool::BUILT_IN,
+ Build::BinaryCaching::NO,
+ Build::FailOnTombstone::NO,
+ };
for (const ExportPlanType plan_type : ORDER)
{
@@ -103,7 +108,7 @@ namespace vcpkg::Export
static std::string create_export_id()
{
- const tm date_time = System::get_current_date_time();
+ const tm date_time = Chrono::get_current_date_time_local();
// Format is: YYYYmmdd-HHMMSS
// 15 characters + 1 null terminating character will be written for a total of 16 chars
@@ -150,7 +155,7 @@ namespace vcpkg::Export
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");
+ const fs::path output_path = output_dir / (nuget_id + "." + nuget_version + ".nupkg");
return output_path;
}
@@ -222,14 +227,10 @@ namespace vcpkg::Export
{
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"},
- {fs::path{"scripts"} / "VcpkgPowershellUtils.ps1"},
+ {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)
diff --git a/toolsrc/src/vcpkg/globalstate.cpp b/toolsrc/src/vcpkg/globalstate.cpp
index a4100acf7..a54c596fb 100644
--- a/toolsrc/src/vcpkg/globalstate.cpp
+++ b/toolsrc/src/vcpkg/globalstate.cpp
@@ -13,4 +13,48 @@ namespace vcpkg
std::atomic<int> GlobalState::g_init_console_cp(0);
std::atomic<int> GlobalState::g_init_console_output_cp(0);
+ std::atomic<bool> GlobalState::g_init_console_initialized(false);
+
+ GlobalState::CtrlCStateMachine GlobalState::g_ctrl_c_state;
+
+ GlobalState::CtrlCStateMachine::CtrlCStateMachine() : m_state(CtrlCState::normal) {}
+
+ void GlobalState::CtrlCStateMachine::transition_to_spawn_process() noexcept
+ {
+ auto expected = CtrlCState::normal;
+ auto transitioned = m_state.compare_exchange_strong(expected, CtrlCState::blocked_on_child);
+ if (!transitioned)
+ {
+ // Ctrl-C was hit and is asynchronously executing on another thread
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+ }
+ void GlobalState::CtrlCStateMachine::transition_from_spawn_process() noexcept
+ {
+ auto expected = CtrlCState::blocked_on_child;
+ auto transitioned = m_state.compare_exchange_strong(expected, CtrlCState::normal);
+ if (!transitioned)
+ {
+ // Ctrl-C was hit while blocked on the child process
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+ }
+ void GlobalState::CtrlCStateMachine::transition_handle_ctrl_c() noexcept
+ {
+ auto prev_state = m_state.exchange(CtrlCState::exit_requested);
+
+ if (prev_state == CtrlCState::normal)
+ {
+ // Not currently blocked on a child process and Ctrl-C has not been hit.
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+ else if (prev_state == CtrlCState::exit_requested)
+ {
+ // Ctrl-C was hit previously
+ }
+ else
+ {
+ // This is the case where we are currently blocked on a child process
+ }
+ }
}
diff --git a/toolsrc/src/vcpkg/help.cpp b/toolsrc/src/vcpkg/help.cpp
index 743619937..5df878a91 100644
--- a/toolsrc/src/vcpkg/help.cpp
+++ b/toolsrc/src/vcpkg/help.cpp
@@ -7,6 +7,13 @@
#include <vcpkg/install.h>
#include <vcpkg/remove.h>
+// Write environment variable names as %VARIABLE% on Windows and $VARIABLE in *nix
+#ifdef _WIN32
+#define ENVVAR(VARNAME) "%%" #VARNAME "%%"
+#else
+#define ENVVAR(VARNAME) "$" #VARNAME
+#endif
+
namespace vcpkg::Help
{
struct Topic
@@ -93,7 +100,7 @@ namespace vcpkg::Help
"%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 edit <pkg> Open up a port for editing (uses " ENVVAR(EDITOR) ", default 'code')\n"
" vcpkg import <pkg> Import a pre-built library\n"
" vcpkg create <pkg> <url>\n"
" [archivename] Create a new package\n"
@@ -104,10 +111,12 @@ namespace vcpkg::Help
"\n"
"Options:\n"
" --triplet <t> Specify the target architecture triplet.\n"
- " (default: %%VCPKG_DEFAULT_TRIPLET%%, see 'vcpkg help triplet')\n"
+ " (default: " ENVVAR(VCPKG_DEFAULT_TRIPLET) ", see 'vcpkg help triplet')\n"
"\n"
" --vcpkg-root <path> Specify the vcpkg root directory\n"
- " (default: %%VCPKG_ROOT%%)\n"
+ " (default: " ENVVAR(VCPKG_ROOT) ")\n"
+ "\n"
+ " @response_file Specify a response file to provide additional parameters\n"
"\n"
"For more help (including examples) see the accompanying README.md.",
Commands::Integrate::INTEGRATE_COMMAND_HELPSTRING);
diff --git a/toolsrc/src/vcpkg/install.cpp b/toolsrc/src/vcpkg/install.cpp
index fc336d6c7..1cfa2bf71 100644
--- a/toolsrc/src/vcpkg/install.cpp
+++ b/toolsrc/src/vcpkg/install.cpp
@@ -62,7 +62,7 @@ namespace vcpkg::Install
auto files = fs.get_files_recursive(source_dir);
for (auto&& file : files)
{
- const auto status = fs.status(file, ec);
+ const auto status = fs.symlink_status(file, ec);
if (ec)
{
System::println(System::Color::error, "failed: %s: %s", file.u8string(), ec.message());
@@ -111,6 +111,23 @@ namespace vcpkg::Install
output.push_back(Strings::format(R"(%s/%s)", destination_subdirectory, suffix));
break;
}
+ case fs::file_type::symlink:
+ {
+ if (fs.exists(target))
+ {
+ System::println(System::Color::warning,
+ "File %s was already present and will be overwritten",
+ target.u8string(),
+ ec.message());
+ }
+ fs.copy_symlink(file, target, 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;
@@ -462,14 +479,14 @@ namespace vcpkg::Install
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_find(suffix, "/share/") != suffix.end() &&
- suffix.substr(suffix.size() - 6) == ".cmake")
+ if (Strings::case_insensitive_ascii_contains(suffix, "/share/") && Strings::ends_with(suffix, ".cmake"))
{
- // File is inside the share folder
+ // 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();
@@ -485,6 +502,21 @@ namespace vcpkg::Install
++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;
+ }
}
}
@@ -497,11 +529,23 @@ namespace vcpkg::Install
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(" find_package(%s REQUIRED)\n"
- " target_link_libraries(main PRIVATE %s)\n",
- library_target_pair.first,
+ System::println(" target_link_libraries(main PRIVATE %s)\n",
Strings::join(" ", library_target_pair.second));
}
else
@@ -509,10 +553,8 @@ namespace vcpkg::Install
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(" find_package(%s REQUIRED)\n"
- " # Note: %zd targets were omitted\n"
+ System::println(" # Note: %zd target(s) were omitted.\n"
" target_link_libraries(main PRIVATE %s)\n",
- library_target_pair.first,
omitted,
Strings::join(" ", library_target_pair.second));
}
@@ -563,7 +605,10 @@ namespace vcpkg::Install
Util::Enum::to_enum<Build::AllowDownloads>(!no_downloads),
Build::CleanBuildtrees::NO,
Build::CleanPackages::NO,
- download_tool};
+ download_tool,
+ GlobalState::g_binary_caching ? Build::BinaryCaching::YES : Build::BinaryCaching::NO,
+ Build::FailOnTombstone::NO,
+ };
auto all_ports = Paragraphs::load_all_ports(paths.get_filesystem(), paths.ports);
std::unordered_map<std::string, SourceControlFile> scf_map;
diff --git a/toolsrc/src/vcpkg/metrics.cpp b/toolsrc/src/vcpkg/metrics.cpp
index 8890c067f..2a73dba89 100644
--- a/toolsrc/src/vcpkg/metrics.cpp
+++ b/toolsrc/src/vcpkg/metrics.cpp
@@ -5,6 +5,7 @@
#include <vcpkg/base/chrono.h>
#include <vcpkg/base/files.h>
+#include <vcpkg/base/hash.h>
#include <vcpkg/base/strings.h>
#include <vcpkg/base/system.h>
@@ -261,7 +262,7 @@ namespace vcpkg::Metrics
const auto match = *next;
if (match[0] != "00-00-00-00-00-00")
{
- return vcpkg::Commands::Hash::get_string_hash(match[0], "SHA256");
+ return vcpkg::Hash::get_string_hash(match[0], "SHA256");
}
++next;
}
@@ -437,13 +438,14 @@ namespace vcpkg::Metrics
const std::string cmd_line = Strings::format("start \"vcpkgmetricsuploader.exe\" \"%s\" \"%s\"",
temp_folder_path_exe.u8string(),
vcpkg_metrics_txt_path.u8string());
+ System::cmd_execute_no_wait(cmd_line);
#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);
+#endif
}
}
diff --git a/toolsrc/src/vcpkg/postbuildlint.cpp b/toolsrc/src/vcpkg/postbuildlint.cpp
index 6fe11951f..650b6e3c9 100644
--- a/toolsrc/src/vcpkg/postbuildlint.cpp
+++ b/toolsrc/src/vcpkg/postbuildlint.cpp
@@ -16,9 +16,9 @@ using vcpkg::Build::PreBuildInfo;
namespace vcpkg::PostBuildLint
{
- static auto has_extension_pred(const Files::Filesystem& fs, const std::string& ext)
+ static auto not_extension_pred(const Files::Filesystem& fs, const std::string& ext)
{
- return [&fs, ext](const fs::path& path) { return !fs.is_directory(path) && path.extension() == ext; };
+ return [&fs, ext](const fs::path& path) { return fs.is_directory(path) || path.extension() != ext; };
}
enum class LintStatus
@@ -104,8 +104,8 @@ namespace vcpkg::PostBuildLint
std::vector<fs::path> files_found = fs.get_files_recursive(debug_include_dir);
- Util::unstable_keep_if(
- files_found, [&fs](const fs::path& path) { return !fs.is_directory(path) && path.extension() != ".ifc"; });
+ Util::erase_remove_if(
+ files_found, [&fs](const fs::path& path) { return fs.is_directory(path) || path.extension() == ".ifc"; });
if (!files_found.empty())
{
@@ -206,7 +206,7 @@ namespace vcpkg::PostBuildLint
static LintStatus check_for_dlls_in_lib_dir(const Files::Filesystem& fs, const fs::path& package_dir)
{
std::vector<fs::path> dlls = fs.get_files_recursive(package_dir / "lib");
- Util::unstable_keep_if(dlls, has_extension_pred(fs, ".dll"));
+ Util::erase_remove_if(dlls, not_extension_pred(fs, ".dll"));
if (!dlls.empty())
{
@@ -280,7 +280,7 @@ namespace vcpkg::PostBuildLint
static LintStatus check_for_exes(const Files::Filesystem& fs, const fs::path& package_dir)
{
std::vector<fs::path> exes = fs.get_files_recursive(package_dir / "bin");
- Util::unstable_keep_if(exes, has_extension_pred(fs, ".exe"));
+ Util::erase_remove_if(exes, not_extension_pred(fs, ".exe"));
if (!exes.empty())
{
@@ -572,8 +572,8 @@ namespace vcpkg::PostBuildLint
{
std::vector<fs::path> empty_directories = fs.get_files_recursive(dir);
- Util::unstable_keep_if(empty_directories, [&fs](const fs::path& current) {
- return fs.is_directory(current) && fs.is_empty(current);
+ Util::erase_remove_if(empty_directories, [&fs](const fs::path& current) {
+ return !fs.is_directory(current) || !fs.is_empty(current);
});
if (!empty_directories.empty())
@@ -617,7 +617,11 @@ namespace vcpkg::PostBuildLint
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", cmd_line);
+ Checks::check_exit(VCPKG_LINE_INFO,
+ ec_data.exit_code == 0,
+ "Running command:\n %s\n failed with message:\n%s",
+ cmd_line,
+ ec_data.output);
for (const BuildType& bad_build_type : bad_build_types)
{
@@ -703,12 +707,15 @@ namespace vcpkg::PostBuildLint
static LintStatus check_no_files_in_dir(const Files::Filesystem& fs, const fs::path& dir)
{
std::vector<fs::path> misplaced_files = fs.get_files_non_recursive(dir);
- Util::unstable_keep_if(misplaced_files, [&fs](const fs::path& path) {
+ Util::erase_remove_if(misplaced_files, [&fs](const fs::path& path) {
const std::string filename = path.filename().generic_string();
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);
+ {
+ return true;
+ }
+
+ return fs.is_directory(path);
});
if (!misplaced_files.empty())
@@ -760,9 +767,9 @@ namespace vcpkg::PostBuildLint
const fs::path release_bin_dir = package_dir / "bin";
std::vector<fs::path> debug_libs = fs.get_files_recursive(debug_lib_dir);
- Util::unstable_keep_if(debug_libs, has_extension_pred(fs, ".lib"));
+ Util::erase_remove_if(debug_libs, not_extension_pred(fs, ".lib"));
std::vector<fs::path> release_libs = fs.get_files_recursive(release_lib_dir);
- Util::unstable_keep_if(release_libs, has_extension_pred(fs, ".lib"));
+ Util::erase_remove_if(release_libs, not_extension_pred(fs, ".lib"));
if (!pre_build_info.build_type)
error_count += check_matching_debug_and_release_binaries(debug_libs, release_libs);
@@ -776,9 +783,9 @@ namespace vcpkg::PostBuildLint
}
std::vector<fs::path> debug_dlls = fs.get_files_recursive(debug_bin_dir);
- Util::unstable_keep_if(debug_dlls, has_extension_pred(fs, ".dll"));
+ Util::erase_remove_if(debug_dlls, not_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"));
+ Util::erase_remove_if(release_dlls, not_extension_pred(fs, ".dll"));
switch (build_info.library_linkage)
{
diff --git a/toolsrc/src/vcpkg/remove.cpp b/toolsrc/src/vcpkg/remove.cpp
index 13cc9325e..921a04c23 100644
--- a/toolsrc/src/vcpkg/remove.cpp
+++ b/toolsrc/src/vcpkg/remove.cpp
@@ -55,7 +55,7 @@ namespace vcpkg::Remove
auto target = paths.installed / suffix;
- const auto status = fs.status(target, ec);
+ const auto status = fs.symlink_status(target, ec);
if (ec)
{
System::println(System::Color::error, "failed: status(%s): %s", target.u8string(), ec.message());
@@ -66,7 +66,7 @@ namespace vcpkg::Remove
{
dirs_touched.push_back(target);
}
- else if (fs::is_regular_file(status))
+ else if (fs::is_regular_file(status) || fs::is_symlink(status))
{
fs.remove(target, ec);
if (ec)
diff --git a/toolsrc/src/vcpkg/statusparagraph.cpp b/toolsrc/src/vcpkg/statusparagraph.cpp
index 462d8d8ed..86946a31a 100644
--- a/toolsrc/src/vcpkg/statusparagraph.cpp
+++ b/toolsrc/src/vcpkg/statusparagraph.cpp
@@ -105,7 +105,7 @@ namespace vcpkg
{
dep.erase(std::find(dep.begin(), dep.end(), '['), dep.end());
}
- Util::unstable_keep_if(deps, [&](auto&& e) { return e != l_spec.name(); });
+ Util::erase_remove_if(deps, [&](auto&& e) { return e == l_spec.name(); });
// </hack>
Util::sort_unique_erase(deps);
diff --git a/toolsrc/src/vcpkg/tools.cpp b/toolsrc/src/vcpkg/tools.cpp
new file mode 100644
index 000000000..f4ee2d653
--- /dev/null
+++ b/toolsrc/src/vcpkg/tools.cpp
@@ -0,0 +1,533 @@
+#include "pch.h"
+
+#include <vcpkg/archives.h>
+#include <vcpkg/tools.h>
+#include <vcpkg/vcpkgpaths.h>
+
+#include <vcpkg/base/checks.h>
+#include <vcpkg/base/downloads.h>
+#include <vcpkg/base/files.h>
+#include <vcpkg/base/optional.h>
+#include <vcpkg/base/stringrange.h>
+#include <vcpkg/base/strings.h>
+#include <vcpkg/base/system.h>
+#include <vcpkg/base/util.h>
+
+namespace vcpkg
+{
+ 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;
+ }
+
+ 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 =
+ StringRange::find_exactly_one_enclosed(XML, match_tool_entry[0], "</tool>").to_string();
+ const std::string version_as_string =
+ StringRange::find_exactly_one_enclosed(tool_data, "<version>", "</version>").to_string();
+ const std::string exe_relative_path =
+ StringRange::find_exactly_one_enclosed(tool_data, "<exeRelativePath>", "</exeRelativePath>").to_string();
+ const std::string url = StringRange::find_exactly_one_enclosed(tool_data, "<url>", "</url>").to_string();
+ const std::string sha512 =
+ StringRange::find_exactly_one_enclosed(tool_data, "<sha512>", "</sha512>").to_string();
+ auto archive_name = StringRange::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
+ }
+
+ struct PathAndVersion
+ {
+ fs::path path;
+ std::string version;
+ };
+
+ static Optional<PathAndVersion> find_first_with_sufficient_version(const std::vector<PathAndVersion>& candidates,
+ const std::array<int, 3>& expected_version)
+ {
+ const auto it = Util::find_if(candidates, [&](const PathAndVersion& candidate) {
+ const auto parsed_version = parse_version_string(candidate.version);
+ if (!parsed_version.has_value())
+ {
+ return false;
+ }
+
+ const std::array<int, 3> actual_version = *parsed_version.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]);
+ });
+
+ if (it == candidates.cend())
+ {
+ return nullopt;
+ }
+
+ return *it;
+ }
+
+ struct VersionProvider
+ {
+ virtual Optional<std::string> get_version(const fs::path& path_to_exe) const = 0;
+
+ std::vector<PathAndVersion> get_versions(const std::vector<fs::path>& candidate_paths) const
+ {
+ auto&& fs = Files::get_real_filesystem();
+
+ std::vector<PathAndVersion> output;
+ for (auto&& p : candidate_paths)
+ {
+ if (!fs.exists(p)) continue;
+ auto maybe_version = this->get_version(p);
+ if (const auto version = maybe_version.get())
+ {
+ output.emplace_back(PathAndVersion{p, *version});
+ return output;
+ }
+ }
+
+ return output;
+ }
+ };
+
+ 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 %s.",
+ tool_name,
+ version_as_string,
+ tool_name);
+ 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);
+ System::println(" %s -> %s", tool_data.url, tool_data.download_path.string());
+ Downloads::download_file(fs, tool_data.url, tool_data.download_path, tool_data.sha512);
+ }
+ else
+ {
+ Downloads::verify_downloaded_file_hash(fs, tool_data.url, tool_data.download_path, tool_data.sha512);
+ }
+
+ if (tool_data.is_archive)
+ {
+ System::println("Extracting %s...", tool_name);
+ Archives::extract_archive(paths, tool_data.download_path, tool_data.tool_dir_path);
+ }
+ 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 PathAndVersion fetch_tool(const VcpkgPaths& paths,
+ const std::string& tool_name,
+ const ToolData& tool_data,
+ const VersionProvider& version_provider)
+ {
+ const auto downloaded_path = fetch_tool(paths, tool_name, tool_data);
+ const auto downloaded_version = version_provider.get_version(downloaded_path).value_or_exit(VCPKG_LINE_INFO);
+ return {downloaded_path, downloaded_version};
+ }
+
+ namespace CMake
+ {
+ struct CmakeVersionProvider : VersionProvider
+ {
+ Optional<std::string> get_version(const fs::path& path_to_exe) const override
+ {
+ const std::string cmd = Strings::format(R"("%s" --version)", path_to_exe.u8string());
+ const auto rc = System::cmd_execute_and_capture_output(cmd);
+ if (rc.exit_code != 0)
+ {
+ return nullopt;
+ }
+
+ /* Sample output:
+ cmake version 3.10.2
+
+ CMake suite maintained and supported by Kitware (kitware.com/cmake).
+ */
+ return StringRange::find_exactly_one_enclosed(rc.output, "cmake version ", "\n").to_string();
+ }
+ };
+
+ static PathAndVersion get_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
+ const std::vector<fs::path> from_path = paths.get_filesystem().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 CmakeVersionProvider version_provider{};
+ const std::vector<PathAndVersion> candidates_with_versions = version_provider.get_versions(candidate_paths);
+ const auto maybe_path = find_first_with_sufficient_version(candidates_with_versions, TOOL_DATA.version);
+ if (const auto p = maybe_path.get())
+ {
+ return *p;
+ }
+
+ return fetch_tool(paths, Tools::CMAKE, TOOL_DATA, version_provider);
+ }
+ }
+
+ 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
+ }
+
+ namespace Ninja
+ {
+ struct NinjaVersionProvider : VersionProvider
+ {
+ Optional<std::string> get_version(const fs::path& path_to_exe) const override
+ {
+ const std::string cmd = Strings::format(R"("%s" --version)", path_to_exe.u8string());
+ const auto rc = System::cmd_execute_and_capture_output(cmd);
+ if (rc.exit_code != 0)
+ {
+ return nullopt;
+ }
+
+ /* Sample output:
+ 1.8.2
+ */
+ return rc.output;
+ }
+ };
+
+ static PathAndVersion get_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 = paths.get_filesystem().find_from_PATH("ninja");
+ candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend());
+
+ const NinjaVersionProvider version_provider{};
+ const std::vector<PathAndVersion> candidates_with_versions = version_provider.get_versions(candidate_paths);
+ const auto maybe_path = find_first_with_sufficient_version(candidates_with_versions, TOOL_DATA.version);
+ if (const auto p = maybe_path.get())
+ {
+ return *p;
+ }
+
+ return fetch_tool(paths, Tools::NINJA, TOOL_DATA, version_provider);
+ }
+ }
+
+ namespace Nuget
+ {
+ struct NugetVersionProvider : VersionProvider
+ {
+ Optional<std::string> get_version(const fs::path& path_to_exe) const override
+ {
+ const std::string cmd = Strings::format(R"("%s")", path_to_exe.u8string());
+ const auto rc = System::cmd_execute_and_capture_output(cmd);
+ if (rc.exit_code != 0)
+ {
+ return nullopt;
+ }
+
+ /* Sample output:
+ NuGet Version: 4.6.2.5055
+ usage: NuGet <command> [args] [options]
+ Type 'NuGet help <command>' for help on a specific command.
+
+ [[[List of available commands follows]]]
+ */
+ return StringRange::find_exactly_one_enclosed(rc.output, "NuGet Version: ", "\n").to_string();
+ }
+ };
+
+ static PathAndVersion get_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 = paths.get_filesystem().find_from_PATH("nuget");
+ candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend());
+
+ const NugetVersionProvider version_provider{};
+ const std::vector<PathAndVersion> candidates_with_versions = version_provider.get_versions(candidate_paths);
+ const auto maybe_path = find_first_with_sufficient_version(candidates_with_versions, TOOL_DATA.version);
+ if (const auto p = maybe_path.get())
+ {
+ return *p;
+ }
+
+ return fetch_tool(paths, Tools::NUGET, TOOL_DATA, version_provider);
+ }
+ }
+
+ namespace Git
+ {
+ struct GitVersionProvider : VersionProvider
+ {
+ Optional<std::string> get_version(const fs::path& path_to_exe) const override
+ {
+ const std::string cmd = Strings::format(R"("%s" --version)", path_to_exe.u8string());
+ const auto rc = System::cmd_execute_and_capture_output(cmd);
+ if (rc.exit_code != 0)
+ {
+ return nullopt;
+ }
+
+ /* Sample output:
+ git version 2.17.1.windows.2
+ */
+ const auto idx = rc.output.find("git version ");
+ Checks::check_exit(VCPKG_LINE_INFO,
+ idx != std::string::npos,
+ "Unexpected format of git version string: %s",
+ rc.output);
+ return rc.output.substr(idx);
+ }
+ };
+
+ static PathAndVersion get_path(const VcpkgPaths& paths)
+ {
+ static const ToolData TOOL_DATA = parse_tool_data_from_xml(paths, "git");
+
+ 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 = paths.get_filesystem().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 GitVersionProvider version_provider{};
+ const std::vector<PathAndVersion> candidates_with_versions = version_provider.get_versions(candidate_paths);
+ const auto maybe_path = find_first_with_sufficient_version(candidates_with_versions, TOOL_DATA.version);
+ if (const auto p = maybe_path.get())
+ {
+ return *p;
+ }
+
+ return fetch_tool(paths, Tools::GIT, TOOL_DATA, version_provider);
+ }
+ }
+
+ namespace IfwInstallerBase
+ {
+ struct IfwInstallerBaseVersionProvider : VersionProvider
+ {
+ Optional<std::string> get_version(const fs::path& path_to_exe) const override
+ {
+ const std::string cmd = Strings::format(R"("%s" --framework-version)", path_to_exe.u8string());
+ const auto rc = System::cmd_execute_and_capture_output(cmd);
+ if (rc.exit_code != 0)
+ {
+ return nullopt;
+ }
+
+ /* Sample output:
+ 3.1.81
+ */
+ return rc.output;
+ }
+ };
+
+ static PathAndVersion get_path(const VcpkgPaths& paths)
+ {
+ static const ToolData TOOL_DATA = parse_tool_data_from_xml(paths, "installerbase");
+
+ 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 IfwInstallerBaseVersionProvider version_provider{};
+ const std::vector<PathAndVersion> candidates_with_versions = version_provider.get_versions(candidate_paths);
+ const auto maybe_path = find_first_with_sufficient_version(candidates_with_versions, TOOL_DATA.version);
+ if (const auto p = maybe_path.get())
+ {
+ return *p;
+ }
+
+ return fetch_tool(paths, Tools::IFW_INSTALLER_BASE, TOOL_DATA, version_provider);
+ }
+ }
+
+ struct ToolCacheImpl final : ToolCache
+ {
+ vcpkg::Cache<std::string, fs::path> path_only_cache;
+ vcpkg::Cache<std::string, PathAndVersion> path_version_cache;
+
+ virtual const fs::path& get_tool_path(const VcpkgPaths& paths, const std::string& tool) const override
+ {
+ return path_only_cache.get_lazy(tool, [&]() {
+ // First deal with specially handled tools.
+ // For these we may look in locations like Program Files, the PATH etc as well as the auto-downloaded
+ // location.
+ if (tool == Tools::SEVEN_ZIP) return get_7za_path(paths);
+ if (tool == Tools::CMAKE || tool == Tools::GIT || tool == Tools::NINJA || tool == Tools::NUGET ||
+ tool == Tools::IFW_INSTALLER_BASE)
+ return get_tool_pathversion(paths, tool).path;
+ if (tool == Tools::IFW_BINARYCREATOR)
+ return IfwInstallerBase::get_path(paths).path.parent_path() / "binarycreator.exe";
+ if (tool == Tools::IFW_REPOGEN)
+ return IfwInstallerBase::get_path(paths).path.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 PathAndVersion& get_tool_pathversion(const VcpkgPaths& paths, const std::string& tool) const
+ {
+ return path_version_cache.get_lazy(tool, [&]() {
+ if (tool == Tools::CMAKE) return CMake::get_path(paths);
+ if (tool == Tools::GIT) return Git::get_path(paths);
+ if (tool == Tools::NINJA) return Ninja::get_path(paths);
+ if (tool == Tools::NUGET) return Nuget::get_path(paths);
+ if (tool == Tools::IFW_INSTALLER_BASE) return IfwInstallerBase::get_path(paths);
+
+ Checks::exit_with_message(VCPKG_LINE_INFO, "Finding version for %s is not implemented yet.", tool);
+ });
+ }
+
+ virtual const std::string& get_tool_version(const VcpkgPaths& paths, const std::string& tool) const override
+ {
+ return get_tool_pathversion(paths, tool).version;
+ }
+ };
+
+ std::unique_ptr<ToolCache> get_tool_cache() { return std::make_unique<ToolCacheImpl>(); }
+}
diff --git a/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp b/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp
index 8909e1552..5b3cf9ef1 100644
--- a/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp
+++ b/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp
@@ -51,11 +51,29 @@ namespace vcpkg
std::vector<std::string> v;
for (int i = 1; i < argc; ++i)
{
+ std::string arg;
#if defined(_WIN32)
- v.push_back(Strings::to_utf8(argv[i]));
+ arg = Strings::to_utf8(argv[i]);
#else
- v.push_back(argv[i]);
+ arg = argv[i];
#endif
+ // Response file?
+ if (arg.size() > 0 && arg[0] == '@')
+ {
+ arg.erase(arg.begin());
+ const auto& fs = Files::get_real_filesystem();
+ auto lines = fs.read_lines(fs::u8path(arg));
+ if (!lines.has_value())
+ {
+ System::println(System::Color::error, "Error: Could not open response file %s", arg);
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+ std::copy(lines.get()->begin(), lines.get()->end(), std::back_inserter(v));
+ }
+ else
+ {
+ v.emplace_back(std::move(arg));
+ }
}
return VcpkgCmdArguments::create_from_arg_sequence(v.data(), v.data() + v.size());
diff --git a/toolsrc/src/vcpkg/vcpkgpaths.cpp b/toolsrc/src/vcpkg/vcpkgpaths.cpp
index 0903c2d76..9a51818e8 100644
--- a/toolsrc/src/vcpkg/vcpkgpaths.cpp
+++ b/toolsrc/src/vcpkg/vcpkgpaths.cpp
@@ -9,6 +9,7 @@
#include <vcpkg/metrics.h>
#include <vcpkg/packagespec.h>
#include <vcpkg/vcpkgpaths.h>
+#include <vcpkg/visualstudio.h>
namespace vcpkg
{
@@ -39,6 +40,7 @@ namespace vcpkg
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";
@@ -91,7 +93,13 @@ namespace vcpkg
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); });
+ if (!m_tool_cache) m_tool_cache = get_tool_cache();
+ return m_tool_cache->get_tool_path(*this, tool);
+ }
+ const std::string& VcpkgPaths::get_tool_version(const std::string& tool) const
+ {
+ if (!m_tool_cache) m_tool_cache = get_tool_cache();
+ return m_tool_cache->get_tool_version(*this, tool);
}
const Toolset& VcpkgPaths::get_toolset(const Build::PreBuildInfo& prebuildinfo) const
@@ -113,9 +121,11 @@ namespace vcpkg
return external_toolset;
}
- // Invariant: toolsets are non-empty and sorted with newest at back()
+#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(*this); });
+ this->toolsets.get_lazy([this]() { return VisualStudio::find_toolset_instances_preferred_first(*this); });
std::vector<const Toolset*> candidates = Util::element_pointers(vs_toolsets);
const auto tsv = prebuildinfo.platform_toolset.get();
@@ -127,8 +137,8 @@ namespace vcpkg
if (tsv && vsp)
{
- Util::stable_keep_if(
- candidates, [&](const Toolset* t) { return *tsv == t->version && *vsp == t->visual_studio_root_path; });
+ Util::erase_remove_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.",
@@ -141,7 +151,7 @@ namespace vcpkg
if (tsv)
{
- Util::stable_keep_if(candidates, [&](const Toolset* t) { return *tsv == t->version; });
+ Util::erase_remove_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);
}
@@ -149,8 +159,8 @@ namespace vcpkg
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; });
+ Util::erase_remove_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.",
@@ -159,6 +169,8 @@ namespace vcpkg
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/visualstudio.cpp b/toolsrc/src/vcpkg/visualstudio.cpp
new file mode 100644
index 000000000..83a530a10
--- /dev/null
+++ b/toolsrc/src/vcpkg/visualstudio.cpp
@@ -0,0 +1,326 @@
+#include "pch.h"
+
+#if defined(_WIN32)
+
+#include <vcpkg/base/sortedvector.h>
+#include <vcpkg/base/stringrange.h>
+#include <vcpkg/base/util.h>
+#include <vcpkg/visualstudio.h>
+
+namespace vcpkg::VisualStudio
+{
+ static constexpr CStringView V_120 = "v120";
+ static constexpr CStringView V_140 = "v140";
+ static constexpr CStringView V_141 = "v141";
+
+ struct VisualStudioInstance
+ {
+ enum class ReleaseType
+ {
+ STABLE,
+ PRERELEASE,
+ LEGACY
+ };
+
+ static std::string release_type_to_string(const ReleaseType& release_type)
+ {
+ switch (release_type)
+ {
+ case ReleaseType::STABLE: return "STABLE";
+ case ReleaseType::PRERELEASE: return "PRERELEASE";
+ case ReleaseType::LEGACY: return "LEGACY";
+ default: Checks::unreachable(VCPKG_LINE_INFO);
+ }
+ }
+
+ 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 to_string() const
+ {
+ return Strings::format("%s, %s, %s", root_path.u8string(), version, release_type_to_string(release_type));
+ }
+
+ std::string major_version() const { return version.substr(0, 2); }
+ };
+
+ static std::vector<VisualStudioInstance> get_visual_studio_instances_internal(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" -all -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 =
+ StringRange::find_all_enclosed(code_and_output.output, "<instance>", "</instance>");
+ for (const StringRange& instance : instance_entries)
+ {
+ auto maybe_is_prerelease =
+ StringRange::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(
+ StringRange::find_exactly_one_enclosed(instance, "<installationPath>", "</installationPath>")
+ .to_string(),
+ StringRange::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;
+ }
+
+ std::vector<std::string> get_visual_studio_instances(const VcpkgPaths& paths)
+ {
+ std::vector<VisualStudioInstance> sorted{get_visual_studio_instances_internal(paths)};
+ std::sort(sorted.begin(), sorted.end(), VisualStudioInstance::preferred_first_comparator);
+ return Util::fmap(sorted, [](const VisualStudioInstance& instance) { return instance.to_string(); });
+ }
+
+ 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_internal(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::erase_remove_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 v141_toolset{
+ vs_instance.root_path, dumpbin_path, vcvarsall_bat, {}, V_141, supported_architectures};
+
+ const auto english_language_pack = dumpbin_path.parent_path() / "1033";
+
+ if (!fs.exists(english_language_pack))
+ {
+ excluded_toolsets.push_back(v141_toolset);
+ break;
+ }
+
+ found_toolsets.push_back(v141_toolset);
+
+ if (v140_is_available)
+ {
+ const Toolset v140_toolset{vs_instance.root_path,
+ dumpbin_path,
+ vcvarsall_bat,
+ {"-vcvars_ver=14.0"},
+ V_140,
+ supported_architectures};
+ found_toolsets.push_back(v140_toolset);
+ }
+
+ 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};
+
+ const 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