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.
This commit is contained in:
parent
cb788e8f6d
commit
80282b749f
|
@ -16,6 +16,12 @@
|
|||
=========================================================================*/
|
||||
#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)
|
||||
{
|
||||
|
@ -25,3 +31,293 @@ cmCTestCVS::cmCTestCVS(cmCTest* ct, std::ostream& log): cmCTestVC(ct, log)
|
|||
cmCTestCVS::~cmCTestCVS()
|
||||
{
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,27 @@ public:
|
|||
cmCTestCVS(cmCTest* ctest, std::ostream& log);
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -17,12 +17,16 @@
|
|||
#include "cmCTestSVN.h"
|
||||
|
||||
#include "cmCTest.h"
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmXMLParser.h"
|
||||
#include "cmXMLSafe.h"
|
||||
|
||||
#include <cmsys/RegularExpression.hxx>
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
cmCTestSVN::cmCTestSVN(cmCTest* ct, std::ostream& log): cmCTestVC(ct, log)
|
||||
{
|
||||
this->PriorRev = this->Unknown;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
@ -114,6 +118,7 @@ void cmCTestSVN::NoteOldRevision()
|
|||
this->Log << "Revision before update: " << this->OldRevision << "\n";
|
||||
cmCTestLog(this->CTest, HANDLER_OUTPUT, " Old revision of repository is: "
|
||||
<< this->OldRevision << "\n");
|
||||
this->PriorRev.Rev = this->OldRevision;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
@ -124,6 +129,7 @@ void cmCTestSVN::NoteNewRevision()
|
|||
cmCTestLog(this->CTest, HANDLER_OUTPUT, " New revision of repository is: "
|
||||
<< this->NewRevision << "\n");
|
||||
|
||||
// this->Root = ""; // uncomment to test GuessBase
|
||||
this->Log << "URL = " << this->URL << "\n";
|
||||
this->Log << "Root = " << this->Root << "\n";
|
||||
|
||||
|
@ -136,3 +142,387 @@ void cmCTestSVN::NoteNewRevision()
|
|||
}
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -31,13 +31,25 @@ public:
|
|||
|
||||
virtual ~cmCTestSVN();
|
||||
|
||||
int GetOldRevision() { return atoi(this->OldRevision.c_str()); }
|
||||
int GetNewRevision() { return atoi(this->NewRevision.c_str()); }
|
||||
private:
|
||||
// Implement cmCTestVC internal API.
|
||||
virtual void CleanupImpl();
|
||||
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.
|
||||
std::string OldRevision;
|
||||
|
@ -52,11 +64,33 @@ private:
|
|||
// Directory under repository root checked out in working tree.
|
||||
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;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -63,130 +63,6 @@ static const char* cmCTestUpdateHandlerUpdateToString(int type)
|
|||
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_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
|
||||
{
|
||||
public:
|
||||
|
@ -280,17 +156,10 @@ int cmCTestUpdateHandler::DetermineType(const char* cmd, const char* type)
|
|||
//functions and commented...
|
||||
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");
|
||||
if ( !sourceDirectory )
|
||||
|
@ -340,64 +209,9 @@ int cmCTestUpdateHandler::ProcessHandler()
|
|||
vc->SetCommandLineTool(this->UpdateCommand);
|
||||
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
|
||||
//
|
||||
|
@ -413,56 +227,7 @@ int cmCTestUpdateHandler::ProcessHandler()
|
|||
static_cast<unsigned int>(cmSystemTools::GetTime());
|
||||
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;
|
||||
bool updated = vc->Update();
|
||||
|
||||
os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
<< "<Update mode=\"Client\" Generator=\"ctest-"
|
||||
|
@ -474,404 +239,31 @@ int cmCTestUpdateHandler::ProcessHandler()
|
|||
<< this->CTest->GetTestModelString() << "</BuildStamp>" << std::endl;
|
||||
os << "\t<StartDateTime>" << start_time << "</StartDateTime>\n"
|
||||
<< "\t<StartTime>" << start_time_time << "</StartTime>\n"
|
||||
<< "\t<UpdateCommand>" << cmXMLSafe(command)
|
||||
<< "\t<UpdateCommand>" << cmXMLSafe(vc->GetUpdateCommandLine())
|
||||
<< "</UpdateCommand>\n"
|
||||
<< "\t<UpdateType>" << cmXMLSafe(
|
||||
cmCTestUpdateHandlerUpdateToString(this->UpdateType))
|
||||
<< "</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());
|
||||
vc->WriteXML(os);
|
||||
|
||||
// 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 )
|
||||
int localModifications = 0;
|
||||
if(int numUpdated = vc->GetPathCount(cmCTestVC::PathUpdated))
|
||||
{
|
||||
svn_latest_revision =
|
||||
static_cast<cmCTestSVN*>(vc.get())->GetNewRevision();
|
||||
cmCTestLog(this->CTest, HANDLER_OUTPUT,
|
||||
" Found " << numUpdated << " updated files\n");
|
||||
}
|
||||
|
||||
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 ++ )
|
||||
if(int numModified = vc->GetPathCount(cmCTestVC::PathModified))
|
||||
{
|
||||
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 ++;
|
||||
}
|
||||
cmCTestLog(this->CTest, HANDLER_OUTPUT,
|
||||
" Found " << numModified << " locally modified files\n");
|
||||
localModifications += numModified;
|
||||
}
|
||||
if ( file_count )
|
||||
if(int numConflicting = vc->GetPathCount(cmCTestVC::PathConflicting))
|
||||
{
|
||||
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";
|
||||
cmCTestLog(this->CTest, HANDLER_OUTPUT,
|
||||
" Found " << numConflicting << " conflicting files\n");
|
||||
localModifications += numConflicting;
|
||||
}
|
||||
|
||||
cmCTestLog(this->CTest, DEBUG, "End" << std::endl);
|
||||
|
@ -883,7 +275,7 @@ int cmCTestUpdateHandler::ProcessHandler()
|
|||
static_cast<int>((cmSystemTools::GetTime() - elapsed_time_start)/6)/10.0
|
||||
<< "</ElapsedMinutes>\n"
|
||||
<< "\t<UpdateReturnStatus>";
|
||||
if ( numModified > 0 || numConflicting > 0 )
|
||||
if(localModifications)
|
||||
{
|
||||
os << "Update error: There are modified or conflicting files in the "
|
||||
"repository";
|
||||
|
@ -891,23 +283,14 @@ int cmCTestUpdateHandler::ProcessHandler()
|
|||
" There are modified or conflicting files in the repository"
|
||||
<< std::endl);
|
||||
}
|
||||
if ( updateProducedError )
|
||||
if(!updated)
|
||||
{
|
||||
os << "Update error: ";
|
||||
cmCTestLog(this->CTest, ERROR_MESSAGE, " Update with command: "
|
||||
<< command << " failed" << std::endl);
|
||||
cmCTestLog(this->CTest, ERROR_MESSAGE, " Update command failed: "
|
||||
<< vc->GetUpdateCommandLine() << "\n");
|
||||
}
|
||||
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;
|
||||
return localModifications;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
|
|
@ -17,12 +17,19 @@
|
|||
#include "cmCTestVC.h"
|
||||
|
||||
#include "cmCTest.h"
|
||||
#include "cmXMLSafe.h"
|
||||
|
||||
#include <cmsys/Process.h>
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
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";
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
@ -71,6 +78,22 @@ std::string cmCTestVC::ComputeCommandLine(char const* const* cmd)
|
|||
return line.str();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
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()
|
||||
{
|
||||
|
@ -103,6 +126,17 @@ void cmCTestVC::CleanupImpl()
|
|||
// We do no cleanup by default.
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
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()
|
||||
{
|
||||
|
@ -114,3 +148,51 @@ void cmCTestVC::NoteNewRevision()
|
|||
{
|
||||
// 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];
|
||||
}
|
||||
|
|
|
@ -45,13 +45,49 @@ public:
|
|||
/** Perform cleanup operations on the work tree. */
|
||||
void Cleanup();
|
||||
|
||||
void MarkOldRevision() { this->NoteOldRevision(); }
|
||||
void MarkNewRevision() { this->NoteNewRevision(); }
|
||||
/** 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. */
|
||||
static std::string ComputeCommandLine(char const* const* cmd);
|
||||
|
@ -60,6 +96,15 @@ protected:
|
|||
bool RunChild(char const* const* cmd, OutputParser* out,
|
||||
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;
|
||||
|
||||
|
@ -69,6 +114,15 @@ protected:
|
|||
// Basic information about the working tree.
|
||||
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];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue