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 #include +#include #include #include #include @@ -30,17 +31,34 @@ namespace vcpkg::System std::string output; }; - int cmd_execute_clean(const ZStringView cmd_line, - const std::unordered_map& 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& 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 per_line_cb, + const Environment& env = {}); + + int cmd_execute_and_stream_data(const ZStringView cmd_line, + std::function 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 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(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 #include #include -#include #include #include -#if defined(__APPLE__) -#include -#endif - -#if defined(__FreeBSD__) -#include -#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 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 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 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 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& 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& 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 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 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 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) - /// If non-null, an environment block to use for the new process. If null, the new - /// process will inherit the current environment. - 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(timer.microseconds()), " us\n"); - } -#endif - - int System::cmd_execute_clean(const ZStringView cmd_line, - const std::unordered_map& 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(timer.microseconds()), " us\n"); - return static_cast(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(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(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(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(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(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 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(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 +#include +#include +#include +#include +#include + +#include + +#if defined(__APPLE__) +#include +#endif + +#if defined(__FreeBSD__) +#include +#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 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 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 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& 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& 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 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 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 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&, + 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; + }; + + /// If non-null, an environment block to use for the new process. If null, the + /// new process will inherit the current environment. + static ExpectedT 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 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 + 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(buffer_size); + while (ReadFile(child_stdout, (void*)buf.get(), buffer_size, &bytes_read, nullptr) && bytes_read > 0) + { + f(StringView{buf.get(), static_cast(bytes_read)}); + } + + CloseHandle(child_stdout); + + return proc_info.wait_and_close_handles(); + } + }; + + static ExpectedT 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(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(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(long_exit_code); + g_ctrl_c_state.transition_from_spawn_process(); + + Debug::print( + "cmd_execute() returned ", exit_code, " after ", static_cast(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(timer.microseconds()), " us\n"); +#endif + return exit_code; + } + + int System::cmd_execute_and_stream_lines(const ZStringView cmd_line, + std::function 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 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(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(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 #include #include #include @@ -266,21 +267,24 @@ namespace vcpkg::Build })); } - static auto make_env_passthrough(const PreBuildInfo& pre_build_info) -> std::unordered_map + static const std::unordered_map& make_env_passthrough(const PreBuildInfo& pre_build_info) { - std::unordered_map env; + static Cache, std::unordered_map> envs; + return envs.get_lazy(pre_build_info.passthrough_env_vars, [&]() { + std::unordered_map 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 &1 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 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 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& base_env = make_env_passthrough(pre_build_info); + static Cache*, 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)", + 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 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 @@ + 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 @@ Source Files\vcpkg + + Source Files\vcpkg\base +