| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349 |
- /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
- file LICENSE.rst or https://cmake.org/licensing for details. */
- #include "cmCTestScriptHandler.h"
- #include <chrono>
- #include <cstdlib>
- #include <functional>
- #include <map>
- #include <sstream>
- #include <utility>
- #include <cm/memory>
- #include <cm3p/uv.h>
- #include "cmCTest.h"
- #include "cmCTestBuildCommand.h"
- #include "cmCTestConfigureCommand.h"
- #include "cmCTestCoverageCommand.h"
- #include "cmCTestEmptyBinaryDirectoryCommand.h"
- #include "cmCTestMemCheckCommand.h"
- #include "cmCTestReadCustomFilesCommand.h"
- #include "cmCTestRunScriptCommand.h"
- #include "cmCTestSleepCommand.h"
- #include "cmCTestStartCommand.h"
- #include "cmCTestSubmitCommand.h"
- #include "cmCTestTestCommand.h"
- #include "cmCTestUpdateCommand.h"
- #include "cmCTestUploadCommand.h"
- #include "cmDuration.h"
- #include "cmGlobalGenerator.h"
- #include "cmMakefile.h"
- #include "cmState.h"
- #include "cmStateDirectory.h"
- #include "cmStateSnapshot.h"
- #include "cmSystemTools.h"
- #include "cmUVHandlePtr.h"
- #include "cmUVProcessChain.h"
- #include "cmake.h"
- cmCTestScriptHandler::cmCTestScriptHandler(cmCTest* ctest)
- : CTest(ctest)
- {
- }
- cmCTestScriptHandler::~cmCTestScriptHandler() = default;
- // just adds an argument to the vector
- void cmCTestScriptHandler::AddConfigurationScript(std::string const& script,
- bool pscope)
- {
- this->ConfigurationScripts.emplace_back(script);
- this->ScriptProcessScope.push_back(pscope);
- }
- // the generic entry point for handling scripts, this routine will run all
- // the scripts provides a -S arguments
- int cmCTestScriptHandler::ProcessHandler()
- {
- int res = 0;
- for (size_t i = 0; i < this->ConfigurationScripts.size(); ++i) {
- // for each script run it
- res |= this->RunConfigurationScript(this->ConfigurationScripts[i],
- this->ScriptProcessScope[i]);
- }
- return res;
- }
- void cmCTestScriptHandler::UpdateElapsedTime()
- {
- if (this->Makefile) {
- // set the current elapsed time
- auto itime = cmDurationTo<unsigned int>(this->CTest->GetElapsedTime());
- auto timeString = std::to_string(itime);
- this->Makefile->AddDefinition("CTEST_ELAPSED_TIME", timeString);
- }
- }
- int cmCTestScriptHandler::ExecuteScript(std::string const& total_script_arg)
- {
- // execute the script passing in the arguments to the script as well as the
- // arguments from this invocation of cmake
- std::vector<std::string> argv;
- argv.push_back(cmSystemTools::GetCTestCommand());
- argv.push_back("-SR");
- argv.push_back(total_script_arg);
- cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
- "Executable for CTest is: " << cmSystemTools::GetCTestCommand()
- << "\n");
- // 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) {
- // in a nested subprocess, skip the parent's `-SR <path>` arguments.
- if (initArgs[i] == "-SR") {
- i++; // <path>
- } else {
- argv.push_back(initArgs[i]);
- }
- }
- // Now create process object
- cmUVProcessChainBuilder builder;
- builder.AddCommand(argv)
- .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
- .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
- auto process = builder.Start();
- cm::uv_pipe_ptr outPipe;
- outPipe.init(process.GetLoop(), 0);
- uv_pipe_open(outPipe, process.OutputStream());
- cm::uv_pipe_ptr errPipe;
- errPipe.init(process.GetLoop(), 0);
- uv_pipe_open(errPipe, process.ErrorStream());
- std::vector<char> out;
- std::vector<char> err;
- std::string line;
- auto pipe =
- cmSystemTools::WaitForLine(&process.GetLoop(), outPipe, errPipe, line,
- std::chrono::seconds(100), out, err);
- while (pipe != cmSystemTools::WaitForLineResult::None) {
- cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
- "Output: " << line << "\n");
- if (pipe == cmSystemTools::WaitForLineResult::STDERR) {
- cmCTestLog(this->CTest, ERROR_MESSAGE, line << "\n");
- } else if (pipe == cmSystemTools::WaitForLineResult::STDOUT) {
- cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, line << "\n");
- }
- pipe =
- cmSystemTools::WaitForLine(&process.GetLoop(), outPipe, errPipe, 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();
- 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 (failed) {
- std::ostringstream message;
- message << "Error running command: [";
- message << static_cast<int>(result.first) << "] ";
- for (std::string const& arg : argv) {
- message << arg << " ";
- }
- cmCTestLog(this->CTest, ERROR_MESSAGE, message.str() << std::endl);
- return -1;
- }
- return retVal;
- }
- void cmCTestScriptHandler::CreateCMake()
- {
- // create a cmake instance to read the configuration script
- this->CMake = cm::make_unique<cmake>(cmState::Role::CTest);
- this->CMake->GetCurrentSnapshot().SetDefaultDefinitions();
- this->CMake->AddCMakePaths();
- this->GlobalGenerator =
- cm::make_unique<cmGlobalGenerator>(this->CMake.get());
- cmStateSnapshot snapshot = this->CMake->GetCurrentSnapshot();
- std::string cwd = cmSystemTools::GetLogicalWorkingDirectory();
- snapshot.GetDirectory().SetCurrentSource(cwd);
- snapshot.GetDirectory().SetCurrentBinary(cwd);
- this->Makefile =
- cm::make_unique<cmMakefile>(this->GlobalGenerator.get(), snapshot);
- if (this->ParentMakefile) {
- this->Makefile->SetRecursionDepth(
- this->ParentMakefile->GetRecursionDepth());
- }
- this->CMake->SetProgressCallback(
- [this](std::string const& m, float /*unused*/) {
- if (!m.empty()) {
- cmCTestLog(this->CTest, HANDLER_OUTPUT, "-- " << m << std::endl);
- }
- });
- cmState* state = this->CMake->GetState();
- state->AddBuiltinCommand("ctest_build", cmCTestBuildCommand(this->CTest));
- state->AddBuiltinCommand("ctest_configure",
- cmCTestConfigureCommand(this->CTest));
- state->AddBuiltinCommand("ctest_coverage",
- cmCTestCoverageCommand(this->CTest));
- state->AddBuiltinCommand("ctest_empty_binary_directory",
- cmCTestEmptyBinaryDirectoryCommand);
- state->AddBuiltinCommand("ctest_memcheck",
- cmCTestMemCheckCommand(this->CTest));
- state->AddBuiltinCommand("ctest_read_custom_files",
- cmCTestReadCustomFilesCommand(this->CTest));
- state->AddBuiltinCommand("ctest_run_script",
- cmCTestRunScriptCommand(this->CTest));
- state->AddBuiltinCommand("ctest_sleep", cmCTestSleepCommand);
- state->AddBuiltinCommand("ctest_start", cmCTestStartCommand(this->CTest));
- state->AddBuiltinCommand("ctest_submit", cmCTestSubmitCommand(this->CTest));
- state->AddBuiltinCommand("ctest_test", cmCTestTestCommand(this->CTest));
- state->AddBuiltinCommand("ctest_update", cmCTestUpdateCommand(this->CTest));
- state->AddBuiltinCommand("ctest_upload", cmCTestUploadCommand(this->CTest));
- }
- // this sets up some variables for the script to use, creates the required
- // cmake instance and generators, and then reads in the script
- int cmCTestScriptHandler::ReadInScript(std::string const& total_script_arg)
- {
- // Reset the error flag so that the script is read in no matter what
- cmSystemTools::ResetErrorOccurredFlag();
- // if the argument has a , in it then it needs to be broken into the fist
- // argument (which is the script) and the second argument which will be
- // passed into the scripts as S_ARG
- std::string script;
- std::string script_arg;
- std::string::size_type const comma_pos = total_script_arg.find(',');
- if (comma_pos != std::string::npos) {
- script = total_script_arg.substr(0, comma_pos);
- script_arg = total_script_arg.substr(comma_pos + 1);
- } else {
- script = total_script_arg;
- }
- // make sure the file exists
- if (!cmSystemTools::FileExists(script)) {
- cmSystemTools::Error("Cannot find file: " + script);
- return 1;
- }
- // read in the list file to fill the cache
- // create a cmake instance to read the configuration script
- this->CreateCMake();
- // set a variable with the path to the current script
- this->Makefile->AddDefinition("CTEST_SCRIPT_DIRECTORY",
- cmSystemTools::GetFilenamePath(script));
- this->Makefile->AddDefinition("CTEST_SCRIPT_NAME",
- cmSystemTools::GetFilenameName(script));
- this->Makefile->AddDefinition("CTEST_EXECUTABLE_NAME",
- cmSystemTools::GetCTestCommand());
- this->Makefile->AddDefinition("CMAKE_EXECUTABLE_NAME",
- cmSystemTools::GetCMakeCommand());
- this->UpdateElapsedTime();
- // set the CTEST_CONFIGURATION_TYPE variable to the current value of the
- // the -C argument on the command line.
- if (!this->CTest->GetConfigType().empty()) {
- this->Makefile->AddDefinition("CTEST_CONFIGURATION_TYPE",
- this->CTest->GetConfigType());
- }
- // add the script arg if defined
- if (!script_arg.empty()) {
- this->Makefile->AddDefinition("CTEST_SCRIPT_ARG", script_arg);
- }
- // set a callback function to update the elapsed time
- this->Makefile->OnExecuteCommand([this] { this->UpdateElapsedTime(); });
- /* Execute CTestScriptMode.cmake, which loads CMakeDetermineSystem and
- CMakeSystemSpecificInformation, so
- that variables like CMAKE_SYSTEM and also the search paths for libraries,
- header and executables are set correctly and can be used. Makes new-style
- ctest scripting easier. */
- std::string systemFile =
- this->Makefile->GetModulesFile("CTestScriptMode.cmake");
- if (!this->Makefile->ReadListFile(systemFile) ||
- cmSystemTools::GetErrorOccurredFlag()) {
- cmCTestLog(this->CTest, ERROR_MESSAGE,
- "Error in read:" << systemFile << "\n");
- return -1;
- }
- // Add definitions of variables passed in on the command line:
- std::map<std::string, std::string> const& defs =
- this->CTest->GetDefinitions();
- for (auto const& d : defs) {
- this->Makefile->AddDefinition(d.first, d.second);
- }
- int res = 0;
- // finally read in the script
- if (!this->Makefile->ReadListFile(script) ||
- cmSystemTools::GetErrorOccurredFlag()) {
- // Reset the error flag so that it can run more than
- // one script with an error when you use ctest_run_script.
- cmSystemTools::ResetErrorOccurredFlag();
- res = -1;
- }
- return this->CMake->HasScriptModeExitCode()
- ? this->CMake->GetScriptModeExitCode()
- : res;
- }
- // run a specific script
- int cmCTestScriptHandler::RunConfigurationScript(
- std::string const& total_script_arg, bool pscope)
- {
- #ifndef CMAKE_BOOTSTRAP
- cmSystemTools::SaveRestoreEnvironment sre;
- #endif
- int result;
- // read in the script
- if (pscope) {
- cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
- "Reading Script: " << total_script_arg << std::endl);
- result = this->ReadInScript(total_script_arg);
- } else {
- cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
- "Executing Script: " << total_script_arg << std::endl);
- result = this->ExecuteScript(total_script_arg);
- }
- return result;
- }
- bool cmCTestScriptHandler::RunScript(cmCTest* ctest, cmMakefile* mf,
- std::string const& sname, bool InProcess,
- int* returnValue)
- {
- auto sh = cm::make_unique<cmCTestScriptHandler>(ctest);
- sh->ParentMakefile = mf;
- sh->AddConfigurationScript(sname, InProcess);
- int res = sh->ProcessHandler();
- if (returnValue) {
- *returnValue = res;
- }
- return true;
- }
|