cmake/Source/CTest/cmCTestCVS.cxx

282 lines
8.8 KiB
C++
Raw Normal View History

2016-10-30 18:24:19 +01:00
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
2009-10-04 10:30:41 +03:00
#include "cmCTestCVS.h"
2020-02-01 23:06:01 +01:00
#include <utility>
#include <cm/string_view>
#include "cmsys/FStream.hxx"
#include "cmsys/RegularExpression.hxx"
2009-10-04 10:30:41 +03:00
#include "cmCTest.h"
2016-10-30 18:24:19 +01:00
#include "cmProcessTools.h"
2020-02-01 23:06:01 +01:00
#include "cmStringAlgorithms.h"
2009-10-04 10:30:41 +03:00
#include "cmSystemTools.h"
2015-08-17 11:37:30 +02:00
#include "cmXMLWriter.h"
2009-10-04 10:30:41 +03:00
2016-07-09 11:21:54 +02:00
cmCTestCVS::cmCTestCVS(cmCTest* ct, std::ostream& log)
: cmCTestVC(ct, log)
2009-10-04 10:30:41 +03:00
{
}
2019-11-11 23:01:05 +01:00
cmCTestCVS::~cmCTestCVS() = default;
2009-10-04 10:30:41 +03:00
2016-07-09 11:21:54 +02:00
class cmCTestCVS::UpdateParser : public cmCTestVC::LineParser
2009-10-04 10:30:41 +03:00
{
public:
2016-07-09 11:21:54 +02:00
UpdateParser(cmCTestCVS* cvs, const char* prefix)
: CVS(cvs)
{
2009-10-04 10:30:41 +03:00
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");
2016-07-09 11:21:54 +02:00
}
2009-10-04 10:30:41 +03:00
private:
cmCTestCVS* CVS;
cmsys::RegularExpression RegexFileUpdated;
cmsys::RegularExpression RegexFileModified;
cmsys::RegularExpression RegexFileConflicting;
cmsys::RegularExpression RegexFileRemoved1;
cmsys::RegularExpression RegexFileRemoved2;
2018-01-26 17:06:56 +01:00
bool ProcessLine() override
2016-07-09 11:21:54 +02:00
{
if (this->RegexFileUpdated.find(this->Line)) {
2009-10-04 10:30:41 +03:00
this->DoFile(PathUpdated, this->RegexFileUpdated.match(2));
2016-07-09 11:21:54 +02:00
} else if (this->RegexFileModified.find(this->Line)) {
2009-10-04 10:30:41 +03:00
this->DoFile(PathModified, this->RegexFileModified.match(2));
2016-07-09 11:21:54 +02:00
} else if (this->RegexFileConflicting.find(this->Line)) {
2009-10-04 10:30:41 +03:00
this->DoFile(PathConflicting, this->RegexFileConflicting.match(2));
2016-07-09 11:21:54 +02:00
} else if (this->RegexFileRemoved1.find(this->Line)) {
2009-10-04 10:30:41 +03:00
this->DoFile(PathUpdated, this->RegexFileRemoved1.match(1));
2016-07-09 11:21:54 +02:00
} else if (this->RegexFileRemoved2.find(this->Line)) {
2009-10-04 10:30:41 +03:00
this->DoFile(PathUpdated, this->RegexFileRemoved2.match(1));
}
2016-07-09 11:21:54 +02:00
return true;
}
2009-10-04 10:30:41 +03:00
void DoFile(PathStatus status, std::string const& file)
2016-07-09 11:21:54 +02:00
{
2009-10-04 10:30:41 +03:00
std::string dir = cmSystemTools::GetFilenamePath(file);
std::string name = cmSystemTools::GetFilenameName(file);
this->CVS->Dirs[dir][name] = status;
2016-07-09 11:21:54 +02:00
}
2009-10-04 10:30:41 +03:00
};
bool cmCTestCVS::UpdateImpl()
{
// Get user-specified update options.
std::string opts = this->CTest->GetCTestConfiguration("UpdateOptions");
2016-07-09 11:21:54 +02:00
if (opts.empty()) {
2009-10-04 10:30:41 +03:00
opts = this->CTest->GetCTestConfiguration("CVSUpdateOptions");
2016-07-09 11:21:54 +02:00
if (opts.empty()) {
2009-10-04 10:30:41 +03:00
opts = "-dP";
}
2016-07-09 11:21:54 +02:00
}
2019-11-11 23:01:05 +01:00
std::vector<std::string> args = cmSystemTools::ParseArguments(opts);
2009-10-04 10:30:41 +03:00
// Specify the start time for nightly testing.
2016-07-09 11:21:54 +02:00
if (this->CTest->GetTestModel() == cmCTest::NIGHTLY) {
2009-10-04 10:30:41 +03:00
args.push_back("-D" + this->GetNightlyTime() + " UTC");
2016-07-09 11:21:54 +02:00
}
2009-10-04 10:30:41 +03:00
// 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");
2018-01-26 17:06:56 +01:00
for (std::string const& arg : args) {
cvs_update.push_back(arg.c_str());
2016-07-09 11:21:54 +02:00
}
2018-01-26 17:06:56 +01:00
cvs_update.push_back(nullptr);
2009-10-04 10:30:41 +03:00
UpdateParser out(this, "up-out> ");
UpdateParser err(this, "up-err> ");
2022-08-04 22:12:04 +02:00
return this->RunUpdateCommand(cvs_update.data(), &out, &err);
2009-10-04 10:30:41 +03:00
}
2016-07-09 11:21:54 +02:00
class cmCTestCVS::LogParser : public cmCTestVC::LineParser
2009-10-04 10:30:41 +03:00
{
public:
2020-02-01 23:06:01 +01:00
using Revision = cmCTestCVS::Revision;
2016-07-09 11:21:54 +02:00
LogParser(cmCTestCVS* cvs, const char* prefix, std::vector<Revision>& revs)
: CVS(cvs)
, Revisions(revs)
{
2019-11-11 23:01:05 +01:00
this->SetLog(&cvs->Log, prefix);
this->RegexRevision.compile("^revision +([^ ]*) *$");
2009-10-04 10:30:41 +03:00
this->RegexBranches.compile("^branches: .*$");
this->RegexPerson.compile("^date: +([^;]+); +author: +([^;]+);");
2016-07-09 11:21:54 +02:00
}
2009-10-04 10:30:41 +03:00
private:
cmCTestCVS* CVS;
std::vector<Revision>& Revisions;
cmsys::RegularExpression RegexRevision;
cmsys::RegularExpression RegexBranches;
cmsys::RegularExpression RegexPerson;
2016-07-09 11:21:54 +02:00
enum SectionType
{
SectionHeader,
SectionRevisions,
SectionEnd
};
2022-08-04 22:12:04 +02:00
SectionType Section = SectionHeader;
2009-10-04 10:30:41 +03:00
Revision Rev;
2018-01-26 17:06:56 +01:00
bool ProcessLine() override
2016-07-09 11:21:54 +02:00
{
2018-08-09 18:06:22 +02:00
if (this->Line ==
("======================================="
"======================================")) {
2009-10-04 10:30:41 +03:00
// This line ends the revision list.
2016-07-09 11:21:54 +02:00
if (this->Section == SectionRevisions) {
2009-10-04 10:30:41 +03:00
this->FinishRevision();
}
2016-07-09 11:21:54 +02:00
this->Section = SectionEnd;
} else if (this->Line == "----------------------------") {
2009-10-04 10:30:41 +03:00
// This line divides revisions from the header and each other.
2016-07-09 11:21:54 +02:00
if (this->Section == SectionHeader) {
2009-10-04 10:30:41 +03:00
this->Section = SectionRevisions;
2016-07-09 11:21:54 +02:00
} else if (this->Section == SectionRevisions) {
2009-10-04 10:30:41 +03:00
this->FinishRevision();
}
2016-07-09 11:21:54 +02:00
} else if (this->Section == SectionRevisions) {
2020-08-30 11:54:41 +02:00
// XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165
// NOLINTNEXTLINE(bugprone-branch-clone)
2016-07-09 11:21:54 +02:00
if (!this->Rev.Log.empty()) {
2009-10-04 10:30:41 +03:00
// Continue the existing log.
this->Rev.Log += this->Line;
2020-08-30 11:54:41 +02:00
this->Rev.Log += '\n';
2016-07-09 11:21:54 +02:00
} else if (this->Rev.Rev.empty() &&
this->RegexRevision.find(this->Line)) {
2009-10-04 10:30:41 +03:00
this->Rev.Rev = this->RegexRevision.match(1);
2016-07-09 11:21:54 +02:00
} else if (this->Rev.Date.empty() &&
this->RegexPerson.find(this->Line)) {
2009-10-04 10:30:41 +03:00
this->Rev.Date = this->RegexPerson.match(1);
this->Rev.Author = this->RegexPerson.match(2);
2016-07-09 11:21:54 +02:00
} else if (!this->RegexBranches.find(this->Line)) {
2009-10-04 10:30:41 +03:00
// Start the log.
this->Rev.Log += this->Line;
2020-08-30 11:54:41 +02:00
this->Rev.Log += '\n';
2009-10-04 10:30:41 +03:00
}
}
2016-07-09 11:21:54 +02:00
return this->Section != SectionEnd;
}
2009-10-04 10:30:41 +03:00
void FinishRevision()
2016-07-09 11:21:54 +02:00
{
if (!this->Rev.Rev.empty()) {
2009-10-04 10:30:41 +03:00
// Record this revision.
2016-07-09 11:21:54 +02:00
/* clang-format off */
2009-10-04 10:30:41 +03:00
this->CVS->Log << "Found revision " << this->Rev.Rev << "\n"
<< " author = " << this->Rev.Author << "\n"
<< " date = " << this->Rev.Date << "\n";
2016-07-09 11:21:54 +02:00
/* clang-format on */
2009-10-04 10:30:41 +03:00
this->Revisions.push_back(this->Rev);
// We only need two revisions.
2016-07-09 11:21:54 +02:00
if (this->Revisions.size() >= 2) {
2009-10-04 10:30:41 +03:00
this->Section = SectionEnd;
}
}
2016-07-09 11:21:54 +02:00
this->Rev = Revision();
}
2009-10-04 10:30:41 +03:00
};
std::string cmCTestCVS::ComputeBranchFlag(std::string const& dir)
{
// Compute the tag file location for this directory.
std::string tagFile = this->SourceDirectory;
2016-07-09 11:21:54 +02:00
if (!dir.empty()) {
2009-10-04 10:30:41 +03:00
tagFile += "/";
tagFile += dir;
2016-07-09 11:21:54 +02:00
}
2009-10-04 10:30:41 +03:00
tagFile += "/CVS/Tag";
// Lookup the branch in the tag file, if any.
std::string tagLine;
2014-08-03 19:52:23 +02:00
cmsys::ifstream tagStream(tagFile.c_str());
2016-07-09 11:21:54 +02:00
if (tagStream && cmSystemTools::GetLineFromStream(tagStream, tagLine) &&
tagLine.size() > 1 && tagLine[0] == 'T') {
2009-10-04 10:30:41 +03:00
// Use the branch specified in the tag file.
2020-02-01 23:06:01 +01:00
std::string flag = cmStrCat("-r", cm::string_view(tagLine).substr(1));
2009-10-04 10:30:41 +03:00
return flag;
2016-07-09 11:21:54 +02:00
}
2016-10-30 18:24:19 +01:00
// Use the default branch.
return "-b";
2009-10-04 10:30:41 +03:00
}
2016-07-09 11:21:54 +02:00
void cmCTestCVS::LoadRevisions(std::string const& file, const char* branchFlag,
2009-10-04 10:30:41 +03:00
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();
2018-01-26 17:06:56 +01:00
const char* cvs_log[] = {
cvs, "log", "-N", branchFlag, file.c_str(), nullptr
};
2009-10-04 10:30:41 +03:00
LogParser out(this, "log-out> ", revisions);
OutputLogger err(this->Log, "log-err> ");
this->RunChild(cvs_log, &out, &err);
}
2016-07-09 11:21:54 +02:00
void cmCTestCVS::WriteXMLDirectory(cmXMLWriter& xml, std::string const& path,
2009-10-04 10:30:41 +03:00
Directory const& dir)
{
2016-07-09 11:21:54 +02:00
const char* slash = path.empty() ? "" : "/";
2015-08-17 11:37:30 +02:00
xml.StartElement("Directory");
xml.Element("Name", path);
2009-10-04 10:30:41 +03:00
// 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;
2018-01-26 17:06:56 +01:00
for (auto const& fi : dir) {
std::string full = path + slash + fi.first;
2009-10-04 10:30:41 +03:00
// Load two real or unknown revisions.
revisions.clear();
2018-01-26 17:06:56 +01:00
if (fi.second != PathUpdated) {
2009-10-04 10:30:41 +03:00
// For local modifications the current rev is unknown and the
// prior rev is the latest from cvs.
revisions.push_back(this->Unknown);
2016-07-09 11:21:54 +02:00
}
2009-10-04 10:30:41 +03:00
this->LoadRevisions(full, branchFlag.c_str(), revisions);
revisions.resize(2, this->Unknown);
// Write the entry for this file with these revisions.
2022-08-04 22:12:04 +02:00
File f(fi.second, revisions.data(), revisions.data() + 1);
2018-01-26 17:06:56 +01:00
this->WriteXMLEntry(xml, path, fi.first, full, f);
2016-07-09 11:21:54 +02:00
}
2015-08-17 11:37:30 +02:00
xml.EndElement(); // Directory
2009-10-04 10:30:41 +03:00
}
2015-08-17 11:37:30 +02:00
bool cmCTestCVS::WriteXMLUpdates(cmXMLWriter& xml)
2009-10-04 10:30:41 +03:00
{
cmCTestLog(this->CTest, HANDLER_OUTPUT,
" Gathering version information (one . per updated file):\n"
2016-07-09 11:21:54 +02:00
" "
<< std::flush);
2009-10-04 10:30:41 +03:00
2018-01-26 17:06:56 +01:00
for (auto const& d : this->Dirs) {
this->WriteXMLDirectory(xml, d.first, d.second);
2016-07-09 11:21:54 +02:00
}
2009-10-04 10:30:41 +03:00
cmCTestLog(this->CTest, HANDLER_OUTPUT, std::endl);
return true;
}