The TortoiseCVS version of cvs.exe includes the '.exe' in cvs update messages for files removed from the repository. This change accounts for it in the regular expressions that match such lines. Now removed files are properly reported by ctest_update() when using TortoiseCVS.
325 lines
10 KiB
C++
325 lines
10 KiB
C++
/*=========================================================================
|
|
|
|
Program: CMake - Cross-Platform Makefile Generator
|
|
Module: $RCSfile$
|
|
Language: C++
|
|
Date: $Date$
|
|
Version: $Revision$
|
|
|
|
Copyright (c) 2002 Kitware, Inc. All rights reserved.
|
|
See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
|
|
|
|
This software is distributed WITHOUT ANY WARRANTY; without even
|
|
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
PURPOSE. See the above copyright notices for more information.
|
|
|
|
=========================================================================*/
|
|
#include "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)
|
|
{
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
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", 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;
|
|
}
|