cmCTestScriptHandler.cxx 30.6 KB
Newer Older
1 2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
3 4
#include "cmCTestScriptHandler.h"

5 6
#include "cmsys/Directory.hxx"
#include "cmsys/Process.h"
Daniel Pfeifer's avatar
Daniel Pfeifer committed
7

8 9
#include "cm_memory.hxx"

10
#include "cmCTest.h"
11
#include "cmCTestBuildCommand.h"
12
#include "cmCTestCommand.h"
13
#include "cmCTestConfigureCommand.h"
14
#include "cmCTestCoverageCommand.h"
15
#include "cmCTestEmptyBinaryDirectoryCommand.h"
16
#include "cmCTestMemCheckCommand.h"
17
#include "cmCTestReadCustomFilesCommand.h"
18 19
#include "cmCTestRunScriptCommand.h"
#include "cmCTestSleepCommand.h"
20
#include "cmCTestStartCommand.h"
21
#include "cmCTestSubmitCommand.h"
22 23
#include "cmCTestTestCommand.h"
#include "cmCTestUpdateCommand.h"
Zach's avatar
Zach committed
24
#include "cmCTestUploadCommand.h"
25
#include "cmCommand.h"
26
#include "cmDuration.h"
27 28 29
#include "cmGeneratedFileStream.h"
#include "cmGlobalGenerator.h"
#include "cmMakefile.h"
30
#include "cmState.h"
31
#include "cmStateDirectory.h"
Daniel Pfeifer's avatar
Daniel Pfeifer committed
32
#include "cmStateSnapshot.h"
33
#include "cmStringAlgorithms.h"
34 35 36
#include "cmSystemTools.h"
#include "cmake.h"

37 38 39
#include <cstdio>
#include <cstdlib>
#include <cstring>
40 41 42 43 44 45
#include <map>
#include <memory>
#include <ratio>
#include <sstream>
#include <utility>

46
#ifdef _WIN32
47
#  include <windows.h>
48
#else
49
#  include <unistd.h>
50 51
#endif

Andy Cedilnik's avatar
Andy Cedilnik committed
52 53
#define CTEST_INITIAL_CMAKE_OUTPUT_FILE_NAME "CTestInitialCMakeOutput.log"

54 55
cmCTestScriptHandler::cmCTestScriptHandler()
{
Andy Cedilnik's avatar
Andy Cedilnik committed
56 57 58
  this->Backup = false;
  this->EmptyBinDir = false;
  this->EmptyBinDirOnce = false;
Daniel Pfeifer's avatar
Daniel Pfeifer committed
59
  this->Makefile = nullptr;
60
  this->ParentMakefile = nullptr;
Daniel Pfeifer's avatar
Daniel Pfeifer committed
61 62
  this->CMake = nullptr;
  this->GlobalGenerator = nullptr;
63

64
  this->ScriptStartTime = std::chrono::steady_clock::time_point();
Andy Cedilnik's avatar
Andy Cedilnik committed
65

luz.paz's avatar
luz.paz committed
66
  // the *60 is because the settings are in minutes but GetTime is seconds
67
  this->MinimumInterval = 30 * 60;
Andy Cedilnik's avatar
Andy Cedilnik committed
68
  this->ContinuousDuration = -1;
69 70
}

71 72
void cmCTestScriptHandler::Initialize()
{
73
  this->Superclass::Initialize();
Andy Cedilnik's avatar
Andy Cedilnik committed
74 75 76 77
  this->Backup = false;
  this->EmptyBinDir = false;
  this->EmptyBinDirOnce = false;

78 79 80 81 82 83 84 85 86 87 88 89
  this->SourceDir.clear();
  this->BinaryDir.clear();
  this->BackupSourceDir.clear();
  this->BackupBinaryDir.clear();
  this->CTestRoot.clear();
  this->CVSCheckOut.clear();
  this->CTestCmd.clear();
  this->UpdateCmd.clear();
  this->CTestEnv.clear();
  this->InitialCache.clear();
  this->CMakeCmd.clear();
  this->CMOutFile.clear();
Andy Cedilnik's avatar
Andy Cedilnik committed
90 91
  this->ExtraUpdates.clear();

92
  this->MinimumInterval = 20 * 60;
Andy Cedilnik's avatar
Andy Cedilnik committed
93
  this->ContinuousDuration = -1;
94 95

  // what time in seconds did this script start running
96
  this->ScriptStartTime = std::chrono::steady_clock::time_point();
Andy Cedilnik's avatar
Andy Cedilnik committed
97

98
  delete this->Makefile;
Daniel Pfeifer's avatar
Daniel Pfeifer committed
99
  this->Makefile = nullptr;
100
  this->ParentMakefile = nullptr;
101 102

  delete this->GlobalGenerator;
Daniel Pfeifer's avatar
Daniel Pfeifer committed
103
  this->GlobalGenerator = nullptr;
104 105

  delete this->CMake;
106
}
107 108 109

cmCTestScriptHandler::~cmCTestScriptHandler()
{
110
  delete this->Makefile;
111 112
  delete this->GlobalGenerator;
  delete this->CMake;
113 114 115
}

// just adds an argument to the vector
116
void cmCTestScriptHandler::AddConfigurationScript(const char* script,
Ken Martin's avatar
Ken Martin committed
117
                                                  bool pscope)
118
{
wahikihiki's avatar
wahikihiki committed
119
  this->ConfigurationScripts.emplace_back(script);
120
  this->ScriptProcessScope.push_back(pscope);
121 122 123 124
}

// the generic entry point for handling scripts, this routine will run all
// the scripts provides a -S arguments
125
int cmCTestScriptHandler::ProcessHandler()
126 127
{
  int res = 0;
128
  for (size_t i = 0; i < this->ConfigurationScripts.size(); ++i) {
129
    // for each script run it
130 131 132 133 134
    res |= this->RunConfigurationScript(
      cmSystemTools::CollapseFullPath(this->ConfigurationScripts[i]),
      this->ScriptProcessScope[i]);
  }
  if (res) {
135
    return -1;
136
  }
137
  return 0;
138 139
}

140 141
void cmCTestScriptHandler::UpdateElapsedTime()
{
142
  if (this->Makefile) {
143
    // set the current elapsed time
144 145 146
    auto itime = cmDurationTo<unsigned int>(std::chrono::steady_clock::now() -
                                            this->ScriptStartTime);
    auto timeString = std::to_string(itime);
147
    this->Makefile->AddDefinition("CTEST_ELAPSED_TIME", timeString);
148
  }
149
}
150

151 152
void cmCTestScriptHandler::AddCTestCommand(
  std::string const& name, std::unique_ptr<cmCTestCommand> command)
153
{
154 155
  command->CTest = this->CTest;
  command->CTestScriptHandler = this;
156
  this->CMake->GetState()->AddBuiltinCommand(name, std::move(command));
157 158
}

159 160 161 162 163
int cmCTestScriptHandler::ExecuteScript(const std::string& 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<const char*> argv;
164
  argv.push_back(cmSystemTools::GetCTestCommand().c_str());
165 166 167
  argv.push_back("-SR");
  argv.push_back(total_script_arg.c_str());

168 169 170
  cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
             "Executable for CTest is: " << cmSystemTools::GetCTestCommand()
                                         << "\n");
171 172

  // now pass through all the other arguments
173
  std::vector<std::string>& initArgs =
174
    this->CTest->GetInitialCommandLineArguments();
Bill Hoffman's avatar
Bill Hoffman committed
175
  //*** need to make sure this does not have the current script ***
176
  for (size_t i = 1; i < initArgs.size(); ++i) {
177
    argv.push_back(initArgs[i].c_str());
178
  }
Daniel Pfeifer's avatar
Daniel Pfeifer committed
179
  argv.push_back(nullptr);
180 181 182

  // Now create process object
  cmsysProcess* cp = cmsysProcess_New();
183
  cmsysProcess_SetCommand(cp, argv.data());
184
  // cmsysProcess_SetWorkingDirectory(cp, dir);
185
  cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
186
  // cmsysProcess_SetTimeout(cp, timeout);
187 188 189 190 191
  cmsysProcess_Execute(cp);

  std::vector<char> out;
  std::vector<char> err;
  std::string line;
192 193
  int pipe =
    cmSystemTools::WaitForLine(cp, line, std::chrono::seconds(100), out, err);
194
  while (pipe != cmsysProcess_Pipe_None) {
195 196
    cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
               "Output: " << line << "\n");
197
    if (pipe == cmsysProcess_Pipe_STDERR) {
198
      cmCTestLog(this->CTest, ERROR_MESSAGE, line << "\n");
199
    } else if (pipe == cmsysProcess_Pipe_STDOUT) {
200 201
      cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, line << "\n");
    }
202 203
    pipe = cmSystemTools::WaitForLine(cp, line, std::chrono::seconds(100), out,
                                      err);
204
  }
Brad King's avatar
Brad King committed
205

206
  // Properly handle output of the build command
Daniel Pfeifer's avatar
Daniel Pfeifer committed
207
  cmsysProcess_WaitForExit(cp, nullptr);
208 209
  int result = cmsysProcess_GetState(cp);
  int retVal = 0;
210
  bool failed = false;
211
  if (result == cmsysProcess_State_Exited) {
212
    retVal = cmsysProcess_GetExitValue(cp);
213
  } else if (result == cmsysProcess_State_Exception) {
214
    retVal = cmsysProcess_GetExitException(cp);
215 216
    cmCTestLog(this->CTest, ERROR_MESSAGE,
               "\tThere was an exception: "
217 218
                 << cmsysProcess_GetExceptionString(cp) << " " << retVal
                 << std::endl);
219
    failed = true;
220
  } else if (result == cmsysProcess_State_Expired) {
221 222
    cmCTestLog(this->CTest, ERROR_MESSAGE,
               "\tThere was a timeout" << std::endl);
223
    failed = true;
224
  } else if (result == cmsysProcess_State_Error) {
225 226 227
    cmCTestLog(this->CTest, ERROR_MESSAGE,
               "\tError executing ctest: " << cmsysProcess_GetErrorString(cp)
                                           << std::endl);
228
    failed = true;
229
  }
230
  cmsysProcess_Delete(cp);
231
  if (failed) {
232
    std::ostringstream message;
233 234
    message << "Error running command: [";
    message << result << "] ";
235 236 237
    for (const char* arg : argv) {
      if (arg) {
        message << arg << " ";
238
      }
239
    }
240 241
    cmCTestLog(this->CTest, ERROR_MESSAGE,
               message.str() << argv[0] << std::endl);
242 243
    return -1;
  }
244 245 246
  return retVal;
}

Alexander Neundorf's avatar
 
Alexander Neundorf committed
247 248 249
void cmCTestScriptHandler::CreateCMake()
{
  // create a cmake instance to read the configuration script
250
  if (this->CMake) {
Alexander Neundorf's avatar
 
Alexander Neundorf committed
251 252
    delete this->CMake;
    delete this->GlobalGenerator;
253
    delete this->Makefile;
254
  }
255
  this->CMake = new cmake(cmake::RoleScript, cmState::CTest);
256 257
  this->CMake->SetHomeDirectory("");
  this->CMake->SetHomeOutputDirectory("");
258
  this->CMake->GetCurrentSnapshot().SetDefaultDefinitions();
259
  this->CMake->AddCMakePaths();
260
  this->GlobalGenerator = new cmGlobalGenerator(this->CMake);
Alexander Neundorf's avatar
 
Alexander Neundorf committed
261

262
  cmStateSnapshot snapshot = this->CMake->GetCurrentSnapshot();
263 264 265
  std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();
  snapshot.GetDirectory().SetCurrentSource(cwd);
  snapshot.GetDirectory().SetCurrentBinary(cwd);
266
  this->Makefile = new cmMakefile(this->GlobalGenerator, snapshot);
267 268 269 270
  if (this->ParentMakefile) {
    this->Makefile->SetRecursionDepth(
      this->ParentMakefile->GetRecursionDepth());
  }
Alexander Neundorf's avatar
 
Alexander Neundorf committed
271

272 273 274 275 276 277
  this->CMake->SetProgressCallback(
    [this](const std::string& m, float /*unused*/) {
      if (!m.empty()) {
        cmCTestLog(this->CTest, HANDLER_OUTPUT, "-- " << m << std::endl);
      }
    });
278

279 280 281 282 283
  this->AddCTestCommand("ctest_build", cm::make_unique<cmCTestBuildCommand>());
  this->AddCTestCommand("ctest_configure",
                        cm::make_unique<cmCTestConfigureCommand>());
  this->AddCTestCommand("ctest_coverage",
                        cm::make_unique<cmCTestCoverageCommand>());
284
  this->AddCTestCommand("ctest_empty_binary_directory",
285 286 287
                        cm::make_unique<cmCTestEmptyBinaryDirectoryCommand>());
  this->AddCTestCommand("ctest_memcheck",
                        cm::make_unique<cmCTestMemCheckCommand>());
288
  this->AddCTestCommand("ctest_read_custom_files",
289 290 291 292 293 294 295 296 297 298 299 300
                        cm::make_unique<cmCTestReadCustomFilesCommand>());
  this->AddCTestCommand("ctest_run_script",
                        cm::make_unique<cmCTestRunScriptCommand>());
  this->AddCTestCommand("ctest_sleep", cm::make_unique<cmCTestSleepCommand>());
  this->AddCTestCommand("ctest_start", cm::make_unique<cmCTestStartCommand>());
  this->AddCTestCommand("ctest_submit",
                        cm::make_unique<cmCTestSubmitCommand>());
  this->AddCTestCommand("ctest_test", cm::make_unique<cmCTestTestCommand>());
  this->AddCTestCommand("ctest_update",
                        cm::make_unique<cmCTestUpdateCommand>());
  this->AddCTestCommand("ctest_upload",
                        cm::make_unique<cmCTestUploadCommand>());
Alexander Neundorf's avatar
 
Alexander Neundorf committed
301 302
}

303
// this sets up some variables for the script to use, creates the required
304
// cmake instance and generators, and then reads in the script
305
int cmCTestScriptHandler::ReadInScript(const std::string& total_script_arg)
306
{
307 308 309
  // Reset the error flag so that the script is read in no matter what
  cmSystemTools::ResetErrorOccuredFlag();

310 311 312 313 314
  // 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 = total_script_arg;
  std::string script_arg;
315 316 317 318
  const std::string::size_type 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);
319
  }
320
  // make sure the file exists
321
  if (!cmSystemTools::FileExists(script)) {
322
    cmSystemTools::Error("Cannot find file: " + script);
323
    return 1;
324
  }
325 326

  // read in the list file to fill the cache
Alexander Neundorf's avatar
 
Alexander Neundorf committed
327 328
  // create a cmake instance to read the configuration script
  this->CreateCMake();
Andy Cedilnik's avatar
Andy Cedilnik committed
329

330
  // set a variable with the path to the current script
331 332 333 334
  this->Makefile->AddDefinition("CTEST_SCRIPT_DIRECTORY",
                                cmSystemTools::GetFilenamePath(script));
  this->Makefile->AddDefinition("CTEST_SCRIPT_NAME",
                                cmSystemTools::GetFilenameName(script));
Andy Cedilnik's avatar
Andy Cedilnik committed
335
  this->Makefile->AddDefinition("CTEST_EXECUTABLE_NAME",
336
                                cmSystemTools::GetCTestCommand());
Andy Cedilnik's avatar
Andy Cedilnik committed
337
  this->Makefile->AddDefinition("CMAKE_EXECUTABLE_NAME",
338
                                cmSystemTools::GetCMakeCommand());
339
  this->Makefile->AddDefinitionBool("CTEST_RUN_CURRENT_SCRIPT", true);
340
  this->SetRunCurrentScript(true);
341
  this->UpdateElapsedTime();
Andy Cedilnik's avatar
Andy Cedilnik committed
342

343
  // add the script arg if defined
344
  if (!script_arg.empty()) {
345
    this->Makefile->AddDefinition("CTEST_SCRIPT_ARG", script_arg);
346
  }
347

348 349 350 351
#if defined(__CYGWIN__)
  this->Makefile->AddDefinition("CMAKE_LEGACY_CYGWIN_WIN32", "0");
#endif

352 353
  // set a callback function to update the elapsed time
  this->Makefile->OnExecuteCommand([this] { this->UpdateElapsedTime(); });
Andy Cedilnik's avatar
Andy Cedilnik committed
354

Brad King's avatar
Brad King committed
355 356
  /* Execute CTestScriptMode.cmake, which loads CMakeDetermineSystem and
  CMakeSystemSpecificInformation, so
357 358 359
  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. */
Brad King's avatar
Brad King committed
360
  std::string systemFile =
361
    this->Makefile->GetModulesFile("CTestScriptMode.cmake");
362
  if (!this->Makefile->ReadListFile(systemFile) ||
363
      cmSystemTools::GetErrorOccuredFlag()) {
364 365
    cmCTestLog(this->CTest, ERROR_MESSAGE,
               "Error in read:" << systemFile << "\n");
366
    return 2;
367
  }
368

369
  // Add definitions of variables passed in on the command line:
370
  const std::map<std::string, std::string>& defs =
371
    this->CTest->GetDefinitions();
372
  for (auto const& d : defs) {
373
    this->Makefile->AddDefinition(d.first, d.second);
374
  }
375

376
  // finally read in the script
377
  if (!this->Makefile->ReadListFile(script) ||
378
      cmSystemTools::GetErrorOccuredFlag()) {
Brad King's avatar
Brad King committed
379
    // Reset the error flag so that it can run more than
380
    // one script with an error when you use ctest_run_script.
381
    cmSystemTools::ResetErrorOccuredFlag();
382
    return 2;
383
  }
Andy Cedilnik's avatar
Andy Cedilnik committed
384

385 386 387
  return 0;
}

388
// extract variables from the script to set ivars
389 390
int cmCTestScriptHandler::ExtractVariables()
{
391 392 393
  // Temporary variables
  const char* minInterval;
  const char* contDuration;
394

395 396 397 398
  this->SourceDir =
    this->Makefile->GetSafeDefinition("CTEST_SOURCE_DIRECTORY");
  this->BinaryDir =
    this->Makefile->GetSafeDefinition("CTEST_BINARY_DIRECTORY");
399 400

  // add in translations for src and bin
401 402
  cmSystemTools::AddKeepPath(this->SourceDir);
  cmSystemTools::AddKeepPath(this->BinaryDir);
403

404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
  this->CTestCmd = this->Makefile->GetSafeDefinition("CTEST_COMMAND");
  this->CVSCheckOut = this->Makefile->GetSafeDefinition("CTEST_CVS_CHECKOUT");
  this->CTestRoot = this->Makefile->GetSafeDefinition("CTEST_DASHBOARD_ROOT");
  this->UpdateCmd = this->Makefile->GetSafeDefinition("CTEST_UPDATE_COMMAND");
  if (this->UpdateCmd.empty()) {
    this->UpdateCmd = this->Makefile->GetSafeDefinition("CTEST_CVS_COMMAND");
  }
  this->CTestEnv = this->Makefile->GetSafeDefinition("CTEST_ENVIRONMENT");
  this->InitialCache =
    this->Makefile->GetSafeDefinition("CTEST_INITIAL_CACHE");
  this->CMakeCmd = this->Makefile->GetSafeDefinition("CTEST_CMAKE_COMMAND");
  this->CMOutFile =
    this->Makefile->GetSafeDefinition("CTEST_CMAKE_OUTPUT_FILE_NAME");

  this->Backup = this->Makefile->IsOn("CTEST_BACKUP_AND_RESTORE");
  this->EmptyBinDir =
    this->Makefile->IsOn("CTEST_START_WITH_EMPTY_BINARY_DIRECTORY");
  this->EmptyBinDirOnce =
    this->Makefile->IsOn("CTEST_START_WITH_EMPTY_BINARY_DIRECTORY_ONCE");

  minInterval =
    this->Makefile->GetDefinition("CTEST_CONTINUOUS_MINIMUM_INTERVAL");
  contDuration = this->Makefile->GetDefinition("CTEST_CONTINUOUS_DURATION");
427 428 429

  char updateVar[40];
  int i;
430 431 432 433 434 435
  for (i = 1; i < 10; ++i) {
    sprintf(updateVar, "CTEST_EXTRA_UPDATES_%i", i);
    const char* updateVal = this->Makefile->GetDefinition(updateVar);
    if (updateVal) {
      if (this->UpdateCmd.empty()) {
        cmSystemTools::Error(
436 437
          std::string(updateVar) +
          " specified without specifying CTEST_CVS_COMMAND.");
438 439
        return 12;
      }
wahikihiki's avatar
wahikihiki committed
440
      this->ExtraUpdates.emplace_back(updateVal);
441
    }
442
  }
443 444

  // in order to backup and restore we also must have the cvs root
445
  if (this->Backup && this->CVSCheckOut.empty()) {
446
    cmSystemTools::Error(
Andy Cedilnik's avatar
Andy Cedilnik committed
447
      "Backup was requested without specifying CTEST_CVS_CHECKOUT.");
448
    return 3;
449
  }
Andy Cedilnik's avatar
Andy Cedilnik committed
450

451
  // make sure the required info is here
452 453
  if (this->SourceDir.empty() || this->BinaryDir.empty() ||
      this->CTestCmd.empty()) {
454 455 456 457 458 459 460
    std::string msg =
      cmStrCat("CTEST_SOURCE_DIRECTORY = ",
               (!this->SourceDir.empty()) ? this->SourceDir.c_str() : "(Null)",
               "\nCTEST_BINARY_DIRECTORY = ",
               (!this->BinaryDir.empty()) ? this->BinaryDir.c_str() : "(Null)",
               "\nCTEST_COMMAND = ",
               (!this->CTestCmd.empty()) ? this->CTestCmd.c_str() : "(Null)");
461
    cmSystemTools::Error(
462 463
      "Some required settings in the configuration file were missing:\n" +
      msg);
464
    return 4;
465
  }
Andy Cedilnik's avatar
Andy Cedilnik committed
466

467
  // if the dashboard root isn't specified then we can compute it from the
Andy Cedilnik's avatar
Andy Cedilnik committed
468
  // this->SourceDir
469
  if (this->CTestRoot.empty()) {
470
    this->CTestRoot = cmSystemTools::GetFilenamePath(this->SourceDir);
471
  }
472 473

  // the script may override the minimum continuous interval
474
  if (minInterval) {
Andy Cedilnik's avatar
Andy Cedilnik committed
475
    this->MinimumInterval = 60 * atof(minInterval);
476 477
  }
  if (contDuration) {
Andy Cedilnik's avatar
Andy Cedilnik committed
478
    this->ContinuousDuration = 60.0 * atof(contDuration);
479
  }
Andy Cedilnik's avatar
Andy Cedilnik committed
480

481 482
  this->UpdateElapsedTime();

483 484 485
  return 0;
}

486
void cmCTestScriptHandler::SleepInSeconds(unsigned int secondsToWait)
487 488
{
#if defined(_WIN32)
489
  Sleep(1000 * secondsToWait);
490
#else
491
  sleep(secondsToWait);
492 493 494 495
#endif
}

// run a specific script
496 497
int cmCTestScriptHandler::RunConfigurationScript(
  const std::string& total_script_arg, bool pscope)
498
{
499
#ifndef CMAKE_BOOTSTRAP
500 501 502
  cmSystemTools::SaveRestoreEnvironment sre;
#endif

503
  int result;
Andy Cedilnik's avatar
Andy Cedilnik committed
504

505
  this->ScriptStartTime = std::chrono::steady_clock::now();
Andy Cedilnik's avatar
Andy Cedilnik committed
506

507
  // read in the script
508
  if (pscope) {
509
    cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
510
               "Reading Script: " << total_script_arg << std::endl);
511
    result = this->ReadInScript(total_script_arg);
512
  } else {
513
    cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
514
               "Executing Script: " << total_script_arg << std::endl);
515
    result = this->ExecuteScript(total_script_arg);
516 517
  }
  if (result) {
518
    return result;
519
  }
Andy Cedilnik's avatar
Andy Cedilnik committed
520

521
  // only run the current script if we should
522 523
  if (this->Makefile && this->Makefile->IsOn("CTEST_RUN_CURRENT_SCRIPT") &&
      this->ShouldRunCurrentScript) {
524
    return this->RunCurrentScript();
525
  }
526 527 528
  return result;
}

529
int cmCTestScriptHandler::RunCurrentScript()
530 531 532
{
  int result;

533
  // do not run twice
534
  this->SetRunCurrentScript(false);
535

536 537
  // no popup widows
  cmSystemTools::SetRunCommandHideConsole(true);
Andy Cedilnik's avatar
Andy Cedilnik committed
538

539 540
  // extract the vars from the cache and store in ivars
  result = this->ExtractVariables();
541
  if (result) {
542
    return result;
543
  }
Andy Cedilnik's avatar
Andy Cedilnik committed
544

545
  // set any environment variables
546
  if (!this->CTestEnv.empty()) {
547
    std::vector<std::string> envArgs = cmExpandedList(this->CTestEnv);
548
    cmSystemTools::AppendEnv(envArgs);
549
  }
550 551 552

  // now that we have done most of the error checking finally run the
  // dashboard, we may be asked to repeatedly run this dashboard, such as
luz.paz's avatar
luz.paz committed
553
  // for a continuous, do we need to run it more than once?
554
  if (this->ContinuousDuration >= 0) {
555
    this->UpdateElapsedTime();
Wouter Klouwen's avatar
Wouter Klouwen committed
556 557
    auto ending_time =
      std::chrono::steady_clock::now() + cmDuration(this->ContinuousDuration);
558
    if (this->EmptyBinDirOnce) {
Andy Cedilnik's avatar
Andy Cedilnik committed
559
      this->EmptyBinDir = true;
560 561
    }
    do {
562
      auto startOfInterval = std::chrono::steady_clock::now();
563
      result = this->RunConfigurationDashboard();
564
      auto interval = std::chrono::steady_clock::now() - startOfInterval;
Wouter Klouwen's avatar
Wouter Klouwen committed
565
      auto minimumInterval = cmDuration(this->MinimumInterval);
566
      if (interval < minimumInterval) {
567 568 569
        auto sleepTime =
          cmDurationTo<unsigned int>(minimumInterval - interval);
        this->SleepInSeconds(sleepTime);
570 571
      }
      if (this->EmptyBinDirOnce) {
Andy Cedilnik's avatar
Andy Cedilnik committed
572
        this->EmptyBinDir = false;
573
      }
574
    } while (std::chrono::steady_clock::now() < ending_time);
575
  }
576
  // otherwise just run it once
577
  else {
578
    result = this->RunConfigurationDashboard();
579
  }
580 581 582 583 584 585 586 587 588

  return result;
}

int cmCTestScriptHandler::CheckOutSourceDir()
{
  std::string command;
  std::string output;
  int retVal;
Andy Cedilnik's avatar
Andy Cedilnik committed
589
  bool res;
590

591
  if (!cmSystemTools::FileExists(this->SourceDir) &&
592
      !this->CVSCheckOut.empty()) {
593
    // we must now checkout the src dir
594
    output.clear();
Andy Cedilnik's avatar
Andy Cedilnik committed
595
    cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
596
               "Run cvs: " << this->CVSCheckOut << std::endl);
597
    res = cmSystemTools::RunSingleCommand(
598 599
      this->CVSCheckOut, &output, &output, &retVal, this->CTestRoot.c_str(),
      this->HandlerVerbose, cmDuration::zero() /*this->TimeOut*/);
600
    if (!res || retVal != 0) {
601
      cmSystemTools::Error("Unable to perform cvs checkout:\n" + output);
602 603
      return 6;
    }
604
  }
605 606 607 608 609 610 611 612
  return 0;
}

int cmCTestScriptHandler::BackupDirectories()
{
  int retVal;

  // compute the backup names
613 614
  this->BackupSourceDir = cmStrCat(this->SourceDir, "_CMakeBackup");
  this->BackupBinaryDir = cmStrCat(this->BinaryDir, "_CMakeBackup");
Andy Cedilnik's avatar
Andy Cedilnik committed
615

616
  // backup the binary and src directories if requested
617
  if (this->Backup) {
618
    // if for some reason those directories exist then first delete them
619
    if (cmSystemTools::FileExists(this->BackupSourceDir)) {
620
      cmSystemTools::RemoveADirectory(this->BackupSourceDir);
621
    }
622
    if (cmSystemTools::FileExists(this->BackupBinaryDir)) {
623
      cmSystemTools::RemoveADirectory(this->BackupBinaryDir);
624
    }
Andy Cedilnik's avatar
Andy Cedilnik committed
625 626

    // first rename the src and binary directories
Andy Cedilnik's avatar
Andy Cedilnik committed
627 628
    rename(this->SourceDir.c_str(), this->BackupSourceDir.c_str());
    rename(this->BinaryDir.c_str(), this->BackupBinaryDir.c_str());
Andy Cedilnik's avatar
Andy Cedilnik committed
629

630 631
    // we must now checkout the src dir
    retVal = this->CheckOutSourceDir();
632
    if (retVal) {
633 634 635
      this->RestoreBackupDirectories();
      return retVal;
    }
636
  }
637 638 639 640 641 642 643 644 645

  return 0;
}

int cmCTestScriptHandler::PerformExtraUpdates()
{
  std::string command;
  std::string output;
  int retVal;
Andy Cedilnik's avatar
Andy Cedilnik committed
646
  bool res;
647 648

  // do an initial cvs update as required
649
  command = this->UpdateCmd;
650
  for (std::string const& eu : this->ExtraUpdates) {
651
    std::vector<std::string> cvsArgs = cmExpandedList(eu);
652
    if (cvsArgs.size() == 2) {
653
      std::string fullCommand = cmStrCat(command, " update ", cvsArgs[1]);
654
      output.clear();
655
      retVal = 0;
656 657
      cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                 "Run Update: " << fullCommand << std::endl);
658
      res = cmSystemTools::RunSingleCommand(
659
        fullCommand, &output, &output, &retVal, cvsArgs[0].c_str(),
660
        this->HandlerVerbose, cmDuration::zero() /*this->TimeOut*/);
661
      if (!res || retVal != 0) {
662 663
        cmSystemTools::Error(cmStrCat("Unable to perform extra updates:\n", eu,
                                      "\nWith output:\n", output));
664
        return 0;
665 666
      }
    }
667
  }
668 669 670 671 672 673 674 675 676 677
  return 0;
}

// run a single dashboard entry
int cmCTestScriptHandler::RunConfigurationDashboard()
{
  // local variables
  std::string command;
  std::string output;
  int retVal;
Andy Cedilnik's avatar
Andy Cedilnik committed
678
  bool res;
679 680 681 682

  // make sure the src directory is there, if it isn't then we might be able
  // to check it out from cvs
  retVal = this->CheckOutSourceDir();
683
  if (retVal) {
684
    return retVal;
685
  }
686 687 688

  // backup the dirs if requested
  retVal = this->BackupDirectories();
689
  if (retVal) {
690
    return retVal;
691
  }
Andy Cedilnik's avatar
Andy Cedilnik committed
692

693
  // clear the binary directory?
694 695
  if (this->EmptyBinDir) {
    if (!cmCTestScriptHandler::EmptyBinaryDirectory(this->BinaryDir.c_str())) {
Andy Cedilnik's avatar
Andy Cedilnik committed
696
      cmCTestLog(this->CTest, ERROR_MESSAGE,
697
                 "Problem removing the binary directory" << std::endl);
698
    }
699
  }
Andy Cedilnik's avatar
Andy Cedilnik committed
700