ENH: check in almost building VMS stuff with VMSBuild directory since the bootstrap...
[cmake.git] / Source / CTest / cmCTestCVS.cxx
blob6cd2e0ca1d9e0cf3e67e058d809a22ff4b52c5b5
1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCTestCVS.cxx,v $
5 Language: C++
6 Date: $Date: 2009-02-25 19:42:45 $
7 Version: $Revision: 1.2 $
9 Copyright (c) 2002 Kitware, Inc. All rights reserved.
10 See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
12 This software is distributed WITHOUT ANY WARRANTY; without even
13 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 PURPOSE. See the above copyright notices for more information.
16 =========================================================================*/
17 #include "cmCTestCVS.h"
19 #include "cmCTest.h"
20 #include "cmSystemTools.h"
21 #include "cmXMLSafe.h"
23 #include <cmsys/RegularExpression.hxx>
25 //----------------------------------------------------------------------------
26 cmCTestCVS::cmCTestCVS(cmCTest* ct, std::ostream& log): cmCTestVC(ct, log)
30 //----------------------------------------------------------------------------
31 cmCTestCVS::~cmCTestCVS()
35 //----------------------------------------------------------------------------
36 class cmCTestCVS::UpdateParser: public cmCTestVC::LineParser
38 public:
39 UpdateParser(cmCTestCVS* cvs, const char* prefix): CVS(cvs)
41 this->SetLog(&cvs->Log, prefix);
42 // See "man cvs", section "update output".
43 this->RegexFileUpdated.compile("^([UP]) *(.*)");
44 this->RegexFileModified.compile("^([MRA]) *(.*)");
45 this->RegexFileConflicting.compile("^([C]) *(.*)");
46 this->RegexFileRemoved1.compile(
47 "cvs update: `?([^']*)'? is no longer in the repository");
48 this->RegexFileRemoved2.compile(
49 "cvs update: warning: `?([^']*)'? is not \\(any longer\\) pertinent");
51 private:
52 cmCTestCVS* CVS;
53 cmsys::RegularExpression RegexFileUpdated;
54 cmsys::RegularExpression RegexFileModified;
55 cmsys::RegularExpression RegexFileConflicting;
56 cmsys::RegularExpression RegexFileRemoved1;
57 cmsys::RegularExpression RegexFileRemoved2;
59 virtual bool ProcessLine()
61 if(this->RegexFileUpdated.find(this->Line))
63 this->DoFile(PathUpdated, this->RegexFileUpdated.match(2));
65 else if(this->RegexFileModified.find(this->Line))
67 this->DoFile(PathModified, this->RegexFileModified.match(2));
69 else if(this->RegexFileConflicting.find(this->Line))
71 this->DoFile(PathConflicting, this->RegexFileConflicting.match(2));
73 else if(this->RegexFileRemoved1.find(this->Line))
75 this->DoFile(PathUpdated, this->RegexFileRemoved1.match(1));
77 else if(this->RegexFileRemoved2.find(this->Line))
79 this->DoFile(PathUpdated, this->RegexFileRemoved2.match(1));
81 return true;
84 void DoFile(PathStatus status, std::string const& file)
86 std::string dir = cmSystemTools::GetFilenamePath(file);
87 std::string name = cmSystemTools::GetFilenameName(file);
88 this->CVS->Dirs[dir][name] = status;
92 //----------------------------------------------------------------------------
93 bool cmCTestCVS::UpdateImpl()
95 // Get user-specified update options.
96 std::string opts = this->CTest->GetCTestConfiguration("UpdateOptions");
97 if(opts.empty())
99 opts = this->CTest->GetCTestConfiguration("CVSUpdateOptions");
100 if(opts.empty())
102 opts = "-dP";
105 std::vector<cmStdString> args = cmSystemTools::ParseArguments(opts.c_str());
107 // Specify the start time for nightly testing.
108 if(this->CTest->GetTestModel() == cmCTest::NIGHTLY)
110 args.push_back("-D" + this->GetNightlyTime() + " UTC");
113 // Run "cvs update" to update the work tree.
114 std::vector<char const*> cvs_update;
115 cvs_update.push_back(this->CommandLineTool.c_str());
116 cvs_update.push_back("-z3");
117 cvs_update.push_back("update");
118 for(std::vector<cmStdString>::const_iterator ai = args.begin();
119 ai != args.end(); ++ai)
121 cvs_update.push_back(ai->c_str());
123 cvs_update.push_back(0);
125 UpdateParser out(this, "up-out> ");
126 UpdateParser err(this, "up-err> ");
127 return this->RunUpdateCommand(&cvs_update[0], &out, &err);
130 //----------------------------------------------------------------------------
131 class cmCTestCVS::LogParser: public cmCTestVC::LineParser
133 public:
134 typedef cmCTestCVS::Revision Revision;
135 LogParser(cmCTestCVS* cvs, const char* prefix, std::vector<Revision>& revs):
136 CVS(cvs), Revisions(revs), Section(SectionHeader)
138 this->SetLog(&cvs->Log, prefix),
139 this->RegexRevision.compile("^revision +([^ ]*) *$");
140 this->RegexBranches.compile("^branches: .*$");
141 this->RegexPerson.compile("^date: +([^;]+); +author: +([^;]+);");
143 private:
144 cmCTestCVS* CVS;
145 std::vector<Revision>& Revisions;
146 cmsys::RegularExpression RegexRevision;
147 cmsys::RegularExpression RegexBranches;
148 cmsys::RegularExpression RegexPerson;
149 enum SectionType { SectionHeader, SectionRevisions, SectionEnd };
150 SectionType Section;
151 Revision Rev;
153 virtual bool ProcessLine()
155 if(this->Line == ("======================================="
156 "======================================"))
158 // This line ends the revision list.
159 if(this->Section == SectionRevisions)
161 this->FinishRevision();
163 this->Section = SectionEnd;
165 else if(this->Line == "----------------------------")
167 // This line divides revisions from the header and each other.
168 if(this->Section == SectionHeader)
170 this->Section = SectionRevisions;
172 else if(this->Section == SectionRevisions)
174 this->FinishRevision();
177 else if(this->Section == SectionRevisions)
179 if(!this->Rev.Log.empty())
181 // Continue the existing log.
182 this->Rev.Log += this->Line;
183 this->Rev.Log += "\n";
185 else if(this->Rev.Rev.empty() && this->RegexRevision.find(this->Line))
187 this->Rev.Rev = this->RegexRevision.match(1);
189 else if(this->Rev.Date.empty() && this->RegexPerson.find(this->Line))
191 this->Rev.Date = this->RegexPerson.match(1);
192 this->Rev.Author = this->RegexPerson.match(2);
194 else if(!this->RegexBranches.find(this->Line))
196 // Start the log.
197 this->Rev.Log += this->Line;
198 this->Rev.Log += "\n";
201 return this->Section != SectionEnd;
204 void FinishRevision()
206 if(!this->Rev.Rev.empty())
208 // Record this revision.
209 this->CVS->Log << "Found revision " << this->Rev.Rev << "\n"
210 << " author = " << this->Rev.Author << "\n"
211 << " date = " << this->Rev.Date << "\n";
212 this->Revisions.push_back(this->Rev);
214 // We only need two revisions.
215 if(this->Revisions.size() >= 2)
217 this->Section = SectionEnd;
220 this->Rev = Revision();
224 //----------------------------------------------------------------------------
225 std::string cmCTestCVS::ComputeBranchFlag(std::string const& dir)
227 // Compute the tag file location for this directory.
228 std::string tagFile = this->SourceDirectory;
229 if(!dir.empty())
231 tagFile += "/";
232 tagFile += dir;
234 tagFile += "/CVS/Tag";
236 // Lookup the branch in the tag file, if any.
237 std::string tagLine;
238 std::ifstream tagStream(tagFile.c_str());
239 if(cmSystemTools::GetLineFromStream(tagStream, tagLine) &&
240 tagLine.size() > 1 && tagLine[0] == 'T')
242 // Use the branch specified in the tag file.
243 std::string flag = "-r";
244 flag += tagLine.substr(1);
245 return flag;
247 else
249 // Use the default branch.
250 return "-b";
254 //----------------------------------------------------------------------------
255 void cmCTestCVS::LoadRevisions(std::string const& file,
256 const char* branchFlag,
257 std::vector<Revision>& revisions)
259 cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush);
261 // Run "cvs log" to get revisions of this file on this branch.
262 const char* cvs = this->CommandLineTool.c_str();
263 const char* cvs_log[] =
264 {cvs, "log", "-N", "-d<now", branchFlag, file.c_str(), 0};
266 LogParser out(this, "log-out> ", revisions);
267 OutputLogger err(this->Log, "log-err> ");
268 this->RunChild(cvs_log, &out, &err);
271 //----------------------------------------------------------------------------
272 void cmCTestCVS::WriteXMLDirectory(std::ostream& xml,
273 std::string const& path,
274 Directory const& dir)
276 const char* slash = path.empty()? "":"/";
277 xml << "\t<Directory>\n"
278 << "\t\t<Name>" << cmXMLSafe(path) << "</Name>\n";
280 // Lookup the branch checked out in the working tree.
281 std::string branchFlag = this->ComputeBranchFlag(path);
283 // Load revisions and write an entry for each file in this directory.
284 std::vector<Revision> revisions;
285 for(Directory::const_iterator fi = dir.begin(); fi != dir.end(); ++fi)
287 std::string full = path + slash + fi->first;
289 // Load two real or unknown revisions.
290 revisions.clear();
291 if(fi->second != PathUpdated)
293 // For local modifications the current rev is unknown and the
294 // prior rev is the latest from cvs.
295 revisions.push_back(this->Unknown);
297 this->LoadRevisions(full, branchFlag.c_str(), revisions);
298 revisions.resize(2, this->Unknown);
300 // Write the entry for this file with these revisions.
301 File f(fi->second, &revisions[0], &revisions[1]);
302 this->WriteXMLEntry(xml, path, fi->first, full, f);
304 xml << "\t</Directory>\n";
307 //----------------------------------------------------------------------------
308 bool cmCTestCVS::WriteXMLUpdates(std::ostream& xml)
310 cmCTestLog(this->CTest, HANDLER_OUTPUT,
311 " Gathering version information (one . per updated file):\n"
312 " " << std::flush);
314 for(std::map<cmStdString, Directory>::const_iterator
315 di = this->Dirs.begin(); di != this->Dirs.end(); ++di)
317 this->WriteXMLDirectory(xml, di->first, di->second);
320 cmCTestLog(this->CTest, HANDLER_OUTPUT, std::endl);
322 return true;