| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395 |
- /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
- file Copyright.txt or https://cmake.org/licensing for details. */
- #include "cmUVProcessChain.h"
- #include "cmAlgorithms.h"
- #include "cmGetPipes.h"
- #include "cmUVHandlePtr.h"
- #include "cmUVStreambuf.h"
- #include "cm_uv.h"
- #include <assert.h>
- #include <iterator>
- #include <memory>
- #include <utility>
- struct cmUVProcessChain::InternalData
- {
- struct BasicStreamData
- {
- cmUVStreambuf Streambuf;
- cm::uv_pipe_ptr BuiltinStream;
- uv_stdio_container_t Stdio;
- };
- template <typename IOStream>
- struct StreamData : public BasicStreamData
- {
- StreamData()
- : BuiltinIOStream(&this->Streambuf)
- {
- }
- IOStream BuiltinIOStream;
- IOStream* GetBuiltinStream()
- {
- if (this->BuiltinStream.get()) {
- return &this->BuiltinIOStream;
- }
- return nullptr;
- }
- };
- struct ProcessData
- {
- cmUVProcessChain::InternalData* Data;
- cm::uv_process_ptr Process;
- cm::uv_pipe_ptr OutputPipe;
- bool Finished = false;
- Status ProcessStatus;
- };
- const cmUVProcessChainBuilder* Builder = nullptr;
- bool Valid = false;
- cm::uv_loop_ptr Loop;
- StreamData<std::istream> OutputStreamData;
- StreamData<std::istream> ErrorStreamData;
- unsigned int ProcessesCompleted = 0;
- std::vector<std::unique_ptr<ProcessData>> Processes;
- bool Prepare(const cmUVProcessChainBuilder* builder);
- bool AddCommand(const cmUVProcessChainBuilder::ProcessConfiguration& config,
- bool first, bool last);
- bool Finish();
- static const Status* GetStatus(const ProcessData& data);
- };
- cmUVProcessChainBuilder::cmUVProcessChainBuilder()
- {
- this->SetNoStream(Stream_INPUT)
- .SetNoStream(Stream_OUTPUT)
- .SetNoStream(Stream_ERROR);
- }
- cmUVProcessChainBuilder& cmUVProcessChainBuilder::AddCommand(
- const std::vector<std::string>& arguments)
- {
- if (!arguments.empty()) {
- this->Processes.emplace_back();
- this->Processes.back().Arguments = arguments;
- }
- return *this;
- }
- cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetNoStream(Stream stdio)
- {
- switch (stdio) {
- case Stream_INPUT:
- case Stream_OUTPUT:
- case Stream_ERROR: {
- auto& streamData = this->Stdio[stdio];
- streamData.Type = None;
- break;
- }
- }
- return *this;
- }
- cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetBuiltinStream(
- Stream stdio)
- {
- switch (stdio) {
- case Stream_INPUT:
- // FIXME
- break;
- case Stream_OUTPUT:
- case Stream_ERROR: {
- auto& streamData = this->Stdio[stdio];
- streamData.Type = Builtin;
- break;
- }
- }
- return *this;
- }
- cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetExternalStream(
- Stream stdio, int fd)
- {
- switch (stdio) {
- case Stream_INPUT:
- // FIXME
- break;
- case Stream_OUTPUT:
- case Stream_ERROR: {
- auto& streamData = this->Stdio[stdio];
- streamData.Type = External;
- streamData.FileDescriptor = fd;
- break;
- }
- }
- return *this;
- }
- cmUVProcessChain cmUVProcessChainBuilder::Start() const
- {
- cmUVProcessChain chain;
- if (!chain.Data->Prepare(this)) {
- return chain;
- }
- for (auto it = this->Processes.begin(); it != this->Processes.end(); ++it) {
- if (!chain.Data->AddCommand(*it, it == this->Processes.begin(),
- it == std::prev(this->Processes.end()))) {
- return chain;
- }
- }
- chain.Data->Finish();
- return chain;
- }
- const cmUVProcessChain::Status* cmUVProcessChain::InternalData::GetStatus(
- const cmUVProcessChain::InternalData::ProcessData& data)
- {
- if (data.Finished) {
- return &data.ProcessStatus;
- }
- return nullptr;
- }
- bool cmUVProcessChain::InternalData::Prepare(
- const cmUVProcessChainBuilder* builder)
- {
- this->Builder = builder;
- auto const& output =
- this->Builder->Stdio[cmUVProcessChainBuilder::Stream_OUTPUT];
- auto& outputData = this->OutputStreamData;
- switch (output.Type) {
- case cmUVProcessChainBuilder::None:
- outputData.Stdio.flags = UV_IGNORE;
- break;
- case cmUVProcessChainBuilder::Builtin:
- outputData.BuiltinStream.init(*this->Loop, 0);
- outputData.Stdio.flags =
- static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
- outputData.Stdio.data.stream = outputData.BuiltinStream;
- break;
- case cmUVProcessChainBuilder::External:
- outputData.Stdio.flags = UV_INHERIT_FD;
- outputData.Stdio.data.fd = output.FileDescriptor;
- break;
- }
- auto const& error =
- this->Builder->Stdio[cmUVProcessChainBuilder::Stream_ERROR];
- auto& errorData = this->ErrorStreamData;
- switch (error.Type) {
- case cmUVProcessChainBuilder::None:
- errorData.Stdio.flags = UV_IGNORE;
- break;
- case cmUVProcessChainBuilder::Builtin: {
- int pipeFd[2];
- if (cmGetPipes(pipeFd) < 0) {
- return false;
- }
- errorData.BuiltinStream.init(*this->Loop, 0);
- if (uv_pipe_open(errorData.BuiltinStream, pipeFd[0]) < 0) {
- return false;
- }
- errorData.Stdio.flags = UV_INHERIT_FD;
- errorData.Stdio.data.fd = pipeFd[1];
- break;
- }
- case cmUVProcessChainBuilder::External:
- errorData.Stdio.flags = UV_INHERIT_FD;
- errorData.Stdio.data.fd = error.FileDescriptor;
- break;
- }
- return true;
- }
- bool cmUVProcessChain::InternalData::AddCommand(
- const cmUVProcessChainBuilder::ProcessConfiguration& config, bool first,
- bool last)
- {
- this->Processes.emplace_back(cm::make_unique<ProcessData>());
- auto& process = *this->Processes.back();
- process.Data = this;
- auto options = uv_process_options_t();
- // Bounds were checked at add time, first element is guaranteed to exist
- options.file = config.Arguments[0].c_str();
- std::vector<const char*> arguments;
- for (auto const& arg : config.Arguments) {
- arguments.push_back(arg.c_str());
- }
- arguments.push_back(nullptr);
- options.args = const_cast<char**>(arguments.data());
- options.flags = UV_PROCESS_WINDOWS_HIDE;
- std::array<uv_stdio_container_t, 3> stdio;
- stdio[0] = uv_stdio_container_t();
- if (first) {
- stdio[0].flags = UV_IGNORE;
- } else {
- assert(this->Processes.size() >= 2);
- auto& prev = *this->Processes[this->Processes.size() - 2];
- stdio[0].flags = UV_INHERIT_STREAM;
- stdio[0].data.stream = prev.OutputPipe;
- }
- if (last) {
- stdio[1] = this->OutputStreamData.Stdio;
- } else {
- if (process.OutputPipe.init(*this->Loop, 0) < 0) {
- return false;
- }
- stdio[1] = uv_stdio_container_t();
- stdio[1].flags =
- static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
- stdio[1].data.stream = process.OutputPipe;
- }
- stdio[2] = this->ErrorStreamData.Stdio;
- options.stdio = stdio.data();
- options.stdio_count = 3;
- options.exit_cb = [](uv_process_t* handle, int64_t exitStatus,
- int termSignal) {
- auto* processData = static_cast<ProcessData*>(handle->data);
- processData->Finished = true;
- processData->ProcessStatus.ExitStatus = exitStatus;
- processData->ProcessStatus.TermSignal = termSignal;
- processData->Data->ProcessesCompleted++;
- };
- return process.Process.spawn(*this->Loop, options, &process) >= 0;
- }
- bool cmUVProcessChain::InternalData::Finish()
- {
- if (this->Builder->Stdio[cmUVProcessChainBuilder::Stream_OUTPUT].Type ==
- cmUVProcessChainBuilder::Builtin) {
- this->OutputStreamData.Streambuf.open(
- this->OutputStreamData.BuiltinStream);
- }
- if (this->Builder->Stdio[cmUVProcessChainBuilder::Stream_ERROR].Type ==
- cmUVProcessChainBuilder::Builtin) {
- cm::uv_pipe_ptr tmpPipe;
- if (tmpPipe.init(*this->Loop, 0) < 0) {
- return false;
- }
- if (uv_pipe_open(tmpPipe, this->ErrorStreamData.Stdio.data.fd) < 0) {
- return false;
- }
- tmpPipe.reset();
- this->ErrorStreamData.Streambuf.open(this->ErrorStreamData.BuiltinStream);
- }
- this->Valid = true;
- return true;
- }
- cmUVProcessChain::cmUVProcessChain()
- : Data(cm::make_unique<InternalData>())
- {
- this->Data->Loop.init();
- }
- cmUVProcessChain::cmUVProcessChain(cmUVProcessChain&& other) noexcept
- : Data(std::move(other.Data))
- {
- }
- cmUVProcessChain::~cmUVProcessChain() = default;
- cmUVProcessChain& cmUVProcessChain::operator=(
- cmUVProcessChain&& other) noexcept
- {
- this->Data = std::move(other.Data);
- return *this;
- }
- uv_loop_t& cmUVProcessChain::GetLoop()
- {
- return *this->Data->Loop;
- }
- std::istream* cmUVProcessChain::OutputStream()
- {
- return this->Data->OutputStreamData.GetBuiltinStream();
- }
- std::istream* cmUVProcessChain::ErrorStream()
- {
- return this->Data->ErrorStreamData.GetBuiltinStream();
- }
- bool cmUVProcessChain::Valid() const
- {
- return this->Data->Valid;
- }
- bool cmUVProcessChain::Wait(int64_t milliseconds)
- {
- bool timeout = false;
- cm::uv_timer_ptr timer;
- if (milliseconds >= 0) {
- timer.init(*this->Data->Loop, &timeout);
- timer.start(
- [](uv_timer_t* handle) {
- auto* timeoutPtr = static_cast<bool*>(handle->data);
- *timeoutPtr = true;
- },
- milliseconds, 0);
- }
- while (!timeout &&
- this->Data->ProcessesCompleted < this->Data->Processes.size()) {
- uv_run(this->Data->Loop, UV_RUN_ONCE);
- }
- return !timeout;
- }
- std::vector<const cmUVProcessChain::Status*> cmUVProcessChain::GetStatus()
- const
- {
- std::vector<const cmUVProcessChain::Status*> statuses(
- this->Data->Processes.size(), nullptr);
- for (std::size_t i = 0; i < statuses.size(); i++) {
- statuses[i] = this->GetStatus(i);
- }
- return statuses;
- }
- const cmUVProcessChain::Status* cmUVProcessChain::GetStatus(
- std::size_t index) const
- {
- auto const& process = *this->Data->Processes[index];
- if (process.Finished) {
- return &process.ProcessStatus;
- }
- return nullptr;
- }
|