1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCTestCVS.cxx,v $
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"
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
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");
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));
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");
99 opts
= this->CTest
->GetCTestConfiguration("CVSUpdateOptions");
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
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: +([^;]+);");
145 std::vector
<Revision
>& Revisions
;
146 cmsys::RegularExpression RegexRevision
;
147 cmsys::RegularExpression RegexBranches
;
148 cmsys::RegularExpression RegexPerson
;
149 enum SectionType
{ SectionHeader
, SectionRevisions
, SectionEnd
};
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
))
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
;
234 tagFile
+= "/CVS/Tag";
236 // Lookup the branch in the tag file, if any.
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);
249 // Use the default branch.
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.
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"
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
);