cmCTestCVS.cxx 10.0 KB

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