cmake/Source/CTest/cmCTestSVN.cxx

565 lines
16 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 "cmCTestSVN.h"
2020-02-01 23:06:01 +01:00
#include <cstdlib>
#include <cstring>
#include <map>
#include <ostream>
2020-08-30 11:54:41 +02:00
#include <cmext/algorithm>
2020-02-01 23:06:01 +01:00
#include "cmsys/RegularExpression.hxx"
2009-10-04 10:30:41 +03:00
#include "cmCTest.h"
2016-10-30 18:24:19 +01:00
#include "cmCTestVC.h"
#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"
#include "cmXMLParser.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
struct cmCTestSVN::Revision : public cmCTestVC::Revision
2013-03-16 19:13:01 +02:00
{
cmCTestSVN::SVNInfo* SVNInfo;
};
2016-07-09 11:21:54 +02:00
cmCTestSVN::cmCTestSVN(cmCTest* ct, std::ostream& log)
: cmCTestGlobalVC(ct, log)
2009-10-04 10:30:41 +03:00
{
this->PriorRev = this->Unknown;
}
2019-11-11 23:01:05 +01:00
cmCTestSVN::~cmCTestSVN() = default;
2009-10-04 10:30:41 +03:00
void cmCTestSVN::CleanupImpl()
{
2013-03-16 19:13:01 +02:00
std::vector<const char*> svn_cleanup;
svn_cleanup.push_back("cleanup");
2009-10-04 10:30:41 +03:00
OutputLogger out(this->Log, "cleanup-out> ");
OutputLogger err(this->Log, "cleanup-err> ");
2013-03-16 19:13:01 +02:00
this->RunSVNCommand(svn_cleanup, &out, &err);
2009-10-04 10:30:41 +03:00
}
2016-07-09 11:21:54 +02:00
class cmCTestSVN::InfoParser : public cmCTestVC::LineParser
2009-10-04 10:30:41 +03:00
{
public:
2016-07-09 11:21:54 +02:00
InfoParser(cmCTestSVN* svn, const char* prefix, std::string& rev,
SVNInfo& svninfo)
: Rev(rev)
, SVNRepo(svninfo)
{
2009-10-04 10:30:41 +03:00
this->SetLog(&svn->Log, prefix);
this->RegexRev.compile("^Revision: ([0-9]+)");
this->RegexURL.compile("^URL: +([^ ]+) *$");
this->RegexRoot.compile("^Repository Root: +([^ ]+) *$");
2016-07-09 11:21:54 +02:00
}
2009-10-04 10:30:41 +03:00
private:
std::string& Rev;
2013-03-16 19:13:01 +02:00
cmCTestSVN::SVNInfo& SVNRepo;
2009-10-04 10:30:41 +03:00
cmsys::RegularExpression RegexRev;
cmsys::RegularExpression RegexURL;
cmsys::RegularExpression RegexRoot;
2018-01-26 17:06:56 +01:00
bool ProcessLine() override
2016-07-09 11:21:54 +02:00
{
if (this->RegexRev.find(this->Line)) {
2009-10-04 10:30:41 +03:00
this->Rev = this->RegexRev.match(1);
2016-07-09 11:21:54 +02:00
} else if (this->RegexURL.find(this->Line)) {
2013-03-16 19:13:01 +02:00
this->SVNRepo.URL = this->RegexURL.match(1);
2016-07-09 11:21:54 +02:00
} else if (this->RegexRoot.find(this->Line)) {
2013-03-16 19:13:01 +02:00
this->SVNRepo.Root = this->RegexRoot.match(1);
2009-10-04 10:30:41 +03:00
}
2016-07-09 11:21:54 +02:00
return true;
}
2009-10-04 10:30:41 +03:00
};
static bool cmCTestSVNPathStarts(std::string const& p1, std::string const& p2)
{
// Does path p1 start with path p2?
2016-07-09 11:21:54 +02:00
if (p1.size() == p2.size()) {
2009-10-04 10:30:41 +03:00
return p1 == p2;
2016-10-30 18:24:19 +01:00
}
if (p1.size() > p2.size() && p1[p2.size()] == '/') {
2009-10-04 10:30:41 +03:00
return strncmp(p1.c_str(), p2.c_str(), p2.size()) == 0;
2016-07-09 11:21:54 +02:00
}
2016-10-30 18:24:19 +01:00
return false;
2009-10-04 10:30:41 +03:00
}
2013-03-16 19:13:01 +02:00
std::string cmCTestSVN::LoadInfo(SVNInfo& svninfo)
2009-10-04 10:30:41 +03:00
{
// Run "svn info" to get the repository info from the work tree.
2013-03-16 19:13:01 +02:00
std::vector<const char*> svn_info;
svn_info.push_back("info");
svn_info.push_back(svninfo.LocalPath.c_str());
2009-10-04 10:30:41 +03:00
std::string rev;
2013-03-16 19:13:01 +02:00
InfoParser out(this, "info-out> ", rev, svninfo);
2009-10-04 10:30:41 +03:00
OutputLogger err(this->Log, "info-err> ");
2013-03-16 19:13:01 +02:00
this->RunSVNCommand(svn_info, &out, &err);
2009-10-04 10:30:41 +03:00
return rev;
}
2017-07-20 19:35:53 +02:00
bool cmCTestSVN::NoteOldRevision()
2009-10-04 10:30:41 +03:00
{
2017-07-20 19:35:53 +02:00
if (!this->LoadRepositories()) {
return false;
}
2013-03-16 19:13:01 +02:00
2018-01-26 17:06:56 +01:00
for (SVNInfo& svninfo : this->Repositories) {
2013-03-16 19:13:01 +02:00
svninfo.OldRevision = this->LoadInfo(svninfo);
this->Log << "Revision for repository '" << svninfo.LocalPath
<< "' before update: " << svninfo.OldRevision << "\n";
2018-08-09 18:06:22 +02:00
cmCTestLog(this->CTest, HANDLER_OUTPUT,
" Old revision of external repository '"
<< svninfo.LocalPath << "' is: " << svninfo.OldRevision
<< "\n");
2016-07-09 11:21:54 +02:00
}
2013-03-16 19:13:01 +02:00
// Set the global old revision to the one of the root
this->OldRevision = this->RootInfo->OldRevision;
2009-10-04 10:30:41 +03:00
this->PriorRev.Rev = this->OldRevision;
2017-07-20 19:35:53 +02:00
return true;
2009-10-04 10:30:41 +03:00
}
2017-07-20 19:35:53 +02:00
bool cmCTestSVN::NoteNewRevision()
2009-10-04 10:30:41 +03:00
{
2017-07-20 19:35:53 +02:00
if (!this->LoadRepositories()) {
return false;
}
2016-10-30 18:24:19 +01:00
2018-01-26 17:06:56 +01:00
for (SVNInfo& svninfo : this->Repositories) {
2013-03-16 19:13:01 +02:00
svninfo.NewRevision = this->LoadInfo(svninfo);
this->Log << "Revision for repository '" << svninfo.LocalPath
<< "' after update: " << svninfo.NewRevision << "\n";
2018-08-09 18:06:22 +02:00
cmCTestLog(this->CTest, HANDLER_OUTPUT,
" New revision of external repository '"
<< svninfo.LocalPath << "' is: " << svninfo.NewRevision
<< "\n");
2013-03-16 19:13:01 +02:00
// svninfo.Root = ""; // uncomment to test GuessBase
this->Log << "Repository '" << svninfo.LocalPath
<< "' URL = " << svninfo.URL << "\n";
this->Log << "Repository '" << svninfo.LocalPath
<< "' Root = " << svninfo.Root << "\n";
// Compute the base path the working tree has checked out under
// the repository root.
2016-07-09 11:21:54 +02:00
if (!svninfo.Root.empty() &&
cmCTestSVNPathStarts(svninfo.URL, svninfo.Root)) {
2020-02-01 23:06:01 +01:00
svninfo.Base = cmStrCat(
cmCTest::DecodeURL(svninfo.URL.substr(svninfo.Root.size())), '/');
2016-07-09 11:21:54 +02:00
}
2013-03-16 19:13:01 +02:00
this->Log << "Repository '" << svninfo.LocalPath
<< "' Base = " << svninfo.Base << "\n";
}
2009-10-04 10:30:41 +03:00
2013-03-16 19:13:01 +02:00
// Set the global new revision to the one of the root
this->NewRevision = this->RootInfo->NewRevision;
2017-07-20 19:35:53 +02:00
return true;
2009-10-04 10:30:41 +03:00
}
2013-03-16 19:13:01 +02:00
void cmCTestSVN::GuessBase(SVNInfo& svninfo,
std::vector<Change> const& changes)
2009-10-04 10:30:41 +03:00
{
// 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.
2016-07-09 11:21:54 +02:00
for (std::string::size_type slash = svninfo.URL.find('/');
svninfo.Base.empty() && slash != std::string::npos;
slash = svninfo.URL.find('/', slash + 1)) {
2009-10-04 10:30:41 +03:00
// If the URL suffix is a prefix of at least one path then it is the base.
2013-03-16 19:13:01 +02:00
std::string base = cmCTest::DecodeURL(svninfo.URL.substr(slash));
2020-02-01 23:06:01 +01:00
for (auto ci = changes.begin();
2016-07-09 11:21:54 +02:00
svninfo.Base.empty() && ci != changes.end(); ++ci) {
if (cmCTestSVNPathStarts(ci->Path, base)) {
2013-03-16 19:13:01 +02:00
svninfo.Base = base;
2009-10-04 10:30:41 +03:00
}
}
2016-07-09 11:21:54 +02:00
}
2009-10-04 10:30:41 +03:00
// 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.
2013-03-16 19:13:01 +02:00
svninfo.Base += "/";
2009-10-04 10:30:41 +03:00
2013-03-16 19:13:01 +02:00
this->Log << "Guessed Base = " << svninfo.Base << "\n";
2009-10-04 10:30:41 +03:00
}
2016-07-09 11:21:54 +02:00
class cmCTestSVN::UpdateParser : public cmCTestVC::LineParser
2009-10-04 10:30:41 +03:00
{
public:
2016-07-09 11:21:54 +02:00
UpdateParser(cmCTestSVN* svn, const char* prefix)
: SVN(svn)
{
2009-10-04 10:30:41 +03:00
this->SetLog(&svn->Log, prefix);
this->RegexUpdate.compile("^([ADUCGE ])([ADUCGE ])[B ] +(.+)$");
2016-07-09 11:21:54 +02:00
}
2009-10-04 10:30:41 +03:00
private:
cmCTestSVN* SVN;
cmsys::RegularExpression RegexUpdate;
2018-01-26 17:06:56 +01:00
bool ProcessLine() override
2016-07-09 11:21:54 +02:00
{
if (this->RegexUpdate.find(this->Line)) {
2009-10-04 10:30:41 +03:00
this->DoPath(this->RegexUpdate.match(1)[0],
2016-07-09 11:21:54 +02:00
this->RegexUpdate.match(2)[0], this->RegexUpdate.match(3));
2009-10-04 10:30:41 +03:00
}
2016-07-09 11:21:54 +02:00
return true;
}
2009-10-04 10:30:41 +03:00
void DoPath(char path_status, char prop_status, std::string const& path)
2016-07-09 11:21:54 +02:00
{
char status = (path_status != ' ') ? path_status : prop_status;
2009-10-04 10:30:41 +03:00
std::string dir = cmSystemTools::GetFilenamePath(path);
std::string name = cmSystemTools::GetFilenameName(path);
// See "svn help update".
2016-07-09 11:21:54 +02:00
switch (status) {
2009-10-04 10:30:41 +03:00
case 'G':
this->SVN->Dirs[dir][name].Status = PathModified;
break;
case 'C':
this->SVN->Dirs[dir][name].Status = PathConflicting;
break;
2016-07-09 11:21:54 +02:00
case 'A':
case 'D':
case 'U':
2009-10-04 10:30:41 +03:00
this->SVN->Dirs[dir][name].Status = PathUpdated;
break;
case 'E': // TODO?
2016-07-09 11:21:54 +02:00
case '?':
case ' ':
default:
2009-10-04 10:30:41 +03:00
break;
}
2016-07-09 11:21:54 +02:00
}
2009-10-04 10:30:41 +03:00
};
bool cmCTestSVN::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("SVNUpdateOptions");
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("-r{" + this->GetNightlyTime() + " +0000}");
2016-07-09 11:21:54 +02:00
}
2009-10-04 10:30:41 +03:00
std::vector<char const*> svn_update;
svn_update.push_back("update");
2018-01-26 17:06:56 +01:00
for (std::string const& arg : args) {
svn_update.push_back(arg.c_str());
2016-07-09 11:21:54 +02:00
}
2009-10-04 10:30:41 +03:00
UpdateParser out(this, "up-out> ");
OutputLogger err(this->Log, "up-err> ");
2013-03-16 19:13:01 +02:00
return this->RunSVNCommand(svn_update, &out, &err);
}
bool cmCTestSVN::RunSVNCommand(std::vector<char const*> const& parameters,
2016-07-09 11:21:54 +02:00
OutputParser* out, OutputParser* err)
2013-03-16 19:13:01 +02:00
{
2016-10-30 18:24:19 +01:00
if (parameters.empty()) {
2016-07-09 11:21:54 +02:00
return false;
2016-10-30 18:24:19 +01:00
}
2013-03-16 19:13:01 +02:00
std::vector<char const*> args;
args.push_back(this->CommandLineTool.c_str());
2020-08-30 11:54:41 +02:00
cm::append(args, parameters);
2013-03-16 19:13:01 +02:00
args.push_back("--non-interactive");
2016-07-09 11:21:54 +02:00
std::string userOptions = this->CTest->GetCTestConfiguration("SVNOptions");
2013-03-16 19:13:01 +02:00
2015-04-27 22:25:09 +02:00
std::vector<std::string> parsedUserOptions =
2019-11-11 23:01:05 +01:00
cmSystemTools::ParseArguments(userOptions);
2018-01-26 17:06:56 +01:00
for (std::string const& opt : parsedUserOptions) {
args.push_back(opt.c_str());
2016-07-09 11:21:54 +02:00
}
2013-03-16 19:13:01 +02:00
2018-01-26 17:06:56 +01:00
args.push_back(nullptr);
2013-03-16 19:13:01 +02:00
2016-07-09 11:21:54 +02:00
if (strcmp(parameters[0], "update") == 0) {
2022-08-04 22:12:04 +02:00
return this->RunUpdateCommand(args.data(), out, err);
2016-07-09 11:21:54 +02:00
}
2022-08-04 22:12:04 +02:00
return this->RunChild(args.data(), out, err);
2009-10-04 10:30:41 +03:00
}
2018-08-09 18:06:22 +02:00
class cmCTestSVN::LogParser
: public cmCTestVC::OutputLogger
, private cmXMLParser
2009-10-04 10:30:41 +03:00
{
public:
2016-07-09 11:21:54 +02:00
LogParser(cmCTestSVN* svn, const char* prefix, SVNInfo& svninfo)
: OutputLogger(svn->Log, prefix)
, SVN(svn)
, SVNRepo(svninfo)
{
this->InitializeParser();
}
2018-01-26 17:06:56 +01:00
~LogParser() override { this->CleanupParser(); }
2018-08-09 18:06:22 +02:00
2009-10-04 10:30:41 +03:00
private:
cmCTestSVN* SVN;
2013-03-16 19:13:01 +02:00
cmCTestSVN::SVNInfo& SVNRepo;
2009-10-04 10:30:41 +03:00
2020-02-01 23:06:01 +01:00
using Revision = cmCTestSVN::Revision;
using Change = cmCTestSVN::Change;
2009-10-04 10:30:41 +03:00
Revision Rev;
std::vector<Change> Changes;
Change CurChange;
std::vector<char> CData;
2018-01-26 17:06:56 +01:00
bool ProcessChunk(const char* data, int length) override
2016-07-09 11:21:54 +02:00
{
2009-10-04 10:30:41 +03:00
this->OutputLogger::ProcessChunk(data, length);
this->ParseChunk(data, length);
return true;
2016-07-09 11:21:54 +02:00
}
2009-10-04 10:30:41 +03:00
2018-01-26 17:06:56 +01:00
void StartElement(const std::string& name, const char** atts) override
2016-07-09 11:21:54 +02:00
{
2009-10-04 10:30:41 +03:00
this->CData.clear();
2016-07-09 11:21:54 +02:00
if (name == "logentry") {
2009-10-04 10:30:41 +03:00
this->Rev = Revision();
2021-09-14 00:13:48 +02:00
this->Rev.SVNInfo = &this->SVNRepo;
2019-11-11 23:01:05 +01:00
if (const char* rev =
cmCTestSVN::LogParser::FindAttribute(atts, "revision")) {
2009-10-04 10:30:41 +03:00
this->Rev.Rev = rev;
}
2016-07-09 11:21:54 +02:00
this->Changes.clear();
} else if (name == "path") {
2009-10-04 10:30:41 +03:00
this->CurChange = Change();
2019-11-11 23:01:05 +01:00
if (const char* action =
cmCTestSVN::LogParser::FindAttribute(atts, "action")) {
2009-10-04 10:30:41 +03:00
this->CurChange.Action = action[0];
}
}
2016-07-09 11:21:54 +02:00
}
2009-10-04 10:30:41 +03:00
2018-01-26 17:06:56 +01:00
void CharacterDataHandler(const char* data, int length) override
2016-07-09 11:21:54 +02:00
{
2020-08-30 11:54:41 +02:00
cm::append(this->CData, data, data + length);
2016-07-09 11:21:54 +02:00
}
2009-10-04 10:30:41 +03:00
2018-01-26 17:06:56 +01:00
void EndElement(const std::string& name) override
2016-07-09 11:21:54 +02:00
{
if (name == "logentry") {
2013-03-16 19:13:01 +02:00
this->SVN->DoRevisionSVN(this->Rev, this->Changes);
2016-07-09 11:21:54 +02:00
} else if (!this->CData.empty() && name == "path") {
2022-08-04 22:12:04 +02:00
std::string orig_path(this->CData.data(), this->CData.size());
2021-09-14 00:13:48 +02:00
std::string new_path = this->SVNRepo.BuildLocalPath(orig_path);
2013-03-16 19:13:01 +02:00
this->CurChange.Path.assign(new_path);
2009-10-04 10:30:41 +03:00
this->Changes.push_back(this->CurChange);
2016-07-09 11:21:54 +02:00
} else if (!this->CData.empty() && name == "author") {
2022-08-04 22:12:04 +02:00
this->Rev.Author.assign(this->CData.data(), this->CData.size());
2016-07-09 11:21:54 +02:00
} else if (!this->CData.empty() && name == "date") {
2022-08-04 22:12:04 +02:00
this->Rev.Date.assign(this->CData.data(), this->CData.size());
2016-07-09 11:21:54 +02:00
} else if (!this->CData.empty() && name == "msg") {
2022-08-04 22:12:04 +02:00
this->Rev.Log.assign(this->CData.data(), this->CData.size());
2009-10-04 10:30:41 +03:00
}
2016-07-09 11:21:54 +02:00
this->CData.clear();
}
2009-10-04 10:30:41 +03:00
2018-01-26 17:06:56 +01:00
void ReportError(int /*line*/, int /*column*/, const char* msg) override
2016-07-09 11:21:54 +02:00
{
2009-10-04 10:30:41 +03:00
this->SVN->Log << "Error parsing svn log xml: " << msg << "\n";
2016-07-09 11:21:54 +02:00
}
2009-10-04 10:30:41 +03:00
};
2017-07-20 19:35:53 +02:00
bool cmCTestSVN::LoadRevisions()
2013-03-16 19:13:01 +02:00
{
2017-07-20 19:35:53 +02:00
bool result = true;
2013-03-16 19:13:01 +02:00
// Get revisions for all the external repositories
2018-01-26 17:06:56 +01:00
for (SVNInfo& svninfo : this->Repositories) {
2017-07-20 19:35:53 +02:00
result = this->LoadRevisions(svninfo) && result;
2016-07-09 11:21:54 +02:00
}
2017-07-20 19:35:53 +02:00
return result;
2013-03-16 19:13:01 +02:00
}
2017-07-20 19:35:53 +02:00
bool cmCTestSVN::LoadRevisions(SVNInfo& svninfo)
2009-10-04 10:30:41 +03:00
{
// We are interested in every revision included in the update.
std::string revs;
2016-07-09 11:21:54 +02:00
if (atoi(svninfo.OldRevision.c_str()) < atoi(svninfo.NewRevision.c_str())) {
2013-03-16 19:13:01 +02:00
revs = "-r" + svninfo.OldRevision + ":" + svninfo.NewRevision;
2016-07-09 11:21:54 +02:00
} else {
2013-03-16 19:13:01 +02:00
revs = "-r" + svninfo.NewRevision;
2016-07-09 11:21:54 +02:00
}
2009-10-04 10:30:41 +03:00
// Run "svn log" to get all global revisions of interest.
2013-03-16 19:13:01 +02:00
std::vector<const char*> svn_log;
svn_log.push_back("log");
svn_log.push_back("--xml");
svn_log.push_back("-v");
svn_log.push_back(revs.c_str());
svn_log.push_back(svninfo.LocalPath.c_str());
LogParser out(this, "log-out> ", svninfo);
2009-10-04 10:30:41 +03:00
OutputLogger err(this->Log, "log-err> ");
2017-07-20 19:35:53 +02:00
return this->RunSVNCommand(svn_log, &out, &err);
2009-10-04 10:30:41 +03:00
}
2013-03-16 19:13:01 +02:00
void cmCTestSVN::DoRevisionSVN(Revision const& revision,
std::vector<Change> const& changes)
2009-10-04 10:30:41 +03:00
{
// Guess the base checkout path from the changes if necessary.
2016-07-09 11:21:54 +02:00
if (this->RootInfo->Base.empty() && !changes.empty()) {
2013-03-16 19:13:01 +02:00
this->GuessBase(*this->RootInfo, changes);
2016-07-09 11:21:54 +02:00
}
2013-03-16 19:13:01 +02:00
// Ignore changes in the old revision for external repositories
2016-07-09 11:21:54 +02:00
if (revision.Rev == revision.SVNInfo->OldRevision &&
2018-01-26 17:06:56 +01:00
!revision.SVNInfo->LocalPath.empty()) {
2013-03-16 19:13:01 +02:00
return;
2016-07-09 11:21:54 +02:00
}
2013-03-16 19:13:01 +02:00
2009-10-04 10:30:41 +03:00
this->cmCTestGlobalVC::DoRevision(revision, changes);
}
2016-07-09 11:21:54 +02:00
class cmCTestSVN::StatusParser : public cmCTestVC::LineParser
2009-10-04 10:30:41 +03:00
{
public:
2016-07-09 11:21:54 +02:00
StatusParser(cmCTestSVN* svn, const char* prefix)
: SVN(svn)
{
2009-10-04 10:30:41 +03:00
this->SetLog(&svn->Log, prefix);
this->RegexStatus.compile("^([ACDIMRX?!~ ])([CM ])[ L]... +(.+)$");
2016-07-09 11:21:54 +02:00
}
2009-10-04 10:30:41 +03:00
private:
cmCTestSVN* SVN;
cmsys::RegularExpression RegexStatus;
2018-01-26 17:06:56 +01:00
bool ProcessLine() override
2016-07-09 11:21:54 +02:00
{
if (this->RegexStatus.find(this->Line)) {
2009-10-04 10:30:41 +03:00
this->DoPath(this->RegexStatus.match(1)[0],
2016-07-09 11:21:54 +02:00
this->RegexStatus.match(2)[0], this->RegexStatus.match(3));
2009-10-04 10:30:41 +03:00
}
2016-07-09 11:21:54 +02:00
return true;
}
2009-10-04 10:30:41 +03:00
void DoPath(char path_status, char prop_status, std::string const& path)
2016-07-09 11:21:54 +02:00
{
char status = (path_status != ' ') ? path_status : prop_status;
2009-10-04 10:30:41 +03:00
// See "svn help status".
2016-07-09 11:21:54 +02:00
switch (status) {
case 'M':
case '!':
case 'A':
case 'D':
case 'R':
2009-10-04 10:30:41 +03:00
this->SVN->DoModification(PathModified, path);
break;
2016-07-09 11:21:54 +02:00
case 'C':
case '~':
2009-10-04 10:30:41 +03:00
this->SVN->DoModification(PathConflicting, path);
break;
2016-07-09 11:21:54 +02:00
case 'X':
case 'I':
case '?':
case ' ':
default:
2009-10-04 10:30:41 +03:00
break;
}
2016-07-09 11:21:54 +02:00
}
2009-10-04 10:30:41 +03:00
};
2017-07-20 19:35:53 +02:00
bool cmCTestSVN::LoadModifications()
2009-10-04 10:30:41 +03:00
{
// Run "svn status" which reports local modifications.
2013-03-16 19:13:01 +02:00
std::vector<const char*> svn_status;
svn_status.push_back("status");
2009-10-04 10:30:41 +03:00
StatusParser out(this, "status-out> ");
OutputLogger err(this->Log, "status-err> ");
2013-03-16 19:13:01 +02:00
this->RunSVNCommand(svn_status, &out, &err);
2017-07-20 19:35:53 +02:00
return true;
2009-10-04 10:30:41 +03:00
}
2010-03-17 14:00:29 +02:00
2015-08-17 11:37:30 +02:00
void cmCTestSVN::WriteXMLGlobal(cmXMLWriter& xml)
2010-03-17 14:00:29 +02:00
{
this->cmCTestGlobalVC::WriteXMLGlobal(xml);
2015-08-17 11:37:30 +02:00
xml.Element("SVNPath", this->RootInfo->Base);
2013-03-16 19:13:01 +02:00
}
2016-07-09 11:21:54 +02:00
class cmCTestSVN::ExternalParser : public cmCTestVC::LineParser
2013-03-16 19:13:01 +02:00
{
public:
2016-07-09 11:21:54 +02:00
ExternalParser(cmCTestSVN* svn, const char* prefix)
: SVN(svn)
{
2013-03-16 19:13:01 +02:00
this->SetLog(&svn->Log, prefix);
this->RegexExternal.compile("^X..... +(.+)$");
2016-07-09 11:21:54 +02:00
}
2013-03-16 19:13:01 +02:00
private:
cmCTestSVN* SVN;
cmsys::RegularExpression RegexExternal;
2018-01-26 17:06:56 +01:00
bool ProcessLine() override
2016-07-09 11:21:54 +02:00
{
if (this->RegexExternal.find(this->Line)) {
2013-03-16 19:13:01 +02:00
this->DoPath(this->RegexExternal.match(1));
}
2016-07-09 11:21:54 +02:00
return true;
}
2013-03-16 19:13:01 +02:00
void DoPath(std::string const& path)
2016-07-09 11:21:54 +02:00
{
2013-03-16 19:13:01 +02:00
// Get local path relative to the source directory
std::string local_path;
2016-07-09 11:21:54 +02:00
if (path.size() > this->SVN->SourceDirectory.size() &&
strncmp(path.c_str(), this->SVN->SourceDirectory.c_str(),
this->SVN->SourceDirectory.size()) == 0) {
2019-11-11 23:01:05 +01:00
local_path = path.substr(this->SVN->SourceDirectory.size() + 1);
2016-07-09 11:21:54 +02:00
} else {
2013-03-16 19:13:01 +02:00
local_path = path;
}
2019-11-11 23:01:05 +01:00
this->SVN->Repositories.emplace_back(local_path);
2016-07-09 11:21:54 +02:00
}
2013-03-16 19:13:01 +02:00
};
2017-07-20 19:35:53 +02:00
bool cmCTestSVN::LoadRepositories()
2013-03-16 19:13:01 +02:00
{
2017-04-14 19:02:05 +02:00
if (!this->Repositories.empty()) {
2017-07-20 19:35:53 +02:00
return true;
2017-04-14 19:02:05 +02:00
}
2016-10-30 18:24:19 +01:00
// Info for root repository
2019-11-11 23:01:05 +01:00
this->Repositories.emplace_back();
2016-10-30 18:24:19 +01:00
this->RootInfo = &(this->Repositories.back());
2013-03-16 19:13:01 +02:00
// Run "svn status" to get the list of external repositories
std::vector<const char*> svn_status;
svn_status.push_back("status");
ExternalParser out(this, "external-out> ");
OutputLogger err(this->Log, "external-err> ");
2017-07-20 19:35:53 +02:00
return this->RunSVNCommand(svn_status, &out, &err);
2013-03-16 19:13:01 +02:00
}
std::string cmCTestSVN::SVNInfo::BuildLocalPath(std::string const& path) const
{
std::string local_path;
// Add local path prefix if not empty
2016-07-09 11:21:54 +02:00
if (!this->LocalPath.empty()) {
2013-03-16 19:13:01 +02:00
local_path += this->LocalPath;
local_path += "/";
2016-07-09 11:21:54 +02:00
}
2013-03-16 19:13:01 +02:00
// Add path with base prefix removed
2016-07-09 11:21:54 +02:00
if (path.size() > this->Base.size() &&
strncmp(path.c_str(), this->Base.c_str(), this->Base.size()) == 0) {
2019-11-11 23:01:05 +01:00
local_path += path.substr(this->Base.size());
2016-07-09 11:21:54 +02:00
} else {
2013-03-16 19:13:01 +02:00
local_path += path;
}
return local_path;
2010-03-17 14:00:29 +02:00
}