| /*============================================================================ |
| 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 "cmCTestUpdateHandler.h" |
| |
| #include "cmCTest.h" |
| #include "cmake.h" |
| #include "cmMakefile.h" |
| #include "cmLocalGenerator.h" |
| #include "cmGlobalGenerator.h" |
| #include "cmVersion.h" |
| #include "cmGeneratedFileStream.h" |
| #include "cmXMLParser.h" |
| #include "cmXMLSafe.h" |
| |
| #include "cmCTestVC.h" |
| #include "cmCTestCVS.h" |
| #include "cmCTestSVN.h" |
| #include "cmCTestBZR.h" |
| #include "cmCTestGIT.h" |
| #include "cmCTestHG.h" |
| |
| #include <cmsys/auto_ptr.hxx> |
| |
| //#include <cmsys/RegularExpression.hxx> |
| #include <cmsys/Process.h> |
| |
| // used for sleep |
| #ifdef _WIN32 |
| #include "windows.h" |
| #endif |
| |
| #include <stdlib.h> |
| #include <math.h> |
| #include <float.h> |
| |
| //---------------------------------------------------------------------- |
| static const char* cmCTestUpdateHandlerUpdateStrings[] = |
| { |
| "Unknown", |
| "CVS", |
| "SVN", |
| "BZR", |
| "GIT", |
| "HG" |
| }; |
| |
| static const char* cmCTestUpdateHandlerUpdateToString(int type) |
| { |
| if ( type < cmCTestUpdateHandler::e_UNKNOWN || |
| type >= cmCTestUpdateHandler::e_LAST ) |
| { |
| return cmCTestUpdateHandlerUpdateStrings[cmCTestUpdateHandler::e_UNKNOWN]; |
| } |
| return cmCTestUpdateHandlerUpdateStrings[type]; |
| } |
| |
| class cmCTestUpdateHandlerLocale |
| { |
| public: |
| cmCTestUpdateHandlerLocale(); |
| ~cmCTestUpdateHandlerLocale(); |
| private: |
| std::string saveLCMessages; |
| }; |
| |
| cmCTestUpdateHandlerLocale::cmCTestUpdateHandlerLocale() |
| { |
| const char* lcmess = cmSystemTools::GetEnv("LC_MESSAGES"); |
| if(lcmess) |
| { |
| saveLCMessages = lcmess; |
| } |
| // if LC_MESSAGES is not set to C, then |
| // set it, so that svn/cvs info will be in english ascii |
| if(! (lcmess && strcmp(lcmess, "C") == 0)) |
| { |
| cmSystemTools::PutEnv("LC_MESSAGES=C"); |
| } |
| } |
| |
| cmCTestUpdateHandlerLocale::~cmCTestUpdateHandlerLocale() |
| { |
| // restore the value of LC_MESSAGES after running the version control |
| // commands |
| if(saveLCMessages.size()) |
| { |
| std::string put = "LC_MESSAGES="; |
| put += saveLCMessages; |
| cmSystemTools::PutEnv(put.c_str()); |
| } |
| else |
| { |
| cmSystemTools::UnsetEnv("LC_MESSAGES"); |
| } |
| } |
| |
| //---------------------------------------------------------------------- |
| cmCTestUpdateHandler::cmCTestUpdateHandler() |
| { |
| } |
| |
| //---------------------------------------------------------------------- |
| void cmCTestUpdateHandler::Initialize() |
| { |
| this->Superclass::Initialize(); |
| this->UpdateCommand = ""; |
| this->UpdateType = e_CVS; |
| } |
| |
| //---------------------------------------------------------------------- |
| int cmCTestUpdateHandler::DetermineType(const char* cmd, const char* type) |
| { |
| cmCTestLog(this->CTest, DEBUG, "Determine update type from command: " << cmd |
| << " and type: " << type << std::endl); |
| if ( type && *type ) |
| { |
| cmCTestLog(this->CTest, DEBUG, "Type specified: " << type << std::endl); |
| std::string stype = cmSystemTools::LowerCase(type); |
| if ( stype.find("cvs") != std::string::npos ) |
| { |
| return cmCTestUpdateHandler::e_CVS; |
| } |
| if ( stype.find("svn") != std::string::npos ) |
| { |
| return cmCTestUpdateHandler::e_SVN; |
| } |
| if ( stype.find("bzr") != std::string::npos ) |
| { |
| return cmCTestUpdateHandler::e_BZR; |
| } |
| if ( stype.find("git") != std::string::npos ) |
| { |
| return cmCTestUpdateHandler::e_GIT; |
| } |
| if ( stype.find("hg") != std::string::npos ) |
| { |
| return cmCTestUpdateHandler::e_HG; |
| } |
| } |
| else |
| { |
| cmCTestLog(this->CTest, DEBUG, "Type not specified, check command: " |
| << cmd << std::endl); |
| std::string stype = cmSystemTools::LowerCase(cmd); |
| if ( stype.find("cvs") != std::string::npos ) |
| { |
| return cmCTestUpdateHandler::e_CVS; |
| } |
| if ( stype.find("svn") != std::string::npos ) |
| { |
| return cmCTestUpdateHandler::e_SVN; |
| } |
| if ( stype.find("bzr") != std::string::npos ) |
| { |
| return cmCTestUpdateHandler::e_BZR; |
| } |
| if ( stype.find("git") != std::string::npos ) |
| { |
| return cmCTestUpdateHandler::e_GIT; |
| } |
| if ( stype.find("hg") != std::string::npos ) |
| { |
| return cmCTestUpdateHandler::e_HG; |
| } |
| } |
| return cmCTestUpdateHandler::e_UNKNOWN; |
| } |
| |
| //---------------------------------------------------------------------- |
| //clearly it would be nice if this were broken up into a few smaller |
| //functions and commented... |
| int cmCTestUpdateHandler::ProcessHandler() |
| { |
| // Make sure VCS tool messages are in English so we can parse them. |
| cmCTestUpdateHandlerLocale fixLocale; |
| static_cast<void>(fixLocale); |
| |
| // Get source dir |
| const char* sourceDirectory = this->GetOption("SourceDirectory"); |
| if ( !sourceDirectory ) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, |
| "Cannot find SourceDirectory key in the DartConfiguration.tcl" |
| << std::endl); |
| return -1; |
| } |
| |
| cmGeneratedFileStream ofs; |
| if ( !this->CTest->GetShowOnly() ) |
| { |
| this->StartLogFile("Update", ofs); |
| } |
| |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, " Updating the repository: " |
| << sourceDirectory << std::endl); |
| |
| if(!this->SelectVCS()) |
| { |
| return -1; |
| } |
| |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, " Use " |
| << cmCTestUpdateHandlerUpdateToString(this->UpdateType) |
| << " repository type" |
| << std::endl;); |
| |
| // Create an object to interact with the VCS tool. |
| cmsys::auto_ptr<cmCTestVC> vc; |
| switch (this->UpdateType) |
| { |
| case e_CVS: vc.reset(new cmCTestCVS(this->CTest, ofs)); break; |
| case e_SVN: vc.reset(new cmCTestSVN(this->CTest, ofs)); break; |
| case e_BZR: vc.reset(new cmCTestBZR(this->CTest, ofs)); break; |
| case e_GIT: vc.reset(new cmCTestGIT(this->CTest, ofs)); break; |
| case e_HG: vc.reset(new cmCTestHG(this->CTest, ofs)); break; |
| default: vc.reset(new cmCTestVC(this->CTest, ofs)); break; |
| } |
| vc->SetCommandLineTool(this->UpdateCommand); |
| vc->SetSourceDirectory(sourceDirectory); |
| |
| // Cleanup the working tree. |
| vc->Cleanup(); |
| |
| // |
| // Now update repository and remember what files were updated |
| // |
| cmGeneratedFileStream os; |
| if(!this->StartResultingXML(cmCTest::PartUpdate, "Update", os)) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot open log file" |
| << std::endl); |
| return -1; |
| } |
| std::string start_time = this->CTest->CurrentTime(); |
| unsigned int start_time_time = |
| static_cast<unsigned int>(cmSystemTools::GetTime()); |
| double elapsed_time_start = cmSystemTools::GetTime(); |
| |
| bool updated = vc->Update(); |
| |
| os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" |
| << "<Update mode=\"Client\" Generator=\"ctest-" |
| << cmVersion::GetCMakeVersion() << "\">\n" |
| << "\t<Site>" << this->CTest->GetCTestConfiguration("Site") << "</Site>\n" |
| << "\t<BuildName>" << this->CTest->GetCTestConfiguration("BuildName") |
| << "</BuildName>\n" |
| << "\t<BuildStamp>" << this->CTest->GetCurrentTag() << "-" |
| << this->CTest->GetTestModelString() << "</BuildStamp>" << std::endl; |
| os << "\t<StartDateTime>" << start_time << "</StartDateTime>\n" |
| << "\t<StartTime>" << start_time_time << "</StartTime>\n" |
| << "\t<UpdateCommand>" |
| << cmXMLSafe(vc->GetUpdateCommandLine()).Quotes(false) |
| << "</UpdateCommand>\n" |
| << "\t<UpdateType>" << cmXMLSafe( |
| cmCTestUpdateHandlerUpdateToString(this->UpdateType)) |
| << "</UpdateType>\n"; |
| |
| vc->WriteXML(os); |
| |
| int localModifications = 0; |
| int numUpdated = vc->GetPathCount(cmCTestVC::PathUpdated); |
| if(numUpdated) |
| { |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, |
| " Found " << numUpdated << " updated files\n"); |
| } |
| if(int numModified = vc->GetPathCount(cmCTestVC::PathModified)) |
| { |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, |
| " Found " << numModified << " locally modified files\n"); |
| localModifications += numModified; |
| } |
| if(int numConflicting = vc->GetPathCount(cmCTestVC::PathConflicting)) |
| { |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, |
| " Found " << numConflicting << " conflicting files\n"); |
| localModifications += numConflicting; |
| } |
| |
| cmCTestLog(this->CTest, DEBUG, "End" << std::endl); |
| std::string end_time = this->CTest->CurrentTime(); |
| os << "\t<EndDateTime>" << end_time << "</EndDateTime>\n" |
| << "\t<EndTime>" << static_cast<unsigned int>(cmSystemTools::GetTime()) |
| << "</EndTime>\n" |
| << "<ElapsedMinutes>" << |
| static_cast<int>((cmSystemTools::GetTime() - elapsed_time_start)/6)/10.0 |
| << "</ElapsedMinutes>\n" |
| << "\t<UpdateReturnStatus>"; |
| if(localModifications) |
| { |
| os << "Update error: There are modified or conflicting files in the " |
| "repository"; |
| cmCTestLog(this->CTest, ERROR_MESSAGE, |
| " There are modified or conflicting files in the repository" |
| << std::endl); |
| } |
| if(!updated) |
| { |
| os << "Update command failed:\n" << vc->GetUpdateCommandLine(); |
| cmCTestLog(this->CTest, ERROR_MESSAGE, " Update command failed: " |
| << vc->GetUpdateCommandLine() << "\n"); |
| } |
| os << "</UpdateReturnStatus>" << std::endl; |
| os << "</Update>" << std::endl; |
| return numUpdated; |
| } |
| |
| //---------------------------------------------------------------------- |
| int cmCTestUpdateHandler::DetectVCS(const char* dir) |
| { |
| std::string sourceDirectory = dir; |
| cmCTestLog(this->CTest, DEBUG, "Check directory: " |
| << sourceDirectory.c_str() << std::endl); |
| sourceDirectory += "/.svn"; |
| if ( cmSystemTools::FileExists(sourceDirectory.c_str()) ) |
| { |
| return cmCTestUpdateHandler::e_SVN; |
| } |
| sourceDirectory = dir; |
| sourceDirectory += "/CVS"; |
| if ( cmSystemTools::FileExists(sourceDirectory.c_str()) ) |
| { |
| return cmCTestUpdateHandler::e_CVS; |
| } |
| sourceDirectory = dir; |
| sourceDirectory += "/.bzr"; |
| if ( cmSystemTools::FileExists(sourceDirectory.c_str()) ) |
| { |
| return cmCTestUpdateHandler::e_BZR; |
| } |
| sourceDirectory = dir; |
| sourceDirectory += "/.git"; |
| if ( cmSystemTools::FileExists(sourceDirectory.c_str()) ) |
| { |
| return cmCTestUpdateHandler::e_GIT; |
| } |
| sourceDirectory = dir; |
| sourceDirectory += "/.hg"; |
| if ( cmSystemTools::FileExists(sourceDirectory.c_str()) ) |
| { |
| return cmCTestUpdateHandler::e_HG; |
| } |
| return cmCTestUpdateHandler::e_UNKNOWN; |
| } |
| |
| //---------------------------------------------------------------------- |
| bool cmCTestUpdateHandler::SelectVCS() |
| { |
| // Get update command |
| this->UpdateCommand = this->CTest->GetCTestConfiguration("UpdateCommand"); |
| |
| // Detect the VCS managing the source tree. |
| this->UpdateType = this->DetectVCS(this->GetOption("SourceDirectory")); |
| if (this->UpdateType == e_UNKNOWN) |
| { |
| // The source tree does not have a recognized VCS. Check the |
| // configuration value or command name. |
| this->UpdateType = this->DetermineType(this->UpdateCommand.c_str(), |
| this->CTest->GetCTestConfiguration("UpdateType").c_str()); |
| } |
| |
| // If no update command was specified, lookup one for this VCS tool. |
| if (this->UpdateCommand.empty()) |
| { |
| const char* key = 0; |
| switch (this->UpdateType) |
| { |
| case e_CVS: key = "CVSCommand"; break; |
| case e_SVN: key = "SVNCommand"; break; |
| case e_BZR: key = "BZRCommand"; break; |
| case e_GIT: key = "GITCommand"; break; |
| case e_HG: key = "HGCommand"; break; |
| default: break; |
| } |
| if (key) |
| { |
| this->UpdateCommand = this->CTest->GetCTestConfiguration(key); |
| } |
| if (this->UpdateCommand.empty()) |
| { |
| cmOStringStream e; |
| e << "Cannot find UpdateCommand "; |
| if (key) |
| { |
| e << "or " << key; |
| } |
| e << " configuration key."; |
| cmCTestLog(this->CTest, ERROR_MESSAGE, e.str() << std::endl); |
| return false; |
| } |
| } |
| |
| return true; |
| } |