cmCTestCVS.cxx 10.0 KB

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