cmCTestCVS.cxx 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. /*============================================================================
  2. CMake - Cross Platform Makefile Generator
  3. Copyright 2000-2009 Kitware, Inc.
  4. Distributed under the OSI-approved BSD License (the "License");
  5. see accompanying file Copyright.txt for details.
  6. This software is distributed WITHOUT ANY WARRANTY; without even the
  7. implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  8. See the License for more information.
  9. ============================================================================*/
  10. #include "cmCTestCVS.h"
  11. #include "cmCTest.h"
  12. #include "cmProcessTools.h"
  13. #include "cmSystemTools.h"
  14. #include "cmXMLWriter.h"
  15. #include <cmsys/FStream.hxx>
  16. #include <cmsys/RegularExpression.hxx>
  17. #include <utility>
  18. cmCTestCVS::cmCTestCVS(cmCTest* ct, std::ostream& log)
  19. : cmCTestVC(ct, log)
  20. {
  21. }
  22. cmCTestCVS::~cmCTestCVS()
  23. {
  24. }
  25. class cmCTestCVS::UpdateParser : public cmCTestVC::LineParser
  26. {
  27. public:
  28. UpdateParser(cmCTestCVS* cvs, const char* prefix)
  29. : CVS(cvs)
  30. {
  31. this->SetLog(&cvs->Log, prefix);
  32. // See "man cvs", section "update output".
  33. this->RegexFileUpdated.compile("^([UP]) *(.*)");
  34. this->RegexFileModified.compile("^([MRA]) *(.*)");
  35. this->RegexFileConflicting.compile("^([C]) *(.*)");
  36. this->RegexFileRemoved1.compile(
  37. "cvs[^ ]* update: `?([^']*)'? is no longer in the repository");
  38. this->RegexFileRemoved2.compile(
  39. "cvs[^ ]* update: "
  40. "warning: `?([^']*)'? is not \\(any longer\\) pertinent");
  41. }
  42. private:
  43. cmCTestCVS* CVS;
  44. cmsys::RegularExpression RegexFileUpdated;
  45. cmsys::RegularExpression RegexFileModified;
  46. cmsys::RegularExpression RegexFileConflicting;
  47. cmsys::RegularExpression RegexFileRemoved1;
  48. cmsys::RegularExpression RegexFileRemoved2;
  49. bool ProcessLine() CM_OVERRIDE
  50. {
  51. if (this->RegexFileUpdated.find(this->Line)) {
  52. this->DoFile(PathUpdated, this->RegexFileUpdated.match(2));
  53. } else if (this->RegexFileModified.find(this->Line)) {
  54. this->DoFile(PathModified, this->RegexFileModified.match(2));
  55. } else if (this->RegexFileConflicting.find(this->Line)) {
  56. this->DoFile(PathConflicting, this->RegexFileConflicting.match(2));
  57. } else if (this->RegexFileRemoved1.find(this->Line)) {
  58. this->DoFile(PathUpdated, this->RegexFileRemoved1.match(1));
  59. } else if (this->RegexFileRemoved2.find(this->Line)) {
  60. this->DoFile(PathUpdated, this->RegexFileRemoved2.match(1));
  61. }
  62. return true;
  63. }
  64. void DoFile(PathStatus status, std::string const& file)
  65. {
  66. std::string dir = cmSystemTools::GetFilenamePath(file);
  67. std::string name = cmSystemTools::GetFilenameName(file);
  68. this->CVS->Dirs[dir][name] = status;
  69. }
  70. };
  71. bool cmCTestCVS::UpdateImpl()
  72. {
  73. // Get user-specified update options.
  74. std::string opts = this->CTest->GetCTestConfiguration("UpdateOptions");
  75. if (opts.empty()) {
  76. opts = this->CTest->GetCTestConfiguration("CVSUpdateOptions");
  77. if (opts.empty()) {
  78. opts = "-dP";
  79. }
  80. }
  81. std::vector<std::string> args = cmSystemTools::ParseArguments(opts.c_str());
  82. // Specify the start time for nightly testing.
  83. if (this->CTest->GetTestModel() == cmCTest::NIGHTLY) {
  84. args.push_back("-D" + this->GetNightlyTime() + " UTC");
  85. }
  86. // Run "cvs update" to update the work tree.
  87. std::vector<char const*> cvs_update;
  88. cvs_update.push_back(this->CommandLineTool.c_str());
  89. cvs_update.push_back("-z3");
  90. cvs_update.push_back("update");
  91. for (std::vector<std::string>::const_iterator ai = args.begin();
  92. ai != args.end(); ++ai) {
  93. cvs_update.push_back(ai->c_str());
  94. }
  95. cvs_update.push_back(CM_NULLPTR);
  96. UpdateParser out(this, "up-out> ");
  97. UpdateParser err(this, "up-err> ");
  98. return this->RunUpdateCommand(&cvs_update[0], &out, &err);
  99. }
  100. class cmCTestCVS::LogParser : public cmCTestVC::LineParser
  101. {
  102. public:
  103. typedef cmCTestCVS::Revision Revision;
  104. LogParser(cmCTestCVS* cvs, const char* prefix, std::vector<Revision>& revs)
  105. : CVS(cvs)
  106. , Revisions(revs)
  107. , Section(SectionHeader)
  108. {
  109. this->SetLog(&cvs->Log, prefix),
  110. this->RegexRevision.compile("^revision +([^ ]*) *$");
  111. this->RegexBranches.compile("^branches: .*$");
  112. this->RegexPerson.compile("^date: +([^;]+); +author: +([^;]+);");
  113. }
  114. private:
  115. cmCTestCVS* CVS;
  116. std::vector<Revision>& Revisions;
  117. cmsys::RegularExpression RegexRevision;
  118. cmsys::RegularExpression RegexBranches;
  119. cmsys::RegularExpression RegexPerson;
  120. enum SectionType
  121. {
  122. SectionHeader,
  123. SectionRevisions,
  124. SectionEnd
  125. };
  126. SectionType Section;
  127. Revision Rev;
  128. bool ProcessLine() CM_OVERRIDE
  129. {
  130. if (this->Line == ("======================================="
  131. "======================================")) {
  132. // This line ends the revision list.
  133. if (this->Section == SectionRevisions) {
  134. this->FinishRevision();
  135. }
  136. this->Section = SectionEnd;
  137. } else if (this->Line == "----------------------------") {
  138. // This line divides revisions from the header and each other.
  139. if (this->Section == SectionHeader) {
  140. this->Section = SectionRevisions;
  141. } else if (this->Section == SectionRevisions) {
  142. this->FinishRevision();
  143. }
  144. } else if (this->Section == SectionRevisions) {
  145. if (!this->Rev.Log.empty()) {
  146. // Continue the existing log.
  147. this->Rev.Log += this->Line;
  148. this->Rev.Log += "\n";
  149. } else if (this->Rev.Rev.empty() &&
  150. this->RegexRevision.find(this->Line)) {
  151. this->Rev.Rev = this->RegexRevision.match(1);
  152. } else if (this->Rev.Date.empty() &&
  153. this->RegexPerson.find(this->Line)) {
  154. this->Rev.Date = this->RegexPerson.match(1);
  155. this->Rev.Author = this->RegexPerson.match(2);
  156. } else if (!this->RegexBranches.find(this->Line)) {
  157. // Start the log.
  158. this->Rev.Log += this->Line;
  159. this->Rev.Log += "\n";
  160. }
  161. }
  162. return this->Section != SectionEnd;
  163. }
  164. void FinishRevision()
  165. {
  166. if (!this->Rev.Rev.empty()) {
  167. // Record this revision.
  168. /* clang-format off */
  169. this->CVS->Log << "Found revision " << this->Rev.Rev << "\n"
  170. << " author = " << this->Rev.Author << "\n"
  171. << " date = " << this->Rev.Date << "\n";
  172. /* clang-format on */
  173. this->Revisions.push_back(this->Rev);
  174. // We only need two revisions.
  175. if (this->Revisions.size() >= 2) {
  176. this->Section = SectionEnd;
  177. }
  178. }
  179. this->Rev = Revision();
  180. }
  181. };
  182. std::string cmCTestCVS::ComputeBranchFlag(std::string const& dir)
  183. {
  184. // Compute the tag file location for this directory.
  185. std::string tagFile = this->SourceDirectory;
  186. if (!dir.empty()) {
  187. tagFile += "/";
  188. tagFile += dir;
  189. }
  190. tagFile += "/CVS/Tag";
  191. // Lookup the branch in the tag file, if any.
  192. std::string tagLine;
  193. cmsys::ifstream tagStream(tagFile.c_str());
  194. if (tagStream && cmSystemTools::GetLineFromStream(tagStream, tagLine) &&
  195. tagLine.size() > 1 && tagLine[0] == 'T') {
  196. // Use the branch specified in the tag file.
  197. std::string flag = "-r";
  198. flag += tagLine.substr(1);
  199. return flag;
  200. }
  201. // Use the default branch.
  202. return "-b";
  203. }
  204. void cmCTestCVS::LoadRevisions(std::string const& file, const char* branchFlag,
  205. std::vector<Revision>& revisions)
  206. {
  207. cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush);
  208. // Run "cvs log" to get revisions of this file on this branch.
  209. const char* cvs = this->CommandLineTool.c_str();
  210. const char* cvs_log[] = { cvs, "log", "-N",
  211. branchFlag, file.c_str(), CM_NULLPTR };
  212. LogParser out(this, "log-out> ", revisions);
  213. OutputLogger err(this->Log, "log-err> ");
  214. this->RunChild(cvs_log, &out, &err);
  215. }
  216. void cmCTestCVS::WriteXMLDirectory(cmXMLWriter& xml, std::string const& path,
  217. Directory const& dir)
  218. {
  219. const char* slash = path.empty() ? "" : "/";
  220. xml.StartElement("Directory");
  221. xml.Element("Name", path);
  222. // Lookup the branch checked out in the working tree.
  223. std::string branchFlag = this->ComputeBranchFlag(path);
  224. // Load revisions and write an entry for each file in this directory.
  225. std::vector<Revision> revisions;
  226. for (Directory::const_iterator fi = dir.begin(); fi != dir.end(); ++fi) {
  227. std::string full = path + slash + fi->first;
  228. // Load two real or unknown revisions.
  229. revisions.clear();
  230. if (fi->second != PathUpdated) {
  231. // For local modifications the current rev is unknown and the
  232. // prior rev is the latest from cvs.
  233. revisions.push_back(this->Unknown);
  234. }
  235. this->LoadRevisions(full, branchFlag.c_str(), revisions);
  236. revisions.resize(2, this->Unknown);
  237. // Write the entry for this file with these revisions.
  238. File f(fi->second, &revisions[0], &revisions[1]);
  239. this->WriteXMLEntry(xml, path, fi->first, full, f);
  240. }
  241. xml.EndElement(); // Directory
  242. }
  243. bool cmCTestCVS::WriteXMLUpdates(cmXMLWriter& xml)
  244. {
  245. cmCTestLog(this->CTest, HANDLER_OUTPUT,
  246. " Gathering version information (one . per updated file):\n"
  247. " "
  248. << std::flush);
  249. for (std::map<std::string, Directory>::const_iterator di =
  250. this->Dirs.begin();
  251. di != this->Dirs.end(); ++di) {
  252. this->WriteXMLDirectory(xml, di->first, di->second);
  253. }
  254. cmCTestLog(this->CTest, HANDLER_OUTPUT, std::endl);
  255. return true;
  256. }