cmCTestCVS.cxx 10 KB

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