diff options
-rw-r--r-- | Source/CTest/cmProcess.cxx | 288 | ||||
-rw-r--r-- | Source/CTest/cmProcess.h | 73 |
2 files changed, 361 insertions, 0 deletions
diff --git a/Source/CTest/cmProcess.cxx b/Source/CTest/cmProcess.cxx new file mode 100644 index 0000000..a109b3f --- /dev/null +++ b/Source/CTest/cmProcess.cxx @@ -0,0 +1,288 @@ +/*========================================================================= + + Program: CMake - Cross-Platform Makefile Generator + Module: $RCSfile$ + Language: C++ + Date: $Date$ + Version: $Revision$ + + Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved. + See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ + +#include <cmProcess.h> +#include <iostream> + +cmProcess::cmProcess() +{ + this->Process = 0; + this->Timeout = 0; +} + +cmProcess::~cmProcess() +{ + cmsysProcess_Delete(this->Process); +} +void cmProcess::SetCommand(const char* command) +{ + this->Command = command; +} + +void cmProcess::SetCommandArguments(std::vector<std::string> const& args) +{ + this->Arguments = args; +} + +bool cmProcess::StartProcess() +{ + if(this->Command.size() == 0) + { + return false; + } + this->ProcessArgs.clear(); + // put the command as arg0 + this->ProcessArgs.push_back(this->Command.c_str()); + // now put the command arguments in + for(std::vector<std::string>::iterator i = this->Arguments.begin(); + i != this->Arguments.end(); ++i) + { + this->ProcessArgs.push_back(i->c_str()); + } + this->ProcessArgs.push_back(0); // null terminate the list + this->Process = cmsysProcess_New(); + cmsysProcess_SetCommand(this->Process, &*this->ProcessArgs.begin()); + if(this->WorkingDirectory.size()) + { + cmsysProcess_SetWorkingDirectory(this->Process, + this->WorkingDirectory.c_str()); + } + cmsysProcess_SetOption(this->Process, cmsysProcess_Option_HideWindow, 1); + cmsysProcess_SetTimeout(this->Process, this->Timeout); + cmsysProcess_Execute(this->Process); + return (cmsysProcess_GetState(this->Process) + == cmsysProcess_State_Executing); +} + +// return true if there is a new line of data +// return false if there is no new data +int cmProcess::CheckOutput(double timeout, + std::string& stdOutLine, + std::string& stdErrLine) +{ + stdOutLine = ""; + stdErrLine = ""; + std::vector<char>::iterator outiter = + this->StdOutBuffer.begin(); + std::vector<char>::iterator erriter = + this->StdErrorBuffer.begin(); + while(1) + { + // Check for a newline in stdout. + for(;outiter != this->StdOutBuffer.end(); ++outiter) + { + if((*outiter == '\r') && ((outiter+1) == this->StdOutBuffer.end())) + { + break; + } + else if(*outiter == '\n' || *outiter == '\0') + { + int length = outiter-this->StdOutBuffer.begin(); + if(length > 1 && *(outiter-1) == '\r') + { + --length; + } + if(length > 0) + { + stdOutLine.append(&this->StdOutBuffer[0], length); + } + this->StdOutBuffer.erase(this->StdOutBuffer.begin(), outiter+1); + this->LastOutputPipe = cmsysProcess_Pipe_STDOUT; + return this->LastOutputPipe;; + } + } + + // Check for a newline in stderr. + for(;erriter != this->StdErrorBuffer.end(); ++erriter) + { + if((*erriter == '\r') && ((erriter+1) == this->StdErrorBuffer.end())) + { + break; + } + else if(*erriter == '\n' || *erriter == '\0') + { + int length = erriter-this->StdErrorBuffer.begin(); + if(length > 1 && *(erriter-1) == '\r') + { + --length; + } + if(length > 0) + { + stdErrLine.append(&this->StdErrorBuffer[0], length); + } + this->StdErrorBuffer.erase(this->StdErrorBuffer.begin(), erriter+1); + this->LastOutputPipe = cmsysProcess_Pipe_STDERR; + return this->LastOutputPipe; + } + } + + // No newlines found. Wait for more data from the process. + int length; + char* data; + int pipe = cmsysProcess_WaitForData(this->Process, &data, + &length, &timeout); + if(pipe == cmsysProcess_Pipe_Timeout) + { + // Timeout has been exceeded. + this->LastOutputPipe = pipe; + return pipe; + } + else if(pipe == cmsysProcess_Pipe_STDOUT) + { + // Append to the stdout buffer. + std::vector<char>::size_type size = this->StdOutBuffer.size(); + this->StdOutBuffer.insert(this->StdOutBuffer.end(), data, data+length); + outiter = this->StdOutBuffer.begin()+size; + } + else if(pipe == cmsysProcess_Pipe_STDERR) + { + // Append to the stderr buffer. + std::vector<char>::size_type size = this->StdErrorBuffer.size(); + this->StdErrorBuffer.insert(this->StdErrorBuffer.end(), + data, data+length); + erriter = this->StdErrorBuffer.begin()+size; + } + else if(pipe == cmsysProcess_Pipe_None) + { + // Both stdout and stderr pipes have broken. Return leftover data. + if(!this->StdOutBuffer.empty()) + { + stdOutLine.append(&this->StdOutBuffer[0], + outiter-this->StdOutBuffer.begin()); + this->StdOutBuffer.erase(this->StdOutBuffer.begin(), + this->StdOutBuffer.end()); + this->LastOutputPipe = cmsysProcess_Pipe_STDOUT; + return this->LastOutputPipe; + } + else if(!this->StdErrorBuffer.empty()) + { + stdErrLine.append(&this->StdErrorBuffer[0], + erriter-this->StdErrorBuffer.begin()); + this->StdErrorBuffer.erase(this->StdErrorBuffer.begin(), + this->StdErrorBuffer.end()); + this->LastOutputPipe = cmsysProcess_Pipe_STDERR; + return this->LastOutputPipe; + } + else + { + this->LastOutputPipe = cmsysProcess_Pipe_None; + return this->LastOutputPipe; + } + } + } +} + + +// return the process status +int cmProcess::GetProcessStatus() +{ + if(!this->Process) + { + return cmsysProcess_State_Exited; + } + return cmsysProcess_GetState(this->Process); +} + +// return true if the process is running +bool cmProcess::IsRunning() +{ + int status = this->GetProcessStatus(); + if(status == cmsysProcess_State_Executing ) + { + if(this->LastOutputPipe != 0) + { + return true; + } + } + // if the process is done, then wait for it to exit + cmsysProcess_WaitForExit(this->Process, 0); + this->ExitValue = cmsysProcess_GetExitValue(this->Process); + return false; +} + + +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; + +} diff --git a/Source/CTest/cmProcess.h b/Source/CTest/cmProcess.h new file mode 100644 index 0000000..7898f2d --- /dev/null +++ b/Source/CTest/cmProcess.h @@ -0,0 +1,73 @@ +/*========================================================================= + + Program: CMake - Cross-Platform Makefile Generator + Module: $RCSfile$ + Language: C++ + Date: $Date$ + Version: $Revision$ + + Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved. + See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ +#ifndef cmProcess_h +#define cmProcess_h + +#include <set> +#include <map> +#include <string> +#include <vector> +#include <cmsys/Process.h> + + +/** \class cmProcess + * \brief run a process with c++ + * + * cmProcess wraps the kwsys process stuff in a c++ class. + */ +class cmProcess +{ +public: + cmProcess(); + ~cmProcess(); + const char* GetCommand() { return this->Command.c_str();} + void SetCommand(const char* command); + void SetCommandArguments(std::vector<std::string> const& arg); + void SetWorkingDirectory(const char* dir) { this->WorkingDirectory = dir;} + void SetTimeout(double t) { this->Timeout = t;} + // Return true if the process starts + bool StartProcess(); + + // return process state + int CheckOutput(double timeout, + std::string& stdOutLine, + std::string& stdErrLine); + // return the process status + int GetProcessStatus(); + // return true if the process is running + bool IsRunning(); + // Report the status of the program + int ReportStatus(); + int GetId() { return this->Id; } + void SetId(int id) { this->Id = id;} + int GetExitValue() { return this->ExitValue;} +private: + int LastOutputPipe; + double Timeout; + cmsysProcess* Process; + std::vector<char> StdErrorBuffer; + std::vector<char> StdOutBuffer; + std::string Command; + std::string WorkingDirectory; + std::vector<std::string> Arguments; + std::vector<const char*> ProcessArgs; + std::string Output; + int Id; + int ExitValue; +}; + +#endif |