ENH: added support for -SP scripts in new processes

This commit is contained in:
Ken Martin 2006-04-04 13:04:28 -04:00
parent b45f4a5d45
commit d81ebf0c23
11 changed files with 269 additions and 131 deletions

View File

@ -27,11 +27,17 @@ bool cmCTestRunScriptCommand::InitialPass(
return true;
}
// run each script
unsigned int i;
for (i = 0; i < args.size(); ++i)
bool np = false;
unsigned int i = 0;
if (args[i] == "NEW_PROCESS")
{
cmCTestScriptHandler::RunScript(this->CTest, args[i].c_str());
np = true;
i++;
}
// run each script
for (; i < args.size(); ++i)
{
cmCTestScriptHandler::RunScript(this->CTest, args[i].c_str(), !np);
}
return true;
}

View File

@ -67,11 +67,12 @@ public:
virtual const char* GetFullDocumentation()
{
return
" CTEST_RUN_SCRIPT(script_file_name script_file_name1 \n"
" CTEST_RUN_SCRIPT([NEW_PROCESS] script_file_name script_file_name1 \n"
" script_file_name2 ...)\n"
"Runs a script or scripts much like if it was run from ctest -S. "
"If no argument is provided then the current script is run using "
"the current settings of the variables.";
"the current settings of the variables. If NEW_PROCESS is specified "
"then each script will be run in a seperate process.";
}
cmTypeMacro(cmCTestRunScriptCommand, cmCTestCommand);

View File

@ -167,9 +167,10 @@ cmCTestScriptHandler::~cmCTestScriptHandler()
//----------------------------------------------------------------------
// just adds an argument to the vector
void cmCTestScriptHandler::AddConfigurationScript(const char *script)
void cmCTestScriptHandler::AddConfigurationScript(const char *script, bool pscope)
{
this->ConfigurationScripts.push_back(script);
this->ScriptProcessScope.push_back(pscope);
}
@ -179,14 +180,12 @@ void cmCTestScriptHandler::AddConfigurationScript(const char *script)
int cmCTestScriptHandler::ProcessHandler()
{
int res = 0;
std::vector<cmStdString>::iterator it;
for ( it = this->ConfigurationScripts.begin();
it != this->ConfigurationScripts.end();
it ++ )
for (size_t i=0; i < this->ConfigurationScripts.size(); ++i)
{
// for each script run it
res += this->RunConfigurationScript(
cmSystemTools::CollapseFullPath(it->c_str()));
res += this->RunConfigurationScript
(cmSystemTools::CollapseFullPath(this->ConfigurationScripts[i].c_str()),
this->ScriptProcessScope[i]);
}
if ( res )
{
@ -218,8 +217,73 @@ void cmCTestScriptHandler::AddCTestCommand(cmCTestCommand* command)
this->CMake->AddCommand(newCom);
}
int cmCTestScriptHandler::ExecuteScript(const std::string& total_script_arg)
{
// execute the script passing in the arguments to the script as well as the
// arguments from this invocation of cmake
std::vector<const char*> argv;
argv.push_back(this->CTest->GetCTestExecutable());
argv.push_back("-SR");
argv.push_back(total_script_arg.c_str());
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Executable for CTest is: " <<
this->CTest->GetCTestExecutable() << "\n");
// now pass through all the other arguments
std::vector<cmStdString> &initArgs =
this->CTest->GetInitialCommandLineArguments();
for(size_t i=1; i < initArgs.size(); ++i)
{
argv.push_back(initArgs[i].c_str());
}
argv.push_back(0);
// Now create process object
cmsysProcess* cp = cmsysProcess_New();
cmsysProcess_SetCommand(cp, &*argv.begin());
//cmsysProcess_SetWorkingDirectory(cp, dir);
cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
//cmsysProcess_SetTimeout(cp, timeout);
cmsysProcess_Execute(cp);
std::vector<char> out;
std::vector<char> err;
std::string line;
int pipe = cmSystemTools::WaitForLine(cp, line, 100.0, out, err);
while(pipe != cmsysProcess_Pipe_None)
{
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Output: " << line << "\n");
if(pipe == cmsysProcess_Pipe_STDERR)
{
cmCTestLog(this->CTest, ERROR_MESSAGE, line << "\n");
}
else if(pipe == cmsysProcess_Pipe_STDOUT)
{
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, line << "\n");
}
pipe = cmSystemTools::WaitForLine(cp, line, 100, out, err);
}
// Properly handle output of the build command
cmsysProcess_WaitForExit(cp, 0);
int result = cmsysProcess_GetState(cp);
int retVal = 0;
if(result == cmsysProcess_State_Exited)
{
retVal = cmsysProcess_GetExitValue(cp);
}
else
{
abort();
}
return retVal;
}
//----------------------------------------------------------------------
// this sets up some variables for thew script to use, creates the required
// this sets up some variables for the script to use, creates the required
// cmake instance and generators, and then reads in the script
int cmCTestScriptHandler::ReadInScript(const std::string& total_script_arg)
{
@ -426,8 +490,8 @@ void cmCTestScriptHandler::SleepInSeconds(unsigned int secondsToWait)
//----------------------------------------------------------------------
// run a specific script
int cmCTestScriptHandler::RunConfigurationScript(
const std::string& total_script_arg)
int cmCTestScriptHandler::RunConfigurationScript
(const std::string& total_script_arg, bool pscope)
{
int result;
@ -435,7 +499,18 @@ int cmCTestScriptHandler::RunConfigurationScript(
cmSystemTools::GetTime();
// read in the script
result = this->ReadInScript(total_script_arg);
if (pscope)
{
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Reading Script: " << total_script_arg << std::endl);
result = this->ReadInScript(total_script_arg);
}
else
{
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Executing Script: " << total_script_arg << std::endl);
result = this->ExecuteScript(total_script_arg);
}
if (result)
{
return result;
@ -847,11 +922,11 @@ void cmCTestScriptHandler::RestoreBackupDirectories()
}
}
bool cmCTestScriptHandler::RunScript(cmCTest* ctest, const char *sname)
bool cmCTestScriptHandler::RunScript(cmCTest* ctest, const char *sname, bool InProcess)
{
cmCTestScriptHandler* sh = new cmCTestScriptHandler();
sh->SetCTestInstance(ctest);
sh->AddConfigurationScript(sname);
sh->AddConfigurationScript(sname,InProcess);
sh->ProcessHandler();
delete sh;
return true;

View File

@ -69,9 +69,9 @@ public:
cmTypeMacro(cmCTestScriptHandler, cmCTestGenericHandler);
/**
* Add a script to run
* Add a script to run, and if is should run in the current process
*/
void AddConfigurationScript(const char *);
void AddConfigurationScript(const char *, bool pscope);
/**
* Run a dashboard using a specified confiuration script
@ -81,7 +81,7 @@ public:
/*
* Run a script
*/
static bool RunScript(cmCTest* ctest, const char *script);
static bool RunScript(cmCTest* ctest, const char *script, bool InProcess);
int RunCurrentScript();
/*
@ -99,9 +99,11 @@ public:
~cmCTestScriptHandler();
void Initialize();
private:
// reads in a script
int ReadInScript(const std::string& total_script_arg);
int ExecuteScript(const std::string& total_script_arg);
// extract vars from the script to set ivars
int ExtractVariables();
@ -116,13 +118,14 @@ private:
int BackupDirectories();
void RestoreBackupDirectories();
int RunConfigurationScript(const std::string& script);
int RunConfigurationScript(const std::string& script, bool pscope);
int RunConfigurationDashboard();
// Add ctest command
void AddCTestCommand(cmCTestCommand* command);
std::vector<cmStdString> ConfigurationScripts;
std::vector<bool> ScriptProcessScope;
bool Backup;
bool EmptyBinDir;

View File

@ -1302,6 +1302,13 @@ int cmCTest::Run(std::vector<std::string>const& args, std::string* output)
const char* ctestExec = "ctest";
bool cmakeAndTest = false;
bool performSomeTest = true;
bool SRArgumentSpecified = false;
// copy the command line
for(size_t i=0; i < args.size(); ++i)
{
this->InitialCommandLineArguments.push_back(args[i]);
}
for(size_t i=1; i < args.size(); ++i)
{
std::string arg = args[i];
@ -1347,13 +1354,40 @@ int cmCTest::Run(std::vector<std::string>const& args, std::string* output)
this->ShowOnly = true;
}
if(this->CheckArgument(arg, "-SP", "--script-new-process") && i < args.size() - 1 )
{
this->RunConfigurationScript = true;
i++;
cmCTestScriptHandler* ch
= static_cast<cmCTestScriptHandler*>(this->GetHandler("script"));
// -SR is an internal argument, -SP should be ignored when it is passed
if (!SRArgumentSpecified)
{
ch->AddConfigurationScript(args[i].c_str(),false);
}
}
if(this->CheckArgument(arg, "-SR", "--script-run") && i < args.size() - 1 )
{
SRArgumentSpecified = true;
this->RunConfigurationScript = true;
i++;
cmCTestScriptHandler* ch
= static_cast<cmCTestScriptHandler*>(this->GetHandler("script"));
ch->AddConfigurationScript(args[i].c_str(),true);
}
if(this->CheckArgument(arg, "-S", "--script") && i < args.size() - 1 )
{
this->RunConfigurationScript = true;
i++;
cmCTestScriptHandler* ch
= static_cast<cmCTestScriptHandler*>(this->GetHandler("script"));
ch->AddConfigurationScript(args[i].c_str());
// -SR is an internal argument, -S should be ignored when it is passed
if (!SRArgumentSpecified)
{
ch->AddConfigurationScript(args[i].c_str(),true);
}
}
if(this->CheckArgument(arg, "-O", "--output-log") && i < args.size() - 1 )

View File

@ -293,6 +293,9 @@ public:
//! Read the custom configuration files and apply them to the current ctest
int ReadCustomConfigurationFileTree(const char* dir);
std::vector<cmStdString> &GetInitialCommandLineArguments()
{ return this->InitialCommandLineArguments; };
private:
std::string ConfigType;
bool Verbose;
@ -383,6 +386,7 @@ private:
int DartVersion;
std::set<cmStdString> SubmitFiles;
std::vector<cmStdString> InitialCommandLineArguments;
int SubmitIndex;

View File

@ -21,7 +21,6 @@
#include <cmsys/RegularExpression.hxx>
#include <cmsys/Directory.hxx>
#include <cmsys/Process.h>
// support for realpath call
#ifndef _WIN32
@ -1671,3 +1670,106 @@ bool cmSystemTools::ListTar(const char* outFileName,
return false;
#endif
}
int cmSystemTools::WaitForLine(cmsysProcess* process, std::string& line,
double timeout,
std::vector<char>& out,
std::vector<char>& err)
{
line = "";
std::vector<char>::iterator outiter = out.begin();
std::vector<char>::iterator erriter = err.begin();
while(1)
{
// Check for a newline in stdout.
for(;outiter != out.end(); ++outiter)
{
if((*outiter == '\r') && ((outiter+1) == out.end()))
{
break;
}
else if(*outiter == '\n' || *outiter == '\0')
{
int length = outiter-out.begin();
if(length > 1 && *(outiter-1) == '\r')
{
--length;
}
if(length > 0)
{
line.append(&out[0], length);
}
out.erase(out.begin(), outiter+1);
return cmsysProcess_Pipe_STDOUT;
}
}
// Check for a newline in stderr.
for(;erriter != err.end(); ++erriter)
{
if((*erriter == '\r') && ((erriter+1) == err.end()))
{
break;
}
else if(*erriter == '\n' || *erriter == '\0')
{
int length = erriter-err.begin();
if(length > 1 && *(erriter-1) == '\r')
{
--length;
}
if(length > 0)
{
line.append(&err[0], length);
}
err.erase(err.begin(), erriter+1);
return cmsysProcess_Pipe_STDERR;
}
}
// No newlines found. Wait for more data from the process.
int length;
char* data;
int pipe = cmsysProcess_WaitForData(process, &data, &length, &timeout);
if(pipe == cmsysProcess_Pipe_Timeout)
{
// Timeout has been exceeded.
return pipe;
}
else if(pipe == cmsysProcess_Pipe_STDOUT)
{
// Append to the stdout buffer.
std::vector<char>::size_type size = out.size();
out.insert(out.end(), data, data+length);
outiter = out.begin()+size;
}
else if(pipe == cmsysProcess_Pipe_STDERR)
{
// Append to the stderr buffer.
std::vector<char>::size_type size = err.size();
err.insert(err.end(), data, data+length);
erriter = err.begin()+size;
}
else if(pipe == cmsysProcess_Pipe_None)
{
// Both stdout and stderr pipes have broken. Return leftover data.
if(!out.empty())
{
line.append(&out[0], outiter-out.begin());
out.erase(out.begin(), out.end());
return cmsysProcess_Pipe_STDOUT;
}
else if(!err.empty())
{
line.append(&err[0], erriter-err.begin());
err.erase(err.begin(), err.end());
return cmsysProcess_Pipe_STDERR;
}
else
{
return cmsysProcess_Pipe_None;
}
}
}
}

View File

@ -20,6 +20,9 @@
#include "cmStandardIncludes.h"
#include <cmsys/SystemTools.hxx>
#include <cmsys/Process.h>
/** \class cmSystemTools
* \brief A collection of useful functions for CMake.
@ -257,6 +260,12 @@ public:
*/
static void ReportLastSystemError(const char* m);
/** a general output handler for cmsysProcess */
static int WaitForLine(cmsysProcess* process, std::string& line,
double timeout,
std::vector<char>& out,
std::vector<char>& err);
/** Split a string on its newlines into multiple lines. Returns
false only if the last line stored had no newline. */
static bool Split(const char* s, std::vector<cmStdString>& l);

View File

@ -8,108 +8,6 @@
// error, and re-runs xcodebuild until that error does
// not show up.
int WaitForLine(cmsysProcess* process, std::string& line,
double timeout,
std::vector<char>& out,
std::vector<char>& err)
{
line = "";
std::vector<char>::iterator outiter = out.begin();
std::vector<char>::iterator erriter = err.begin();
while(1)
{
// Check for a newline in stdout.
for(;outiter != out.end(); ++outiter)
{
if((*outiter == '\r') && ((outiter+1) == out.end()))
{
break;
}
else if(*outiter == '\n' || *outiter == '\0')
{
int length = outiter-out.begin();
if(length > 1 && *(outiter-1) == '\r')
{
--length;
}
if(length > 0)
{
line.append(&out[0], length);
}
out.erase(out.begin(), outiter+1);
return cmsysProcess_Pipe_STDOUT;
}
}
// Check for a newline in stderr.
for(;erriter != err.end(); ++erriter)
{
if((*erriter == '\r') && ((erriter+1) == err.end()))
{
break;
}
else if(*erriter == '\n' || *erriter == '\0')
{
int length = erriter-err.begin();
if(length > 1 && *(erriter-1) == '\r')
{
--length;
}
if(length > 0)
{
line.append(&err[0], length);
}
err.erase(err.begin(), erriter+1);
return cmsysProcess_Pipe_STDERR;
}
}
// No newlines found. Wait for more data from the process.
int length;
char* data;
int pipe = cmsysProcess_WaitForData(process, &data, &length, &timeout);
if(pipe == cmsysProcess_Pipe_Timeout)
{
// Timeout has been exceeded.
return pipe;
}
else if(pipe == cmsysProcess_Pipe_STDOUT)
{
// Append to the stdout buffer.
std::vector<char>::size_type size = out.size();
out.insert(out.end(), data, data+length);
outiter = out.begin()+size;
}
else if(pipe == cmsysProcess_Pipe_STDERR)
{
// Append to the stderr buffer.
std::vector<char>::size_type size = err.size();
err.insert(err.end(), data, data+length);
erriter = err.begin()+size;
}
else if(pipe == cmsysProcess_Pipe_None)
{
// Both stdout and stderr pipes have broken. Return leftover data.
if(!out.empty())
{
line.append(&out[0], outiter-out.begin());
out.erase(out.begin(), out.end());
return cmsysProcess_Pipe_STDOUT;
}
else if(!err.empty())
{
line.append(&err[0], erriter-err.begin());
err.erase(err.begin(), err.end());
return cmsysProcess_Pipe_STDERR;
}
else
{
return cmsysProcess_Pipe_None;
}
}
}
}
int RunXCode(std::vector<const char*>& argv, bool& hitbug)
{
hitbug = false;
@ -120,7 +18,7 @@ int RunXCode(std::vector<const char*>& argv, bool& hitbug)
std::vector<char> out;
std::vector<char> err;
std::string line;
int pipe =WaitForLine(cp, line, 100.0, out, err);
int pipe = cmSystemTools::WaitForLine(cp, line, 100.0, out, err);
while(pipe != cmsysProcess_Pipe_None)
{
if(line.find("/bin/sh: bad interpreter: Text file busy")
@ -144,7 +42,7 @@ int RunXCode(std::vector<const char*>& argv, bool& hitbug)
std::cout << line << "\n";
}
}
pipe =WaitForLine(cp, line, 100, out, err);
pipe = cmSystemTools::WaitForLine(cp, line, 100, out, err);
}
cmsysProcess_WaitForExit(cp, 0);
if(cmsysProcess_GetState(cp) == cmsysProcess_State_Exited)

View File

@ -103,6 +103,12 @@ static const cmDocumentationEntry cmDocumentationOptions[] =
"ctest will do what is required to create and run a dashboard. This "
"option basically sets up a dashboard and then runs ctest -D with the "
"appropriate options."},
{"-SP <script>, --script-new-process <script>", "Execute a dashboard for a "
"configuration",
"This option does the same operations as -S but it will do them in a "
"seperate process. This is primarily useful in cases where the script "
"may modify the environment and you do not want the modified enviroment "
"to impact other -S scripts."},
{"-A <file>, --add-notes <file>", "Add a notes file with submission",
"This option tells ctest to include a notes file when submitting "
"dashboard. "},