| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 | /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying   file Copyright.txt or https://cmake.org/licensing for details.  */#include "cmCTestCVS.h"#include "cmCTest.h"#include "cmProcessTools.h"#include "cmSystemTools.h"#include "cmXMLWriter.h"#include "cmsys/FStream.hxx"#include "cmsys/RegularExpression.hxx"#include <utility>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;  bool ProcessLine() override  {    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<std::string> 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::string const& arg : args) {    cvs_update.push_back(arg.c_str());  }  cvs_update.push_back(nullptr);  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;  bool ProcessLine() override  {    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.      /* clang-format off */      this->CVS->Log << "Found revision " << this->Rev.Rev << "\n"                     << "  author = " << this->Rev.Author << "\n"                     << "  date = " << this->Rev.Date << "\n";      /* clang-format on */      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;  cmsys::ifstream tagStream(tagFile.c_str());  if (tagStream && 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;  }  // 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(), nullptr  };  LogParser out(this, "log-out> ", revisions);  OutputLogger err(this->Log, "log-err> ");  this->RunChild(cvs_log, &out, &err);}void cmCTestCVS::WriteXMLDirectory(cmXMLWriter& xml, std::string const& path,                                   Directory const& dir){  const char* slash = path.empty() ? "" : "/";  xml.StartElement("Directory");  xml.Element("Name", path);  // 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 (auto const& fi : dir) {    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.EndElement(); // Directory}bool cmCTestCVS::WriteXMLUpdates(cmXMLWriter& xml){  cmCTestLog(this->CTest, HANDLER_OUTPUT,             "   Gathering version information (one . per updated file):\n"             "    "               << std::flush);  for (auto const& d : this->Dirs) {    this->WriteXMLDirectory(xml, d.first, d.second);  }  cmCTestLog(this->CTest, HANDLER_OUTPUT, std::endl);  return true;}
 |