diff --git a/Source/CTest/cmCTestRunScriptCommand.cxx b/Source/CTest/cmCTestRunScriptCommand.cxx index 5df84bbb3..abb2e48cf 100644 --- a/Source/CTest/cmCTestRunScriptCommand.cxx +++ b/Source/CTest/cmCTestRunScriptCommand.cxx @@ -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; } diff --git a/Source/CTest/cmCTestRunScriptCommand.h b/Source/CTest/cmCTestRunScriptCommand.h index 1ca022e8e..949cb77e0 100644 --- a/Source/CTest/cmCTestRunScriptCommand.h +++ b/Source/CTest/cmCTestRunScriptCommand.h @@ -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); diff --git a/Source/CTest/cmCTestScriptHandler.cxx b/Source/CTest/cmCTestScriptHandler.cxx index 887d8ad4a..9350334d4 100644 --- a/Source/CTest/cmCTestScriptHandler.cxx +++ b/Source/CTest/cmCTestScriptHandler.cxx @@ -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::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 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 &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 out; + std::vector 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; diff --git a/Source/CTest/cmCTestScriptHandler.h b/Source/CTest/cmCTestScriptHandler.h index 344192ba4..e3540053f 100644 --- a/Source/CTest/cmCTestScriptHandler.h +++ b/Source/CTest/cmCTestScriptHandler.h @@ -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 ConfigurationScripts; + std::vector ScriptProcessScope; bool Backup; bool EmptyBinDir; diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx index f84430cb1..c5ad1f4e1 100644 --- a/Source/cmCTest.cxx +++ b/Source/cmCTest.cxx @@ -1302,6 +1302,13 @@ int cmCTest::Run(std::vectorconst& 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::vectorconst& 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(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(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(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 ) diff --git a/Source/cmCTest.h b/Source/cmCTest.h index f94cb1f51..37e05e909 100644 --- a/Source/cmCTest.h +++ b/Source/cmCTest.h @@ -293,6 +293,9 @@ public: //! Read the custom configuration files and apply them to the current ctest int ReadCustomConfigurationFileTree(const char* dir); + std::vector &GetInitialCommandLineArguments() + { return this->InitialCommandLineArguments; }; + private: std::string ConfigType; bool Verbose; @@ -383,7 +386,8 @@ private: int DartVersion; std::set SubmitFiles; - + std::vector InitialCommandLineArguments; + int SubmitIndex; cmGeneratedFileStream* OutputLogFile; diff --git a/Source/cmForEachCommand.cxx b/Source/cmForEachCommand.cxx index c9b39dc4d..e2dce93c9 100644 --- a/Source/cmForEachCommand.cxx +++ b/Source/cmForEachCommand.cxx @@ -42,7 +42,7 @@ IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf) this->Executing = true; std::vector::const_iterator j = this->Args.begin(); ++j; - + std::string tmps; cmListFileArgument arg; for( ; j != this->Args.end(); ++j) diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 29fcd9219..e25ce136a 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -21,7 +21,6 @@ #include #include -#include // 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& out, + std::vector& err) +{ + line = ""; + std::vector::iterator outiter = out.begin(); + std::vector::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::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::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; + } + } + } +} + diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index 0973a076d..9b3fa687e 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h @@ -20,6 +20,9 @@ #include "cmStandardIncludes.h" #include +#include + + /** \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& out, + std::vector& 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& l); diff --git a/Source/cmakexbuild.cxx b/Source/cmakexbuild.cxx index 6bf4da49a..5f673fbb3 100644 --- a/Source/cmakexbuild.cxx +++ b/Source/cmakexbuild.cxx @@ -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& out, - std::vector& err) -{ - line = ""; - std::vector::iterator outiter = out.begin(); - std::vector::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::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::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& argv, bool& hitbug) { hitbug = false; @@ -120,7 +18,7 @@ int RunXCode(std::vector& argv, bool& hitbug) std::vector out; std::vector 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& 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) diff --git a/Source/ctest.cxx b/Source/ctest.cxx index 189d1cc86..6a8c90cb8 100644 --- a/Source/ctest.cxx +++ b/Source/ctest.cxx @@ -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