[Cmake-commits] [cmake-commits] king committed cmCTestCVS.cxx 1.1 1.2 cmCTestCVS.h 1.1 1.2 cmCTestSVN.cxx 1.4 1.5 cmCTestSVN.h 1.4 1.5 cmCTestUpdateHandler.cxx 1.61 1.62 cmCTestVC.cxx 1.6 1.7 cmCTestVC.h 1.5 1.6
cmake-commits at cmake.org
cmake-commits at cmake.org
Wed Feb 25 14:42:47 EST 2009
Update of /cvsroot/CMake/CMake/Source/CTest
In directory public:/mounts/ram/cvs-serv30309/Source/CTest
Modified Files:
cmCTestCVS.cxx cmCTestCVS.h cmCTestSVN.cxx cmCTestSVN.h
cmCTestUpdateHandler.cxx cmCTestVC.cxx cmCTestVC.h
Log Message:
ENH: Rewrite CTest Update implementation
This adds a new VCS update implementation to the cmCTestVC hierarchy and
removes it from cmCTestUpdateHandler. The new implementation has the
following advantages:
- Factorized implementation instead of monolithic function
- Logs vcs tool output as it is parsed (less memory, inline messages)
- Uses one global svn log instead of one log per file
- Reports changes on cvs branches (instead of latest trunk change)
- Generates simpler Update.xml (only one Directory element per dir)
Shared components of the new implementation appear in cmCTestVC and may
be re-used by subclasses for other VCS tools in the future.
Index: cmCTestSVN.h
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/CTest/cmCTestSVN.h,v
retrieving revision 1.4
retrieving revision 1.5
diff -C 2 -d -r1.4 -r1.5
*** cmCTestSVN.h 24 Feb 2009 20:43:25 -0000 1.4
--- cmCTestSVN.h 25 Feb 2009 19:42:45 -0000 1.5
***************
*** 32,37 ****
virtual ~cmCTestSVN();
- int GetOldRevision() { return atoi(this->OldRevision.c_str()); }
- int GetNewRevision() { return atoi(this->NewRevision.c_str()); }
private:
// Implement cmCTestVC internal API.
--- 32,35 ----
***************
*** 39,42 ****
--- 37,54 ----
virtual void NoteOldRevision();
virtual void NoteNewRevision();
+ virtual bool UpdateImpl();
+ virtual bool WriteXMLUpdates(std::ostream& xml);
+
+ /** Represent a subversion-reported action for one path in a revision. */
+ struct Change
+ {
+ char Action;
+ std::string Path;
+ Change(): Action('?') {}
+ };
+
+ // Update status for files in each directory.
+ class Directory: public std::map<cmStdString, File> {};
+ std::map<cmStdString, Directory> Dirs;
// Old and new repository revisions.
***************
*** 53,61 ****
--- 65,95 ----
std::string Base;
+ // Information known about old revision.
+ Revision PriorRev;
+
+ // Information about revisions from a svn log.
+ std::list<Revision> Revisions;
+
std::string LoadInfo();
+ void LoadModifications();
+ void LoadRevisions();
+
+ void GuessBase(std::vector<Change> const& changes);
+ const char* LocalPath(std::string const& path);
+
+ void DoRevision(Revision const& revision,
+ std::vector<Change> const& changes);
+ void WriteXMLDirectory(std::ostream& xml, std::string const& path,
+ Directory const& dir);
// Parsing helper classes.
class InfoParser;
+ class LogParser;
+ class StatusParser;
+ class UpdateParser;
friend class InfoParser;
+ friend class LogParser;
+ friend class StatusParser;
+ friend class UpdateParser;
};
Index: cmCTestSVN.cxx
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/CTest/cmCTestSVN.cxx,v
retrieving revision 1.4
retrieving revision 1.5
diff -C 2 -d -r1.4 -r1.5
*** cmCTestSVN.cxx 24 Feb 2009 20:43:25 -0000 1.4
--- cmCTestSVN.cxx 25 Feb 2009 19:42:45 -0000 1.5
***************
*** 18,21 ****
--- 18,24 ----
#include "cmCTest.h"
+ #include "cmSystemTools.h"
+ #include "cmXMLParser.h"
+ #include "cmXMLSafe.h"
#include <cmsys/RegularExpression.hxx>
***************
*** 24,27 ****
--- 27,31 ----
cmCTestSVN::cmCTestSVN(cmCTest* ct, std::ostream& log): cmCTestVC(ct, log)
{
+ this->PriorRev = this->Unknown;
}
***************
*** 115,118 ****
--- 119,123 ----
cmCTestLog(this->CTest, HANDLER_OUTPUT, " Old revision of repository is: "
<< this->OldRevision << "\n");
+ this->PriorRev.Rev = this->OldRevision;
}
***************
*** 125,128 ****
--- 130,134 ----
<< this->NewRevision << "\n");
+ // this->Root = ""; // uncomment to test GuessBase
this->Log << "URL = " << this->URL << "\n";
this->Log << "Root = " << this->Root << "\n";
***************
*** 137,138 ****
--- 143,528 ----
this->Log << "Base = " << this->Base << "\n";
}
+
+ //----------------------------------------------------------------------------
+ void cmCTestSVN::GuessBase(std::vector<Change> const& changes)
+ {
+ // Subversion did not give us a good repository root so we need to
+ // guess the base path from the URL and the paths in a revision with
+ // changes under it.
+
+ // Consider each possible URL suffix from longest to shortest.
+ for(std::string::size_type slash = this->URL.find('/');
+ this->Base.empty() && slash != std::string::npos;
+ slash = this->URL.find('/', slash+1))
+ {
+ // If the URL suffix is a prefix of at least one path then it is the base.
+ std::string base = cmCTest::DecodeURL(this->URL.substr(slash));
+ for(std::vector<Change>::const_iterator ci = changes.begin();
+ this->Base.empty() && ci != changes.end(); ++ci)
+ {
+ if(cmCTestSVNPathStarts(ci->Path, base))
+ {
+ this->Base = base;
+ }
+ }
+ }
+
+ // We always append a slash so that we know paths beginning in the
+ // base lie under its path. If no base was found then the working
+ // tree must be a checkout of the entire repo and this will match
+ // the leading slash in all paths.
+ this->Base += "/";
+
+ this->Log << "Guessed Base = " << this->Base << "\n";
+ }
+
+ //----------------------------------------------------------------------------
+ const char* cmCTestSVN::LocalPath(std::string const& path)
+ {
+ if(path.size() > this->Base.size() &&
+ strncmp(path.c_str(), this->Base.c_str(), this->Base.size()) == 0)
+ {
+ // This path lies under the base, so return a relative path.
+ return path.c_str() + this->Base.size();
+ }
+ else
+ {
+ // This path does not lie under the base, so ignore it.
+ return 0;
+ }
+ }
+
+ //----------------------------------------------------------------------------
+ class cmCTestSVN::UpdateParser: public cmCTestVC::LineParser
+ {
+ public:
+ UpdateParser(cmCTestSVN* svn, const char* prefix): SVN(svn)
+ {
+ this->SetLog(&svn->Log, prefix);
+ this->RegexUpdate.compile("^([ADUCGE ])([ADUCGE ])[B ] +(.+)$");
+ }
+ private:
+ cmCTestSVN* SVN;
+ cmsys::RegularExpression RegexUpdate;
+
+ bool ProcessLine()
+ {
+ if(this->RegexUpdate.find(this->Line))
+ {
+ this->DoPath(this->RegexUpdate.match(1)[0],
+ this->RegexUpdate.match(2)[0],
+ this->RegexUpdate.match(3));
+ }
+ return true;
+ }
+
+ void DoPath(char path_status, char prop_status, std::string const& path)
+ {
+ char status = (path_status != ' ')? path_status : prop_status;
+ std::string dir = cmSystemTools::GetFilenamePath(path);
+ std::string name = cmSystemTools::GetFilenameName(path);
+ // See "svn help update".
+ switch(status)
+ {
+ case 'G':
+ this->SVN->Dirs[dir][name].Status = PathModified;
+ break;
+ case 'C':
+ this->SVN->Dirs[dir][name].Status = PathConflicting;
+ break;
+ case 'A': case 'D': case 'U':
+ this->SVN->Dirs[dir][name].Status = PathUpdated;
+ break;
+ case 'E': // TODO?
+ case '?': case ' ': default:
+ break;
+ }
+ }
+ };
+
+ //----------------------------------------------------------------------------
+ bool cmCTestSVN::UpdateImpl()
+ {
+ // Get user-specified update options.
+ std::string opts = this->CTest->GetCTestConfiguration("UpdateOptions");
+ if(opts.empty())
+ {
+ opts = this->CTest->GetCTestConfiguration("SVNUpdateOptions");
+ }
+ std::vector<cmStdString> args = cmSystemTools::ParseArguments(opts.c_str());
+
+ // Specify the start time for nightly testing.
+ if(this->CTest->GetTestModel() == cmCTest::NIGHTLY)
+ {
+ args.push_back("-r{" + this->GetNightlyTime() + " +0000}");
+ }
+
+ std::vector<char const*> svn_update;
+ svn_update.push_back(this->CommandLineTool.c_str());
+ svn_update.push_back("update");
+ svn_update.push_back("--non-interactive");
+ for(std::vector<cmStdString>::const_iterator ai = args.begin();
+ ai != args.end(); ++ai)
+ {
+ svn_update.push_back(ai->c_str());
+ }
+ svn_update.push_back(0);
+
+ UpdateParser out(this, "up-out> ");
+ OutputLogger err(this->Log, "up-err> ");
+ return this->RunUpdateCommand(&svn_update[0], &out, &err);
+ }
+
+ //----------------------------------------------------------------------------
+ class cmCTestSVN::LogParser: public OutputLogger, private cmXMLParser
+ {
+ public:
+ LogParser(cmCTestSVN* svn, const char* prefix):
+ OutputLogger(svn->Log, prefix), SVN(svn) { this->InitializeParser(); }
+ ~LogParser() { this->CleanupParser(); }
+ private:
+ cmCTestSVN* SVN;
+
+ typedef cmCTestSVN::Revision Revision;
+ typedef cmCTestSVN::Change Change;
+ Revision Rev;
+ std::vector<Change> Changes;
+ Change CurChange;
+ std::vector<char> CData;
+
+ virtual bool ProcessChunk(const char* data, int length)
+ {
+ this->OutputLogger::ProcessChunk(data, length);
+ this->ParseChunk(data, length);
+ return true;
+ }
+
+ virtual void StartElement(const char* name, const char** atts)
+ {
+ this->CData.clear();
+ if(strcmp(name, "logentry") == 0)
+ {
+ this->Rev = Revision();
+ if(const char* rev = this->FindAttribute(atts, "revision"))
+ {
+ this->Rev.Rev = rev;
+ }
+ this->Changes.clear();
+ }
+ else if(strcmp(name, "path") == 0)
+ {
+ this->CurChange = Change();
+ if(const char* action = this->FindAttribute(atts, "action"))
+ {
+ this->CurChange.Action = action[0];
+ }
+ }
+ }
+
+ virtual void CharacterDataHandler(const char* data, int length)
+ {
+ this->CData.insert(this->CData.end(), data, data+length);
+ }
+
+ virtual void EndElement(const char* name)
+ {
+ if(strcmp(name, "logentry") == 0)
+ {
+ this->SVN->DoRevision(this->Rev, this->Changes);
+ }
+ else if(strcmp(name, "path") == 0 && !this->CData.empty())
+ {
+ this->CurChange.Path.assign(&this->CData[0], this->CData.size());
+ this->Changes.push_back(this->CurChange);
+ }
+ else if(strcmp(name, "author") == 0 && !this->CData.empty())
+ {
+ this->Rev.Author.assign(&this->CData[0], this->CData.size());
+ }
+ else if(strcmp(name, "date") == 0 && !this->CData.empty())
+ {
+ this->Rev.Date.assign(&this->CData[0], this->CData.size());
+ }
+ else if(strcmp(name, "msg") == 0 && !this->CData.empty())
+ {
+ this->Rev.Log.assign(&this->CData[0], this->CData.size());
+ }
+ this->CData.clear();
+ }
+
+ virtual void ReportError(int, int, const char* msg)
+ {
+ this->SVN->Log << "Error parsing svn log xml: " << msg << "\n";
+ }
+ };
+
+ //----------------------------------------------------------------------------
+ void cmCTestSVN::LoadRevisions()
+ {
+ cmCTestLog(this->CTest, HANDLER_OUTPUT,
+ " Gathering version information (one . per revision):\n"
+ " " << std::flush);
+
+ // We are interested in every revision included in the update.
+ std::string revs;
+ if(atoi(this->OldRevision.c_str()) < atoi(this->NewRevision.c_str()))
+ {
+ revs = "-r" + this->OldRevision + ":" + this->NewRevision;
+ }
+ else
+ {
+ revs = "-r" + this->NewRevision;
+ }
+
+ // Run "svn log" to get all global revisions of interest.
+ const char* svn = this->CommandLineTool.c_str();
+ const char* svn_log[] = {svn, "log", "--xml", "-v", revs.c_str(), 0};
+ {
+ LogParser out(this, "log-out> ");
+ OutputLogger err(this->Log, "log-err> ");
+ this->RunChild(svn_log, &out, &err);
+ }
+ cmCTestLog(this->CTest, HANDLER_OUTPUT, std::endl);
+ }
+
+ //----------------------------------------------------------------------------
+ void cmCTestSVN::DoRevision(Revision const& revision,
+ std::vector<Change> const& changes)
+ {
+ // Guess the base checkout path from the changes if necessary.
+ if(this->Base.empty() && !changes.empty())
+ {
+ this->GuessBase(changes);
+ }
+
+ // Indicate we found a revision.
+ cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush);
+
+ // Ignore changes in the old revision.
+ if(revision.Rev == this->OldRevision)
+ {
+ this->PriorRev = revision;
+ return;
+ }
+
+ // Store the revision.
+ this->Revisions.push_back(revision);
+
+ // Report this revision.
+ Revision const& rev = this->Revisions.back();
+ this->Log << "Found revision " << rev.Rev << "\n"
+ << " author = " << rev.Author << "\n"
+ << " date = " << rev.Date << "\n";
+
+ // Update information about revisions of the changed files.
+ for(std::vector<Change>::const_iterator ci = changes.begin();
+ ci != changes.end(); ++ci)
+ {
+ if(const char* local = this->LocalPath(ci->Path))
+ {
+ std::string dir = cmSystemTools::GetFilenamePath(local);
+ std::string name = cmSystemTools::GetFilenameName(local);
+ File& file = this->Dirs[dir][name];
+ file.PriorRev = file.Rev? file.Rev : &this->PriorRev;
+ file.Rev = &rev;
+ this->Log << " " << ci->Action << " " << local << " " << "\n";
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------------
+ class cmCTestSVN::StatusParser: public cmCTestVC::LineParser
+ {
+ public:
+ StatusParser(cmCTestSVN* svn, const char* prefix): SVN(svn)
+ {
+ this->SetLog(&svn->Log, prefix);
+ this->RegexStatus.compile("^([ACDIMRX?!~ ])([CM ])[ L]... +(.+)$");
+ }
+ private:
+ cmCTestSVN* SVN;
+ cmsys::RegularExpression RegexStatus;
+ bool ProcessLine()
+ {
+ if(this->RegexStatus.find(this->Line))
+ {
+ this->DoPath(this->RegexStatus.match(1)[0],
+ this->RegexStatus.match(2)[0],
+ this->RegexStatus.match(3));
+ }
+ return true;
+ }
+
+ void DoPath(char path_status, char prop_status, std::string const& path)
+ {
+ char status = (path_status != ' ')? path_status : prop_status;
+ // See "svn help status".
+ switch(status)
+ {
+ case 'M': case '!': case 'A': case 'D': case 'R': case 'X':
+ this->DoPath(PathModified, path);
+ break;
+ case 'C': case '~':
+ this->DoPath(PathConflicting, path);
+ break;
+ case 'I': case '?': case ' ': default:
+ break;
+ }
+ }
+
+ void DoPath(PathStatus status, std::string const& path)
+ {
+ std::string dir = cmSystemTools::GetFilenamePath(path);
+ std::string name = cmSystemTools::GetFilenameName(path);
+ File& file = this->SVN->Dirs[dir][name];
+ file.Status = status;
+ // For local modifications the current rev is unknown and the
+ // prior rev is the latest from svn.
+ if(!file.Rev && !file.PriorRev)
+ {
+ file.PriorRev = &this->SVN->PriorRev;
+ }
+ }
+ };
+
+ //----------------------------------------------------------------------------
+ void cmCTestSVN::LoadModifications()
+ {
+ // Run "svn status" which reports local modifications.
+ const char* svn = this->CommandLineTool.c_str();
+ const char* svn_status[] = {svn, "status", "--non-interactive", 0};
+ StatusParser out(this, "status-out> ");
+ OutputLogger err(this->Log, "status-err> ");
+ this->RunChild(svn_status, &out, &err);
+ }
+
+ //----------------------------------------------------------------------------
+ void cmCTestSVN::WriteXMLDirectory(std::ostream& xml,
+ std::string const& path,
+ Directory const& dir)
+ {
+ const char* slash = path.empty()? "":"/";
+ xml << "\t<Directory>\n"
+ << "\t\t<Name>" << cmXMLSafe(path) << "</Name>\n";
+ for(Directory::const_iterator fi = dir.begin(); fi != dir.end(); ++fi)
+ {
+ std::string full = path + slash + fi->first;
+ this->WriteXMLEntry(xml, path, fi->first, full, fi->second);
+ }
+ xml << "\t</Directory>\n";
+ }
+
+ //----------------------------------------------------------------------------
+ bool cmCTestSVN::WriteXMLUpdates(std::ostream& xml)
+ {
+ this->LoadRevisions();
+ this->LoadModifications();
+
+ for(std::map<cmStdString, Directory>::const_iterator
+ di = this->Dirs.begin(); di != this->Dirs.end(); ++di)
+ {
+ this->WriteXMLDirectory(xml, di->first, di->second);
+ }
+
+ return true;
+ }
Index: cmCTestCVS.cxx
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/CTest/cmCTestCVS.cxx,v
retrieving revision 1.1
retrieving revision 1.2
diff -C 2 -d -r1.1 -r1.2
*** cmCTestCVS.cxx 24 Feb 2009 15:39:54 -0000 1.1
--- cmCTestCVS.cxx 25 Feb 2009 19:42:45 -0000 1.2
***************
*** 17,20 ****
--- 17,26 ----
#include "cmCTestCVS.h"
+ #include "cmCTest.h"
+ #include "cmSystemTools.h"
+ #include "cmXMLSafe.h"
+
+ #include <cmsys/RegularExpression.hxx>
+
//----------------------------------------------------------------------------
cmCTestCVS::cmCTestCVS(cmCTest* ct, std::ostream& log): cmCTestVC(ct, log)
***************
*** 26,27 ****
--- 32,323 ----
{
}
+
+ //----------------------------------------------------------------------------
+ class cmCTestCVS::UpdateParser: public cmCTestVC::LineParser
+ {
+ public:
+ UpdateParser(cmCTestCVS* cvs, const char* prefix): CVS(cvs)
+ {
+ this->SetLog(&cvs->Log, prefix);
+ // See "man cvs", section "update output".
+ this->RegexFileUpdated.compile("^([UP]) *(.*)");
+ this->RegexFileModified.compile("^([MRA]) *(.*)");
+ this->RegexFileConflicting.compile("^([C]) *(.*)");
+ this->RegexFileRemoved1.compile(
+ "cvs update: `?([^']*)'? is no longer in the repository");
+ this->RegexFileRemoved2.compile(
+ "cvs update: warning: `?([^']*)'? is not \\(any longer\\) pertinent");
+ }
+ private:
+ cmCTestCVS* CVS;
+ cmsys::RegularExpression RegexFileUpdated;
+ cmsys::RegularExpression RegexFileModified;
+ cmsys::RegularExpression RegexFileConflicting;
+ cmsys::RegularExpression RegexFileRemoved1;
+ cmsys::RegularExpression RegexFileRemoved2;
+
+ virtual bool ProcessLine()
+ {
+ if(this->RegexFileUpdated.find(this->Line))
+ {
+ this->DoFile(PathUpdated, this->RegexFileUpdated.match(2));
+ }
+ else if(this->RegexFileModified.find(this->Line))
+ {
+ this->DoFile(PathModified, this->RegexFileModified.match(2));
+ }
+ else if(this->RegexFileConflicting.find(this->Line))
+ {
+ this->DoFile(PathConflicting, this->RegexFileConflicting.match(2));
+ }
+ else if(this->RegexFileRemoved1.find(this->Line))
+ {
+ this->DoFile(PathUpdated, this->RegexFileRemoved1.match(1));
+ }
+ else if(this->RegexFileRemoved2.find(this->Line))
+ {
+ this->DoFile(PathUpdated, this->RegexFileRemoved2.match(1));
+ }
+ return true;
+ }
+
+ void DoFile(PathStatus status, std::string const& file)
+ {
+ std::string dir = cmSystemTools::GetFilenamePath(file);
+ std::string name = cmSystemTools::GetFilenameName(file);
+ this->CVS->Dirs[dir][name] = status;
+ }
+ };
+
+ //----------------------------------------------------------------------------
+ bool cmCTestCVS::UpdateImpl()
+ {
+ // Get user-specified update options.
+ std::string opts = this->CTest->GetCTestConfiguration("UpdateOptions");
+ if(opts.empty())
+ {
+ opts = this->CTest->GetCTestConfiguration("CVSUpdateOptions");
+ if(opts.empty())
+ {
+ opts = "-dP";
+ }
+ }
+ std::vector<cmStdString> args = cmSystemTools::ParseArguments(opts.c_str());
+
+ // Specify the start time for nightly testing.
+ if(this->CTest->GetTestModel() == cmCTest::NIGHTLY)
+ {
+ args.push_back("-D" + this->GetNightlyTime() + " UTC");
+ }
+
+ // Run "cvs update" to update the work tree.
+ std::vector<char const*> cvs_update;
+ cvs_update.push_back(this->CommandLineTool.c_str());
+ cvs_update.push_back("-z3");
+ cvs_update.push_back("update");
+ for(std::vector<cmStdString>::const_iterator ai = args.begin();
+ ai != args.end(); ++ai)
+ {
+ cvs_update.push_back(ai->c_str());
+ }
+ cvs_update.push_back(0);
+
+ UpdateParser out(this, "up-out> ");
+ UpdateParser err(this, "up-err> ");
+ return this->RunUpdateCommand(&cvs_update[0], &out, &err);
+ }
+
+ //----------------------------------------------------------------------------
+ class cmCTestCVS::LogParser: public cmCTestVC::LineParser
+ {
+ public:
+ typedef cmCTestCVS::Revision Revision;
+ LogParser(cmCTestCVS* cvs, const char* prefix, std::vector<Revision>& revs):
+ CVS(cvs), Revisions(revs), Section(SectionHeader)
+ {
+ this->SetLog(&cvs->Log, prefix),
+ this->RegexRevision.compile("^revision +([^ ]*) *$");
+ this->RegexBranches.compile("^branches: .*$");
+ this->RegexPerson.compile("^date: +([^;]+); +author: +([^;]+);");
+ }
+ private:
+ cmCTestCVS* CVS;
+ std::vector<Revision>& Revisions;
+ cmsys::RegularExpression RegexRevision;
+ cmsys::RegularExpression RegexBranches;
+ cmsys::RegularExpression RegexPerson;
+ enum SectionType { SectionHeader, SectionRevisions, SectionEnd };
+ SectionType Section;
+ Revision Rev;
+
+ virtual bool ProcessLine()
+ {
+ if(this->Line == ("======================================="
+ "======================================"))
+ {
+ // This line ends the revision list.
+ if(this->Section == SectionRevisions)
+ {
+ this->FinishRevision();
+ }
+ this->Section = SectionEnd;
+ }
+ else if(this->Line == "----------------------------")
+ {
+ // This line divides revisions from the header and each other.
+ if(this->Section == SectionHeader)
+ {
+ this->Section = SectionRevisions;
+ }
+ else if(this->Section == SectionRevisions)
+ {
+ this->FinishRevision();
+ }
+ }
+ else if(this->Section == SectionRevisions)
+ {
+ if(!this->Rev.Log.empty())
+ {
+ // Continue the existing log.
+ this->Rev.Log += this->Line;
+ this->Rev.Log += "\n";
+ }
+ else if(this->Rev.Rev.empty() && this->RegexRevision.find(this->Line))
+ {
+ this->Rev.Rev = this->RegexRevision.match(1);
+ }
+ else if(this->Rev.Date.empty() && this->RegexPerson.find(this->Line))
+ {
+ this->Rev.Date = this->RegexPerson.match(1);
+ this->Rev.Author = this->RegexPerson.match(2);
+ }
+ else if(!this->RegexBranches.find(this->Line))
+ {
+ // Start the log.
+ this->Rev.Log += this->Line;
+ this->Rev.Log += "\n";
+ }
+ }
+ return this->Section != SectionEnd;
+ }
+
+ void FinishRevision()
+ {
+ if(!this->Rev.Rev.empty())
+ {
+ // Record this revision.
+ this->CVS->Log << "Found revision " << this->Rev.Rev << "\n"
+ << " author = " << this->Rev.Author << "\n"
+ << " date = " << this->Rev.Date << "\n";
+ this->Revisions.push_back(this->Rev);
+
+ // We only need two revisions.
+ if(this->Revisions.size() >= 2)
+ {
+ this->Section = SectionEnd;
+ }
+ }
+ this->Rev = Revision();
+ }
+ };
+
+ //----------------------------------------------------------------------------
+ std::string cmCTestCVS::ComputeBranchFlag(std::string const& dir)
+ {
+ // Compute the tag file location for this directory.
+ std::string tagFile = this->SourceDirectory;
+ if(!dir.empty())
+ {
+ tagFile += "/";
+ tagFile += dir;
+ }
+ tagFile += "/CVS/Tag";
+
+ // Lookup the branch in the tag file, if any.
+ std::string tagLine;
+ std::ifstream tagStream(tagFile.c_str());
+ if(cmSystemTools::GetLineFromStream(tagStream, tagLine) &&
+ tagLine.size() > 1 && tagLine[0] == 'T')
+ {
+ // Use the branch specified in the tag file.
+ std::string flag = "-r";
+ flag += tagLine.substr(1);
+ return flag;
+ }
+ else
+ {
+ // Use the default branch.
+ return "-b";
+ }
+ }
+
+ //----------------------------------------------------------------------------
+ void cmCTestCVS::LoadRevisions(std::string const& file,
+ const char* branchFlag,
+ std::vector<Revision>& revisions)
+ {
+ cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush);
+
+ // Run "cvs log" to get revisions of this file on this branch.
+ const char* cvs = this->CommandLineTool.c_str();
+ const char* cvs_log[] =
+ {cvs, "log", "-N", "-d<now", branchFlag, file.c_str(), 0};
+
+ LogParser out(this, "log-out> ", revisions);
+ OutputLogger err(this->Log, "log-err> ");
+ this->RunChild(cvs_log, &out, &err);
+ }
+
+ //----------------------------------------------------------------------------
+ void cmCTestCVS::WriteXMLDirectory(std::ostream& xml,
+ std::string const& path,
+ Directory const& dir)
+ {
+ const char* slash = path.empty()? "":"/";
+ xml << "\t<Directory>\n"
+ << "\t\t<Name>" << cmXMLSafe(path) << "</Name>\n";
+
+ // Lookup the branch checked out in the working tree.
+ std::string branchFlag = this->ComputeBranchFlag(path);
+
+ // Load revisions and write an entry for each file in this directory.
+ std::vector<Revision> revisions;
+ for(Directory::const_iterator fi = dir.begin(); fi != dir.end(); ++fi)
+ {
+ std::string full = path + slash + fi->first;
+
+ // Load two real or unknown revisions.
+ revisions.clear();
+ if(fi->second != PathUpdated)
+ {
+ // For local modifications the current rev is unknown and the
+ // prior rev is the latest from cvs.
+ revisions.push_back(this->Unknown);
+ }
+ this->LoadRevisions(full, branchFlag.c_str(), revisions);
+ revisions.resize(2, this->Unknown);
+
+ // Write the entry for this file with these revisions.
+ File f(fi->second, &revisions[0], &revisions[1]);
+ this->WriteXMLEntry(xml, path, fi->first, full, f);
+ }
+ xml << "\t</Directory>\n";
+ }
+
+ //----------------------------------------------------------------------------
+ bool cmCTestCVS::WriteXMLUpdates(std::ostream& xml)
+ {
+ cmCTestLog(this->CTest, HANDLER_OUTPUT,
+ " Gathering version information (one . per updated file):\n"
+ " " << std::flush);
+
+ for(std::map<cmStdString, Directory>::const_iterator
+ di = this->Dirs.begin(); di != this->Dirs.end(); ++di)
+ {
+ this->WriteXMLDirectory(xml, di->first, di->second);
+ }
+
+ cmCTestLog(this->CTest, HANDLER_OUTPUT, std::endl);
+
+ return true;
+ }
Index: cmCTestVC.cxx
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/CTest/cmCTestVC.cxx,v
retrieving revision 1.6
retrieving revision 1.7
diff -C 2 -d -r1.6 -r1.7
*** cmCTestVC.cxx 25 Feb 2009 14:20:26 -0000 1.6
--- cmCTestVC.cxx 25 Feb 2009 19:42:45 -0000 1.7
***************
*** 18,21 ****
--- 18,22 ----
#include "cmCTest.h"
+ #include "cmXMLSafe.h"
#include <cmsys/Process.h>
***************
*** 24,27 ****
--- 25,34 ----
cmCTestVC::cmCTestVC(cmCTest* ct, std::ostream& log): CTest(ct), Log(log)
{
+ this->PathCount[PathUpdated] = 0;
+ this->PathCount[PathModified] = 0;
+ this->PathCount[PathConflicting] = 0;
+ this->Unknown.Date = "Unknown";
+ this->Unknown.Author = "Unknown";
+ this->Unknown.Rev = "Unknown";
}
***************
*** 73,76 ****
--- 80,99 ----
//----------------------------------------------------------------------------
+ bool cmCTestVC::RunUpdateCommand(char const* const* cmd,
+ OutputParser* out, OutputParser* err)
+ {
+ // Report the command line.
+ this->UpdateCommandLine = this->ComputeCommandLine(cmd);
+ if(this->CTest->GetShowOnly())
+ {
+ this->Log << this->UpdateCommandLine << "\n";
+ return true;
+ }
+
+ // Run the command.
+ return this->RunChild(cmd, out, err);
+ }
+
+ //----------------------------------------------------------------------------
std::string cmCTestVC::GetNightlyTime()
{
***************
*** 105,108 ****
--- 128,142 ----
//----------------------------------------------------------------------------
+ bool cmCTestVC::Update()
+ {
+ this->NoteOldRevision();
+ this->Log << "--- Begin Update ---\n";
+ bool result = this->UpdateImpl();
+ this->Log << "--- End Update ---\n";
+ this->NoteNewRevision();
+ return result;
+ }
+
+ //----------------------------------------------------------------------------
void cmCTestVC::NoteOldRevision()
{
***************
*** 115,116 ****
--- 149,198 ----
// We do nothing by default.
}
+
+ //----------------------------------------------------------------------------
+ bool cmCTestVC::UpdateImpl()
+ {
+ cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
+ "* Unknown VCS tool, not updating!" << std::endl);
+ return true;
+ }
+
+ //----------------------------------------------------------------------------
+ bool cmCTestVC::WriteXML(std::ostream& xml)
+ {
+ this->Log << "--- Begin Revisions ---\n";
+ bool result = this->WriteXMLUpdates(xml);
+ this->Log << "--- End Revisions ---\n";
+ return result;
+ }
+
+ //----------------------------------------------------------------------------
+ bool cmCTestVC::WriteXMLUpdates(std::ostream&)
+ {
+ cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
+ "* CTest cannot extract updates for this VCS tool.\n");
+ return true;
+ }
+
+ //----------------------------------------------------------------------------
+ void cmCTestVC::WriteXMLEntry(std::ostream& xml,
+ std::string const& path,
+ std::string const& name,
+ std::string const& full,
+ File const& f)
+ {
+ static const char* desc[3] = { "Updated", "Modified", "Conflicting"};
+ Revision const& rev = f.Rev? *f.Rev : this->Unknown;
+ std::string prior = f.PriorRev? f.PriorRev->Rev : std::string("Unknown");
+ xml << "\t\t<" << desc[f.Status] << ">\n"
+ << "\t\t\t<File>" << cmXMLSafe(name) << "</File>\n"
+ << "\t\t\t<Directory>" << cmXMLSafe(path) << "</Directory>\n"
+ << "\t\t\t<FullName>" << cmXMLSafe(full) << "</FullName>\n"
+ << "\t\t\t<CheckinDate>" << cmXMLSafe(rev.Date) << "</CheckinDate>\n"
+ << "\t\t\t<Author>" << cmXMLSafe(rev.Author) << "</Author>\n"
+ << "\t\t\t<Log>" << cmXMLSafe(rev.Log) << "</Log>\n"
+ << "\t\t\t<Revision>" << cmXMLSafe(rev.Rev) << "</Revision>\n"
+ << "\t\t\t<PriorRevision>" << cmXMLSafe(prior) << "</PriorRevision>\n"
+ << "\t\t</" << desc[f.Status] << ">\n";
+ ++this->PathCount[f.Status];
+ }
Index: cmCTestUpdateHandler.cxx
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/CTest/cmCTestUpdateHandler.cxx,v
retrieving revision 1.61
retrieving revision 1.62
diff -C 2 -d -r1.61 -r1.62
*** cmCTestUpdateHandler.cxx 24 Feb 2009 17:52:36 -0000 1.61
--- cmCTestUpdateHandler.cxx 25 Feb 2009 19:42:45 -0000 1.62
***************
*** 64,191 ****
}
- //----------------------------------------------------------------------
- //**********************************************************************
- 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_CommitLog> 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<char> CharacterData;
- cmCTestUpdateHandler* UpdateHandler;
- t_CommitLog CommitLog;
-
- t_VectorOfCommits Commits;
- int MinRevision;
- int MaxRevision;
- };
- //**********************************************************************
- //----------------------------------------------------------------------
-
class cmCTestUpdateHandlerLocale
{
--- 64,67 ----
***************
*** 281,295 ****
int cmCTestUpdateHandler::ProcessHandler()
{
- int count = 0;
- std::string::size_type cc, kk;
- std::string goutput;
- std::string errors;
-
// Make sure VCS tool messages are in English so we can parse them.
cmCTestUpdateHandlerLocale fixLocale;
static_cast<void>(fixLocale);
- int retVal = 0;
-
// Get source dir
const char* sourceDirectory = this->GetOption("SourceDirectory");
--- 157,164 ----
***************
*** 341,402 ****
vc->SetSourceDirectory(sourceDirectory);
- // And update options
- std::string updateOptions
- = this->CTest->GetCTestConfiguration("UpdateOptions");
- if ( updateOptions.empty() )
- {
- switch (this->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 )
- {
- std::string today_update_date = vc->GetNightlyTime();
-
- // TODO: SVN
- switch ( this->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;
- }
- }
-
// Cleanup the working tree.
vc->Cleanup();
- bool res = true;
-
- // 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.
- vc->MarkOldRevision();
- if(this->UpdateType == e_SVN)
- {
- svn_current_revision =
- static_cast<cmCTestSVN*>(vc.get())->GetOldRevision();
- }
-
-
//
// Now update repository and remember what files were updated
--- 210,216 ----
***************
*** 414,467 ****
double elapsed_time_start = cmSystemTools::GetTime();
! std::string command;
! cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "* Update repository: "
! << command.c_str() << std::endl);
! if ( !this->CTest->GetShowOnly() )
! {
! command = "";
! switch( this->UpdateType )
! {
! case cmCTestUpdateHandler::e_CVS:
! command = "\""+this->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 = "\"" + this->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 = "\"" + this->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;
! }
! }
! bool updateProducedError = !res || retVal;
os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
--- 228,232 ----
double elapsed_time_start = cmSystemTools::GetTime();
! bool updated = vc->Update();
os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
***************
*** 475,479 ****
os << "\t<StartDateTime>" << start_time << "</StartDateTime>\n"
<< "\t<StartTime>" << start_time_time << "</StartTime>\n"
! << "\t<UpdateCommand>" << cmXMLSafe(command)
<< "</UpdateCommand>\n"
<< "\t<UpdateType>" << cmXMLSafe(
--- 240,244 ----
os << "\t<StartDateTime>" << start_time << "</StartDateTime>\n"
<< "\t<StartTime>" << start_time_time << "</StartTime>\n"
! << "\t<UpdateCommand>" << cmXMLSafe(vc->GetUpdateCommandLine())
<< "</UpdateCommand>\n"
<< "\t<UpdateType>" << cmXMLSafe(
***************
*** 481,876 ****
<< "</UpdateType>\n";
! // Even though it failed, we may have some useful information. Try to
! // continue...
! std::vector<cmStdString> lines;
! cmSystemTools::Split(goutput.c_str(), lines);
! std::vector<cmStdString> 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 = "<no-path>";
! bool first_file = true;
!
! std::set<cmStdString> author_set;
! int numUpdated = 0;
! int numModified = 0;
! int numConflicting = 0;
!
! // Get final repository information if that is possible.
! vc->MarkNewRevision();
! if ( this->UpdateType == cmCTestUpdateHandler::e_SVN )
! {
! svn_latest_revision =
! static_cast<cmCTestSVN*>(vc.get())->GetNewRevision();
! }
!
! 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 ( this->UpdateType )
! {
! case cmCTestUpdateHandler::e_CVS:
! logcommand = "\"" + this->UpdateCommand + "\" -z3 log -N \""
! + file + "\"";
! break;
! case cmCTestUpdateHandler::e_SVN:
! if ( svn_latest_revision > 0 &&
! svn_latest_revision > svn_current_revision )
! {
! cmOStringStream logCommandStream;
! logCommandStream << "\"" << this->UpdateCommand << "\" log -r "
! << svn_current_revision << ":" << svn_latest_revision
! << " --xml \"" << file << "\"";
! logcommand = logCommandStream.str();
! }
! else
! {
! logcommand = "\"" + this->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 ( this->UpdateType == cmCTestUpdateHandler::e_CVS )
! {
! bool have_first = false;
! bool have_second = false;
! std::vector<cmStdString> 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 ( this->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</Directory>" << std::endl;
! }
! else
! {
! first_file = false;
! }
! os << "\t<Directory>\n"
! << "\t\t<Name>" << path << "</Name>" << std::endl;
! }
! if ( mod == 'C' )
! {
! numConflicting ++;
! os << "\t<Conflicting>" << std::endl;
! }
! else if ( mod == 'G' )
! {
! numConflicting ++;
! os << "\t<Conflicting>" << std::endl;
! }
! else if ( mod == 'M' )
! {
! numModified ++;
! os << "\t<Modified>" << std::endl;
! }
! else
! {
! numUpdated ++;
! os << "\t<Updated>" << 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<File>"
! << cmXMLSafe(fname)
! << "</File>\n"
! << "\t\t<Directory>" << cmXMLSafe(path)
! << "</Directory>\n"
! << "\t\t<FullName>" << cmXMLSafe(file) << "</FullName>\n"
! << "\t\t<CheckinDate>" << cmXMLSafe(sdate1)
! << "</CheckinDate>\n"
! << "\t\t<Author>" << cmXMLSafe(sauthor1) << "</Author>\n"
! << "\t\t<Email>" << cmXMLSafe(semail1) << "</Email>\n"
! << "\t\t<Log>" << cmXMLSafe(comment1) << "</Log>\n"
! << "\t\t<Revision>" << srevision1 << "</Revision>\n"
! << "\t\t<PriorRevision>" << srevision2 << "</PriorRevision>"
! << std::endl;
! if ( mod == 'C' )
! {
! os << "\t</Conflicting>" << std::endl;
! }
! else if ( mod == 'G' )
! {
! os << "\t</Conflicting>" << std::endl;
! }
! else if ( mod == 'M' )
! {
! os << "\t</Modified>" << std::endl;
! }
! else
! {
! os << "\t</Updated>" << 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</Directory>" << std::endl;
}
!
! // TODO: Skip the author list when submitting to CDash.
! for(std::set<cmStdString>::const_iterator ai = author_set.begin();
! ai != author_set.end(); ++ai)
{
! os << "\t<Author><Name>" << cmXMLSafe(*ai) << "</Name></Author>\n";
}
--- 246,268 ----
<< "</UpdateType>\n";
! vc->WriteXML(os);
! int localModifications = 0;
! if(int numUpdated = vc->GetPathCount(cmCTestVC::PathUpdated))
{
! 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;
}
***************
*** 884,888 ****
<< "</ElapsedMinutes>\n"
<< "\t<UpdateReturnStatus>";
! if ( numModified > 0 || numConflicting > 0 )
{
os << "Update error: There are modified or conflicting files in the "
--- 276,280 ----
<< "</ElapsedMinutes>\n"
<< "\t<UpdateReturnStatus>";
! if(localModifications)
{
os << "Update error: There are modified or conflicting files in the "
***************
*** 892,912 ****
<< std::endl);
}
! if ( updateProducedError )
{
! os << "Update error: ";
! cmCTestLog(this->CTest, ERROR_MESSAGE, " Update with command: "
! << command << " failed" << std::endl);
}
os << "</UpdateReturnStatus>" << std::endl;
os << "</Update>" << 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;
}
--- 284,295 ----
<< std::endl);
}
! if(!updated)
{
! cmCTestLog(this->CTest, ERROR_MESSAGE, " Update command failed: "
! << vc->GetUpdateCommandLine() << "\n");
}
os << "</UpdateReturnStatus>" << std::endl;
os << "</Update>" << std::endl;
! return localModifications;
}
Index: cmCTestVC.h
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/CTest/cmCTestVC.h,v
retrieving revision 1.5
retrieving revision 1.6
diff -C 2 -d -r1.5 -r1.6
*** cmCTestVC.h 24 Feb 2009 17:52:36 -0000 1.5
--- cmCTestVC.h 25 Feb 2009 19:42:45 -0000 1.6
***************
*** 46,56 ****
void Cleanup();
! void MarkOldRevision() { this->NoteOldRevision(); }
! void MarkNewRevision() { this->NoteNewRevision(); }
protected:
// Internal API to be implemented by subclasses.
virtual void CleanupImpl();
virtual void NoteOldRevision();
virtual void NoteNewRevision();
/** Convert a list of arguments to a human-readable command line. */
--- 46,92 ----
void Cleanup();
! /** Update the working tree to the new revision. */
! bool Update();
!
! /** Get the command line used by the Update method. */
! std::string const& GetUpdateCommandLine() const
! { return this->UpdateCommandLine; }
!
! /** Write Update.xml entries for the updates found. */
! bool WriteXML(std::ostream& xml);
!
! /** Enumerate non-trivial working tree states during update. */
! enum PathStatus { PathUpdated, PathModified, PathConflicting };
!
! /** Get the number of working tree paths in each state after update. */
! int GetPathCount(PathStatus s) const { return this->PathCount[s]; }
!
protected:
// Internal API to be implemented by subclasses.
virtual void CleanupImpl();
virtual void NoteOldRevision();
+ virtual bool UpdateImpl();
virtual void NoteNewRevision();
+ virtual bool WriteXMLUpdates(std::ostream& xml);
+
+ /** Basic information about one revision of a tree or file. */
+ struct Revision
+ {
+ std::string Rev;
+ std::string Date;
+ std::string Author;
+ std::string Log;
+ };
+
+ /** Represent change to one file. */
+ struct File
+ {
+ PathStatus Status;
+ Revision const* Rev;
+ Revision const* PriorRev;
+ File(): Status(PathUpdated), Rev(0), PriorRev(0) {}
+ File(PathStatus status, Revision const* rev, Revision const* priorRev):
+ Status(status), Rev(rev), PriorRev(priorRev) {}
+ };
/** Convert a list of arguments to a human-readable command line. */
***************
*** 61,64 ****
--- 97,109 ----
OutputParser* err, const char* workDir = 0);
+ /** Run VC update command line and send output to given parsers. */
+ bool RunUpdateCommand(char const* const* cmd,
+ OutputParser* out, OutputParser* err = 0);
+
+ /** Write xml element for one file. */
+ void WriteXMLEntry(std::ostream& xml, std::string const& path,
+ std::string const& name, std::string const& full,
+ File const& f);
+
// Instance of cmCTest running the script.
cmCTest* CTest;
***************
*** 70,73 ****
--- 115,127 ----
std::string CommandLineTool;
std::string SourceDirectory;
+
+ // Record update command info.
+ std::string UpdateCommandLine;
+
+ // Placeholder for unknown revisions.
+ Revision Unknown;
+
+ // Count paths reported with each PathStatus value.
+ int PathCount[3];
};
Index: cmCTestCVS.h
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/CTest/cmCTestCVS.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -C 2 -d -r1.1 -r1.2
*** cmCTestCVS.h 24 Feb 2009 15:39:55 -0000 1.1
--- cmCTestCVS.h 25 Feb 2009 19:42:45 -0000 1.2
***************
*** 31,34 ****
--- 31,55 ----
virtual ~cmCTestCVS();
+
+ private:
+ // Implement cmCTestVC internal API.
+ virtual bool UpdateImpl();
+ virtual bool WriteXMLUpdates(std::ostream& xml);
+
+ // Update status for files in each directory.
+ class Directory: public std::map<cmStdString, PathStatus> {};
+ std::map<cmStdString, Directory> Dirs;
+
+ std::string ComputeBranchFlag(std::string const& dir);
+ void LoadRevisions(std::string const& file, const char* branchFlag,
+ std::vector<Revision>& revisions);
+ void WriteXMLDirectory(std::ostream& xml, std::string const& path,
+ Directory const& dir);
+
+ // Parsing helper classes.
+ class UpdateParser;
+ class LogParser;
+ friend class UpdateParser;
+ friend class LogParser;
};
More information about the Cmake-commits
mailing list