From 41fb7fb672c043c78f60d1abf972da01474fea64 Mon Sep 17 00:00:00 2001 From: nicole mazzuca Date: Thu, 14 Jan 2021 09:06:31 -0800 Subject: [PATCH] [vcpkg] Rewriting CmdLineBuilder (2/n) (#15627) * [vcpkg] Rewriting CmdLineBuilder (2/n) I would like, and I think the team would like generally, to switch to using stuff like `posix_spawn`, as opposed to the existing use of `system` and `popen`. This requires a pretty large change to how we use CmdLineBuilder. The first change we have to make is that the execute functions _cannot_ take a StringView anymore. This PR makes that change. --- include/vcpkg/base/cache.h | 4 +- include/vcpkg/base/system.process.h | 42 +++++++++------ include/vcpkg/build.h | 4 +- include/vcpkg/buildenvironment.h | 6 +-- src/vcpkg/archives.cpp | 33 +++++++----- src/vcpkg/base/system.process.cpp | 81 ++++++++++++++++------------- src/vcpkg/binarycaching.cpp | 12 +++-- src/vcpkg/build.cpp | 19 +++---- src/vcpkg/buildenvironment.cpp | 6 +-- src/vcpkg/commands.contact.cpp | 2 +- src/vcpkg/commands.create.cpp | 2 +- src/vcpkg/commands.edit.cpp | 14 ++--- src/vcpkg/commands.env.cpp | 6 ++- src/vcpkg/commands.integrate.cpp | 18 ++++--- src/vcpkg/commands.porthistory.cpp | 22 ++++---- src/vcpkg/commands.portsdiff.cpp | 21 ++++---- src/vcpkg/export.chocolatey.cpp | 10 ++-- src/vcpkg/export.ifw.cpp | 33 ++++++------ src/vcpkg/export.prefab.cpp | 22 ++++---- src/vcpkg/metrics.cpp | 22 +++++--- src/vcpkg/postbuildlint.cpp | 23 ++++---- src/vcpkg/tools.cpp | 16 +++--- src/vcpkg/vcpkgpaths.cpp | 18 ++++--- src/vcpkg/visualstudio.cpp | 10 +++- 24 files changed, 257 insertions(+), 189 deletions(-) diff --git a/include/vcpkg/base/cache.h b/include/vcpkg/base/cache.h index 1996f2587d9169..7efae1361a7171 100644 --- a/include/vcpkg/base/cache.h +++ b/include/vcpkg/base/cache.h @@ -4,7 +4,7 @@ namespace vcpkg { - template + template> struct Cache { template @@ -16,6 +16,6 @@ namespace vcpkg } private: - mutable std::map m_cache; + mutable std::map m_cache; }; } diff --git a/include/vcpkg/base/system.process.h b/include/vcpkg/base/system.process.h index 1c62fa28585df5..68d4da4e4b504b 100644 --- a/include/vcpkg/base/system.process.h +++ b/include/vcpkg/base/system.process.h @@ -20,10 +20,6 @@ namespace vcpkg::System std::string s; }; - std::string make_basic_cmake_cmd(const fs::path& cmake_tool_path, - const fs::path& cmake_script, - const std::vector& pass_variables); - struct CmdLineBuilder { CmdLineBuilder() = default; @@ -46,15 +42,27 @@ namespace vcpkg::System CmdLineBuilder&& raw_arg(StringView s) && { return std::move(raw_arg(s)); } std::string&& extract() && { return std::move(buf); } - operator StringView() noexcept { return buf; } StringView command_line() const { return buf; } void clear() { buf.clear(); } + bool empty() const { return buf.empty(); } private: std::string buf; }; + struct CmdLineBuilderMapLess + { + bool operator()(const CmdLineBuilder& lhs, const CmdLineBuilder& rhs) const + { + return lhs.command_line() < rhs.command_line(); + } + }; + + CmdLineBuilder make_basic_cmake_cmd(const fs::path& cmake_tool_path, + const fs::path& cmake_script, + const std::vector& pass_variables); + fs::path get_exe_path_of_current_process(); struct ExitCodeAndOutput @@ -79,48 +87,48 @@ namespace vcpkg::System const fs::path& working_directory; }; - int cmd_execute(StringView cmd_line, InWorkingDirectory wd, const Environment& env = {}); - inline int cmd_execute(StringView cmd_line, const Environment& env = {}) + int cmd_execute(const CmdLineBuilder& cmd_line, InWorkingDirectory wd, const Environment& env = {}); + inline int cmd_execute(const CmdLineBuilder& cmd_line, const Environment& env = {}) { return cmd_execute(cmd_line, InWorkingDirectory{fs::path()}, env); } - int cmd_execute_clean(StringView cmd_line, InWorkingDirectory wd); - inline int cmd_execute_clean(StringView cmd_line) + int cmd_execute_clean(const CmdLineBuilder& cmd_line, InWorkingDirectory wd); + inline int cmd_execute_clean(const CmdLineBuilder& cmd_line) { return cmd_execute_clean(cmd_line, InWorkingDirectory{fs::path()}); } #if defined(_WIN32) - Environment cmd_execute_modify_env(StringView cmd_line, const Environment& env = {}); + Environment cmd_execute_modify_env(const CmdLineBuilder& cmd_line, const Environment& env = {}); - void cmd_execute_background(const StringView cmd_line); + void cmd_execute_background(const CmdLineBuilder& cmd_line); #endif - ExitCodeAndOutput cmd_execute_and_capture_output(StringView cmd_line, + ExitCodeAndOutput cmd_execute_and_capture_output(const CmdLineBuilder& cmd_line, InWorkingDirectory wd, const Environment& env = {}); - inline ExitCodeAndOutput cmd_execute_and_capture_output(StringView cmd_line, const Environment& env = {}) + inline ExitCodeAndOutput cmd_execute_and_capture_output(const CmdLineBuilder& cmd_line, const Environment& env = {}) { return cmd_execute_and_capture_output(cmd_line, InWorkingDirectory{fs::path()}, env); } - int cmd_execute_and_stream_lines(StringView cmd_line, + int cmd_execute_and_stream_lines(const CmdLineBuilder& cmd_line, InWorkingDirectory wd, std::function per_line_cb, const Environment& env = {}); - inline int cmd_execute_and_stream_lines(StringView cmd_line, + inline int cmd_execute_and_stream_lines(const CmdLineBuilder& cmd_line, std::function per_line_cb, const Environment& env = {}) { return cmd_execute_and_stream_lines(cmd_line, InWorkingDirectory{fs::path()}, std::move(per_line_cb), env); } - int cmd_execute_and_stream_data(StringView cmd_line, + int cmd_execute_and_stream_data(const CmdLineBuilder& cmd_line, InWorkingDirectory wd, std::function data_cb, const Environment& env = {}); - inline int cmd_execute_and_stream_data(StringView cmd_line, + inline int cmd_execute_and_stream_data(const CmdLineBuilder& cmd_line, std::function data_cb, const Environment& env = {}) { diff --git a/include/vcpkg/build.h b/include/vcpkg/build.h index bd5188b9fbd18e..0659eedcd12661 100644 --- a/include/vcpkg/build.h +++ b/include/vcpkg/build.h @@ -232,7 +232,7 @@ namespace vcpkg::Build const VcpkgPaths& m_paths; }; - std::string make_build_env_cmd(const PreBuildInfo& pre_build_info, const Toolset& toolset); + System::CmdLineBuilder make_build_env_cmd(const PreBuildInfo& pre_build_info, const Toolset& toolset); struct ExtendedBuildResult { @@ -369,7 +369,7 @@ namespace vcpkg::Build struct EnvMapEntry { std::unordered_map env_map; - Cache cmd_cache; + Cache cmd_cache; }; Cache, EnvMapEntry> envs; diff --git a/include/vcpkg/buildenvironment.h b/include/vcpkg/buildenvironment.h index aa69708b670746..54977232051458 100644 --- a/include/vcpkg/buildenvironment.h +++ b/include/vcpkg/buildenvironment.h @@ -9,7 +9,7 @@ namespace vcpkg { - std::string make_cmake_cmd(const VcpkgPaths& paths, - const fs::path& cmake_script, - std::vector&& pass_variables); + System::CmdLineBuilder make_cmake_cmd(const VcpkgPaths& paths, + const fs::path& cmake_script, + std::vector&& pass_variables); } diff --git a/src/vcpkg/archives.cpp b/src/vcpkg/archives.cpp index 33132f892c4ac3..25449cd63d5677 100644 --- a/src/vcpkg/archives.cpp +++ b/src/vcpkg/archives.cpp @@ -44,13 +44,21 @@ namespace vcpkg::Archives const std::string nugetid = match[1]; const std::string version = match[2]; - const auto code_and_output = System::cmd_execute_and_capture_output(Strings::format( - R"("%s" install %s -Version %s -OutputDirectory "%s" -Source "%s" -nocache -DirectDownload -NonInteractive -ForceEnglishOutput -PackageSaveMode nuspec)", - fs::u8string(nuget_exe), - nugetid, - version, - fs::u8string(to_path_partial), - fs::u8string(paths.downloads))); + const auto code_and_output = System::cmd_execute_and_capture_output(System::CmdLineBuilder{nuget_exe} + .string_arg("install") + .string_arg(nugetid) + .string_arg("-Version") + .string_arg(version) + .string_arg("-OutputDirectory") + .path_arg(to_path_partial) + .string_arg("-Source") + .path_arg(paths.downloads) + .string_arg("-nocache") + .string_arg("-DirectDownload") + .string_arg("-NonInteractive") + .string_arg("-ForceEnglishOutput") + .string_arg("-PackageSaveMode") + .string_arg("nuspec")); Checks::check_exit(VCPKG_LINE_INFO, code_and_output.exit_code == 0, @@ -65,11 +73,12 @@ namespace vcpkg::Archives Checks::check_exit(VCPKG_LINE_INFO, !recursion_limiter_sevenzip); recursion_limiter_sevenzip = true; const auto seven_zip = paths.get_tool_exe(Tools::SEVEN_ZIP); - const auto code_and_output = - System::cmd_execute_and_capture_output(Strings::format(R"("%s" x "%s" -o"%s" -y)", - fs::u8string(seven_zip), - fs::u8string(archive), - fs::u8string(to_path_partial))); + const auto code_and_output = System::cmd_execute_and_capture_output( + System::CmdLineBuilder{seven_zip} + .string_arg("x") + .path_arg(archive) + .string_arg(Strings::format("-o%s", fs::u8string(to_path_partial))) + .string_arg("-y")); Checks::check_exit(VCPKG_LINE_INFO, code_and_output.exit_code == 0, "7zip failed while extracting '%s' with message:\n%s", diff --git a/src/vcpkg/base/system.process.cpp b/src/vcpkg/base/system.process.cpp index ff608ea0e50ee5..e4218b7162fa39 100644 --- a/src/vcpkg/base/system.process.cpp +++ b/src/vcpkg/base/system.process.cpp @@ -184,18 +184,17 @@ namespace vcpkg } System::CMakeVariable::CMakeVariable(std::string var) : s(std::move(var)) { } - std::string System::make_basic_cmake_cmd(const fs::path& cmake_tool_path, - const fs::path& cmake_script, - const std::vector& pass_variables) + System::CmdLineBuilder System::make_basic_cmake_cmd(const fs::path& cmake_tool_path, + const fs::path& cmake_script, + const std::vector& pass_variables) { - System::CmdLineBuilder cmd; - cmd.path_arg(cmake_tool_path); + System::CmdLineBuilder cmd{cmake_tool_path}; for (auto&& var : pass_variables) { cmd.string_arg(var.s); } cmd.string_arg("-P").path_arg(cmake_script); - return std::move(cmd).extract(); + return cmd; } System::CmdLineBuilder& System::CmdLineBuilder::string_arg(StringView s) & @@ -384,7 +383,7 @@ namespace vcpkg return clean_env; } - int System::cmd_execute_clean(StringView cmd_line, InWorkingDirectory wd) + int System::cmd_execute_clean(const CmdLineBuilder& cmd_line, InWorkingDirectory wd) { return cmd_execute(cmd_line, wd, get_clean_environment()); } @@ -565,12 +564,12 @@ namespace vcpkg #endif #if defined(_WIN32) - void System::cmd_execute_background(StringView cmd_line) + void System::cmd_execute_background(const CmdLineBuilder& cmd_line) { auto timer = Chrono::ElapsedTimer::create_started(); auto process_info = - windows_create_windowless_process(cmd_line, + windows_create_windowless_process(cmd_line.command_line(), InWorkingDirectory{fs::path()}, {}, CREATE_NEW_CONSOLE | CREATE_NO_WINDOW | CREATE_BREAKAWAY_FROM_JOB); @@ -582,48 +581,55 @@ namespace vcpkg Debug::print("cmd_execute_background() took ", static_cast(timer.microseconds()), " us\n"); } - Environment System::cmd_execute_modify_env(StringView cmd_line, const Environment& env) + Environment System::cmd_execute_modify_env(const CmdLineBuilder& cmd_line, const Environment& env) { static StringLiteral magic_string = "cdARN4xjKueKScMy9C6H"; - auto actual_cmd_line = Strings::concat(cmd_line, " & echo ", magic_string, "& set"); + auto actual_cmd_line = cmd_line; + actual_cmd_line.raw_arg(Strings::concat(" & 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; + Debug::print("command line: ", actual_cmd_line.command_line(), "\n"); + Debug::print(rc_output.output, "\n"); + + auto it = Strings::search(rc_output.output, magic_string); + const char* const last = rc_output.output.data() + rc_output.output.size(); + + Checks::check_exit(VCPKG_LINE_INFO, it != last); + // find the first non-whitespace character after the magic string + it = std::find_if_not(it + magic_string.size(), last, ::isspace); + Checks::check_exit(VCPKG_LINE_INFO, it != last); std::wstring out_env; for (;;) { - 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))); + auto equal_it = std::find(it, last, '='); + if (equal_it == last) break; + StringView variable_name(it, equal_it); + auto newline_it = std::find(equal_it + 1, last, '\r'); + if (newline_it == last) break; + StringView value(equal_it + 1, newline_it); + + out_env.append(Strings::to_utf16(Strings::concat(variable_name, '=', value))); out_env.push_back(L'\0'); - it = nl + 1; - if (it != e && *it == '\n') ++it; + it = newline_it + 1; + if (it != last && *it == '\n') ++it; } return {std::move(out_env)}; } #endif - int System::cmd_execute(StringView cmd_line, System::InWorkingDirectory wd, const Environment& env) + int System::cmd_execute(const CmdLineBuilder& cmd_line, System::InWorkingDirectory wd, 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_windowless_process(cmd_line, wd, env, 0); + auto proc_info = windows_create_windowless_process(cmd_line.command_line(), wd, env, 0); auto long_exit_code = [&]() -> unsigned long { if (auto p = proc_info.get()) return p->wait(); @@ -641,12 +647,15 @@ namespace vcpkg std::string real_command_line; if (wd.working_directory.empty()) { - real_command_line.assign(cmd_line.begin(), cmd_line.end()); + real_command_line = cmd_line.command_line().to_string(); } else { - real_command_line = - System::CmdLineBuilder("cd").path_arg(wd.working_directory).raw_arg("&&").raw_arg(cmd_line).extract(); + real_command_line = System::CmdLineBuilder("cd") + .path_arg(wd.working_directory) + .raw_arg("&&") + .raw_arg(cmd_line.command_line()) + .extract(); } Debug::print("system(", real_command_line, ")\n"); fflush(nullptr); @@ -658,7 +667,7 @@ namespace vcpkg return exit_code; } - int System::cmd_execute_and_stream_lines(StringView cmd_line, + int System::cmd_execute_and_stream_lines(const CmdLineBuilder& cmd_line, System::InWorkingDirectory wd, std::function per_line_cb, const Environment& env) @@ -687,7 +696,7 @@ namespace vcpkg return rc; } - int System::cmd_execute_and_stream_data(StringView cmd_line, + int System::cmd_execute_and_stream_data(const CmdLineBuilder& cmd_line, System::InWorkingDirectory wd, std::function data_cb, const Environment& env) @@ -698,7 +707,7 @@ namespace vcpkg using vcpkg::g_ctrl_c_state; g_ctrl_c_state.transition_to_spawn_process(); - auto maybe_proc_info = windows_create_process_redirect(cmd_line, wd, env, 0); + auto maybe_proc_info = windows_create_process_redirect(cmd_line.command_line(), wd, env, 0); auto exit_code = [&]() -> unsigned long { if (auto p = maybe_proc_info.get()) return p->wait_and_stream_output(data_cb); @@ -711,14 +720,14 @@ namespace vcpkg std::string actual_cmd_line; if (wd.working_directory.empty()) { - actual_cmd_line = Strings::format(R"(%s 2>&1)", cmd_line); + actual_cmd_line = Strings::format(R"(%s 2>&1)", cmd_line.command_line()); } else { actual_cmd_line = System::CmdLineBuilder("cd") .path_arg(wd.working_directory) .raw_arg("&&") - .raw_arg(cmd_line) + .raw_arg(cmd_line.command_line()) .raw_arg("2>&1") .extract(); } @@ -754,7 +763,7 @@ namespace vcpkg return exit_code; } - ExitCodeAndOutput System::cmd_execute_and_capture_output(StringView cmd_line, + ExitCodeAndOutput System::cmd_execute_and_capture_output(const CmdLineBuilder& cmd_line, System::InWorkingDirectory wd, const Environment& env) { diff --git a/src/vcpkg/binarycaching.cpp b/src/vcpkg/binarycaching.cpp index 4592760ecb211f..d3e464f77d5c87 100644 --- a/src/vcpkg/binarycaching.cpp +++ b/src/vcpkg/binarycaching.cpp @@ -420,7 +420,7 @@ namespace { } - int run_nuget_commandline(StringView cmdline) + int run_nuget_commandline(const System::CmdLineBuilder& cmdline) { if (m_interactive) { @@ -450,7 +450,9 @@ namespace } else if (res.output.find("for example \"-ApiKey AzureDevOps\"") != std::string::npos) { - auto res2 = System::cmd_execute_and_capture_output(Strings::concat(cmdline, " -ApiKey AzureDevOps")); + auto real_cmdline = cmdline; + real_cmdline.string_arg("-ApiKey").string_arg("AzureDevOps"); + auto res2 = System::cmd_execute_and_capture_output(real_cmdline); if (Debug::g_debugging) { System::print2(res2.output); @@ -514,7 +516,7 @@ namespace }; const auto& nuget_exe = paths.get_tool_exe("nuget"); - std::vector cmdlines; + std::vector cmdlines; if (!m_read_sources.empty()) { @@ -544,7 +546,7 @@ namespace cmdline.string_arg("-NonInteractive"); } - cmdlines.push_back(std::move(cmdline).extract()); + cmdlines.push_back(std::move(cmdline)); } for (auto&& cfg : m_read_configs) { @@ -574,7 +576,7 @@ namespace cmdline.string_arg("-NonInteractive"); } - cmdlines.push_back(std::move(cmdline).extract()); + cmdlines.push_back(std::move(cmdline)); } const size_t current_restored = m_restored.size(); diff --git a/src/vcpkg/build.cpp b/src/vcpkg/build.cpp index 52385c2792af38..29514b2b27ee7d 100644 --- a/src/vcpkg/build.cpp +++ b/src/vcpkg/build.cpp @@ -328,7 +328,7 @@ namespace vcpkg::Build #if defined(_WIN32) const System::Environment& EnvCache::get_action_env(const VcpkgPaths& paths, const AbiInfo& abi_info) { - std::string build_env_cmd = + auto build_env_cmd = make_build_env_cmd(*abi_info.pre_build_info, abi_info.toolset.value_or_exit(VCPKG_LINE_INFO)); const auto& base_env = envs.get_lazy(abi_info.pre_build_info->passthrough_env_vars, [&]() -> EnvMapEntry { @@ -439,9 +439,9 @@ namespace vcpkg::Build }); } - std::string make_build_env_cmd(const PreBuildInfo& pre_build_info, const Toolset& toolset) + System::CmdLineBuilder make_build_env_cmd(const PreBuildInfo& pre_build_info, const Toolset& toolset) { - if (!pre_build_info.using_vcvars()) return ""; + if (!pre_build_info.using_vcvars()) return {}; const char* tonull = " >nul"; if (Debug::g_debugging) @@ -452,12 +452,13 @@ 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"(cmd /c ""%s" %s %s %s %s 2>&1 &1 create_binary_control_file( diff --git a/src/vcpkg/buildenvironment.cpp b/src/vcpkg/buildenvironment.cpp index 2b63a16b508572..cfcedff2fc75e9 100644 --- a/src/vcpkg/buildenvironment.cpp +++ b/src/vcpkg/buildenvironment.cpp @@ -4,9 +4,9 @@ namespace vcpkg { - std::string make_cmake_cmd(const VcpkgPaths& paths, - const fs::path& cmake_script, - std::vector&& pass_variables) + System::CmdLineBuilder make_cmake_cmd(const VcpkgPaths& paths, + const fs::path& cmake_script, + std::vector&& pass_variables) { auto local_variables = std::move(pass_variables); local_variables.emplace_back("VCPKG_ROOT_DIR", paths.root); diff --git a/src/vcpkg/commands.contact.cpp b/src/vcpkg/commands.contact.cpp index e40aa34b29236c..68519c7e7e57f6 100644 --- a/src/vcpkg/commands.contact.cpp +++ b/src/vcpkg/commands.contact.cpp @@ -45,7 +45,7 @@ namespace vcpkg::Commands::Contact } #if defined(_WIN32) - System::cmd_execute("start https://aka.ms/NPS_vcpkg"); + System::cmd_execute(System::CmdLineBuilder("start").string_arg("https://aka.ms/NPS_vcpkg")); System::print2("Default browser launched to https://aka.ms/NPS_vcpkg; thank you for your feedback!\n"); #else System::print2("Please navigate to https://aka.ms/NPS_vcpkg in your preferred browser. Thank you for your " diff --git a/src/vcpkg/commands.create.cpp b/src/vcpkg/commands.create.cpp index 28f0377c58f9d0..44c2c55ab9b789 100644 --- a/src/vcpkg/commands.create.cpp +++ b/src/vcpkg/commands.create.cpp @@ -51,7 +51,7 @@ namespace vcpkg::Commands::Create cmake_args.emplace_back("FILENAME", zip_file_name); } - const std::string cmd_launch_cmake = make_cmake_cmd(paths, paths.ports_cmake, std::move(cmake_args)); + auto cmd_launch_cmake = make_cmake_cmd(paths, paths.ports_cmake, std::move(cmake_args)); return System::cmd_execute_clean(cmd_launch_cmake); } diff --git a/src/vcpkg/commands.edit.cpp b/src/vcpkg/commands.edit.cpp index e23b35d0cf1829..86e0b7e3289bb9 100644 --- a/src/vcpkg/commands.edit.cpp +++ b/src/vcpkg/commands.edit.cpp @@ -219,14 +219,15 @@ 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(System::CmdLineBuilder("command").string_arg("-v").string_arg("xdg-mime")) == 0) { - auto mime_qry = Strings::format(R"(xdg-mime query default text/plain)"); + auto mime_qry = + System::CmdLineBuilder("xdg-mime").string_arg("query").string_arg("default").string_arg("text/plain"); auto execute_result = System::cmd_execute_and_capture_output(mime_qry); 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 = System::CmdLineBuilder("command").string_arg("-v").string_arg( + 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()) { @@ -254,7 +255,7 @@ namespace vcpkg::Commands::Edit const fs::path env_editor = *it; const std::vector arguments = create_editor_arguments(paths, options, ports); const auto args_as_string = Strings::join(" ", arguments); - const auto cmd_line = Strings::format(R"("%s" %s -n)", fs::u8string(env_editor), args_as_string); + auto cmd_line = System::CmdLineBuilder(env_editor).raw_arg(args_as_string).string_arg("-n"); auto editor_exe = fs::u8string(env_editor.filename()); @@ -262,7 +263,8 @@ namespace vcpkg::Commands::Edit if (editor_exe == "Code.exe" || editor_exe == "Code - Insiders.exe") { // note that we are invoking cmd silently but Code.exe is relaunched from there - System::cmd_execute_background(Strings::concat("cmd /c \"", cmd_line, " NuGet Package Manager->Package Manager Console const fs::path script_path = paths.scripts / "addPoshVcpkgToPowershellProfile.ps1"; const auto& ps = paths.get_tool_exe("powershell-core"); - const std::string cmd = Strings::format(R"("%s" -NoProfile -ExecutionPolicy Bypass -Command "& {& '%s' }")", - fs::u8string(ps), - fs::u8string(script_path)); + auto cmd = System::CmdLineBuilder(ps) + .string_arg("-NoProfile") + .string_arg("-ExecutionPolicy") + .string_arg("Bypass") + .string_arg("-Command") + .string_arg(Strings::format("& {& '%s' }", fs::u8string(script_path))); const int rc = System::cmd_execute(cmd); if (rc) { diff --git a/src/vcpkg/commands.porthistory.cpp b/src/vcpkg/commands.porthistory.cpp index 392bde7d0d6287..a2938242f55330 100644 --- a/src/vcpkg/commands.porthistory.cpp +++ b/src/vcpkg/commands.porthistory.cpp @@ -31,21 +31,20 @@ namespace vcpkg::Commands::PortHistory const System::ExitCodeAndOutput run_git_command_inner(const VcpkgPaths& paths, const fs::path& dot_git_directory, const fs::path& working_directory, - StringView cmd) + const System::CmdLineBuilder& cmd) { const fs::path& git_exe = paths.get_tool_exe(Tools::GIT); - System::CmdLineBuilder builder; - builder.path_arg(git_exe) - .string_arg(Strings::concat("--git-dir=", fs::u8string(dot_git_directory))) - .string_arg(Strings::concat("--work-tree=", fs::u8string(working_directory))); - const std::string full_cmd = Strings::concat(std::move(builder).extract(), " ", cmd); + auto full_cmd = System::CmdLineBuilder(git_exe) + .string_arg(Strings::concat("--git-dir=", fs::u8string(dot_git_directory))) + .string_arg(Strings::concat("--work-tree=", fs::u8string(working_directory))) + .raw_arg(cmd.command_line()); - const auto output = System::cmd_execute_and_capture_output(full_cmd); + auto output = System::cmd_execute_and_capture_output(full_cmd); return output; } - const System::ExitCodeAndOutput run_git_command(const VcpkgPaths& paths, StringView cmd) + const System::ExitCodeAndOutput run_git_command(const VcpkgPaths& paths, const System::CmdLineBuilder& cmd) { const fs::path& work_dir = paths.root; const fs::path dot_git_dir = paths.root / ".git"; @@ -89,7 +88,8 @@ namespace vcpkg::Commands::PortHistory const std::string& commit_date, const std::string& port_name) { - const std::string rev_parse_cmd = Strings::format("rev-parse %s:ports/%s", commit_id, port_name); + auto rev_parse_cmd = + System::CmdLineBuilder("rev-parse").string_arg(Strings::concat(commit_id, ":ports/", port_name)); auto rev_parse_output = run_git_command(paths, rev_parse_cmd); if (rev_parse_output.exit_code == 0) { @@ -97,7 +97,7 @@ namespace vcpkg::Commands::PortHistory const auto git_tree = Strings::trim(std::move(rev_parse_output.output)); // Do we have a manifest file? - const std::string manifest_cmd = Strings::format(R"(show %s:vcpkg.json)", git_tree, port_name); + auto manifest_cmd = System::CmdLineBuilder("show").string_arg(Strings::concat(git_tree, ":vcpkg.json")); auto manifest_output = run_git_command(paths, manifest_cmd); if (manifest_output.exit_code == 0) { @@ -105,7 +105,7 @@ namespace vcpkg::Commands::PortHistory manifest_output.output, git_tree, commit_id, commit_date, port_name, true); } - const std::string cmd = Strings::format(R"(show %s:CONTROL)", git_tree, commit_id, port_name); + auto cmd = System::CmdLineBuilder("show").string_arg(Strings::concat(git_tree, ":CONTROL")); auto control_output = run_git_command(paths, cmd); if (control_output.exit_code == 0) diff --git a/src/vcpkg/commands.portsdiff.cpp b/src/vcpkg/commands.portsdiff.cpp index d93d4d98a84981..2de30355b67667 100644 --- a/src/vcpkg/commands.portsdiff.cpp +++ b/src/vcpkg/commands.portsdiff.cpp @@ -91,15 +91,18 @@ 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)", - fs::u8string(git_exe), - fs::u8string(dot_git_dir), - fs::u8string(temp_checkout_path), - git_commit_id, - checkout_this_dir, - ".vcpkg-root"); + auto cmd = System::CmdLineBuilder(git_exe) + .string_arg(Strings::format("--git-dir=%s", fs::u8string(dot_git_dir))) + .string_arg(Strings::format("--work-tree=%s", fs::u8string(temp_checkout_path))) + .string_arg("checkout") + .string_arg(git_commit_id) + .string_arg("-f") + .string_arg("-q") + .string_arg("--") + .string_arg(checkout_this_dir) + .string_arg(".vcpkg-root"); System::cmd_execute_and_capture_output(cmd, System::get_clean_environment()); - System::cmd_execute_and_capture_output(Strings::format(R"("%s" reset)", fs::u8string(git_exe)), + System::cmd_execute_and_capture_output(System::CmdLineBuilder(git_exe).string_arg("reset"), System::get_clean_environment()); const auto ports_at_commit = Paragraphs::load_overlay_ports(paths, temp_checkout_path / ports_dir_name_as_string); @@ -117,7 +120,7 @@ namespace vcpkg::Commands::PortsDiff { static const std::string VALID_COMMIT_OUTPUT = "commit\n"; - const auto cmd = Strings::format(R"("%s" cat-file -t %s)", fs::u8string(git_exe), git_commit_id); + auto cmd = System::CmdLineBuilder(git_exe).string_arg("cat-file").string_arg("-t").string_arg(git_commit_id); const System::ExitCodeAndOutput output = System::cmd_execute_and_capture_output(cmd); Checks::check_exit( VCPKG_LINE_INFO, output.output == VALID_COMMIT_OUTPUT, "Invalid commit id %s", git_commit_id); diff --git a/src/vcpkg/export.chocolatey.cpp b/src/vcpkg/export.chocolatey.cpp index b9f14a4d1e2a2f..13df2c909f0bfb 100644 --- a/src/vcpkg/export.chocolatey.cpp +++ b/src/vcpkg/export.chocolatey.cpp @@ -216,10 +216,12 @@ 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)", - fs::u8string(nuget_exe), - fs::u8string(exported_dir_path), - fs::u8string(nuspec_file_path)); + auto cmd_line = System::CmdLineBuilder(nuget_exe) + .string_arg("pack") + .string_arg("-OutputDirectory") + .path_arg(exported_dir_path) + .path_arg(nuspec_file_path) + .string_arg("-NoDefaultExcludes"); const int exit_code = System::cmd_execute_and_capture_output(cmd_line, System::get_clean_environment()).exit_code; diff --git a/src/vcpkg/export.ifw.cpp b/src/vcpkg/export.ifw.cpp index 9726a93f92d13a..a2582d213e8e49 100644 --- a/src/vcpkg/export.ifw.cpp +++ b/src/vcpkg/export.ifw.cpp @@ -370,10 +370,10 @@ namespace vcpkg::Export::IFW fs::generic_u8string(repository_dir), failure_point.string()); - const auto cmd_line = Strings::format(R"("%s" --packages "%s" "%s")", - fs::u8string(repogen_exe), - fs::u8string(packages_dir), - fs::u8string(repository_dir)); + auto cmd_line = System::CmdLineBuilder(repogen_exe) + .string_arg("--packages") + .path_arg(packages_dir) + .path_arg(repository_dir); const int exit_code = System::cmd_execute_and_capture_output(cmd_line, System::get_clean_environment()).exit_code; @@ -393,24 +393,27 @@ namespace vcpkg::Export::IFW System::printf("Generating installer %s...\n", fs::generic_u8string(installer_file)); - std::string cmd_line; + System::CmdLineBuilder cmd_line; std::string ifw_repo_url = ifw_options.maybe_repository_url.value_or(""); if (!ifw_repo_url.empty()) { - cmd_line = Strings::format(R"("%s" --online-only --config "%s" --repository "%s" "%s")", - fs::u8string(binarycreator_exe), - fs::u8string(config_file), - fs::u8string(repository_dir), - fs::u8string(installer_file)); + cmd_line = System::CmdLineBuilder(binarycreator_exe) + .string_arg("--online-only") + .string_arg("--config") + .path_arg(config_file) + .string_arg("--repository") + .path_arg(repository_dir) + .path_arg(installer_file); } else { - cmd_line = Strings::format(R"("%s" --config "%s" --packages "%s" "%s")", - fs::u8string(binarycreator_exe), - fs::u8string(config_file), - fs::u8string(packages_dir), - fs::u8string(installer_file)); + cmd_line = System::CmdLineBuilder(binarycreator_exe) + .string_arg("--config") + .path_arg(config_file) + .string_arg("--packages") + .path_arg(packages_dir) + .path_arg(installer_file); } const int exit_code = diff --git a/src/vcpkg/export.prefab.cpp b/src/vcpkg/export.prefab.cpp index 3fb4d17306567f..9a625157c54e2d 100644 --- a/src/vcpkg/export.prefab.cpp +++ b/src/vcpkg/export.prefab.cpp @@ -206,10 +206,11 @@ namespace vcpkg::Export::Prefab #if defined(_WIN32) auto&& seven_zip_exe = paths.get_tool_exe(Tools::SEVEN_ZIP); - System::cmd_execute_and_capture_output( - Strings::format( - R"("%s" a "%s" "%s\*")", fs::u8string(seven_zip_exe), fs::u8string(destination), fs::u8string(source)), - System::get_clean_environment()); + System::cmd_execute_and_capture_output(System::CmdLineBuilder(seven_zip_exe) + .string_arg("a") + .path_arg(destination) + .path_arg(source / fs::u8path("*")), + System::get_clean_environment()); #else System::cmd_execute_clean( System::CmdLineBuilder{"zip"}.string_arg("--quiet").string_arg("-r").path_arg(destination).string_arg("*"), @@ -223,11 +224,14 @@ namespace vcpkg::Export::Prefab { System::print2("\n[DEBUG] Installing POM and AAR file to ~/.m2\n\n"); } - const char* cmd_line_format = prefab_options.enable_debug - ? R"("%s" "install:install-file" "-Dfile=%s" "-DpomFile=%s")" - : R"("%s" "-q" "install:install-file" "-Dfile=%s" "-DpomFile=%s")"; - - const auto cmd_line = Strings::format(cmd_line_format, Tools::MAVEN, fs::u8string(aar), fs::u8string(pom)); + auto cmd_line = System::CmdLineBuilder(Tools::MAVEN); + if (!prefab_options.enable_debug) + { + cmd_line.string_arg("-q"); + } + cmd_line.string_arg("install:install-file") + .string_arg(Strings::concat("-Dfile=", fs::u8string(aar))) + .string_arg(Strings::concat("-DpomFile=", fs::u8string(pom))); const int exit_code = System::cmd_execute_clean(cmd_line); Checks::check_exit( VCPKG_LINE_INFO, exit_code == 0, "Error: %s installing maven file", fs::generic_u8string(aar)); diff --git a/src/vcpkg/metrics.cpp b/src/vcpkg/metrics.cpp index 9e9f43c1582f3e..587e48d0b12ba2 100644 --- a/src/vcpkg/metrics.cpp +++ b/src/vcpkg/metrics.cpp @@ -257,7 +257,7 @@ namespace vcpkg::Metrics return "{}"; } - auto getmac = System::cmd_execute_and_capture_output("getmac"); + auto getmac = System::cmd_execute_and_capture_output(System::CmdLineBuilder("getmac")); if (getmac.exit_code != 0) return "0"; @@ -480,11 +480,21 @@ namespace vcpkg::Metrics builder.path_arg(vcpkg_metrics_txt_path); System::cmd_execute_background(builder); #else - auto escaped_path = Strings::escape_string(fs::u8string(vcpkg_metrics_txt_path), '\'', '\\'); - const std::string cmd_line = Strings::format( - R"((curl "https://dc.services.visualstudio.com/v2/track" -H "Content-Type: application/json" -X POST --tlsv1.2 --data '@%s' >/dev/null 2>&1; rm '%s') &)", - escaped_path, - escaped_path); + // TODO: convert to cmd_execute_background or something. + auto curl = System::CmdLineBuilder("curl") + .string_arg("https://dc.services.visualstudio.com/v2/track") + .string_arg("-H") + .string_arg("Content-Type: application/json") + .string_arg("-X") + .string_arg("POST") + .string_arg("--tlsv1.2") + .string_arg("--data") + .string_arg(Strings::concat("@", fs::u8string(vcpkg_metrics_txt_path))) + .raw_arg(">/dev/null") + .raw_arg("2>&1"); + auto remove = System::CmdLineBuilder("rm").path_arg(vcpkg_metrics_txt_path); + System::CmdLineBuilder cmd_line; + cmd_line.raw_arg("(").raw_arg(curl.command_line()).raw_arg(";").raw_arg(remove.command_line()).raw_arg(") &"); System::cmd_execute_clean(cmd_line); #endif } diff --git a/src/vcpkg/postbuildlint.cpp b/src/vcpkg/postbuildlint.cpp index 0ea5786f6a9776..5248a025a0cf1b 100644 --- a/src/vcpkg/postbuildlint.cpp +++ b/src/vcpkg/postbuildlint.cpp @@ -390,10 +390,10 @@ namespace vcpkg::PostBuildLint std::vector dlls_with_no_exports; for (const fs::path& dll : dlls) { - const std::string cmd_line = - Strings::format(R"("%s" /exports "%s")", fs::u8string(dumpbin_exe), fs::u8string(dll)); + auto cmd_line = System::CmdLineBuilder(dumpbin_exe).string_arg("/exports").path_arg(dll); System::ExitCodeAndOutput ec_data = System::cmd_execute_and_capture_output(cmd_line); - Checks::check_exit(VCPKG_LINE_INFO, ec_data.exit_code == 0, "Running command:\n %s\n failed", cmd_line); + Checks::check_exit( + VCPKG_LINE_INFO, ec_data.exit_code == 0, "Running command:\n %s\n failed", cmd_line.command_line()); if (ec_data.output.find("ordinal hint RVA name") == std::string::npos) { @@ -428,10 +428,10 @@ namespace vcpkg::PostBuildLint std::vector dlls_with_improper_uwp_bit; for (const fs::path& dll : dlls) { - const std::string cmd_line = - Strings::format(R"("%s" /headers "%s")", fs::u8string(dumpbin_exe), fs::u8string(dll)); + auto cmd_line = System::CmdLineBuilder(dumpbin_exe).string_arg("/headers").path_arg(dll); System::ExitCodeAndOutput ec_data = System::cmd_execute_and_capture_output(cmd_line); - Checks::check_exit(VCPKG_LINE_INFO, ec_data.exit_code == 0, "Running command:\n %s\n failed", cmd_line); + Checks::check_exit( + VCPKG_LINE_INFO, ec_data.exit_code == 0, "Running command:\n %s\n failed", cmd_line.command_line()); if (ec_data.output.find("App Container") == std::string::npos) { @@ -720,13 +720,12 @@ namespace vcpkg::PostBuildLint for (const fs::path& lib : libs) { - const std::string cmd_line = - Strings::format(R"("%s" /directives "%s")", fs::u8string(dumpbin_exe), fs::u8string(lib)); + auto cmd_line = System::CmdLineBuilder(dumpbin_exe).string_arg("/directives").path_arg(lib); System::ExitCodeAndOutput ec_data = System::cmd_execute_and_capture_output(cmd_line); Checks::check_exit(VCPKG_LINE_INFO, ec_data.exit_code == 0, "Running command:\n %s\n failed with message:\n%s", - cmd_line, + cmd_line.command_line(), ec_data.output); for (const BuildType& bad_build_type : bad_build_types) @@ -775,10 +774,10 @@ namespace vcpkg::PostBuildLint for (const fs::path& dll : dlls) { - const auto cmd_line = - Strings::format(R"("%s" /dependents "%s")", fs::u8string(dumpbin_exe), fs::u8string(dll)); + auto cmd_line = System::CmdLineBuilder(dumpbin_exe).string_arg("/dependents").path_arg(dll); System::ExitCodeAndOutput ec_data = System::cmd_execute_and_capture_output(cmd_line); - Checks::check_exit(VCPKG_LINE_INFO, ec_data.exit_code == 0, "Running command:\n %s\n failed", cmd_line); + Checks::check_exit( + VCPKG_LINE_INFO, ec_data.exit_code == 0, "Running command:\n %s\n failed", cmd_line.command_line()); for (const OutdatedDynamicCrt& outdated_crt : get_outdated_dynamic_crts(pre_build_info.platform_toolset)) { diff --git a/src/vcpkg/tools.cpp b/src/vcpkg/tools.cpp index bdf2bdf6b79811..55476f2683e273 100644 --- a/src/vcpkg/tools.cpp +++ b/src/vcpkg/tools.cpp @@ -297,7 +297,7 @@ namespace vcpkg } virtual ExpectedS get_version(const VcpkgPaths&, const fs::path& path_to_exe) const override { - const std::string cmd = Strings::format(R"("%s" --version)", fs::u8string(path_to_exe)); + auto cmd = System::CmdLineBuilder(path_to_exe).string_arg("--version"); auto rc = System::cmd_execute_and_capture_output(cmd); if (rc.exit_code != 0) { @@ -326,7 +326,7 @@ CMake suite maintained and supported by Kitware (kitware.com/cmake). virtual ExpectedS get_version(const VcpkgPaths&, const fs::path& path_to_exe) const override { - const std::string cmd = Strings::format(R"("%s" --version)", fs::u8string(path_to_exe)); + auto cmd = System::CmdLineBuilder(path_to_exe).string_arg("--version"); auto rc = System::cmd_execute_and_capture_output(cmd); if (rc.exit_code != 0) { @@ -414,7 +414,7 @@ Type 'NuGet help ' for help on a specific command. virtual ExpectedS get_version(const VcpkgPaths&, const fs::path& path_to_exe) const override { - const std::string cmd = Strings::format(R"("%s" --version)", fs::u8string(path_to_exe)); + auto cmd = System::CmdLineBuilder(path_to_exe).string_arg("--version"); auto rc = System::cmd_execute_and_capture_output(cmd); if (rc.exit_code != 0) { @@ -443,8 +443,8 @@ git version 2.17.1.windows.2 virtual ExpectedS get_version(const VcpkgPaths&, const fs::path& path_to_exe) const override { - auto rc = System::cmd_execute_and_capture_output( - System::CmdLineBuilder().path_arg(path_to_exe).string_arg("--version").extract()); + auto rc = + System::cmd_execute_and_capture_output(System::CmdLineBuilder(path_to_exe).string_arg("--version")); if (rc.exit_code != 0) { return {Strings::concat( @@ -485,7 +485,7 @@ Mono JIT compiler version 6.8.0.105 (Debian 6.8.0.105+dfsg-2 Wed Feb 26 23:23:50 virtual ExpectedS get_version(const VcpkgPaths&, const fs::path& path_to_exe) const override { - const std::string cmd = Strings::format(R"("%s" --framework-version)", fs::u8string(path_to_exe)); + auto cmd = System::CmdLineBuilder(path_to_exe).string_arg("--framework-version"); auto rc = System::cmd_execute_and_capture_output(cmd); if (rc.exit_code != 0) { @@ -512,8 +512,8 @@ Mono JIT compiler version 6.8.0.105 (Debian 6.8.0.105+dfsg-2 Wed Feb 26 23:23:50 virtual ExpectedS get_version(const VcpkgPaths&, const fs::path& path_to_exe) const override { - auto rc = System::cmd_execute_and_capture_output( - System::CmdLineBuilder().path_arg(path_to_exe).string_arg("--version").extract()); + auto rc = + System::cmd_execute_and_capture_output(System::CmdLineBuilder(path_to_exe).string_arg("--version")); if (rc.exit_code != 0) { return {Strings::concat( diff --git a/src/vcpkg/vcpkgpaths.cpp b/src/vcpkg/vcpkgpaths.cpp index 407a14fd63982e..1c65cd17001281 100644 --- a/src/vcpkg/vcpkgpaths.cpp +++ b/src/vcpkg/vcpkgpaths.cpp @@ -573,8 +573,7 @@ If you wish to silence this error and use classic mode, you can: .string_arg("-d") .string_arg("HEAD") .string_arg("--") - .path_arg(path_with_separator) - .extract(); + .path_arg(path_with_separator); auto output = System::cmd_execute_and_capture_output(git_cmd); if (output.exit_code != 0) @@ -589,19 +588,22 @@ If you wish to silence this error and use classic mode, you can: // SP SP TAB auto split_line = Strings::split(line, '\t'); if (split_line.size() != 2) - return Strings::format( - "Error: Unexpected output from command `%s`. Couldn't split by `\\t`.\n%s", git_cmd, line); + return Strings::format("Error: Unexpected output from command `%s`. Couldn't split by `\\t`.\n%s", + git_cmd.command_line(), + line); auto file_info_section = Strings::split(split_line[0], ' '); if (file_info_section.size() != 3) - return Strings::format( - "Error: Unexepcted output from command `%s`. Couldn't split by ` `.\n%s", git_cmd, line); + return Strings::format("Error: Unexepcted output from command `%s`. Couldn't split by ` `.\n%s", + git_cmd.command_line(), + line); const auto index = split_line[1].find_last_of('/'); if (index == std::string::npos) { - return Strings::format( - "Error: Unexpected output from command `%s`. Couldn't split by `/`.\n%s", git_cmd, line); + return Strings::format("Error: Unexpected output from command `%s`. Couldn't split by `/`.\n%s", + git_cmd.command_line(), + line); } ret.emplace(split_line[1].substr(index + 1), file_info_section.back()); diff --git a/src/vcpkg/visualstudio.cpp b/src/vcpkg/visualstudio.cpp index 7c9331da5bf99b..f55f8e6bc0bdf9 100644 --- a/src/vcpkg/visualstudio.cpp +++ b/src/vcpkg/visualstudio.cpp @@ -84,8 +84,14 @@ namespace vcpkg::VisualStudio const fs::path vswhere_exe = program_files_32_bit / "Microsoft Visual Studio" / "Installer" / "vswhere.exe"; if (fs.exists(vswhere_exe)) { - const auto code_and_output = System::cmd_execute_and_capture_output( - Strings::format(R"("%s" -all -prerelease -legacy -products * -format xml)", fs::u8string(vswhere_exe))); + const auto code_and_output = System::cmd_execute_and_capture_output(System::CmdLineBuilder(vswhere_exe) + .string_arg("-all") + .string_arg("-prerelease") + .string_arg("-legacy") + .string_arg("-products") + .string_arg("*") + .string_arg("-format") + .string_arg("xml")); Checks::check_exit(VCPKG_LINE_INFO, code_and_output.exit_code == 0, "Running vswhere.exe failed with message:\n%s",