1
0

cmCTestCVS.cxx 10 KB

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