[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
This commit is contained in:
Robert Schumacher 2020-02-08 22:45:36 -08:00 committed by GitHub
parent b20c6d3b89
commit f7fb56decd
18 changed files with 749 additions and 561 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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();
}

View File

@ -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)

View File

@ -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 (;;)
{

View File

@ -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(); }
}

View File

@ -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
}

View File

@ -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);
return env;
if (env_val)
{
env[env_var] = env_val.value_or_exit(VCPKG_LINE_INFO);
}
}
return env;
});
}
std::string make_build_env_cmd(const PreBuildInfo& pre_build_info, const Toolset& toolset)
@ -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)
{

View File

@ -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

View File

@ -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);
}
}

View File

@ -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(

View File

@ -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(

View File

@ -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;

View File

@ -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");
}
}

View File

@ -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");

View File

@ -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);

View File

@ -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" />

View File

@ -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">