diff options
author | Brad King <brad.king@kitware.com> | 2024-01-25 15:52:01 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2024-01-25 15:53:04 (GMT) |
commit | 151601c2e3419bd65bb276e77588c551487b4744 (patch) | |
tree | 6fef8b57dd7aa10c9f15447b18dc74951de68dad /Source | |
parent | 23747f705602cbc47672c6db6716afbe8b5b013e (diff) | |
parent | bcbb212df704d36736731aa567b291fd97401804 (diff) | |
download | CMake-151601c2e3419bd65bb276e77588c551487b4744.zip CMake-151601c2e3419bd65bb276e77588c551487b4744.tar.gz CMake-151601c2e3419bd65bb276e77588c551487b4744.tar.bz2 |
Merge topic 'revert-replace-cmsysprocess-with-cmuvprocesschain' into release-3.28
bcbb212df7 Revert use of libuv for process execution for 3.28
Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !9176
Diffstat (limited to 'Source')
31 files changed, 1186 insertions, 1221 deletions
diff --git a/Source/CPack/cmCPackSTGZGenerator.cxx b/Source/CPack/cmCPackSTGZGenerator.cxx index 1248d17..6ad3755 100644 --- a/Source/CPack/cmCPackSTGZGenerator.cxx +++ b/Source/CPack/cmCPackSTGZGenerator.cxx @@ -7,8 +7,6 @@ #include <string> #include <vector> -#include <fcntl.h> - #include "cmsys/FStream.hxx" #include "cm_sys_stat.h" diff --git a/Source/CTest/cmCTestBZR.cxx b/Source/CTest/cmCTestBZR.cxx index 36df344..246e811 100644 --- a/Source/CTest/cmCTestBZR.cxx +++ b/Source/CTest/cmCTestBZR.cxx @@ -135,14 +135,14 @@ private: std::string cmCTestBZR::LoadInfo() { // Run "bzr info" to get the repository info from the work tree. - std::string bzr = this->CommandLineTool; - std::vector<std::string> bzr_info = { bzr, "info" }; + const char* bzr = this->CommandLineTool.c_str(); + const char* bzr_info[] = { bzr, "info", nullptr }; 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. - std::vector<std::string> bzr_revno = { bzr, "revno" }; + const char* bzr_revno[] = { bzr, "revno", nullptr }; std::string rev; RevnoParser rout(this, "revno-out> ", rev); OutputLogger rerr(this->Log, "revno-err> "); @@ -372,18 +372,22 @@ bool cmCTestBZR::UpdateImpl() // TODO: if(this->CTest->GetTestModel() == cmCTest::NIGHTLY) // Use "bzr pull" to update the working tree. - std::vector<std::string> bzr_update; - bzr_update.push_back(this->CommandLineTool); + std::vector<char const*> bzr_update; + bzr_update.push_back(this->CommandLineTool.c_str()); bzr_update.push_back("pull"); - cm::append(bzr_update, args); + for (std::string const& arg : args) { + bzr_update.push_back(arg.c_str()); + } + + bzr_update.push_back(this->URL.c_str()); - bzr_update.push_back(this->URL); + bzr_update.push_back(nullptr); // 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, &out, &err); + return this->RunUpdateCommand(bzr_update.data(), &out, &err); } bool cmCTestBZR::LoadRevisions() @@ -404,9 +408,10 @@ bool cmCTestBZR::LoadRevisions() } // Run "bzr log" to get all global revisions of interest. - std::string bzr = this->CommandLineTool; - std::vector<std::string> bzr_log = { bzr, "log", "-v", "-r", - revs, "--xml", this->URL }; + const char* bzr = this->CommandLineTool.c_str(); + const char* bzr_log[] = { + bzr, "log", "-v", "-r", revs.c_str(), "--xml", this->URL.c_str(), nullptr + }; { LogParser out(this, "log-out> "); OutputLogger err(this->Log, "log-err> "); @@ -462,8 +467,8 @@ private: bool cmCTestBZR::LoadModifications() { // Run "bzr status" which reports local modifications. - std::string bzr = this->CommandLineTool; - std::vector<std::string> bzr_status = { bzr, "status", "-SV" }; + const char* bzr = this->CommandLineTool.c_str(); + const char* bzr_status[] = { bzr, "status", "-SV", nullptr }; StatusParser out(this, "status-out> "); OutputLogger err(this->Log, "status-err> "); this->RunChild(bzr_status, &out, &err); diff --git a/Source/CTest/cmCTestBuildAndTestHandler.cxx b/Source/CTest/cmCTestBuildAndTestHandler.cxx index bb6ccc3..5feb953 100644 --- a/Source/CTest/cmCTestBuildAndTestHandler.cxx +++ b/Source/CTest/cmCTestBuildAndTestHandler.cxx @@ -7,6 +7,8 @@ #include <cstring> #include <ratio> +#include "cmsys/Process.h" + #include "cmBuildOptions.h" #include "cmCTest.h" #include "cmCTestTestHandler.h" @@ -306,11 +308,12 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring) return 1; } - std::vector<std::string> testCommand; - testCommand.push_back(fullPath); + std::vector<const char*> testCommand; + testCommand.push_back(fullPath.c_str()); for (std::string const& testCommandArg : this->TestCommandArgs) { - testCommand.push_back(testCommandArg); + testCommand.push_back(testCommandArg.c_str()); } + testCommand.push_back(nullptr); std::string outs; int retval = 0; // run the test from the this->BuildRunDir if set @@ -346,10 +349,10 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring) } } - bool runTestRes = this->CTest->RunTest(testCommand, &outs, &retval, nullptr, - remainingTime, nullptr); + int runTestRes = this->CTest->RunTest(testCommand, &outs, &retval, nullptr, + remainingTime, nullptr); - if (!runTestRes || retval != 0) { + if (runTestRes != cmsysProcess_State_Exited || retval != 0) { out << "Test command failed: " << testCommand[0] << "\n"; retval = 1; } diff --git a/Source/CTest/cmCTestBuildHandler.cxx b/Source/CTest/cmCTestBuildHandler.cxx index 859798e..00ecf42 100644 --- a/Source/CTest/cmCTestBuildHandler.cxx +++ b/Source/CTest/cmCTestBuildHandler.cxx @@ -3,17 +3,15 @@ #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" @@ -26,9 +24,6 @@ #include "cmStringAlgorithms.h" #include "cmStringReplaceHelper.h" #include "cmSystemTools.h" -#include "cmUVHandlePtr.h" -#include "cmUVProcessChain.h" -#include "cmUVStream.h" #include "cmValue.h" #include "cmXMLWriter.h" @@ -425,7 +420,7 @@ int cmCTestBuildHandler::ProcessHandler() cmStringReplaceHelper colorRemover("\x1b\\[[0-9;]*m", "", nullptr); this->ColorRemover = &colorRemover; int retVal = 0; - bool res = true; + int res = cmsysProcess_State_Exited; if (!this->CTest->GetShowOnly()) { res = this->RunMakeCommand(makeCommand, &retVal, buildDirectory.c_str(), 0, ofs); @@ -480,7 +475,7 @@ int cmCTestBuildHandler::ProcessHandler() } this->GenerateXMLFooter(xml, elapsed_build_time); - if (!res || retVal || this->TotalErrors > 0) { + if (res != cmsysProcess_State_Exited || retVal || this->TotalErrors > 0) { cmCTestLog(this->CTest, ERROR_MESSAGE, "Error(s) when building project" << std::endl); } @@ -769,10 +764,10 @@ void cmCTestBuildHandler::LaunchHelper::WriteScrapeMatchers( } } -bool cmCTestBuildHandler::RunMakeCommand(const std::string& command, - int* retVal, const char* dir, - int timeout, std::ostream& ofs, - Encoding encoding) +int 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); @@ -781,9 +776,19 @@ bool 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 (auto const& arg : args) { + for (char const* arg : argv) { + if (!arg) { + break; + } cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " \"" << arg << "\"", this->Quiet); } @@ -795,20 +800,21 @@ bool cmCTestBuildHandler::RunMakeCommand(const std::string& command, static_cast<void>(launchHelper); // Now create process object - cmUVProcessChainBuilder builder; - builder.AddCommand(args) - .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) - .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR); - if (dir) { - builder.SetWorkingDirectory(dir); - } - auto chain = builder.Start(); + 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); // Initialize tick's std::string::size_type tick = 0; - static constexpr std::string::size_type tick_len = 1024; + const 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 " @@ -830,65 +836,39 @@ bool 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 - 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'; - } - } + 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'; + } + } - // 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); + // 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); + } + this->ProcessBuffer(nullptr, 0, tick, tick_len, ofs, &this->BuildProcessingQueue); this->ProcessBuffer(nullptr, 0, tick, tick_len, ofs, @@ -899,93 +879,90 @@ bool cmCTestBuildHandler::RunMakeCommand(const std::string& command, << std::endl, this->Quiet); - 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++; + // 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 (!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 { + } 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) { 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); } - return true; + cmsysProcess_Delete(cp); + return result; } // ###################################################################### diff --git a/Source/CTest/cmCTestBuildHandler.h b/Source/CTest/cmCTestBuildHandler.h index 90945b1..e33294d 100644 --- a/Source/CTest/cmCTestBuildHandler.h +++ b/Source/CTest/cmCTestBuildHandler.h @@ -53,9 +53,9 @@ private: //! Run command specialized for make and configure. Returns process status // and retVal is return value or exception. - bool RunMakeCommand(const std::string& command, int* retVal, const char* dir, - int timeout, std::ostream& ofs, - Encoding encoding = cmProcessOutput::Auto); + int RunMakeCommand(const std::string& command, int* retVal, const char* dir, + int timeout, std::ostream& ofs, + Encoding encoding = cmProcessOutput::Auto); enum { diff --git a/Source/CTest/cmCTestCVS.cxx b/Source/CTest/cmCTestCVS.cxx index ef95b25..95e898c 100644 --- a/Source/CTest/cmCTestCVS.cxx +++ b/Source/CTest/cmCTestCVS.cxx @@ -5,7 +5,6 @@ #include <utility> #include <cm/string_view> -#include <cmext/algorithm> #include "cmsys/FStream.hxx" #include "cmsys/RegularExpression.hxx" @@ -90,15 +89,18 @@ bool cmCTestCVS::UpdateImpl() } // Run "cvs update" to update the work tree. - std::vector<std::string> cvs_update; - cvs_update.push_back(this->CommandLineTool); + std::vector<char const*> cvs_update; + cvs_update.push_back(this->CommandLineTool.c_str()); cvs_update.push_back("-z3"); cvs_update.push_back("update"); - cm::append(cvs_update, args); + for (std::string const& arg : args) { + cvs_update.push_back(arg.c_str()); + } + cvs_update.push_back(nullptr); UpdateParser out(this, "up-out> "); UpdateParser err(this, "up-err> "); - return this->RunUpdateCommand(cvs_update, &out, &err); + return this->RunUpdateCommand(cvs_update.data(), &out, &err); } class cmCTestCVS::LogParser : public cmCTestVC::LineParser @@ -219,8 +221,10 @@ 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. - std::string cvs = this->CommandLineTool; - std::vector<std::string> cvs_log = { cvs, "log", "-N", branchFlag, file }; + const char* cvs = this->CommandLineTool.c_str(); + const char* cvs_log[] = { + cvs, "log", "-N", branchFlag, file.c_str(), nullptr + }; LogParser out(this, "log-out> ", revisions); OutputLogger err(this->Log, "log-err> "); diff --git a/Source/CTest/cmCTestConfigureHandler.cxx b/Source/CTest/cmCTestConfigureHandler.cxx index dd8952f..914930e 100644 --- a/Source/CTest/cmCTestConfigureHandler.cxx +++ b/Source/CTest/cmCTestConfigureHandler.cxx @@ -45,7 +45,7 @@ int cmCTestConfigureHandler::ProcessHandler() auto elapsed_time_start = std::chrono::steady_clock::now(); std::string output; int retVal = 0; - bool res = false; + int res = 0; if (!this->CTest->GetShowOnly()) { cmGeneratedFileStream os; if (!this->StartResultingXML(cmCTest::PartConfigure, "Configure", os)) { diff --git a/Source/CTest/cmCTestCoverageHandler.cxx b/Source/CTest/cmCTestCoverageHandler.cxx index f9f9add..2874be7 100644 --- a/Source/CTest/cmCTestCoverageHandler.cxx +++ b/Source/CTest/cmCTestCoverageHandler.cxx @@ -9,7 +9,6 @@ #include <cstring> #include <iomanip> #include <iterator> -#include <memory> #include <ratio> #include <sstream> #include <type_traits> @@ -19,6 +18,7 @@ #include "cmsys/FStream.hxx" #include "cmsys/Glob.hxx" +#include "cmsys/Process.h" #include "cmsys/RegularExpression.hxx" #include "cmCTest.h" @@ -33,7 +33,6 @@ #include "cmParsePHPCoverage.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" -#include "cmUVProcessChain.h" #include "cmWorkingDirectory.h" #include "cmXMLWriter.h" @@ -41,6 +40,85 @@ 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() @@ -1862,35 +1940,34 @@ 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 - cmUVProcessChainBuilder builder; + cmCTestRunProcess runCoverageSrc; + runCoverageSrc.SetCommand(program.c_str()); + runCoverageSrc.AddArgument(arg); std::string stdoutFile = cmStrCat(cont->BinaryDir, "/Testing/Temporary/", this->GetCTestInstance()->GetCurrentTag(), '-', cmd); std::string stderrFile = stdoutFile; stdoutFile += ".stdout"; stderrFile += ".stderr"; - 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, - stdoutHandle.get()) - .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, - stderrHandle.get()); + 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; + } // since we set the output file names wait for it to end - auto chain = builder.Start(); - chain.Wait(); + runCoverageSrc.WaitForExit(); outputFile = stdoutFile; return 1; } diff --git a/Source/CTest/cmCTestGIT.cxx b/Source/CTest/cmCTestGIT.cxx index ca8659e..5f8cb74 100644 --- a/Source/CTest/cmCTestGIT.cxx +++ b/Source/CTest/cmCTestGIT.cxx @@ -9,9 +9,8 @@ #include <utility> #include <vector> -#include <cmext/algorithm> - #include "cmsys/FStream.hxx" +#include "cmsys/Process.h" #include "cmCTest.h" #include "cmCTestVC.h" @@ -19,7 +18,6 @@ #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, @@ -60,9 +58,9 @@ private: std::string cmCTestGIT::GetWorkingRevision() { // Run plumbing "git rev-list" to get work tree revision. - std::string git = this->CommandLineTool; - std::vector<std::string> git_rev_list = { git, "rev-list", "-n", - "1", "HEAD", "--" }; + const char* git = this->CommandLineTool.c_str(); + const char* git_rev_list[] = { git, "rev-list", "-n", "1", + "HEAD", "--", nullptr }; std::string rev; OneLineParser out(this, "rl-out> ", rev); OutputLogger err(this->Log, "rl-err> "); @@ -94,13 +92,13 @@ std::string cmCTestGIT::FindGitDir() std::string git_dir; // Run "git rev-parse --git-dir" to locate the real .git directory. - std::string git = this->CommandLineTool; - std::vector<std::string> git_rev_parse = { git, "rev-parse", "--git-dir" }; + const char* git = this->CommandLineTool.c_str(); + char const* git_rev_parse[] = { git, "rev-parse", "--git-dir", nullptr }; 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, - std::string{}, cmProcessOutput::UTF8)) { + if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err, nullptr, + cmProcessOutput::UTF8)) { git_dir = git_dir_line; } if (git_dir.empty()) { @@ -119,10 +117,11 @@ std::string cmCTestGIT::FindGitDir() std::string cygpath_exe = cmStrCat(cmSystemTools::GetFilenamePath(git), "/cygpath.exe"); if (cmSystemTools::FileExists(cygpath_exe)) { - std::vector<std::string> cygpath = { cygpath_exe, "-w", git_dir }; + char const* cygpath[] = { cygpath_exe.c_str(), "-w", git_dir.c_str(), + 0 }; OneLineParser cygpath_out(this, "cygpath-out> ", git_dir_line); OutputLogger cygpath_err(this->Log, "cygpath-err> "); - if (this->RunChild(cygpath, &cygpath_out, &cygpath_err, std::string{}, + if (this->RunChild(cygpath, &cygpath_out, &cygpath_err, nullptr, cmProcessOutput::UTF8)) { git_dir = git_dir_line; } @@ -137,12 +136,12 @@ std::string cmCTestGIT::FindTopDir() std::string top_dir = this->SourceDirectory; // Run "git rev-parse --show-cdup" to locate the top of the tree. - std::string git = this->CommandLineTool; - std::vector<std::string> git_rev_parse = { git, "rev-parse", "--show-cdup" }; + const char* git = this->CommandLineTool.c_str(); + char const* git_rev_parse[] = { git, "rev-parse", "--show-cdup", nullptr }; 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, "", + if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err, nullptr, cmProcessOutput::UTF8) && !cdup.empty()) { top_dir += "/"; @@ -154,10 +153,10 @@ std::string cmCTestGIT::FindTopDir() bool cmCTestGIT::UpdateByFetchAndReset() { - std::string git = this->CommandLineTool; + const char* git = this->CommandLineTool.c_str(); // Use "git fetch" to get remote commits. - std::vector<std::string> git_fetch; + std::vector<char const*> git_fetch; git_fetch.push_back(git); git_fetch.push_back("fetch"); @@ -167,12 +166,17 @@ bool cmCTestGIT::UpdateByFetchAndReset() opts = this->CTest->GetCTestConfiguration("GITUpdateOptions"); } std::vector<std::string> args = cmSystemTools::ParseArguments(opts); - cm::append(git_fetch, args); + for (std::string const& arg : args) { + git_fetch.push_back(arg.c_str()); + } + + // Sentinel argument. + git_fetch.push_back(nullptr); // Fetch upstream refs. OutputLogger fetch_out(this->Log, "fetch-out> "); OutputLogger fetch_err(this->Log, "fetch-err> "); - if (!this->RunUpdateCommand(git_fetch, &fetch_out, &fetch_err)) { + if (!this->RunUpdateCommand(git_fetch.data(), &fetch_out, &fetch_err)) { return false; } @@ -203,22 +207,25 @@ bool cmCTestGIT::UpdateByFetchAndReset() } // Reset the local branch to point at that tracked from upstream. - std::vector<std::string> git_reset = { git, "reset", "--hard", sha1 }; + char const* git_reset[] = { git, "reset", "--hard", sha1.c_str(), nullptr }; OutputLogger reset_out(this->Log, "reset-out> "); OutputLogger reset_err(this->Log, "reset-err> "); - return this->RunChild(git_reset, &reset_out, &reset_err); + return this->RunChild(&git_reset[0], &reset_out, &reset_err); } bool cmCTestGIT::UpdateByCustom(std::string const& custom) { cmList git_custom_command{ custom, cmList::EmptyElements::Yes }; - std::vector<std::string> git_custom; - git_custom.reserve(git_custom_command.size()); - cm::append(git_custom, git_custom_command); + 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); OutputLogger custom_out(this->Log, "custom-out> "); OutputLogger custom_err(this->Log, "custom-err> "); - return this->RunUpdateCommand(git_custom, &custom_out, &custom_err); + return this->RunUpdateCommand(git_custom.data(), &custom_out, &custom_err); } bool cmCTestGIT::UpdateInternal() @@ -237,14 +244,13 @@ bool cmCTestGIT::UpdateImpl() } std::string top_dir = this->FindTopDir(); - std::string git = this->CommandLineTool; - std::string recursive = "--recursive"; - std::string sync_recursive = "--recursive"; + const char* git = this->CommandLineTool.c_str(); + const char* recursive = "--recursive"; + const char* sync_recursive = "--recursive"; // Git < 1.6.5 did not support submodule --recursive - bool support_recursive = true; if (this->GetGitVersion() < cmCTestGITVersion(1, 6, 5, 0)) { - support_recursive = false; + recursive = nullptr; // 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"; @@ -252,9 +258,8 @@ 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)) { - support_sync_recursive = false; + sync_recursive = nullptr; // 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"; @@ -269,39 +274,35 @@ bool cmCTestGIT::UpdateImpl() std::string init_submodules = this->CTest->GetCTestConfiguration("GITInitSubmodules"); if (cmIsOn(init_submodules)) { - std::vector<std::string> git_submodule_init = { git, "submodule", "init" }; + char const* git_submodule_init[] = { git, "submodule", "init", nullptr }; ret = this->RunChild(git_submodule_init, &submodule_out, &submodule_err, - top_dir); + top_dir.c_str()); if (!ret) { return false; } } - std::vector<std::string> git_submodule_sync = { git, "submodule", "sync" }; - if (support_sync_recursive) { - git_submodule_sync.push_back(sync_recursive); - } + char const* git_submodule_sync[] = { git, "submodule", "sync", + sync_recursive, nullptr }; ret = this->RunChild(git_submodule_sync, &submodule_out, &submodule_err, - top_dir); + top_dir.c_str()); if (!ret) { return false; } - std::vector<std::string> git_submodule = { git, "submodule", "update" }; - if (support_recursive) { - git_submodule.push_back(recursive); - } + char const* git_submodule[] = { git, "submodule", "update", recursive, + nullptr }; return this->RunChild(git_submodule, &submodule_out, &submodule_err, - top_dir); + top_dir.c_str()); } unsigned int cmCTestGIT::GetGitVersion() { if (!this->CurrentGitVersion) { - std::string git = this->CommandLineTool; - std::vector<std::string> git_version = { git, "--version" }; + const char* git = this->CommandLineTool.c_str(); + char const* git_version[] = { git, "--version", nullptr }; std::string version; OneLineParser version_out(this, "version-out> ", version); OutputLogger version_err(this->Log, "version-err> "); @@ -604,49 +605,50 @@ bool cmCTestGIT::LoadRevisions() { // Use 'git rev-list ... | git diff-tree ...' to get revisions. std::string range = this->OldRevision + ".." + this->NewRevision; - 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" + 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 }; this->Log << cmCTestGIT::ComputeCommandLine(git_rev_list) << " | " << cmCTestGIT::ComputeCommandLine(git_diff_tree) << "\n"; - cmUVProcessChainBuilder builder; - builder.AddCommand(git_rev_list) - .AddCommand(git_diff_tree) - .SetWorkingDirectory(this->SourceDirectory); + cmsysProcess* cp = cmsysProcess_New(); + cmsysProcess_AddCommand(cp, git_rev_list); + cmsysProcess_AddCommand(cp, git_diff_tree); + cmsysProcess_SetWorkingDirectory(cp, this->SourceDirectory.c_str()); CommitParser out(this, "dt-out> "); OutputLogger err(this->Log, "dt-err> "); - cmCTestGIT::RunProcess(builder, &out, &err, cmProcessOutput::UTF8); + cmCTestGIT::RunProcess(cp, &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() { - std::string git = this->CommandLineTool; + const char* git = this->CommandLineTool.c_str(); // Use 'git update-index' to refresh the index w.r.t. the work tree. - std::vector<std::string> git_update_index = { git, "update-index", - "--refresh" }; + const char* git_update_index[] = { git, "update-index", "--refresh", + nullptr }; OutputLogger ui_out(this->Log, "ui-out> "); OutputLogger ui_err(this->Log, "ui-err> "); - this->RunChild(git_update_index, &ui_out, &ui_err, "", + this->RunChild(git_update_index, &ui_out, &ui_err, nullptr, cmProcessOutput::UTF8); // Use 'git diff-index' to get modified files. - std::vector<std::string> git_diff_index = { git, "diff-index", "-z", "HEAD", - "--" }; + const char* git_diff_index[] = { git, "diff-index", "-z", + "HEAD", "--", nullptr }; DiffParser out(this, "di-out> "); OutputLogger err(this->Log, "di-err> "); - this->RunChild(git_diff_index, &out, &err, "", cmProcessOutput::UTF8); + this->RunChild(git_diff_index, &out, &err, nullptr, cmProcessOutput::UTF8); for (Change const& c : out.Changes) { this->DoModification(PathModified, c.Path); diff --git a/Source/CTest/cmCTestHG.cxx b/Source/CTest/cmCTestHG.cxx index e1a945d..02837ba 100644 --- a/Source/CTest/cmCTestHG.cxx +++ b/Source/CTest/cmCTestHG.cxx @@ -95,8 +95,8 @@ private: std::string cmCTestHG::GetWorkingRevision() { // Run plumbing "hg identify" to get work tree revision. - std::string hg = this->CommandLineTool; - std::vector<std::string> hg_identify = { hg, "identify", "-i" }; + const char* hg = this->CommandLineTool.c_str(); + const char* hg_identify[] = { hg, "identify", "-i", nullptr }; 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. { - std::string hg = this->CommandLineTool; - std::vector<std::string> hg_pull = { hg, "pull", "-v" }; + const char* hg = this->CommandLineTool.c_str(); + const char* hg_pull[] = { hg, "pull", "-v", nullptr }; OutputLogger out(this->Log, "pull-out> "); OutputLogger err(this->Log, "pull-err> "); - this->RunChild(hg_pull, &out, &err); + this->RunChild(&hg_pull[0], &out, &err); } // TODO: if(this->CTest->GetTestModel() == cmCTest::NIGHTLY) - std::vector<std::string> hg_update; + std::vector<char const*> hg_update; hg_update.push_back(this->CommandLineTool.c_str()); hg_update.push_back("update"); hg_update.push_back("-v"); @@ -147,11 +147,16 @@ bool cmCTestHG::UpdateImpl() opts = this->CTest->GetCTestConfiguration("HGUpdateOptions"); } std::vector<std::string> args = cmSystemTools::ParseArguments(opts); - cm::append(hg_update, args); + for (std::string const& arg : args) { + hg_update.push_back(arg.c_str()); + } + + // Sentinel argument. + hg_update.push_back(nullptr); OutputLogger out(this->Log, "update-out> "); OutputLogger err(this->Log, "update-err> "); - return this->RunUpdateCommand(hg_update, &out, &err); + return this->RunUpdateCommand(hg_update.data(), &out, &err); } class cmCTestHG::LogParser @@ -272,8 +277,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; - std::string hg = this->CommandLineTool; - std::string hgXMLTemplate = "<logentry\n" + const char* hg = this->CommandLineTool.c_str(); + const char* hgXMLTemplate = "<logentry\n" " revision=\"{node|short}\">\n" " <author>{author|person}</author>\n" " <email>{author|email}</email>\n" @@ -283,8 +288,10 @@ bool cmCTestHG::LoadRevisions() " <file_adds>{file_adds}</file_adds>\n" " <file_dels>{file_dels}</file_dels>\n" "</logentry>\n"; - std::vector<std::string> hg_log = { hg, "log", "--removed", "-r", - range, "--template", hgXMLTemplate }; + const char* hg_log[] = { + hg, "log", "--removed", "-r", range.c_str(), + "--template", hgXMLTemplate, nullptr + }; LogParser out(this, "log-out> "); out.Process("<?xml version=\"1.0\"?>\n" @@ -298,8 +305,8 @@ bool cmCTestHG::LoadRevisions() bool cmCTestHG::LoadModifications() { // Use 'hg status' to get modified files. - std::string hg = this->CommandLineTool; - std::vector<std::string> hg_status = { hg, "status" }; + const char* hg = this->CommandLineTool.c_str(); + const char* hg_status[] = { hg, "status", nullptr }; StatusParser out(this, "status-out> "); OutputLogger err(this->Log, "status-err> "); this->RunChild(hg_status, &out, &err); diff --git a/Source/CTest/cmCTestLaunch.cxx b/Source/CTest/cmCTestLaunch.cxx index 9669d76..4a33869 100644 --- a/Source/CTest/cmCTestLaunch.cxx +++ b/Source/CTest/cmCTestLaunch.cxx @@ -2,15 +2,11 @@ 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 "cmCTestLaunchReporter.h" @@ -21,9 +17,6 @@ #include "cmStateSnapshot.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" -#include "cmUVHandlePtr.h" -#include "cmUVProcessChain.h" -#include "cmUVStream.h" #include "cmake.h" #ifdef _WIN32 @@ -35,6 +28,8 @@ cmCTestLaunch::cmCTestLaunch(int argc, const char* const* argv) { + this->Process = nullptr; + if (!this->ParseArguments(argc, argv)) { return; } @@ -45,9 +40,13 @@ cmCTestLaunch::cmCTestLaunch(int argc, const char* const* argv) this->ScrapeRulesLoaded = false; this->HaveOut = false; this->HaveErr = false; + this->Process = cmsysProcess_New(); } -cmCTestLaunch::~cmCTestLaunch() = default; +cmCTestLaunch::~cmCTestLaunch() +{ + cmsysProcess_Delete(this->Process); +} bool cmCTestLaunch::ParseArguments(int argc, const char* const* argv) { @@ -114,12 +113,15 @@ bool cmCTestLaunch::ParseArguments(int argc, const char* const* argv) // Extract the real command line. if (arg0) { - for (int i = 0; i < argc - arg0; ++i) { - this->RealArgV.emplace_back((argv + arg0)[i]); - this->HandleRealArg((argv + arg0)[i]); + this->RealArgC = argc - arg0; + this->RealArgV = argv + arg0; + for (int i = 0; i < this->RealArgC; ++i) { + this->HandleRealArg(this->RealArgV[i]); } return true; } + this->RealArgC = 0; + this->RealArgV = nullptr; std::cerr << "No launch/command separator ('--') found!\n"; return false; } @@ -149,19 +151,17 @@ void cmCTestLaunch::RunChild() } // Prepare to run the real command. - cmUVProcessChainBuilder builder; - builder.AddCommand(this->RealArgV); + cmsysProcess* cp = this->Process; + cmsysProcess_SetCommand(cp, this->RealArgV); cmsys::ofstream fout; cmsys::ofstream ferr; if (this->Reporter.Passthru) { // In passthru mode we just share the output pipes. - builder.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, stdout) - .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, stderr); + cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1); + cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1); } 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,65 +174,51 @@ void cmCTestLaunch::RunChild() #endif // Run the real command. - auto chain = builder.Start(); + cmsysProcess_Execute(cp); // 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) { - 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); + 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()); + } } // Wait for the real command to finish. - 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); - } + cmsysProcess_WaitForExit(cp, nullptr); + this->Reporter.ExitCode = cmsysProcess_GetExitValue(cp); } int cmCTestLaunch::Run() { + if (!this->Process) { + std::cerr << "Could not allocate cmsysProcess instance!\n"; + return -1; + } + this->RunChild(); if (this->CheckResults()) { @@ -240,6 +226,7 @@ int cmCTestLaunch::Run() } this->LoadConfig(); + this->Reporter.Process = this->Process; this->Reporter.WriteXML(); return this->Reporter.ExitCode; diff --git a/Source/CTest/cmCTestLaunch.h b/Source/CTest/cmCTestLaunch.h index ef21a26..c5a6476 100644 --- a/Source/CTest/cmCTestLaunch.h +++ b/Source/CTest/cmCTestLaunch.h @@ -43,12 +43,15 @@ private: bool ParseArguments(int argc, const char* const* argv); // The real command line appearing after launcher arguments. - std::vector<std::string> RealArgV; + int RealArgC; + const char* const* 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; diff --git a/Source/CTest/cmCTestLaunchReporter.cxx b/Source/CTest/cmCTestLaunchReporter.cxx index 4b4e5c5..149ba5d 100644 --- a/Source/CTest/cmCTestLaunchReporter.cxx +++ b/Source/CTest/cmCTestLaunchReporter.cxx @@ -2,9 +2,8 @@ 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" @@ -23,7 +22,6 @@ cmCTestLaunchReporter::cmCTestLaunchReporter() { this->Passthru = true; - this->Status.Finished = true; this->ExitCode = 1; this->CWD = cmSystemTools::GetCurrentWorkingDirectory(); @@ -233,23 +231,35 @@ void cmCTestLaunchReporter::WriteXMLResult(cmXMLElement& e2) // ExitCondition cmXMLElement e4(e3, "ExitCondition"); - 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"); + 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; } } diff --git a/Source/CTest/cmCTestLaunchReporter.h b/Source/CTest/cmCTestLaunchReporter.h index 2bb78f8..4be0d9b 100644 --- a/Source/CTest/cmCTestLaunchReporter.h +++ b/Source/CTest/cmCTestLaunchReporter.h @@ -10,8 +10,6 @@ #include "cmsys/RegularExpression.hxx" -#include "cmUVProcessChain.h" - class cmXMLElement; /** \class cmCTestLaunchReporter @@ -50,7 +48,7 @@ public: void ComputeFileNames(); bool Passthru; - cmUVProcessChain::Status Status; + struct cmsysProcess_s* Process; int ExitCode; // Temporary log files for stdout and stderr of real command. diff --git a/Source/CTest/cmCTestP4.cxx b/Source/CTest/cmCTestP4.cxx index 5d71b84..0e002b9 100644 --- a/Source/CTest/cmCTestP4.cxx +++ b/Source/CTest/cmCTestP4.cxx @@ -149,16 +149,17 @@ cmCTestP4::User cmCTestP4::GetUserData(const std::string& username) auto it = this->Users.find(username); if (it == this->Users.end()) { - std::vector<std::string> p4_users; + std::vector<char const*> 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); + p4_users.push_back(username.c_str()); + p4_users.push_back(nullptr); UserParser out(this, "users-out> "); OutputLogger err(this->Log, "users-err> "); - this->RunChild(p4_users, &out, &err); + this->RunChild(p4_users.data(), &out, &err); // The user should now be added to the map. Search again. it = this->Users.find(username); @@ -302,10 +303,10 @@ private: } }; -void cmCTestP4::SetP4Options(std::vector<std::string>& CommandOptions) +void cmCTestP4::SetP4Options(std::vector<char const*>& CommandOptions) { if (this->P4Options.empty()) { - std::string p4 = this->CommandLineTool; + const char* p4 = this->CommandLineTool.c_str(); this->P4Options.emplace_back(p4); // The CTEST_P4_CLIENT variable sets the P4 client used when issuing @@ -327,12 +328,15 @@ void cmCTestP4::SetP4Options(std::vector<std::string>& CommandOptions) cm::append(this->P4Options, cmSystemTools::ParseArguments(opts)); } - CommandOptions = this->P4Options; + CommandOptions.clear(); + for (std::string const& o : this->P4Options) { + CommandOptions.push_back(o.c_str()); + } } std::string cmCTestP4::GetWorkingRevision() { - std::vector<std::string> p4_identify; + std::vector<char const*> p4_identify; this->SetP4Options(p4_identify); p4_identify.push_back("changes"); @@ -341,13 +345,14 @@ std::string cmCTestP4::GetWorkingRevision() p4_identify.push_back("-t"); std::string source = this->SourceDirectory + "/...#have"; - p4_identify.push_back(source); + p4_identify.push_back(source.c_str()); + p4_identify.push_back(nullptr); std::string rev; IdentifyParser out(this, "p4_changes-out> ", rev); OutputLogger err(this->Log, "p4_changes-err> "); - bool result = this->RunChild(p4_identify, &out, &err); + bool result = this->RunChild(p4_identify.data(), &out, &err); // If there was a problem contacting the server return "<unknown>" if (!result) { @@ -383,7 +388,7 @@ bool cmCTestP4::NoteNewRevision() bool cmCTestP4::LoadRevisions() { - std::vector<std::string> p4_changes; + std::vector<char const*> p4_changes; this->SetP4Options(p4_changes); // Use 'p4 changes ...@old,new' to get a list of changelists @@ -404,36 +409,38 @@ bool cmCTestP4::LoadRevisions() .append(this->NewRevision); p4_changes.push_back("changes"); - p4_changes.push_back(range); + p4_changes.push_back(range.c_str()); + p4_changes.push_back(nullptr); ChangesParser out(this, "p4_changes-out> "); OutputLogger err(this->Log, "p4_changes-err> "); this->ChangeLists.clear(); - this->RunChild(p4_changes, &out, &err); + this->RunChild(p4_changes.data(), &out, &err); if (this->ChangeLists.empty()) { return true; } // p4 describe -s ...@1111111,2222222 - std::vector<std::string> p4_describe; + std::vector<char const*> 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); + p4_describe.push_back(i.c_str()); + p4_describe.push_back(nullptr); DescribeParser outDescribe(this, "p4_describe-out> "); OutputLogger errDescribe(this->Log, "p4_describe-err> "); - this->RunChild(p4_describe, &outDescribe, &errDescribe); + this->RunChild(p4_describe.data(), &outDescribe, &errDescribe); } return true; } bool cmCTestP4::LoadModifications() { - std::vector<std::string> p4_diff; + std::vector<char const*> p4_diff; this->SetP4Options(p4_diff); p4_diff.push_back("diff"); @@ -441,11 +448,12 @@ 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); + p4_diff.push_back(source.c_str()); + p4_diff.push_back(nullptr); DiffParser out(this, "p4_diff-out> "); OutputLogger err(this->Log, "p4_diff-err> "); - this->RunChild(p4_diff, &out, &err); + this->RunChild(p4_diff.data(), &out, &err); return true; } @@ -453,14 +461,17 @@ bool cmCTestP4::UpdateCustom(const std::string& custom) { cmList p4_custom_command{ custom, cmList::EmptyElements::Yes }; - std::vector<std::string> p4_custom; - p4_custom.reserve(p4_custom_command.size()); - cm::append(p4_custom, p4_custom_command); + 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); OutputLogger custom_out(this->Log, "p4_customsync-out> "); OutputLogger custom_err(this->Log, "p4_customsync-err> "); - return this->RunUpdateCommand(p4_custom, &custom_out, &custom_err); + return this->RunUpdateCommand(p4_custom.data(), &custom_out, &custom_err); } bool cmCTestP4::UpdateImpl() @@ -477,7 +488,7 @@ bool cmCTestP4::UpdateImpl() return false; } - std::vector<std::string> p4_sync; + std::vector<char const*> p4_sync; this->SetP4Options(p4_sync); p4_sync.push_back("sync"); @@ -488,7 +499,9 @@ bool cmCTestP4::UpdateImpl() opts = this->CTest->GetCTestConfiguration("P4UpdateOptions"); } std::vector<std::string> args = cmSystemTools::ParseArguments(opts); - cm::append(p4_sync, args); + for (std::string const& arg : args) { + p4_sync.push_back(arg.c_str()); + } std::string source = this->SourceDirectory + "/..."; @@ -502,10 +515,11 @@ bool cmCTestP4::UpdateImpl() source.append("@\"").append(date).append("\""); } - p4_sync.push_back(source); + p4_sync.push_back(source.c_str()); + p4_sync.push_back(nullptr); OutputLogger out(this->Log, "p4_sync-out> "); OutputLogger err(this->Log, "p4_sync-err> "); - return this->RunUpdateCommand(p4_sync, &out, &err); + return this->RunUpdateCommand(p4_sync.data(), &out, &err); } diff --git a/Source/CTest/cmCTestP4.h b/Source/CTest/cmCTestP4.h index 827caa1..1889520 100644 --- a/Source/CTest/cmCTestP4.h +++ b/Source/CTest/cmCTestP4.h @@ -39,7 +39,7 @@ private: std::vector<std::string> P4Options; User GetUserData(const std::string& username); - void SetP4Options(std::vector<std::string>& options); + void SetP4Options(std::vector<char const*>& options); std::string GetWorkingRevision(); bool NoteOldRevision() override; diff --git a/Source/CTest/cmCTestSVN.cxx b/Source/CTest/cmCTestSVN.cxx index 14bc510..91a1177 100644 --- a/Source/CTest/cmCTestSVN.cxx +++ b/Source/CTest/cmCTestSVN.cxx @@ -33,7 +33,7 @@ cmCTestSVN::~cmCTestSVN() = default; void cmCTestSVN::CleanupImpl() { - std::vector<std::string> svn_cleanup; + std::vector<const char*> 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<std::string> svn_info; + std::vector<const char*> svn_info; svn_info.push_back("info"); - svn_info.push_back(svninfo.LocalPath); + svn_info.push_back(svninfo.LocalPath.c_str()); std::string rev; InfoParser out(this, "info-out> ", rev, svninfo); OutputLogger err(this->Log, "info-err> "); @@ -251,24 +251,26 @@ bool cmCTestSVN::UpdateImpl() args.push_back("-r{" + this->GetNightlyTime() + " +0000}"); } - std::vector<std::string> svn_update; + std::vector<char const*> svn_update; svn_update.push_back("update"); - cm::append(svn_update, args); + for (std::string const& arg : args) { + svn_update.push_back(arg.c_str()); + } UpdateParser out(this, "up-out> "); OutputLogger err(this->Log, "up-err> "); return this->RunSVNCommand(svn_update, &out, &err); } -bool cmCTestSVN::RunSVNCommand(std::vector<std::string> const& parameters, +bool cmCTestSVN::RunSVNCommand(std::vector<char const*> const& parameters, OutputParser* out, OutputParser* err) { if (parameters.empty()) { return false; } - std::vector<std::string> args; - args.push_back(this->CommandLineTool); + std::vector<char const*> args; + args.push_back(this->CommandLineTool.c_str()); cm::append(args, parameters); args.push_back("--non-interactive"); @@ -276,12 +278,16 @@ bool cmCTestSVN::RunSVNCommand(std::vector<std::string> const& parameters, std::vector<std::string> parsedUserOptions = cmSystemTools::ParseArguments(userOptions); - cm::append(args, parsedUserOptions); + for (std::string const& opt : parsedUserOptions) { + args.push_back(opt.c_str()); + } + + args.push_back(nullptr); - if (parameters[0] == "update") { - return this->RunUpdateCommand(args, out, err); + if (strcmp(parameters[0], "update") == 0) { + return this->RunUpdateCommand(args.data(), out, err); } - return this->RunChild(args, out, err); + return this->RunChild(args.data(), out, err); } class cmCTestSVN::LogParser @@ -387,7 +393,7 @@ bool cmCTestSVN::LoadRevisions(SVNInfo& svninfo) } // Run "svn log" to get all global revisions of interest. - std::vector<std::string> svn_log; + std::vector<const char*> svn_log; svn_log.push_back("log"); svn_log.push_back("--xml"); svn_log.push_back("-v"); @@ -466,7 +472,7 @@ private: bool cmCTestSVN::LoadModifications() { // Run "svn status" which reports local modifications. - std::vector<std::string> svn_status; + std::vector<const char*> svn_status; svn_status.push_back("status"); StatusParser out(this, "status-out> "); OutputLogger err(this->Log, "status-err> "); @@ -528,7 +534,7 @@ bool cmCTestSVN::LoadRepositories() this->RootInfo = &(this->Repositories.back()); // Run "svn status" to get the list of external repositories - std::vector<std::string> svn_status; + std::vector<const char*> svn_status; svn_status.push_back("status"); ExternalParser out(this, "external-out> "); OutputLogger err(this->Log, "external-err> "); diff --git a/Source/CTest/cmCTestSVN.h b/Source/CTest/cmCTestSVN.h index 1485dc0..370d176 100644 --- a/Source/CTest/cmCTestSVN.h +++ b/Source/CTest/cmCTestSVN.h @@ -33,7 +33,7 @@ private: bool NoteNewRevision() override; bool UpdateImpl() override; - bool RunSVNCommand(std::vector<std::string> const& parameters, + bool RunSVNCommand(std::vector<char const*> const& parameters, OutputParser* out, OutputParser* err); // Information about an SVN repository (root repository or external) diff --git a/Source/CTest/cmCTestScriptHandler.cxx b/Source/CTest/cmCTestScriptHandler.cxx index 48f8f6d..461ad1a 100644 --- a/Source/CTest/cmCTestScriptHandler.cxx +++ b/Source/CTest/cmCTestScriptHandler.cxx @@ -11,9 +11,8 @@ #include <cm/memory> -#include <cm3p/uv.h> - #include "cmsys/Directory.hxx" +#include "cmsys/Process.h" #include "cmCTest.h" #include "cmCTestBuildCommand.h" @@ -41,8 +40,6 @@ #include "cmStateSnapshot.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" -#include "cmUVHandlePtr.h" -#include "cmUVProcessChain.h" #include "cmValue.h" #include "cmake.h" @@ -151,65 +148,66 @@ 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 - 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()); + 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); std::vector<char> out; std::vector<char> err; std::string line; - auto pipe = - cmSystemTools::WaitForLine(&process.GetLoop(), outPipe, errPipe, line, - std::chrono::seconds(100), out, err); - while (pipe != cmSystemTools::WaitForLineResult::None) { + int pipe = + cmSystemTools::WaitForLine(cp, line, std::chrono::seconds(100), out, err); + while (pipe != cmsysProcess_Pipe_None) { cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Output: " << line << "\n"); - if (pipe == cmSystemTools::WaitForLineResult::STDERR) { + if (pipe == cmsysProcess_Pipe_STDERR) { cmCTestLog(this->CTest, ERROR_MESSAGE, line << "\n"); - } else if (pipe == cmSystemTools::WaitForLineResult::STDOUT) { + } else if (pipe == cmsysProcess_Pipe_STDOUT) { cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, line << "\n"); } - pipe = - cmSystemTools::WaitForLine(&process.GetLoop(), outPipe, errPipe, line, - std::chrono::seconds(100), out, err); + pipe = cmSystemTools::WaitForLine(cp, line, std::chrono::seconds(100), out, + err); } // Properly handle output of the build command - process.Wait(); - auto const& status = process.GetStatus(0); - auto result = status.GetException(); + cmsysProcess_WaitForExit(cp, nullptr); + int result = cmsysProcess_GetState(cp); int retVal = 0; bool failed = false; - 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; + 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; } + cmsysProcess_Delete(cp); if (failed) { std::ostringstream message; message << "Error running command: ["; - message << static_cast<int>(result.first) << "] "; + message << result << "] "; for (const char* arg : argv) { if (arg) { message << arg << " "; diff --git a/Source/CTest/cmCTestVC.cxx b/Source/CTest/cmCTestVC.cxx index cbbb5a5..609ccba 100644 --- a/Source/CTest/cmCTestVC.cxx +++ b/Source/CTest/cmCTestVC.cxx @@ -7,9 +7,10 @@ #include <sstream> #include <vector> +#include "cmsys/Process.h" + #include "cmCTest.h" #include "cmSystemTools.h" -#include "cmUVProcessChain.h" #include "cmValue.h" #include "cmXMLWriter.h" @@ -54,12 +55,18 @@ 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(args, &out, &err, parent); + bool result = this->RunChild(vc_co.data(), &out, &err, parent.c_str()); this->Log << "--- End Initial Checkout ---\n"; if (!result) { cmCTestLog(this->CTest, ERROR_MESSAGE, @@ -68,35 +75,35 @@ bool cmCTestVC::InitialCheckout(const std::string& command) return result; } -bool cmCTestVC::RunChild(const std::vector<std::string>& cmd, - OutputParser* out, OutputParser* err, - std::string workDir, Encoding encoding) +bool cmCTestVC::RunChild(char const* const* cmd, OutputParser* out, + OutputParser* err, const char* workDir, + Encoding encoding) { this->Log << cmCTestVC::ComputeCommandLine(cmd) << "\n"; - 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; + cmsysProcess* cp = cmsysProcess_New(); + cmsysProcess_SetCommand(cp, cmd); + workDir = workDir ? workDir : this->SourceDirectory.c_str(); + cmsysProcess_SetWorkingDirectory(cp, workDir); + cmCTestVC::RunProcess(cp, out, err, encoding); + int result = cmsysProcess_GetExitValue(cp); + cmsysProcess_Delete(cp); + return result == 0; } -std::string cmCTestVC::ComputeCommandLine(const std::vector<std::string>& cmd) +std::string cmCTestVC::ComputeCommandLine(char const* const* cmd) { std::ostringstream line; const char* sep = ""; - for (auto const& arg : cmd) { - line << sep << "\"" << arg << "\""; + for (const char* const* arg = cmd; *arg; ++arg) { + line << sep << "\"" << *arg << "\""; sep = " "; } return line.str(); } -bool cmCTestVC::RunUpdateCommand(const std::vector<std::string>& cmd, - OutputParser* out, OutputParser* err, - Encoding encoding) +bool cmCTestVC::RunUpdateCommand(char const* const* cmd, OutputParser* out, + OutputParser* err, Encoding encoding) { // Report the command line. this->UpdateCommandLine = this->ComputeCommandLine(cmd); @@ -106,7 +113,7 @@ bool cmCTestVC::RunUpdateCommand(const std::vector<std::string>& cmd, } // Run the command. - return this->RunChild(cmd, out, err, "", encoding); + return this->RunChild(cmd, out, err, nullptr, encoding); } std::string cmCTestVC::GetNightlyTime() diff --git a/Source/CTest/cmCTestVC.h b/Source/CTest/cmCTestVC.h index dd5456d..7b03d10 100644 --- a/Source/CTest/cmCTestVC.h +++ b/Source/CTest/cmCTestVC.h @@ -6,7 +6,6 @@ #include <iosfwd> #include <string> -#include <vector> #include "cmProcessOutput.h" #include "cmProcessTools.h" @@ -109,15 +108,15 @@ protected: }; /** Convert a list of arguments to a human-readable command line. */ - static std::string ComputeCommandLine(const std::vector<std::string>& cmd); + static std::string ComputeCommandLine(char const* const* cmd); /** Run a command line and send output to given parsers. */ - bool RunChild(const std::vector<std::string>& cmd, OutputParser* out, - OutputParser* err, std::string workDir = {}, + bool RunChild(char const* const* cmd, OutputParser* out, OutputParser* err, + const char* workDir = nullptr, Encoding encoding = cmProcessOutput::Auto); /** Run VC update command line and send output to given parsers. */ - bool RunUpdateCommand(const std::vector<std::string>& cmd, OutputParser* out, + bool RunUpdateCommand(char const* const* cmd, OutputParser* out, OutputParser* err = nullptr, Encoding encoding = cmProcessOutput::Auto); diff --git a/Source/LexerParser/cmCTestResourceGroupsLexer.cxx b/Source/LexerParser/cmCTestResourceGroupsLexer.cxx index f1d351a..85b379b 100644 --- a/Source/LexerParser/cmCTestResourceGroupsLexer.cxx +++ b/Source/LexerParser/cmCTestResourceGroupsLexer.cxx @@ -667,10 +667,6 @@ Modify cmCTestResourceGroupsLexer.cxx: #include <cstddef> -#ifndef _WIN32 -# include <termios.h> -#endif - /*--------------------------------------------------------------------------*/ #define INITIAL 0 diff --git a/Source/LexerParser/cmCTestResourceGroupsLexer.in.l b/Source/LexerParser/cmCTestResourceGroupsLexer.in.l index ac9cbaf..2befa85 100644 --- a/Source/LexerParser/cmCTestResourceGroupsLexer.in.l +++ b/Source/LexerParser/cmCTestResourceGroupsLexer.in.l @@ -26,10 +26,6 @@ Modify cmCTestResourceGroupsLexer.cxx: #include <cstddef> -#ifndef _WIN32 -# include <termios.h> -#endif - /*--------------------------------------------------------------------------*/ %} diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx index 6e684a3..988a4eb 100644 --- a/Source/cmCTest.cxx +++ b/Source/cmCTest.cxx @@ -5,7 +5,6 @@ #include <algorithm> #include <cctype> #include <chrono> -#include <cstdint> #include <cstdio> #include <cstdlib> #include <cstring> @@ -25,13 +24,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) @@ -65,9 +64,6 @@ #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" @@ -1077,9 +1073,9 @@ int cmCTest::GetTestModelFromString(const std::string& str) // ###################################################################### // ###################################################################### -bool cmCTest::RunMakeCommand(const std::string& command, std::string& output, - int* retVal, const char* dir, cmDuration timeout, - std::ostream& ofs, Encoding encoding) +int 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); @@ -1088,107 +1084,107 @@ bool 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 (auto const& arg : args) { + for (char const* arg : argv) { + if (!arg) { + break; + } cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, " \"" << arg << "\""); } cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, std::endl); // Now create process object - 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()); + 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); // 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); - 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'; - } + while (cmsysProcess_WaitForData(cp, &data, &length, nullptr)) { + processOutput.DecodeText(data, length, 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); - } - } - 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()); - } + } + 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); } - }); - - bool finished = chain.Wait(static_cast<uint64_t>(timeout.count() * 1000.0)); + } + 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_PROGRESS_OUTPUT, " Size of output: " << int(double(output.size()) / 1024.0) << "K" << std::endl); - 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 { + 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) { 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); } - return true; + cmsysProcess_Delete(cp); + + return result; } // ###################################################################### @@ -1196,10 +1192,9 @@ bool cmCTest::RunMakeCommand(const std::string& command, std::string& output, // ###################################################################### // ###################################################################### -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) +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 modifyEnv = (environment && !environment->empty()); @@ -1238,16 +1233,19 @@ bool cmCTest::RunTest(const std::vector<std::string>& argv, inst.SetStreams(&oss, &oss); std::vector<std::string> args; - 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))); + 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); } - args.emplace_back(i); } if (log) { *log << "* Run internal CTest" << std::endl; @@ -1273,7 +1271,7 @@ bool cmCTest::RunTest(const std::vector<std::string>& argv, << std::endl); } - return true; + return cmsysProcess_State_Exited; } std::vector<char> tempOutput; if (output) { @@ -1286,43 +1284,41 @@ bool cmCTest::RunTest(const std::vector<std::string>& argv, cmSystemTools::AppendEnv(*environment); } - cmUVProcessChainBuilder builder; - builder.AddCommand(argv).SetMergedBuiltinStreams(); + cmsysProcess* cp = cmsysProcess_New(); + cmsysProcess_SetCommand(cp, argv.data()); cmCTestLog(this, DEBUG, "Command is: " << argv[0] << std::endl); - auto chain = builder.Start(); + if (cmSystemTools::GetRunCommandHideConsole()) { + cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); + } + cmsysProcess_SetTimeout(cp, timeout.count()); + cmsysProcess_Execute(cp); + + char* data; + int length; cmProcessOutput processOutput(encoding); - 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()); - } - } - }); + 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()); + } + } - bool complete = chain.Wait(static_cast<uint64_t>(timeout.count() * 1000.0)); + cmsysProcess_WaitForExit(cp, nullptr); processOutput.DecodeText(tempOutput, tempOutput); if (output && tempOutput.begin() != tempOutput.end()) { output->append(tempOutput.data(), tempOutput.size()); @@ -1330,41 +1326,33 @@ bool cmCTest::RunTest(const std::vector<std::string>& argv, cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, "-- Process completed" << std::endl); - bool result = false; + int result = cmsysProcess_GetState(cp); - 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; + if (result == cmsysProcess_State_Exited) { + *retVal = cmsysProcess_GetExitValue(cp); + if (*retVal != 0 && this->Impl->OutputTestOutputOnTestFailure) { + this->OutputTestErrors(tempOutput); + } + } 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; } @@ -3482,70 +3470,49 @@ bool cmCTest::RunCommand(std::vector<std::string> const& args, stdOut->clear(); stdErr->clear(); - 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* cp = cmsysProcess_New(); + cmsysProcess_SetCommand(cp, argv.data()); + cmsysProcess_SetWorkingDirectory(cp, dir); + if (cmSystemTools::GetRunCommandHideConsole()) { + cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); } + cmsysProcess_SetTimeout(cp, timeout.count()); + cmsysProcess_Execute(cp); std::vector<char> tempOutput; - bool outFinished = false; - cm::uv_pipe_ptr outStream; std::vector<char> tempError; - bool errFinished = false; - cm::uv_pipe_ptr errStream; + char* data; + int length; cmProcessOutput processOutput(encoding); - 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); + 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); + } } if (this->Impl->ExtraVerbose) { - std::string strdata; processOutput.DecodeText(std::string(), strdata); if (!strdata.empty()) { cmSystemTools::Stdout(strdata); } } - while (!timedOut && !chain.Finished()) { - uv_run(&chain.GetLoop(), UV_RUN_ONCE); - } + cmsysProcess_WaitForExit(cp, nullptr); if (!tempOutput.empty()) { processOutput.DecodeText(tempOutput, tempOutput); stdOut->append(tempOutput.data(), tempOutput.size()); @@ -3556,32 +3523,32 @@ bool cmCTest::RunCommand(std::vector<std::string> const& args, } bool result = true; - if (timedOut) { + 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) { 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; } diff --git a/Source/cmCTest.h b/Source/cmCTest.h index 1644d84..9a8d5a6 100644 --- a/Source/cmCTest.h +++ b/Source/cmCTest.h @@ -254,10 +254,10 @@ public: * Run command specialized for make and configure. Returns process status * and retVal is return value or exception. */ - bool RunMakeCommand(const std::string& command, std::string& output, - int* retVal, const char* dir, cmDuration timeout, - std::ostream& ofs, - Encoding encoding = cmProcessOutput::Auto); + int 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. */ - 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); + 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); /** * Get the handler object diff --git a/Source/cmExecuteProcessCommand.cxx b/Source/cmExecuteProcessCommand.cxx index 26b9424..3efd3bd 100644 --- a/Source/cmExecuteProcessCommand.cxx +++ b/Source/cmExecuteProcessCommand.cxx @@ -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,7 @@ #include <cmext/algorithm> #include <cmext/string_view> -#include <cm3p/uv.h> +#include "cmsys/Process.h" #include "cmArgumentParser.h" #include "cmExecutionStatus.h" @@ -26,9 +26,6 @@ #include "cmProcessOutput.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" -#include "cmUVHandlePtr.h" -#include "cmUVProcessChain.h" -#include "cmUVStream.h" namespace { bool cmExecuteProcessCommandIsWhitespace(char c) @@ -39,7 +36,7 @@ bool cmExecuteProcessCommandIsWhitespace(char c) void cmExecuteProcessCommandFixText(std::vector<char>& output, bool strip_trailing_whitespace); void cmExecuteProcessCommandAppend(std::vector<char>& output, const char* data, - std::size_t length); + int length); } // cmExecuteProcessCommand @@ -164,68 +161,57 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args, } } // Create a process instance. - cmUVProcessChainBuilder builder; + std::unique_ptr<cmsysProcess, void (*)(cmsysProcess*)> cp_ptr( + cmsysProcess_New(), cmsysProcess_Delete); + cmsysProcess* cp = cp_ptr.get(); // Set the command sequence. for (std::vector<std::string> const& cmd : arguments.Commands) { - builder.AddCommand(cmd); + 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()); } // Set the process working directory. if (!arguments.WorkingDirectory.empty()) { - builder.SetWorkingDirectory(arguments.WorkingDirectory); + cmsysProcess_SetWorkingDirectory(cp, arguments.WorkingDirectory.c_str()); } - // Check the output variables. - std::unique_ptr<FILE, int (*)(FILE*)> inputFile(nullptr, fclose); - if (!inputFilename.empty()) { - inputFile.reset(cmsys::SystemTools::Fopen(inputFilename, "rb")); - if (inputFile) { - builder.SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT, - inputFile.get()); - } - } else { - builder.SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT, stdin); - } + // Always hide the process window. + cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); - std::unique_ptr<FILE, int (*)(FILE*)> outputFile(nullptr, fclose); - if (!outputFilename.empty()) { - outputFile.reset(cmsys::SystemTools::Fopen(outputFilename, "wb")); - if (outputFile) { - builder.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, - outputFile.get()); - } - } else { - if (arguments.OutputVariable == arguments.ErrorVariable && - !arguments.ErrorVariable.empty()) { - builder.SetMergedBuiltinStreams(); + // Check the output variables. + bool merge_output = false; + if (!arguments.InputFile.empty()) { + cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDIN, + arguments.InputFile.c_str()); + } + 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; } else { - builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT); + cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDERR, + arguments.ErrorFile.c_str()); } } - - std::unique_ptr<FILE, int (*)(FILE*)> errorFile(nullptr, fclose); - if (!errorFilename.empty()) { - if (errorFilename == outputFilename) { - if (outputFile) { - builder.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, - outputFile.get()); - } - } else { - errorFile.reset(cmsys::SystemTools::Fopen(errorFilename, "wb")); - if (errorFile) { - builder.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, - errorFile.get()); - } - } - } else if (arguments.ErrorVariable.empty() || - (!arguments.ErrorVariable.empty() && - arguments.OutputVariable != arguments.ErrorVariable)) { - builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR); + if (!arguments.OutputVariable.empty() && + arguments.OutputVariable == arguments.ErrorVariable) { + merge_output = true; + } + if (merge_output) { + cmsysProcess_SetOption(cp, cmsysProcess_Option_MergeOutput, 1); } // Set the timeout if any. - int64_t timeoutMillis = static_cast<int64_t>(timeout * 1000.0); + if (timeout >= 0) { + cmsysProcess_SetTimeout(cp, timeout); + } bool echo_stdout = false; bool echo_stderr = false; @@ -273,86 +259,36 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args, } } // Start the process. - 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); - } + cmsysProcess_Execute(cp); // Read the process output. - struct ReadData - { - bool Finished = false; - std::vector<char> Output; - cm::uv_pipe_ptr Stream; - }; - ReadData outputData; - ReadData errorData; + std::vector<char> tempOutput; + std::vector<char> tempError; + int length; + char* data; + int p; cmProcessOutput processOutput( cmProcessOutput::FindEncoding(arguments.Encoding)); std::string strdata; - - 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); + 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); + } + } } if (!arguments.OutputQuiet && (arguments.OutputVariable.empty() || arguments.EchoOutputVariable)) { @@ -369,102 +305,151 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args, } } - // All output has been read. - processOutput.DecodeText(outputData.Output, outputData.Output); - processOutput.DecodeText(errorData.Output, errorData.Output); + // All output has been read. Wait for the process to exit. + cmsysProcess_WaitForExit(cp, nullptr); + processOutput.DecodeText(tempOutput, tempOutput); + processOutput.DecodeText(tempError, tempError); // Fix the text in the output strings. - cmExecuteProcessCommandFixText(outputData.Output, + cmExecuteProcessCommandFixText(tempOutput, arguments.OutputStripTrailingWhitespace); - cmExecuteProcessCommandFixText(errorData.Output, + cmExecuteProcessCommandFixText(tempError, arguments.ErrorStripTrailingWhitespace); // Store the output obtained. - if (!arguments.OutputVariable.empty() && !outputData.Output.empty()) { + if (!arguments.OutputVariable.empty() && !tempOutput.empty()) { status.GetMakefile().AddDefinition(arguments.OutputVariable, - outputData.Output.data()); + tempOutput.data()); } - if (arguments.ErrorVariable != arguments.OutputVariable && - !arguments.ErrorVariable.empty() && !errorData.Output.empty()) { + if (!merge_output && !arguments.ErrorVariable.empty() && + !tempError.empty()) { status.GetMakefile().AddDefinition(arguments.ErrorVariable, - errorData.Output.data()); + tempError.data()); } // Store the result of running the process. if (!arguments.ResultVariable.empty()) { - 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) { + 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: status.GetMakefile().AddDefinition( - arguments.ResultVariable, - std::to_string(static_cast<int>(lastStatus->ExitStatus))); - } else { + arguments.ResultVariable, cmsysProcess_GetExceptionString(cp)); + break; + case cmsysProcess_State_Error: status.GetMakefile().AddDefinition(arguments.ResultVariable, - exception.second); - } + cmsysProcess_GetErrorString(cp)); + break; + case cmsysProcess_State_Expired: + status.GetMakefile().AddDefinition( + arguments.ResultVariable, "Process terminated due to timeout"); + break; } } // Store the result of running the processes. if (!arguments.ResultsVariable.empty()) { - 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); + 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; + } } - } - status.GetMakefile().AddDefinition(arguments.ResultsVariable, - cmList::to_string(res)); + 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; } } - 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); + 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; } - return ""; + case kwsysProcess_StateByIndex_Error: + default: + processStatus = "Error getting the child return code"; + break; } - return cmStrCat("Abnormal exit with child return code: ", - exception.second); + return processStatus; }; if (arguments.CommandErrorIsFatal == "ANY"_s) { bool ret = true; - 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; - } - } - if (!failureIndices.empty()) { - std::ostringstream oss; - oss << "failed command indexes:\n"; - for (auto const& e : failureIndices) { - oss << " " << e.first + 1 << ": \"" << e.second << "\"\n"; + 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; + } } - status.SetError(oss.str()); + } 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) { @@ -475,23 +460,29 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args, if (arguments.CommandErrorIsFatal == "LAST"_s) { bool ret = true; - 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 { + switch (cmsysProcess_GetState(cp)) { + case cmsysProcess_State_Exited: { 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(); @@ -534,7 +525,7 @@ void cmExecuteProcessCommandFixText(std::vector<char>& output, } void cmExecuteProcessCommandAppend(std::vector<char>& output, const char* data, - std::size_t length) + int length) { #if defined(__APPLE__) // HACK on Apple to work around bug with inserting at the diff --git a/Source/cmProcessTools.cxx b/Source/cmProcessTools.cxx index 1dd1dce..9e7854b 100644 --- a/Source/cmProcessTools.cxx +++ b/Source/cmProcessTools.cxx @@ -2,68 +2,48 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmProcessTools.h" -#include <algorithm> -#include <iterator> #include <ostream> -#include <cm3p/uv.h> +#include "cmsys/Process.h" #include "cmProcessOutput.h" -#include "cmUVHandlePtr.h" -#include "cmUVStream.h" -std::vector<cmUVProcessChain::Status> cmProcessTools::RunProcess( - cmUVProcessChainBuilder& builder, OutputParser* out, OutputParser* err, - Encoding encoding) +void cmProcessTools::RunProcess(struct cmsysProcess_s* cp, 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; - 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; - } + 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; } - }, - [&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; - } + } 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; } - }, - [&err]() { err = nullptr; }); - while (out || err || !chain.Finished()) { - uv_run(&chain.GetLoop(), UV_RUN_ONCE); + } } - - 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; + 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); } cmProcessTools::LineParser::LineParser(char sep, bool ignoreCR) diff --git a/Source/cmProcessTools.h b/Source/cmProcessTools.h index 2bdabea..74ec5e0 100644 --- a/Source/cmProcessTools.h +++ b/Source/cmProcessTools.h @@ -7,10 +7,8 @@ #include <cstring> #include <iosfwd> #include <string> -#include <vector> #include "cmProcessOutput.h" -#include "cmUVProcessChain.h" /** \class cmProcessTools * \brief Helper classes for process output parsing @@ -83,7 +81,7 @@ public: }; /** Run a process and send output to given parsers. */ - static std::vector<cmUVProcessChain::Status> RunProcess( - cmUVProcessChainBuilder& builder, OutputParser* out, - OutputParser* err = nullptr, Encoding encoding = cmProcessOutput::Auto); + static void RunProcess(struct cmsysProcess_s* cp, OutputParser* out, + OutputParser* err = nullptr, + Encoding encoding = cmProcessOutput::Auto); }; diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 1f5333f..2bdc928 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -31,9 +31,6 @@ #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) @@ -62,14 +59,12 @@ #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> @@ -573,111 +568,85 @@ bool cmSystemTools::RunSingleCommand(std::vector<std::string> const& command, const char* dir, OutputOption outputflag, cmDuration timeout, Encoding encoding) { - cmUVProcessChainBuilder builder; - builder.SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT, stdin) - .AddCommand(command); - if (dir) { - builder.SetWorkingDirectory(dir); + 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); } 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, stdout) - .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, stderr); } else if (outputflag == OUTPUT_MERGE || (captureStdErr && captureStdErr == captureStdOut)) { - builder.SetMergedBuiltinStreams(); + cmsysProcess_SetOption(cp, cmsysProcess_Option_MergeOutput, 1); captureStdErr = nullptr; - } else { - builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) - .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR); } assert(!captureStdErr || captureStdErr != captureStdOut); - 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); - } + cmsysProcess_SetTimeout(cp, timeout.count()); + cmsysProcess_Execute(cp); std::vector<char> tempStdOut; std::vector<char> tempStdErr; - cm::uv_pipe_ptr outStream; - bool outFinished = true; - cm::uv_pipe_ptr errStream; - bool errFinished = true; + char* data; + int length; + int pipe; cmProcessOutput processOutput(encoding); - std::unique_ptr<cmUVStreamReadHandle> outputHandle; - std::unique_ptr<cmUVStreamReadHandle> errorHandle; + std::string strdata; if (outputflag != OUTPUT_PASSTHROUGH && (captureStdOut || captureStdErr || outputflag != OUTPUT_NONE)) { - 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; - } - - pipe.init(chain.GetLoop(), 0); - uv_pipe_open(pipe, stream); - - 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 = ' '; - } - } + 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] = ' '; + } + } - 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); - } - } - }); - }; + 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); + } + } + } - 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); + 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); + } } } - while (!timedOut && !(chain.Finished() && outFinished && errFinished)) { - uv_run(&chain.GetLoop(), UV_RUN_ONCE); - } + cmsysProcess_WaitForExit(cp, nullptr); if (captureStdOut) { captureStdOut->assign(tempStdOut.begin(), tempStdOut.end()); @@ -689,43 +658,48 @@ bool cmSystemTools::RunSingleCommand(std::vector<std::string> const& command, } bool result = true; - if (timedOut) { - const char* error_str = "Process terminated due to timeout\n"; + 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 { - 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; + } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Expired) { + const char* error_str = "Process terminated due to timeout\n"; + if (outputflag != OUTPUT_NONE) { + std::cerr << error_str << std::endl; + } + if (captureStdErr) { + captureStdErr->append(error_str, strlen(error_str)); } + result = false; } + cmsysProcess_Delete(cp); return result; } @@ -2239,10 +2213,9 @@ bool cmSystemTools::ListTar(const std::string& outFileName, #endif } -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) +int cmSystemTools::WaitForLine(cmsysProcess* process, std::string& line, + cmDuration timeout, std::vector<char>& out, + std::vector<char>& err) { line.clear(); auto outiter = out.begin(); @@ -2264,7 +2237,7 @@ cmSystemTools::WaitForLineResult cmSystemTools::WaitForLine( line.append(out.data(), length); } out.erase(out.begin(), outiter + 1); - return WaitForLineResult::STDOUT; + return cmsysProcess_Pipe_STDOUT; } } @@ -2282,66 +2255,33 @@ cmSystemTools::WaitForLineResult cmSystemTools::WaitForLine( line.append(err.data(), length); } err.erase(err.begin(), erriter + 1); - return WaitForLineResult::STDERR; + return cmsysProcess_Pipe_STDERR; } } // No newlines found. Wait for more data from the process. - 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) { + int length; + char* data; + double timeoutAsDbl = timeout.count(); + int pipe = + cmsysProcess_WaitForData(process, &data, &length, &timeoutAsDbl); + if (pipe == cmsysProcess_Pipe_Timeout) { // Timeout has been exceeded. - return WaitForLineResult::Timeout; + return pipe; } - if (outData.Read) { - processOutput.DecodeText(outData.Buffer.data(), outData.Buffer.size(), - strdata, 1); + if (pipe == cmsysProcess_Pipe_STDOUT) { + processOutput.DecodeText(data, length, 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 (errData.Read) { - processOutput.DecodeText(errData.Buffer.data(), errData.Buffer.size(), - strdata, 2); + } else if (pipe == cmsysProcess_Pipe_STDERR) { + processOutput.DecodeText(data, length, 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 (outData.Finished && errData.Finished) { + } else if (pipe == cmsysProcess_Pipe_None) { // Both stdout and stderr pipes have broken. Return leftover data. processOutput.DecodeText(std::string(), strdata, 1); if (!strdata.empty()) { @@ -2358,20 +2298,14 @@ cmSystemTools::WaitForLineResult cmSystemTools::WaitForLine( if (!out.empty()) { line.append(out.data(), outiter - out.begin()); out.erase(out.begin(), out.end()); - return WaitForLineResult::STDOUT; + return cmsysProcess_Pipe_STDOUT; } if (!err.empty()) { line.append(err.data(), erriter - err.begin()); err.erase(err.begin(), err.end()); - return WaitForLineResult::STDERR; + return cmsysProcess_Pipe_STDERR; } - return WaitForLineResult::None; - } - if (!outData.Finished) { - uv_read_stop(outPipe); - } - if (!errData.Finished) { - uv_read_stop(errPipe); + return cmsysProcess_Pipe_None; } } } diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index 7e33e58..9563fd6 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h @@ -18,8 +18,7 @@ #include <cm/optional> #include <cm/string_view> -#include <cm3p/uv.h> - +#include "cmsys/Process.h" #include "cmsys/Status.hxx" // IWYU pragma: export #include "cmsys/SystemTools.hxx" // IWYU pragma: export @@ -340,20 +339,10 @@ public: */ static void ReportLastSystemError(const char* m); - 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); + /** a general output handler for cmsysProcess */ + static int WaitForLine(cmsysProcess* process, 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; } diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index 5bf8edc..9d7bf50 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -72,6 +72,7 @@ #include "cmsys/Directory.hxx" #include "cmsys/FStream.hxx" +#include "cmsys/Process.h" #include "cmsys/RegularExpression.hxx" #include "cmsys/Terminal.h" @@ -294,8 +295,14 @@ int CLCompileAndDependencies(const std::vector<std::string>& args) } } - cmUVProcessChainBuilder builder; - builder.AddCommand(command).SetWorkingDirectory(currentBinaryDir); + 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()); cmsys::ofstream fout(depFile.c_str()); if (!fout) { @@ -306,18 +313,22 @@ int CLCompileAndDependencies(const std::vector<std::string>& args) CLOutputLogger errLogger(std::cerr); // Start the process. - auto result = - cmProcessTools::RunProcess(builder, &includeParser, &errLogger); - auto const& subStatus = result.front(); + cmProcessTools::RunProcess(cp.get(), &includeParser, &errLogger); int status = 0; // handle status of process - if (subStatus.SpawnResult != 0) { - status = 2; - } else if (subStatus.TermSignal != 0) { - status = 1; - } else { - status = static_cast<int>(subStatus.ExitStatus); + 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 (status != 0) { @@ -1105,8 +1116,7 @@ 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, nullptr, - cmSystemTools::OUTPUT_PASSTHROUGH); + cmSystemTools::RunSingleCommand(command, nullptr, nullptr, &ret); auto time_finish = std::chrono::steady_clock::now(); std::chrono::duration<double> time_elapsed = time_finish - time_start; @@ -1880,6 +1890,21 @@ 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) { @@ -1897,21 +1922,9 @@ 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, stdout) - .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, stderr); - // Setup this command line. - std::vector<std::string> args2; -#ifdef _WIN32 - cmSystemTools::ParseWindowsCommandLine(command.c_str(), args2); -#else - cmSystemTools::ParseUnixCommandLine(command.c_str(), args2); -#endif - builder.AddCommand(args2); + const char* cmd[2] = { command.c_str(), nullptr }; + cmsysProcess_SetCommand(cp, cmd); // Report the command if verbose output is enabled. if (verbose) { @@ -1919,29 +1932,35 @@ int cmcmd::ExecuteLinkScript(std::vector<std::string> const& args) } // Run the command and wait for it to exit. - auto chain = builder.Start(); - chain.Wait(); + cmsysProcess_Execute(cp); + cmsysProcess_WaitForExit(cp, nullptr); // Report failure if any. - 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); + switch (cmsysProcess_GetState(cp)) { + case cmsysProcess_State_Exited: { + int value = cmsysProcess_GetExitValue(cp); + if (value != 0) { + result = value; } + } break; + case cmsysProcess_State_Exception: + std::cerr << "Error running link command: " + << cmsysProcess_GetExceptionString(cp) << std::endl; + result = 1; break; - case cmUVProcessChain::ExceptionCode::Spawn: - std::cerr << "Error running link command: " << exception.second; + case cmsysProcess_State_Error: + std::cerr << "Error running link command: " + << cmsysProcess_GetErrorString(cp) << std::endl; 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; } |