| 69 | } |
| 70 | |
| 71 | kota::task<std::expected<std::string, std::string>> |
| 72 | execute_async(std::vector<std::string> arguments, bool capture_stdout = false) { |
| 73 | kota::process::options opts; |
| 74 | opts.file = arguments[0]; |
| 75 | opts.args = std::move(arguments); |
| 76 | #ifndef _WIN32 |
| 77 | opts.env = process_env(); |
| 78 | #endif |
| 79 | opts.streams = { |
| 80 | kota::process::stdio::ignore(), |
| 81 | kota::process::stdio::pipe(false, true), |
| 82 | kota::process::stdio::pipe(false, true), |
| 83 | }; |
| 84 | |
| 85 | LOG_INFO("Execute command: {}", opts.file); |
| 86 | |
| 87 | auto spawn = kota::process::spawn(opts); |
| 88 | if(!spawn.has_value()) { |
| 89 | co_return std::unexpected( |
| 90 | std::format("Failed to spawn {}: {}", opts.file, spawn.error().message())); |
| 91 | } |
| 92 | auto& s = *spawn; |
| 93 | |
| 94 | // Drain both pipes concurrently with process exit: a child blocking on a |
| 95 | // full pipe would otherwise deadlock against our wait(). |
| 96 | auto [stdout_data, stderr_data] = co_await kota::when_all(drain_pipe(std::move(s.stdout_pipe)), |
| 97 | drain_pipe(std::move(s.stderr_pipe))); |
| 98 | |
| 99 | auto exit_result = co_await s.proc.wait(); |
| 100 | if(!exit_result.has_value()) { |
| 101 | co_return std::unexpected( |
| 102 | std::format("Process wait failed: {}", exit_result.error().message())); |
| 103 | } |
| 104 | |
| 105 | auto& exit = *exit_result; |
| 106 | if(exit.status != 0) { |
| 107 | co_return std::unexpected( |
| 108 | std::format("Process {} exited with code {}", opts.file, exit.status)); |
| 109 | } |
| 110 | |
| 111 | co_return capture_stdout ? std::move(stdout_data) : std::move(stderr_data); |
| 112 | } |
| 113 | |
| 114 | std::expected<void, std::string> query_driver( |
| 115 | llvm::ArrayRef<const char*> arguments, |
no test coverage detected