/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmProcess.h" #include "cmProcessOutput.h" #include "cmSystemTools.h" #include cmProcess::cmProcess() { this->Process = nullptr; this->Timeout = 0; this->TotalTime = 0; this->ExitValue = 0; this->Id = 0; this->StartTime = 0; } cmProcess::~cmProcess() { cmsysProcess_Delete(this->Process); } void cmProcess::SetCommand(const char* command) { this->Command = command; } void cmProcess::SetCommandArguments(std::vector const& args) { this->Arguments = args; } bool cmProcess::StartProcess() { if (this->Command.empty()) { return false; } this->StartTime = cmSystemTools::GetTime(); this->ProcessArgs.clear(); // put the command as arg0 this->ProcessArgs.push_back(this->Command.c_str()); // now put the command arguments in for (std::string const& arg : this->Arguments) { this->ProcessArgs.push_back(arg.c_str()); } this->ProcessArgs.push_back(nullptr); // null terminate the list this->Process = cmsysProcess_New(); cmsysProcess_SetCommand(this->Process, &*this->ProcessArgs.begin()); if (!this->WorkingDirectory.empty()) { cmsysProcess_SetWorkingDirectory(this->Process, this->WorkingDirectory.c_str()); } cmsysProcess_SetTimeout(this->Process, this->Timeout); cmsysProcess_SetOption(this->Process, cmsysProcess_Option_MergeOutput, 1); cmsysProcess_Execute(this->Process); return (cmsysProcess_GetState(this->Process) == cmsysProcess_State_Executing); } bool cmProcess::Buffer::GetLine(std::string& line) { // Scan for the next newline. for (size_type sz = this->size(); this->Last != sz; ++this->Last) { if ((*this)[this->Last] == '\n' || (*this)[this->Last] == '\0') { // Extract the range first..last as a line. const char* text = &*this->begin() + this->First; size_type length = this->Last - this->First; while (length && text[length - 1] == '\r') { length--; } line.assign(text, length); // Start a new range for the next line. ++this->Last; this->First = Last; // Return the line extracted. return true; } } // Available data have been exhausted without a newline. if (this->First != 0) { // Move the partial line to the beginning of the buffer. this->erase(this->begin(), this->begin() + this->First); this->First = 0; this->Last = this->size(); } return false; } bool cmProcess::Buffer::GetLast(std::string& line) { // Return the partial last line, if any. if (!this->empty()) { line.assign(&*this->begin(), this->size()); this->First = this->Last = 0; this->clear(); return true; } return false; } int cmProcess::GetNextOutputLine(std::string& line, double timeout) { cmProcessOutput processOutput(cmProcessOutput::UTF8); std::string strdata; for (;;) { // Look for lines already buffered. if (this->Output.GetLine(line)) { return cmsysProcess_Pipe_STDOUT; } // Check for more data from the process. char* data; int length; int p = cmsysProcess_WaitForData(this->Process, &data, &length, &timeout); if (p == cmsysProcess_Pipe_Timeout) { return cmsysProcess_Pipe_Timeout; } if (p == cmsysProcess_Pipe_STDOUT) { processOutput.DecodeText(data, length, strdata); this->Output.insert(this->Output.end(), strdata.begin(), strdata.end()); } else { // p == cmsysProcess_Pipe_None // The process will provide no more data. break; } } processOutput.DecodeText(std::string(), strdata); if (!strdata.empty()) { this->Output.insert(this->Output.end(), strdata.begin(), strdata.end()); } // Look for partial last lines. if (this->Output.GetLast(line)) { return cmsysProcess_Pipe_STDOUT; } // No more data. Wait for process exit. if (!cmsysProcess_WaitForExit(this->Process, &timeout)) { return cmsysProcess_Pipe_Timeout; } // Record exit information. this->ExitValue = cmsysProcess_GetExitValue(this->Process); this->TotalTime = cmSystemTools::GetTime() - this->StartTime; // Because of a processor clock scew the runtime may become slightly // negative. If someone changed the system clock while the process was // running this may be even more. Make sure not to report a negative // duration here. if (this->TotalTime <= 0.0) { this->TotalTime = 0.0; } // std::cerr << "Time to run: " << this->TotalTime << "\n"; return cmsysProcess_Pipe_None; } // return the process status int cmProcess::GetProcessStatus() { if (!this->Process) { return cmsysProcess_State_Exited; } return cmsysProcess_GetState(this->Process); } int cmProcess::ReportStatus() { int result = 1; switch (cmsysProcess_GetState(this->Process)) { case cmsysProcess_State_Starting: { std::cerr << "cmProcess: Never started " << this->Command << " process.\n"; } break; case cmsysProcess_State_Error: { std::cerr << "cmProcess: Error executing " << this->Command << " process: " << cmsysProcess_GetErrorString(this->Process) << "\n"; } break; case cmsysProcess_State_Exception: { std::cerr << "cmProcess: " << this->Command << " process exited with an exception: "; switch (cmsysProcess_GetExitException(this->Process)) { case cmsysProcess_Exception_None: { std::cerr << "None"; } break; case cmsysProcess_Exception_Fault: { std::cerr << "Segmentation fault"; } break; case cmsysProcess_Exception_Illegal: { std::cerr << "Illegal instruction"; } break; case cmsysProcess_Exception_Interrupt: { std::cerr << "Interrupted by user"; } break; case cmsysProcess_Exception_Numerical: { std::cerr << "Numerical exception"; } break; case cmsysProcess_Exception_Other: { std::cerr << "Unknown"; } break; } std::cerr << "\n"; } break; case cmsysProcess_State_Executing: { std::cerr << "cmProcess: Never terminated " << this->Command << " process.\n"; } break; case cmsysProcess_State_Exited: { result = cmsysProcess_GetExitValue(this->Process); std::cerr << "cmProcess: " << this->Command << " process exited with code " << result << "\n"; } break; case cmsysProcess_State_Expired: { std::cerr << "cmProcess: killed " << this->Command << " process due to timeout.\n"; } break; case cmsysProcess_State_Killed: { std::cerr << "cmProcess: killed " << this->Command << " process.\n"; } break; } return result; } void cmProcess::ChangeTimeout(double t) { this->Timeout = t; cmsysProcess_SetTimeout(this->Process, this->Timeout); } void cmProcess::ResetStartTime() { cmsysProcess_ResetStartTime(this->Process); this->StartTime = cmSystemTools::GetTime(); } int cmProcess::GetExitException() { return cmsysProcess_GetExitException(this->Process); } std::string cmProcess::GetExitExceptionString() { return cmsysProcess_GetExceptionString(this->Process); }