/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCTestVC.h" #include #include #include #include #include "cmsys/Process.h" #include "cmCTest.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmXMLWriter.h" cmCTestVC::cmCTestVC(cmCTest* ct, std::ostream& log) : CTest(ct) , Log(log) { this->PathCount[PathUpdated] = 0; this->PathCount[PathModified] = 0; this->PathCount[PathConflicting] = 0; this->Unknown.Date = "Unknown"; this->Unknown.Author = "Unknown"; this->Unknown.Rev = "Unknown"; } cmCTestVC::~cmCTestVC() = default; void cmCTestVC::SetCommandLineTool(std::string const& tool) { this->CommandLineTool = tool; } void cmCTestVC::SetSourceDirectory(std::string const& dir) { this->SourceDirectory = dir; } bool cmCTestVC::InitialCheckout(const std::string& command) { cmCTestLog(this->CTest, HANDLER_OUTPUT, " First perform the initial checkout: " << command << "\n"); // Make the parent directory in which to perform the checkout. std::string parent = cmSystemTools::GetFilenamePath(this->SourceDirectory); cmCTestLog(this->CTest, HANDLER_OUTPUT, " Perform checkout in directory: " << parent << "\n"); if (!cmSystemTools::MakeDirectory(parent)) { cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot create directory: " << parent << std::endl); return false; } // Construct the initial checkout command line. std::vector args = cmSystemTools::ParseArguments(command); std::vector vc_co; vc_co.reserve(args.size() + 1); for (std::string const& arg : args) { vc_co.push_back(arg.c_str()); } vc_co.push_back(nullptr); // Run the initial checkout command and log its output. this->Log << "--- Begin Initial Checkout ---\n"; OutputLogger out(this->Log, "co-out> "); OutputLogger err(this->Log, "co-err> "); bool result = this->RunChild(&vc_co[0], &out, &err, parent.c_str()); this->Log << "--- End Initial Checkout ---\n"; if (!result) { cmCTestLog(this->CTest, ERROR_MESSAGE, "Initial checkout failed!" << std::endl); } return result; } bool cmCTestVC::RunChild(char const* const* cmd, OutputParser* out, OutputParser* err, const char* workDir, Encoding encoding) { this->Log << cmCTestVC::ComputeCommandLine(cmd) << "\n"; cmsysProcess* cp = cmsysProcess_New(); cmsysProcess_SetCommand(cp, cmd); workDir = workDir ? workDir : this->SourceDirectory.c_str(); cmsysProcess_SetWorkingDirectory(cp, workDir); cmCTestVC::RunProcess(cp, out, err, encoding); int result = cmsysProcess_GetExitValue(cp); cmsysProcess_Delete(cp); return result == 0; } std::string cmCTestVC::ComputeCommandLine(char const* const* cmd) { std::ostringstream line; const char* sep = ""; for (const char* const* arg = cmd; *arg; ++arg) { line << sep << "\"" << *arg << "\""; sep = " "; } return line.str(); } bool cmCTestVC::RunUpdateCommand(char const* const* cmd, OutputParser* out, OutputParser* err, Encoding encoding) { // Report the command line. this->UpdateCommandLine = this->ComputeCommandLine(cmd); if (this->CTest->GetShowOnly()) { this->Log << this->UpdateCommandLine << "\n"; return true; } // Run the command. return this->RunChild(cmd, out, err, nullptr, encoding); } std::string cmCTestVC::GetNightlyTime() { // Get the nightly start time corresponding to the current dau. struct tm* t = this->CTest->GetNightlyTime( this->CTest->GetCTestConfiguration("NightlyStartTime"), this->CTest->GetTomorrowTag()); char current_time[1024]; sprintf(current_time, "%04d-%02d-%02d %02d:%02d:%02d", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); return std::string(current_time); } void cmCTestVC::Cleanup() { this->Log << "--- Begin Cleanup ---\n"; this->CleanupImpl(); this->Log << "--- End Cleanup ---\n"; } void cmCTestVC::CleanupImpl() { // We do no cleanup by default. } bool cmCTestVC::Update() { bool result = true; // Use the explicitly specified version. std::string versionOverride = this->CTest->GetCTestConfiguration("UpdateVersionOverride"); if (!versionOverride.empty()) { this->SetNewRevision(versionOverride); return true; } // if update version only is on then do not actually update, // just note the current version and finish if (!cmIsOn(this->CTest->GetCTestConfiguration("UpdateVersionOnly"))) { result = this->NoteOldRevision() && result; this->Log << "--- Begin Update ---\n"; result = this->UpdateImpl() && result; this->Log << "--- End Update ---\n"; } result = this->NoteNewRevision() && result; return result; } bool cmCTestVC::NoteOldRevision() { // We do nothing by default. return true; } bool cmCTestVC::NoteNewRevision() { // We do nothing by default. return true; } void cmCTestVC::SetNewRevision(std::string const& /*unused*/) { // We do nothing by default. } bool cmCTestVC::UpdateImpl() { cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "* Unknown VCS tool, not updating!" << std::endl); return true; } bool cmCTestVC::WriteXML(cmXMLWriter& xml) { this->Log << "--- Begin Revisions ---\n"; bool result = this->WriteXMLUpdates(xml); this->Log << "--- End Revisions ---\n"; return result; } bool cmCTestVC::WriteXMLUpdates(cmXMLWriter& /*unused*/) { cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "* CTest cannot extract updates for this VCS tool.\n"); return true; } void cmCTestVC::WriteXMLEntry(cmXMLWriter& xml, std::string const& path, std::string const& name, std::string const& full, File const& f) { static const char* desc[3] = { "Updated", "Modified", "Conflicting" }; Revision const& rev = f.Rev ? *f.Rev : this->Unknown; std::string prior = f.PriorRev ? f.PriorRev->Rev : std::string("Unknown"); xml.StartElement(desc[f.Status]); xml.Element("File", name); xml.Element("Directory", path); xml.Element("FullName", full); xml.Element("CheckinDate", rev.Date); xml.Element("Author", rev.Author); xml.Element("Email", rev.EMail); xml.Element("Committer", rev.Committer); xml.Element("CommitterEmail", rev.CommitterEMail); xml.Element("CommitDate", rev.CommitDate); xml.Element("Log", rev.Log); xml.Element("Revision", rev.Rev); xml.Element("PriorRevision", prior); xml.EndElement(); ++this->PathCount[f.Status]; }