1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmCTestVC.h"
4
5 #include <cstdio>
6 #include <ctime>
7 #include <sstream>
8 #include <vector>
9
10 #include "cmsys/Process.h"
11
12 #include "cmCTest.h"
13 #include "cmSystemTools.h"
14 #include "cmValue.h"
15 #include "cmXMLWriter.h"
16
cmCTestVC(cmCTest * ct,std::ostream & log)17 cmCTestVC::cmCTestVC(cmCTest* ct, std::ostream& log)
18 : CTest(ct)
19 , Log(log)
20 {
21 this->PathCount[PathUpdated] = 0;
22 this->PathCount[PathModified] = 0;
23 this->PathCount[PathConflicting] = 0;
24 this->Unknown.Date = "Unknown";
25 this->Unknown.Author = "Unknown";
26 this->Unknown.Rev = "Unknown";
27 }
28
29 cmCTestVC::~cmCTestVC() = default;
30
SetCommandLineTool(std::string const & tool)31 void cmCTestVC::SetCommandLineTool(std::string const& tool)
32 {
33 this->CommandLineTool = tool;
34 }
35
SetSourceDirectory(std::string const & dir)36 void cmCTestVC::SetSourceDirectory(std::string const& dir)
37 {
38 this->SourceDirectory = dir;
39 }
40
InitialCheckout(const std::string & command)41 bool cmCTestVC::InitialCheckout(const std::string& command)
42 {
43 cmCTestLog(this->CTest, HANDLER_OUTPUT,
44 " First perform the initial checkout: " << command << "\n");
45
46 // Make the parent directory in which to perform the checkout.
47 std::string parent = cmSystemTools::GetFilenamePath(this->SourceDirectory);
48 cmCTestLog(this->CTest, HANDLER_OUTPUT,
49 " Perform checkout in directory: " << parent << "\n");
50 if (!cmSystemTools::MakeDirectory(parent)) {
51 cmCTestLog(this->CTest, ERROR_MESSAGE,
52 "Cannot create directory: " << parent << std::endl);
53 return false;
54 }
55
56 // Construct the initial checkout command line.
57 std::vector<std::string> args = cmSystemTools::ParseArguments(command);
58 std::vector<char const*> vc_co;
59 vc_co.reserve(args.size() + 1);
60 for (std::string const& arg : args) {
61 vc_co.push_back(arg.c_str());
62 }
63 vc_co.push_back(nullptr);
64
65 // Run the initial checkout command and log its output.
66 this->Log << "--- Begin Initial Checkout ---\n";
67 OutputLogger out(this->Log, "co-out> ");
68 OutputLogger err(this->Log, "co-err> ");
69 bool result = this->RunChild(&vc_co[0], &out, &err, parent.c_str());
70 this->Log << "--- End Initial Checkout ---\n";
71 if (!result) {
72 cmCTestLog(this->CTest, ERROR_MESSAGE,
73 "Initial checkout failed!" << std::endl);
74 }
75 return result;
76 }
77
RunChild(char const * const * cmd,OutputParser * out,OutputParser * err,const char * workDir,Encoding encoding)78 bool cmCTestVC::RunChild(char const* const* cmd, OutputParser* out,
79 OutputParser* err, const char* workDir,
80 Encoding encoding)
81 {
82 this->Log << cmCTestVC::ComputeCommandLine(cmd) << "\n";
83
84 cmsysProcess* cp = cmsysProcess_New();
85 cmsysProcess_SetCommand(cp, cmd);
86 workDir = workDir ? workDir : this->SourceDirectory.c_str();
87 cmsysProcess_SetWorkingDirectory(cp, workDir);
88 cmCTestVC::RunProcess(cp, out, err, encoding);
89 int result = cmsysProcess_GetExitValue(cp);
90 cmsysProcess_Delete(cp);
91 return result == 0;
92 }
93
ComputeCommandLine(char const * const * cmd)94 std::string cmCTestVC::ComputeCommandLine(char const* const* cmd)
95 {
96 std::ostringstream line;
97 const char* sep = "";
98 for (const char* const* arg = cmd; *arg; ++arg) {
99 line << sep << "\"" << *arg << "\"";
100 sep = " ";
101 }
102 return line.str();
103 }
104
RunUpdateCommand(char const * const * cmd,OutputParser * out,OutputParser * err,Encoding encoding)105 bool cmCTestVC::RunUpdateCommand(char const* const* cmd, OutputParser* out,
106 OutputParser* err, Encoding encoding)
107 {
108 // Report the command line.
109 this->UpdateCommandLine = this->ComputeCommandLine(cmd);
110 if (this->CTest->GetShowOnly()) {
111 this->Log << this->UpdateCommandLine << "\n";
112 return true;
113 }
114
115 // Run the command.
116 return this->RunChild(cmd, out, err, nullptr, encoding);
117 }
118
GetNightlyTime()119 std::string cmCTestVC::GetNightlyTime()
120 {
121 // Get the nightly start time corresponding to the current dau.
122 struct tm* t = this->CTest->GetNightlyTime(
123 this->CTest->GetCTestConfiguration("NightlyStartTime"),
124 this->CTest->GetTomorrowTag());
125 char current_time[1024];
126 sprintf(current_time, "%04d-%02d-%02d %02d:%02d:%02d", t->tm_year + 1900,
127 t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
128 return std::string(current_time);
129 }
130
Cleanup()131 void cmCTestVC::Cleanup()
132 {
133 this->Log << "--- Begin Cleanup ---\n";
134 this->CleanupImpl();
135 this->Log << "--- End Cleanup ---\n";
136 }
137
CleanupImpl()138 void cmCTestVC::CleanupImpl()
139 {
140 // We do no cleanup by default.
141 }
142
Update()143 bool cmCTestVC::Update()
144 {
145 bool result = true;
146
147 // Use the explicitly specified version.
148 std::string versionOverride =
149 this->CTest->GetCTestConfiguration("UpdateVersionOverride");
150 if (!versionOverride.empty()) {
151 this->SetNewRevision(versionOverride);
152 return true;
153 }
154
155 // if update version only is on then do not actually update,
156 // just note the current version and finish
157 if (!cmIsOn(this->CTest->GetCTestConfiguration("UpdateVersionOnly"))) {
158 result = this->NoteOldRevision() && result;
159 this->Log << "--- Begin Update ---\n";
160 result = this->UpdateImpl() && result;
161 this->Log << "--- End Update ---\n";
162 }
163 result = this->NoteNewRevision() && result;
164 return result;
165 }
166
NoteOldRevision()167 bool cmCTestVC::NoteOldRevision()
168 {
169 // We do nothing by default.
170 return true;
171 }
172
NoteNewRevision()173 bool cmCTestVC::NoteNewRevision()
174 {
175 // We do nothing by default.
176 return true;
177 }
178
SetNewRevision(std::string const &)179 void cmCTestVC::SetNewRevision(std::string const& /*unused*/)
180 {
181 // We do nothing by default.
182 }
183
UpdateImpl()184 bool cmCTestVC::UpdateImpl()
185 {
186 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
187 "* Unknown VCS tool, not updating!" << std::endl);
188 return true;
189 }
190
WriteXML(cmXMLWriter & xml)191 bool cmCTestVC::WriteXML(cmXMLWriter& xml)
192 {
193 this->Log << "--- Begin Revisions ---\n";
194 bool result = this->WriteXMLUpdates(xml);
195 this->Log << "--- End Revisions ---\n";
196 return result;
197 }
198
WriteXMLUpdates(cmXMLWriter &)199 bool cmCTestVC::WriteXMLUpdates(cmXMLWriter& /*unused*/)
200 {
201 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
202 "* CTest cannot extract updates for this VCS tool.\n");
203 return true;
204 }
205
WriteXMLEntry(cmXMLWriter & xml,std::string const & path,std::string const & name,std::string const & full,File const & f)206 void cmCTestVC::WriteXMLEntry(cmXMLWriter& xml, std::string const& path,
207 std::string const& name, std::string const& full,
208 File const& f)
209 {
210 static const char* desc[3] = { "Updated", "Modified", "Conflicting" };
211 Revision const& rev = f.Rev ? *f.Rev : this->Unknown;
212 std::string prior = f.PriorRev ? f.PriorRev->Rev : std::string("Unknown");
213 xml.StartElement(desc[f.Status]);
214 xml.Element("File", name);
215 xml.Element("Directory", path);
216 xml.Element("FullName", full);
217 xml.Element("CheckinDate", rev.Date);
218 xml.Element("Author", rev.Author);
219 xml.Element("Email", rev.EMail);
220 xml.Element("Committer", rev.Committer);
221 xml.Element("CommitterEmail", rev.CommitterEMail);
222 xml.Element("CommitDate", rev.CommitDate);
223 xml.Element("Log", rev.Log);
224 xml.Element("Revision", rev.Rev);
225 xml.Element("PriorRevision", prior);
226 xml.EndElement();
227 ++this->PathCount[f.Status];
228 }
229