| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157 | /*============================================================================  CMake - Cross Platform Makefile Generator  Copyright 2000-2009 Kitware, Inc., Insight Software Consortium  Distributed under the OSI-approved BSD License (the "License");  see accompanying file Copyright.txt for details.  This software is distributed WITHOUT ANY WARRANTY; without even the  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the License for more information.============================================================================*/#include "cmCTestScriptHandler.h"#include "cmCTest.h"#include "cmake.h"#include "cmFunctionBlocker.h"#include "cmMakefile.h"#include "cmLocalGenerator.h"#include "cmGlobalGenerator.h"#include "cmGeneratedFileStream.h"//#include <cmsys/RegularExpression.hxx>#include <cmsys/Process.h>#include <cmsys/Directory.hxx>// used for sleep#ifdef _WIN32#include "windows.h"#endif#include <stdlib.h>#include <time.h>#include <math.h>#include <float.h>// needed for sleep#if !defined(_WIN32)# include <unistd.h>#endif#include "cmCTestBuildCommand.h"#include "cmCTestConfigureCommand.h"#include "cmCTestCoverageCommand.h"#include "cmCTestEmptyBinaryDirectoryCommand.h"#include "cmCTestMemCheckCommand.h"#include "cmCTestReadCustomFilesCommand.h"#include "cmCTestRunScriptCommand.h"#include "cmCTestSleepCommand.h"#include "cmCTestStartCommand.h"#include "cmCTestSubmitCommand.h"#include "cmCTestTestCommand.h"#include "cmCTestUpdateCommand.h"#include "cmCTestUploadCommand.h"#define CTEST_INITIAL_CMAKE_OUTPUT_FILE_NAME "CTestInitialCMakeOutput.log"// used to keep elapsed time up to dateclass cmCTestScriptFunctionBlocker : public cmFunctionBlocker{public:  cmCTestScriptFunctionBlocker() {}  virtual ~cmCTestScriptFunctionBlocker() {}  virtual bool IsFunctionBlocked(const cmListFileFunction& lff,                                 cmMakefile &mf,                                 cmExecutionStatus &);  //virtual bool ShouldRemove(const cmListFileFunction& lff, cmMakefile &mf);  //virtual void ScopeEnded(cmMakefile &mf);  cmCTestScriptHandler* CTestScriptHandler;};// simply update the time and don't block anythingbool cmCTestScriptFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& , cmMakefile &,                  cmExecutionStatus &){  this->CTestScriptHandler->UpdateElapsedTime();  return false;}//----------------------------------------------------------------------cmCTestScriptHandler::cmCTestScriptHandler(){  this->Backup = false;  this->EmptyBinDir = false;  this->EmptyBinDirOnce = false;  this->Makefile = 0;  this->LocalGenerator = 0;  this->CMake = 0;  this->GlobalGenerator = 0;  this->ScriptStartTime = 0;  // the *60 is becuase the settings are in minutes but GetTime is seconds  this->MinimumInterval = 30*60;  this->ContinuousDuration = -1;}//----------------------------------------------------------------------void cmCTestScriptHandler::Initialize(){  this->Superclass::Initialize();  this->Backup = false;  this->EmptyBinDir = false;  this->EmptyBinDirOnce = false;  this->SourceDir = "";  this->BinaryDir = "";  this->BackupSourceDir = "";  this->BackupBinaryDir = "";  this->CTestRoot = "";  this->CVSCheckOut = "";  this->CTestCmd = "";  this->UpdateCmd = "";  this->CTestEnv = "";  this->InitialCache = "";  this->CMakeCmd = "";  this->CMOutFile = "";  this->ExtraUpdates.clear();  this->MinimumInterval = 20*60;  this->ContinuousDuration = -1;  // what time in seconds did this script start running  this->ScriptStartTime = 0;  this->Makefile = 0;  if (this->LocalGenerator)    {    delete this->LocalGenerator;    }  this->LocalGenerator = 0;  if (this->GlobalGenerator)    {    delete this->GlobalGenerator;    }  this->GlobalGenerator = 0;  if (this->CMake)    {    delete this->CMake;    }}//----------------------------------------------------------------------cmCTestScriptHandler::~cmCTestScriptHandler(){  // local generator owns the makefile  this->Makefile = 0;  if (this->LocalGenerator)    {    delete this->LocalGenerator;    }  this->LocalGenerator = 0;  if (this->GlobalGenerator)    {    delete this->GlobalGenerator;    }  this->GlobalGenerator = 0;  if (this->CMake)    {    delete this->CMake;    }}//----------------------------------------------------------------------// just adds an argument to the vectorvoid cmCTestScriptHandler::AddConfigurationScript(const char *script,                                                  bool pscope){  this->ConfigurationScripts.push_back(script);  this->ScriptProcessScope.push_back(pscope);}//----------------------------------------------------------------------// the generic entry point for handling scripts, this routine will run all// the scripts provides a -S argumentsint cmCTestScriptHandler::ProcessHandler(){  int res = 0;  for (size_t i=0; i <  this->ConfigurationScripts.size(); ++i)    {    // for each script run it    res |= this->RunConfigurationScript      (cmSystemTools::CollapseFullPath(this->ConfigurationScripts[i]),       this->ScriptProcessScope[i]);    }  if ( res )    {    return -1;    }  return 0;}void cmCTestScriptHandler::UpdateElapsedTime(){  if (this->LocalGenerator)    {    // set the current elapsed time    char timeString[20];    int itime = static_cast<unsigned int>(cmSystemTools::GetTime()                                          - this->ScriptStartTime);    sprintf(timeString,"%i",itime);    this->LocalGenerator->GetMakefile()->AddDefinition("CTEST_ELAPSED_TIME",                                                   timeString);    }}//----------------------------------------------------------------------void cmCTestScriptHandler::AddCTestCommand(cmCTestCommand* command){  cmCTestCommand* newCom = command;  newCom->CTest = this->CTest;  newCom->CTestScriptHandler = this;  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(cmSystemTools::GetCTestCommand().c_str());  argv.push_back("-SR");  argv.push_back(total_script_arg.c_str());  cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,             "Executable for CTest is: " <<             cmSystemTools::GetCTestCommand() << "\n");  // now pass through all the other arguments  std::vector<std::string> &initArgs =    this->CTest->GetInitialCommandLineArguments();  //*** need to make sure this does not have the current script ***  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;  bool failed = false;  if(result == cmsysProcess_State_Exited)    {    retVal = cmsysProcess_GetExitValue(cp);    }  else if(result == cmsysProcess_State_Exception)    {    retVal = cmsysProcess_GetExitException(cp);    cmCTestLog(this->CTest, ERROR_MESSAGE, "\tThere was an exception: "               << cmsysProcess_GetExceptionString(cp) << " " <<               retVal << std::endl);    failed = true;    }  else if(result == cmsysProcess_State_Expired)    {    cmCTestLog(this->CTest, ERROR_MESSAGE, "\tThere was a timeout"               << std::endl);    failed = true;    }  else if(result == cmsysProcess_State_Error)    {    cmCTestLog(this->CTest, ERROR_MESSAGE, "\tError executing ctest: "               << cmsysProcess_GetErrorString(cp) << std::endl);    failed = true;    }  cmsysProcess_Delete(cp);  if(failed)    {    std::ostringstream message;    message << "Error running command: [";    message << result << "] ";    for(std::vector<const char*>::iterator i = argv.begin();        i != argv.end(); ++i)      {      if(*i)        {        message  << *i << " ";        }      }    cmCTestLog(this->CTest, ERROR_MESSAGE,               message.str() << argv[0] << std::endl);    return -1;    }  return retVal;}static void ctestScriptProgressCallback(const char *m, float, void* cd){  cmCTest* ctest = static_cast<cmCTest*>(cd);  if(m && *m)    {    cmCTestLog(ctest, HANDLER_OUTPUT, "-- " << m << std::endl);    }}void cmCTestScriptHandler::CreateCMake(){  // create a cmake instance to read the configuration script  if (this->CMake)    {    delete this->CMake;    delete this->GlobalGenerator;    delete this->LocalGenerator;    }  this->CMake = new cmake;  this->CMake->AddCMakePaths();  this->GlobalGenerator = new cmGlobalGenerator;  this->GlobalGenerator->SetCMakeInstance(this->CMake);  this->LocalGenerator = this->GlobalGenerator->CreateLocalGenerator();  this->Makefile = this->LocalGenerator->GetMakefile();  this->CMake->SetProgressCallback(ctestScriptProgressCallback, this->CTest);  // Set CMAKE_CURRENT_SOURCE_DIR and CMAKE_CURRENT_BINARY_DIR.  // Also, some commands need Makefile->GetCurrentDirectory().  std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();  this->Makefile->SetStartDirectory(cwd);  this->Makefile->SetStartOutputDirectory(cwd);  // remove all cmake commands which are not scriptable, since they can't be  // used in ctest scripts  this->CMake->RemoveUnscriptableCommands();  // add any ctest specific commands, probably should have common superclass  // for ctest commands to clean this up. If a couple more commands are  // created with the same format lets do that - ken  this->AddCTestCommand(new cmCTestBuildCommand);  this->AddCTestCommand(new cmCTestConfigureCommand);  this->AddCTestCommand(new cmCTestCoverageCommand);  this->AddCTestCommand(new cmCTestEmptyBinaryDirectoryCommand);  this->AddCTestCommand(new cmCTestMemCheckCommand);  this->AddCTestCommand(new cmCTestReadCustomFilesCommand);  this->AddCTestCommand(new cmCTestRunScriptCommand);  this->AddCTestCommand(new cmCTestSleepCommand);  this->AddCTestCommand(new cmCTestStartCommand);  this->AddCTestCommand(new cmCTestSubmitCommand);  this->AddCTestCommand(new cmCTestTestCommand);  this->AddCTestCommand(new cmCTestUpdateCommand);  this->AddCTestCommand(new cmCTestUploadCommand);}//----------------------------------------------------------------------// this sets up some variables for the script to use, creates the required// cmake instance and generators, and then reads in the scriptint cmCTestScriptHandler::ReadInScript(const std::string& total_script_arg){  // Reset the error flag so that the script is read in no matter what  cmSystemTools::ResetErrorOccuredFlag();  // if the argument has a , in it then it needs to be broken into the fist  // argument (which is the script) and the second argument which will be  // passed into the scripts as S_ARG  std::string script = total_script_arg;  std::string script_arg;  if (total_script_arg.find(",") != std::string::npos)    {    script = total_script_arg.substr(0,total_script_arg.find(","));    script_arg = total_script_arg.substr(total_script_arg.find(",")+1);    }  // make sure the file exists  if (!cmSystemTools::FileExists(script.c_str()))    {    cmSystemTools::Error("Cannot find file: ", script.c_str());    return 1;    }  // read in the list file to fill the cache  // create a cmake instance to read the configuration script  this->CreateCMake();  // set a variable with the path to the current script  this->Makefile->AddDefinition("CTEST_SCRIPT_DIRECTORY",                            cmSystemTools::GetFilenamePath(script).c_str());  this->Makefile->AddDefinition("CTEST_SCRIPT_NAME",                            cmSystemTools::GetFilenameName(script).c_str());  this->Makefile->AddDefinition("CTEST_EXECUTABLE_NAME",                                cmSystemTools::GetCTestCommand().c_str());  this->Makefile->AddDefinition("CMAKE_EXECUTABLE_NAME",                                cmSystemTools::GetCMakeCommand().c_str());  this->Makefile->AddDefinition("CTEST_RUN_CURRENT_SCRIPT", true);  this->UpdateElapsedTime();  // add the script arg if defined  if (!script_arg.empty())    {    this->Makefile->AddDefinition("CTEST_SCRIPT_ARG", script_arg.c_str());    }  // always add a function blocker to update the elapsed time  cmCTestScriptFunctionBlocker *f = new cmCTestScriptFunctionBlocker();  f->CTestScriptHandler = this;  this->Makefile->AddFunctionBlocker(f);  /* Execute CTestScriptMode.cmake, which loads CMakeDetermineSystem and  CMakeSystemSpecificInformation, so  that variables like CMAKE_SYSTEM and also the search paths for libraries,  header and executables are set correctly and can be used. Makes new-style  ctest scripting easier. */  std::string systemFile =      this->Makefile->GetModulesFile("CTestScriptMode.cmake");  if (!this->Makefile->ReadListFile(0, systemFile.c_str()) ||      cmSystemTools::GetErrorOccuredFlag())    {    cmCTestLog(this->CTest, ERROR_MESSAGE, "Error in read:"               << systemFile << "\n");    return 2;    }  // Add definitions of variables passed in on the command line:  const std::map<std::string, std::string> &defs =    this->CTest->GetDefinitions();  for (std::map<std::string, std::string>::const_iterator it = defs.begin();       it != defs.end(); ++it)    {    this->Makefile->AddDefinition(it->first, it->second.c_str());    }  // finally read in the script  if (!this->Makefile->ReadListFile(0, script.c_str()) ||    cmSystemTools::GetErrorOccuredFlag())    {    cmCTestLog(this->CTest, ERROR_MESSAGE, "Error in read script: "               << script               << std::endl);    // Reset the error flag so that it can run more than    // one script with an error when you    // use ctest_run_script    cmSystemTools::ResetErrorOccuredFlag();    return 2;    }  return 0;}//----------------------------------------------------------------------// extract variabels from the script to set ivarsint cmCTestScriptHandler::ExtractVariables(){  // Temporary variables  const char* minInterval;  const char* contDuration;  this->SourceDir    = this->Makefile->GetSafeDefinition("CTEST_SOURCE_DIRECTORY");  this->BinaryDir    = this->Makefile->GetSafeDefinition("CTEST_BINARY_DIRECTORY");  // add in translations for src and bin  cmSystemTools::AddKeepPath(this->SourceDir);  cmSystemTools::AddKeepPath(this->BinaryDir);  this->CTestCmd    = this->Makefile->GetSafeDefinition("CTEST_COMMAND");  this->CVSCheckOut    = this->Makefile->GetSafeDefinition("CTEST_CVS_CHECKOUT");  this->CTestRoot    = this->Makefile->GetSafeDefinition("CTEST_DASHBOARD_ROOT");  this->UpdateCmd    = this->Makefile->GetSafeDefinition("CTEST_UPDATE_COMMAND");  if ( this->UpdateCmd.empty() )    {    this->UpdateCmd      = this->Makefile->GetSafeDefinition("CTEST_CVS_COMMAND");    }  this->CTestEnv    = this->Makefile->GetSafeDefinition("CTEST_ENVIRONMENT");  this->InitialCache    = this->Makefile->GetSafeDefinition("CTEST_INITIAL_CACHE");  this->CMakeCmd    = this->Makefile->GetSafeDefinition("CTEST_CMAKE_COMMAND");  this->CMOutFile    = this->Makefile->GetSafeDefinition("CTEST_CMAKE_OUTPUT_FILE_NAME");  this->Backup    = this->Makefile->IsOn("CTEST_BACKUP_AND_RESTORE");  this->EmptyBinDir    = this->Makefile->IsOn("CTEST_START_WITH_EMPTY_BINARY_DIRECTORY");  this->EmptyBinDirOnce    = this->Makefile->IsOn("CTEST_START_WITH_EMPTY_BINARY_DIRECTORY_ONCE");  minInterval    = this->Makefile->GetDefinition("CTEST_CONTINUOUS_MINIMUM_INTERVAL");  contDuration    = this->Makefile->GetDefinition("CTEST_CONTINUOUS_DURATION");  char updateVar[40];  int i;  for (i = 1; i < 10; ++i)    {    sprintf(updateVar,"CTEST_EXTRA_UPDATES_%i",i);    const char *updateVal = this->Makefile->GetDefinition(updateVar);    if ( updateVal )      {      if ( this->UpdateCmd.empty() )        {        cmSystemTools::Error(updateVar,          " specified without specifying CTEST_CVS_COMMAND.");        return 12;        }      this->ExtraUpdates.push_back(updateVal);      }    }  // in order to backup and restore we also must have the cvs root  if (this->Backup && this->CVSCheckOut.empty())    {    cmSystemTools::Error(      "Backup was requested without specifying CTEST_CVS_CHECKOUT.");    return 3;    }  // make sure the required info is here  if (this->SourceDir.empty() ||      this->BinaryDir.empty() ||      this->CTestCmd.empty())    {    std::string msg = "CTEST_SOURCE_DIRECTORY = ";    msg += (!this->SourceDir.empty()) ? this->SourceDir.c_str() : "(Null)";    msg += "\nCTEST_BINARY_DIRECTORY = ";    msg += (!this->BinaryDir.empty()) ? this->BinaryDir.c_str() : "(Null)";    msg += "\nCTEST_COMMAND = ";    msg += (!this->CTestCmd.empty()) ? this->CTestCmd.c_str() : "(Null)";    cmSystemTools::Error(      "Some required settings in the configuration file were missing:\n",      msg.c_str());    return 4;    }  // if the dashboard root isn't specified then we can compute it from the  // this->SourceDir  if (this->CTestRoot.empty() )    {    this->CTestRoot = cmSystemTools::GetFilenamePath(this->SourceDir).c_str();    }  // the script may override the minimum continuous interval  if (minInterval)    {    this->MinimumInterval = 60 * atof(minInterval);    }  if (contDuration)    {    this->ContinuousDuration = 60.0 * atof(contDuration);    }  this->UpdateElapsedTime();  return 0;}//----------------------------------------------------------------------void cmCTestScriptHandler::SleepInSeconds(unsigned int secondsToWait){#if defined(_WIN32)        Sleep(1000*secondsToWait);#else        sleep(secondsToWait);#endif}//----------------------------------------------------------------------// run a specific scriptint cmCTestScriptHandler::RunConfigurationScript(const std::string& total_script_arg, bool pscope){#ifdef CMAKE_BUILD_WITH_CMAKE  cmSystemTools::SaveRestoreEnvironment sre;#endif  int result;  this->ScriptStartTime =    cmSystemTools::GetTime();  // read in the script  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;    }  // only run the curent script if we should  if (this->Makefile && this->Makefile->IsOn("CTEST_RUN_CURRENT_SCRIPT"))    {    return this->RunCurrentScript();    }  return result;}//----------------------------------------------------------------------int cmCTestScriptHandler::RunCurrentScript(){  int result;  // do not run twice  this->Makefile->AddDefinition("CTEST_RUN_CURRENT_SCRIPT", false);  // no popup widows  cmSystemTools::SetRunCommandHideConsole(true);  // extract the vars from the cache and store in ivars  result = this->ExtractVariables();  if (result)    {    return result;    }  // set any environment variables  if (!this->CTestEnv.empty())    {    std::vector<std::string> envArgs;    cmSystemTools::ExpandListArgument(this->CTestEnv,envArgs);    cmSystemTools::AppendEnv(envArgs);    }  // now that we have done most of the error checking finally run the  // dashboard, we may be asked to repeatedly run this dashboard, such as  // for a continuous, do we ned to run it more than once?  if ( this->ContinuousDuration >= 0 )    {    this->UpdateElapsedTime();    double ending_time  = cmSystemTools::GetTime() + this->ContinuousDuration;    if (this->EmptyBinDirOnce)      {      this->EmptyBinDir = true;      }    do      {      double interval = cmSystemTools::GetTime();      result = this->RunConfigurationDashboard();      interval = cmSystemTools::GetTime() - interval;      if (interval < this->MinimumInterval)        {        this->SleepInSeconds(          static_cast<unsigned int>(this->MinimumInterval - interval));        }      if (this->EmptyBinDirOnce)        {        this->EmptyBinDir = false;        }      }    while (cmSystemTools::GetTime() < ending_time);    }  // otherwise just run it once  else    {    result = this->RunConfigurationDashboard();    }  return result;}//----------------------------------------------------------------------int cmCTestScriptHandler::CheckOutSourceDir(){  std::string command;  std::string output;  int retVal;  bool res;  if (!cmSystemTools::FileExists(this->SourceDir.c_str()) &&      !this->CVSCheckOut.empty())    {    // we must now checkout the src dir    output = "";    cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,      "Run cvs: " << this->CVSCheckOut << std::endl);    res = cmSystemTools::RunSingleCommand(this->CVSCheckOut.c_str(), &output,      &retVal, this->CTestRoot.c_str(), this->HandlerVerbose,      0 /*this->TimeOut*/);    if (!res || retVal != 0)      {      cmSystemTools::Error("Unable to perform cvs checkout:\n",                           output.c_str());      return 6;      }    }  return 0;}//----------------------------------------------------------------------int cmCTestScriptHandler::BackupDirectories(){  int retVal;  // compute the backup names  this->BackupSourceDir = this->SourceDir;  this->BackupSourceDir += "_CMakeBackup";  this->BackupBinaryDir = this->BinaryDir;  this->BackupBinaryDir += "_CMakeBackup";  // backup the binary and src directories if requested  if (this->Backup)    {    // if for some reason those directories exist then first delete them    if (cmSystemTools::FileExists(this->BackupSourceDir.c_str()))      {      cmSystemTools::RemoveADirectory(this->BackupSourceDir);      }    if (cmSystemTools::FileExists(this->BackupBinaryDir.c_str()))      {      cmSystemTools::RemoveADirectory(this->BackupBinaryDir);      }    // first rename the src and binary directories    rename(this->SourceDir.c_str(), this->BackupSourceDir.c_str());    rename(this->BinaryDir.c_str(), this->BackupBinaryDir.c_str());    // we must now checkout the src dir    retVal = this->CheckOutSourceDir();    if (retVal)      {      this->RestoreBackupDirectories();      return retVal;      }    }  return 0;}//----------------------------------------------------------------------int cmCTestScriptHandler::PerformExtraUpdates(){  std::string command;  std::string output;  int retVal;  bool res;  // do an initial cvs update as required  command = this->UpdateCmd;  std::vector<std::string>::iterator it;  for (it = this->ExtraUpdates.begin();    it != this->ExtraUpdates.end();    ++ it )    {    std::vector<std::string> cvsArgs;    cmSystemTools::ExpandListArgument(*it,cvsArgs);    if (cvsArgs.size() == 2)      {      std::string fullCommand = command;      fullCommand += " update ";      fullCommand += cvsArgs[1];      output = "";      retVal = 0;      cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Run Update: "        << fullCommand << std::endl);      res = cmSystemTools::RunSingleCommand(fullCommand.c_str(), &output,        &retVal, cvsArgs[0].c_str(),        this->HandlerVerbose, 0 /*this->TimeOut*/);      if (!res || retVal != 0)        {        cmSystemTools::Error("Unable to perform extra updates:\n",          it->c_str(), "\nWith output:\n",          output.c_str());        return 0;        }      }    }  return 0;}//----------------------------------------------------------------------// run a single dashboard entryint cmCTestScriptHandler::RunConfigurationDashboard(){  // local variables  std::string command;  std::string output;  int retVal;  bool res;  // make sure the src directory is there, if it isn't then we might be able  // to check it out from cvs  retVal = this->CheckOutSourceDir();  if (retVal)    {    return retVal;    }  // backup the dirs if requested  retVal = this->BackupDirectories();  if (retVal)    {    return retVal;    }  // clear the binary directory?  if (this->EmptyBinDir)    {    if ( !cmCTestScriptHandler::EmptyBinaryDirectory(        this->BinaryDir.c_str()) )      {      cmCTestLog(this->CTest, ERROR_MESSAGE,        "Problem removing the binary directory" << std::endl);      }    }  // make sure the binary directory exists if it isn't the srcdir  if (!cmSystemTools::FileExists(this->BinaryDir.c_str()) &&      this->SourceDir != this->BinaryDir)    {    if (!cmSystemTools::MakeDirectory(this->BinaryDir.c_str()))      {      cmSystemTools::Error("Unable to create the binary directory:\n",                           this->BinaryDir.c_str());      this->RestoreBackupDirectories();      return 7;      }    }  // if the binary directory and the source directory are the same,  // and we are starting with an empty binary directory, then that means  // we must check out the source tree  if (this->EmptyBinDir && this->SourceDir == this->BinaryDir)    {    // make sure we have the required info    if (this->CVSCheckOut.empty())      {      cmSystemTools::Error("You have specified the source and binary "        "directories to be the same (an in source build). You have also "        "specified that the binary directory is to be erased. This means "        "that the source will have to be checked out from CVS. But you have "        "not specified CTEST_CVS_CHECKOUT");      return 8;      }    // we must now checkout the src dir    retVal = this->CheckOutSourceDir();    if (retVal)      {      this->RestoreBackupDirectories();      return retVal;      }    }  // backup the dirs if requested  retVal = this->PerformExtraUpdates();  if (retVal)    {    return retVal;    }  // put the initial cache into the bin dir  if (!this->InitialCache.empty())    {    if (!this->WriteInitialCache(this->BinaryDir.c_str(),         this->InitialCache.c_str()))      {      this->RestoreBackupDirectories();      return 9;      }    }  // do an initial cmake to setup the DartConfig file  int cmakeFailed = 0;  std::string cmakeFailedOuput;  if (!this->CMakeCmd.empty())    {    command = this->CMakeCmd;    command += " \"";    command += this->SourceDir;    output = "";    command += "\"";    retVal = 0;    cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Run cmake command: "      << command << std::endl);    res = cmSystemTools::RunSingleCommand(command.c_str(), &output,      &retVal, this->BinaryDir.c_str(),      this->HandlerVerbose, 0 /*this->TimeOut*/);    if ( !this->CMOutFile.empty() )      {      std::string cmakeOutputFile = this->CMOutFile;      if ( !cmSystemTools::FileIsFullPath(cmakeOutputFile.c_str()) )        {        cmakeOutputFile = this->BinaryDir + "/" + cmakeOutputFile;        }      cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,        "Write CMake output to file: " << cmakeOutputFile        << std::endl);      cmGeneratedFileStream fout(cmakeOutputFile.c_str());      if ( fout )        {        fout << output.c_str();        }      else        {        cmCTestLog(this->CTest, ERROR_MESSAGE,          "Cannot open CMake output file: "          << cmakeOutputFile << " for writing" << std::endl);        }      }    if (!res || retVal != 0)      {      // even if this fails continue to the next step      cmakeFailed = 1;      cmakeFailedOuput = output;      }    }  // run ctest, it may be more than one command in here  std::vector<std::string> ctestCommands;  cmSystemTools::ExpandListArgument(this->CTestCmd,ctestCommands);  // for each variable/argument do a putenv  for (unsigned i = 0; i < ctestCommands.size(); ++i)    {    command = ctestCommands[i];    output = "";    retVal = 0;    cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Run ctest command: "      << command << std::endl);    res = cmSystemTools::RunSingleCommand(command.c_str(), &output,      &retVal, this->BinaryDir.c_str(), this->HandlerVerbose,      0 /*this->TimeOut*/);    // did something critical fail in ctest    if (!res || cmakeFailed ||        retVal & cmCTest::BUILD_ERRORS)      {      this->RestoreBackupDirectories();      if (cmakeFailed)        {        cmCTestLog(this->CTest, ERROR_MESSAGE,          "Unable to run cmake:" << std::endl          << cmakeFailedOuput << std::endl);        return 10;        }      cmCTestLog(this->CTest, ERROR_MESSAGE,        "Unable to run ctest:" << std::endl        << "command: " << command << std::endl        << "output: " << output << std::endl);      if (!res)        {        return 11;        }      return retVal * 100;      }    }  // if all was succesful, delete the backup dirs to free up disk space  if (this->Backup)    {    cmSystemTools::RemoveADirectory(this->BackupSourceDir);    cmSystemTools::RemoveADirectory(this->BackupBinaryDir);    }  return 0;}//-------------------------------------------------------------------------bool cmCTestScriptHandler::WriteInitialCache(const char* directory,                                             const char* text){  std::string cacheFile = directory;  cacheFile += "/CMakeCache.txt";  cmGeneratedFileStream fout(cacheFile.c_str());  if(!fout)    {    return false;    }  if (text!=0)    {    fout.write(text, strlen(text));    }  // Make sure the operating system has finished writing the file  // before closing it.  This will ensure the file is finished before  // the check below.  fout.flush();  fout.close();  return true;}//-------------------------------------------------------------------------void cmCTestScriptHandler::RestoreBackupDirectories(){  // if we backed up the dirs and the build failed, then restore  // the backed up dirs  if (this->Backup)    {    // if for some reason those directories exist then first delete them    if (cmSystemTools::FileExists(this->SourceDir.c_str()))      {      cmSystemTools::RemoveADirectory(this->SourceDir);      }    if (cmSystemTools::FileExists(this->BinaryDir.c_str()))      {      cmSystemTools::RemoveADirectory(this->BinaryDir);      }    // rename the src and binary directories    rename(this->BackupSourceDir.c_str(), this->SourceDir.c_str());    rename(this->BackupBinaryDir.c_str(), this->BinaryDir.c_str());    }}bool cmCTestScriptHandler::RunScript(cmCTest* ctest, const char *sname,                                     bool InProcess, int* returnValue){  cmCTestScriptHandler* sh = new cmCTestScriptHandler();  sh->SetCTestInstance(ctest);  sh->AddConfigurationScript(sname,InProcess);  int res = sh->ProcessHandler();  if(returnValue)    {    *returnValue = res;    }  delete sh;  return true;}bool cmCTestScriptHandler::EmptyBinaryDirectory(const char *sname){  // try to avoid deleting root  if (!sname || strlen(sname) < 2)    {    return false;    }  // consider non existing target directory a success  if(!cmSystemTools::FileExists(sname))    {    return true;    }  // try to avoid deleting directories that we shouldn't  std::string check = sname;  check += "/CMakeCache.txt";  if(!cmSystemTools::FileExists(check.c_str()))    {    return false;    }  for(int i = 0; i < 5; ++i)    {    if(TryToRemoveBinaryDirectoryOnce(sname))      {      return true;      }    cmSystemTools::Delay(100);    }  return false;}//-------------------------------------------------------------------------bool cmCTestScriptHandler::TryToRemoveBinaryDirectoryOnce(  const std::string& directoryPath){  cmsys::Directory directory;  directory.Load(directoryPath);  for(unsigned long i = 0; i < directory.GetNumberOfFiles(); ++i)    {    std::string path = directory.GetFile(i);    if(path == "." || path == ".." || path == "CMakeCache.txt")      {      continue;      }    std::string fullPath = directoryPath + std::string("/") + path;    bool isDirectory = cmSystemTools::FileIsDirectory(fullPath) &&      !cmSystemTools::FileIsSymlink(fullPath);    if(isDirectory)      {      if(!cmSystemTools::RemoveADirectory(fullPath))        {        return false;        }      }    else      {      if(!cmSystemTools::RemoveFile(fullPath))        {        return false;        }      }  }  return cmSystemTools::RemoveADirectory(directoryPath);}//-------------------------------------------------------------------------double cmCTestScriptHandler::GetRemainingTimeAllowed(){  if (!this->Makefile)    {    return 1.0e7;    }  const char *timelimitS    = this->Makefile->GetDefinition("CTEST_TIME_LIMIT");  if (!timelimitS)    {    return 1.0e7;    }  double timelimit = atof(timelimitS);  return timelimit - cmSystemTools::GetTime() + this->ScriptStartTime;}
 |