mirror of
https://github.com/Kitware/CMake.git
synced 2026-01-07 06:09:52 -06:00
cmCTestLaunchReporter: Replace cmsysProcess with cmUVProcessChain
And convert cmCTestLaunch and cmCTestBuildHandler too.
This commit is contained in:
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ######################################################################
|
// ######################################################################
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user