CMake/Source/CTest/cmProcess.cxx

292 lines
8.6 KiB
C++

/*=========================================================================
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>
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;
}