mirror of
https://github.com/Kitware/CMake.git
synced 2025-12-30 18:29:37 -06:00
Merge topic 'replace-cmsysprocess-with-cmuvprocesschain'
a5f98df778Source: Fix mysterious new IWYU warnings0712e3cfeacmCTestCoverageHandler: Replace cmsysProcess with cmUVProcessChain96b3dd329ecmCTestLaunchReporter: Replace cmsysProcess with cmUVProcessChainb15ad7ebb6cmCTest: Replace cmsysProcess with cmUVProcessChain50a6e78a82cmSystemTools::RunSingleCommand(): Replace cmsysProcess with cmUVProcessChain49a37d5a97cmCTestScriptHandler: Replace cmsysProcess with cmUVProcessChainbc702aa97ecmcmd: Replace cmsysProcess with cmUVProcessChainec124582accmProcessTools::RunProcess(): Replace cmsysProcess with cmUVProcessChain ... Acked-by: Kitware Robot <kwrobot@kitware.com> Tested-by: buildbot <buildbot@kitware.com> Merge-request: !8665
This commit is contained in:
@@ -7,6 +7,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "cmsys/FStream.hxx"
|
||||
|
||||
#include "cm_sys_stat.h"
|
||||
|
||||
@@ -135,14 +135,14 @@ private:
|
||||
std::string cmCTestBZR::LoadInfo()
|
||||
{
|
||||
// Run "bzr info" to get the repository info from the work tree.
|
||||
const char* bzr = this->CommandLineTool.c_str();
|
||||
const char* bzr_info[] = { bzr, "info", nullptr };
|
||||
std::string bzr = this->CommandLineTool;
|
||||
std::vector<std::string> bzr_info = { bzr, "info" };
|
||||
InfoParser iout(this, "info-out> ");
|
||||
OutputLogger ierr(this->Log, "info-err> ");
|
||||
this->RunChild(bzr_info, &iout, &ierr);
|
||||
|
||||
// Run "bzr revno" to get the repository revision number from the work tree.
|
||||
const char* bzr_revno[] = { bzr, "revno", nullptr };
|
||||
std::vector<std::string> bzr_revno = { bzr, "revno" };
|
||||
std::string rev;
|
||||
RevnoParser rout(this, "revno-out> ", rev);
|
||||
OutputLogger rerr(this->Log, "revno-err> ");
|
||||
@@ -372,22 +372,18 @@ bool cmCTestBZR::UpdateImpl()
|
||||
// TODO: if(this->CTest->GetTestModel() == cmCTest::NIGHTLY)
|
||||
|
||||
// Use "bzr pull" to update the working tree.
|
||||
std::vector<char const*> bzr_update;
|
||||
bzr_update.push_back(this->CommandLineTool.c_str());
|
||||
std::vector<std::string> bzr_update;
|
||||
bzr_update.push_back(this->CommandLineTool);
|
||||
bzr_update.push_back("pull");
|
||||
|
||||
for (std::string const& arg : args) {
|
||||
bzr_update.push_back(arg.c_str());
|
||||
}
|
||||
cm::append(bzr_update, args);
|
||||
|
||||
bzr_update.push_back(this->URL.c_str());
|
||||
|
||||
bzr_update.push_back(nullptr);
|
||||
bzr_update.push_back(this->URL);
|
||||
|
||||
// For some reason bzr uses stderr to display the update status.
|
||||
OutputLogger out(this->Log, "pull-out> ");
|
||||
UpdateParser err(this, "pull-err> ");
|
||||
return this->RunUpdateCommand(bzr_update.data(), &out, &err);
|
||||
return this->RunUpdateCommand(bzr_update, &out, &err);
|
||||
}
|
||||
|
||||
bool cmCTestBZR::LoadRevisions()
|
||||
@@ -408,10 +404,9 @@ bool cmCTestBZR::LoadRevisions()
|
||||
}
|
||||
|
||||
// Run "bzr log" to get all global revisions of interest.
|
||||
const char* bzr = this->CommandLineTool.c_str();
|
||||
const char* bzr_log[] = {
|
||||
bzr, "log", "-v", "-r", revs.c_str(), "--xml", this->URL.c_str(), nullptr
|
||||
};
|
||||
std::string bzr = this->CommandLineTool;
|
||||
std::vector<std::string> bzr_log = { bzr, "log", "-v", "-r",
|
||||
revs, "--xml", this->URL };
|
||||
{
|
||||
LogParser out(this, "log-out> ");
|
||||
OutputLogger err(this->Log, "log-err> ");
|
||||
@@ -467,8 +462,8 @@ private:
|
||||
bool cmCTestBZR::LoadModifications()
|
||||
{
|
||||
// Run "bzr status" which reports local modifications.
|
||||
const char* bzr = this->CommandLineTool.c_str();
|
||||
const char* bzr_status[] = { bzr, "status", "-SV", nullptr };
|
||||
std::string bzr = this->CommandLineTool;
|
||||
std::vector<std::string> bzr_status = { bzr, "status", "-SV" };
|
||||
StatusParser out(this, "status-out> ");
|
||||
OutputLogger err(this->Log, "status-err> ");
|
||||
this->RunChild(bzr_status, &out, &err);
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
#include <cstring>
|
||||
#include <ratio>
|
||||
|
||||
#include "cmsys/Process.h"
|
||||
|
||||
#include "cmBuildOptions.h"
|
||||
#include "cmCTest.h"
|
||||
#include "cmCTestTestHandler.h"
|
||||
@@ -308,12 +306,11 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring)
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::vector<const char*> testCommand;
|
||||
testCommand.push_back(fullPath.c_str());
|
||||
std::vector<std::string> testCommand;
|
||||
testCommand.push_back(fullPath);
|
||||
for (std::string const& testCommandArg : this->TestCommandArgs) {
|
||||
testCommand.push_back(testCommandArg.c_str());
|
||||
testCommand.push_back(testCommandArg);
|
||||
}
|
||||
testCommand.push_back(nullptr);
|
||||
std::string outs;
|
||||
int retval = 0;
|
||||
// run the test from the this->BuildRunDir if set
|
||||
@@ -349,10 +346,10 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring)
|
||||
}
|
||||
}
|
||||
|
||||
int runTestRes = this->CTest->RunTest(testCommand, &outs, &retval, nullptr,
|
||||
remainingTime, nullptr);
|
||||
bool runTestRes = this->CTest->RunTest(testCommand, &outs, &retval, nullptr,
|
||||
remainingTime, nullptr);
|
||||
|
||||
if (runTestRes != cmsysProcess_State_Exited || retval != 0) {
|
||||
if (!runTestRes || retval != 0) {
|
||||
out << "Test command failed: " << testCommand[0] << "\n";
|
||||
retval = 1;
|
||||
}
|
||||
|
||||
@@ -3,15 +3,17 @@
|
||||
#include "cmCTestBuildHandler.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <ratio>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
|
||||
#include <cmext/algorithm>
|
||||
|
||||
#include <cm3p/uv.h>
|
||||
|
||||
#include "cmsys/Directory.hxx"
|
||||
#include "cmsys/FStream.hxx"
|
||||
#include "cmsys/Process.h"
|
||||
|
||||
#include "cmCTest.h"
|
||||
#include "cmCTestLaunchReporter.h"
|
||||
@@ -24,6 +26,9 @@
|
||||
#include "cmStringAlgorithms.h"
|
||||
#include "cmStringReplaceHelper.h"
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmUVHandlePtr.h"
|
||||
#include "cmUVProcessChain.h"
|
||||
#include "cmUVStream.h"
|
||||
#include "cmValue.h"
|
||||
#include "cmXMLWriter.h"
|
||||
|
||||
@@ -420,7 +425,7 @@ int cmCTestBuildHandler::ProcessHandler()
|
||||
cmStringReplaceHelper colorRemover("\x1b\\[[0-9;]*m", "", nullptr);
|
||||
this->ColorRemover = &colorRemover;
|
||||
int retVal = 0;
|
||||
int res = cmsysProcess_State_Exited;
|
||||
bool res = true;
|
||||
if (!this->CTest->GetShowOnly()) {
|
||||
res = this->RunMakeCommand(makeCommand, &retVal, buildDirectory.c_str(), 0,
|
||||
ofs);
|
||||
@@ -475,7 +480,7 @@ int cmCTestBuildHandler::ProcessHandler()
|
||||
}
|
||||
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,
|
||||
"Error(s) when building project" << std::endl);
|
||||
}
|
||||
@@ -764,10 +769,10 @@ void cmCTestBuildHandler::LaunchHelper::WriteScrapeMatchers(
|
||||
}
|
||||
}
|
||||
|
||||
int cmCTestBuildHandler::RunMakeCommand(const std::string& command,
|
||||
int* retVal, const char* dir,
|
||||
int timeout, std::ostream& ofs,
|
||||
Encoding encoding)
|
||||
bool cmCTestBuildHandler::RunMakeCommand(const std::string& command,
|
||||
int* retVal, const char* dir,
|
||||
int timeout, std::ostream& ofs,
|
||||
Encoding encoding)
|
||||
{
|
||||
// First generate the command and arguments
|
||||
std::vector<std::string> args = cmSystemTools::ParseArguments(command);
|
||||
@@ -776,19 +781,9 @@ int cmCTestBuildHandler::RunMakeCommand(const std::string& command,
|
||||
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,
|
||||
"Run command:", this->Quiet);
|
||||
for (char const* arg : argv) {
|
||||
if (!arg) {
|
||||
break;
|
||||
}
|
||||
for (auto const& arg : args) {
|
||||
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
|
||||
" \"" << arg << "\"", this->Quiet);
|
||||
}
|
||||
@@ -800,21 +795,20 @@ int cmCTestBuildHandler::RunMakeCommand(const std::string& command,
|
||||
static_cast<void>(launchHelper);
|
||||
|
||||
// Now create process object
|
||||
cmsysProcess* cp = cmsysProcess_New();
|
||||
cmsysProcess_SetCommand(cp, argv.data());
|
||||
cmsysProcess_SetWorkingDirectory(cp, dir);
|
||||
cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
|
||||
cmsysProcess_SetTimeout(cp, timeout);
|
||||
cmsysProcess_Execute(cp);
|
||||
cmUVProcessChainBuilder builder;
|
||||
builder.AddCommand(args)
|
||||
.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
|
||||
.SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
|
||||
if (dir) {
|
||||
builder.SetWorkingDirectory(dir);
|
||||
}
|
||||
auto chain = builder.Start();
|
||||
|
||||
// Initialize tick's
|
||||
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);
|
||||
std::string strdata;
|
||||
cmCTestOptionalLog(
|
||||
this->CTest, HANDLER_PROGRESS_OUTPUT,
|
||||
" Each symbol represents "
|
||||
@@ -836,39 +830,65 @@ int cmCTestBuildHandler::RunMakeCommand(const std::string& command,
|
||||
this->WarningQuotaReached = 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
|
||||
int res;
|
||||
while ((res = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) {
|
||||
// Replace '\0' with '\n', since '\0' does not really make sense. This is
|
||||
// for Visual Studio output
|
||||
for (int cc = 0; cc < length; ++cc) {
|
||||
if (data[cc] == 0) {
|
||||
data[cc] = '\n';
|
||||
}
|
||||
}
|
||||
cm::uv_pipe_ptr outputStream;
|
||||
bool outFinished = false;
|
||||
cm::uv_pipe_ptr errorStream;
|
||||
bool errFinished = false;
|
||||
auto startRead = [this, &chain, &processOutput, &tick,
|
||||
&ofs](cm::uv_pipe_ptr& pipe, int stream,
|
||||
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
|
||||
if (res == cmsysProcess_Pipe_STDERR) {
|
||||
processOutput.DecodeText(data, length, strdata, 1);
|
||||
this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs,
|
||||
&this->BuildProcessingErrorQueue);
|
||||
} else {
|
||||
processOutput.DecodeText(data, length, strdata, 2);
|
||||
this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs,
|
||||
&this->BuildProcessingQueue);
|
||||
}
|
||||
}
|
||||
processOutput.DecodeText(std::string(), strdata, 1);
|
||||
if (!strdata.empty()) {
|
||||
this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs,
|
||||
&this->BuildProcessingErrorQueue);
|
||||
}
|
||||
processOutput.DecodeText(std::string(), strdata, 2);
|
||||
if (!strdata.empty()) {
|
||||
this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs,
|
||||
&this->BuildProcessingQueue);
|
||||
}
|
||||
// Process the chunk of data
|
||||
std::string strdata;
|
||||
processOutput.DecodeText(data.data(), data.size(), strdata, id);
|
||||
this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len,
|
||||
ofs, &queue);
|
||||
},
|
||||
[this, &processOutput, &queue, id, &tick, &ofs, &finished]() {
|
||||
std::string strdata;
|
||||
processOutput.DecodeText(std::string(), strdata, id);
|
||||
if (!strdata.empty()) {
|
||||
this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len,
|
||||
ofs, &queue);
|
||||
}
|
||||
finished = true;
|
||||
});
|
||||
};
|
||||
auto outputHandle = startRead(outputStream, chain.OutputStream(),
|
||||
this->BuildProcessingQueue, outFinished, 1);
|
||||
auto errorHandle =
|
||||
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->BuildProcessingQueue);
|
||||
this->ProcessBuffer(nullptr, 0, tick, tick_len, ofs,
|
||||
@@ -879,90 +899,93 @@ int cmCTestBuildHandler::RunMakeCommand(const std::string& command,
|
||||
<< std::endl,
|
||||
this->Quiet);
|
||||
|
||||
// Properly handle output of the build command
|
||||
cmsysProcess_WaitForExit(cp, nullptr);
|
||||
int result = cmsysProcess_GetState(cp);
|
||||
|
||||
if (result == cmsysProcess_State_Exited) {
|
||||
if (retVal) {
|
||||
*retVal = cmsysProcess_GetExitValue(cp);
|
||||
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
|
||||
"Command exited with the value: " << *retVal
|
||||
<< std::endl,
|
||||
this->Quiet);
|
||||
// if a non zero return value
|
||||
if (*retVal) {
|
||||
// If there was an error running command, report that on the
|
||||
// dashboard.
|
||||
if (this->UseCTestLaunch) {
|
||||
// For launchers, do not record this top-level error if other
|
||||
// more granular build errors have already been captured.
|
||||
bool launcherXMLFound = false;
|
||||
cmsys::Directory launchDir;
|
||||
launchDir.Load(this->CTestLaunchDir);
|
||||
unsigned long n = launchDir.GetNumberOfFiles();
|
||||
for (unsigned long i = 0; i < n; ++i) {
|
||||
const char* fname = launchDir.GetFile(i);
|
||||
if (cmHasLiteralSuffix(fname, ".xml")) {
|
||||
launcherXMLFound = true;
|
||||
break;
|
||||
if (chain.Finished()) {
|
||||
auto const& status = chain.GetStatus(0);
|
||||
auto exception = status.GetException();
|
||||
switch (exception.first) {
|
||||
case cmUVProcessChain::ExceptionCode::None:
|
||||
if (retVal) {
|
||||
*retVal = static_cast<int>(status.ExitStatus);
|
||||
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
|
||||
"Command exited with the value: " << *retVal
|
||||
<< std::endl,
|
||||
this->Quiet);
|
||||
// if a non zero return value
|
||||
if (*retVal) {
|
||||
// If there was an error running command, report that on the
|
||||
// dashboard.
|
||||
if (this->UseCTestLaunch) {
|
||||
// For launchers, do not record this top-level error if other
|
||||
// more granular build errors have already been captured.
|
||||
bool launcherXMLFound = false;
|
||||
cmsys::Directory launchDir;
|
||||
launchDir.Load(this->CTestLaunchDir);
|
||||
unsigned long n = launchDir.GetNumberOfFiles();
|
||||
for (unsigned long i = 0; i < n; ++i) {
|
||||
const char* fname = launchDir.GetFile(i);
|
||||
if (cmHasLiteralSuffix(fname, ".xml")) {
|
||||
launcherXMLFound = true;
|
||||
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) {
|
||||
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) {
|
||||
} else {
|
||||
cmCTestOptionalLog(this->CTest, WARNING,
|
||||
"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 result;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ######################################################################
|
||||
|
||||
@@ -53,9 +53,9 @@ private:
|
||||
|
||||
//! Run command specialized for make and configure. Returns process status
|
||||
// and retVal is return value or exception.
|
||||
int RunMakeCommand(const std::string& command, int* retVal, const char* dir,
|
||||
int timeout, std::ostream& ofs,
|
||||
Encoding encoding = cmProcessOutput::Auto);
|
||||
bool RunMakeCommand(const std::string& command, int* retVal, const char* dir,
|
||||
int timeout, std::ostream& ofs,
|
||||
Encoding encoding = cmProcessOutput::Auto);
|
||||
|
||||
enum
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include <cm/string_view>
|
||||
#include <cmext/algorithm>
|
||||
|
||||
#include "cmsys/FStream.hxx"
|
||||
#include "cmsys/RegularExpression.hxx"
|
||||
@@ -89,18 +90,15 @@ bool cmCTestCVS::UpdateImpl()
|
||||
}
|
||||
|
||||
// Run "cvs update" to update the work tree.
|
||||
std::vector<char const*> cvs_update;
|
||||
cvs_update.push_back(this->CommandLineTool.c_str());
|
||||
std::vector<std::string> cvs_update;
|
||||
cvs_update.push_back(this->CommandLineTool);
|
||||
cvs_update.push_back("-z3");
|
||||
cvs_update.push_back("update");
|
||||
for (std::string const& arg : args) {
|
||||
cvs_update.push_back(arg.c_str());
|
||||
}
|
||||
cvs_update.push_back(nullptr);
|
||||
cm::append(cvs_update, args);
|
||||
|
||||
UpdateParser out(this, "up-out> ");
|
||||
UpdateParser err(this, "up-err> ");
|
||||
return this->RunUpdateCommand(cvs_update.data(), &out, &err);
|
||||
return this->RunUpdateCommand(cvs_update, &out, &err);
|
||||
}
|
||||
|
||||
class cmCTestCVS::LogParser : public cmCTestVC::LineParser
|
||||
@@ -221,10 +219,8 @@ void cmCTestCVS::LoadRevisions(std::string const& file, const char* branchFlag,
|
||||
cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush);
|
||||
|
||||
// Run "cvs log" to get revisions of this file on this branch.
|
||||
const char* cvs = this->CommandLineTool.c_str();
|
||||
const char* cvs_log[] = {
|
||||
cvs, "log", "-N", branchFlag, file.c_str(), nullptr
|
||||
};
|
||||
std::string cvs = this->CommandLineTool;
|
||||
std::vector<std::string> cvs_log = { cvs, "log", "-N", branchFlag, file };
|
||||
|
||||
LogParser out(this, "log-out> ", revisions);
|
||||
OutputLogger err(this->Log, "log-err> ");
|
||||
|
||||
@@ -45,7 +45,7 @@ int cmCTestConfigureHandler::ProcessHandler()
|
||||
auto elapsed_time_start = std::chrono::steady_clock::now();
|
||||
std::string output;
|
||||
int retVal = 0;
|
||||
int res = 0;
|
||||
bool res = false;
|
||||
if (!this->CTest->GetShowOnly()) {
|
||||
cmGeneratedFileStream os;
|
||||
if (!this->StartResultingXML(cmCTest::PartConfigure, "Configure", os)) {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <ratio>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
@@ -18,9 +19,10 @@
|
||||
|
||||
#include "cmsys/FStream.hxx"
|
||||
#include "cmsys/Glob.hxx"
|
||||
#include "cmsys/Process.h"
|
||||
#include "cmsys/RegularExpression.hxx"
|
||||
|
||||
#include "cm_fileno.hxx"
|
||||
|
||||
#include "cmCTest.h"
|
||||
#include "cmDuration.h"
|
||||
#include "cmGeneratedFileStream.h"
|
||||
@@ -33,6 +35,7 @@
|
||||
#include "cmParsePHPCoverage.h"
|
||||
#include "cmStringAlgorithms.h"
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmUVProcessChain.h"
|
||||
#include "cmWorkingDirectory.h"
|
||||
#include "cmXMLWriter.h"
|
||||
|
||||
@@ -40,85 +43,6 @@ class cmMakefile;
|
||||
|
||||
#define SAFEDIV(x, y) (((y) != 0) ? ((x) / (y)) : (0))
|
||||
|
||||
class cmCTestRunProcess
|
||||
{
|
||||
public:
|
||||
cmCTestRunProcess()
|
||||
{
|
||||
this->Process = cmsysProcess_New();
|
||||
this->PipeState = -1;
|
||||
this->TimeOut = cmDuration(-1);
|
||||
}
|
||||
~cmCTestRunProcess()
|
||||
{
|
||||
if (this->PipeState != -1 && this->PipeState != cmsysProcess_Pipe_None &&
|
||||
this->PipeState != cmsysProcess_Pipe_Timeout) {
|
||||
this->WaitForExit();
|
||||
}
|
||||
cmsysProcess_Delete(this->Process);
|
||||
}
|
||||
cmCTestRunProcess(const cmCTestRunProcess&) = delete;
|
||||
cmCTestRunProcess& operator=(const cmCTestRunProcess&) = delete;
|
||||
void SetCommand(const char* command)
|
||||
{
|
||||
this->CommandLineStrings.clear();
|
||||
this->CommandLineStrings.emplace_back(command);
|
||||
}
|
||||
void AddArgument(const char* arg)
|
||||
{
|
||||
if (arg) {
|
||||
this->CommandLineStrings.emplace_back(arg);
|
||||
}
|
||||
}
|
||||
void SetWorkingDirectory(const char* dir) { this->WorkingDirectory = dir; }
|
||||
void SetTimeout(cmDuration t) { this->TimeOut = t; }
|
||||
bool StartProcess()
|
||||
{
|
||||
std::vector<const char*> args;
|
||||
args.reserve(this->CommandLineStrings.size());
|
||||
for (std::string const& cl : this->CommandLineStrings) {
|
||||
args.push_back(cl.c_str());
|
||||
}
|
||||
args.push_back(nullptr); // null terminate
|
||||
cmsysProcess_SetCommand(this->Process, args.data());
|
||||
if (!this->WorkingDirectory.empty()) {
|
||||
cmsysProcess_SetWorkingDirectory(this->Process,
|
||||
this->WorkingDirectory.c_str());
|
||||
}
|
||||
|
||||
cmsysProcess_SetOption(this->Process, cmsysProcess_Option_HideWindow, 1);
|
||||
if (this->TimeOut >= cmDuration::zero()) {
|
||||
cmsysProcess_SetTimeout(this->Process, this->TimeOut.count());
|
||||
}
|
||||
cmsysProcess_Execute(this->Process);
|
||||
this->PipeState = cmsysProcess_GetState(this->Process);
|
||||
// if the process is running or exited return true
|
||||
return this->PipeState == cmsysProcess_State_Executing ||
|
||||
this->PipeState == cmsysProcess_State_Exited;
|
||||
}
|
||||
void SetStdoutFile(const char* fname)
|
||||
{
|
||||
cmsysProcess_SetPipeFile(this->Process, cmsysProcess_Pipe_STDOUT, fname);
|
||||
}
|
||||
void SetStderrFile(const char* fname)
|
||||
{
|
||||
cmsysProcess_SetPipeFile(this->Process, cmsysProcess_Pipe_STDERR, fname);
|
||||
}
|
||||
int WaitForExit(double* timeout = nullptr)
|
||||
{
|
||||
this->PipeState = cmsysProcess_WaitForExit(this->Process, timeout);
|
||||
return this->PipeState;
|
||||
}
|
||||
int GetProcessState() const { return this->PipeState; }
|
||||
|
||||
private:
|
||||
int PipeState;
|
||||
cmsysProcess* Process;
|
||||
std::vector<std::string> CommandLineStrings;
|
||||
std::string WorkingDirectory;
|
||||
cmDuration TimeOut;
|
||||
};
|
||||
|
||||
cmCTestCoverageHandler::cmCTestCoverageHandler() = default;
|
||||
|
||||
void cmCTestCoverageHandler::Initialize()
|
||||
@@ -1940,34 +1864,35 @@ int cmCTestCoverageHandler::RunBullseyeCommand(
|
||||
cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot find :" << cmd << "\n");
|
||||
return 0;
|
||||
}
|
||||
std::vector<std::string> args{ cmd };
|
||||
if (arg) {
|
||||
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
|
||||
"Run : " << program << " " << arg << "\n", this->Quiet);
|
||||
args.emplace_back(arg);
|
||||
} else {
|
||||
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
|
||||
"Run : " << program << "\n", this->Quiet);
|
||||
}
|
||||
// create a process object and start it
|
||||
cmCTestRunProcess runCoverageSrc;
|
||||
runCoverageSrc.SetCommand(program.c_str());
|
||||
runCoverageSrc.AddArgument(arg);
|
||||
cmUVProcessChainBuilder builder;
|
||||
std::string stdoutFile =
|
||||
cmStrCat(cont->BinaryDir, "/Testing/Temporary/",
|
||||
this->GetCTestInstance()->GetCurrentTag(), '-', cmd);
|
||||
std::string stderrFile = stdoutFile;
|
||||
stdoutFile += ".stdout";
|
||||
stderrFile += ".stderr";
|
||||
runCoverageSrc.SetStdoutFile(stdoutFile.c_str());
|
||||
runCoverageSrc.SetStderrFile(stderrFile.c_str());
|
||||
if (!runCoverageSrc.StartProcess()) {
|
||||
cmCTestLog(this->CTest, ERROR_MESSAGE,
|
||||
"Could not run : " << program << " " << arg << "\n"
|
||||
<< "kwsys process state : "
|
||||
<< runCoverageSrc.GetProcessState());
|
||||
return 0;
|
||||
}
|
||||
std::unique_ptr<FILE, int (*)(FILE*)> stdoutHandle(
|
||||
cmsys::SystemTools::Fopen(stdoutFile, "w"), fclose);
|
||||
std::unique_ptr<FILE, int (*)(FILE*)> stderrHandle(
|
||||
cmsys::SystemTools::Fopen(stderrFile, "w"), fclose);
|
||||
builder.AddCommand(args)
|
||||
.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT,
|
||||
cm_fileno(stdoutHandle.get()))
|
||||
.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
|
||||
cm_fileno(stderrHandle.get()));
|
||||
// since we set the output file names wait for it to end
|
||||
runCoverageSrc.WaitForExit();
|
||||
auto chain = builder.Start();
|
||||
chain.Wait();
|
||||
outputFile = stdoutFile;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -9,8 +9,9 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <cmext/algorithm>
|
||||
|
||||
#include "cmsys/FStream.hxx"
|
||||
#include "cmsys/Process.h"
|
||||
|
||||
#include "cmCTest.h"
|
||||
#include "cmCTestVC.h"
|
||||
@@ -18,6 +19,7 @@
|
||||
#include "cmProcessOutput.h"
|
||||
#include "cmStringAlgorithms.h"
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmUVProcessChain.h"
|
||||
#include "cmValue.h"
|
||||
|
||||
static unsigned int cmCTestGITVersion(unsigned int epic, unsigned int major,
|
||||
@@ -58,9 +60,9 @@ private:
|
||||
std::string cmCTestGIT::GetWorkingRevision()
|
||||
{
|
||||
// Run plumbing "git rev-list" to get work tree revision.
|
||||
const char* git = this->CommandLineTool.c_str();
|
||||
const char* git_rev_list[] = { git, "rev-list", "-n", "1",
|
||||
"HEAD", "--", nullptr };
|
||||
std::string git = this->CommandLineTool;
|
||||
std::vector<std::string> git_rev_list = { git, "rev-list", "-n",
|
||||
"1", "HEAD", "--" };
|
||||
std::string rev;
|
||||
OneLineParser out(this, "rl-out> ", rev);
|
||||
OutputLogger err(this->Log, "rl-err> ");
|
||||
@@ -92,13 +94,13 @@ std::string cmCTestGIT::FindGitDir()
|
||||
std::string git_dir;
|
||||
|
||||
// Run "git rev-parse --git-dir" to locate the real .git directory.
|
||||
const char* git = this->CommandLineTool.c_str();
|
||||
char const* git_rev_parse[] = { git, "rev-parse", "--git-dir", nullptr };
|
||||
std::string git = this->CommandLineTool;
|
||||
std::vector<std::string> git_rev_parse = { git, "rev-parse", "--git-dir" };
|
||||
std::string git_dir_line;
|
||||
OneLineParser rev_parse_out(this, "rev-parse-out> ", git_dir_line);
|
||||
OutputLogger rev_parse_err(this->Log, "rev-parse-err> ");
|
||||
if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err, nullptr,
|
||||
cmProcessOutput::UTF8)) {
|
||||
if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err,
|
||||
std::string{}, cmProcessOutput::UTF8)) {
|
||||
git_dir = git_dir_line;
|
||||
}
|
||||
if (git_dir.empty()) {
|
||||
@@ -117,11 +119,10 @@ std::string cmCTestGIT::FindGitDir()
|
||||
std::string cygpath_exe =
|
||||
cmStrCat(cmSystemTools::GetFilenamePath(git), "/cygpath.exe");
|
||||
if (cmSystemTools::FileExists(cygpath_exe)) {
|
||||
char const* cygpath[] = { cygpath_exe.c_str(), "-w", git_dir.c_str(),
|
||||
0 };
|
||||
std::vector<std::string> cygpath = { cygpath_exe, "-w", git_dir };
|
||||
OneLineParser cygpath_out(this, "cygpath-out> ", git_dir_line);
|
||||
OutputLogger cygpath_err(this->Log, "cygpath-err> ");
|
||||
if (this->RunChild(cygpath, &cygpath_out, &cygpath_err, nullptr,
|
||||
if (this->RunChild(cygpath, &cygpath_out, &cygpath_err, std::string{},
|
||||
cmProcessOutput::UTF8)) {
|
||||
git_dir = git_dir_line;
|
||||
}
|
||||
@@ -136,12 +137,12 @@ std::string cmCTestGIT::FindTopDir()
|
||||
std::string top_dir = this->SourceDirectory;
|
||||
|
||||
// Run "git rev-parse --show-cdup" to locate the top of the tree.
|
||||
const char* git = this->CommandLineTool.c_str();
|
||||
char const* git_rev_parse[] = { git, "rev-parse", "--show-cdup", nullptr };
|
||||
std::string git = this->CommandLineTool;
|
||||
std::vector<std::string> git_rev_parse = { git, "rev-parse", "--show-cdup" };
|
||||
std::string cdup;
|
||||
OneLineParser rev_parse_out(this, "rev-parse-out> ", cdup);
|
||||
OutputLogger rev_parse_err(this->Log, "rev-parse-err> ");
|
||||
if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err, nullptr,
|
||||
if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err, "",
|
||||
cmProcessOutput::UTF8) &&
|
||||
!cdup.empty()) {
|
||||
top_dir += "/";
|
||||
@@ -153,10 +154,10 @@ std::string cmCTestGIT::FindTopDir()
|
||||
|
||||
bool cmCTestGIT::UpdateByFetchAndReset()
|
||||
{
|
||||
const char* git = this->CommandLineTool.c_str();
|
||||
std::string git = this->CommandLineTool;
|
||||
|
||||
// Use "git fetch" to get remote commits.
|
||||
std::vector<char const*> git_fetch;
|
||||
std::vector<std::string> git_fetch;
|
||||
git_fetch.push_back(git);
|
||||
git_fetch.push_back("fetch");
|
||||
|
||||
@@ -166,17 +167,12 @@ bool cmCTestGIT::UpdateByFetchAndReset()
|
||||
opts = this->CTest->GetCTestConfiguration("GITUpdateOptions");
|
||||
}
|
||||
std::vector<std::string> args = cmSystemTools::ParseArguments(opts);
|
||||
for (std::string const& arg : args) {
|
||||
git_fetch.push_back(arg.c_str());
|
||||
}
|
||||
|
||||
// Sentinel argument.
|
||||
git_fetch.push_back(nullptr);
|
||||
cm::append(git_fetch, args);
|
||||
|
||||
// Fetch upstream refs.
|
||||
OutputLogger fetch_out(this->Log, "fetch-out> ");
|
||||
OutputLogger fetch_err(this->Log, "fetch-err> ");
|
||||
if (!this->RunUpdateCommand(git_fetch.data(), &fetch_out, &fetch_err)) {
|
||||
if (!this->RunUpdateCommand(git_fetch, &fetch_out, &fetch_err)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -207,25 +203,22 @@ bool cmCTestGIT::UpdateByFetchAndReset()
|
||||
}
|
||||
|
||||
// Reset the local branch to point at that tracked from upstream.
|
||||
char const* git_reset[] = { git, "reset", "--hard", sha1.c_str(), nullptr };
|
||||
std::vector<std::string> git_reset = { git, "reset", "--hard", sha1 };
|
||||
OutputLogger reset_out(this->Log, "reset-out> ");
|
||||
OutputLogger reset_err(this->Log, "reset-err> ");
|
||||
return this->RunChild(&git_reset[0], &reset_out, &reset_err);
|
||||
return this->RunChild(git_reset, &reset_out, &reset_err);
|
||||
}
|
||||
|
||||
bool cmCTestGIT::UpdateByCustom(std::string const& custom)
|
||||
{
|
||||
cmList git_custom_command{ custom, cmList::EmptyElements::Yes };
|
||||
std::vector<char const*> git_custom;
|
||||
git_custom.reserve(git_custom_command.size() + 1);
|
||||
for (std::string const& i : git_custom_command) {
|
||||
git_custom.push_back(i.c_str());
|
||||
}
|
||||
git_custom.push_back(nullptr);
|
||||
std::vector<std::string> git_custom;
|
||||
git_custom.reserve(git_custom_command.size());
|
||||
cm::append(git_custom, git_custom_command);
|
||||
|
||||
OutputLogger custom_out(this->Log, "custom-out> ");
|
||||
OutputLogger custom_err(this->Log, "custom-err> ");
|
||||
return this->RunUpdateCommand(git_custom.data(), &custom_out, &custom_err);
|
||||
return this->RunUpdateCommand(git_custom, &custom_out, &custom_err);
|
||||
}
|
||||
|
||||
bool cmCTestGIT::UpdateInternal()
|
||||
@@ -244,13 +237,14 @@ bool cmCTestGIT::UpdateImpl()
|
||||
}
|
||||
|
||||
std::string top_dir = this->FindTopDir();
|
||||
const char* git = this->CommandLineTool.c_str();
|
||||
const char* recursive = "--recursive";
|
||||
const char* sync_recursive = "--recursive";
|
||||
std::string git = this->CommandLineTool;
|
||||
std::string recursive = "--recursive";
|
||||
std::string sync_recursive = "--recursive";
|
||||
|
||||
// Git < 1.6.5 did not support submodule --recursive
|
||||
bool support_recursive = true;
|
||||
if (this->GetGitVersion() < cmCTestGITVersion(1, 6, 5, 0)) {
|
||||
recursive = nullptr;
|
||||
support_recursive = false;
|
||||
// No need to require >= 1.6.5 if there are no submodules.
|
||||
if (cmSystemTools::FileExists(top_dir + "/.gitmodules")) {
|
||||
this->Log << "Git < 1.6.5 cannot update submodules recursively\n";
|
||||
@@ -258,8 +252,9 @@ bool cmCTestGIT::UpdateImpl()
|
||||
}
|
||||
|
||||
// Git < 1.8.1 did not support sync --recursive
|
||||
bool support_sync_recursive = true;
|
||||
if (this->GetGitVersion() < cmCTestGITVersion(1, 8, 1, 0)) {
|
||||
sync_recursive = nullptr;
|
||||
support_sync_recursive = false;
|
||||
// No need to require >= 1.8.1 if there are no submodules.
|
||||
if (cmSystemTools::FileExists(top_dir + "/.gitmodules")) {
|
||||
this->Log << "Git < 1.8.1 cannot synchronize submodules recursively\n";
|
||||
@@ -274,35 +269,39 @@ bool cmCTestGIT::UpdateImpl()
|
||||
std::string init_submodules =
|
||||
this->CTest->GetCTestConfiguration("GITInitSubmodules");
|
||||
if (cmIsOn(init_submodules)) {
|
||||
char const* git_submodule_init[] = { git, "submodule", "init", nullptr };
|
||||
std::vector<std::string> git_submodule_init = { git, "submodule", "init" };
|
||||
ret = this->RunChild(git_submodule_init, &submodule_out, &submodule_err,
|
||||
top_dir.c_str());
|
||||
top_dir);
|
||||
|
||||
if (!ret) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
char const* git_submodule_sync[] = { git, "submodule", "sync",
|
||||
sync_recursive, nullptr };
|
||||
std::vector<std::string> git_submodule_sync = { git, "submodule", "sync" };
|
||||
if (support_sync_recursive) {
|
||||
git_submodule_sync.push_back(sync_recursive);
|
||||
}
|
||||
ret = this->RunChild(git_submodule_sync, &submodule_out, &submodule_err,
|
||||
top_dir.c_str());
|
||||
top_dir);
|
||||
|
||||
if (!ret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char const* git_submodule[] = { git, "submodule", "update", recursive,
|
||||
nullptr };
|
||||
std::vector<std::string> git_submodule = { git, "submodule", "update" };
|
||||
if (support_recursive) {
|
||||
git_submodule.push_back(recursive);
|
||||
}
|
||||
return this->RunChild(git_submodule, &submodule_out, &submodule_err,
|
||||
top_dir.c_str());
|
||||
top_dir);
|
||||
}
|
||||
|
||||
unsigned int cmCTestGIT::GetGitVersion()
|
||||
{
|
||||
if (!this->CurrentGitVersion) {
|
||||
const char* git = this->CommandLineTool.c_str();
|
||||
char const* git_version[] = { git, "--version", nullptr };
|
||||
std::string git = this->CommandLineTool;
|
||||
std::vector<std::string> git_version = { git, "--version" };
|
||||
std::string version;
|
||||
OneLineParser version_out(this, "version-out> ", version);
|
||||
OutputLogger version_err(this->Log, "version-err> ");
|
||||
@@ -605,50 +604,49 @@ bool cmCTestGIT::LoadRevisions()
|
||||
{
|
||||
// Use 'git rev-list ... | git diff-tree ...' to get revisions.
|
||||
std::string range = this->OldRevision + ".." + this->NewRevision;
|
||||
const char* git = this->CommandLineTool.c_str();
|
||||
const char* git_rev_list[] = { git, "rev-list", "--reverse",
|
||||
range.c_str(), "--", nullptr };
|
||||
const char* git_diff_tree[] = {
|
||||
git, "diff-tree", "--stdin", "--always", "-z",
|
||||
"-r", "--pretty=raw", "--encoding=utf-8", nullptr
|
||||
std::string git = this->CommandLineTool;
|
||||
std::vector<std::string> git_rev_list = { git, "rev-list", "--reverse",
|
||||
range, "--" };
|
||||
std::vector<std::string> git_diff_tree = {
|
||||
git, "diff-tree", "--stdin", "--always",
|
||||
"-z", "-r", "--pretty=raw", "--encoding=utf-8"
|
||||
};
|
||||
this->Log << cmCTestGIT::ComputeCommandLine(git_rev_list) << " | "
|
||||
<< cmCTestGIT::ComputeCommandLine(git_diff_tree) << "\n";
|
||||
|
||||
cmsysProcess* cp = cmsysProcess_New();
|
||||
cmsysProcess_AddCommand(cp, git_rev_list);
|
||||
cmsysProcess_AddCommand(cp, git_diff_tree);
|
||||
cmsysProcess_SetWorkingDirectory(cp, this->SourceDirectory.c_str());
|
||||
cmUVProcessChainBuilder builder;
|
||||
builder.AddCommand(git_rev_list)
|
||||
.AddCommand(git_diff_tree)
|
||||
.SetWorkingDirectory(this->SourceDirectory);
|
||||
|
||||
CommitParser out(this, "dt-out> ");
|
||||
OutputLogger err(this->Log, "dt-err> ");
|
||||
cmCTestGIT::RunProcess(cp, &out, &err, cmProcessOutput::UTF8);
|
||||
cmCTestGIT::RunProcess(builder, &out, &err, cmProcessOutput::UTF8);
|
||||
|
||||
// Send one extra zero-byte to terminate the last record.
|
||||
out.Process("", 1);
|
||||
|
||||
cmsysProcess_Delete(cp);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cmCTestGIT::LoadModifications()
|
||||
{
|
||||
const char* git = this->CommandLineTool.c_str();
|
||||
std::string git = this->CommandLineTool;
|
||||
|
||||
// Use 'git update-index' to refresh the index w.r.t. the work tree.
|
||||
const char* git_update_index[] = { git, "update-index", "--refresh",
|
||||
nullptr };
|
||||
std::vector<std::string> git_update_index = { git, "update-index",
|
||||
"--refresh" };
|
||||
OutputLogger ui_out(this->Log, "ui-out> ");
|
||||
OutputLogger ui_err(this->Log, "ui-err> ");
|
||||
this->RunChild(git_update_index, &ui_out, &ui_err, nullptr,
|
||||
this->RunChild(git_update_index, &ui_out, &ui_err, "",
|
||||
cmProcessOutput::UTF8);
|
||||
|
||||
// Use 'git diff-index' to get modified files.
|
||||
const char* git_diff_index[] = { git, "diff-index", "-z",
|
||||
"HEAD", "--", nullptr };
|
||||
std::vector<std::string> git_diff_index = { git, "diff-index", "-z", "HEAD",
|
||||
"--" };
|
||||
DiffParser out(this, "di-out> ");
|
||||
OutputLogger err(this->Log, "di-err> ");
|
||||
this->RunChild(git_diff_index, &out, &err, nullptr, cmProcessOutput::UTF8);
|
||||
this->RunChild(git_diff_index, &out, &err, "", cmProcessOutput::UTF8);
|
||||
|
||||
for (Change const& c : out.Changes) {
|
||||
this->DoModification(PathModified, c.Path);
|
||||
|
||||
@@ -95,8 +95,8 @@ private:
|
||||
std::string cmCTestHG::GetWorkingRevision()
|
||||
{
|
||||
// Run plumbing "hg identify" to get work tree revision.
|
||||
const char* hg = this->CommandLineTool.c_str();
|
||||
const char* hg_identify[] = { hg, "identify", "-i", nullptr };
|
||||
std::string hg = this->CommandLineTool;
|
||||
std::vector<std::string> hg_identify = { hg, "identify", "-i" };
|
||||
std::string rev;
|
||||
IdentifyParser out(this, "rev-out> ", rev);
|
||||
OutputLogger err(this->Log, "rev-err> ");
|
||||
@@ -127,16 +127,16 @@ bool cmCTestHG::UpdateImpl()
|
||||
{
|
||||
// Use "hg pull" followed by "hg update" to update the working tree.
|
||||
{
|
||||
const char* hg = this->CommandLineTool.c_str();
|
||||
const char* hg_pull[] = { hg, "pull", "-v", nullptr };
|
||||
std::string hg = this->CommandLineTool;
|
||||
std::vector<std::string> hg_pull = { hg, "pull", "-v" };
|
||||
OutputLogger out(this->Log, "pull-out> ");
|
||||
OutputLogger err(this->Log, "pull-err> ");
|
||||
this->RunChild(&hg_pull[0], &out, &err);
|
||||
this->RunChild(hg_pull, &out, &err);
|
||||
}
|
||||
|
||||
// TODO: if(this->CTest->GetTestModel() == cmCTest::NIGHTLY)
|
||||
|
||||
std::vector<char const*> hg_update;
|
||||
std::vector<std::string> hg_update;
|
||||
hg_update.push_back(this->CommandLineTool.c_str());
|
||||
hg_update.push_back("update");
|
||||
hg_update.push_back("-v");
|
||||
@@ -147,16 +147,11 @@ bool cmCTestHG::UpdateImpl()
|
||||
opts = this->CTest->GetCTestConfiguration("HGUpdateOptions");
|
||||
}
|
||||
std::vector<std::string> args = cmSystemTools::ParseArguments(opts);
|
||||
for (std::string const& arg : args) {
|
||||
hg_update.push_back(arg.c_str());
|
||||
}
|
||||
|
||||
// Sentinel argument.
|
||||
hg_update.push_back(nullptr);
|
||||
cm::append(hg_update, args);
|
||||
|
||||
OutputLogger out(this->Log, "update-out> ");
|
||||
OutputLogger err(this->Log, "update-err> ");
|
||||
return this->RunUpdateCommand(hg_update.data(), &out, &err);
|
||||
return this->RunUpdateCommand(hg_update, &out, &err);
|
||||
}
|
||||
|
||||
class cmCTestHG::LogParser
|
||||
@@ -277,8 +272,8 @@ bool cmCTestHG::LoadRevisions()
|
||||
// the project has spaces in the path. Also, they may not have
|
||||
// proper XML escapes.
|
||||
std::string range = this->OldRevision + ":" + this->NewRevision;
|
||||
const char* hg = this->CommandLineTool.c_str();
|
||||
const char* hgXMLTemplate = "<logentry\n"
|
||||
std::string hg = this->CommandLineTool;
|
||||
std::string hgXMLTemplate = "<logentry\n"
|
||||
" revision=\"{node|short}\">\n"
|
||||
" <author>{author|person}</author>\n"
|
||||
" <email>{author|email}</email>\n"
|
||||
@@ -288,10 +283,8 @@ bool cmCTestHG::LoadRevisions()
|
||||
" <file_adds>{file_adds}</file_adds>\n"
|
||||
" <file_dels>{file_dels}</file_dels>\n"
|
||||
"</logentry>\n";
|
||||
const char* hg_log[] = {
|
||||
hg, "log", "--removed", "-r", range.c_str(),
|
||||
"--template", hgXMLTemplate, nullptr
|
||||
};
|
||||
std::vector<std::string> hg_log = { hg, "log", "--removed", "-r",
|
||||
range, "--template", hgXMLTemplate };
|
||||
|
||||
LogParser out(this, "log-out> ");
|
||||
out.Process("<?xml version=\"1.0\"?>\n"
|
||||
@@ -305,8 +298,8 @@ bool cmCTestHG::LoadRevisions()
|
||||
bool cmCTestHG::LoadModifications()
|
||||
{
|
||||
// Use 'hg status' to get modified files.
|
||||
const char* hg = this->CommandLineTool.c_str();
|
||||
const char* hg_status[] = { hg, "status", nullptr };
|
||||
std::string hg = this->CommandLineTool;
|
||||
std::vector<std::string> hg_status = { hg, "status" };
|
||||
StatusParser out(this, "status-out> ");
|
||||
OutputLogger err(this->Log, "status-err> ");
|
||||
this->RunChild(hg_status, &out, &err);
|
||||
|
||||
@@ -2,13 +2,19 @@
|
||||
file Copyright.txt or https://cmake.org/licensing for details. */
|
||||
#include "cmCTestLaunch.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include <cm3p/uv.h>
|
||||
|
||||
#include "cmsys/FStream.hxx"
|
||||
#include "cmsys/Process.h"
|
||||
#include "cmsys/RegularExpression.hxx"
|
||||
|
||||
#include "cm_fileno.hxx"
|
||||
|
||||
#include "cmCTestLaunchReporter.h"
|
||||
#include "cmGlobalGenerator.h"
|
||||
#include "cmMakefile.h"
|
||||
@@ -17,6 +23,9 @@
|
||||
#include "cmStateSnapshot.h"
|
||||
#include "cmStringAlgorithms.h"
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmUVHandlePtr.h"
|
||||
#include "cmUVProcessChain.h"
|
||||
#include "cmUVStream.h"
|
||||
#include "cmake.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -28,8 +37,6 @@
|
||||
|
||||
cmCTestLaunch::cmCTestLaunch(int argc, const char* const* argv)
|
||||
{
|
||||
this->Process = nullptr;
|
||||
|
||||
if (!this->ParseArguments(argc, argv)) {
|
||||
return;
|
||||
}
|
||||
@@ -40,13 +47,9 @@ cmCTestLaunch::cmCTestLaunch(int argc, const char* const* argv)
|
||||
this->ScrapeRulesLoaded = false;
|
||||
this->HaveOut = false;
|
||||
this->HaveErr = false;
|
||||
this->Process = cmsysProcess_New();
|
||||
}
|
||||
|
||||
cmCTestLaunch::~cmCTestLaunch()
|
||||
{
|
||||
cmsysProcess_Delete(this->Process);
|
||||
}
|
||||
cmCTestLaunch::~cmCTestLaunch() = default;
|
||||
|
||||
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.
|
||||
if (arg0) {
|
||||
this->RealArgC = argc - arg0;
|
||||
this->RealArgV = argv + arg0;
|
||||
for (int i = 0; i < this->RealArgC; ++i) {
|
||||
this->HandleRealArg(this->RealArgV[i]);
|
||||
for (int i = 0; i < argc - arg0; ++i) {
|
||||
this->RealArgV.emplace_back((argv + arg0)[i]);
|
||||
this->HandleRealArg((argv + arg0)[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
this->RealArgC = 0;
|
||||
this->RealArgV = nullptr;
|
||||
std::cerr << "No launch/command separator ('--') found!\n";
|
||||
return false;
|
||||
}
|
||||
@@ -151,17 +151,22 @@ void cmCTestLaunch::RunChild()
|
||||
}
|
||||
|
||||
// Prepare to run the real command.
|
||||
cmsysProcess* cp = this->Process;
|
||||
cmsysProcess_SetCommand(cp, this->RealArgV);
|
||||
cmUVProcessChainBuilder builder;
|
||||
builder.AddCommand(this->RealArgV);
|
||||
|
||||
cmsys::ofstream fout;
|
||||
cmsys::ofstream ferr;
|
||||
if (this->Reporter.Passthru) {
|
||||
// In passthru mode we just share the output pipes.
|
||||
cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1);
|
||||
cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1);
|
||||
builder
|
||||
.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT,
|
||||
cm_fileno(stdout))
|
||||
.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
|
||||
cm_fileno(stderr));
|
||||
} else {
|
||||
// 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);
|
||||
ferr.open(this->Reporter.LogErr.c_str(), std::ios::out | std::ios::binary);
|
||||
}
|
||||
@@ -174,51 +179,65 @@ void cmCTestLaunch::RunChild()
|
||||
#endif
|
||||
|
||||
// Run the real command.
|
||||
cmsysProcess_Execute(cp);
|
||||
auto chain = builder.Start();
|
||||
|
||||
// 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) {
|
||||
char* data = nullptr;
|
||||
int length = 0;
|
||||
cmProcessOutput processOutput;
|
||||
std::string strdata;
|
||||
while (int p = cmsysProcess_WaitForData(cp, &data, &length, nullptr)) {
|
||||
if (p == cmsysProcess_Pipe_STDOUT) {
|
||||
processOutput.DecodeText(data, length, strdata, 1);
|
||||
fout.write(strdata.c_str(), strdata.size());
|
||||
std::cout.write(strdata.c_str(), strdata.size());
|
||||
this->HaveOut = true;
|
||||
} else if (p == cmsysProcess_Pipe_STDERR) {
|
||||
processOutput.DecodeText(data, length, strdata, 2);
|
||||
ferr.write(strdata.c_str(), strdata.size());
|
||||
std::cerr.write(strdata.c_str(), strdata.size());
|
||||
this->HaveErr = true;
|
||||
}
|
||||
}
|
||||
processOutput.DecodeText(std::string(), strdata, 1);
|
||||
if (!strdata.empty()) {
|
||||
fout.write(strdata.c_str(), strdata.size());
|
||||
std::cout.write(strdata.c_str(), strdata.size());
|
||||
}
|
||||
processOutput.DecodeText(std::string(), strdata, 2);
|
||||
if (!strdata.empty()) {
|
||||
ferr.write(strdata.c_str(), strdata.size());
|
||||
std::cerr.write(strdata.c_str(), strdata.size());
|
||||
}
|
||||
auto beginRead = [&chain, &processOutput](
|
||||
cm::uv_pipe_ptr& pipe, int stream, std::ostream& out,
|
||||
cmsys::ofstream& file, bool& haveData, bool& finished,
|
||||
int id) -> std::unique_ptr<cmUVStreamReadHandle> {
|
||||
pipe.init(chain.GetLoop(), 0);
|
||||
uv_pipe_open(pipe, stream);
|
||||
finished = false;
|
||||
return cmUVStreamRead(
|
||||
pipe,
|
||||
[&processOutput, &out, &file, id, &haveData](std::vector<char> data) {
|
||||
std::string strdata;
|
||||
processOutput.DecodeText(data.data(), data.size(), strdata, id);
|
||||
file.write(strdata.c_str(), strdata.size());
|
||||
out.write(strdata.c_str(), strdata.size());
|
||||
haveData = true;
|
||||
},
|
||||
[&processOutput, &out, &file, &finished, id]() {
|
||||
std::string strdata;
|
||||
processOutput.DecodeText(std::string(), strdata, id);
|
||||
if (!strdata.empty()) {
|
||||
file.write(strdata.c_str(), strdata.size());
|
||||
out.write(strdata.c_str(), strdata.size());
|
||||
}
|
||||
finished = true;
|
||||
});
|
||||
};
|
||||
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.
|
||||
cmsysProcess_WaitForExit(cp, nullptr);
|
||||
this->Reporter.ExitCode = cmsysProcess_GetExitValue(cp);
|
||||
while (!(chain.Finished() && outFinished && errFinished)) {
|
||||
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()
|
||||
{
|
||||
if (!this->Process) {
|
||||
std::cerr << "Could not allocate cmsysProcess instance!\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
this->RunChild();
|
||||
|
||||
if (this->CheckResults()) {
|
||||
@@ -226,7 +245,6 @@ int cmCTestLaunch::Run()
|
||||
}
|
||||
|
||||
this->LoadConfig();
|
||||
this->Reporter.Process = this->Process;
|
||||
this->Reporter.WriteXML();
|
||||
|
||||
return this->Reporter.ExitCode;
|
||||
|
||||
@@ -43,15 +43,12 @@ private:
|
||||
bool ParseArguments(int argc, const char* const* argv);
|
||||
|
||||
// The real command line appearing after launcher arguments.
|
||||
int RealArgC;
|
||||
const char* const* RealArgV;
|
||||
std::vector<std::string> RealArgV;
|
||||
|
||||
// The real command line after response file expansion.
|
||||
std::vector<std::string> RealArgs;
|
||||
void HandleRealArg(const char* arg);
|
||||
|
||||
struct cmsysProcess_s* Process;
|
||||
|
||||
// Whether or not any data have been written to stdout or stderr.
|
||||
bool HaveOut;
|
||||
bool HaveErr;
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
file Copyright.txt or https://cmake.org/licensing for details. */
|
||||
#include "cmCTestLaunchReporter.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "cmsys/FStream.hxx"
|
||||
#include "cmsys/Process.h"
|
||||
#include "cmsys/RegularExpression.hxx"
|
||||
|
||||
#include "cmCryptoHash.h"
|
||||
@@ -22,6 +23,7 @@
|
||||
cmCTestLaunchReporter::cmCTestLaunchReporter()
|
||||
{
|
||||
this->Passthru = true;
|
||||
this->Status.Finished = true;
|
||||
this->ExitCode = 1;
|
||||
this->CWD = cmSystemTools::GetCurrentWorkingDirectory();
|
||||
|
||||
@@ -231,35 +233,23 @@ void cmCTestLaunchReporter::WriteXMLResult(cmXMLElement& e2)
|
||||
|
||||
// ExitCondition
|
||||
cmXMLElement e4(e3, "ExitCondition");
|
||||
cmsysProcess* cp = this->Process;
|
||||
switch (cmsysProcess_GetState(cp)) {
|
||||
case cmsysProcess_State_Starting:
|
||||
e4.Content("No process has been executed");
|
||||
break;
|
||||
case cmsysProcess_State_Executing:
|
||||
e4.Content("The process is still executing");
|
||||
break;
|
||||
case cmsysProcess_State_Disowned:
|
||||
e4.Content("Disowned");
|
||||
break;
|
||||
case cmsysProcess_State_Killed:
|
||||
e4.Content("Killed by parent");
|
||||
break;
|
||||
|
||||
case cmsysProcess_State_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;
|
||||
if (this->Status.Finished) {
|
||||
auto exception = this->Status.GetException();
|
||||
switch (exception.first) {
|
||||
case cmUVProcessChain::ExceptionCode::None:
|
||||
e4.Content(this->ExitCode);
|
||||
break;
|
||||
case cmUVProcessChain::ExceptionCode::Spawn:
|
||||
e4.Content("Error administrating child process: ");
|
||||
e4.Content(exception.second);
|
||||
break;
|
||||
default:
|
||||
e4.Content("Terminated abnormally: ");
|
||||
e4.Content(exception.second);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
e4.Content("Killed when timeout expired");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
#include "cmsys/RegularExpression.hxx"
|
||||
|
||||
#include "cmUVProcessChain.h"
|
||||
|
||||
class cmXMLElement;
|
||||
|
||||
/** \class cmCTestLaunchReporter
|
||||
@@ -48,7 +50,7 @@ public:
|
||||
void ComputeFileNames();
|
||||
|
||||
bool Passthru;
|
||||
struct cmsysProcess_s* Process;
|
||||
cmUVProcessChain::Status Status;
|
||||
int ExitCode;
|
||||
|
||||
// Temporary log files for stdout and stderr of real command.
|
||||
|
||||
@@ -149,17 +149,16 @@ cmCTestP4::User cmCTestP4::GetUserData(const std::string& username)
|
||||
auto it = this->Users.find(username);
|
||||
|
||||
if (it == this->Users.end()) {
|
||||
std::vector<char const*> p4_users;
|
||||
std::vector<std::string> p4_users;
|
||||
this->SetP4Options(p4_users);
|
||||
p4_users.push_back("users");
|
||||
p4_users.push_back("-m");
|
||||
p4_users.push_back("1");
|
||||
p4_users.push_back(username.c_str());
|
||||
p4_users.push_back(nullptr);
|
||||
p4_users.push_back(username);
|
||||
|
||||
UserParser out(this, "users-out> ");
|
||||
OutputLogger err(this->Log, "users-err> ");
|
||||
this->RunChild(p4_users.data(), &out, &err);
|
||||
this->RunChild(p4_users, &out, &err);
|
||||
|
||||
// The user should now be added to the map. Search again.
|
||||
it = this->Users.find(username);
|
||||
@@ -303,10 +302,10 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
void cmCTestP4::SetP4Options(std::vector<char const*>& CommandOptions)
|
||||
void cmCTestP4::SetP4Options(std::vector<std::string>& CommandOptions)
|
||||
{
|
||||
if (this->P4Options.empty()) {
|
||||
const char* p4 = this->CommandLineTool.c_str();
|
||||
std::string p4 = this->CommandLineTool;
|
||||
this->P4Options.emplace_back(p4);
|
||||
|
||||
// The CTEST_P4_CLIENT variable sets the P4 client used when issuing
|
||||
@@ -328,15 +327,12 @@ void cmCTestP4::SetP4Options(std::vector<char const*>& CommandOptions)
|
||||
cm::append(this->P4Options, cmSystemTools::ParseArguments(opts));
|
||||
}
|
||||
|
||||
CommandOptions.clear();
|
||||
for (std::string const& o : this->P4Options) {
|
||||
CommandOptions.push_back(o.c_str());
|
||||
}
|
||||
CommandOptions = this->P4Options;
|
||||
}
|
||||
|
||||
std::string cmCTestP4::GetWorkingRevision()
|
||||
{
|
||||
std::vector<char const*> p4_identify;
|
||||
std::vector<std::string> p4_identify;
|
||||
this->SetP4Options(p4_identify);
|
||||
|
||||
p4_identify.push_back("changes");
|
||||
@@ -345,14 +341,13 @@ std::string cmCTestP4::GetWorkingRevision()
|
||||
p4_identify.push_back("-t");
|
||||
|
||||
std::string source = this->SourceDirectory + "/...#have";
|
||||
p4_identify.push_back(source.c_str());
|
||||
p4_identify.push_back(nullptr);
|
||||
p4_identify.push_back(source);
|
||||
|
||||
std::string rev;
|
||||
IdentifyParser out(this, "p4_changes-out> ", rev);
|
||||
OutputLogger err(this->Log, "p4_changes-err> ");
|
||||
|
||||
bool result = this->RunChild(p4_identify.data(), &out, &err);
|
||||
bool result = this->RunChild(p4_identify, &out, &err);
|
||||
|
||||
// If there was a problem contacting the server return "<unknown>"
|
||||
if (!result) {
|
||||
@@ -388,7 +383,7 @@ bool cmCTestP4::NoteNewRevision()
|
||||
|
||||
bool cmCTestP4::LoadRevisions()
|
||||
{
|
||||
std::vector<char const*> p4_changes;
|
||||
std::vector<std::string> p4_changes;
|
||||
this->SetP4Options(p4_changes);
|
||||
|
||||
// Use 'p4 changes ...@old,new' to get a list of changelists
|
||||
@@ -409,38 +404,36 @@ bool cmCTestP4::LoadRevisions()
|
||||
.append(this->NewRevision);
|
||||
|
||||
p4_changes.push_back("changes");
|
||||
p4_changes.push_back(range.c_str());
|
||||
p4_changes.push_back(nullptr);
|
||||
p4_changes.push_back(range);
|
||||
|
||||
ChangesParser out(this, "p4_changes-out> ");
|
||||
OutputLogger err(this->Log, "p4_changes-err> ");
|
||||
|
||||
this->ChangeLists.clear();
|
||||
this->RunChild(p4_changes.data(), &out, &err);
|
||||
this->RunChild(p4_changes, &out, &err);
|
||||
|
||||
if (this->ChangeLists.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// p4 describe -s ...@1111111,2222222
|
||||
std::vector<char const*> p4_describe;
|
||||
std::vector<std::string> p4_describe;
|
||||
for (std::string const& i : cmReverseRange(this->ChangeLists)) {
|
||||
this->SetP4Options(p4_describe);
|
||||
p4_describe.push_back("describe");
|
||||
p4_describe.push_back("-s");
|
||||
p4_describe.push_back(i.c_str());
|
||||
p4_describe.push_back(nullptr);
|
||||
p4_describe.push_back(i);
|
||||
|
||||
DescribeParser outDescribe(this, "p4_describe-out> ");
|
||||
OutputLogger errDescribe(this->Log, "p4_describe-err> ");
|
||||
this->RunChild(p4_describe.data(), &outDescribe, &errDescribe);
|
||||
this->RunChild(p4_describe, &outDescribe, &errDescribe);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cmCTestP4::LoadModifications()
|
||||
{
|
||||
std::vector<char const*> p4_diff;
|
||||
std::vector<std::string> p4_diff;
|
||||
this->SetP4Options(p4_diff);
|
||||
|
||||
p4_diff.push_back("diff");
|
||||
@@ -448,12 +441,11 @@ bool cmCTestP4::LoadModifications()
|
||||
// Ideally we would use -Od but not all clients support it
|
||||
p4_diff.push_back("-dn");
|
||||
std::string source = this->SourceDirectory + "/...";
|
||||
p4_diff.push_back(source.c_str());
|
||||
p4_diff.push_back(nullptr);
|
||||
p4_diff.push_back(source);
|
||||
|
||||
DiffParser out(this, "p4_diff-out> ");
|
||||
OutputLogger err(this->Log, "p4_diff-err> ");
|
||||
this->RunChild(p4_diff.data(), &out, &err);
|
||||
this->RunChild(p4_diff, &out, &err);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -461,17 +453,14 @@ bool cmCTestP4::UpdateCustom(const std::string& custom)
|
||||
{
|
||||
cmList p4_custom_command{ custom, cmList::EmptyElements::Yes };
|
||||
|
||||
std::vector<char const*> p4_custom;
|
||||
p4_custom.reserve(p4_custom_command.size() + 1);
|
||||
for (std::string const& i : p4_custom_command) {
|
||||
p4_custom.push_back(i.c_str());
|
||||
}
|
||||
p4_custom.push_back(nullptr);
|
||||
std::vector<std::string> p4_custom;
|
||||
p4_custom.reserve(p4_custom_command.size());
|
||||
cm::append(p4_custom, p4_custom_command);
|
||||
|
||||
OutputLogger custom_out(this->Log, "p4_customsync-out> ");
|
||||
OutputLogger custom_err(this->Log, "p4_customsync-err> ");
|
||||
|
||||
return this->RunUpdateCommand(p4_custom.data(), &custom_out, &custom_err);
|
||||
return this->RunUpdateCommand(p4_custom, &custom_out, &custom_err);
|
||||
}
|
||||
|
||||
bool cmCTestP4::UpdateImpl()
|
||||
@@ -488,7 +477,7 @@ bool cmCTestP4::UpdateImpl()
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<char const*> p4_sync;
|
||||
std::vector<std::string> p4_sync;
|
||||
this->SetP4Options(p4_sync);
|
||||
|
||||
p4_sync.push_back("sync");
|
||||
@@ -499,9 +488,7 @@ bool cmCTestP4::UpdateImpl()
|
||||
opts = this->CTest->GetCTestConfiguration("P4UpdateOptions");
|
||||
}
|
||||
std::vector<std::string> args = cmSystemTools::ParseArguments(opts);
|
||||
for (std::string const& arg : args) {
|
||||
p4_sync.push_back(arg.c_str());
|
||||
}
|
||||
cm::append(p4_sync, args);
|
||||
|
||||
std::string source = this->SourceDirectory + "/...";
|
||||
|
||||
@@ -515,11 +502,10 @@ bool cmCTestP4::UpdateImpl()
|
||||
source.append("@\"").append(date).append("\"");
|
||||
}
|
||||
|
||||
p4_sync.push_back(source.c_str());
|
||||
p4_sync.push_back(nullptr);
|
||||
p4_sync.push_back(source);
|
||||
|
||||
OutputLogger out(this->Log, "p4_sync-out> ");
|
||||
OutputLogger err(this->Log, "p4_sync-err> ");
|
||||
|
||||
return this->RunUpdateCommand(p4_sync.data(), &out, &err);
|
||||
return this->RunUpdateCommand(p4_sync, &out, &err);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ private:
|
||||
std::vector<std::string> P4Options;
|
||||
|
||||
User GetUserData(const std::string& username);
|
||||
void SetP4Options(std::vector<char const*>& options);
|
||||
void SetP4Options(std::vector<std::string>& options);
|
||||
|
||||
std::string GetWorkingRevision();
|
||||
bool NoteOldRevision() override;
|
||||
|
||||
@@ -33,7 +33,7 @@ cmCTestSVN::~cmCTestSVN() = default;
|
||||
|
||||
void cmCTestSVN::CleanupImpl()
|
||||
{
|
||||
std::vector<const char*> svn_cleanup;
|
||||
std::vector<std::string> svn_cleanup;
|
||||
svn_cleanup.push_back("cleanup");
|
||||
OutputLogger out(this->Log, "cleanup-out> ");
|
||||
OutputLogger err(this->Log, "cleanup-err> ");
|
||||
@@ -88,9 +88,9 @@ static bool cmCTestSVNPathStarts(std::string const& p1, std::string const& p2)
|
||||
std::string cmCTestSVN::LoadInfo(SVNInfo& svninfo)
|
||||
{
|
||||
// Run "svn info" to get the repository info from the work tree.
|
||||
std::vector<const char*> svn_info;
|
||||
std::vector<std::string> svn_info;
|
||||
svn_info.push_back("info");
|
||||
svn_info.push_back(svninfo.LocalPath.c_str());
|
||||
svn_info.push_back(svninfo.LocalPath);
|
||||
std::string rev;
|
||||
InfoParser out(this, "info-out> ", rev, svninfo);
|
||||
OutputLogger err(this->Log, "info-err> ");
|
||||
@@ -251,26 +251,24 @@ bool cmCTestSVN::UpdateImpl()
|
||||
args.push_back("-r{" + this->GetNightlyTime() + " +0000}");
|
||||
}
|
||||
|
||||
std::vector<char const*> svn_update;
|
||||
std::vector<std::string> svn_update;
|
||||
svn_update.push_back("update");
|
||||
for (std::string const& arg : args) {
|
||||
svn_update.push_back(arg.c_str());
|
||||
}
|
||||
cm::append(svn_update, args);
|
||||
|
||||
UpdateParser out(this, "up-out> ");
|
||||
OutputLogger err(this->Log, "up-err> ");
|
||||
return this->RunSVNCommand(svn_update, &out, &err);
|
||||
}
|
||||
|
||||
bool cmCTestSVN::RunSVNCommand(std::vector<char const*> const& parameters,
|
||||
bool cmCTestSVN::RunSVNCommand(std::vector<std::string> const& parameters,
|
||||
OutputParser* out, OutputParser* err)
|
||||
{
|
||||
if (parameters.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<char const*> args;
|
||||
args.push_back(this->CommandLineTool.c_str());
|
||||
std::vector<std::string> args;
|
||||
args.push_back(this->CommandLineTool);
|
||||
cm::append(args, parameters);
|
||||
args.push_back("--non-interactive");
|
||||
|
||||
@@ -278,16 +276,12 @@ bool cmCTestSVN::RunSVNCommand(std::vector<char const*> const& parameters,
|
||||
|
||||
std::vector<std::string> parsedUserOptions =
|
||||
cmSystemTools::ParseArguments(userOptions);
|
||||
for (std::string const& opt : parsedUserOptions) {
|
||||
args.push_back(opt.c_str());
|
||||
}
|
||||
cm::append(args, parsedUserOptions);
|
||||
|
||||
args.push_back(nullptr);
|
||||
|
||||
if (strcmp(parameters[0], "update") == 0) {
|
||||
return this->RunUpdateCommand(args.data(), out, err);
|
||||
if (parameters[0] == "update") {
|
||||
return this->RunUpdateCommand(args, out, err);
|
||||
}
|
||||
return this->RunChild(args.data(), out, err);
|
||||
return this->RunChild(args, out, err);
|
||||
}
|
||||
|
||||
class cmCTestSVN::LogParser
|
||||
@@ -393,7 +387,7 @@ bool cmCTestSVN::LoadRevisions(SVNInfo& svninfo)
|
||||
}
|
||||
|
||||
// Run "svn log" to get all global revisions of interest.
|
||||
std::vector<const char*> svn_log;
|
||||
std::vector<std::string> svn_log;
|
||||
svn_log.push_back("log");
|
||||
svn_log.push_back("--xml");
|
||||
svn_log.push_back("-v");
|
||||
@@ -472,7 +466,7 @@ private:
|
||||
bool cmCTestSVN::LoadModifications()
|
||||
{
|
||||
// Run "svn status" which reports local modifications.
|
||||
std::vector<const char*> svn_status;
|
||||
std::vector<std::string> svn_status;
|
||||
svn_status.push_back("status");
|
||||
StatusParser out(this, "status-out> ");
|
||||
OutputLogger err(this->Log, "status-err> ");
|
||||
@@ -534,7 +528,7 @@ bool cmCTestSVN::LoadRepositories()
|
||||
this->RootInfo = &(this->Repositories.back());
|
||||
|
||||
// Run "svn status" to get the list of external repositories
|
||||
std::vector<const char*> svn_status;
|
||||
std::vector<std::string> svn_status;
|
||||
svn_status.push_back("status");
|
||||
ExternalParser out(this, "external-out> ");
|
||||
OutputLogger err(this->Log, "external-err> ");
|
||||
|
||||
@@ -33,7 +33,7 @@ private:
|
||||
bool NoteNewRevision() override;
|
||||
bool UpdateImpl() override;
|
||||
|
||||
bool RunSVNCommand(std::vector<char const*> const& parameters,
|
||||
bool RunSVNCommand(std::vector<std::string> const& parameters,
|
||||
OutputParser* out, OutputParser* err);
|
||||
|
||||
// Information about an SVN repository (root repository or external)
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
|
||||
#include <cm/memory>
|
||||
|
||||
#include <cm3p/uv.h>
|
||||
|
||||
#include "cmsys/Directory.hxx"
|
||||
#include "cmsys/Process.h"
|
||||
|
||||
#include "cmCTest.h"
|
||||
#include "cmCTestBuildCommand.h"
|
||||
@@ -40,6 +41,8 @@
|
||||
#include "cmStateSnapshot.h"
|
||||
#include "cmStringAlgorithms.h"
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmUVHandlePtr.h"
|
||||
#include "cmUVProcessChain.h"
|
||||
#include "cmValue.h"
|
||||
#include "cmake.h"
|
||||
|
||||
@@ -148,66 +151,65 @@ int cmCTestScriptHandler::ExecuteScript(const std::string& total_script_arg)
|
||||
// now pass through all the other arguments
|
||||
std::vector<std::string>& initArgs =
|
||||
this->CTest->GetInitialCommandLineArguments();
|
||||
//*** need to make sure this does not have the current script ***
|
||||
for (size_t i = 1; i < initArgs.size(); ++i) {
|
||||
argv.push_back(initArgs[i].c_str());
|
||||
}
|
||||
argv.push_back(nullptr);
|
||||
|
||||
// Now create process object
|
||||
cmsysProcess* cp = cmsysProcess_New();
|
||||
cmsysProcess_SetCommand(cp, argv.data());
|
||||
// cmsysProcess_SetWorkingDirectory(cp, dir);
|
||||
cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
|
||||
// cmsysProcess_SetTimeout(cp, timeout);
|
||||
cmsysProcess_Execute(cp);
|
||||
cmUVProcessChainBuilder builder;
|
||||
builder.AddCommand(initArgs)
|
||||
.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
|
||||
.SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
|
||||
auto process = builder.Start();
|
||||
cm::uv_pipe_ptr outPipe;
|
||||
outPipe.init(process.GetLoop(), 0);
|
||||
uv_pipe_open(outPipe, process.OutputStream());
|
||||
cm::uv_pipe_ptr errPipe;
|
||||
errPipe.init(process.GetLoop(), 0);
|
||||
uv_pipe_open(errPipe, process.ErrorStream());
|
||||
|
||||
std::vector<char> out;
|
||||
std::vector<char> err;
|
||||
std::string line;
|
||||
int pipe =
|
||||
cmSystemTools::WaitForLine(cp, line, std::chrono::seconds(100), out, err);
|
||||
while (pipe != cmsysProcess_Pipe_None) {
|
||||
auto pipe =
|
||||
cmSystemTools::WaitForLine(&process.GetLoop(), outPipe, errPipe, line,
|
||||
std::chrono::seconds(100), out, err);
|
||||
while (pipe != cmSystemTools::WaitForLineResult::None) {
|
||||
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
|
||||
"Output: " << line << "\n");
|
||||
if (pipe == cmsysProcess_Pipe_STDERR) {
|
||||
if (pipe == cmSystemTools::WaitForLineResult::STDERR) {
|
||||
cmCTestLog(this->CTest, ERROR_MESSAGE, line << "\n");
|
||||
} else if (pipe == cmsysProcess_Pipe_STDOUT) {
|
||||
} else if (pipe == cmSystemTools::WaitForLineResult::STDOUT) {
|
||||
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, line << "\n");
|
||||
}
|
||||
pipe = cmSystemTools::WaitForLine(cp, line, std::chrono::seconds(100), out,
|
||||
err);
|
||||
pipe =
|
||||
cmSystemTools::WaitForLine(&process.GetLoop(), outPipe, errPipe, line,
|
||||
std::chrono::seconds(100), out, err);
|
||||
}
|
||||
|
||||
// Properly handle output of the build command
|
||||
cmsysProcess_WaitForExit(cp, nullptr);
|
||||
int result = cmsysProcess_GetState(cp);
|
||||
process.Wait();
|
||||
auto const& status = process.GetStatus(0);
|
||||
auto result = status.GetException();
|
||||
int retVal = 0;
|
||||
bool failed = false;
|
||||
if (result == cmsysProcess_State_Exited) {
|
||||
retVal = cmsysProcess_GetExitValue(cp);
|
||||
} else if (result == cmsysProcess_State_Exception) {
|
||||
retVal = cmsysProcess_GetExitException(cp);
|
||||
cmCTestLog(this->CTest, ERROR_MESSAGE,
|
||||
"\tThere was an exception: "
|
||||
<< cmsysProcess_GetExceptionString(cp) << " " << retVal
|
||||
<< std::endl);
|
||||
failed = true;
|
||||
} else if (result == cmsysProcess_State_Expired) {
|
||||
cmCTestLog(this->CTest, ERROR_MESSAGE,
|
||||
"\tThere was a timeout" << std::endl);
|
||||
failed = true;
|
||||
} else if (result == cmsysProcess_State_Error) {
|
||||
cmCTestLog(this->CTest, ERROR_MESSAGE,
|
||||
"\tError executing ctest: " << cmsysProcess_GetErrorString(cp)
|
||||
<< std::endl);
|
||||
failed = true;
|
||||
switch (result.first) {
|
||||
case cmUVProcessChain::ExceptionCode::None:
|
||||
retVal = static_cast<int>(status.ExitStatus);
|
||||
break;
|
||||
case cmUVProcessChain::ExceptionCode::Spawn:
|
||||
cmCTestLog(this->CTest, ERROR_MESSAGE,
|
||||
"\tError executing ctest: " << result.second << std::endl);
|
||||
failed = true;
|
||||
break;
|
||||
default:
|
||||
retVal = status.TermSignal;
|
||||
cmCTestLog(this->CTest, ERROR_MESSAGE,
|
||||
"\tThere was an exception: " << result.second << " " << retVal
|
||||
<< std::endl);
|
||||
failed = true;
|
||||
}
|
||||
cmsysProcess_Delete(cp);
|
||||
if (failed) {
|
||||
std::ostringstream message;
|
||||
message << "Error running command: [";
|
||||
message << result << "] ";
|
||||
message << static_cast<int>(result.first) << "] ";
|
||||
for (const char* arg : argv) {
|
||||
if (arg) {
|
||||
message << arg << " ";
|
||||
|
||||
@@ -7,10 +7,9 @@
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "cmsys/Process.h"
|
||||
|
||||
#include "cmCTest.h"
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmUVProcessChain.h"
|
||||
#include "cmValue.h"
|
||||
#include "cmXMLWriter.h"
|
||||
|
||||
@@ -55,18 +54,12 @@ bool cmCTestVC::InitialCheckout(const std::string& command)
|
||||
|
||||
// Construct the initial checkout command line.
|
||||
std::vector<std::string> args = cmSystemTools::ParseArguments(command);
|
||||
std::vector<char const*> 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.data(), &out, &err, parent.c_str());
|
||||
bool result = this->RunChild(args, &out, &err, parent);
|
||||
this->Log << "--- End Initial Checkout ---\n";
|
||||
if (!result) {
|
||||
cmCTestLog(this->CTest, ERROR_MESSAGE,
|
||||
@@ -75,35 +68,35 @@ bool cmCTestVC::InitialCheckout(const std::string& command)
|
||||
return result;
|
||||
}
|
||||
|
||||
bool cmCTestVC::RunChild(char const* const* cmd, OutputParser* out,
|
||||
OutputParser* err, const char* workDir,
|
||||
Encoding encoding)
|
||||
bool cmCTestVC::RunChild(const std::vector<std::string>& cmd,
|
||||
OutputParser* out, OutputParser* err,
|
||||
std::string 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;
|
||||
cmUVProcessChainBuilder builder;
|
||||
if (workDir.empty()) {
|
||||
workDir = this->SourceDirectory;
|
||||
}
|
||||
builder.AddCommand(cmd).SetWorkingDirectory(workDir);
|
||||
auto status = cmCTestVC::RunProcess(builder, out, err, encoding);
|
||||
return status.front().SpawnResult == 0 && status.front().ExitStatus == 0;
|
||||
}
|
||||
|
||||
std::string cmCTestVC::ComputeCommandLine(char const* const* cmd)
|
||||
std::string cmCTestVC::ComputeCommandLine(const std::vector<std::string>& cmd)
|
||||
{
|
||||
std::ostringstream line;
|
||||
const char* sep = "";
|
||||
for (const char* const* arg = cmd; *arg; ++arg) {
|
||||
line << sep << "\"" << *arg << "\"";
|
||||
for (auto const& arg : cmd) {
|
||||
line << sep << "\"" << arg << "\"";
|
||||
sep = " ";
|
||||
}
|
||||
return line.str();
|
||||
}
|
||||
|
||||
bool cmCTestVC::RunUpdateCommand(char const* const* cmd, OutputParser* out,
|
||||
OutputParser* err, Encoding encoding)
|
||||
bool cmCTestVC::RunUpdateCommand(const std::vector<std::string>& cmd,
|
||||
OutputParser* out, OutputParser* err,
|
||||
Encoding encoding)
|
||||
{
|
||||
// Report the command line.
|
||||
this->UpdateCommandLine = this->ComputeCommandLine(cmd);
|
||||
@@ -113,7 +106,7 @@ bool cmCTestVC::RunUpdateCommand(char const* const* cmd, OutputParser* out,
|
||||
}
|
||||
|
||||
// Run the command.
|
||||
return this->RunChild(cmd, out, err, nullptr, encoding);
|
||||
return this->RunChild(cmd, out, err, "", encoding);
|
||||
}
|
||||
|
||||
std::string cmCTestVC::GetNightlyTime()
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "cmProcessOutput.h"
|
||||
#include "cmProcessTools.h"
|
||||
@@ -108,15 +109,15 @@ protected:
|
||||
};
|
||||
|
||||
/** Convert a list of arguments to a human-readable command line. */
|
||||
static std::string ComputeCommandLine(char const* const* cmd);
|
||||
static std::string ComputeCommandLine(const std::vector<std::string>& cmd);
|
||||
|
||||
/** Run a command line and send output to given parsers. */
|
||||
bool RunChild(char const* const* cmd, OutputParser* out, OutputParser* err,
|
||||
const char* workDir = nullptr,
|
||||
bool RunChild(const std::vector<std::string>& cmd, OutputParser* out,
|
||||
OutputParser* err, std::string workDir = {},
|
||||
Encoding encoding = cmProcessOutput::Auto);
|
||||
|
||||
/** Run VC update command line and send output to given parsers. */
|
||||
bool RunUpdateCommand(char const* const* cmd, OutputParser* out,
|
||||
bool RunUpdateCommand(const std::vector<std::string>& cmd, OutputParser* out,
|
||||
OutputParser* err = nullptr,
|
||||
Encoding encoding = cmProcessOutput::Auto);
|
||||
|
||||
|
||||
@@ -667,6 +667,10 @@ Modify cmCTestResourceGroupsLexer.cxx:
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <termios.h>
|
||||
#endif
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
#define INITIAL 0
|
||||
|
||||
@@ -26,6 +26,10 @@ Modify cmCTestResourceGroupsLexer.cxx:
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <termios.h>
|
||||
#endif
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
%}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
@@ -24,13 +25,13 @@
|
||||
#include <cmext/string_view>
|
||||
|
||||
#include <cm3p/curl/curl.h>
|
||||
#include <cm3p/uv.h>
|
||||
#include <cm3p/zlib.h>
|
||||
|
||||
#include "cmsys/Base64.h"
|
||||
#include "cmsys/Directory.hxx"
|
||||
#include "cmsys/FStream.hxx"
|
||||
#include "cmsys/Glob.hxx"
|
||||
#include "cmsys/Process.h"
|
||||
#include "cmsys/RegularExpression.hxx"
|
||||
#include "cmsys/SystemInformation.hxx"
|
||||
#if defined(_WIN32)
|
||||
@@ -64,6 +65,9 @@
|
||||
#include "cmStateTypes.h"
|
||||
#include "cmStringAlgorithms.h"
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmUVHandlePtr.h"
|
||||
#include "cmUVProcessChain.h"
|
||||
#include "cmUVStream.h"
|
||||
#include "cmValue.h"
|
||||
#include "cmVersion.h"
|
||||
#include "cmVersionConfig.h"
|
||||
@@ -1074,9 +1078,9 @@ int cmCTest::GetTestModelFromString(const std::string& str)
|
||||
// ######################################################################
|
||||
// ######################################################################
|
||||
|
||||
int cmCTest::RunMakeCommand(const std::string& command, std::string& output,
|
||||
int* retVal, const char* dir, cmDuration timeout,
|
||||
std::ostream& ofs, Encoding encoding)
|
||||
bool cmCTest::RunMakeCommand(const std::string& command, std::string& output,
|
||||
int* retVal, const char* dir, cmDuration timeout,
|
||||
std::ostream& ofs, Encoding encoding)
|
||||
{
|
||||
// First generate the command and arguments
|
||||
std::vector<std::string> args = cmSystemTools::ParseArguments(command);
|
||||
@@ -1085,107 +1089,107 @@ int cmCTest::RunMakeCommand(const std::string& command, std::string& output,
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<const char*> argv;
|
||||
argv.reserve(args.size() + 1);
|
||||
for (std::string const& a : args) {
|
||||
argv.push_back(a.c_str());
|
||||
}
|
||||
argv.push_back(nullptr);
|
||||
|
||||
output.clear();
|
||||
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, "Run command:");
|
||||
for (char const* arg : argv) {
|
||||
if (!arg) {
|
||||
break;
|
||||
}
|
||||
for (auto const& arg : args) {
|
||||
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, " \"" << arg << "\"");
|
||||
}
|
||||
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, std::endl);
|
||||
|
||||
// Now create process object
|
||||
cmsysProcess* cp = cmsysProcess_New();
|
||||
cmsysProcess_SetCommand(cp, argv.data());
|
||||
cmsysProcess_SetWorkingDirectory(cp, dir);
|
||||
cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
|
||||
cmsysProcess_SetTimeout(cp, timeout.count());
|
||||
cmsysProcess_Execute(cp);
|
||||
cmUVProcessChainBuilder builder;
|
||||
builder.AddCommand(args).SetMergedBuiltinStreams();
|
||||
if (dir) {
|
||||
builder.SetWorkingDirectory(dir);
|
||||
}
|
||||
auto chain = builder.Start();
|
||||
cm::uv_pipe_ptr outputStream;
|
||||
outputStream.init(chain.GetLoop(), 0);
|
||||
uv_pipe_open(outputStream, chain.OutputStream());
|
||||
|
||||
// Initialize tick's
|
||||
std::string::size_type tick = 0;
|
||||
std::string::size_type tick_len = 1024;
|
||||
std::string::size_type tick_line_len = 50;
|
||||
|
||||
char* data;
|
||||
int length;
|
||||
cmProcessOutput processOutput(encoding);
|
||||
std::string strdata;
|
||||
cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
|
||||
" Each . represents " << tick_len
|
||||
<< " bytes of output\n"
|
||||
" "
|
||||
<< std::flush);
|
||||
while (cmsysProcess_WaitForData(cp, &data, &length, nullptr)) {
|
||||
processOutput.DecodeText(data, length, strdata);
|
||||
for (char& cc : strdata) {
|
||||
if (cc == 0) {
|
||||
cc = '\n';
|
||||
auto outputHandle = cmUVStreamRead(
|
||||
outputStream,
|
||||
[this, &processOutput, &output, &tick, &tick_len, &tick_line_len,
|
||||
&ofs](std::vector<char> data) {
|
||||
std::string strdata;
|
||||
processOutput.DecodeText(data.data(), data.size(), strdata);
|
||||
for (char& cc : strdata) {
|
||||
if (cc == 0) {
|
||||
cc = '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
output.append(strdata);
|
||||
while (output.size() > (tick * tick_len)) {
|
||||
tick++;
|
||||
cmCTestLog(this, HANDLER_PROGRESS_OUTPUT, "." << std::flush);
|
||||
if (tick % tick_line_len == 0 && tick > 0) {
|
||||
cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
|
||||
" Size: " << int((double(output.size()) / 1024.0) + 1)
|
||||
<< "K\n " << std::flush);
|
||||
output.append(strdata);
|
||||
while (output.size() > (tick * tick_len)) {
|
||||
tick++;
|
||||
cmCTestLog(this, HANDLER_PROGRESS_OUTPUT, "." << std::flush);
|
||||
if (tick % tick_line_len == 0 && tick > 0) {
|
||||
cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
|
||||
" Size: " << int((double(output.size()) / 1024.0) + 1)
|
||||
<< "K\n " << std::flush);
|
||||
}
|
||||
}
|
||||
}
|
||||
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
|
||||
cmCTestLogWrite(strdata.c_str(), strdata.size()));
|
||||
if (ofs) {
|
||||
ofs << cmCTestLogWrite(strdata.c_str(), strdata.size());
|
||||
}
|
||||
}
|
||||
processOutput.DecodeText(std::string(), strdata);
|
||||
if (!strdata.empty()) {
|
||||
output.append(strdata);
|
||||
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
|
||||
cmCTestLogWrite(strdata.c_str(), strdata.size()));
|
||||
if (ofs) {
|
||||
ofs << cmCTestLogWrite(strdata.c_str(), strdata.size());
|
||||
}
|
||||
}
|
||||
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
|
||||
cmCTestLogWrite(strdata.c_str(), strdata.size()));
|
||||
if (ofs) {
|
||||
ofs << cmCTestLogWrite(strdata.c_str(), strdata.size());
|
||||
}
|
||||
},
|
||||
[this, &processOutput, &output, &ofs]() {
|
||||
std::string strdata;
|
||||
processOutput.DecodeText(std::string(), strdata);
|
||||
if (!strdata.empty()) {
|
||||
output.append(strdata);
|
||||
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
|
||||
cmCTestLogWrite(strdata.c_str(), strdata.size()));
|
||||
if (ofs) {
|
||||
ofs << cmCTestLogWrite(strdata.c_str(), strdata.size());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
bool finished = chain.Wait(static_cast<uint64_t>(timeout.count() * 1000.0));
|
||||
cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
|
||||
" Size of output: " << int(double(output.size()) / 1024.0) << "K"
|
||||
<< std::endl);
|
||||
|
||||
cmsysProcess_WaitForExit(cp, nullptr);
|
||||
|
||||
int result = cmsysProcess_GetState(cp);
|
||||
|
||||
if (result == cmsysProcess_State_Exited) {
|
||||
*retVal = cmsysProcess_GetExitValue(cp);
|
||||
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
|
||||
"Command exited with the value: " << *retVal << std::endl);
|
||||
} else if (result == cmsysProcess_State_Exception) {
|
||||
*retVal = cmsysProcess_GetExitException(cp);
|
||||
cmCTestLog(this, WARNING,
|
||||
"There was an exception: " << *retVal << std::endl);
|
||||
} else if (result == cmsysProcess_State_Expired) {
|
||||
if (finished) {
|
||||
auto const& status = chain.GetStatus(0);
|
||||
auto exception = status.GetException();
|
||||
switch (exception.first) {
|
||||
case cmUVProcessChain::ExceptionCode::None:
|
||||
*retVal = static_cast<int>(status.ExitStatus);
|
||||
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
|
||||
"Command exited with the value: " << *retVal << std::endl);
|
||||
break;
|
||||
case cmUVProcessChain::ExceptionCode::Spawn:
|
||||
output += "\n*** ERROR executing: ";
|
||||
output += exception.second;
|
||||
output += "\n***The build process failed.";
|
||||
cmCTestLog(this, ERROR_MESSAGE,
|
||||
"There was an error: " << exception.second << std::endl);
|
||||
break;
|
||||
default:
|
||||
*retVal = static_cast<int>(exception.first);
|
||||
cmCTestLog(this, WARNING,
|
||||
"There was an exception: " << *retVal << std::endl);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
cmCTestLog(this, WARNING, "There was a timeout" << std::endl);
|
||||
} else if (result == cmsysProcess_State_Error) {
|
||||
output += "\n*** ERROR executing: ";
|
||||
output += cmsysProcess_GetErrorString(cp);
|
||||
output += "\n***The build process failed.";
|
||||
cmCTestLog(this, ERROR_MESSAGE,
|
||||
"There was an error: " << cmsysProcess_GetErrorString(cp)
|
||||
<< std::endl);
|
||||
}
|
||||
|
||||
cmsysProcess_Delete(cp);
|
||||
|
||||
return result;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ######################################################################
|
||||
@@ -1193,9 +1197,10 @@ int cmCTest::RunMakeCommand(const std::string& command, std::string& output,
|
||||
// ######################################################################
|
||||
// ######################################################################
|
||||
|
||||
int cmCTest::RunTest(std::vector<const char*> argv, std::string* output,
|
||||
int* retVal, std::ostream* log, cmDuration testTimeOut,
|
||||
std::vector<std::string>* environment, Encoding encoding)
|
||||
bool cmCTest::RunTest(const std::vector<std::string>& argv,
|
||||
std::string* output, int* retVal, std::ostream* log,
|
||||
cmDuration testTimeOut,
|
||||
std::vector<std::string>* environment, Encoding encoding)
|
||||
{
|
||||
bool modifyEnv = (environment && !environment->empty());
|
||||
|
||||
@@ -1234,19 +1239,16 @@ int cmCTest::RunTest(std::vector<const char*> argv, std::string* output,
|
||||
inst.SetStreams(&oss, &oss);
|
||||
|
||||
std::vector<std::string> args;
|
||||
for (char const* i : argv) {
|
||||
if (i) {
|
||||
// make sure we pass the timeout in for any build and test
|
||||
// invocations. Since --build-generator is required this is a
|
||||
// good place to check for it, and to add the arguments in
|
||||
if (strcmp(i, "--build-generator") == 0 &&
|
||||
timeout != cmCTest::MaxDuration() &&
|
||||
timeout > cmDuration::zero()) {
|
||||
args.emplace_back("--test-timeout");
|
||||
args.push_back(std::to_string(cmDurationTo<unsigned int>(timeout)));
|
||||
}
|
||||
args.emplace_back(i);
|
||||
for (auto const& i : argv) {
|
||||
// make sure we pass the timeout in for any build and test
|
||||
// invocations. Since --build-generator is required this is a
|
||||
// good place to check for it, and to add the arguments in
|
||||
if (i == "--build-generator" && timeout != cmCTest::MaxDuration() &&
|
||||
timeout > cmDuration::zero()) {
|
||||
args.emplace_back("--test-timeout");
|
||||
args.push_back(std::to_string(cmDurationTo<unsigned int>(timeout)));
|
||||
}
|
||||
args.emplace_back(i);
|
||||
}
|
||||
if (log) {
|
||||
*log << "* Run internal CTest" << std::endl;
|
||||
@@ -1272,7 +1274,7 @@ int cmCTest::RunTest(std::vector<const char*> argv, std::string* output,
|
||||
<< std::endl);
|
||||
}
|
||||
|
||||
return cmsysProcess_State_Exited;
|
||||
return true;
|
||||
}
|
||||
std::vector<char> tempOutput;
|
||||
if (output) {
|
||||
@@ -1285,41 +1287,43 @@ int cmCTest::RunTest(std::vector<const char*> argv, std::string* output,
|
||||
cmSystemTools::AppendEnv(*environment);
|
||||
}
|
||||
|
||||
cmsysProcess* cp = cmsysProcess_New();
|
||||
cmsysProcess_SetCommand(cp, argv.data());
|
||||
cmUVProcessChainBuilder builder;
|
||||
builder.AddCommand(argv).SetMergedBuiltinStreams();
|
||||
cmCTestLog(this, DEBUG, "Command is: " << argv[0] << std::endl);
|
||||
if (cmSystemTools::GetRunCommandHideConsole()) {
|
||||
cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
|
||||
}
|
||||
auto chain = builder.Start();
|
||||
|
||||
cmsysProcess_SetTimeout(cp, timeout.count());
|
||||
cmsysProcess_Execute(cp);
|
||||
|
||||
char* data;
|
||||
int length;
|
||||
cmProcessOutput processOutput(encoding);
|
||||
std::string strdata;
|
||||
while (cmsysProcess_WaitForData(cp, &data, &length, nullptr)) {
|
||||
processOutput.DecodeText(data, length, strdata);
|
||||
if (output) {
|
||||
cm::append(tempOutput, data, data + length);
|
||||
}
|
||||
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
|
||||
cmCTestLogWrite(strdata.c_str(), strdata.size()));
|
||||
if (log) {
|
||||
log->write(strdata.c_str(), strdata.size());
|
||||
}
|
||||
}
|
||||
processOutput.DecodeText(std::string(), strdata);
|
||||
if (!strdata.empty()) {
|
||||
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
|
||||
cmCTestLogWrite(strdata.c_str(), strdata.size()));
|
||||
if (log) {
|
||||
log->write(strdata.c_str(), strdata.size());
|
||||
}
|
||||
}
|
||||
cm::uv_pipe_ptr outputStream;
|
||||
outputStream.init(chain.GetLoop(), 0);
|
||||
uv_pipe_open(outputStream, chain.OutputStream());
|
||||
auto outputHandle = cmUVStreamRead(
|
||||
outputStream,
|
||||
[this, &processOutput, &output, &tempOutput,
|
||||
&log](std::vector<char> data) {
|
||||
std::string strdata;
|
||||
processOutput.DecodeText(data.data(), data.size(), strdata);
|
||||
if (output) {
|
||||
cm::append(tempOutput, data.data(), data.data() + data.size());
|
||||
}
|
||||
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
|
||||
cmCTestLogWrite(strdata.c_str(), strdata.size()));
|
||||
if (log) {
|
||||
log->write(strdata.c_str(), strdata.size());
|
||||
}
|
||||
},
|
||||
[this, &processOutput, &log]() {
|
||||
std::string strdata;
|
||||
processOutput.DecodeText(std::string(), strdata);
|
||||
if (!strdata.empty()) {
|
||||
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
|
||||
cmCTestLogWrite(strdata.c_str(), strdata.size()));
|
||||
if (log) {
|
||||
log->write(strdata.c_str(), strdata.size());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cmsysProcess_WaitForExit(cp, nullptr);
|
||||
bool complete = chain.Wait(static_cast<uint64_t>(timeout.count() * 1000.0));
|
||||
processOutput.DecodeText(tempOutput, tempOutput);
|
||||
if (output && tempOutput.begin() != tempOutput.end()) {
|
||||
output->append(tempOutput.data(), tempOutput.size());
|
||||
@@ -1327,33 +1331,41 @@ int cmCTest::RunTest(std::vector<const char*> argv, std::string* output,
|
||||
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
|
||||
"-- Process completed" << std::endl);
|
||||
|
||||
int result = cmsysProcess_GetState(cp);
|
||||
bool result = false;
|
||||
|
||||
if (result == cmsysProcess_State_Exited) {
|
||||
*retVal = cmsysProcess_GetExitValue(cp);
|
||||
if (*retVal != 0 && this->Impl->OutputTestOutputOnTestFailure) {
|
||||
this->OutputTestErrors(tempOutput);
|
||||
if (complete) {
|
||||
auto const& status = chain.GetStatus(0);
|
||||
auto exception = status.GetException();
|
||||
switch (exception.first) {
|
||||
case cmUVProcessChain::ExceptionCode::None:
|
||||
*retVal = static_cast<int>(status.ExitStatus);
|
||||
if (*retVal != 0 && this->Impl->OutputTestOutputOnTestFailure) {
|
||||
this->OutputTestErrors(tempOutput);
|
||||
}
|
||||
result = true;
|
||||
break;
|
||||
case cmUVProcessChain::ExceptionCode::Spawn: {
|
||||
std::string outerr =
|
||||
cmStrCat("\n*** ERROR executing: ", exception.second);
|
||||
if (output) {
|
||||
*output += outerr;
|
||||
}
|
||||
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl);
|
||||
} break;
|
||||
default: {
|
||||
if (this->Impl->OutputTestOutputOnTestFailure) {
|
||||
this->OutputTestErrors(tempOutput);
|
||||
}
|
||||
*retVal = status.TermSignal;
|
||||
std::string outerr =
|
||||
cmStrCat("\n*** Exception executing: ", exception.second);
|
||||
if (output) {
|
||||
*output += outerr;
|
||||
}
|
||||
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl);
|
||||
} break;
|
||||
}
|
||||
} else if (result == cmsysProcess_State_Exception) {
|
||||
if (this->Impl->OutputTestOutputOnTestFailure) {
|
||||
this->OutputTestErrors(tempOutput);
|
||||
}
|
||||
*retVal = cmsysProcess_GetExitException(cp);
|
||||
std::string outerr = cmStrCat("\n*** Exception executing: ",
|
||||
cmsysProcess_GetExceptionString(cp));
|
||||
if (output) {
|
||||
*output += outerr;
|
||||
}
|
||||
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl);
|
||||
} else if (result == cmsysProcess_State_Error) {
|
||||
std::string outerr =
|
||||
cmStrCat("\n*** ERROR executing: ", cmsysProcess_GetErrorString(cp));
|
||||
if (output) {
|
||||
*output += outerr;
|
||||
}
|
||||
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl);
|
||||
}
|
||||
cmsysProcess_Delete(cp);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -3471,49 +3483,70 @@ bool cmCTest::RunCommand(std::vector<std::string> const& args,
|
||||
stdOut->clear();
|
||||
stdErr->clear();
|
||||
|
||||
cmsysProcess* cp = cmsysProcess_New();
|
||||
cmsysProcess_SetCommand(cp, argv.data());
|
||||
cmsysProcess_SetWorkingDirectory(cp, dir);
|
||||
if (cmSystemTools::GetRunCommandHideConsole()) {
|
||||
cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
|
||||
cmUVProcessChainBuilder builder;
|
||||
builder.AddCommand(args)
|
||||
.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
|
||||
.SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
|
||||
if (dir) {
|
||||
builder.SetWorkingDirectory(dir);
|
||||
}
|
||||
auto chain = builder.Start();
|
||||
|
||||
cm::uv_timer_ptr timer;
|
||||
bool timedOut = false;
|
||||
if (timeout.count()) {
|
||||
timer.init(chain.GetLoop(), &timedOut);
|
||||
timer.start(
|
||||
[](uv_timer_t* t) {
|
||||
auto* timedOutPtr = static_cast<bool*>(t->data);
|
||||
*timedOutPtr = true;
|
||||
},
|
||||
static_cast<uint64_t>(timeout.count() * 1000.0), 0);
|
||||
}
|
||||
cmsysProcess_SetTimeout(cp, timeout.count());
|
||||
cmsysProcess_Execute(cp);
|
||||
|
||||
std::vector<char> tempOutput;
|
||||
bool outFinished = false;
|
||||
cm::uv_pipe_ptr outStream;
|
||||
std::vector<char> tempError;
|
||||
char* data;
|
||||
int length;
|
||||
bool errFinished = false;
|
||||
cm::uv_pipe_ptr errStream;
|
||||
cmProcessOutput processOutput(encoding);
|
||||
std::string strdata;
|
||||
int res;
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
res = cmsysProcess_WaitForData(cp, &data, &length, nullptr);
|
||||
switch (res) {
|
||||
case cmsysProcess_Pipe_STDOUT:
|
||||
cm::append(tempOutput, data, data + length);
|
||||
break;
|
||||
case cmsysProcess_Pipe_STDERR:
|
||||
cm::append(tempError, data, data + length);
|
||||
break;
|
||||
default:
|
||||
done = true;
|
||||
}
|
||||
if ((res == cmsysProcess_Pipe_STDOUT || res == cmsysProcess_Pipe_STDERR) &&
|
||||
this->Impl->ExtraVerbose) {
|
||||
processOutput.DecodeText(data, length, strdata);
|
||||
cmSystemTools::Stdout(strdata);
|
||||
}
|
||||
auto startRead = [this, &chain, &processOutput](
|
||||
cm::uv_pipe_ptr& pipe, int stream,
|
||||
std::vector<char>& temp,
|
||||
bool& finished) -> std::unique_ptr<cmUVStreamReadHandle> {
|
||||
pipe.init(chain.GetLoop(), 0);
|
||||
uv_pipe_open(pipe, stream);
|
||||
return cmUVStreamRead(
|
||||
pipe,
|
||||
[this, &temp, &processOutput](std::vector<char> data) {
|
||||
cm::append(temp, data);
|
||||
if (this->Impl->ExtraVerbose) {
|
||||
std::string strdata;
|
||||
processOutput.DecodeText(data.data(), data.size(), strdata);
|
||||
cmSystemTools::Stdout(strdata);
|
||||
}
|
||||
},
|
||||
[&finished]() { finished = true; });
|
||||
};
|
||||
auto outputHandle =
|
||||
startRead(outStream, chain.OutputStream(), tempOutput, outFinished);
|
||||
auto errorHandle =
|
||||
startRead(errStream, chain.ErrorStream(), tempError, errFinished);
|
||||
while (!timedOut && !(outFinished && errFinished)) {
|
||||
uv_run(&chain.GetLoop(), UV_RUN_ONCE);
|
||||
}
|
||||
if (this->Impl->ExtraVerbose) {
|
||||
std::string strdata;
|
||||
processOutput.DecodeText(std::string(), strdata);
|
||||
if (!strdata.empty()) {
|
||||
cmSystemTools::Stdout(strdata);
|
||||
}
|
||||
}
|
||||
|
||||
cmsysProcess_WaitForExit(cp, nullptr);
|
||||
while (!timedOut && !chain.Finished()) {
|
||||
uv_run(&chain.GetLoop(), UV_RUN_ONCE);
|
||||
}
|
||||
if (!tempOutput.empty()) {
|
||||
processOutput.DecodeText(tempOutput, tempOutput);
|
||||
stdOut->append(tempOutput.data(), tempOutput.size());
|
||||
@@ -3524,32 +3557,32 @@ bool cmCTest::RunCommand(std::vector<std::string> const& args,
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exited) {
|
||||
if (retVal) {
|
||||
*retVal = cmsysProcess_GetExitValue(cp);
|
||||
} else {
|
||||
if (cmsysProcess_GetExitValue(cp) != 0) {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
} else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exception) {
|
||||
const char* exception_str = cmsysProcess_GetExceptionString(cp);
|
||||
cmCTestLog(this, ERROR_MESSAGE, exception_str << std::endl);
|
||||
stdErr->append(exception_str, strlen(exception_str));
|
||||
result = false;
|
||||
} else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Error) {
|
||||
const char* error_str = cmsysProcess_GetErrorString(cp);
|
||||
cmCTestLog(this, ERROR_MESSAGE, error_str << std::endl);
|
||||
stdErr->append(error_str, strlen(error_str));
|
||||
result = false;
|
||||
} else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Expired) {
|
||||
if (timedOut) {
|
||||
const char* error_str = "Process terminated due to timeout\n";
|
||||
cmCTestLog(this, ERROR_MESSAGE, error_str << std::endl);
|
||||
stdErr->append(error_str, strlen(error_str));
|
||||
result = false;
|
||||
} else {
|
||||
auto const& status = chain.GetStatus(0);
|
||||
auto exception = status.GetException();
|
||||
switch (exception.first) {
|
||||
case cmUVProcessChain::ExceptionCode::None:
|
||||
if (retVal) {
|
||||
*retVal = static_cast<int>(status.ExitStatus);
|
||||
} else {
|
||||
if (status.ExitStatus != 0) {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
cmCTestLog(this, ERROR_MESSAGE, exception.second << std::endl);
|
||||
stdErr->append(exception.second);
|
||||
result = false;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
cmsysProcess_Delete(cp);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -254,10 +254,10 @@ public:
|
||||
* Run command specialized for make and configure. Returns process status
|
||||
* and retVal is return value or exception.
|
||||
*/
|
||||
int RunMakeCommand(const std::string& command, std::string& output,
|
||||
int* retVal, const char* dir, cmDuration timeout,
|
||||
std::ostream& ofs,
|
||||
Encoding encoding = cmProcessOutput::Auto);
|
||||
bool RunMakeCommand(const std::string& command, std::string& output,
|
||||
int* retVal, const char* dir, cmDuration timeout,
|
||||
std::ostream& ofs,
|
||||
Encoding encoding = cmProcessOutput::Auto);
|
||||
|
||||
/** Return the current tag */
|
||||
std::string GetCurrentTag();
|
||||
@@ -303,10 +303,10 @@ public:
|
||||
* environment variables prior to running the test. After running the test,
|
||||
* environment variables are restored to their previous values.
|
||||
*/
|
||||
int RunTest(std::vector<const char*> args, std::string* output, int* retVal,
|
||||
std::ostream* logfile, cmDuration testTimeOut,
|
||||
std::vector<std::string>* environment,
|
||||
Encoding encoding = cmProcessOutput::Auto);
|
||||
bool RunTest(const std::vector<std::string>& args, std::string* output,
|
||||
int* retVal, std::ostream* logfile, cmDuration testTimeOut,
|
||||
std::vector<std::string>* environment,
|
||||
Encoding encoding = cmProcessOutput::Auto);
|
||||
|
||||
/**
|
||||
* Get the handler object
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
file Copyright.txt or https://cmake.org/licensing for details. */
|
||||
#include "cmExecuteProcessCommand.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype> /* isspace */
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
@@ -16,7 +16,9 @@
|
||||
#include <cmext/algorithm>
|
||||
#include <cmext/string_view>
|
||||
|
||||
#include "cmsys/Process.h"
|
||||
#include <cm3p/uv.h>
|
||||
|
||||
#include "cm_fileno.hxx"
|
||||
|
||||
#include "cmArgumentParser.h"
|
||||
#include "cmExecutionStatus.h"
|
||||
@@ -26,6 +28,9 @@
|
||||
#include "cmProcessOutput.h"
|
||||
#include "cmStringAlgorithms.h"
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmUVHandlePtr.h"
|
||||
#include "cmUVProcessChain.h"
|
||||
#include "cmUVStream.h"
|
||||
|
||||
namespace {
|
||||
bool cmExecuteProcessCommandIsWhitespace(char c)
|
||||
@@ -36,7 +41,7 @@ bool cmExecuteProcessCommandIsWhitespace(char c)
|
||||
void cmExecuteProcessCommandFixText(std::vector<char>& output,
|
||||
bool strip_trailing_whitespace);
|
||||
void cmExecuteProcessCommandAppend(std::vector<char>& output, const char* data,
|
||||
int length);
|
||||
std::size_t length);
|
||||
}
|
||||
|
||||
// cmExecuteProcessCommand
|
||||
@@ -143,57 +148,61 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
|
||||
}
|
||||
}
|
||||
// Create a process instance.
|
||||
std::unique_ptr<cmsysProcess, void (*)(cmsysProcess*)> cp_ptr(
|
||||
cmsysProcess_New(), cmsysProcess_Delete);
|
||||
cmsysProcess* cp = cp_ptr.get();
|
||||
cmUVProcessChainBuilder builder;
|
||||
|
||||
// Set the command sequence.
|
||||
for (std::vector<std::string> const& cmd : arguments.Commands) {
|
||||
std::vector<const char*> argv(cmd.size() + 1);
|
||||
std::transform(cmd.begin(), cmd.end(), argv.begin(),
|
||||
[](std::string const& s) { return s.c_str(); });
|
||||
argv.back() = nullptr;
|
||||
cmsysProcess_AddCommand(cp, argv.data());
|
||||
builder.AddCommand(cmd);
|
||||
}
|
||||
|
||||
// Set the process working directory.
|
||||
if (!arguments.WorkingDirectory.empty()) {
|
||||
cmsysProcess_SetWorkingDirectory(cp, arguments.WorkingDirectory.c_str());
|
||||
builder.SetWorkingDirectory(arguments.WorkingDirectory);
|
||||
}
|
||||
|
||||
// Always hide the process window.
|
||||
cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
|
||||
|
||||
// Check the output variables.
|
||||
bool merge_output = false;
|
||||
std::unique_ptr<FILE, int (*)(FILE*)> inputFile(nullptr, fclose);
|
||||
if (!arguments.InputFile.empty()) {
|
||||
cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDIN,
|
||||
arguments.InputFile.c_str());
|
||||
inputFile.reset(cmsys::SystemTools::Fopen(arguments.InputFile, "rb"));
|
||||
builder.SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT,
|
||||
cm_fileno(inputFile.get()));
|
||||
} else {
|
||||
builder.SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT,
|
||||
cm_fileno(stdin));
|
||||
}
|
||||
|
||||
std::unique_ptr<FILE, int (*)(FILE*)> outputFile(nullptr, fclose);
|
||||
if (!arguments.OutputFile.empty()) {
|
||||
cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDOUT,
|
||||
arguments.OutputFile.c_str());
|
||||
}
|
||||
if (!arguments.ErrorFile.empty()) {
|
||||
if (arguments.ErrorFile == arguments.OutputFile) {
|
||||
merge_output = true;
|
||||
outputFile.reset(cmsys::SystemTools::Fopen(arguments.OutputFile, "wb"));
|
||||
builder.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT,
|
||||
cm_fileno(outputFile.get()));
|
||||
} else {
|
||||
if (arguments.OutputVariable == arguments.ErrorVariable &&
|
||||
!arguments.ErrorVariable.empty()) {
|
||||
builder.SetMergedBuiltinStreams();
|
||||
} else {
|
||||
cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDERR,
|
||||
arguments.ErrorFile.c_str());
|
||||
builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT);
|
||||
}
|
||||
}
|
||||
if (!arguments.OutputVariable.empty() &&
|
||||
arguments.OutputVariable == arguments.ErrorVariable) {
|
||||
merge_output = true;
|
||||
}
|
||||
if (merge_output) {
|
||||
cmsysProcess_SetOption(cp, cmsysProcess_Option_MergeOutput, 1);
|
||||
|
||||
std::unique_ptr<FILE, int (*)(FILE*)> errorFile(nullptr, fclose);
|
||||
if (!arguments.ErrorFile.empty()) {
|
||||
if (arguments.ErrorFile == arguments.OutputFile) {
|
||||
builder.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
|
||||
cm_fileno(outputFile.get()));
|
||||
} else {
|
||||
errorFile.reset(cmsys::SystemTools::Fopen(arguments.ErrorFile, "wb"));
|
||||
builder.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
|
||||
cm_fileno(errorFile.get()));
|
||||
}
|
||||
} else if (arguments.ErrorVariable.empty() ||
|
||||
(!arguments.ErrorVariable.empty() &&
|
||||
arguments.OutputVariable != arguments.ErrorVariable)) {
|
||||
builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
|
||||
}
|
||||
|
||||
// Set the timeout if any.
|
||||
if (timeout >= 0) {
|
||||
cmsysProcess_SetTimeout(cp, timeout);
|
||||
}
|
||||
int64_t timeoutMillis = static_cast<int64_t>(timeout * 1000.0);
|
||||
|
||||
bool echo_stdout = false;
|
||||
bool echo_stderr = false;
|
||||
@@ -241,36 +250,86 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
|
||||
}
|
||||
}
|
||||
// Start the process.
|
||||
cmsysProcess_Execute(cp);
|
||||
auto chain = builder.Start();
|
||||
|
||||
bool timedOut = false;
|
||||
cm::uv_timer_ptr timer;
|
||||
|
||||
if (timeoutMillis >= 0) {
|
||||
timer.init(chain.GetLoop(), &timedOut);
|
||||
timer.start(
|
||||
[](uv_timer_t* handle) {
|
||||
auto* timeoutPtr = static_cast<bool*>(handle->data);
|
||||
*timeoutPtr = true;
|
||||
},
|
||||
timeoutMillis, 0);
|
||||
}
|
||||
|
||||
// Read the process output.
|
||||
std::vector<char> tempOutput;
|
||||
std::vector<char> tempError;
|
||||
int length;
|
||||
char* data;
|
||||
int p;
|
||||
struct ReadData
|
||||
{
|
||||
bool Finished = false;
|
||||
std::vector<char> Output;
|
||||
cm::uv_pipe_ptr Stream;
|
||||
};
|
||||
ReadData outputData;
|
||||
ReadData errorData;
|
||||
cmProcessOutput processOutput(
|
||||
cmProcessOutput::FindEncoding(arguments.Encoding));
|
||||
std::string strdata;
|
||||
while ((p = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) {
|
||||
// Put the output in the right place.
|
||||
if (p == cmsysProcess_Pipe_STDOUT && !arguments.OutputQuiet) {
|
||||
if (arguments.OutputVariable.empty() || arguments.EchoOutputVariable) {
|
||||
processOutput.DecodeText(data, length, strdata, 1);
|
||||
cmSystemTools::Stdout(strdata);
|
||||
}
|
||||
if (!arguments.OutputVariable.empty()) {
|
||||
cmExecuteProcessCommandAppend(tempOutput, data, length);
|
||||
}
|
||||
} else if (p == cmsysProcess_Pipe_STDERR && !arguments.ErrorQuiet) {
|
||||
if (arguments.ErrorVariable.empty() || arguments.EchoErrorVariable) {
|
||||
processOutput.DecodeText(data, length, strdata, 2);
|
||||
cmSystemTools::Stderr(strdata);
|
||||
}
|
||||
if (!arguments.ErrorVariable.empty()) {
|
||||
cmExecuteProcessCommandAppend(tempError, data, length);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<cmUVStreamReadHandle> outputHandle;
|
||||
if (chain.OutputStream() >= 0) {
|
||||
outputData.Stream.init(chain.GetLoop(), 0);
|
||||
uv_pipe_open(outputData.Stream, chain.OutputStream());
|
||||
outputHandle = cmUVStreamRead(
|
||||
outputData.Stream,
|
||||
[&arguments, &processOutput, &outputData,
|
||||
&strdata](std::vector<char> data) {
|
||||
if (!arguments.OutputQuiet) {
|
||||
if (arguments.OutputVariable.empty() ||
|
||||
arguments.EchoOutputVariable) {
|
||||
processOutput.DecodeText(data.data(), data.size(), strdata, 1);
|
||||
cmSystemTools::Stdout(strdata);
|
||||
}
|
||||
if (!arguments.OutputVariable.empty()) {
|
||||
cmExecuteProcessCommandAppend(outputData.Output, data.data(),
|
||||
data.size());
|
||||
}
|
||||
}
|
||||
},
|
||||
[&outputData]() { outputData.Finished = true; });
|
||||
} else {
|
||||
outputData.Finished = true;
|
||||
}
|
||||
std::unique_ptr<cmUVStreamReadHandle> errorHandle;
|
||||
if (chain.ErrorStream() >= 0 &&
|
||||
chain.ErrorStream() != chain.OutputStream()) {
|
||||
errorData.Stream.init(chain.GetLoop(), 0);
|
||||
uv_pipe_open(errorData.Stream, chain.ErrorStream());
|
||||
errorHandle = cmUVStreamRead(
|
||||
errorData.Stream,
|
||||
[&arguments, &processOutput, &errorData,
|
||||
&strdata](std::vector<char> data) {
|
||||
if (!arguments.ErrorQuiet) {
|
||||
if (arguments.ErrorVariable.empty() || arguments.EchoErrorVariable) {
|
||||
processOutput.DecodeText(data.data(), data.size(), strdata, 2);
|
||||
cmSystemTools::Stderr(strdata);
|
||||
}
|
||||
if (!arguments.ErrorVariable.empty()) {
|
||||
cmExecuteProcessCommandAppend(errorData.Output, data.data(),
|
||||
data.size());
|
||||
}
|
||||
}
|
||||
},
|
||||
[&errorData]() { errorData.Finished = true; });
|
||||
} else {
|
||||
errorData.Finished = true;
|
||||
}
|
||||
|
||||
while (chain.Valid() && !timedOut &&
|
||||
!(chain.Finished() && outputData.Finished && errorData.Finished)) {
|
||||
uv_run(&chain.GetLoop(), UV_RUN_ONCE);
|
||||
}
|
||||
if (!arguments.OutputQuiet &&
|
||||
(arguments.OutputVariable.empty() || arguments.EchoOutputVariable)) {
|
||||
@@ -287,151 +346,102 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
|
||||
}
|
||||
}
|
||||
|
||||
// All output has been read. Wait for the process to exit.
|
||||
cmsysProcess_WaitForExit(cp, nullptr);
|
||||
processOutput.DecodeText(tempOutput, tempOutput);
|
||||
processOutput.DecodeText(tempError, tempError);
|
||||
// All output has been read.
|
||||
processOutput.DecodeText(outputData.Output, outputData.Output);
|
||||
processOutput.DecodeText(errorData.Output, errorData.Output);
|
||||
|
||||
// Fix the text in the output strings.
|
||||
cmExecuteProcessCommandFixText(tempOutput,
|
||||
cmExecuteProcessCommandFixText(outputData.Output,
|
||||
arguments.OutputStripTrailingWhitespace);
|
||||
cmExecuteProcessCommandFixText(tempError,
|
||||
cmExecuteProcessCommandFixText(errorData.Output,
|
||||
arguments.ErrorStripTrailingWhitespace);
|
||||
|
||||
// Store the output obtained.
|
||||
if (!arguments.OutputVariable.empty() && !tempOutput.empty()) {
|
||||
if (!arguments.OutputVariable.empty() && !outputData.Output.empty()) {
|
||||
status.GetMakefile().AddDefinition(arguments.OutputVariable,
|
||||
tempOutput.data());
|
||||
outputData.Output.data());
|
||||
}
|
||||
if (!merge_output && !arguments.ErrorVariable.empty() &&
|
||||
!tempError.empty()) {
|
||||
if (arguments.ErrorVariable != arguments.OutputVariable &&
|
||||
!arguments.ErrorVariable.empty() && !errorData.Output.empty()) {
|
||||
status.GetMakefile().AddDefinition(arguments.ErrorVariable,
|
||||
tempError.data());
|
||||
errorData.Output.data());
|
||||
}
|
||||
|
||||
// Store the result of running the process.
|
||||
if (!arguments.ResultVariable.empty()) {
|
||||
switch (cmsysProcess_GetState(cp)) {
|
||||
case cmsysProcess_State_Exited: {
|
||||
int v = cmsysProcess_GetExitValue(cp);
|
||||
char buf[16];
|
||||
snprintf(buf, sizeof(buf), "%d", v);
|
||||
status.GetMakefile().AddDefinition(arguments.ResultVariable, buf);
|
||||
} break;
|
||||
case cmsysProcess_State_Exception:
|
||||
if (timedOut) {
|
||||
status.GetMakefile().AddDefinition(arguments.ResultVariable,
|
||||
"Process terminated due to timeout");
|
||||
} else {
|
||||
auto const* lastStatus = chain.GetStatus().back();
|
||||
auto exception = lastStatus->GetException();
|
||||
if (exception.first == cmUVProcessChain::ExceptionCode::None) {
|
||||
status.GetMakefile().AddDefinition(
|
||||
arguments.ResultVariable, cmsysProcess_GetExceptionString(cp));
|
||||
break;
|
||||
case cmsysProcess_State_Error:
|
||||
arguments.ResultVariable,
|
||||
std::to_string(static_cast<int>(lastStatus->ExitStatus)));
|
||||
} else {
|
||||
status.GetMakefile().AddDefinition(arguments.ResultVariable,
|
||||
cmsysProcess_GetErrorString(cp));
|
||||
break;
|
||||
case cmsysProcess_State_Expired:
|
||||
status.GetMakefile().AddDefinition(
|
||||
arguments.ResultVariable, "Process terminated due to timeout");
|
||||
break;
|
||||
exception.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Store the result of running the processes.
|
||||
if (!arguments.ResultsVariable.empty()) {
|
||||
switch (cmsysProcess_GetState(cp)) {
|
||||
case cmsysProcess_State_Exited: {
|
||||
std::vector<std::string> res;
|
||||
for (size_t i = 0; i < arguments.Commands.size(); ++i) {
|
||||
switch (cmsysProcess_GetStateByIndex(cp, static_cast<int>(i))) {
|
||||
case kwsysProcess_StateByIndex_Exited: {
|
||||
int exitCode =
|
||||
cmsysProcess_GetExitValueByIndex(cp, static_cast<int>(i));
|
||||
char buf[16];
|
||||
snprintf(buf, sizeof(buf), "%d", exitCode);
|
||||
res.emplace_back(buf);
|
||||
} break;
|
||||
case kwsysProcess_StateByIndex_Exception:
|
||||
res.emplace_back(cmsysProcess_GetExceptionStringByIndex(
|
||||
cp, static_cast<int>(i)));
|
||||
break;
|
||||
case kwsysProcess_StateByIndex_Error:
|
||||
default:
|
||||
res.emplace_back("Error getting the child return code");
|
||||
break;
|
||||
}
|
||||
if (timedOut) {
|
||||
status.GetMakefile().AddDefinition(arguments.ResultsVariable,
|
||||
"Process terminated due to timeout");
|
||||
} else {
|
||||
std::vector<std::string> res;
|
||||
for (auto const* processStatus : chain.GetStatus()) {
|
||||
auto exception = processStatus->GetException();
|
||||
if (exception.first == cmUVProcessChain::ExceptionCode::None) {
|
||||
res.emplace_back(
|
||||
std::to_string(static_cast<int>(processStatus->ExitStatus)));
|
||||
} else {
|
||||
res.emplace_back(exception.second);
|
||||
}
|
||||
status.GetMakefile().AddDefinition(arguments.ResultsVariable,
|
||||
cmList::to_string(res));
|
||||
} break;
|
||||
case cmsysProcess_State_Exception:
|
||||
status.GetMakefile().AddDefinition(
|
||||
arguments.ResultsVariable, cmsysProcess_GetExceptionString(cp));
|
||||
break;
|
||||
case cmsysProcess_State_Error:
|
||||
status.GetMakefile().AddDefinition(arguments.ResultsVariable,
|
||||
cmsysProcess_GetErrorString(cp));
|
||||
break;
|
||||
case cmsysProcess_State_Expired:
|
||||
status.GetMakefile().AddDefinition(
|
||||
arguments.ResultsVariable, "Process terminated due to timeout");
|
||||
break;
|
||||
}
|
||||
status.GetMakefile().AddDefinition(arguments.ResultsVariable,
|
||||
cmList::to_string(res));
|
||||
}
|
||||
}
|
||||
|
||||
auto queryProcessStatusByIndex = [&cp](int index) -> std::string {
|
||||
std::string processStatus;
|
||||
switch (cmsysProcess_GetStateByIndex(cp, static_cast<int>(index))) {
|
||||
case kwsysProcess_StateByIndex_Exited: {
|
||||
int exitCode = cmsysProcess_GetExitValueByIndex(cp, index);
|
||||
if (exitCode) {
|
||||
processStatus = "Child return code: " + std::to_string(exitCode);
|
||||
}
|
||||
} break;
|
||||
case kwsysProcess_StateByIndex_Exception: {
|
||||
processStatus = cmStrCat(
|
||||
"Abnormal exit with child return code: ",
|
||||
cmsysProcess_GetExceptionStringByIndex(cp, static_cast<int>(index)));
|
||||
break;
|
||||
auto queryProcessStatusByIndex = [&chain](std::size_t index) -> std::string {
|
||||
auto const& processStatus = chain.GetStatus(index);
|
||||
auto exception = processStatus.GetException();
|
||||
if (exception.first == cmUVProcessChain::ExceptionCode::None) {
|
||||
if (processStatus.ExitStatus) {
|
||||
return cmStrCat("Child return code: ", processStatus.ExitStatus);
|
||||
}
|
||||
case kwsysProcess_StateByIndex_Error:
|
||||
default:
|
||||
processStatus = "Error getting the child return code";
|
||||
break;
|
||||
return "";
|
||||
}
|
||||
return processStatus;
|
||||
return cmStrCat("Abnormal exit with child return code: ",
|
||||
exception.second);
|
||||
};
|
||||
|
||||
if (arguments.CommandErrorIsFatal == "ANY"_s) {
|
||||
bool ret = true;
|
||||
switch (cmsysProcess_GetState(cp)) {
|
||||
case cmsysProcess_State_Exited: {
|
||||
std::map<int, std::string> failureIndices;
|
||||
for (int i = 0; i < static_cast<int>(arguments.Commands.size()); ++i) {
|
||||
std::string processStatus = queryProcessStatusByIndex(i);
|
||||
if (!processStatus.empty()) {
|
||||
failureIndices[i] = processStatus;
|
||||
}
|
||||
if (!failureIndices.empty()) {
|
||||
std::ostringstream oss;
|
||||
oss << "failed command indexes:\n";
|
||||
for (auto const& e : failureIndices) {
|
||||
oss << " " << e.first + 1 << ": \"" << e.second << "\"\n";
|
||||
}
|
||||
status.SetError(oss.str());
|
||||
ret = false;
|
||||
}
|
||||
if (timedOut) {
|
||||
status.SetError("Process terminated due to timeout");
|
||||
ret = false;
|
||||
} else {
|
||||
std::map<std::size_t, std::string> failureIndices;
|
||||
auto statuses = chain.GetStatus();
|
||||
for (std::size_t i = 0; i < statuses.size(); ++i) {
|
||||
std::string processStatus = queryProcessStatusByIndex(i);
|
||||
if (!processStatus.empty()) {
|
||||
failureIndices[i] = processStatus;
|
||||
}
|
||||
} break;
|
||||
case cmsysProcess_State_Exception:
|
||||
status.SetError(
|
||||
cmStrCat("abnormal exit: ", cmsysProcess_GetExceptionString(cp)));
|
||||
}
|
||||
if (!failureIndices.empty()) {
|
||||
std::ostringstream oss;
|
||||
oss << "failed command indexes:\n";
|
||||
for (auto const& e : failureIndices) {
|
||||
oss << " " << e.first + 1 << ": \"" << e.second << "\"\n";
|
||||
}
|
||||
status.SetError(oss.str());
|
||||
ret = false;
|
||||
break;
|
||||
case cmsysProcess_State_Error:
|
||||
status.SetError(cmStrCat("error getting child return code: ",
|
||||
cmsysProcess_GetErrorString(cp)));
|
||||
ret = false;
|
||||
break;
|
||||
case cmsysProcess_State_Expired:
|
||||
status.SetError("Process terminated due to timeout");
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
@@ -442,29 +452,23 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
|
||||
|
||||
if (arguments.CommandErrorIsFatal == "LAST"_s) {
|
||||
bool ret = true;
|
||||
switch (cmsysProcess_GetState(cp)) {
|
||||
case cmsysProcess_State_Exited: {
|
||||
if (timedOut) {
|
||||
status.SetError("Process terminated due to timeout");
|
||||
ret = false;
|
||||
} else {
|
||||
auto const& lastStatus = chain.GetStatus(arguments.Commands.size() - 1);
|
||||
auto exception = lastStatus.GetException();
|
||||
if (exception.first != cmUVProcessChain::ExceptionCode::None) {
|
||||
status.SetError(cmStrCat("Abnormal exit: ", exception.second));
|
||||
ret = false;
|
||||
} else {
|
||||
int lastIndex = static_cast<int>(arguments.Commands.size() - 1);
|
||||
const std::string processStatus = queryProcessStatusByIndex(lastIndex);
|
||||
if (!processStatus.empty()) {
|
||||
status.SetError("last command failed");
|
||||
ret = false;
|
||||
}
|
||||
} break;
|
||||
case cmsysProcess_State_Exception:
|
||||
status.SetError(
|
||||
cmStrCat("Abnormal exit: ", cmsysProcess_GetExceptionString(cp)));
|
||||
ret = false;
|
||||
break;
|
||||
case cmsysProcess_State_Error:
|
||||
status.SetError(cmStrCat("Error getting child return code: ",
|
||||
cmsysProcess_GetErrorString(cp)));
|
||||
ret = false;
|
||||
break;
|
||||
case cmsysProcess_State_Expired:
|
||||
status.SetError("Process terminated due to timeout");
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ret) {
|
||||
cmSystemTools::SetFatalErrorOccurred();
|
||||
@@ -507,7 +511,7 @@ void cmExecuteProcessCommandFixText(std::vector<char>& output,
|
||||
}
|
||||
|
||||
void cmExecuteProcessCommandAppend(std::vector<char>& output, const char* data,
|
||||
int length)
|
||||
std::size_t length)
|
||||
{
|
||||
#if defined(__APPLE__)
|
||||
// HACK on Apple to work around bug with inserting at the
|
||||
|
||||
@@ -2,48 +2,68 @@
|
||||
file Copyright.txt or https://cmake.org/licensing for details. */
|
||||
#include "cmProcessTools.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <ostream>
|
||||
|
||||
#include "cmsys/Process.h"
|
||||
#include <cm3p/uv.h>
|
||||
|
||||
#include "cmProcessOutput.h"
|
||||
#include "cmUVHandlePtr.h"
|
||||
#include "cmUVStream.h"
|
||||
|
||||
void cmProcessTools::RunProcess(struct cmsysProcess_s* cp, OutputParser* out,
|
||||
OutputParser* err, Encoding encoding)
|
||||
std::vector<cmUVProcessChain::Status> cmProcessTools::RunProcess(
|
||||
cmUVProcessChainBuilder& builder, OutputParser* out, OutputParser* err,
|
||||
Encoding encoding)
|
||||
{
|
||||
cmsysProcess_Execute(cp);
|
||||
char* data = nullptr;
|
||||
int length = 0;
|
||||
int p;
|
||||
cmProcessOutput processOutput(encoding);
|
||||
|
||||
builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
|
||||
.SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
|
||||
|
||||
auto chain = builder.Start();
|
||||
|
||||
std::string strdata;
|
||||
while ((out || err) &&
|
||||
(p = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) {
|
||||
if (out && p == cmsysProcess_Pipe_STDOUT) {
|
||||
processOutput.DecodeText(data, length, strdata, 1);
|
||||
if (!out->Process(strdata.c_str(), static_cast<int>(strdata.size()))) {
|
||||
out = nullptr;
|
||||
cm::uv_pipe_ptr outputPipe;
|
||||
outputPipe.init(chain.GetLoop(), 0);
|
||||
uv_pipe_open(outputPipe, chain.OutputStream());
|
||||
auto outputHandle = cmUVStreamRead(
|
||||
outputPipe,
|
||||
[&out, &processOutput, &strdata](std::vector<char> data) {
|
||||
if (out) {
|
||||
processOutput.DecodeText(data.data(), data.size(), strdata, 1);
|
||||
if (!out->Process(strdata.c_str(), static_cast<int>(strdata.size()))) {
|
||||
out = nullptr;
|
||||
}
|
||||
}
|
||||
} else if (err && p == cmsysProcess_Pipe_STDERR) {
|
||||
processOutput.DecodeText(data, length, strdata, 2);
|
||||
if (!err->Process(strdata.c_str(), static_cast<int>(strdata.size()))) {
|
||||
err = nullptr;
|
||||
},
|
||||
[&out]() { out = nullptr; });
|
||||
cm::uv_pipe_ptr errorPipe;
|
||||
errorPipe.init(chain.GetLoop(), 0);
|
||||
uv_pipe_open(errorPipe, chain.ErrorStream());
|
||||
auto errorHandle = cmUVStreamRead(
|
||||
errorPipe,
|
||||
[&err, &processOutput, &strdata](std::vector<char> data) {
|
||||
if (err) {
|
||||
processOutput.DecodeText(data.data(), data.size(), strdata, 2);
|
||||
if (!err->Process(strdata.c_str(), static_cast<int>(strdata.size()))) {
|
||||
err = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
[&err]() { err = nullptr; });
|
||||
while (out || err || !chain.Finished()) {
|
||||
uv_run(&chain.GetLoop(), UV_RUN_ONCE);
|
||||
}
|
||||
if (out) {
|
||||
processOutput.DecodeText(std::string(), strdata, 1);
|
||||
if (!strdata.empty()) {
|
||||
out->Process(strdata.c_str(), static_cast<int>(strdata.size()));
|
||||
}
|
||||
}
|
||||
if (err) {
|
||||
processOutput.DecodeText(std::string(), strdata, 2);
|
||||
if (!strdata.empty()) {
|
||||
err->Process(strdata.c_str(), static_cast<int>(strdata.size()));
|
||||
}
|
||||
}
|
||||
cmsysProcess_WaitForExit(cp, nullptr);
|
||||
|
||||
std::vector<cmUVProcessChain::Status> result;
|
||||
auto status = chain.GetStatus();
|
||||
std::transform(
|
||||
status.begin(), status.end(), std::back_inserter(result),
|
||||
[](const cmUVProcessChain::Status* s) -> cmUVProcessChain::Status {
|
||||
return *s;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
cmProcessTools::LineParser::LineParser(char sep, bool ignoreCR)
|
||||
|
||||
@@ -7,8 +7,10 @@
|
||||
#include <cstring>
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "cmProcessOutput.h"
|
||||
#include "cmUVProcessChain.h"
|
||||
|
||||
/** \class cmProcessTools
|
||||
* \brief Helper classes for process output parsing
|
||||
@@ -81,7 +83,7 @@ public:
|
||||
};
|
||||
|
||||
/** Run a process and send output to given parsers. */
|
||||
static void RunProcess(struct cmsysProcess_s* cp, OutputParser* out,
|
||||
OutputParser* err = nullptr,
|
||||
Encoding encoding = cmProcessOutput::Auto);
|
||||
static std::vector<cmUVProcessChain::Status> RunProcess(
|
||||
cmUVProcessChainBuilder& builder, OutputParser* out,
|
||||
OutputParser* err = nullptr, Encoding encoding = cmProcessOutput::Auto);
|
||||
};
|
||||
|
||||
@@ -25,12 +25,17 @@
|
||||
|
||||
#include <cm3p/uv.h>
|
||||
|
||||
#include "cm_fileno.hxx"
|
||||
|
||||
#include "cmDuration.h"
|
||||
#include "cmELF.h"
|
||||
#include "cmMessageMetadata.h"
|
||||
#include "cmProcessOutput.h"
|
||||
#include "cmRange.h"
|
||||
#include "cmStringAlgorithms.h"
|
||||
#include "cmUVHandlePtr.h"
|
||||
#include "cmUVProcessChain.h"
|
||||
#include "cmUVStream.h"
|
||||
#include "cmValue.h"
|
||||
|
||||
#if !defined(CMAKE_BOOTSTRAP)
|
||||
@@ -59,12 +64,14 @@
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <cerrno>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -568,85 +575,113 @@ bool cmSystemTools::RunSingleCommand(std::vector<std::string> const& command,
|
||||
const char* dir, OutputOption outputflag,
|
||||
cmDuration timeout, Encoding encoding)
|
||||
{
|
||||
std::vector<const char*> argv;
|
||||
argv.reserve(command.size() + 1);
|
||||
for (std::string const& cmd : command) {
|
||||
argv.push_back(cmd.c_str());
|
||||
}
|
||||
argv.push_back(nullptr);
|
||||
|
||||
cmsysProcess* cp = cmsysProcess_New();
|
||||
cmsysProcess_SetCommand(cp, argv.data());
|
||||
cmsysProcess_SetWorkingDirectory(cp, dir);
|
||||
if (cmSystemTools::GetRunCommandHideConsole()) {
|
||||
cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
|
||||
cmUVProcessChainBuilder builder;
|
||||
builder.AddCommand(command);
|
||||
if (dir) {
|
||||
builder.SetWorkingDirectory(dir);
|
||||
}
|
||||
|
||||
if (outputflag == OUTPUT_PASSTHROUGH) {
|
||||
cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1);
|
||||
cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1);
|
||||
captureStdOut = nullptr;
|
||||
captureStdErr = nullptr;
|
||||
builder
|
||||
.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT,
|
||||
cm_fileno(stdout))
|
||||
.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
|
||||
cm_fileno(stderr));
|
||||
} else if (outputflag == OUTPUT_MERGE ||
|
||||
(captureStdErr && captureStdErr == captureStdOut)) {
|
||||
cmsysProcess_SetOption(cp, cmsysProcess_Option_MergeOutput, 1);
|
||||
builder.SetMergedBuiltinStreams();
|
||||
captureStdErr = nullptr;
|
||||
} else {
|
||||
builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
|
||||
.SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
|
||||
}
|
||||
assert(!captureStdErr || captureStdErr != captureStdOut);
|
||||
|
||||
cmsysProcess_SetTimeout(cp, timeout.count());
|
||||
cmsysProcess_Execute(cp);
|
||||
auto chain = builder.Start();
|
||||
bool timedOut = false;
|
||||
cm::uv_timer_ptr timer;
|
||||
if (timeout.count()) {
|
||||
timer.init(chain.GetLoop(), &timedOut);
|
||||
timer.start(
|
||||
[](uv_timer_t* t) {
|
||||
auto* timedOutPtr = static_cast<bool*>(t->data);
|
||||
*timedOutPtr = true;
|
||||
},
|
||||
static_cast<uint64_t>(timeout.count() * 1000.0), 0);
|
||||
}
|
||||
|
||||
std::vector<char> tempStdOut;
|
||||
std::vector<char> tempStdErr;
|
||||
char* data;
|
||||
int length;
|
||||
int pipe;
|
||||
cm::uv_pipe_ptr outStream;
|
||||
bool outFinished = true;
|
||||
cm::uv_pipe_ptr errStream;
|
||||
bool errFinished = true;
|
||||
cmProcessOutput processOutput(encoding);
|
||||
std::string strdata;
|
||||
std::unique_ptr<cmUVStreamReadHandle> outputHandle;
|
||||
std::unique_ptr<cmUVStreamReadHandle> errorHandle;
|
||||
if (outputflag != OUTPUT_PASSTHROUGH &&
|
||||
(captureStdOut || captureStdErr || outputflag != OUTPUT_NONE)) {
|
||||
while ((pipe = cmsysProcess_WaitForData(cp, &data, &length, nullptr)) >
|
||||
0) {
|
||||
// Translate NULL characters in the output into valid text.
|
||||
for (int i = 0; i < length; ++i) {
|
||||
if (data[i] == '\0') {
|
||||
data[i] = ' ';
|
||||
}
|
||||
auto startRead =
|
||||
[&outputflag, &processOutput,
|
||||
&chain](cm::uv_pipe_ptr& pipe, int stream, std::string* captureStd,
|
||||
std::vector<char>& tempStd, int id,
|
||||
void (*outputFunc)(const std::string&),
|
||||
bool& finished) -> std::unique_ptr<cmUVStreamReadHandle> {
|
||||
if (stream < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (pipe == cmsysProcess_Pipe_STDOUT) {
|
||||
if (outputflag != OUTPUT_NONE) {
|
||||
processOutput.DecodeText(data, length, strdata, 1);
|
||||
cmSystemTools::Stdout(strdata);
|
||||
}
|
||||
if (captureStdOut) {
|
||||
cm::append(tempStdOut, data, data + length);
|
||||
}
|
||||
} else if (pipe == cmsysProcess_Pipe_STDERR) {
|
||||
if (outputflag != OUTPUT_NONE) {
|
||||
processOutput.DecodeText(data, length, strdata, 2);
|
||||
cmSystemTools::Stderr(strdata);
|
||||
}
|
||||
if (captureStdErr) {
|
||||
cm::append(tempStdErr, data, data + length);
|
||||
}
|
||||
}
|
||||
}
|
||||
pipe.init(chain.GetLoop(), 0);
|
||||
uv_pipe_open(pipe, stream);
|
||||
|
||||
if (outputflag != OUTPUT_NONE) {
|
||||
processOutput.DecodeText(std::string(), strdata, 1);
|
||||
if (!strdata.empty()) {
|
||||
cmSystemTools::Stdout(strdata);
|
||||
}
|
||||
processOutput.DecodeText(std::string(), strdata, 2);
|
||||
if (!strdata.empty()) {
|
||||
cmSystemTools::Stderr(strdata);
|
||||
}
|
||||
finished = false;
|
||||
return cmUVStreamRead(
|
||||
pipe,
|
||||
[outputflag, &processOutput, captureStd, &tempStd, id,
|
||||
outputFunc](std::vector<char> data) {
|
||||
// Translate NULL characters in the output into valid text.
|
||||
for (auto& c : data) {
|
||||
if (c == '\0') {
|
||||
c = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
if (outputflag != OUTPUT_NONE) {
|
||||
std::string strdata;
|
||||
processOutput.DecodeText(data.data(), data.size(), strdata, id);
|
||||
outputFunc(strdata);
|
||||
}
|
||||
if (captureStd) {
|
||||
cm::append(tempStd, data.data(), data.data() + data.size());
|
||||
}
|
||||
},
|
||||
[&finished, outputflag, &processOutput, id, outputFunc]() {
|
||||
finished = true;
|
||||
if (outputflag != OUTPUT_NONE) {
|
||||
std::string strdata;
|
||||
processOutput.DecodeText(std::string(), strdata, id);
|
||||
if (!strdata.empty()) {
|
||||
outputFunc(strdata);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
outputHandle =
|
||||
startRead(outStream, chain.OutputStream(), captureStdOut, tempStdOut, 1,
|
||||
cmSystemTools::Stdout, outFinished);
|
||||
if (chain.OutputStream() != chain.ErrorStream()) {
|
||||
errorHandle =
|
||||
startRead(errStream, chain.ErrorStream(), captureStdErr, tempStdErr, 2,
|
||||
cmSystemTools::Stderr, errFinished);
|
||||
}
|
||||
}
|
||||
|
||||
cmsysProcess_WaitForExit(cp, nullptr);
|
||||
while (!timedOut && !(chain.Finished() && outFinished && errFinished)) {
|
||||
uv_run(&chain.GetLoop(), UV_RUN_ONCE);
|
||||
}
|
||||
|
||||
if (captureStdOut) {
|
||||
captureStdOut->assign(tempStdOut.begin(), tempStdOut.end());
|
||||
@@ -658,37 +693,7 @@ bool cmSystemTools::RunSingleCommand(std::vector<std::string> const& command,
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exited) {
|
||||
if (retVal) {
|
||||
*retVal = cmsysProcess_GetExitValue(cp);
|
||||
} else {
|
||||
if (cmsysProcess_GetExitValue(cp) != 0) {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
} else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exception) {
|
||||
const char* exception_str = cmsysProcess_GetExceptionString(cp);
|
||||
if (outputflag != OUTPUT_NONE) {
|
||||
std::cerr << exception_str << std::endl;
|
||||
}
|
||||
if (captureStdErr) {
|
||||
captureStdErr->append(exception_str, strlen(exception_str));
|
||||
} else if (captureStdOut) {
|
||||
captureStdOut->append(exception_str, strlen(exception_str));
|
||||
}
|
||||
result = false;
|
||||
} else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Error) {
|
||||
const char* error_str = cmsysProcess_GetErrorString(cp);
|
||||
if (outputflag != OUTPUT_NONE) {
|
||||
std::cerr << error_str << std::endl;
|
||||
}
|
||||
if (captureStdErr) {
|
||||
captureStdErr->append(error_str, strlen(error_str));
|
||||
} else if (captureStdOut) {
|
||||
captureStdOut->append(error_str, strlen(error_str));
|
||||
}
|
||||
result = false;
|
||||
} else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Expired) {
|
||||
if (timedOut) {
|
||||
const char* error_str = "Process terminated due to timeout\n";
|
||||
if (outputflag != OUTPUT_NONE) {
|
||||
std::cerr << error_str << std::endl;
|
||||
@@ -697,9 +702,34 @@ bool cmSystemTools::RunSingleCommand(std::vector<std::string> const& command,
|
||||
captureStdErr->append(error_str, strlen(error_str));
|
||||
}
|
||||
result = false;
|
||||
} else {
|
||||
auto const& status = chain.GetStatus(0);
|
||||
auto exception = status.GetException();
|
||||
|
||||
switch (exception.first) {
|
||||
case cmUVProcessChain::ExceptionCode::None:
|
||||
if (retVal) {
|
||||
*retVal = static_cast<int>(status.ExitStatus);
|
||||
} else {
|
||||
if (status.ExitStatus != 0) {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
if (outputflag != OUTPUT_NONE) {
|
||||
std::cerr << exception.second << std::endl;
|
||||
}
|
||||
if (captureStdErr) {
|
||||
captureStdErr->append(exception.second);
|
||||
} else if (captureStdOut) {
|
||||
captureStdOut->append(exception.second);
|
||||
}
|
||||
result = false;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
cmsysProcess_Delete(cp);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -2213,9 +2243,10 @@ bool cmSystemTools::ListTar(const std::string& outFileName,
|
||||
#endif
|
||||
}
|
||||
|
||||
int cmSystemTools::WaitForLine(cmsysProcess* process, std::string& line,
|
||||
cmDuration timeout, std::vector<char>& out,
|
||||
std::vector<char>& err)
|
||||
cmSystemTools::WaitForLineResult cmSystemTools::WaitForLine(
|
||||
uv_loop_t* loop, uv_stream_t* outPipe, uv_stream_t* errPipe,
|
||||
std::string& line, cmDuration timeout, std::vector<char>& out,
|
||||
std::vector<char>& err)
|
||||
{
|
||||
line.clear();
|
||||
auto outiter = out.begin();
|
||||
@@ -2237,7 +2268,7 @@ int cmSystemTools::WaitForLine(cmsysProcess* process, std::string& line,
|
||||
line.append(out.data(), length);
|
||||
}
|
||||
out.erase(out.begin(), outiter + 1);
|
||||
return cmsysProcess_Pipe_STDOUT;
|
||||
return WaitForLineResult::STDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2255,33 +2286,66 @@ int cmSystemTools::WaitForLine(cmsysProcess* process, std::string& line,
|
||||
line.append(err.data(), length);
|
||||
}
|
||||
err.erase(err.begin(), erriter + 1);
|
||||
return cmsysProcess_Pipe_STDERR;
|
||||
return WaitForLineResult::STDERR;
|
||||
}
|
||||
}
|
||||
|
||||
// No newlines found. Wait for more data from the process.
|
||||
int length;
|
||||
char* data;
|
||||
double timeoutAsDbl = timeout.count();
|
||||
int pipe =
|
||||
cmsysProcess_WaitForData(process, &data, &length, &timeoutAsDbl);
|
||||
if (pipe == cmsysProcess_Pipe_Timeout) {
|
||||
struct ReadData
|
||||
{
|
||||
uv_stream_t* Stream;
|
||||
std::vector<char> Buffer;
|
||||
bool Read = false;
|
||||
bool Finished = false;
|
||||
};
|
||||
auto startRead =
|
||||
[](uv_stream_t* stream,
|
||||
ReadData& data) -> std::unique_ptr<cmUVStreamReadHandle> {
|
||||
data.Stream = stream;
|
||||
return cmUVStreamRead(
|
||||
stream,
|
||||
[&data](std::vector<char> buf) {
|
||||
data.Buffer = std::move(buf);
|
||||
data.Read = true;
|
||||
uv_read_stop(data.Stream);
|
||||
},
|
||||
[&data]() { data.Finished = true; });
|
||||
};
|
||||
ReadData outData;
|
||||
auto outHandle = startRead(outPipe, outData);
|
||||
ReadData errData;
|
||||
auto errHandle = startRead(errPipe, errData);
|
||||
|
||||
cm::uv_timer_ptr timer;
|
||||
bool timedOut = false;
|
||||
timer.init(*loop, &timedOut);
|
||||
timer.start(
|
||||
[](uv_timer_t* handle) {
|
||||
auto* timedOutPtr = static_cast<bool*>(handle->data);
|
||||
*timedOutPtr = true;
|
||||
},
|
||||
static_cast<uint64_t>(timeout.count() * 1000.0), 0);
|
||||
|
||||
uv_run(loop, UV_RUN_ONCE);
|
||||
if (timedOut) {
|
||||
// Timeout has been exceeded.
|
||||
return pipe;
|
||||
return WaitForLineResult::Timeout;
|
||||
}
|
||||
if (pipe == cmsysProcess_Pipe_STDOUT) {
|
||||
processOutput.DecodeText(data, length, strdata, 1);
|
||||
if (outData.Read) {
|
||||
processOutput.DecodeText(outData.Buffer.data(), outData.Buffer.size(),
|
||||
strdata, 1);
|
||||
// Append to the stdout buffer.
|
||||
std::vector<char>::size_type size = out.size();
|
||||
cm::append(out, strdata);
|
||||
outiter = out.begin() + size;
|
||||
} else if (pipe == cmsysProcess_Pipe_STDERR) {
|
||||
processOutput.DecodeText(data, length, strdata, 2);
|
||||
} else if (errData.Read) {
|
||||
processOutput.DecodeText(errData.Buffer.data(), errData.Buffer.size(),
|
||||
strdata, 2);
|
||||
// Append to the stderr buffer.
|
||||
std::vector<char>::size_type size = err.size();
|
||||
cm::append(err, strdata);
|
||||
erriter = err.begin() + size;
|
||||
} else if (pipe == cmsysProcess_Pipe_None) {
|
||||
} else if (outData.Finished && errData.Finished) {
|
||||
// Both stdout and stderr pipes have broken. Return leftover data.
|
||||
processOutput.DecodeText(std::string(), strdata, 1);
|
||||
if (!strdata.empty()) {
|
||||
@@ -2298,14 +2362,20 @@ int cmSystemTools::WaitForLine(cmsysProcess* process, std::string& line,
|
||||
if (!out.empty()) {
|
||||
line.append(out.data(), outiter - out.begin());
|
||||
out.erase(out.begin(), out.end());
|
||||
return cmsysProcess_Pipe_STDOUT;
|
||||
return WaitForLineResult::STDOUT;
|
||||
}
|
||||
if (!err.empty()) {
|
||||
line.append(err.data(), erriter - err.begin());
|
||||
err.erase(err.begin(), err.end());
|
||||
return cmsysProcess_Pipe_STDERR;
|
||||
return WaitForLineResult::STDERR;
|
||||
}
|
||||
return cmsysProcess_Pipe_None;
|
||||
return WaitForLineResult::None;
|
||||
}
|
||||
if (!outData.Finished) {
|
||||
uv_read_stop(outPipe);
|
||||
}
|
||||
if (!errData.Finished) {
|
||||
uv_read_stop(errPipe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
#include <cm/optional>
|
||||
#include <cm/string_view>
|
||||
|
||||
#include "cmsys/Process.h"
|
||||
#include <cm3p/uv.h>
|
||||
|
||||
#include "cmsys/Status.hxx" // IWYU pragma: export
|
||||
#include "cmsys/SystemTools.hxx" // IWYU pragma: export
|
||||
|
||||
@@ -339,10 +340,20 @@ public:
|
||||
*/
|
||||
static void ReportLastSystemError(const char* m);
|
||||
|
||||
/** a general output handler for cmsysProcess */
|
||||
static int WaitForLine(cmsysProcess* process, std::string& line,
|
||||
cmDuration timeout, std::vector<char>& out,
|
||||
std::vector<char>& err);
|
||||
enum class WaitForLineResult
|
||||
{
|
||||
None,
|
||||
STDOUT,
|
||||
STDERR,
|
||||
Timeout,
|
||||
};
|
||||
|
||||
/** a general output handler for libuv */
|
||||
static WaitForLineResult WaitForLine(uv_loop_t* loop, uv_stream_t* outPipe,
|
||||
uv_stream_t* errPipe, std::string& line,
|
||||
cmDuration timeout,
|
||||
std::vector<char>& out,
|
||||
std::vector<char>& err);
|
||||
|
||||
static void SetForceUnixPaths(bool v) { s_ForceUnixPaths = v; }
|
||||
static bool GetForceUnixPaths() { return s_ForceUnixPaths; }
|
||||
|
||||
102
Source/cmcmd.cxx
102
Source/cmcmd.cxx
@@ -11,6 +11,8 @@
|
||||
#include <cm3p/uv.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "cm_fileno.hxx"
|
||||
|
||||
#include "cmCommandLineArgument.h"
|
||||
#include "cmConsoleBuf.h"
|
||||
#include "cmCryptoHash.h"
|
||||
@@ -72,7 +74,6 @@
|
||||
|
||||
#include "cmsys/Directory.hxx"
|
||||
#include "cmsys/FStream.hxx"
|
||||
#include "cmsys/Process.h"
|
||||
#include "cmsys/RegularExpression.hxx"
|
||||
#include "cmsys/Terminal.h"
|
||||
|
||||
@@ -295,14 +296,8 @@ int CLCompileAndDependencies(const std::vector<std::string>& args)
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<cmsysProcess, void (*)(cmsysProcess*)> cp(
|
||||
cmsysProcess_New(), cmsysProcess_Delete);
|
||||
std::vector<const char*> argv(command.size() + 1);
|
||||
std::transform(command.begin(), command.end(), argv.begin(),
|
||||
[](std::string const& s) { return s.c_str(); });
|
||||
argv.back() = nullptr;
|
||||
cmsysProcess_SetCommand(cp.get(), argv.data());
|
||||
cmsysProcess_SetWorkingDirectory(cp.get(), currentBinaryDir.c_str());
|
||||
cmUVProcessChainBuilder builder;
|
||||
builder.AddCommand(command).SetWorkingDirectory(currentBinaryDir);
|
||||
|
||||
cmsys::ofstream fout(depFile.c_str());
|
||||
if (!fout) {
|
||||
@@ -313,22 +308,18 @@ int CLCompileAndDependencies(const std::vector<std::string>& args)
|
||||
CLOutputLogger errLogger(std::cerr);
|
||||
|
||||
// Start the process.
|
||||
cmProcessTools::RunProcess(cp.get(), &includeParser, &errLogger);
|
||||
auto result =
|
||||
cmProcessTools::RunProcess(builder, &includeParser, &errLogger);
|
||||
auto const& subStatus = result.front();
|
||||
|
||||
int status = 0;
|
||||
// handle status of process
|
||||
switch (cmsysProcess_GetState(cp.get())) {
|
||||
case cmsysProcess_State_Exited:
|
||||
status = cmsysProcess_GetExitValue(cp.get());
|
||||
break;
|
||||
case cmsysProcess_State_Exception:
|
||||
status = 1;
|
||||
break;
|
||||
case cmsysProcess_State_Error:
|
||||
status = 2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
if (subStatus.SpawnResult != 0) {
|
||||
status = 2;
|
||||
} else if (subStatus.TermSignal != 0) {
|
||||
status = 1;
|
||||
} else {
|
||||
status = static_cast<int>(subStatus.ExitStatus);
|
||||
}
|
||||
|
||||
if (status != 0) {
|
||||
@@ -1116,7 +1107,8 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
|
||||
|
||||
int ret = 0;
|
||||
auto time_start = std::chrono::steady_clock::now();
|
||||
cmSystemTools::RunSingleCommand(command, nullptr, nullptr, &ret);
|
||||
cmSystemTools::RunSingleCommand(command, nullptr, nullptr, &ret, nullptr,
|
||||
cmSystemTools::OUTPUT_PASSTHROUGH);
|
||||
auto time_finish = std::chrono::steady_clock::now();
|
||||
|
||||
std::chrono::duration<double> time_elapsed = time_finish - time_start;
|
||||
@@ -1892,21 +1884,6 @@ int cmcmd::ExecuteLinkScript(std::vector<std::string> const& args)
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate a process instance.
|
||||
cmsysProcess* cp = cmsysProcess_New();
|
||||
if (!cp) {
|
||||
std::cerr << "Error allocating process instance in link script."
|
||||
<< std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Children should share stdout and stderr with this process.
|
||||
cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1);
|
||||
cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1);
|
||||
|
||||
// Run the command lines verbatim.
|
||||
cmsysProcess_SetOption(cp, cmsysProcess_Option_Verbatim, 1);
|
||||
|
||||
// Read command lines from the script.
|
||||
cmsys::ifstream fin(args[2].c_str());
|
||||
if (!fin) {
|
||||
@@ -1924,9 +1901,24 @@ int cmcmd::ExecuteLinkScript(std::vector<std::string> const& args)
|
||||
continue;
|
||||
}
|
||||
|
||||
// Allocate a process instance.
|
||||
cmUVProcessChainBuilder builder;
|
||||
|
||||
// Children should share stdout and stderr with this process.
|
||||
builder
|
||||
.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT,
|
||||
cm_fileno(stdout))
|
||||
.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
|
||||
cm_fileno(stderr));
|
||||
|
||||
// Setup this command line.
|
||||
const char* cmd[2] = { command.c_str(), nullptr };
|
||||
cmsysProcess_SetCommand(cp, cmd);
|
||||
std::vector<std::string> args2;
|
||||
#ifdef _WIN32
|
||||
cmSystemTools::ParseWindowsCommandLine(command.c_str(), args2);
|
||||
#else
|
||||
cmSystemTools::ParseUnixCommandLine(command.c_str(), args2);
|
||||
#endif
|
||||
builder.AddCommand(args2);
|
||||
|
||||
// Report the command if verbose output is enabled.
|
||||
if (verbose) {
|
||||
@@ -1934,35 +1926,29 @@ int cmcmd::ExecuteLinkScript(std::vector<std::string> const& args)
|
||||
}
|
||||
|
||||
// Run the command and wait for it to exit.
|
||||
cmsysProcess_Execute(cp);
|
||||
cmsysProcess_WaitForExit(cp, nullptr);
|
||||
auto chain = builder.Start();
|
||||
chain.Wait();
|
||||
|
||||
// Report failure if any.
|
||||
switch (cmsysProcess_GetState(cp)) {
|
||||
case cmsysProcess_State_Exited: {
|
||||
int value = cmsysProcess_GetExitValue(cp);
|
||||
if (value != 0) {
|
||||
result = value;
|
||||
auto const& status = chain.GetStatus(0);
|
||||
auto exception = status.GetException();
|
||||
switch (exception.first) {
|
||||
case cmUVProcessChain::ExceptionCode::None:
|
||||
if (status.ExitStatus != 0) {
|
||||
result = static_cast<int>(status.ExitStatus);
|
||||
}
|
||||
} break;
|
||||
case cmsysProcess_State_Exception:
|
||||
std::cerr << "Error running link command: "
|
||||
<< cmsysProcess_GetExceptionString(cp) << std::endl;
|
||||
result = 1;
|
||||
break;
|
||||
case cmsysProcess_State_Error:
|
||||
std::cerr << "Error running link command: "
|
||||
<< cmsysProcess_GetErrorString(cp) << std::endl;
|
||||
case cmUVProcessChain::ExceptionCode::Spawn:
|
||||
std::cerr << "Error running link command: " << exception.second;
|
||||
result = 2;
|
||||
break;
|
||||
default:
|
||||
std::cerr << "Error running link command: " << exception.second;
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Free the process instance.
|
||||
cmsysProcess_Delete(cp);
|
||||
|
||||
// Return the final resulting return value.
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -7,10 +7,6 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "cmSystemTools.h"
|
||||
|
||||
static std::string getStdin()
|
||||
|
||||
@@ -466,6 +466,7 @@ add_RunCMake_test(build_command)
|
||||
add_executable(exit_code exit_code.c)
|
||||
set(execute_process_ARGS
|
||||
-DEXIT_CODE_EXE=$<TARGET_FILE:exit_code>
|
||||
-DPRINT_STDIN_EXE=$<TARGET_FILE:print_stdin>
|
||||
-DPython_EXECUTABLE=${Python_EXECUTABLE}
|
||||
)
|
||||
if(NOT CMake_TEST_EXTERNAL_CMAKE)
|
||||
|
||||
@@ -34,6 +34,7 @@ run_cmake_command(AnyCommandGood ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/AnyC
|
||||
run_cmake_command(LastCommandError ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/LastCommandError.cmake)
|
||||
run_cmake_command(LastCommandTimeout ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/LastCommandTimeout.cmake)
|
||||
run_cmake_command(LastCommandGood ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/LastCommandGood.cmake)
|
||||
run_cmake_command(Stdin ${CMAKE_COMMAND} -DPRINT_STDIN_EXE=${PRINT_STDIN_EXE} -P ${RunCMake_SOURCE_DIR}/Stdin.cmake)
|
||||
|
||||
if(UNIX AND Python_EXECUTABLE)
|
||||
run_cmake_command(AnyCommandAbnormalExit ${CMAKE_COMMAND} -DPython_EXECUTABLE=${Python_EXECUTABLE} -P ${RunCMake_SOURCE_DIR}/AnyCommandAbnormalExit.cmake)
|
||||
|
||||
1
Tests/RunCMake/execute_process/Stdin-stdin.txt
Normal file
1
Tests/RunCMake/execute_process/Stdin-stdin.txt
Normal file
@@ -0,0 +1 @@
|
||||
Hello world!
|
||||
1
Tests/RunCMake/execute_process/Stdin-stdout.txt
Normal file
1
Tests/RunCMake/execute_process/Stdin-stdout.txt
Normal file
@@ -0,0 +1 @@
|
||||
^Hello world!$
|
||||
1
Tests/RunCMake/execute_process/Stdin.cmake
Normal file
1
Tests/RunCMake/execute_process/Stdin.cmake
Normal file
@@ -0,0 +1 @@
|
||||
execute_process(COMMAND ${PRINT_STDIN_EXE})
|
||||
@@ -75,6 +75,10 @@
|
||||
{ include: [ "<ostream>", public, "\"cmsys/FStream.hxx\"", public ] },
|
||||
{ include: [ "<fstream>", public, "\"cmsys/FStream.hxx\"", public ] },
|
||||
|
||||
{ symbol: [ "mode_t", private, "\"cm_sys_stat.h\"", public ] },
|
||||
{ symbol: [ "S_IWUSR", private, "\"cm_sys_stat.h\"", public ] },
|
||||
{ symbol: [ "S_IWGRP", private, "\"cm_sys_stat.h\"", public ] },
|
||||
|
||||
{ include: [ "<filesystem>", public, "<cm/filesystem>", public ] },
|
||||
{ include: [ "<optional>", public, "<cm/optional>", public ] },
|
||||
{ include: [ "<shared_mutex>", public, "<cm/shared_mutex>", public ] },
|
||||
|
||||
Reference in New Issue
Block a user