cmUVProcessChain.cxx 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmUVProcessChain.h"
  4. #include "cmAlgorithms.h"
  5. #include "cmGetPipes.h"
  6. #include "cmUVHandlePtr.h"
  7. #include "cmUVStreambuf.h"
  8. #include "cm_uv.h"
  9. #include <assert.h>
  10. #include <iterator>
  11. #include <memory>
  12. #include <utility>
  13. struct cmUVProcessChain::InternalData
  14. {
  15. struct BasicStreamData
  16. {
  17. cmUVStreambuf Streambuf;
  18. cm::uv_pipe_ptr BuiltinStream;
  19. uv_stdio_container_t Stdio;
  20. };
  21. template <typename IOStream>
  22. struct StreamData : public BasicStreamData
  23. {
  24. StreamData()
  25. : BuiltinIOStream(&this->Streambuf)
  26. {
  27. }
  28. IOStream BuiltinIOStream;
  29. IOStream* GetBuiltinStream()
  30. {
  31. if (this->BuiltinStream.get()) {
  32. return &this->BuiltinIOStream;
  33. }
  34. return nullptr;
  35. }
  36. };
  37. struct ProcessData
  38. {
  39. cmUVProcessChain::InternalData* Data;
  40. cm::uv_process_ptr Process;
  41. cm::uv_pipe_ptr OutputPipe;
  42. bool Finished = false;
  43. Status ProcessStatus;
  44. };
  45. const cmUVProcessChainBuilder* Builder = nullptr;
  46. bool Valid = false;
  47. cm::uv_loop_ptr Loop;
  48. StreamData<std::istream> OutputStreamData;
  49. StreamData<std::istream> ErrorStreamData;
  50. unsigned int ProcessesCompleted = 0;
  51. std::vector<std::unique_ptr<ProcessData>> Processes;
  52. bool Prepare(const cmUVProcessChainBuilder* builder);
  53. bool AddCommand(const cmUVProcessChainBuilder::ProcessConfiguration& config,
  54. bool first, bool last);
  55. bool Finish();
  56. static const Status* GetStatus(const ProcessData& data);
  57. };
  58. cmUVProcessChainBuilder::cmUVProcessChainBuilder()
  59. {
  60. this->SetNoStream(Stream_INPUT)
  61. .SetNoStream(Stream_OUTPUT)
  62. .SetNoStream(Stream_ERROR);
  63. }
  64. cmUVProcessChainBuilder& cmUVProcessChainBuilder::AddCommand(
  65. const std::vector<std::string>& arguments)
  66. {
  67. if (!arguments.empty()) {
  68. this->Processes.emplace_back();
  69. this->Processes.back().Arguments = arguments;
  70. }
  71. return *this;
  72. }
  73. cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetNoStream(Stream stdio)
  74. {
  75. switch (stdio) {
  76. case Stream_INPUT:
  77. case Stream_OUTPUT:
  78. case Stream_ERROR: {
  79. auto& streamData = this->Stdio[stdio];
  80. streamData.Type = None;
  81. break;
  82. }
  83. }
  84. return *this;
  85. }
  86. cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetBuiltinStream(
  87. Stream stdio)
  88. {
  89. switch (stdio) {
  90. case Stream_INPUT:
  91. // FIXME
  92. break;
  93. case Stream_OUTPUT:
  94. case Stream_ERROR: {
  95. auto& streamData = this->Stdio[stdio];
  96. streamData.Type = Builtin;
  97. break;
  98. }
  99. }
  100. return *this;
  101. }
  102. cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetExternalStream(
  103. Stream stdio, int fd)
  104. {
  105. switch (stdio) {
  106. case Stream_INPUT:
  107. // FIXME
  108. break;
  109. case Stream_OUTPUT:
  110. case Stream_ERROR: {
  111. auto& streamData = this->Stdio[stdio];
  112. streamData.Type = External;
  113. streamData.FileDescriptor = fd;
  114. break;
  115. }
  116. }
  117. return *this;
  118. }
  119. cmUVProcessChain cmUVProcessChainBuilder::Start() const
  120. {
  121. cmUVProcessChain chain;
  122. if (!chain.Data->Prepare(this)) {
  123. return chain;
  124. }
  125. for (auto it = this->Processes.begin(); it != this->Processes.end(); ++it) {
  126. if (!chain.Data->AddCommand(*it, it == this->Processes.begin(),
  127. it == std::prev(this->Processes.end()))) {
  128. return chain;
  129. }
  130. }
  131. chain.Data->Finish();
  132. return chain;
  133. }
  134. const cmUVProcessChain::Status* cmUVProcessChain::InternalData::GetStatus(
  135. const cmUVProcessChain::InternalData::ProcessData& data)
  136. {
  137. if (data.Finished) {
  138. return &data.ProcessStatus;
  139. }
  140. return nullptr;
  141. }
  142. bool cmUVProcessChain::InternalData::Prepare(
  143. const cmUVProcessChainBuilder* builder)
  144. {
  145. this->Builder = builder;
  146. auto const& output =
  147. this->Builder->Stdio[cmUVProcessChainBuilder::Stream_OUTPUT];
  148. auto& outputData = this->OutputStreamData;
  149. switch (output.Type) {
  150. case cmUVProcessChainBuilder::None:
  151. outputData.Stdio.flags = UV_IGNORE;
  152. break;
  153. case cmUVProcessChainBuilder::Builtin:
  154. outputData.BuiltinStream.init(*this->Loop, 0);
  155. outputData.Stdio.flags =
  156. static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
  157. outputData.Stdio.data.stream = outputData.BuiltinStream;
  158. break;
  159. case cmUVProcessChainBuilder::External:
  160. outputData.Stdio.flags = UV_INHERIT_FD;
  161. outputData.Stdio.data.fd = output.FileDescriptor;
  162. break;
  163. }
  164. auto const& error =
  165. this->Builder->Stdio[cmUVProcessChainBuilder::Stream_ERROR];
  166. auto& errorData = this->ErrorStreamData;
  167. switch (error.Type) {
  168. case cmUVProcessChainBuilder::None:
  169. errorData.Stdio.flags = UV_IGNORE;
  170. break;
  171. case cmUVProcessChainBuilder::Builtin: {
  172. int pipeFd[2];
  173. if (cmGetPipes(pipeFd) < 0) {
  174. return false;
  175. }
  176. errorData.BuiltinStream.init(*this->Loop, 0);
  177. if (uv_pipe_open(errorData.BuiltinStream, pipeFd[0]) < 0) {
  178. return false;
  179. }
  180. errorData.Stdio.flags = UV_INHERIT_FD;
  181. errorData.Stdio.data.fd = pipeFd[1];
  182. break;
  183. }
  184. case cmUVProcessChainBuilder::External:
  185. errorData.Stdio.flags = UV_INHERIT_FD;
  186. errorData.Stdio.data.fd = error.FileDescriptor;
  187. break;
  188. }
  189. return true;
  190. }
  191. bool cmUVProcessChain::InternalData::AddCommand(
  192. const cmUVProcessChainBuilder::ProcessConfiguration& config, bool first,
  193. bool last)
  194. {
  195. this->Processes.emplace_back(cm::make_unique<ProcessData>());
  196. auto& process = *this->Processes.back();
  197. process.Data = this;
  198. auto options = uv_process_options_t();
  199. // Bounds were checked at add time, first element is guaranteed to exist
  200. options.file = config.Arguments[0].c_str();
  201. std::vector<const char*> arguments;
  202. for (auto const& arg : config.Arguments) {
  203. arguments.push_back(arg.c_str());
  204. }
  205. arguments.push_back(nullptr);
  206. options.args = const_cast<char**>(arguments.data());
  207. options.flags = UV_PROCESS_WINDOWS_HIDE;
  208. std::array<uv_stdio_container_t, 3> stdio;
  209. stdio[0] = uv_stdio_container_t();
  210. if (first) {
  211. stdio[0].flags = UV_IGNORE;
  212. } else {
  213. assert(this->Processes.size() >= 2);
  214. auto& prev = *this->Processes[this->Processes.size() - 2];
  215. stdio[0].flags = UV_INHERIT_STREAM;
  216. stdio[0].data.stream = prev.OutputPipe;
  217. }
  218. if (last) {
  219. stdio[1] = this->OutputStreamData.Stdio;
  220. } else {
  221. if (process.OutputPipe.init(*this->Loop, 0) < 0) {
  222. return false;
  223. }
  224. stdio[1] = uv_stdio_container_t();
  225. stdio[1].flags =
  226. static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
  227. stdio[1].data.stream = process.OutputPipe;
  228. }
  229. stdio[2] = this->ErrorStreamData.Stdio;
  230. options.stdio = stdio.data();
  231. options.stdio_count = 3;
  232. options.exit_cb = [](uv_process_t* handle, int64_t exitStatus,
  233. int termSignal) {
  234. auto* processData = static_cast<ProcessData*>(handle->data);
  235. processData->Finished = true;
  236. processData->ProcessStatus.ExitStatus = exitStatus;
  237. processData->ProcessStatus.TermSignal = termSignal;
  238. processData->Data->ProcessesCompleted++;
  239. };
  240. return process.Process.spawn(*this->Loop, options, &process) >= 0;
  241. }
  242. bool cmUVProcessChain::InternalData::Finish()
  243. {
  244. if (this->Builder->Stdio[cmUVProcessChainBuilder::Stream_OUTPUT].Type ==
  245. cmUVProcessChainBuilder::Builtin) {
  246. this->OutputStreamData.Streambuf.open(
  247. this->OutputStreamData.BuiltinStream);
  248. }
  249. if (this->Builder->Stdio[cmUVProcessChainBuilder::Stream_ERROR].Type ==
  250. cmUVProcessChainBuilder::Builtin) {
  251. cm::uv_pipe_ptr tmpPipe;
  252. if (tmpPipe.init(*this->Loop, 0) < 0) {
  253. return false;
  254. }
  255. if (uv_pipe_open(tmpPipe, this->ErrorStreamData.Stdio.data.fd) < 0) {
  256. return false;
  257. }
  258. tmpPipe.reset();
  259. this->ErrorStreamData.Streambuf.open(this->ErrorStreamData.BuiltinStream);
  260. }
  261. this->Valid = true;
  262. return true;
  263. }
  264. cmUVProcessChain::cmUVProcessChain()
  265. : Data(cm::make_unique<InternalData>())
  266. {
  267. this->Data->Loop.init();
  268. }
  269. cmUVProcessChain::cmUVProcessChain(cmUVProcessChain&& other) noexcept
  270. : Data(std::move(other.Data))
  271. {
  272. }
  273. cmUVProcessChain::~cmUVProcessChain() = default;
  274. cmUVProcessChain& cmUVProcessChain::operator=(
  275. cmUVProcessChain&& other) noexcept
  276. {
  277. this->Data = std::move(other.Data);
  278. return *this;
  279. }
  280. uv_loop_t& cmUVProcessChain::GetLoop()
  281. {
  282. return *this->Data->Loop;
  283. }
  284. std::istream* cmUVProcessChain::OutputStream()
  285. {
  286. return this->Data->OutputStreamData.GetBuiltinStream();
  287. }
  288. std::istream* cmUVProcessChain::ErrorStream()
  289. {
  290. return this->Data->ErrorStreamData.GetBuiltinStream();
  291. }
  292. bool cmUVProcessChain::Valid() const
  293. {
  294. return this->Data->Valid;
  295. }
  296. bool cmUVProcessChain::Wait(int64_t milliseconds)
  297. {
  298. bool timeout = false;
  299. cm::uv_timer_ptr timer;
  300. if (milliseconds >= 0) {
  301. timer.init(*this->Data->Loop, &timeout);
  302. timer.start(
  303. [](uv_timer_t* handle) {
  304. auto* timeoutPtr = static_cast<bool*>(handle->data);
  305. *timeoutPtr = true;
  306. },
  307. milliseconds, 0);
  308. }
  309. while (!timeout &&
  310. this->Data->ProcessesCompleted < this->Data->Processes.size()) {
  311. uv_run(this->Data->Loop, UV_RUN_ONCE);
  312. }
  313. return !timeout;
  314. }
  315. std::vector<const cmUVProcessChain::Status*> cmUVProcessChain::GetStatus()
  316. const
  317. {
  318. std::vector<const cmUVProcessChain::Status*> statuses(
  319. this->Data->Processes.size(), nullptr);
  320. for (std::size_t i = 0; i < statuses.size(); i++) {
  321. statuses[i] = this->GetStatus(i);
  322. }
  323. return statuses;
  324. }
  325. const cmUVProcessChain::Status* cmUVProcessChain::GetStatus(
  326. std::size_t index) const
  327. {
  328. auto const& process = *this->Data->Processes[index];
  329. if (process.Finished) {
  330. return &process.ProcessStatus;
  331. }
  332. return nullptr;
  333. }