/*========================================================================= Program: CMake - Cross-Platform Makefile Generator Module: $RCSfile$ Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved. See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices 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 #include // used for sleep #ifdef _WIN32 #include "windows.h" #endif #include #include #include //---------------------------------------------------------------------- static const char* cmCTestUpdateHandlerUpdateStrings[] = { "Unknown", "CVS", "SVN" }; static const char* cmCTestUpdateHandlerUpdateToString(int type) { if ( type < cmCTestUpdateHandler::e_UNKNOWN || type >= cmCTestUpdateHandler::e_LAST ) { return cmCTestUpdateHandlerUpdateStrings[cmCTestUpdateHandler::e_UNKNOWN]; } return cmCTestUpdateHandlerUpdateStrings[type]; } //---------------------------------------------------------------------- //********************************************************************** class cmCTestUpdateHandlerSVNXMLParser : public cmXMLParser { public: struct t_CommitLog { int Revision; std::string Author; std::string Date; std::string Message; }; cmCTestUpdateHandlerSVNXMLParser(cmCTestUpdateHandler* up) : cmXMLParser(), UpdateHandler(up), MinRevision(-1), MaxRevision(-1) { } int Parse(const char* str) { this->MinRevision = -1; this->MaxRevision = -1; int res = this->cmXMLParser::Parse(str); if ( this->MinRevision == -1 || this->MaxRevision == -1 ) { return 0; } return res; } typedef std::vector t_VectorOfCommits; t_VectorOfCommits* GetCommits() { return &this->Commits; } int GetMinRevision() { return this->MinRevision; } int GetMaxRevision() { return this->MaxRevision; } protected: void StartElement(const char* name, const char** atts) { if ( strcmp(name, "logentry") == 0 ) { this->CommitLog = t_CommitLog(); const char* rev = this->FindAttribute(atts, "revision"); if ( rev) { this->CommitLog.Revision = atoi(rev); if ( this->MinRevision < 0 || this->MinRevision > this->CommitLog.Revision ) { this->MinRevision = this->CommitLog.Revision; } if ( this->MaxRevision < 0 || this->MaxRevision < this->CommitLog.Revision ) { this->MaxRevision = this->CommitLog.Revision; } } } this->CharacterData.erase( this->CharacterData.begin(), this->CharacterData.end()); } void EndElement(const char* name) { if ( strcmp(name, "logentry") == 0 ) { cmCTestLog(this->UpdateHandler->GetCTestInstance(), HANDLER_VERBOSE_OUTPUT, "\tRevision: " << this->CommitLog.Revision<< std::endl << "\tAuthor: " << this->CommitLog.Author.c_str() << std::endl << "\tDate: " << this->CommitLog.Date.c_str() << std::endl << "\tMessage: " << this->CommitLog.Message.c_str() << std::endl); this->Commits.push_back(this->CommitLog); } else if ( strcmp(name, "author") == 0 ) { this->CommitLog.Author.assign(&(*(this->CharacterData.begin())), this->CharacterData.size()); } else if ( strcmp(name, "date") == 0 ) { this->CommitLog.Date.assign(&(*(this->CharacterData.begin())), this->CharacterData.size()); } else if ( strcmp(name, "msg") == 0 ) { this->CommitLog.Message.assign(&(*(this->CharacterData.begin())), this->CharacterData.size()); } this->CharacterData.erase(this->CharacterData.begin(), this->CharacterData.end()); } void CharacterDataHandler(const char* data, int length) { this->CharacterData.insert(this->CharacterData.end(), data, data+length); } const char* FindAttribute( const char** atts, const char* attribute ) { if ( !atts || !attribute ) { return 0; } const char **atr = atts; while ( *atr && **atr && **(atr+1) ) { if ( strcmp(*atr, attribute) == 0 ) { return *(atr+1); } atr+=2; } return 0; } private: std::vector CharacterData; cmCTestUpdateHandler* UpdateHandler; t_CommitLog CommitLog; t_VectorOfCommits Commits; int MinRevision; int MaxRevision; }; //********************************************************************** //---------------------------------------------------------------------- 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(); } //---------------------------------------------------------------------- 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; } } 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; } } std::string sourceDirectory = this->GetOption("SourceDirectory"); cmCTestLog(this->CTest, DEBUG, "Check directory: " << sourceDirectory.c_str() << std::endl); sourceDirectory += "/.svn"; if ( cmSystemTools::FileExists(sourceDirectory.c_str()) ) { return cmCTestUpdateHandler::e_SVN; } sourceDirectory = this->GetOption("SourceDirectory"); sourceDirectory += "/CVS"; if ( cmSystemTools::FileExists(sourceDirectory.c_str()) ) { return cmCTestUpdateHandler::e_CVS; } return cmCTestUpdateHandler::e_UNKNOWN; } //---------------------------------------------------------------------- //clearly it would be nice if this were broken up into a few smaller //functions and commented... int cmCTestUpdateHandler::ProcessHandler() { int count = 0; int updateType = e_CVS; std::string::size_type cc, kk; bool updateProducedError = false; std::string goutput; std::string errors; // Make sure VCS tool messages are in English so we can parse them. cmCTestUpdateHandlerLocale fixLocale; static_cast(fixLocale); std::string checkoutErrorMessages; int retVal = 0; // 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" << std::endl); const char* initialCheckoutCommand = this->GetOption("InitialCheckout"); if ( initialCheckoutCommand ) { cmCTestLog(this->CTest, HANDLER_OUTPUT, " First perform the initial checkout: " << initialCheckoutCommand << std::endl); cmStdString parent = cmSystemTools::GetParentDirectory(sourceDirectory); if ( parent.empty() ) { cmCTestLog(this->CTest, ERROR_MESSAGE, "Something went wrong when trying " "to determine the parent directory of " << sourceDirectory << std::endl); return -1; } cmCTestLog(this->CTest, HANDLER_OUTPUT, " Perform checkout in directory: " << parent.c_str() << std::endl); if ( !cmSystemTools::MakeDirectory(parent.c_str()) ) { cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot create parent directory: " << parent.c_str() << " of the source directory: " << sourceDirectory << std::endl); return -1; } ofs << "* Run initial checkout" << std::endl; ofs << " Command: " << initialCheckoutCommand << std::endl; cmCTestLog(this->CTest, DEBUG, " Before: " << initialCheckoutCommand << std::endl); bool retic = this->CTest->RunCommand(initialCheckoutCommand, &goutput, &errors, &retVal, parent.c_str(), 0 /* Timeout */); cmCTestLog(this->CTest, DEBUG, " After: " << initialCheckoutCommand << std::endl); ofs << " Output: " << goutput.c_str() << std::endl; ofs << " Errors: " << errors.c_str() << std::endl; if ( !retic || retVal ) { cmOStringStream ostr; ostr << "Problem running initial checkout Output [" << goutput << "] Errors [" << errors << "]"; cmCTestLog(this->CTest, ERROR_MESSAGE, ostr.str().c_str() << std::endl); checkoutErrorMessages += ostr.str(); updateProducedError = true; } if(!this->CTest->InitializeFromCommand(this->Command)) { cmCTestLog(this->CTest, HANDLER_OUTPUT, " Fatal Error in initialize: " << std::endl); cmSystemTools::SetFatalErrorOccured(); return -1; } } cmCTestLog(this->CTest, HANDLER_OUTPUT, " Updating the repository: " << sourceDirectory << std::endl); // Get update command std::string updateCommand = this->CTest->GetCTestConfiguration("UpdateCommand"); if ( updateCommand.empty() ) { updateCommand = this->CTest->GetCTestConfiguration("CVSCommand"); if ( updateCommand.empty() ) { updateCommand = this->CTest->GetCTestConfiguration("SVNCommand"); if ( updateCommand.empty() ) { cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot find CVSCommand, SVNCommand, or UpdateCommand key in the " "DartConfiguration.tcl" << std::endl); return -1; } else { updateType = e_SVN; } } else { updateType = e_CVS; } } else { updateType = this->DetermineType(updateCommand.c_str(), this->CTest->GetCTestConfiguration("UpdateType").c_str()); } cmCTestLog(this->CTest, HANDLER_OUTPUT, " Use " << cmCTestUpdateHandlerUpdateToString(updateType) << " repository type" << std::endl;); // And update options std::string updateOptions = this->CTest->GetCTestConfiguration("UpdateOptions"); if ( updateOptions.empty() ) { switch (updateType) { case cmCTestUpdateHandler::e_CVS: updateOptions = this->CTest->GetCTestConfiguration("CVSUpdateOptions"); if ( updateOptions.empty() ) { updateOptions = "-dP"; } break; case cmCTestUpdateHandler::e_SVN: updateOptions = this->CTest->GetCTestConfiguration("SVNUpdateOptions"); break; } } // Get update time std::string extra_update_opts; if ( this->CTest->GetTestModel() == cmCTest::NIGHTLY ) { struct tm* t = this->CTest->GetNightlyTime( this->CTest->GetCTestConfiguration("NightlyStartTime"), this->CTest->GetTomorrowTag()); char current_time[1024]; sprintf(current_time, "%04d-%02d-%02d %02d:%02d:%02d", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); std::string today_update_date = current_time; // TODO: SVN switch ( updateType ) { case cmCTestUpdateHandler::e_CVS: extra_update_opts += "-D \"" + today_update_date +" UTC\""; break; case cmCTestUpdateHandler::e_SVN: extra_update_opts += "-r \"{" + today_update_date +" +0000}\""; break; } } bool res = true; // First, check what the current state of repository is std::string command = ""; switch( updateType ) { case cmCTestUpdateHandler::e_CVS: // TODO: CVS - for now just leave empty break; case cmCTestUpdateHandler::e_SVN: command = "\"" + updateCommand + "\" cleanup"; break; } // // Get initial repository information if that is possible. With subversion, // this will check the current revision. // if ( !command.empty() ) { cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "* Cleanup repository: " << command.c_str() << std::endl); if ( !this->CTest->GetShowOnly() ) { ofs << "* Cleanup repository" << std::endl; ofs << " Command: " << command.c_str() << std::endl; res = this->CTest->RunCommand(command.c_str(), &goutput, &errors, &retVal, sourceDirectory, 0 /*this->TimeOut*/); ofs << " Output: " << goutput.c_str() << std::endl; ofs << " Errors: " << errors.c_str() << std::endl; if ( ofs ) { ofs << "--- Cleanup ---" << std::endl; ofs << goutput << std::endl; } } else { cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Cleanup with command: " << command << std::endl); } } // First, check what the current state of repository is command = ""; switch( updateType ) { case cmCTestUpdateHandler::e_CVS: // TODO: CVS - for now just leave empty break; case cmCTestUpdateHandler::e_SVN: command = "\"" + updateCommand + "\" info"; break; } // CVS variables // SVN variables int svn_current_revision = 0; int svn_latest_revision = 0; int svn_use_status = 0; // // Get initial repository information if that is possible. With subversion, // this will check the current revision. // if ( !command.empty() ) { cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "* Get repository information: " << command.c_str() << std::endl); if ( !this->CTest->GetShowOnly() ) { ofs << "* Get repository information" << std::endl; ofs << " Command: " << command.c_str() << std::endl; res = this->CTest->RunCommand(command.c_str(), &goutput, &errors, &retVal, sourceDirectory, 0 /*this->TimeOut*/); ofs << " Output: " << goutput.c_str() << std::endl; ofs << " Errors: " << errors.c_str() << std::endl; if ( ofs ) { ofs << "--- Update information ---" << std::endl; ofs << goutput << std::endl; } switch ( updateType ) { case cmCTestUpdateHandler::e_CVS: // TODO: CVS - for now just leave empty break; case cmCTestUpdateHandler::e_SVN: { cmsys::RegularExpression current_revision_regex( "Revision: ([0-9]+)"); if ( current_revision_regex.find(goutput.c_str()) ) { std::string currentRevisionString = current_revision_regex.match(1); svn_current_revision = atoi(currentRevisionString.c_str()); cmCTestLog(this->CTest, HANDLER_OUTPUT, " Old revision of repository is: " << svn_current_revision << std::endl); } } break; } } else { cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Get information with command: " << command << std::endl); } } // // 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(cmSystemTools::GetTime()); double elapsed_time_start = cmSystemTools::GetTime(); cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "* Update repository: " << command.c_str() << std::endl); if ( !this->CTest->GetShowOnly() ) { command = ""; switch( updateType ) { case cmCTestUpdateHandler::e_CVS: command = "\"" + updateCommand + "\" -z3 update " + updateOptions + " " + extra_update_opts; ofs << "* Update repository: " << std::endl; ofs << " Command: " << command.c_str() << std::endl; res = this->CTest->RunCommand(command.c_str(), &goutput, &errors, &retVal, sourceDirectory, 0 /*this->TimeOut*/); ofs << " Output: " << goutput.c_str() << std::endl; ofs << " Errors: " << errors.c_str() << std::endl; break; case cmCTestUpdateHandler::e_SVN: { std::string partialOutput; command = "\"" + updateCommand + "\" update " + updateOptions + " " + extra_update_opts; ofs << "* Update repository: " << std::endl; ofs << " Command: " << command.c_str() << std::endl; bool res1 = this->CTest->RunCommand(command.c_str(), &partialOutput, &errors, &retVal, sourceDirectory, 0 /*this->TimeOut*/); ofs << " Output: " << partialOutput.c_str() << std::endl; ofs << " Errors: " << errors.c_str() << std::endl; goutput = partialOutput; command = "\"" + updateCommand + "\" status"; ofs << "* Status repository: " << std::endl; ofs << " Command: " << command.c_str() << std::endl; res = this->CTest->RunCommand(command.c_str(), &partialOutput, &errors, &retVal, sourceDirectory, 0 /*this->TimeOut*/); ofs << " Output: " << partialOutput.c_str() << std::endl; ofs << " Errors: " << errors.c_str() << std::endl; goutput += partialOutput; res = res && res1; ofs << " Total output of update: " << goutput.c_str() << std::endl; } } if ( ofs ) { ofs << "--- Update repository ---" << std::endl; ofs << goutput << std::endl; } } if ( !res || retVal ) { updateProducedError = true; checkoutErrorMessages += " " + goutput; } os << "\n" << "\n" << "\t" << this->CTest->GetCTestConfiguration("Site") << "\n" << "\t" << this->CTest->GetCTestConfiguration("BuildName") << "\n" << "\t" << this->CTest->GetCurrentTag() << "-" << this->CTest->GetTestModelString() << "" << std::endl; os << "\t" << start_time << "\n" << "\t" << start_time_time << "\n" << "\t" << cmXMLSafe(command) << "\n" << "\t" << cmXMLSafe( cmCTestUpdateHandlerUpdateToString(updateType)) << "\n"; // Even though it failed, we may have some useful information. Try to // continue... std::vector lines; cmSystemTools::Split(goutput.c_str(), lines); std::vector errLines; cmSystemTools::Split(errors.c_str(), errLines); lines.insert(lines.end(), errLines.begin(), errLines.end()); // CVS style regular expressions cmsys::RegularExpression cvs_date_author_regex( "^date: +([^;]+); +author: +([^;]+); +state: +[^;]+;"); cmsys::RegularExpression cvs_revision_regex("^revision +([^ ]*) *$"); cmsys::RegularExpression cvs_end_of_file_regex( "^==========================================" "===================================$"); cmsys::RegularExpression cvs_end_of_comment_regex( "^----------------------------$"); // Subversion style regular expressions cmsys::RegularExpression svn_status_line_regex( "^ *([0-9]+) *([0-9]+) *([^ ]+) *([^ ][^\t\r\n]*)[ \t\r\n]*$"); cmsys::RegularExpression svn_latest_revision_regex( "(Updated to|At) revision ([0-9]+)\\."); cmsys::RegularExpression file_removed_line( "cvs update: `?([^']*)'? is no longer in the repository"); cmsys::RegularExpression file_removed_line2( "cvs update: warning: `?([^']*)'? is not \\(any longer\\) pertinent"); cmsys::RegularExpression file_update_line("([A-Z]) *(.*)"); std::string current_path = ""; bool first_file = true; std::set author_set; int numUpdated = 0; int numModified = 0; int numConflicting = 0; // In subversion, get the latest revision if ( updateType == cmCTestUpdateHandler::e_SVN ) { for ( cc= 0; cc < lines.size(); cc ++ ) { const char* line = lines[cc].c_str(); if ( svn_latest_revision_regex.find(line) ) { svn_latest_revision = atoi( svn_latest_revision_regex.match(2).c_str()); } } if ( svn_latest_revision <= 0 ) { cmCTestLog(this->CTest, ERROR_MESSAGE, "Problem determining the current " "revision of the repository from output:" << std::endl << goutput.c_str() << std::endl); } else { cmCTestLog(this->CTest, HANDLER_OUTPUT, " Current revision of repository is: " << svn_latest_revision << std::endl); } } cmCTestLog(this->CTest, HANDLER_OUTPUT, " Gathering version information (each . represents one updated file):" << std::endl); int file_count = 0; std::string removed_line; for ( cc= 0; cc < lines.size(); cc ++ ) { const char* line = lines[cc].c_str(); if ( file_removed_line.find(line) ) { removed_line = "D " + file_removed_line.match(1); line = removed_line.c_str(); } else if ( file_removed_line2.find(line) ) { removed_line = "D " + file_removed_line2.match(1); line = removed_line.c_str(); } if ( file_update_line.find(line) ) { if ( file_count == 0 ) { cmCTestLog(this->CTest, HANDLER_OUTPUT, " " << std::flush); } cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush); std::string upChar = file_update_line.match(1); std::string upFile = file_update_line.match(2); char mod = upChar[0]; bool notLocallyModified = false; if ( mod == 'X' || mod == 'L') { continue; } if ( mod != 'M' && mod != 'C' && mod != 'G' ) { count ++; notLocallyModified = true; } const char* file = upFile.c_str(); cmCTestLog(this->CTest, DEBUG, "Line" << cc << ": " << mod << " - " << file << std::endl); std::string output; if ( notLocallyModified ) { std::string logcommand; switch ( updateType ) { case cmCTestUpdateHandler::e_CVS: logcommand = "\""+updateCommand+"\" -z3 log -N \"" + file + "\""; break; case cmCTestUpdateHandler::e_SVN: if ( svn_latest_revision > 0 && svn_latest_revision > svn_current_revision ) { cmOStringStream logCommandStream; logCommandStream << "\"" << updateCommand << "\" log -r " << svn_current_revision << ":" << svn_latest_revision << " --xml \"" << file << "\""; logcommand = logCommandStream.str(); } else { logcommand = "\"" + updateCommand + "\" status --verbose \"" + file + "\""; svn_use_status = 1; } break; } cmCTestLog(this->CTest, DEBUG, "Do log: " << logcommand << std::endl); cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "* Get file update information: " << logcommand.c_str() << std::endl); ofs << "* Get log information for file: " << file << std::endl; ofs << " Command: " << logcommand.c_str() << std::endl; res = this->CTest->RunCommand(logcommand.c_str(), &output, &errors, &retVal, sourceDirectory, 0 /*this->TimeOut*/); ofs << " Output: " << output.c_str() << std::endl; ofs << " Errors: " << errors.c_str() << std::endl; if ( ofs ) { ofs << output << std::endl; } } else { res = false; } if ( res ) { cmCTestLog(this->CTest, DEBUG, output << std::endl); std::string::size_type sline = 0; std::string srevision1 = "Unknown"; std::string sdate1 = "Unknown"; std::string sauthor1 = "Unknown"; std::string semail1 = "Unknown"; std::string comment1 = ""; std::string srevision2 = "Unknown"; if ( updateType == cmCTestUpdateHandler::e_CVS ) { bool have_first = false; bool have_second = false; std::vector ulines; cmSystemTools::Split(output.c_str(), ulines); for ( kk = 0; kk < ulines.size(); kk ++ ) { const char* clp = ulines[kk].c_str(); if ( !have_second && !sline && cvs_revision_regex.find(clp) ) { if ( !have_first ) { srevision1 = cvs_revision_regex.match(1); } else { srevision2 = cvs_revision_regex.match(1); } } else if ( !have_second && !sline && cvs_date_author_regex.find(clp) ) { sline = kk + 1; if ( !have_first ) { sdate1 = cvs_date_author_regex.match(1); sauthor1 = cvs_date_author_regex.match(2); } } else if ( sline && cvs_end_of_comment_regex.find(clp) || cvs_end_of_file_regex.find(clp)) { if ( !have_first ) { have_first = true; } else if ( !have_second ) { have_second = true; } sline = 0; } else if ( sline ) { if ( !have_first ) { comment1 += clp; comment1 += "\n"; } } } } else if ( updateType == cmCTestUpdateHandler::e_SVN ) { if ( svn_use_status ) { cmOStringStream str; str << svn_current_revision; srevision1 = str.str(); if (!svn_status_line_regex.find(output)) { cmCTestLog(this->CTest, ERROR_MESSAGE, "Bad output from SVN status command: " << output << std::endl); } else if ( svn_status_line_regex.match(4) != file ) { cmCTestLog(this->CTest, ERROR_MESSAGE, "Bad output from SVN status command. " "The file name returned: \"" << svn_status_line_regex.match(4) << "\" was different than the file specified: \"" << file << "\"" << std::endl); } else { srevision1 = svn_status_line_regex.match(2); int latest_revision = atoi( svn_status_line_regex.match(2).c_str()); if ( svn_current_revision < latest_revision ) { srevision2 = str.str(); } sauthor1 = svn_status_line_regex.match(3); } } else { cmCTestUpdateHandlerSVNXMLParser parser(this); if ( parser.Parse(output.c_str()) ) { int minrev = parser.GetMinRevision(); int maxrev = parser.GetMaxRevision(); cmCTestUpdateHandlerSVNXMLParser:: t_VectorOfCommits::iterator it; for ( it = parser.GetCommits()->begin(); it != parser.GetCommits()->end(); ++ it ) { if ( it->Revision == maxrev ) { cmOStringStream mRevStream; mRevStream << maxrev; srevision1 = mRevStream.str(); sauthor1 = it->Author; comment1 = it->Message; sdate1 = it->Date; } else if ( it->Revision == minrev ) { cmOStringStream mRevStream; mRevStream << minrev; srevision2 = mRevStream.str(); } } } } } if ( mod == 'M' ) { comment1 = "Locally modified file\n"; sauthor1 = "Local User"; } if ( mod == 'D' ) { comment1 += " - Removed file\n"; } if ( mod == 'C' ) { comment1 = "Conflict while updating\n"; sauthor1 = "Local User"; } std::string path = cmSystemTools::GetFilenamePath(file); std::string fname = cmSystemTools::GetFilenameName(file); if ( path != current_path ) { if ( !first_file ) { os << "\t" << std::endl; } else { first_file = false; } os << "\t\n" << "\t\t" << path << "" << std::endl; } if ( mod == 'C' ) { numConflicting ++; os << "\t" << std::endl; } else if ( mod == 'G' ) { numConflicting ++; os << "\t" << std::endl; } else if ( mod == 'M' ) { numModified ++; os << "\t" << std::endl; } else { numUpdated ++; os << "\t" << std::endl; } if ( srevision2 == "Unknown" ) { srevision2 = srevision1; } cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "File: " << path.c_str() << " / " << fname.c_str() << " was updated by " << sauthor1.c_str() << " to revision: " << srevision1.c_str() << " from revision: " << srevision2.c_str() << std::endl); os << "\t\t" << cmXMLSafe(fname) << "\n" << "\t\t" << cmXMLSafe(path) << "\n" << "\t\t" << cmXMLSafe(file) << "\n" << "\t\t" << cmXMLSafe(sdate1) << "\n" << "\t\t" << cmXMLSafe(sauthor1) << "\n" << "\t\t" << cmXMLSafe(semail1) << "\n" << "\t\t" << cmXMLSafe(comment1) << "\n" << "\t\t" << srevision1 << "\n" << "\t\t" << srevision2 << "" << std::endl; if ( mod == 'C' ) { os << "\t" << std::endl; } else if ( mod == 'G' ) { os << "\t" << std::endl; } else if ( mod == 'M' ) { os << "\t" << std::endl; } else { os << "\t" << std::endl; } author_set.insert(sauthor1); current_path = path; } file_count ++; } } if ( file_count ) { cmCTestLog(this->CTest, HANDLER_OUTPUT, std::endl); } if ( numUpdated ) { cmCTestLog(this->CTest, HANDLER_OUTPUT, " Found " << numUpdated << " updated files" << std::endl); } if ( numModified ) { cmCTestLog(this->CTest, HANDLER_OUTPUT, " Found " << numModified << " locally modified files" << std::endl); } if ( numConflicting ) { cmCTestLog(this->CTest, HANDLER_OUTPUT, " Found " << numConflicting << " conflicting files" << std::endl); } if ( numModified == 0 && numConflicting == 0 && numUpdated == 0 ) { cmCTestLog(this->CTest, HANDLER_OUTPUT, " Project is up-to-date" << std::endl); } if ( !first_file ) { os << "\t" << std::endl; } // TODO: Skip the author list when submitting to CDash. for(std::set::const_iterator ai = author_set.begin(); ai != author_set.end(); ++ai) { os << "\t" << cmXMLSafe(*ai) << "\n"; } cmCTestLog(this->CTest, DEBUG, "End" << std::endl); std::string end_time = this->CTest->CurrentTime(); os << "\t" << end_time << "\n" << "\t" << static_cast(cmSystemTools::GetTime()) << "\n" << "" << static_cast((cmSystemTools::GetTime() - elapsed_time_start)/6)/10.0 << "\n" << "\t"; if ( numModified > 0 || numConflicting > 0 ) { 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 ( updateProducedError ) { os << "Update error: "; os << cmXMLSafe(checkoutErrorMessages); cmCTestLog(this->CTest, ERROR_MESSAGE, " Update with command: " << command << " failed" << std::endl); } os << "" << std::endl; os << "" << std::endl; if (! res ) { cmCTestLog(this->CTest, ERROR_MESSAGE, "Error(s) when updating the project" << std::endl); cmCTestLog(this->CTest, ERROR_MESSAGE, "Output: " << goutput << std::endl); return -1; } return count; }