cmCTestLaunchReporter: Replace cmsysProcess with cmUVProcessChain

And convert cmCTestLaunch and cmCTestBuildHandler too.
This commit is contained in:
Kyle Edwards
2023-07-26 16:58:34 -04:00
parent b15ad7ebb6
commit 96b3dd329e
6 changed files with 256 additions and 226 deletions

View File

@@ -3,15 +3,17 @@
#include "cmCTestBuildHandler.h" #include "cmCTestBuildHandler.h"
#include <cstdlib> #include <cstdlib>
#include <memory>
#include <ratio> #include <ratio>
#include <set> #include <set>
#include <utility> #include <utility>
#include <cmext/algorithm> #include <cmext/algorithm>
#include <cm3p/uv.h>
#include "cmsys/Directory.hxx" #include "cmsys/Directory.hxx"
#include "cmsys/FStream.hxx" #include "cmsys/FStream.hxx"
#include "cmsys/Process.h"
#include "cmCTest.h" #include "cmCTest.h"
#include "cmCTestLaunchReporter.h" #include "cmCTestLaunchReporter.h"
@@ -24,6 +26,9 @@
#include "cmStringAlgorithms.h" #include "cmStringAlgorithms.h"
#include "cmStringReplaceHelper.h" #include "cmStringReplaceHelper.h"
#include "cmSystemTools.h" #include "cmSystemTools.h"
#include "cmUVHandlePtr.h"
#include "cmUVProcessChain.h"
#include "cmUVStream.h"
#include "cmValue.h" #include "cmValue.h"
#include "cmXMLWriter.h" #include "cmXMLWriter.h"
@@ -420,7 +425,7 @@ int cmCTestBuildHandler::ProcessHandler()
cmStringReplaceHelper colorRemover("\x1b\\[[0-9;]*m", "", nullptr); cmStringReplaceHelper colorRemover("\x1b\\[[0-9;]*m", "", nullptr);
this->ColorRemover = &colorRemover; this->ColorRemover = &colorRemover;
int retVal = 0; int retVal = 0;
int res = cmsysProcess_State_Exited; bool res = true;
if (!this->CTest->GetShowOnly()) { if (!this->CTest->GetShowOnly()) {
res = this->RunMakeCommand(makeCommand, &retVal, buildDirectory.c_str(), 0, res = this->RunMakeCommand(makeCommand, &retVal, buildDirectory.c_str(), 0,
ofs); ofs);
@@ -475,7 +480,7 @@ int cmCTestBuildHandler::ProcessHandler()
} }
this->GenerateXMLFooter(xml, elapsed_build_time); this->GenerateXMLFooter(xml, elapsed_build_time);
if (res != cmsysProcess_State_Exited || retVal || this->TotalErrors > 0) { if (!res || retVal || this->TotalErrors > 0) {
cmCTestLog(this->CTest, ERROR_MESSAGE, cmCTestLog(this->CTest, ERROR_MESSAGE,
"Error(s) when building project" << std::endl); "Error(s) when building project" << std::endl);
} }
@@ -764,10 +769,10 @@ void cmCTestBuildHandler::LaunchHelper::WriteScrapeMatchers(
} }
} }
int cmCTestBuildHandler::RunMakeCommand(const std::string& command, bool cmCTestBuildHandler::RunMakeCommand(const std::string& command,
int* retVal, const char* dir, int* retVal, const char* dir,
int timeout, std::ostream& ofs, int timeout, std::ostream& ofs,
Encoding encoding) Encoding encoding)
{ {
// First generate the command and arguments // First generate the command and arguments
std::vector<std::string> args = cmSystemTools::ParseArguments(command); std::vector<std::string> args = cmSystemTools::ParseArguments(command);
@@ -776,19 +781,9 @@ int cmCTestBuildHandler::RunMakeCommand(const std::string& command,
return false; return false;
} }
std::vector<const char*> argv;
argv.reserve(args.size() + 1);
for (std::string const& arg : args) {
argv.push_back(arg.c_str());
}
argv.push_back(nullptr);
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Run command:", this->Quiet); "Run command:", this->Quiet);
for (char const* arg : argv) { for (auto const& arg : args) {
if (!arg) {
break;
}
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
" \"" << arg << "\"", this->Quiet); " \"" << arg << "\"", this->Quiet);
} }
@@ -800,21 +795,20 @@ int cmCTestBuildHandler::RunMakeCommand(const std::string& command,
static_cast<void>(launchHelper); static_cast<void>(launchHelper);
// Now create process object // Now create process object
cmsysProcess* cp = cmsysProcess_New(); cmUVProcessChainBuilder builder;
cmsysProcess_SetCommand(cp, argv.data()); builder.AddCommand(args)
cmsysProcess_SetWorkingDirectory(cp, dir); .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
cmsysProcess_SetTimeout(cp, timeout); if (dir) {
cmsysProcess_Execute(cp); builder.SetWorkingDirectory(dir);
}
auto chain = builder.Start();
// Initialize tick's // Initialize tick's
std::string::size_type tick = 0; std::string::size_type tick = 0;
const std::string::size_type tick_len = 1024; static constexpr std::string::size_type tick_len = 1024;
char* data;
int length;
cmProcessOutput processOutput(encoding); cmProcessOutput processOutput(encoding);
std::string strdata;
cmCTestOptionalLog( cmCTestOptionalLog(
this->CTest, HANDLER_PROGRESS_OUTPUT, this->CTest, HANDLER_PROGRESS_OUTPUT,
" Each symbol represents " " Each symbol represents "
@@ -836,39 +830,65 @@ int cmCTestBuildHandler::RunMakeCommand(const std::string& command,
this->WarningQuotaReached = false; this->WarningQuotaReached = false;
this->ErrorQuotaReached = false; this->ErrorQuotaReached = false;
cm::uv_timer_ptr timer;
bool timedOut = false;
timer.init(chain.GetLoop(), &timedOut);
if (timeout > 0) {
timer.start(
[](uv_timer_t* t) {
auto* timedOutPtr = static_cast<bool*>(t->data);
*timedOutPtr = true;
},
timeout * 1000, 0);
}
// For every chunk of data // For every chunk of data
int res; cm::uv_pipe_ptr outputStream;
while ((res = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) { bool outFinished = false;
// Replace '\0' with '\n', since '\0' does not really make sense. This is cm::uv_pipe_ptr errorStream;
// for Visual Studio output bool errFinished = false;
for (int cc = 0; cc < length; ++cc) { auto startRead = [this, &chain, &processOutput, &tick,
if (data[cc] == 0) { &ofs](cm::uv_pipe_ptr& pipe, int stream,
data[cc] = '\n'; t_BuildProcessingQueueType& queue, bool& finished,
} int id) -> std::unique_ptr<cmUVStreamReadHandle> {
} pipe.init(chain.GetLoop(), 0);
uv_pipe_open(pipe, stream);
return cmUVStreamRead(
pipe,
[this, &processOutput, &queue, id, &tick, &ofs](std::vector<char> data) {
// Replace '\0' with '\n', since '\0' does not really make sense. This
// is for Visual Studio output
for (auto& c : data) {
if (c == 0) {
c = '\n';
}
}
// Process the chunk of data // Process the chunk of data
if (res == cmsysProcess_Pipe_STDERR) { std::string strdata;
processOutput.DecodeText(data, length, strdata, 1); processOutput.DecodeText(data.data(), data.size(), strdata, id);
this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs, this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len,
&this->BuildProcessingErrorQueue); ofs, &queue);
} else { },
processOutput.DecodeText(data, length, strdata, 2); [this, &processOutput, &queue, id, &tick, &ofs, &finished]() {
this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs, std::string strdata;
&this->BuildProcessingQueue); processOutput.DecodeText(std::string(), strdata, id);
} if (!strdata.empty()) {
} this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len,
processOutput.DecodeText(std::string(), strdata, 1); ofs, &queue);
if (!strdata.empty()) { }
this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs, finished = true;
&this->BuildProcessingErrorQueue); });
} };
processOutput.DecodeText(std::string(), strdata, 2); auto outputHandle = startRead(outputStream, chain.OutputStream(),
if (!strdata.empty()) { this->BuildProcessingQueue, outFinished, 1);
this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs, auto errorHandle =
&this->BuildProcessingQueue); startRead(errorStream, chain.ErrorStream(),
} this->BuildProcessingErrorQueue, errFinished, 2);
while (!timedOut && !(outFinished && errFinished && chain.Finished())) {
uv_run(&chain.GetLoop(), UV_RUN_ONCE);
}
this->ProcessBuffer(nullptr, 0, tick, tick_len, ofs, this->ProcessBuffer(nullptr, 0, tick, tick_len, ofs,
&this->BuildProcessingQueue); &this->BuildProcessingQueue);
this->ProcessBuffer(nullptr, 0, tick, tick_len, ofs, this->ProcessBuffer(nullptr, 0, tick, tick_len, ofs,
@@ -879,90 +899,93 @@ int cmCTestBuildHandler::RunMakeCommand(const std::string& command,
<< std::endl, << std::endl,
this->Quiet); this->Quiet);
// Properly handle output of the build command if (chain.Finished()) {
cmsysProcess_WaitForExit(cp, nullptr); auto const& status = chain.GetStatus(0);
int result = cmsysProcess_GetState(cp); auto exception = status.GetException();
switch (exception.first) {
if (result == cmsysProcess_State_Exited) { case cmUVProcessChain::ExceptionCode::None:
if (retVal) { if (retVal) {
*retVal = cmsysProcess_GetExitValue(cp); *retVal = static_cast<int>(status.ExitStatus);
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Command exited with the value: " << *retVal "Command exited with the value: " << *retVal
<< std::endl, << std::endl,
this->Quiet); this->Quiet);
// if a non zero return value // if a non zero return value
if (*retVal) { if (*retVal) {
// If there was an error running command, report that on the // If there was an error running command, report that on the
// dashboard. // dashboard.
if (this->UseCTestLaunch) { if (this->UseCTestLaunch) {
// For launchers, do not record this top-level error if other // For launchers, do not record this top-level error if other
// more granular build errors have already been captured. // more granular build errors have already been captured.
bool launcherXMLFound = false; bool launcherXMLFound = false;
cmsys::Directory launchDir; cmsys::Directory launchDir;
launchDir.Load(this->CTestLaunchDir); launchDir.Load(this->CTestLaunchDir);
unsigned long n = launchDir.GetNumberOfFiles(); unsigned long n = launchDir.GetNumberOfFiles();
for (unsigned long i = 0; i < n; ++i) { for (unsigned long i = 0; i < n; ++i) {
const char* fname = launchDir.GetFile(i); const char* fname = launchDir.GetFile(i);
if (cmHasLiteralSuffix(fname, ".xml")) { if (cmHasLiteralSuffix(fname, ".xml")) {
launcherXMLFound = true; launcherXMLFound = true;
break; break;
}
}
if (!launcherXMLFound) {
cmCTestLaunchReporter reporter;
reporter.RealArgs = args;
reporter.ComputeFileNames();
reporter.ExitCode = *retVal;
reporter.Status = status;
// Use temporary BuildLog file to populate this error for
// CDash.
ofs.flush();
reporter.LogOut = this->LogFileNames["Build"];
reporter.LogOut += ".tmp";
reporter.WriteXML();
}
} else {
cmCTestBuildErrorWarning errorwarning;
errorwarning.LineNumber = 0;
errorwarning.LogLine = 1;
errorwarning.Text = cmStrCat(
"*** WARNING non-zero return value in ctest from: ", args[0]);
errorwarning.PreContext.clear();
errorwarning.PostContext.clear();
errorwarning.Error = false;
this->ErrorsAndWarnings.push_back(std::move(errorwarning));
this->TotalWarnings++;
} }
} }
if (!launcherXMLFound) {
cmCTestLaunchReporter reporter;
reporter.RealArgs = args;
reporter.ComputeFileNames();
reporter.ExitCode = *retVal;
reporter.Process = cp;
// Use temporary BuildLog file to populate this error for CDash.
ofs.flush();
reporter.LogOut = this->LogFileNames["Build"];
reporter.LogOut += ".tmp";
reporter.WriteXML();
}
} else {
cmCTestBuildErrorWarning errorwarning;
errorwarning.LineNumber = 0;
errorwarning.LogLine = 1;
errorwarning.Text = cmStrCat(
"*** WARNING non-zero return value in ctest from: ", argv[0]);
errorwarning.PreContext.clear();
errorwarning.PostContext.clear();
errorwarning.Error = false;
this->ErrorsAndWarnings.push_back(std::move(errorwarning));
this->TotalWarnings++;
} }
} break;
case cmUVProcessChain::ExceptionCode::Spawn: {
// If there was an error running command, report that on the dashboard.
cmCTestBuildErrorWarning errorwarning;
errorwarning.LineNumber = 0;
errorwarning.LogLine = 1;
errorwarning.Text =
cmStrCat("*** ERROR executing: ", exception.second);
errorwarning.PreContext.clear();
errorwarning.PostContext.clear();
errorwarning.Error = true;
this->ErrorsAndWarnings.push_back(std::move(errorwarning));
this->TotalErrors++;
cmCTestLog(this->CTest, ERROR_MESSAGE,
"There was an error: " << exception.second << std::endl);
} break;
default:
if (retVal) {
*retVal = status.TermSignal;
cmCTestOptionalLog(
this->CTest, WARNING,
"There was an exception: " << *retVal << std::endl, this->Quiet);
}
break;
} }
} else if (result == cmsysProcess_State_Exception) { } else {
if (retVal) {
*retVal = cmsysProcess_GetExitException(cp);
cmCTestOptionalLog(this->CTest, WARNING,
"There was an exception: " << *retVal << std::endl,
this->Quiet);
}
} else if (result == cmsysProcess_State_Expired) {
cmCTestOptionalLog(this->CTest, WARNING, cmCTestOptionalLog(this->CTest, WARNING,
"There was a timeout" << std::endl, this->Quiet); "There was a timeout" << std::endl, this->Quiet);
} else if (result == cmsysProcess_State_Error) {
// If there was an error running command, report that on the dashboard.
cmCTestBuildErrorWarning errorwarning;
errorwarning.LineNumber = 0;
errorwarning.LogLine = 1;
errorwarning.Text =
cmStrCat("*** ERROR executing: ", cmsysProcess_GetErrorString(cp));
errorwarning.PreContext.clear();
errorwarning.PostContext.clear();
errorwarning.Error = true;
this->ErrorsAndWarnings.push_back(std::move(errorwarning));
this->TotalErrors++;
cmCTestLog(this->CTest, ERROR_MESSAGE,
"There was an error: " << cmsysProcess_GetErrorString(cp)
<< std::endl);
} }
cmsysProcess_Delete(cp); return true;
return result;
} }
// ###################################################################### // ######################################################################

View File

@@ -53,9 +53,9 @@ private:
//! Run command specialized for make and configure. Returns process status //! Run command specialized for make and configure. Returns process status
// and retVal is return value or exception. // and retVal is return value or exception.
int RunMakeCommand(const std::string& command, int* retVal, const char* dir, bool RunMakeCommand(const std::string& command, int* retVal, const char* dir,
int timeout, std::ostream& ofs, int timeout, std::ostream& ofs,
Encoding encoding = cmProcessOutput::Auto); Encoding encoding = cmProcessOutput::Auto);
enum enum
{ {

View File

@@ -2,13 +2,19 @@
file Copyright.txt or https://cmake.org/licensing for details. */ file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmCTestLaunch.h" #include "cmCTestLaunch.h"
#include <cstdio>
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include <memory>
#include <utility>
#include <cm3p/uv.h>
#include "cmsys/FStream.hxx" #include "cmsys/FStream.hxx"
#include "cmsys/Process.h"
#include "cmsys/RegularExpression.hxx" #include "cmsys/RegularExpression.hxx"
#include "cm_fileno.hxx"
#include "cmCTestLaunchReporter.h" #include "cmCTestLaunchReporter.h"
#include "cmGlobalGenerator.h" #include "cmGlobalGenerator.h"
#include "cmMakefile.h" #include "cmMakefile.h"
@@ -17,6 +23,9 @@
#include "cmStateSnapshot.h" #include "cmStateSnapshot.h"
#include "cmStringAlgorithms.h" #include "cmStringAlgorithms.h"
#include "cmSystemTools.h" #include "cmSystemTools.h"
#include "cmUVHandlePtr.h"
#include "cmUVProcessChain.h"
#include "cmUVStream.h"
#include "cmake.h" #include "cmake.h"
#ifdef _WIN32 #ifdef _WIN32
@@ -28,8 +37,6 @@
cmCTestLaunch::cmCTestLaunch(int argc, const char* const* argv) cmCTestLaunch::cmCTestLaunch(int argc, const char* const* argv)
{ {
this->Process = nullptr;
if (!this->ParseArguments(argc, argv)) { if (!this->ParseArguments(argc, argv)) {
return; return;
} }
@@ -40,13 +47,9 @@ cmCTestLaunch::cmCTestLaunch(int argc, const char* const* argv)
this->ScrapeRulesLoaded = false; this->ScrapeRulesLoaded = false;
this->HaveOut = false; this->HaveOut = false;
this->HaveErr = false; this->HaveErr = false;
this->Process = cmsysProcess_New();
} }
cmCTestLaunch::~cmCTestLaunch() cmCTestLaunch::~cmCTestLaunch() = default;
{
cmsysProcess_Delete(this->Process);
}
bool cmCTestLaunch::ParseArguments(int argc, const char* const* argv) bool cmCTestLaunch::ParseArguments(int argc, const char* const* argv)
{ {
@@ -113,15 +116,12 @@ bool cmCTestLaunch::ParseArguments(int argc, const char* const* argv)
// Extract the real command line. // Extract the real command line.
if (arg0) { if (arg0) {
this->RealArgC = argc - arg0; for (int i = 0; i < argc - arg0; ++i) {
this->RealArgV = argv + arg0; this->RealArgV.emplace_back((argv + arg0)[i]);
for (int i = 0; i < this->RealArgC; ++i) { this->HandleRealArg((argv + arg0)[i]);
this->HandleRealArg(this->RealArgV[i]);
} }
return true; return true;
} }
this->RealArgC = 0;
this->RealArgV = nullptr;
std::cerr << "No launch/command separator ('--') found!\n"; std::cerr << "No launch/command separator ('--') found!\n";
return false; return false;
} }
@@ -151,17 +151,22 @@ void cmCTestLaunch::RunChild()
} }
// Prepare to run the real command. // Prepare to run the real command.
cmsysProcess* cp = this->Process; cmUVProcessChainBuilder builder;
cmsysProcess_SetCommand(cp, this->RealArgV); builder.AddCommand(this->RealArgV);
cmsys::ofstream fout; cmsys::ofstream fout;
cmsys::ofstream ferr; cmsys::ofstream ferr;
if (this->Reporter.Passthru) { if (this->Reporter.Passthru) {
// In passthru mode we just share the output pipes. // In passthru mode we just share the output pipes.
cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1); builder
cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1); .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT,
cm_fileno(stdout))
.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
cm_fileno(stderr));
} else { } else {
// In full mode we record the child output pipes to log files. // In full mode we record the child output pipes to log files.
builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
.SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
fout.open(this->Reporter.LogOut.c_str(), std::ios::out | std::ios::binary); fout.open(this->Reporter.LogOut.c_str(), std::ios::out | std::ios::binary);
ferr.open(this->Reporter.LogErr.c_str(), std::ios::out | std::ios::binary); ferr.open(this->Reporter.LogErr.c_str(), std::ios::out | std::ios::binary);
} }
@@ -174,51 +179,65 @@ void cmCTestLaunch::RunChild()
#endif #endif
// Run the real command. // Run the real command.
cmsysProcess_Execute(cp); auto chain = builder.Start();
// Record child stdout and stderr if necessary. // Record child stdout and stderr if necessary.
cm::uv_pipe_ptr outPipe;
cm::uv_pipe_ptr errPipe;
bool outFinished = true;
bool errFinished = true;
cmProcessOutput processOutput;
std::unique_ptr<cmUVStreamReadHandle> outputHandle;
std::unique_ptr<cmUVStreamReadHandle> errorHandle;
if (!this->Reporter.Passthru) { if (!this->Reporter.Passthru) {
char* data = nullptr; auto beginRead = [&chain, &processOutput](
int length = 0; cm::uv_pipe_ptr& pipe, int stream, std::ostream& out,
cmProcessOutput processOutput; cmsys::ofstream& file, bool& haveData, bool& finished,
std::string strdata; int id) -> std::unique_ptr<cmUVStreamReadHandle> {
while (int p = cmsysProcess_WaitForData(cp, &data, &length, nullptr)) { pipe.init(chain.GetLoop(), 0);
if (p == cmsysProcess_Pipe_STDOUT) { uv_pipe_open(pipe, stream);
processOutput.DecodeText(data, length, strdata, 1); finished = false;
fout.write(strdata.c_str(), strdata.size()); return cmUVStreamRead(
std::cout.write(strdata.c_str(), strdata.size()); pipe,
this->HaveOut = true; [&processOutput, &out, &file, id, &haveData](std::vector<char> data) {
} else if (p == cmsysProcess_Pipe_STDERR) { std::string strdata;
processOutput.DecodeText(data, length, strdata, 2); processOutput.DecodeText(data.data(), data.size(), strdata, id);
ferr.write(strdata.c_str(), strdata.size()); file.write(strdata.c_str(), strdata.size());
std::cerr.write(strdata.c_str(), strdata.size()); out.write(strdata.c_str(), strdata.size());
this->HaveErr = true; haveData = true;
} },
} [&processOutput, &out, &file, &finished, id]() {
processOutput.DecodeText(std::string(), strdata, 1); std::string strdata;
if (!strdata.empty()) { processOutput.DecodeText(std::string(), strdata, id);
fout.write(strdata.c_str(), strdata.size()); if (!strdata.empty()) {
std::cout.write(strdata.c_str(), strdata.size()); file.write(strdata.c_str(), strdata.size());
} out.write(strdata.c_str(), strdata.size());
processOutput.DecodeText(std::string(), strdata, 2); }
if (!strdata.empty()) { finished = true;
ferr.write(strdata.c_str(), strdata.size()); });
std::cerr.write(strdata.c_str(), strdata.size()); };
} outputHandle = beginRead(outPipe, chain.OutputStream(), std::cout, fout,
this->HaveOut, outFinished, 1);
errorHandle = beginRead(errPipe, chain.ErrorStream(), std::cerr, ferr,
this->HaveErr, errFinished, 2);
} }
// Wait for the real command to finish. // Wait for the real command to finish.
cmsysProcess_WaitForExit(cp, nullptr); while (!(chain.Finished() && outFinished && errFinished)) {
this->Reporter.ExitCode = cmsysProcess_GetExitValue(cp); uv_run(&chain.GetLoop(), UV_RUN_ONCE);
}
this->Reporter.Status = chain.GetStatus(0);
if (this->Reporter.Status.GetException().first ==
cmUVProcessChain::ExceptionCode::Spawn) {
this->Reporter.ExitCode = 1;
} else {
this->Reporter.ExitCode =
static_cast<int>(this->Reporter.Status.ExitStatus);
}
} }
int cmCTestLaunch::Run() int cmCTestLaunch::Run()
{ {
if (!this->Process) {
std::cerr << "Could not allocate cmsysProcess instance!\n";
return -1;
}
this->RunChild(); this->RunChild();
if (this->CheckResults()) { if (this->CheckResults()) {
@@ -226,7 +245,6 @@ int cmCTestLaunch::Run()
} }
this->LoadConfig(); this->LoadConfig();
this->Reporter.Process = this->Process;
this->Reporter.WriteXML(); this->Reporter.WriteXML();
return this->Reporter.ExitCode; return this->Reporter.ExitCode;

View File

@@ -43,15 +43,12 @@ private:
bool ParseArguments(int argc, const char* const* argv); bool ParseArguments(int argc, const char* const* argv);
// The real command line appearing after launcher arguments. // The real command line appearing after launcher arguments.
int RealArgC; std::vector<std::string> RealArgV;
const char* const* RealArgV;
// The real command line after response file expansion. // The real command line after response file expansion.
std::vector<std::string> RealArgs; std::vector<std::string> RealArgs;
void HandleRealArg(const char* arg); void HandleRealArg(const char* arg);
struct cmsysProcess_s* Process;
// Whether or not any data have been written to stdout or stderr. // Whether or not any data have been written to stdout or stderr.
bool HaveOut; bool HaveOut;
bool HaveErr; bool HaveErr;

View File

@@ -2,8 +2,9 @@
file Copyright.txt or https://cmake.org/licensing for details. */ file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmCTestLaunchReporter.h" #include "cmCTestLaunchReporter.h"
#include <utility>
#include "cmsys/FStream.hxx" #include "cmsys/FStream.hxx"
#include "cmsys/Process.h"
#include "cmsys/RegularExpression.hxx" #include "cmsys/RegularExpression.hxx"
#include "cmCryptoHash.h" #include "cmCryptoHash.h"
@@ -22,6 +23,7 @@
cmCTestLaunchReporter::cmCTestLaunchReporter() cmCTestLaunchReporter::cmCTestLaunchReporter()
{ {
this->Passthru = true; this->Passthru = true;
this->Status.Finished = true;
this->ExitCode = 1; this->ExitCode = 1;
this->CWD = cmSystemTools::GetCurrentWorkingDirectory(); this->CWD = cmSystemTools::GetCurrentWorkingDirectory();
@@ -231,35 +233,23 @@ void cmCTestLaunchReporter::WriteXMLResult(cmXMLElement& e2)
// ExitCondition // ExitCondition
cmXMLElement e4(e3, "ExitCondition"); cmXMLElement e4(e3, "ExitCondition");
cmsysProcess* cp = this->Process; if (this->Status.Finished) {
switch (cmsysProcess_GetState(cp)) { auto exception = this->Status.GetException();
case cmsysProcess_State_Starting: switch (exception.first) {
e4.Content("No process has been executed"); case cmUVProcessChain::ExceptionCode::None:
break; e4.Content(this->ExitCode);
case cmsysProcess_State_Executing: break;
e4.Content("The process is still executing"); case cmUVProcessChain::ExceptionCode::Spawn:
break; e4.Content("Error administrating child process: ");
case cmsysProcess_State_Disowned: e4.Content(exception.second);
e4.Content("Disowned"); break;
break; default:
case cmsysProcess_State_Killed: e4.Content("Terminated abnormally: ");
e4.Content("Killed by parent"); e4.Content(exception.second);
break; break;
}
case cmsysProcess_State_Expired: } else {
e4.Content("Killed when timeout expired"); e4.Content("Killed when timeout expired");
break;
case cmsysProcess_State_Exited:
e4.Content(this->ExitCode);
break;
case cmsysProcess_State_Exception:
e4.Content("Terminated abnormally: ");
e4.Content(cmsysProcess_GetExceptionString(cp));
break;
case cmsysProcess_State_Error:
e4.Content("Error administrating child process: ");
e4.Content(cmsysProcess_GetErrorString(cp));
break;
} }
} }

View File

@@ -10,6 +10,8 @@
#include "cmsys/RegularExpression.hxx" #include "cmsys/RegularExpression.hxx"
#include "cmUVProcessChain.h"
class cmXMLElement; class cmXMLElement;
/** \class cmCTestLaunchReporter /** \class cmCTestLaunchReporter
@@ -48,7 +50,7 @@ public:
void ComputeFileNames(); void ComputeFileNames();
bool Passthru; bool Passthru;
struct cmsysProcess_s* Process; cmUVProcessChain::Status Status;
int ExitCode; int ExitCode;
// Temporary log files for stdout and stderr of real command. // Temporary log files for stdout and stderr of real command.