aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Schumacher <roschuma@microsoft.com>2020-02-08 22:45:36 -0800
committerGitHub <noreply@github.com>2020-02-08 22:45:36 -0800
commitf7fb56decd9d03cf79a1d23f44daa3a23560b995 (patch)
tree3ab3d78aa90da2d368a16b65ae624e263e26d19e
parentb20c6d3b89b9fa22a19e3609e6df2fcfc20f968f (diff)
downloadvcpkg-f7fb56decd9d03cf79a1d23f44daa3a23560b995.tar.gz
vcpkg-f7fb56decd9d03cf79a1d23f44daa3a23560b995.zip
[vcpkg] Use CreateProcess on Windows. Improve EnvVars manipulation and handling. (#9897)
* [vcpkg] Rewrite process spawning on windows to always use CreateProcess, enabling better environment handling * [vcpkg] Use environment cache to avoid calling vcvars multiple times * [vcpkg] Increase buffer size while computing hashes * [vcpkg] Remove unneeded cmd.exe wrapper process on all CreateProcess calls * [vcpkg] Fix .vcxproj{,.filters} * [vcpkg] Upgrade Ctrl-C state machine to handle multiple background processes. * [vcpkg] Fix regression while launching metrics: 'start' can't be passed directly to CreateProcessW * [vcpkg] Fix typo on non-Windows * [vcpkg] Fix various uses of >NUL across the code * [vcpkg] Fix various uses of >NUL across the code
-rw-r--r--toolsrc/CMakeLists.txt6
-rw-r--r--toolsrc/include/vcpkg/base/checks.h3
-rw-r--r--toolsrc/include/vcpkg/base/system.process.h28
-rw-r--r--toolsrc/src/vcpkg/base/checks.cpp16
-rw-r--r--toolsrc/src/vcpkg/base/hash.cpp2
-rw-r--r--toolsrc/src/vcpkg/base/system.cpp457
-rw-r--r--toolsrc/src/vcpkg/base/system.process.cpp611
-rw-r--r--toolsrc/src/vcpkg/build.cpp101
-rw-r--r--toolsrc/src/vcpkg/commands.edit.cpp25
-rw-r--r--toolsrc/src/vcpkg/commands.env.cpp2
-rw-r--r--toolsrc/src/vcpkg/commands.exportifw.cpp12
-rw-r--r--toolsrc/src/vcpkg/commands.integrate.cpp9
-rw-r--r--toolsrc/src/vcpkg/commands.portsdiff.cpp20
-rw-r--r--toolsrc/src/vcpkg/export.chocolatey.cpp5
-rw-r--r--toolsrc/src/vcpkg/export.cpp5
-rw-r--r--toolsrc/src/vcpkg/metrics.cpp2
-rw-r--r--toolsrc/vcpkglib/vcpkglib.vcxproj1
-rw-r--r--toolsrc/vcpkglib/vcpkglib.vcxproj.filters3
18 files changed, 748 insertions, 560 deletions
diff --git a/toolsrc/CMakeLists.txt b/toolsrc/CMakeLists.txt
index 9dcf95d14..4fb1890e2 100644
--- a/toolsrc/CMakeLists.txt
+++ b/toolsrc/CMakeLists.txt
@@ -139,7 +139,7 @@ if (BUILD_TESTING)
endif()
if(MSVC)
- get_target_property(_srcs vcpkg SOURCES)
+ get_target_property(_srcs vcpkglib SOURCES)
if(NOT CMAKE_GENERATOR MATCHES "Visual Studio .*")
set_property(SOURCE src/pch.cpp APPEND PROPERTY OBJECT_OUTPUTS "${CMAKE_CURRENT_BINARY_DIR}/pch.pch")
@@ -147,8 +147,8 @@ if(MSVC)
endif()
set_source_files_properties(src/pch.cpp PROPERTIES COMPILE_FLAGS "/Ycpch.h")
- target_sources(vcpkg PRIVATE src/pch.cpp)
- target_compile_options(vcpkg PRIVATE /Yupch.h /FIpch.h /Zm200)
+ target_sources(vcpkglib PRIVATE src/pch.cpp)
+ target_compile_options(vcpkglib PRIVATE /Yupch.h /FIpch.h /Zm200)
endif()
if (MINGW)
diff --git a/toolsrc/include/vcpkg/base/checks.h b/toolsrc/include/vcpkg/base/checks.h
index 3c0c073d2..519ca58f4 100644
--- a/toolsrc/include/vcpkg/base/checks.h
+++ b/toolsrc/include/vcpkg/base/checks.h
@@ -8,6 +8,9 @@ namespace vcpkg::Checks
{
void register_global_shutdown_handler(void (*func)());
+ // Note: for internal use
+ [[noreturn]] void final_cleanup_and_exit(const int exit_code);
+
// Indicate that an internal error has occurred and exit the tool. This should be used when invariants have been
// broken.
[[noreturn]] void unreachable(const LineInfo& line_info);
diff --git a/toolsrc/include/vcpkg/base/system.process.h b/toolsrc/include/vcpkg/base/system.process.h
index 14e2eb109..b2e76ff74 100644
--- a/toolsrc/include/vcpkg/base/system.process.h
+++ b/toolsrc/include/vcpkg/base/system.process.h
@@ -3,6 +3,7 @@
#include <vcpkg/base/files.h>
#include <vcpkg/base/zstringview.h>
+#include <functional>
#include <string>
#include <unordered_map>
#include <vector>
@@ -30,17 +31,34 @@ namespace vcpkg::System
std::string output;
};
- int cmd_execute_clean(const ZStringView cmd_line,
- const std::unordered_map<std::string, std::string>& extra_env = {},
- const std::string& prepend_to_path = {});
+ struct Environment
+ {
+#if defined(_WIN32)
+ std::wstring m_env_data;
+#endif
+ };
- int cmd_execute(const ZStringView cmd_line);
+ const Environment& get_clean_environment();
+ Environment get_modified_clean_environment(const std::unordered_map<std::string, std::string>& extra_env,
+ const std::string& prepend_to_path = {});
+
+ int cmd_execute(const ZStringView cmd_line, const Environment& env = {});
+ int cmd_execute_clean(const ZStringView cmd_line);
#if defined(_WIN32)
+ Environment cmd_execute_modify_env(const ZStringView cmd_line, const Environment& env = {});
+
void cmd_execute_no_wait(const StringView cmd_line);
#endif
- ExitCodeAndOutput cmd_execute_and_capture_output(const ZStringView cmd_line);
+ ExitCodeAndOutput cmd_execute_and_capture_output(const ZStringView cmd_line, const Environment& env = {});
+
+ int cmd_execute_and_stream_lines(const ZStringView cmd_line,
+ std::function<void(const std::string&)> per_line_cb,
+ const Environment& env = {});
+ int cmd_execute_and_stream_data(const ZStringView cmd_line,
+ std::function<void(StringView)> data_cb,
+ const Environment& env = {});
void register_console_ctrl_handler();
}
diff --git a/toolsrc/src/vcpkg/base/checks.cpp b/toolsrc/src/vcpkg/base/checks.cpp
index c7584258a..6093b5188 100644
--- a/toolsrc/src/vcpkg/base/checks.cpp
+++ b/toolsrc/src/vcpkg/base/checks.cpp
@@ -15,11 +15,17 @@ namespace vcpkg
g_shutdown_handler = func;
}
- [[noreturn]] static void cleanup_and_exit(const int exit_code)
+ void Checks::final_cleanup_and_exit(const int exit_code)
{
static std::atomic<bool> have_entered{false};
- if (have_entered) std::terminate();
- have_entered = true;
+ if (have_entered.exchange(true))
+ {
+#if defined(_WIN32)
+ ::TerminateProcess(::GetCurrentProcess(), exit_code);
+#else
+ std::terminate();
+#endif
+ }
if (g_shutdown_handler) g_shutdown_handler();
@@ -38,14 +44,14 @@ namespace vcpkg
#ifndef NDEBUG
std::abort();
#else
- cleanup_and_exit(EXIT_FAILURE);
+ final_cleanup_and_exit(EXIT_FAILURE);
#endif
}
void Checks::exit_with_code(const LineInfo& line_info, const int exit_code)
{
Debug::print(System::Color::error, line_info, '\n');
- cleanup_and_exit(exit_code);
+ final_cleanup_and_exit(exit_code);
}
void Checks::exit_with_message(const LineInfo& line_info, StringView error_message)
diff --git a/toolsrc/src/vcpkg/base/hash.cpp b/toolsrc/src/vcpkg/base/hash.cpp
index 81635b875..b70897a2b 100644
--- a/toolsrc/src/vcpkg/base/hash.cpp
+++ b/toolsrc/src/vcpkg/base/hash.cpp
@@ -746,7 +746,7 @@ namespace vcpkg::Hash
}
return do_hash(algo, [&file, &ec](Hasher& hasher) {
- constexpr std::size_t buffer_size = 4096;
+ constexpr std::size_t buffer_size = 1024 * 32;
auto buffer = std::make_unique<char[]>(buffer_size);
for (;;)
{
diff --git a/toolsrc/src/vcpkg/base/system.cpp b/toolsrc/src/vcpkg/base/system.cpp
index b71ab7c44..96cdeb84a 100644
--- a/toolsrc/src/vcpkg/base/system.cpp
+++ b/toolsrc/src/vcpkg/base/system.cpp
@@ -4,121 +4,14 @@
#include <vcpkg/base/chrono.h>
#include <vcpkg/base/system.debug.h>
#include <vcpkg/base/system.h>
-#include <vcpkg/base/system.process.h>
#include <vcpkg/base/util.h>
#include <ctime>
-#if defined(__APPLE__)
-#include <mach-o/dyld.h>
-#endif
-
-#if defined(__FreeBSD__)
-#include <sys/sysctl.h>
-#endif
-
-#if defined(_WIN32)
-#pragma comment(lib, "Advapi32")
-#endif
-
using namespace vcpkg::System;
namespace vcpkg
{
-#if defined(_WIN32)
- namespace
- {
- struct CtrlCStateMachine
- {
- CtrlCStateMachine() : m_state(CtrlCState::normal) {}
-
- void 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 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, so exit immediately
- Checks::exit_fail(VCPKG_LINE_INFO);
- }
- }
- void 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
- {
- // We are currently blocked on a child process. Upon return, transition_from_spawn_process() will be
- // called and exit.
- }
- }
-
- private:
- enum class CtrlCState
- {
- normal,
- blocked_on_child,
- exit_requested,
- };
-
- std::atomic<CtrlCState> m_state;
- };
-
- static CtrlCStateMachine g_ctrl_c_state;
- }
-#endif
-
- fs::path System::get_exe_path_of_current_process()
- {
-#if defined(_WIN32)
- wchar_t buf[_MAX_PATH];
- const int bytes = GetModuleFileNameW(nullptr, buf, _MAX_PATH);
- if (bytes == 0) std::abort();
- return fs::path(buf, buf + bytes);
-#elif defined(__APPLE__)
- static constexpr const uint32_t buff_size = 1024 * 32;
- uint32_t size = buff_size;
- char buf[buff_size] = {};
- int result = _NSGetExecutablePath(buf, &size);
- Checks::check_exit(VCPKG_LINE_INFO, result != -1, "Could not determine current executable path.");
- std::unique_ptr<char> canonicalPath(realpath(buf, NULL));
- Checks::check_exit(VCPKG_LINE_INFO, result != -1, "Could not determine current executable path.");
- return fs::path(std::string(canonicalPath.get()));
-#elif defined(__FreeBSD__)
- int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
- char exePath[2048];
- size_t len = sizeof(exePath);
- auto rcode = sysctl(mib, 4, exePath, &len, NULL, 0);
- Checks::check_exit(VCPKG_LINE_INFO, rcode == 0, "Could not determine current executable path.");
- Checks::check_exit(VCPKG_LINE_INFO, len > 0, "Could not determine current executable path.");
- return fs::path(exePath, exePath + len - 1);
-#else /* LINUX */
- std::array<char, 1024 * 4> buf;
- auto written = readlink("/proc/self/exe", buf.data(), buf.size());
- Checks::check_exit(VCPKG_LINE_INFO, written != -1, "Could not determine current executable path.");
- return fs::path(buf.data(), buf.data() + written);
-#endif
- }
-
Optional<CPUArchitecture> System::to_cpu_architecture(StringView arch)
{
if (Strings::case_insensitive_ascii_equals(arch, "x86")) return CPUArchitecture::X86;
@@ -179,338 +72,6 @@ namespace vcpkg
return supported_architectures;
}
- System::CMakeVariable::CMakeVariable(const StringView varname, const char* varvalue)
- : s(Strings::format(R"("-D%s=%s")", varname, varvalue))
- {
- }
- System::CMakeVariable::CMakeVariable(const StringView varname, const std::string& varvalue)
- : CMakeVariable(varname, varvalue.c_str())
- {
- }
- System::CMakeVariable::CMakeVariable(const StringView varname, const fs::path& path)
- : CMakeVariable(varname, path.generic_u8string())
- {
- }
-
- std::string System::make_cmake_cmd(const fs::path& cmake_exe,
- const fs::path& cmake_script,
- const std::vector<CMakeVariable>& pass_variables)
- {
- const std::string cmd_cmake_pass_variables = Strings::join(" ", pass_variables, [](auto&& v) { return v.s; });
- return Strings::format(
- R"("%s" %s -P "%s")", cmake_exe.u8string(), cmd_cmake_pass_variables, cmake_script.generic_u8string());
- }
-
-#if defined(_WIN32)
- static std::wstring compute_clean_environment(const std::unordered_map<std::string, std::string>& extra_env,
- const std::string& prepend_to_path)
- {
- static const std::string SYSTEM_ROOT = get_environment_variable("SystemRoot").value_or_exit(VCPKG_LINE_INFO);
- static const std::string SYSTEM_32 = SYSTEM_ROOT + R"(\system32)";
- std::string new_path = Strings::format(R"(Path=%s%s;%s;%s\Wbem;%s\WindowsPowerShell\v1.0\)",
- prepend_to_path,
- SYSTEM_32,
- SYSTEM_ROOT,
- SYSTEM_32,
- SYSTEM_32);
-
- std::vector<std::wstring> env_wstrings = {
- L"ALLUSERSPROFILE",
- L"APPDATA",
- L"CommonProgramFiles",
- L"CommonProgramFiles(x86)",
- L"CommonProgramW6432",
- L"COMPUTERNAME",
- L"ComSpec",
- L"HOMEDRIVE",
- L"HOMEPATH",
- L"LOCALAPPDATA",
- L"LOGONSERVER",
- L"NUMBER_OF_PROCESSORS",
- L"OS",
- L"PATHEXT",
- L"PROCESSOR_ARCHITECTURE",
- L"PROCESSOR_ARCHITEW6432",
- L"PROCESSOR_IDENTIFIER",
- L"PROCESSOR_LEVEL",
- L"PROCESSOR_REVISION",
- L"ProgramData",
- L"ProgramFiles",
- L"ProgramFiles(x86)",
- L"ProgramW6432",
- L"PROMPT",
- L"PSModulePath",
- L"PUBLIC",
- L"SystemDrive",
- L"SystemRoot",
- L"TEMP",
- L"TMP",
- L"USERDNSDOMAIN",
- L"USERDOMAIN",
- L"USERDOMAIN_ROAMINGPROFILE",
- L"USERNAME",
- L"USERPROFILE",
- L"windir",
- // Enables proxy information to be passed to Curl, the underlying download library in cmake.exe
- L"http_proxy",
- L"https_proxy",
- // Enables find_package(CUDA) and enable_language(CUDA) in CMake
- L"CUDA_PATH",
- L"CUDA_PATH_V9_0",
- L"CUDA_PATH_V9_1",
- L"CUDA_PATH_V10_0",
- L"CUDA_PATH_V10_1",
- L"CUDA_TOOLKIT_ROOT_DIR",
- // 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",
- // Enable targeted Android NDK
- L"ANDROID_NDK_HOME",
- };
-
- const Optional<std::string> keep_vars = System::get_environment_variable("VCPKG_KEEP_ENV_VARS");
- const auto k = keep_vars.get();
-
- if (k && !k->empty())
- {
- auto vars = Strings::split(*k, ";");
-
- for (auto&& var : vars)
- {
- env_wstrings.push_back(Strings::to_utf16(var));
- }
- }
-
- std::wstring env_cstr;
-
- for (auto&& env_wstring : env_wstrings)
- {
- const Optional<std::string> value = System::get_environment_variable(Strings::to_utf8(env_wstring.c_str()));
- const auto v = value.get();
- if (!v || v->empty()) continue;
-
- env_cstr.append(env_wstring);
- env_cstr.push_back(L'=');
- env_cstr.append(Strings::to_utf16(*v));
- env_cstr.push_back(L'\0');
- }
-
- if (extra_env.find("PATH") != extra_env.end())
- new_path += Strings::format(";%s", extra_env.find("PATH")->second);
- env_cstr.append(Strings::to_utf16(new_path));
- env_cstr.push_back(L'\0');
- env_cstr.append(L"VSLANG=1033");
- env_cstr.push_back(L'\0');
-
- for (const auto& item : extra_env)
- {
- if (item.first == "PATH") continue;
- env_cstr.append(Strings::to_utf16(item.first));
- env_cstr.push_back(L'=');
- env_cstr.append(Strings::to_utf16(item.second));
- env_cstr.push_back(L'\0');
- }
-
- 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 StringView cmd_line,
- const wchar_t* environment_block,
- PROCESS_INFORMATION& process_info,
- DWORD dwCreationFlags) noexcept
- {
- STARTUPINFOW startup_info;
- memset(&startup_info, 0, sizeof(STARTUPINFOW));
- startup_info.cb = sizeof(STARTUPINFOW);
-
- // Flush stdout before launching external process
- fflush(nullptr);
-
- // 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::print("CreateProcessW(", actual_cmd_line, ")\n");
- bool succeeded = TRUE == CreateProcessW(nullptr,
- Strings::to_utf16(actual_cmd_line).data(),
- nullptr,
- nullptr,
- FALSE,
- IDLE_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT | dwCreationFlags,
- (void*)environment_block,
- nullptr,
- &startup_info,
- &process_info);
-
- Checks::check_exit(VCPKG_LINE_INFO, succeeded, "Process creation failed with error code: %lu", GetLastError());
- }
-#endif
-
-#if defined(_WIN32)
- void System::cmd_execute_no_wait(StringView cmd_line)
- {
- auto timer = Chrono::ElapsedTimer::create_started();
-
- PROCESS_INFORMATION process_info;
- memset(&process_info, 0, sizeof(PROCESS_INFORMATION));
-
- windows_create_process(cmd_line, nullptr, process_info, DETACHED_PROCESS);
-
- CloseHandle(process_info.hThread);
- CloseHandle(process_info.hProcess);
-
- Debug::print("CreateProcessW() took ", static_cast<int>(timer.microseconds()), " us\n");
- }
-#endif
-
- int System::cmd_execute_clean(const ZStringView cmd_line,
- const std::unordered_map<std::string, std::string>& extra_env,
- const std::string& prepend_to_path)
- {
- auto timer = Chrono::ElapsedTimer::create_started();
-#if defined(_WIN32)
-
- PROCESS_INFORMATION process_info;
- memset(&process_info, 0, sizeof(PROCESS_INFORMATION));
-
- g_ctrl_c_state.transition_to_spawn_process();
- auto clean_env = compute_clean_environment(extra_env, prepend_to_path);
- windows_create_process(cmd_line, clean_env.data(), process_info, 0);
-
- CloseHandle(process_info.hThread);
-
- const DWORD result = WaitForSingleObject(process_info.hProcess, INFINITE);
- 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);
-
- CloseHandle(process_info.hProcess);
-
- Debug::print(
- "CreateProcessW() returned ", exit_code, " after ", static_cast<int>(timer.microseconds()), " us\n");
- return static_cast<int>(exit_code);
-#else
- // TODO: this should create a clean environment on Linux/macOS
- Util::unused(extra_env, prepend_to_path);
- Debug::print("system(", cmd_line, ")\n");
- fflush(nullptr);
- int rc = system(cmd_line.c_str());
- Debug::print("system() returned ", rc, " after ", static_cast<int>(timer.microseconds()), " us\n");
- return rc;
-#endif
- }
-
- int System::cmd_execute(const ZStringView cmd_line)
- {
- // Flush stdout before launching external process
- fflush(nullptr);
-
- auto timer = Chrono::ElapsedTimer::create_started();
-#if defined(_WIN32)
- // We are wrap the command line in quotes to cause cmd.exe to correctly process it
- auto actual_cmd_line = Strings::concat('"', cmd_line, '"');
- Debug::print("_wsystem(", actual_cmd_line, ")\n");
- g_ctrl_c_state.transition_to_spawn_process();
- const int exit_code = _wsystem(Strings::to_utf16(actual_cmd_line).c_str());
- g_ctrl_c_state.transition_from_spawn_process();
- Debug::print("_wsystem() returned ",
- exit_code,
- " after ",
- Strings::format("%8d", static_cast<int>(timer.microseconds())),
- " us\n");
-#else
- Debug::print("_system(", cmd_line, ")\n");
- const int exit_code = system(cmd_line.c_str());
- Debug::print("_system() returned ",
- exit_code,
- " after ",
- Strings::format("%8d", static_cast<int>(timer.microseconds())),
- " us\n");
-#endif
- return exit_code;
- }
-
- ExitCodeAndOutput System::cmd_execute_and_capture_output(const ZStringView cmd_line)
- {
- auto timer = Chrono::ElapsedTimer::create_started();
-
-#if defined(_WIN32)
- const auto actual_cmd_line = Strings::format(R"###("%s 2>&1")###", cmd_line);
-
- Debug::print("_wpopen(", actual_cmd_line, ")\n");
- std::wstring output;
- auto buf = std::make_unique<wchar_t[]>(1024 * 32);
- g_ctrl_c_state.transition_to_spawn_process();
- // Flush stdout before launching external process
- fflush(stdout);
- const auto pipe = _wpopen(Strings::to_utf16(actual_cmd_line).c_str(), L"r");
- if (pipe == nullptr)
- {
- g_ctrl_c_state.transition_from_spawn_process();
- return {1, Strings::to_utf8(output.c_str())};
- }
- while (fgetws(buf.get(), 1024 * 32, pipe))
- {
- output.append(buf.get());
- }
- if (!feof(pipe))
- {
- g_ctrl_c_state.transition_from_spawn_process();
- return {1, Strings::to_utf8(output.c_str())};
- }
-
- const auto ec = _pclose(pipe);
- 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
- const wchar_t* a = output.c_str();
- while (output.size() >= 3 && a[0] == 0xEF && a[1] == 0xBB && a[2] == 0xBF)
- {
- output.erase(0, 3);
- }
-
- Debug::print("_pclose() returned ",
- ec,
- " after ",
- Strings::format("%8d", static_cast<int>(timer.microseconds())),
- " us\n");
- return {ec, Strings::to_utf8(output.c_str())};
-#else
- const auto actual_cmd_line = Strings::format(R"###(%s 2>&1)###", cmd_line);
-
- Debug::print("popen(", actual_cmd_line, ")\n");
- 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)
- {
- return {1, output};
- }
- while (fgets(buf, 1024, pipe))
- {
- output.append(buf);
- }
- if (!feof(pipe))
- {
- return {1, output};
- }
-
- const auto ec = pclose(pipe);
-
- Debug::print("_pclose() returned ", ec, " after ", Strings::format("%8d", (int)timer.microseconds()), " us\n");
-
- return {ec, output};
-#endif
- }
-
Optional<std::string> System::get_environment_variable(ZStringView varname) noexcept
{
#if defined(_WIN32)
@@ -609,24 +170,6 @@ namespace vcpkg
return PATH;
}
-#if defined(_WIN32)
- static BOOL ctrl_handler(DWORD fdw_ctrl_type)
- {
- switch (fdw_ctrl_type)
- {
- case CTRL_C_EVENT: g_ctrl_c_state.transition_handle_ctrl_c(); return TRUE;
- default: return FALSE;
- }
- }
-
- void System::register_console_ctrl_handler()
- {
- SetConsoleCtrlHandler(reinterpret_cast<PHANDLER_ROUTINE>(ctrl_handler), TRUE);
- }
-#else
- void System::register_console_ctrl_handler() {}
-#endif
-
int System::get_num_logical_cores() { return std::thread::hardware_concurrency(); }
}
diff --git a/toolsrc/src/vcpkg/base/system.process.cpp b/toolsrc/src/vcpkg/base/system.process.cpp
new file mode 100644
index 000000000..076a1edba
--- /dev/null
+++ b/toolsrc/src/vcpkg/base/system.process.cpp
@@ -0,0 +1,611 @@
+#include "pch.h"
+
+#include <vcpkg/base/checks.h>
+#include <vcpkg/base/chrono.h>
+#include <vcpkg/base/system.debug.h>
+#include <vcpkg/base/system.h>
+#include <vcpkg/base/system.process.h>
+#include <vcpkg/base/util.h>
+
+#include <ctime>
+
+#if defined(__APPLE__)
+#include <mach-o/dyld.h>
+#endif
+
+#if defined(__FreeBSD__)
+#include <sys/sysctl.h>
+#endif
+
+#if defined(_WIN32)
+#pragma comment(lib, "Advapi32")
+#endif
+
+using namespace vcpkg::System;
+
+namespace vcpkg
+{
+#if defined(_WIN32)
+ namespace
+ {
+ struct CtrlCStateMachine
+ {
+ CtrlCStateMachine() : m_number_of_external_processes(0) {}
+
+ void transition_to_spawn_process() noexcept
+ {
+ int cur = 0;
+ while (!m_number_of_external_processes.compare_exchange_strong(cur, cur + 1))
+ {
+ if (cur < 0)
+ {
+ // Ctrl-C was hit and is asynchronously executing on another thread.
+ // Some other processes are outstanding.
+ // Sleep forever -- the other process will complete and exit the program
+ while (true)
+ {
+ std::this_thread::sleep_for(std::chrono::seconds(10));
+ System::print2("Waiting for child processes to exit...\n");
+ }
+ }
+ }
+ }
+ void transition_from_spawn_process() noexcept
+ {
+ auto previous = m_number_of_external_processes.fetch_add(-1);
+ if (previous == INT_MIN + 1)
+ {
+ // Ctrl-C was hit while blocked on the child process
+ // This is the last external process to complete
+ // Therefore, exit
+ Checks::final_cleanup_and_exit(1);
+ }
+ else if (previous < 0)
+ {
+ // Ctrl-C was hit while blocked on the child process
+ // Some other processes are outstanding.
+ // Sleep forever -- the other process will complete and exit the program
+ while (true)
+ {
+ std::this_thread::sleep_for(std::chrono::seconds(10));
+ System::print2("Waiting for child processes to exit...\n");
+ }
+ }
+ }
+ void transition_handle_ctrl_c() noexcept
+ {
+ int old_value = 0;
+ while (!m_number_of_external_processes.compare_exchange_strong(old_value, old_value + INT_MIN))
+ {
+ if (old_value < 0)
+ {
+ // Repeat calls to Ctrl-C -- a previous one succeeded.
+ return;
+ }
+ }
+
+ if (old_value == 0)
+ {
+ // Not currently blocked on a child process
+ Checks::final_cleanup_and_exit(1);
+ }
+ else
+ {
+ // We are currently blocked on a child process. Upon return, transition_from_spawn_process()
+ // will be called and exit.
+ }
+ }
+
+ private:
+ std::atomic<int> m_number_of_external_processes;
+ };
+
+ static CtrlCStateMachine g_ctrl_c_state;
+ }
+#endif
+
+ fs::path System::get_exe_path_of_current_process()
+ {
+#if defined(_WIN32)
+ wchar_t buf[_MAX_PATH];
+ const int bytes = GetModuleFileNameW(nullptr, buf, _MAX_PATH);
+ if (bytes == 0) std::abort();
+ return fs::path(buf, buf + bytes);
+#elif defined(__APPLE__)
+ static constexpr const uint32_t buff_size = 1024 * 32;
+ uint32_t size = buff_size;
+ char buf[buff_size] = {};
+ int result = _NSGetExecutablePath(buf, &size);
+ Checks::check_exit(VCPKG_LINE_INFO, result != -1, "Could not determine current executable path.");
+ std::unique_ptr<char> canonicalPath(realpath(buf, NULL));
+ Checks::check_exit(VCPKG_LINE_INFO, result != -1, "Could not determine current executable path.");
+ return fs::path(std::string(canonicalPath.get()));
+#elif defined(__FreeBSD__)
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
+ char exePath[2048];
+ size_t len = sizeof(exePath);
+ auto rcode = sysctl(mib, 4, exePath, &len, NULL, 0);
+ Checks::check_exit(VCPKG_LINE_INFO, rcode == 0, "Could not determine current executable path.");
+ Checks::check_exit(VCPKG_LINE_INFO, len > 0, "Could not determine current executable path.");
+ return fs::path(exePath, exePath + len - 1);
+#else /* LINUX */
+ std::array<char, 1024 * 4> buf;
+ auto written = readlink("/proc/self/exe", buf.data(), buf.size());
+ Checks::check_exit(VCPKG_LINE_INFO, written != -1, "Could not determine current executable path.");
+ return fs::path(buf.data(), buf.data() + written);
+#endif
+ }
+
+ System::CMakeVariable::CMakeVariable(const StringView varname, const char* varvalue)
+ : s(Strings::format(R"("-D%s=%s")", varname, varvalue))
+ {
+ }
+ System::CMakeVariable::CMakeVariable(const StringView varname, const std::string& varvalue)
+ : CMakeVariable(varname, varvalue.c_str())
+ {
+ }
+ System::CMakeVariable::CMakeVariable(const StringView varname, const fs::path& path)
+ : CMakeVariable(varname, path.generic_u8string())
+ {
+ }
+
+ std::string System::make_cmake_cmd(const fs::path& cmake_exe,
+ const fs::path& cmake_script,
+ const std::vector<CMakeVariable>& pass_variables)
+ {
+ const std::string cmd_cmake_pass_variables = Strings::join(" ", pass_variables, [](auto&& v) { return v.s; });
+ return Strings::format(
+ R"("%s" %s -P "%s")", cmake_exe.u8string(), cmd_cmake_pass_variables, cmake_script.generic_u8string());
+ }
+
+#if defined(_WIN32)
+ Environment System::get_modified_clean_environment(const std::unordered_map<std::string, std::string>& extra_env,
+ const std::string& prepend_to_path)
+ {
+ static const std::string SYSTEM_ROOT = get_environment_variable("SystemRoot").value_or_exit(VCPKG_LINE_INFO);
+ static const std::string SYSTEM_32 = SYSTEM_ROOT + R"(\system32)";
+ std::string new_path = Strings::format(R"(Path=%s%s;%s;%s\Wbem;%s\WindowsPowerShell\v1.0\)",
+ prepend_to_path,
+ SYSTEM_32,
+ SYSTEM_ROOT,
+ SYSTEM_32,
+ SYSTEM_32);
+
+ std::vector<std::wstring> env_wstrings = {
+ L"ALLUSERSPROFILE",
+ L"APPDATA",
+ L"CommonProgramFiles",
+ L"CommonProgramFiles(x86)",
+ L"CommonProgramW6432",
+ L"COMPUTERNAME",
+ L"ComSpec",
+ L"HOMEDRIVE",
+ L"HOMEPATH",
+ L"LOCALAPPDATA",
+ L"LOGONSERVER",
+ L"NUMBER_OF_PROCESSORS",
+ L"OS",
+ L"PATHEXT",
+ L"PROCESSOR_ARCHITECTURE",
+ L"PROCESSOR_ARCHITEW6432",
+ L"PROCESSOR_IDENTIFIER",
+ L"PROCESSOR_LEVEL",
+ L"PROCESSOR_REVISION",
+ L"ProgramData",
+ L"ProgramFiles",
+ L"ProgramFiles(x86)",
+ L"ProgramW6432",
+ L"PROMPT",
+ L"PSModulePath",
+ L"PUBLIC",
+ L"SystemDrive",
+ L"SystemRoot",
+ L"TEMP",
+ L"TMP",
+ L"USERDNSDOMAIN",
+ L"USERDOMAIN",
+ L"USERDOMAIN_ROAMINGPROFILE",
+ L"USERNAME",
+ L"USERPROFILE",
+ L"windir",
+ // Enables proxy information to be passed to Curl, the underlying download library in cmake.exe
+ L"http_proxy",
+ L"https_proxy",
+ // Enables find_package(CUDA) and enable_language(CUDA) in CMake
+ L"CUDA_PATH",
+ L"CUDA_PATH_V9_0",
+ L"CUDA_PATH_V9_1",
+ L"CUDA_PATH_V10_0",
+ L"CUDA_PATH_V10_1",
+ L"CUDA_TOOLKIT_ROOT_DIR",
+ // 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",
+ // Enable targeted Android NDK
+ L"ANDROID_NDK_HOME",
+ };
+
+ const Optional<std::string> keep_vars = System::get_environment_variable("VCPKG_KEEP_ENV_VARS");
+ const auto k = keep_vars.get();
+
+ if (k && !k->empty())
+ {
+ auto vars = Strings::split(*k, ";");
+
+ for (auto&& var : vars)
+ {
+ env_wstrings.push_back(Strings::to_utf16(var));
+ }
+ }
+
+ std::wstring env_cstr;
+
+ for (auto&& env_wstring : env_wstrings)
+ {
+ const Optional<std::string> value = System::get_environment_variable(Strings::to_utf8(env_wstring.c_str()));
+ const auto v = value.get();
+ if (!v || v->empty()) continue;
+
+ env_cstr.append(env_wstring);
+ env_cstr.push_back(L'=');
+ env_cstr.append(Strings::to_utf16(*v));
+ env_cstr.push_back(L'\0');
+ }
+
+ if (extra_env.find("PATH") != extra_env.end())
+ new_path += Strings::format(";%s", extra_env.find("PATH")->second);
+ env_cstr.append(Strings::to_utf16(new_path));
+ env_cstr.push_back(L'\0');
+ env_cstr.append(L"VSLANG=1033");
+ env_cstr.push_back(L'\0');
+
+ for (const auto& item : extra_env)
+ {
+ if (item.first == "PATH") continue;
+ env_cstr.append(Strings::to_utf16(item.first));
+ env_cstr.push_back(L'=');
+ env_cstr.append(Strings::to_utf16(item.second));
+ env_cstr.push_back(L'\0');
+ }
+
+ return {env_cstr};
+ }
+#else
+ Environment System::get_modified_clean_environment(const std::unordered_map<std::string, std::string>&,
+ const std::string&)
+ {
+ return {};
+ }
+#endif
+ const Environment& System::get_clean_environment()
+ {
+ static const Environment clean_env = get_modified_clean_environment({});
+ return clean_env;
+ }
+
+ int System::cmd_execute_clean(const ZStringView cmd_line) { return cmd_execute(cmd_line, get_clean_environment()); }
+
+#if defined(_WIN32)
+ struct ProcessInfo
+ {
+ constexpr ProcessInfo() : proc_info{} {}
+
+ unsigned int wait_and_close_handles()
+ {
+ CloseHandle(proc_info.hThread);
+
+ const DWORD result = WaitForSingleObject(proc_info.hProcess, INFINITE);
+ Checks::check_exit(VCPKG_LINE_INFO, result != WAIT_FAILED, "WaitForSingleObject failed");
+
+ DWORD exit_code = 0;
+ GetExitCodeProcess(proc_info.hProcess, &exit_code);
+
+ CloseHandle(proc_info.hProcess);
+
+ return exit_code;
+ }
+
+ void close_handles()
+ {
+ CloseHandle(proc_info.hThread);
+ CloseHandle(proc_info.hProcess);
+ }
+
+ PROCESS_INFORMATION proc_info;
+ };
+
+ /// <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 ExpectedT<ProcessInfo, unsigned long> windows_create_process(const StringView cmd_line,
+ const Environment& env,
+ DWORD dwCreationFlags,
+ STARTUPINFOW& startup_info) noexcept
+ {
+ ProcessInfo process_info;
+ Debug::print("CreateProcessW(", cmd_line, ")\n");
+
+ // Flush stdout before launching external process
+ fflush(nullptr);
+ bool succeeded = TRUE == CreateProcessW(nullptr,
+ Strings::to_utf16(cmd_line).data(),
+ nullptr,
+ nullptr,
+ TRUE,
+ IDLE_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT | dwCreationFlags,
+ (void*)(env.m_env_data.empty() ? nullptr : env.m_env_data.data()),
+ nullptr,
+ &startup_info,
+ &process_info.proc_info);
+ if (succeeded)
+ return process_info;
+ else
+ return GetLastError();
+ }
+
+ static ExpectedT<ProcessInfo, unsigned long> windows_create_process(const StringView cmd_line,
+ const Environment& env,
+ DWORD dwCreationFlags) noexcept
+ {
+ STARTUPINFOW startup_info;
+ memset(&startup_info, 0, sizeof(STARTUPINFOW));
+ startup_info.cb = sizeof(STARTUPINFOW);
+
+ return windows_create_process(cmd_line, env, dwCreationFlags, startup_info);
+ }
+
+ struct ProcessInfoAndPipes
+ {
+ ProcessInfo proc_info;
+ HANDLE child_stdin = 0;
+ HANDLE child_stdout = 0;
+
+ template<class Function>
+ int wait_and_stream_output(Function&& f)
+ {
+ CloseHandle(child_stdin);
+
+ unsigned long bytes_read = 0;
+ static constexpr int buffer_size = 1024 * 32;
+ auto buf = std::make_unique<char[]>(buffer_size);
+ while (ReadFile(child_stdout, (void*)buf.get(), buffer_size, &bytes_read, nullptr) && bytes_read > 0)
+ {
+ f(StringView{buf.get(), static_cast<size_t>(bytes_read)});
+ }
+
+ CloseHandle(child_stdout);
+
+ return proc_info.wait_and_close_handles();
+ }
+ };
+
+ static ExpectedT<ProcessInfoAndPipes, unsigned long> windows_create_process_redirect(const StringView cmd_line,
+ const Environment& env,
+ DWORD dwCreationFlags) noexcept
+ {
+ ProcessInfoAndPipes ret;
+
+ STARTUPINFOW startup_info;
+ memset(&startup_info, 0, sizeof(STARTUPINFOW));
+ startup_info.cb = sizeof(STARTUPINFOW);
+ startup_info.dwFlags |= STARTF_USESTDHANDLES;
+
+ SECURITY_ATTRIBUTES saAttr;
+ memset(&saAttr, 0, sizeof(SECURITY_ATTRIBUTES));
+ saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ saAttr.bInheritHandle = TRUE;
+ saAttr.lpSecurityDescriptor = NULL;
+
+ // Create a pipe for the child process's STDOUT.
+ if (!CreatePipe(&ret.child_stdout, &startup_info.hStdOutput, &saAttr, 0)) Checks::exit_fail(VCPKG_LINE_INFO);
+ // Ensure the read handle to the pipe for STDOUT is not inherited.
+ if (!SetHandleInformation(ret.child_stdout, HANDLE_FLAG_INHERIT, 0)) Checks::exit_fail(VCPKG_LINE_INFO);
+ // Create a pipe for the child process's STDIN.
+ if (!CreatePipe(&startup_info.hStdInput, &ret.child_stdin, &saAttr, 0)) Checks::exit_fail(VCPKG_LINE_INFO);
+ // Ensure the write handle to the pipe for STDIN is not inherited.
+ if (!SetHandleInformation(ret.child_stdin, HANDLE_FLAG_INHERIT, 0)) Checks::exit_fail(VCPKG_LINE_INFO);
+ startup_info.hStdError = startup_info.hStdOutput;
+
+ auto maybe_proc_info = windows_create_process(cmd_line, env, dwCreationFlags, startup_info);
+
+ CloseHandle(startup_info.hStdInput);
+ CloseHandle(startup_info.hStdOutput);
+
+ if (auto proc_info = maybe_proc_info.get())
+ {
+ ret.proc_info = std::move(*proc_info);
+ return std::move(ret);
+ }
+ else
+ {
+ return maybe_proc_info.error();
+ }
+ }
+#endif
+
+#if defined(_WIN32)
+ void System::cmd_execute_no_wait(StringView cmd_line)
+ {
+ auto timer = Chrono::ElapsedTimer::create_started();
+
+ auto process_info = windows_create_process(cmd_line, {}, DETACHED_PROCESS);
+ if (auto p = process_info.get())
+ {
+ p->close_handles();
+ }
+ else
+ {
+ Debug::print("cmd_execute_no_wait() failed with error code ", process_info.error(), "\n");
+ }
+
+ Debug::print("cmd_execute_no_wait() took ", static_cast<int>(timer.microseconds()), " us\n");
+ }
+
+ Environment System::cmd_execute_modify_env(const ZStringView cmd_line, const Environment& env)
+ {
+ static StringLiteral magic_string = "cdARN4xjKueKScMy9C6H";
+
+ auto actual_cmd_line = Strings::concat(cmd_line, " & echo ", magic_string, "& set");
+
+ auto rc_output = cmd_execute_and_capture_output(actual_cmd_line, env);
+ Checks::check_exit(VCPKG_LINE_INFO, rc_output.exit_code == 0);
+ auto it = Strings::search(rc_output.output, Strings::concat(magic_string, "\r\n"));
+ const auto e = static_cast<const char*>(rc_output.output.data()) + rc_output.output.size();
+ Checks::check_exit(VCPKG_LINE_INFO, it != e);
+ it += magic_string.size() + 2;
+
+ std::wstring out_env;
+
+ while (1)
+ {
+ auto eq = std::find(it, e, '=');
+ if (eq == e) break;
+ StringView varname(it, eq);
+ auto nl = std::find(eq + 1, e, '\r');
+ if (nl == e) break;
+ StringView value(eq + 1, nl);
+
+ out_env.append(Strings::to_utf16(Strings::concat(varname, '=', value)));
+ out_env.push_back(L'\0');
+
+ it = nl + 1;
+ if (it != e && *it == '\n') ++it;
+ }
+
+ return {std::move(out_env)};
+ }
+#endif
+
+ int System::cmd_execute(const ZStringView cmd_line, const Environment& env)
+ {
+ auto timer = Chrono::ElapsedTimer::create_started();
+#if defined(_WIN32)
+ using vcpkg::g_ctrl_c_state;
+ g_ctrl_c_state.transition_to_spawn_process();
+ auto proc_info = windows_create_process(cmd_line, env, NULL);
+ auto long_exit_code = [&]() -> unsigned long {
+ if (auto p = proc_info.get())
+ return p->wait_and_close_handles();
+ else
+ return proc_info.error();
+ }();
+ if (long_exit_code > INT_MAX) long_exit_code = INT_MAX;
+ int exit_code = static_cast<int>(long_exit_code);
+ g_ctrl_c_state.transition_from_spawn_process();
+
+ Debug::print(
+ "cmd_execute() returned ", exit_code, " after ", static_cast<unsigned int>(timer.microseconds()), " us\n");
+#else
+ Debug::print("system(", cmd_line, ")\n");
+ fflush(nullptr);
+ int exit_code = system(cmd_line.c_str());
+ Debug::print(
+ "system() returned ", exit_code, " after ", static_cast<unsigned int>(timer.microseconds()), " us\n");
+#endif
+ return exit_code;
+ }
+
+ int System::cmd_execute_and_stream_lines(const ZStringView cmd_line,
+ std::function<void(const std::string&)> per_line_cb,
+ const Environment& env)
+ {
+ std::string buf;
+
+ auto rc = cmd_execute_and_stream_data(
+ cmd_line,
+ [&](StringView sv) {
+ auto prev_size = buf.size();
+ Strings::append(buf, sv);
+
+ auto it = std::find(buf.begin() + prev_size, buf.end(), '\n');
+ while (it != buf.end())
+ {
+ std::string s(buf.begin(), it);
+ per_line_cb(s);
+ buf.erase(buf.begin(), it + 1);
+ it = std::find(buf.begin(), buf.end(), '\n');
+ }
+ },
+ env);
+
+ per_line_cb(buf);
+ return rc;
+ }
+
+ int System::cmd_execute_and_stream_data(const ZStringView cmd_line,
+ std::function<void(StringView)> data_cb,
+ const Environment& env)
+ {
+ auto timer = Chrono::ElapsedTimer::create_started();
+
+#if defined(_WIN32)
+ using vcpkg::g_ctrl_c_state;
+
+ g_ctrl_c_state.transition_to_spawn_process();
+ auto maybe_proc_info = windows_create_process_redirect(cmd_line, env, NULL);
+ auto exit_code = [&]() -> unsigned long {
+ if (auto p = maybe_proc_info.get())
+ return p->wait_and_stream_output(data_cb);
+ else
+ return maybe_proc_info.error();
+ }();
+ g_ctrl_c_state.transition_from_spawn_process();
+#else
+ const auto actual_cmd_line = Strings::format(R"###(%s 2>&1)###", cmd_line);
+
+ Debug::print("popen(", actual_cmd_line, ")\n");
+ // Flush stdout before launching external process
+ fflush(stdout);
+ const auto pipe = popen(actual_cmd_line.c_str(), "r");
+ if (pipe == nullptr)
+ {
+ return 1;
+ }
+ char buf[1024];
+ while (fgets(buf, 1024, pipe))
+ {
+ data_cb(StringView{buf, strlen(buf)});
+ }
+
+ if (!feof(pipe))
+ {
+ return 1;
+ }
+
+ const auto exit_code = pclose(pipe);
+#endif
+ Debug::print("cmd_execute_and_stream_data() returned ",
+ exit_code,
+ " after ",
+ Strings::format("%8d", static_cast<int>(timer.microseconds())),
+ " us\n");
+
+ return exit_code;
+ }
+
+ ExitCodeAndOutput System::cmd_execute_and_capture_output(const ZStringView cmd_line, const Environment& env)
+ {
+ std::string output;
+ auto rc = cmd_execute_and_stream_data(
+ cmd_line, [&](StringView sv) { Strings::append(output, sv); }, env);
+ return {rc, std::move(output)};
+ }
+
+#if defined(_WIN32)
+ static BOOL ctrl_handler(DWORD fdw_ctrl_type)
+ {
+ switch (fdw_ctrl_type)
+ {
+ case CTRL_C_EVENT: g_ctrl_c_state.transition_handle_ctrl_c(); return TRUE;
+ default: return FALSE;
+ }
+ }
+
+ void System::register_console_ctrl_handler()
+ {
+ SetConsoleCtrlHandler(reinterpret_cast<PHANDLER_ROUTINE>(ctrl_handler), TRUE);
+ }
+#else
+ void System::register_console_ctrl_handler() {}
+#endif
+}
diff --git a/toolsrc/src/vcpkg/build.cpp b/toolsrc/src/vcpkg/build.cpp
index 01a22ba90..2637410b2 100644
--- a/toolsrc/src/vcpkg/build.cpp
+++ b/toolsrc/src/vcpkg/build.cpp
@@ -1,5 +1,6 @@
#include "pch.h"
+#include <vcpkg/base/cache.h>
#include <vcpkg/base/checks.h>
#include <vcpkg/base/chrono.h>
#include <vcpkg/base/enums.h>
@@ -266,21 +267,24 @@ namespace vcpkg::Build
}));
}
- static auto make_env_passthrough(const PreBuildInfo& pre_build_info) -> std::unordered_map<std::string, std::string>
+ static const std::unordered_map<std::string, std::string>& make_env_passthrough(const PreBuildInfo& pre_build_info)
{
- std::unordered_map<std::string, std::string> env;
+ static Cache<std::vector<std::string>, std::unordered_map<std::string, std::string>> envs;
+ return envs.get_lazy(pre_build_info.passthrough_env_vars, [&]() {
+ std::unordered_map<std::string, std::string> env;
- for (auto&& env_var : pre_build_info.passthrough_env_vars)
- {
- auto env_val = System::get_environment_variable(env_var);
-
- if (env_val)
+ for (auto&& env_var : pre_build_info.passthrough_env_vars)
{
- env[env_var] = env_val.value_or_exit(VCPKG_LINE_INFO);
+ auto env_val = System::get_environment_variable(env_var);
+
+ if (env_val)
+ {
+ env[env_var] = env_val.value_or_exit(VCPKG_LINE_INFO);
+ }
}
- }
- return env;
+ return env;
+ });
}
std::string make_build_env_cmd(const PreBuildInfo& pre_build_info, const Toolset& toolset)
@@ -297,7 +301,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 <NUL)",
+ return Strings::format(R"(cmd /c ""%s" %s %s %s %s 2>&1 <NUL")",
toolset.vcvarsall.u8string(),
Strings::join(" ", toolset.vcvarsall_options),
arch,
@@ -419,32 +423,6 @@ namespace vcpkg::Build
return variables;
}
- static std::string make_build_cmd(const VcpkgPaths& paths,
- const PreBuildInfo& pre_build_info,
- const BuildPackageConfig& config,
- Triplet triplet)
- {
- const Toolset& toolset = paths.get_toolset(pre_build_info);
- const fs::path& cmake_exe_path = paths.get_tool_exe(Tools::CMAKE);
- std::vector<System::CMakeVariable> variables = get_cmake_vars(paths, config, triplet, toolset);
-
- const std::string cmd_launch_cmake = System::make_cmake_cmd(cmake_exe_path, paths.ports_cmake, variables);
-
- std::string command = make_build_env_cmd(pre_build_info, toolset);
- if (!command.empty())
- {
-#ifdef _WIN32
- command.append(" & ");
-#else
- command.append(" && ");
-#endif
- }
-
- command.append(cmd_launch_cmake);
-
- return command;
- }
-
static std::string get_triplet_abi(const VcpkgPaths& paths, const PreBuildInfo& pre_build_info, Triplet triplet)
{
static std::map<fs::path, std::string> s_hash_cache;
@@ -536,14 +514,29 @@ namespace vcpkg::Build
const auto timer = Chrono::ElapsedTimer::create_started();
- std::string command = make_build_cmd(paths, pre_build_info, config, triplet);
- std::unordered_map<std::string, std::string> env = make_env_passthrough(pre_build_info);
-
+ auto command =
+ System::make_cmake_cmd(paths.get_tool_exe(Tools::CMAKE),
+ paths.ports_cmake,
+ get_cmake_vars(paths, config, triplet, paths.get_toolset(pre_build_info)));
#if defined(_WIN32)
- const int return_code =
- System::cmd_execute_clean(command, env, powershell_exe_path.parent_path().u8string() + ";");
+ std::string build_env_cmd = make_build_env_cmd(pre_build_info, paths.get_toolset(pre_build_info));
+
+ const std::unordered_map<std::string, std::string>& base_env = make_env_passthrough(pre_build_info);
+ static Cache<std::pair<const std::unordered_map<std::string, std::string>*, std::string>, System::Environment>
+ build_env_cache;
+
+ const auto& env = build_env_cache.get_lazy({&base_env, build_env_cmd}, [&]() {
+ auto clean_env =
+ System::get_modified_clean_environment(base_env, powershell_exe_path.parent_path().u8string() + ";");
+ if (build_env_cmd.empty())
+ return clean_env;
+ else
+ return System::cmd_execute_modify_env(build_env_cmd, clean_env);
+ });
+
+ const int return_code = System::cmd_execute(command, env);
#else
- const int return_code = System::cmd_execute_clean(command, env);
+ const int return_code = System::cmd_execute_clean(command);
#endif
// With the exception of empty packages, builds in "Download Mode" always result in failure.
if (config.build_package_options.only_downloads == Build::OnlyDownloads::YES)
@@ -757,7 +750,9 @@ namespace vcpkg::Build
return nullopt;
}
- static int decompress_archive(const VcpkgPaths& paths, const PackageSpec& spec, const fs::path& archive_path)
+ static System::ExitCodeAndOutput decompress_archive(const VcpkgPaths& paths,
+ const PackageSpec& spec,
+ const fs::path& archive_path)
{
auto& fs = paths.get_filesystem();
@@ -770,14 +765,12 @@ namespace vcpkg::Build
#if defined(_WIN32)
auto&& seven_zip_exe = paths.get_tool_exe(Tools::SEVEN_ZIP);
-
- int result = System::cmd_execute_clean(Strings::format(
- R"("%s" x "%s" -o"%s" -y >nul)", seven_zip_exe.u8string(), archive_path.u8string(), pkg_path.u8string()));
+ auto cmd = Strings::format(
+ R"("%s" x "%s" -o"%s" -y)", seven_zip_exe.u8string(), archive_path.u8string(), pkg_path.u8string());
#else
- int result = System::cmd_execute_clean(
- Strings::format(R"(unzip -qq "%s" "-d%s")", archive_path.u8string(), pkg_path.u8string()));
+ auto cmd = Strings::format(R"(unzip -qq "%s" "-d%s")", archive_path.u8string(), pkg_path.u8string());
#endif
- return result;
+ return System::cmd_execute_and_capture_output(cmd, System::get_clean_environment());
}
// Compress the source directory into the destination file.
@@ -793,8 +786,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(), destination.u8string(), source.u8string()));
+ System::cmd_execute_and_capture_output(
+ Strings::format(
+ R"("%s" a "%s" "%s\*")", seven_zip_exe.u8string(), destination.u8string(), source.u8string()),
+ System::get_clean_environment());
#else
System::cmd_execute_clean(
Strings::format(R"(cd '%s' && zip --quiet -r '%s' *)", source.u8string(), destination.u8string()));
@@ -873,7 +868,7 @@ namespace vcpkg::Build
{
System::print2("Using cached binary package: ", archive_path.u8string(), "\n");
- int archive_result = decompress_archive(paths, spec, archive_path);
+ int archive_result = decompress_archive(paths, spec, archive_path).exit_code;
if (archive_result != 0)
{
diff --git a/toolsrc/src/vcpkg/commands.edit.cpp b/toolsrc/src/vcpkg/commands.edit.cpp
index 314af1167..8391b3476 100644
--- a/toolsrc/src/vcpkg/commands.edit.cpp
+++ b/toolsrc/src/vcpkg/commands.edit.cpp
@@ -169,19 +169,19 @@ namespace vcpkg::Commands::Edit
candidate_paths.insert(candidate_paths.end(), from_registry.cbegin(), from_registry.cend());
const auto txt_default = System::get_registry_string(HKEY_CLASSES_ROOT, R"(.txt\ShellNew)", "ItemName");
- if(const auto entry = txt_default.get())
+ if (const auto entry = txt_default.get())
{
- #ifdef UNICODE
+#ifdef UNICODE
LPWSTR dst = new wchar_t[MAX_PATH];
ExpandEnvironmentStrings(Strings::to_utf16(*entry).c_str(), dst, MAX_PATH);
auto full_path = Strings::to_utf8(dst);
- #else
+#else
LPSTR dst = new char[MAX_PATH];
ExpandEnvironmentStrings(entry->c_str(), dst, MAX_PATH);
auto full_path = std::string(dst);
- #endif
+#endif
auto begin = full_path.find_first_not_of('@');
- candidate_paths.push_back(fs::u8path(full_path.substr(begin, full_path.find_first_of(',')-begin)));
+ candidate_paths.push_back(fs::u8path(full_path.substr(begin, full_path.find_first_of(',') - begin)));
}
#elif defined(__APPLE__)
candidate_paths.push_back(
@@ -191,17 +191,20 @@ namespace vcpkg::Commands::Edit
candidate_paths.push_back(fs::path{"/usr/share/code/bin/code"});
candidate_paths.push_back(fs::path{"/usr/bin/code"});
- if(System::cmd_execute("command -v xdg-mime") == 0)
+ if (System::cmd_execute("command -v xdg-mime") == 0)
{
auto mime_qry = Strings::format(R"(xdg-mime query default text/plain)");
auto execute_result = System::cmd_execute_and_capture_output(mime_qry);
- if(execute_result.exit_code == 0 && !execute_result.output.empty())
+ if (execute_result.exit_code == 0 && !execute_result.output.empty())
{
- mime_qry = Strings::format(R"(command -v %s)", execute_result.output.substr(0, execute_result.output.find('.')));
+ mime_qry = Strings::format(R"(command -v %s)",
+ execute_result.output.substr(0, execute_result.output.find('.')));
execute_result = System::cmd_execute_and_capture_output(mime_qry);
- if(execute_result.exit_code == 0 && !execute_result.output.empty())
+ if (execute_result.exit_code == 0 && !execute_result.output.empty())
{
- execute_result.output.erase(std::remove(std::begin(execute_result.output), std::end(execute_result.output), '\n'), std::end(execute_result.output));
+ execute_result.output.erase(
+ std::remove(std::begin(execute_result.output), std::end(execute_result.output), '\n'),
+ std::end(execute_result.output));
candidate_paths.push_back(fs::path{execute_result.output});
}
}
@@ -230,7 +233,7 @@ namespace vcpkg::Commands::Edit
#ifdef _WIN32
if (editor_exe == "Code.exe" || editor_exe == "Code - Insiders.exe")
{
- System::cmd_execute_no_wait(cmd_line + " <NUL");
+ System::cmd_execute_no_wait(Strings::concat("cmd /c \"", cmd_line, " <NUL\""));
Checks::exit_success(VCPKG_LINE_INFO);
}
#endif
diff --git a/toolsrc/src/vcpkg/commands.env.cpp b/toolsrc/src/vcpkg/commands.env.cpp
index b05c1f653..6d959d425 100644
--- a/toolsrc/src/vcpkg/commands.env.cpp
+++ b/toolsrc/src/vcpkg/commands.env.cpp
@@ -78,7 +78,7 @@ namespace vcpkg::Commands::Env
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);
+ System::cmd_execute(cmd, System::get_modified_clean_environment(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 bc75069cd..33b6acf0b 100644
--- a/toolsrc/src/vcpkg/commands.exportifw.cpp
+++ b/toolsrc/src/vcpkg/commands.exportifw.cpp
@@ -370,12 +370,13 @@ namespace vcpkg::Export::IFW
repository_dir.generic_u8string(),
failure_point.string());
- const auto cmd_line = Strings::format(R"("%s" --packages "%s" "%s" > nul)",
+ const auto cmd_line = Strings::format(R"("%s" --packages "%s" "%s")",
repogen_exe.u8string(),
packages_dir.u8string(),
repository_dir.u8string());
- const int exit_code = System::cmd_execute_clean(cmd_line);
+ const int exit_code =
+ System::cmd_execute_and_capture_output(cmd_line, System::get_clean_environment()).exit_code;
Checks::check_exit(VCPKG_LINE_INFO, exit_code == 0, "Error: IFW repository generating failed");
System::printf(
@@ -397,7 +398,7 @@ namespace vcpkg::Export::IFW
std::string ifw_repo_url = ifw_options.maybe_repository_url.value_or("");
if (!ifw_repo_url.empty())
{
- cmd_line = Strings::format(R"("%s" --online-only --config "%s" --repository "%s" "%s" > nul)",
+ cmd_line = Strings::format(R"("%s" --online-only --config "%s" --repository "%s" "%s")",
binarycreator_exe.u8string(),
config_file.u8string(),
repository_dir.u8string(),
@@ -405,14 +406,15 @@ namespace vcpkg::Export::IFW
}
else
{
- cmd_line = Strings::format(R"("%s" --config "%s" --packages "%s" "%s" > nul)",
+ cmd_line = Strings::format(R"("%s" --config "%s" --packages "%s" "%s")",
binarycreator_exe.u8string(),
config_file.u8string(),
packages_dir.u8string(),
installer_file.u8string());
}
- const int exit_code = System::cmd_execute_clean(cmd_line);
+ const int exit_code =
+ System::cmd_execute_and_capture_output(cmd_line, System::get_clean_environment()).exit_code;
Checks::check_exit(VCPKG_LINE_INFO, exit_code == 0, "Error: IFW installer generating failed");
System::printf(
diff --git a/toolsrc/src/vcpkg/commands.integrate.cpp b/toolsrc/src/vcpkg/commands.integrate.cpp
index 8b2b377bf..b427cb247 100644
--- a/toolsrc/src/vcpkg/commands.integrate.cpp
+++ b/toolsrc/src/vcpkg/commands.integrate.cpp
@@ -180,7 +180,7 @@ namespace vcpkg::Commands::Integrate
if (fs.exists(old_system_wide_targets_file))
{
const std::string param =
- Strings::format(R"(/c DEL "%s" /Q > nul)", old_system_wide_targets_file.string());
+ Strings::format(R"(/c "DEL "%s" /Q > nul")", old_system_wide_targets_file.string());
const ElevationPromptChoice user_choice = elevated_cmd_execute(param);
switch (user_choice)
{
@@ -211,7 +211,7 @@ namespace vcpkg::Commands::Integrate
const fs::path sys_src_path = tmp_dir / "vcpkg.system.targets";
fs.write_contents(sys_src_path, create_system_targets_shortcut(), VCPKG_LINE_INFO);
- const std::string param = Strings::format(R"(/c mkdir "%s" & copy "%s" "%s" /Y > nul)",
+ const std::string param = Strings::format(R"(/c "mkdir "%s" & copy "%s" "%s" /Y > nul")",
SYSTEM_WIDE_TARGETS_FILE.parent_path().string(),
sys_src_path.string(),
SYSTEM_WIDE_TARGETS_FILE.string());
@@ -347,12 +347,13 @@ CMake projects should use: "-DCMAKE_TOOLCHAIN_FILE=%s"
nuspec_file_path, create_nuspec_file_contents(paths.root, nuget_id, nupkg_version), VCPKG_LINE_INFO);
// Using all forward slashes for the command line
- const std::string cmd_line = Strings::format(R"("%s" pack -OutputDirectory "%s" "%s" > nul)",
+ const std::string cmd_line = Strings::format(R"("%s" pack -OutputDirectory "%s" "%s")",
nuget_exe.u8string(),
buildsystems_dir.u8string(),
nuspec_file_path.u8string());
- const int exit_code = System::cmd_execute_clean(cmd_line);
+ const int exit_code =
+ System::cmd_execute_and_capture_output(cmd_line, System::get_clean_environment()).exit_code;
const fs::path nuget_package = buildsystems_dir / Strings::format("%s.%s.nupkg", nuget_id, nupkg_version);
Checks::check_exit(
diff --git a/toolsrc/src/vcpkg/commands.portsdiff.cpp b/toolsrc/src/vcpkg/commands.portsdiff.cpp
index cddc274b8..7a9a49da4 100644
--- a/toolsrc/src/vcpkg/commands.portsdiff.cpp
+++ b/toolsrc/src/vcpkg/commands.portsdiff.cpp
@@ -90,16 +90,16 @@ namespace vcpkg::Commands::PortsDiff
const auto checkout_this_dir =
Strings::format(R"(.\%s)", ports_dir_name_as_string); // Must be relative to the root of the repository
- const std::string cmd =
- Strings::format(R"("%s" --git-dir="%s" --work-tree="%s" checkout %s -f -q -- %s %s & "%s" reset >NUL)",
- git_exe.u8string(),
- dot_git_dir.u8string(),
- temp_checkout_path.u8string(),
- git_commit_id,
- checkout_this_dir,
- ".vcpkg-root",
- git_exe.u8string());
- System::cmd_execute_clean(cmd);
+ const std::string cmd = Strings::format(R"("%s" --git-dir="%s" --work-tree="%s" checkout %s -f -q -- %s %s)",
+ git_exe.u8string(),
+ dot_git_dir.u8string(),
+ temp_checkout_path.u8string(),
+ git_commit_id,
+ checkout_this_dir,
+ ".vcpkg-root");
+ System::cmd_execute_and_capture_output(cmd, System::get_clean_environment());
+ System::cmd_execute_and_capture_output(Strings::format(R"("%s" reset)", git_exe.u8string()),
+ System::get_clean_environment());
const auto all_ports =
Paragraphs::load_all_ports(paths.get_filesystem(), temp_checkout_path / ports_dir_name_as_string);
std::map<std::string, VersionT> names_and_versions;
diff --git a/toolsrc/src/vcpkg/export.chocolatey.cpp b/toolsrc/src/vcpkg/export.chocolatey.cpp
index 492e5d371..81bdeacd7 100644
--- a/toolsrc/src/vcpkg/export.chocolatey.cpp
+++ b/toolsrc/src/vcpkg/export.chocolatey.cpp
@@ -218,12 +218,13 @@ if (Test-Path $installedDir)
const fs::path chocolatey_uninstall_file_path = per_package_dir_path / "tools" / "chocolateyUninstall.ps1";
fs.write_contents(chocolatey_uninstall_file_path, chocolatey_uninstall_content, VCPKG_LINE_INFO);
- const auto cmd_line = Strings::format(R"("%s" pack -OutputDirectory "%s" "%s" -NoDefaultExcludes > nul)",
+ const auto cmd_line = Strings::format(R"("%s" pack -OutputDirectory "%s" "%s" -NoDefaultExcludes)",
nuget_exe.u8string(),
exported_dir_path.u8string(),
nuspec_file_path.u8string());
- const int exit_code = System::cmd_execute_clean(cmd_line);
+ const int exit_code =
+ System::cmd_execute_and_capture_output(cmd_line, System::get_clean_environment()).exit_code;
Checks::check_exit(VCPKG_LINE_INFO, exit_code == 0, "Error: NuGet package creation failed");
}
}
diff --git a/toolsrc/src/vcpkg/export.cpp b/toolsrc/src/vcpkg/export.cpp
index 736ab6def..9abc48620 100644
--- a/toolsrc/src/vcpkg/export.cpp
+++ b/toolsrc/src/vcpkg/export.cpp
@@ -151,12 +151,13 @@ namespace vcpkg::Export
fs.write_contents(nuspec_file_path, nuspec_file_content, VCPKG_LINE_INFO);
// -NoDefaultExcludes is needed for ".vcpkg-root"
- const auto cmd_line = Strings::format(R"("%s" pack -OutputDirectory "%s" "%s" -NoDefaultExcludes > nul)",
+ const auto cmd_line = Strings::format(R"("%s" pack -OutputDirectory "%s" "%s" -NoDefaultExcludes)",
nuget_exe.u8string(),
output_dir.u8string(),
nuspec_file_path.u8string());
- const int exit_code = System::cmd_execute_clean(cmd_line);
+ const int exit_code =
+ System::cmd_execute_and_capture_output(cmd_line, System::get_clean_environment()).exit_code;
Checks::check_exit(VCPKG_LINE_INFO, exit_code == 0, "Error: NuGet package creation failed");
const fs::path output_path = output_dir / (nuget_id + "." + nuget_version + ".nupkg");
diff --git a/toolsrc/src/vcpkg/metrics.cpp b/toolsrc/src/vcpkg/metrics.cpp
index b971d96da..308ee0d51 100644
--- a/toolsrc/src/vcpkg/metrics.cpp
+++ b/toolsrc/src/vcpkg/metrics.cpp
@@ -441,7 +441,7 @@ namespace vcpkg::Metrics
if (ec) return;
#if defined(_WIN32)
- const std::string cmd_line = Strings::format("start \"vcpkgmetricsuploader.exe\" \"%s\" \"%s\"",
+ const std::string cmd_line = Strings::format("cmd /c \"start \"vcpkgmetricsuploader.exe\" \"%s\" \"%s\"\"",
temp_folder_path_exe.u8string(),
vcpkg_metrics_txt_path.u8string());
System::cmd_execute_no_wait(cmd_line);
diff --git a/toolsrc/vcpkglib/vcpkglib.vcxproj b/toolsrc/vcpkglib/vcpkglib.vcxproj
index e033625a0..a36cb9bd0 100644
--- a/toolsrc/vcpkglib/vcpkglib.vcxproj
+++ b/toolsrc/vcpkglib/vcpkglib.vcxproj
@@ -227,6 +227,7 @@
<ClCompile Include="..\src\vcpkg\base\stringview.cpp" />
<ClCompile Include="..\src\vcpkg\base\system.cpp" />
<ClCompile Include="..\src\vcpkg\base\system.print.cpp" />
+ <ClCompile Include="..\src\vcpkg\base\system.process.cpp" />
<ClCompile Include="..\src\vcpkg\binaryparagraph.cpp" />
<ClCompile Include="..\src\vcpkg\build.cpp" />
<ClCompile Include="..\src\vcpkg\cmakevars.cpp" />
diff --git a/toolsrc/vcpkglib/vcpkglib.vcxproj.filters b/toolsrc/vcpkglib/vcpkglib.vcxproj.filters
index d7adfcdfd..bc0a37029 100644
--- a/toolsrc/vcpkglib/vcpkglib.vcxproj.filters
+++ b/toolsrc/vcpkglib/vcpkglib.vcxproj.filters
@@ -228,6 +228,9 @@
<ClCompile Include="..\src\vcpkg\portfileprovider.cpp">
<Filter>Source Files\vcpkg</Filter>
</ClCompile>
+ <ClCompile Include="..\src\vcpkg\base\system.process.cpp">
+ <Filter>Source Files\vcpkg\base</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\include\pch.h">