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; return true;
} }
// run each script bool np = false;
unsigned int i; unsigned int i = 0;
for (i = 0; i < args.size(); ++i) 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; return true;
} }

View File

@ -67,11 +67,12 @@ public:
virtual const char* GetFullDocumentation() virtual const char* GetFullDocumentation()
{ {
return 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" " script_file_name2 ...)\n"
"Runs a script or scripts much like if it was run from ctest -S. " "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 " "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); cmTypeMacro(cmCTestRunScriptCommand, cmCTestCommand);

View File

@ -167,9 +167,10 @@ cmCTestScriptHandler::~cmCTestScriptHandler()
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// just adds an argument to the vector // 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->ConfigurationScripts.push_back(script);
this->ScriptProcessScope.push_back(pscope);
} }
@ -179,14 +180,12 @@ void cmCTestScriptHandler::AddConfigurationScript(const char *script)
int cmCTestScriptHandler::ProcessHandler() int cmCTestScriptHandler::ProcessHandler()
{ {
int res = 0; int res = 0;
std::vector<cmStdString>::iterator it; for (size_t i=0; i < this->ConfigurationScripts.size(); ++i)
for ( it = this->ConfigurationScripts.begin();
it != this->ConfigurationScripts.end();
it ++ )
{ {
// for each script run it // for each script run it
res += this->RunConfigurationScript( res += this->RunConfigurationScript
cmSystemTools::CollapseFullPath(it->c_str())); (cmSystemTools::CollapseFullPath(this->ConfigurationScripts[i].c_str()),
this->ScriptProcessScope[i]);
} }
if ( res ) if ( res )
{ {
@ -218,8 +217,73 @@ void cmCTestScriptHandler::AddCTestCommand(cmCTestCommand* command)
this->CMake->AddCommand(newCom); 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 // cmake instance and generators, and then reads in the script
int cmCTestScriptHandler::ReadInScript(const std::string& total_script_arg) int cmCTestScriptHandler::ReadInScript(const std::string& total_script_arg)
{ {
@ -426,8 +490,8 @@ void cmCTestScriptHandler::SleepInSeconds(unsigned int secondsToWait)
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// run a specific script // run a specific script
int cmCTestScriptHandler::RunConfigurationScript( int cmCTestScriptHandler::RunConfigurationScript
const std::string& total_script_arg) (const std::string& total_script_arg, bool pscope)
{ {
int result; int result;
@ -435,7 +499,18 @@ int cmCTestScriptHandler::RunConfigurationScript(
cmSystemTools::GetTime(); cmSystemTools::GetTime();
// read in the script // 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) if (result)
{ {
return 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(); cmCTestScriptHandler* sh = new cmCTestScriptHandler();
sh->SetCTestInstance(ctest); sh->SetCTestInstance(ctest);
sh->AddConfigurationScript(sname); sh->AddConfigurationScript(sname,InProcess);
sh->ProcessHandler(); sh->ProcessHandler();
delete sh; delete sh;
return true; return true;

View File

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

View File

@ -1302,6 +1302,13 @@ int cmCTest::Run(std::vector<std::string>const& args, std::string* output)
const char* ctestExec = "ctest"; const char* ctestExec = "ctest";
bool cmakeAndTest = false; bool cmakeAndTest = false;
bool performSomeTest = true; 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) for(size_t i=1; i < args.size(); ++i)
{ {
std::string arg = args[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; 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 ) if(this->CheckArgument(arg, "-S", "--script") && i < args.size() - 1 )
{ {
this->RunConfigurationScript = true; this->RunConfigurationScript = true;
i++; i++;
cmCTestScriptHandler* ch cmCTestScriptHandler* ch
= static_cast<cmCTestScriptHandler*>(this->GetHandler("script")); = 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 ) 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 //! Read the custom configuration files and apply them to the current ctest
int ReadCustomConfigurationFileTree(const char* dir); int ReadCustomConfigurationFileTree(const char* dir);
std::vector<cmStdString> &GetInitialCommandLineArguments()
{ return this->InitialCommandLineArguments; };
private: private:
std::string ConfigType; std::string ConfigType;
bool Verbose; bool Verbose;
@ -383,7 +386,8 @@ private:
int DartVersion; int DartVersion;
std::set<cmStdString> SubmitFiles; std::set<cmStdString> SubmitFiles;
std::vector<cmStdString> InitialCommandLineArguments;
int SubmitIndex; int SubmitIndex;
cmGeneratedFileStream* OutputLogFile; cmGeneratedFileStream* OutputLogFile;

View File

@ -42,7 +42,7 @@ IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf)
this->Executing = true; this->Executing = true;
std::vector<std::string>::const_iterator j = this->Args.begin(); std::vector<std::string>::const_iterator j = this->Args.begin();
++j; ++j;
std::string tmps; std::string tmps;
cmListFileArgument arg; cmListFileArgument arg;
for( ; j != this->Args.end(); ++j) for( ; j != this->Args.end(); ++j)

View File

@ -21,7 +21,6 @@
#include <cmsys/RegularExpression.hxx> #include <cmsys/RegularExpression.hxx>
#include <cmsys/Directory.hxx> #include <cmsys/Directory.hxx>
#include <cmsys/Process.h>
// support for realpath call // support for realpath call
#ifndef _WIN32 #ifndef _WIN32
@ -1671,3 +1670,106 @@ bool cmSystemTools::ListTar(const char* outFileName,
return false; return false;
#endif #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 "cmStandardIncludes.h"
#include <cmsys/SystemTools.hxx> #include <cmsys/SystemTools.hxx>
#include <cmsys/Process.h>
/** \class cmSystemTools /** \class cmSystemTools
* \brief A collection of useful functions for CMake. * \brief A collection of useful functions for CMake.
@ -257,6 +260,12 @@ public:
*/ */
static void ReportLastSystemError(const char* m); 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 /** Split a string on its newlines into multiple lines. Returns
false only if the last line stored had no newline. */ false only if the last line stored had no newline. */
static bool Split(const char* s, std::vector<cmStdString>& l); 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 // error, and re-runs xcodebuild until that error does
// not show up. // 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) int RunXCode(std::vector<const char*>& argv, bool& hitbug)
{ {
hitbug = false; hitbug = false;
@ -120,7 +18,7 @@ int RunXCode(std::vector<const char*>& argv, bool& hitbug)
std::vector<char> out; std::vector<char> out;
std::vector<char> err; std::vector<char> err;
std::string line; 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) while(pipe != cmsysProcess_Pipe_None)
{ {
if(line.find("/bin/sh: bad interpreter: Text file busy") 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"; std::cout << line << "\n";
} }
} }
pipe =WaitForLine(cp, line, 100, out, err); pipe = cmSystemTools::WaitForLine(cp, line, 100, out, err);
} }
cmsysProcess_WaitForExit(cp, 0); cmsysProcess_WaitForExit(cp, 0);
if(cmsysProcess_GetState(cp) == cmsysProcess_State_Exited) 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 " "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 " "option basically sets up a dashboard and then runs ctest -D with the "
"appropriate options."}, "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", {"-A <file>, --add-notes <file>", "Add a notes file with submission",
"This option tells ctest to include a notes file when submitting " "This option tells ctest to include a notes file when submitting "
"dashboard. "}, "dashboard. "},