cmCTest.cxx 98.8 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
#include "cmCTest.h"
Brad King's avatar
Brad King committed
4

5
#include <algorithm>
6
#include <cctype>
7
#include <chrono>
8 9 10 11
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
12 13 14 15 16 17
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
18 19 20 21 22 23 24 25 26 27

#include "cmsys/Base64.h"
#include "cmsys/Directory.hxx"
#include "cmsys/FStream.hxx"
#include "cmsys/Glob.hxx"
#include "cmsys/Process.h"
#include "cmsys/SystemInformation.hxx"

#include "cm_curl.h"
#include "cm_zlib.h"
28 29 30 31 32
#if defined(_WIN32)
#  include <windows.h> // IWYU pragma: keep
#else
#  include <unistd.h> // IWYU pragma: keep
#endif
33

34
#include <cm/memory>
35

36
#include "cmAlgorithms.h"
37
#include "cmCTestBuildAndTestHandler.h"
38
#include "cmCTestBuildHandler.h"
39
#include "cmCTestConfigureHandler.h"
Ken Martin's avatar
Ken Martin committed
40
#include "cmCTestCoverageHandler.h"
41
#include "cmCTestGenericHandler.h"
42
#include "cmCTestMemCheckHandler.h"
Ken Martin's avatar
Ken Martin committed
43
#include "cmCTestScriptHandler.h"
44
#include "cmCTestStartCommand.h"
Zach's avatar
Zach committed
45
#include "cmCTestSubmitHandler.h"
Ken Martin's avatar
Ken Martin committed
46
#include "cmCTestTestHandler.h"
Ken Martin's avatar
Ken Martin committed
47
#include "cmCTestUpdateHandler.h"
Zach's avatar
Zach committed
48
#include "cmCTestUploadHandler.h"
49 50 51 52
#include "cmDynamicLoader.h"
#include "cmGeneratedFileStream.h"
#include "cmGlobalGenerator.h"
#include "cmMakefile.h"
53
#include "cmProcessOutput.h"
54
#include "cmState.h"
55
#include "cmStateSnapshot.h"
56
#include "cmStateTypes.h"
57
#include "cmStringAlgorithms.h"
58
#include "cmSystemTools.h"
59
#include "cmVersion.h"
60 61 62
#include "cmVersionConfig.h"
#include "cmXMLWriter.h"
#include "cmake.h"
Andy Cedilnik's avatar
Andy Cedilnik committed
63

64
#if defined(__BEOS__) || defined(__HAIKU__)
65
#  include <be/kernel/OS.h> /* disable_debugger() API. */
66 67
#endif

68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
struct cmCTest::Private
{
  /** Representation of one part.  */
  struct PartInfo
  {
    void SetName(const std::string& name) { this->Name = name; }
    const std::string& GetName() const { return this->Name; }

    void Enable() { this->Enabled = true; }
    explicit operator bool() const { return this->Enabled; }

    std::vector<std::string> SubmitFiles;

  private:
    bool Enabled = false;
    std::string Name;
  };

86 87
  int RepeatTests = 1; // default to run each test once
  bool RepeatUntilFail = false;
88 89 90
  std::string ConfigType;
  std::string ScheduleType;
  std::chrono::system_clock::time_point StopTime;
91 92 93 94 95 96 97 98 99
  bool TestProgressOutput = false;
  bool Verbose = false;
  bool ExtraVerbose = false;
  bool ProduceXML = false;
  bool LabelSummary = true;
  bool SubprojectSummary = true;
  bool UseHTTP10 = false;
  bool PrintLabels = false;
  bool Failover = false;
100

101
  bool FlushTestProgressLine = false;
102

103
  bool ForceNewCTestProcess = false;
104

105
  bool RunConfigurationScript = false;
106 107

  // these are helper classes
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
  cmCTestBuildHandler BuildHandler;
  cmCTestBuildAndTestHandler BuildAndTestHandler;
  cmCTestCoverageHandler CoverageHandler;
  cmCTestScriptHandler ScriptHandler;
  cmCTestTestHandler TestHandler;
  cmCTestUpdateHandler UpdateHandler;
  cmCTestConfigureHandler ConfigureHandler;
  cmCTestMemCheckHandler MemCheckHandler;
  cmCTestSubmitHandler SubmitHandler;
  cmCTestUploadHandler UploadHandler;

  std::vector<cmCTestGenericHandler*> GetTestingHandlers()
  {
    return { &this->BuildHandler,     &this->BuildAndTestHandler,
             &this->CoverageHandler,  &this->ScriptHandler,
             &this->TestHandler,      &this->UpdateHandler,
             &this->ConfigureHandler, &this->MemCheckHandler,
             &this->SubmitHandler,    &this->UploadHandler };
  }

  std::map<std::string, cmCTestGenericHandler*> GetNamedTestingHandlers()
  {
    return { { "build", &this->BuildHandler },
             { "buildtest", &this->BuildAndTestHandler },
             { "coverage", &this->CoverageHandler },
             { "script", &this->ScriptHandler },
             { "test", &this->TestHandler },
             { "update", &this->UpdateHandler },
             { "configure", &this->ConfigureHandler },
             { "memcheck", &this->MemCheckHandler },
             { "submit", &this->SubmitHandler },
             { "upload", &this->UploadHandler } };
  }
141

142 143 144
  bool ShowOnly = false;
  bool OutputAsJson = false;
  int OutputAsJsonVersion = 1;
145 146 147 148 149

  // TODO: The ctest configuration should be a hierarchy of
  // configuration option sources: command-line, script, ini file.
  // Then the ini file can get re-loaded whenever it changes without
  // affecting any higher-precedence settings.
wahikihiki's avatar
wahikihiki committed
150 151 152
  std::map<std::string, std::string> CTestConfiguration;
  std::map<std::string, std::string> CTestConfigurationOverwrites;

153
  PartInfo Parts[PartCount];
wahikihiki's avatar
wahikihiki committed
154
  std::map<std::string, Part> PartMap;
155 156

  std::string CurrentTag;
157
  bool TomorrowTag = false;
158

159
  int TestModel = cmCTest::EXPERIMENTAL;
Zack Galbreath's avatar
Zack Galbreath committed
160
  std::string SpecificGroup;
161

162
  cmDuration TimeOut = cmDuration::zero();
163

164
  cmDuration GlobalTimeout = cmDuration::zero();
165

166
  int MaxTestNameWidth = 30;
167

168 169
  int ParallelLevel = 1;
  bool ParallelLevelSetInCli = false;
170

171
  unsigned long TestLoad = 0;
172 173 174 175 176 177 178 179

  int CompatibilityMode;

  // information for the --build-and-test options
  std::string BinaryDir;

  std::string NotesFiles;

180
  bool InteractiveDebugMode = true;
181

182
  bool ShortDateFormat = true;
183

184 185
  bool CompressXMLFiles = false;
  bool CompressTestOutput = true;
186

187 188 189
  // By default we write output to the process output streams.
  std::ostream* StreamOut = &std::cout;
  std::ostream* StreamErr = &std::cerr;
190

191
  bool SuppressUpdatingCTestConfiguration = false;
192

193 194 195
  bool Debug = false;
  bool ShowLineNumbers = false;
  bool Quiet = false;
196 197 198 199 200

  std::string BuildID;

  std::vector<std::string> InitialCommandLineArguments;

201
  int SubmitIndex = 0;
202

203 204
  cmGeneratedFileStream* OutputLogFile = nullptr;
  int OutputLogFileLastTag = -1;
205

206 207
  bool OutputTestOutputOnTestFailure = false;
  bool OutputColorCode = cmCTest::ColoredOutputSupportedByConsole();
208 209 210 211

  std::map<std::string, std::string> Definitions;
};

212
struct tm* cmCTest::GetNightlyTime(std::string const& str, bool tomorrowtag)
Andy Cedilnik's avatar
Andy Cedilnik committed
213 214
{
  struct tm* lctime;
Daniel Pfeifer's avatar
Daniel Pfeifer committed
215
  time_t tctime = time(nullptr);
216 217 218 219
  lctime = gmtime(&tctime);
  char buf[1024];
  // add todays year day and month to the time in str because
  // curl_getdate no longer assumes the day is today
220 221
  sprintf(buf, "%d%02d%02d %s", lctime->tm_year + 1900, lctime->tm_mon + 1,
          lctime->tm_mday, str.c_str());
222 223 224 225
  cmCTestLog(this, OUTPUT,
             "Determine Nightly Start Time" << std::endl
                                            << "   Specified time: " << str
                                            << std::endl);
226 227 228 229 230 231
  // Convert the nightly start time to seconds. Since we are
  // providing only a time and a timezone, the current date of
  // the local machine is assumed. Consequently, nightlySeconds
  // is the time at which the nightly dashboard was opened or
  // will be opened on the date of the current client machine.
  // As such, this time may be in the past or in the future.
232
  time_t ntime = curl_getdate(buf, &tctime);
233
  cmCTestLog(this, DEBUG, "   Get curl time: " << ntime << std::endl);
Daniel Pfeifer's avatar
Daniel Pfeifer committed
234
  tctime = time(nullptr);
235
  cmCTestLog(this, DEBUG, "   Get the current time: " << tctime << std::endl);
236 237

  const int dayLength = 24 * 60 * 60;
238
  cmCTestLog(this, DEBUG, "Seconds: " << tctime << std::endl);
239
  while (ntime > tctime) {
Andy Cedilnik's avatar
Andy Cedilnik committed
240 241 242 243 244
    // If nightlySeconds is in the past, this is the current
    // open dashboard, then return nightlySeconds.  If
    // nightlySeconds is in the future, this is the next
    // dashboard to be opened, so subtract 24 hours to get the
    // time of the current open dashboard
245
    ntime -= dayLength;
246
    cmCTestLog(this, DEBUG, "Pick yesterday" << std::endl);
247 248
    cmCTestLog(this, DEBUG,
               "   Future time, subtract day: " << ntime << std::endl);
249 250
  }
  while (tctime > (ntime + dayLength)) {
251
    ntime += dayLength;
252
    cmCTestLog(this, DEBUG, "   Past time, add day: " << ntime << std::endl);
253
  }
254
  cmCTestLog(this, DEBUG, "nightlySeconds: " << ntime << std::endl);
255 256 257
  cmCTestLog(this, DEBUG,
             "   Current time: " << tctime << " Nightly time: " << ntime
                                 << std::endl);
258
  if (tomorrowtag) {
259
    cmCTestLog(this, OUTPUT, "   Use future tag, Add a day" << std::endl);
260
    ntime += dayLength;
261
  }
Andy Cedilnik's avatar
Andy Cedilnik committed
262 263 264 265
  lctime = gmtime(&ntime);
  return lctime;
}

266 267
bool cmCTest::GetTomorrowTag() const
{
268
  return this->Impl->TomorrowTag;
269 270
}

Ken Martin's avatar
Ken Martin committed
271
std::string cmCTest::CleanString(const std::string& str)
272
{
273 274
  std::string::size_type spos = str.find_first_not_of(" \n\t\r\f\v");
  std::string::size_type epos = str.find_last_not_of(" \n\t\r\f\v");
275
  if (spos == std::string::npos) {
276
    return std::string();
277
  }
278
  if (epos != std::string::npos) {
279
    epos = epos - spos + 1;
280
  }
281 282 283
  return str.substr(spos, epos);
}

284
std::string cmCTest::CurrentTime()
285
{
Daniel Pfeifer's avatar
Daniel Pfeifer committed
286
  time_t currenttime = time(nullptr);
287
  struct tm* t = localtime(&currenttime);
288
  // return ::CleanString(ctime(&currenttime));
289
  char current_time[1024];
290
  if (this->Impl->ShortDateFormat) {
Andy Cedilnik's avatar
Andy Cedilnik committed
291
    strftime(current_time, 1000, "%b %d %H:%M %Z", t);
292
  } else {
Andy Cedilnik's avatar
Andy Cedilnik committed
293
    strftime(current_time, 1000, "%a %b %d %H:%M:%S %Z %Y", t);
294
  }
295
  cmCTestLog(this, DEBUG, "   Current_Time: " << current_time << std::endl);
296
  return cmCTest::CleanString(current_time);
297 298
}

299 300 301
std::string cmCTest::GetCostDataFile()
{
  std::string fname = this->GetCTestConfiguration("CostDataFile");
302
  if (fname.empty()) {
303 304
    fname = this->GetBinaryDir() + "/Testing/Temporary/CTestCostData.txt";
  }
305 306 307
  return fname;
}

Brad King's avatar
Brad King committed
308 309 310
std::string cmCTest::DecodeURL(const std::string& in)
{
  std::string out;
311 312 313
  for (const char* c = in.c_str(); *c; ++c) {
    if (*c == '%' && isxdigit(*(c + 1)) && isxdigit(*(c + 2))) {
      char buf[3] = { *(c + 1), *(c + 2), 0 };
Daniel Pfeifer's avatar
Daniel Pfeifer committed
314
      out.append(1, char(strtoul(buf, nullptr, 16)));
Brad King's avatar
Brad King committed
315
      c += 2;
316
    } else {
Brad King's avatar
Brad King committed
317 318
      out.append(1, *c);
    }
319
  }
Brad King's avatar
Brad King committed
320 321 322
  return out;
}

Andy Cedilnik's avatar
Andy Cedilnik committed
323
cmCTest::cmCTest()
324 325
  : Impl(new Private)
{
326 327
  std::string envValue;
  if (cmSystemTools::GetEnv("CTEST_OUTPUT_ON_FAILURE", envValue)) {
328
    this->Impl->OutputTestOutputOnTestFailure = !cmIsOff(envValue);
329 330 331
  }
  envValue.clear();
  if (cmSystemTools::GetEnv("CTEST_PROGRESS_OUTPUT", envValue)) {
332
    this->Impl->TestProgressOutput = !cmIsOff(envValue);
333
  }
334

335 336 337 338 339 340 341 342 343 344 345 346
  this->Impl->Parts[PartStart].SetName("Start");
  this->Impl->Parts[PartUpdate].SetName("Update");
  this->Impl->Parts[PartConfigure].SetName("Configure");
  this->Impl->Parts[PartBuild].SetName("Build");
  this->Impl->Parts[PartTest].SetName("Test");
  this->Impl->Parts[PartCoverage].SetName("Coverage");
  this->Impl->Parts[PartMemCheck].SetName("MemCheck");
  this->Impl->Parts[PartSubmit].SetName("Submit");
  this->Impl->Parts[PartNotes].SetName("Notes");
  this->Impl->Parts[PartExtraFiles].SetName("ExtraFiles");
  this->Impl->Parts[PartUpload].SetName("Upload");
  this->Impl->Parts[PartDone].SetName("Done");
347 348

  // Fill the part name-to-id map.
349
  for (Part p = PartStart; p != PartCount; p = Part(p + 1)) {
350 351
    this->Impl
      ->PartMap[cmSystemTools::LowerCase(this->Impl->Parts[p].GetName())] = p;
352
  }
353

354 355
  for (auto& handler : this->Impl->GetTestingHandlers()) {
    handler->SetCTestInstance(this);
356
  }
357

358 359
  // Make sure we can capture the build tool output.
  cmSystemTools::EnableVSConsoleOutput();
Ken Martin's avatar
Ken Martin committed
360 361
}

Andy Cedilnik's avatar
Andy Cedilnik committed
362 363
cmCTest::~cmCTest()
{
364
  delete this->Impl->OutputLogFile;
365 366
}

367 368
int cmCTest::GetParallelLevel() const
{
369
  return this->Impl->ParallelLevel;
370 371
}

372 373
void cmCTest::SetParallelLevel(int level)
{
374
  this->Impl->ParallelLevel = level < 1 ? 1 : level;
375 376
}

377 378
unsigned long cmCTest::GetTestLoad() const
{
379
  return this->Impl->TestLoad;
380 381
}

382 383
void cmCTest::SetTestLoad(unsigned long load)
{
384
  this->Impl->TestLoad = load;
385 386
}

387 388
bool cmCTest::ShouldCompressTestOutput()
{
389
  return this->Impl->CompressTestOutput;
390 391
}

392 393 394 395
cmCTest::Part cmCTest::GetPartFromName(const char* name)
{
  // Look up by lower-case to make names case-insensitive.
  std::string lower_name = cmSystemTools::LowerCase(name);
396 397
  auto const i = this->Impl->PartMap.find(lower_name);
  if (i != this->Impl->PartMap.end()) {
398
    return i->second;
399
  }
400 401 402 403 404

  // The string does not name a valid part.
  return PartCount;
}

405
int cmCTest::Initialize(const char* binary_dir, cmCTestStartCommand* command)
406
{
Zack Galbreath's avatar
Zack Galbreath committed
407
  bool quiet = false;
408
  if (command && command->ShouldBeQuiet()) {
Zack Galbreath's avatar
Zack Galbreath committed
409
    quiet = true;
410
  }
Zack Galbreath's avatar
Zack Galbreath committed
411 412

  cmCTestOptionalLog(this, DEBUG, "Here: " << __LINE__ << std::endl, quiet);
413
  if (!this->Impl->InteractiveDebugMode) {
414
    this->BlockTestErrorDiagnostics();
415
  } else {
416
    cmSystemTools::PutEnv("CTEST_INTERACTIVE_DEBUG_MODE=1");
417
  }
Andy Cedilnik's avatar
Andy Cedilnik committed
418

419 420
  this->Impl->BinaryDir = binary_dir;
  cmSystemTools::ConvertToUnixSlashes(this->Impl->BinaryDir);
421 422 423

  this->UpdateCTestConfiguration();

Zack Galbreath's avatar
Zack Galbreath committed
424
  cmCTestOptionalLog(this, DEBUG, "Here: " << __LINE__ << std::endl, quiet);
425
  if (this->Impl->ProduceXML) {
Zack Galbreath's avatar
Zack Galbreath committed
426
    cmCTestOptionalLog(this, DEBUG, "Here: " << __LINE__ << std::endl, quiet);
427 428 429 430 431 432 433 434
    cmCTestOptionalLog(this, OUTPUT,
                       "   Site: "
                         << this->GetCTestConfiguration("Site") << std::endl
                         << "   Build name: "
                         << cmCTest::SafeBuildIdField(
                              this->GetCTestConfiguration("BuildName"))
                         << std::endl,
                       quiet);
Zack Galbreath's avatar
Zack Galbreath committed
435
    cmCTestOptionalLog(this, DEBUG, "Produce XML is on" << std::endl, quiet);
436
    if (this->Impl->TestModel == cmCTest::NIGHTLY &&
437 438 439
        this->GetCTestConfiguration("NightlyStartTime").empty()) {
      cmCTestOptionalLog(
        this, WARNING,
Zack Galbreath's avatar
Zack Galbreath committed
440
        "WARNING: No nightly start time found please set in CTestConfig.cmake"
441 442
        " or DartConfig.cmake"
          << std::endl,
Zack Galbreath's avatar
Zack Galbreath committed
443
        quiet);
444 445
      cmCTestOptionalLog(this, DEBUG, "Here: " << __LINE__ << std::endl,
                         quiet);
446 447
      return 0;
    }
448
  }
449

450
  cmake cm(cmake::RoleScript, cmState::CTest);
451 452
  cm.SetHomeDirectory("");
  cm.SetHomeOutputDirectory("");
453
  cm.GetCurrentSnapshot().SetDefaultDefinitions();
454
  cmGlobalGenerator gg(&cm);
455
  cmMakefile mf(&gg, cm.GetCurrentSnapshot());
456 457
  if (!this->ReadCustomConfigurationFileTree(this->Impl->BinaryDir.c_str(),
                                             &mf)) {
458 459 460
    cmCTestOptionalLog(
      this, DEBUG, "Cannot find custom configuration file tree" << std::endl,
      quiet);
461
    return 0;
462
  }
463

464
  if (this->Impl->ProduceXML) {
465 466
    // Verify "Testing" directory exists:
    //
467
    std::string testingDir = this->Impl->BinaryDir + "/Testing";
468
    if (cmSystemTools::FileExists(testingDir)) {
469
      if (!cmSystemTools::FileIsDirectory(testingDir)) {
470 471 472 473
        cmCTestLog(this, ERROR_MESSAGE,
                   "File " << testingDir
                           << " is in the place of the testing directory"
                           << std::endl);
474
        return 0;
475
      }
476
    } else {
477
      if (!cmSystemTools::MakeDirectory(testingDir)) {
478 479
        cmCTestLog(this, ERROR_MESSAGE,
                   "Cannot create directory " << testingDir << std::endl);
480
        return 0;
481
      }
482
    }
483 484 485

    // Create new "TAG" file or read existing one:
    //
486
    bool createNewTag = true;
487
    if (command) {
488
      createNewTag = command->ShouldCreateNewTag();
489
    }
490

491
    std::string tagfile = testingDir + "/TAG";
492
    cmsys::ifstream tfin(tagfile.c_str());
493
    std::string tag;
494

495
    if (createNewTag) {
Daniel Pfeifer's avatar
Daniel Pfeifer committed
496
      time_t tctime = time(nullptr);
497
      if (this->Impl->TomorrowTag) {
498 499 500 501
        tctime += (24 * 60 * 60);
      }
      struct tm* lctime = gmtime(&tctime);
      if (tfin && cmSystemTools::GetLineFromStream(tfin, tag)) {
502 503 504 505 506
        int year = 0;
        int mon = 0;
        int day = 0;
        int hour = 0;
        int min = 0;
507 508 509 510
        sscanf(tag.c_str(), "%04d%02d%02d-%02d%02d", &year, &mon, &day, &hour,
               &min);
        if (year != lctime->tm_year + 1900 || mon != lctime->tm_mon + 1 ||
            day != lctime->tm_mday) {
511
          tag.clear();
512
        }
Zack Galbreath's avatar
Zack Galbreath committed
513 514
        std::string group;
        if (cmSystemTools::GetLineFromStream(tfin, group) &&
515
            !this->Impl->Parts[PartStart] && !command) {
Zack Galbreath's avatar
Zack Galbreath committed
516
          this->Impl->SpecificGroup = group;
517 518
        }
        std::string model;
519
        if (cmSystemTools::GetLineFromStream(tfin, model) &&
520 521
            !this->Impl->Parts[PartStart] && !command) {
          this->Impl->TestModel = GetTestModelFromString(model.c_str());
522
        }
523 524
        tfin.close();
      }
525 526
      if (tag.empty() || (nullptr != command) ||
          this->Impl->Parts[PartStart]) {
527 528 529
        cmCTestOptionalLog(
          this, DEBUG,
          "TestModel: " << this->GetTestModelString() << std::endl, quiet);
530 531 532 533
        cmCTestOptionalLog(this, DEBUG,
                           "TestModel: " << this->Impl->TestModel << std::endl,
                           quiet);
        if (this->Impl->TestModel == cmCTest::NIGHTLY) {
534
          lctime = this->GetNightlyTime(
Bill Hoffman's avatar
Bill Hoffman committed
535
            this->GetCTestConfiguration("NightlyStartTime"),
536
            this->Impl->TomorrowTag);
537
        }
538
        char datestring[100];
539 540
        sprintf(datestring, "%04d%02d%02d-%02d%02d", lctime->tm_year + 1900,
                lctime->tm_mon + 1, lctime->tm_mday, lctime->tm_hour,
541 542
                lctime->tm_min);
        tag = datestring;
543
        cmsys::ofstream ofs(tagfile.c_str());
544
        if (ofs) {
545 546
          ofs << tag << std::endl;
          ofs << this->GetTestModelString() << std::endl;
547
          switch (this->Impl->TestModel) {
548 549 550 551 552 553 554 555 556 557
            case cmCTest::EXPERIMENTAL:
              ofs << "Experimental" << std::endl;
              break;
            case cmCTest::NIGHTLY:
              ofs << "Nightly" << std::endl;
              break;
            case cmCTest::CONTINUOUS:
              ofs << "Continuous" << std::endl;
              break;
          }
558
        }
559
        ofs.close();
Daniel Pfeifer's avatar
Daniel Pfeifer committed
560
        if (nullptr == command) {
561 562 563 564
          cmCTestOptionalLog(this, OUTPUT,
                             "Create new tag: " << tag << " - "
                                                << this->GetTestModelString()
                                                << std::endl,
565
                             quiet);
566
        }
567
      }
568
    } else {
Zack Galbreath's avatar
Zack Galbreath committed
569
      std::string group;
570 571 572
      std::string modelStr;
      int model = cmCTest::UNKNOWN;

573
      if (tfin) {
574
        cmSystemTools::GetLineFromStream(tfin, tag);
Zack Galbreath's avatar
Zack Galbreath committed
575
        cmSystemTools::GetLineFromStream(tfin, group);
576 577 578
        if (cmSystemTools::GetLineFromStream(tfin, modelStr)) {
          model = GetTestModelFromString(modelStr.c_str());
        }
579
        tfin.close();
580
      }
581

582
      if (tag.empty()) {
583 584 585
        cmCTestLog(this, ERROR_MESSAGE,
                   "Cannot read existing TAG file in " << testingDir
                                                       << std::endl);
586
        return 0;
587
      }
588

589
      if (this->Impl->TestModel == cmCTest::UNKNOWN) {
590 591 592 593 594 595 596 597 598 599 600
        if (model == cmCTest::UNKNOWN) {
          cmCTestLog(this, ERROR_MESSAGE,
                     "TAG file does not contain model and "
                     "no model specified in start command"
                       << std::endl);
          return 0;
        }

        this->SetTestModel(model);
      }

601 602
      if (model != this->Impl->TestModel && model != cmCTest::UNKNOWN &&
          this->Impl->TestModel != cmCTest::UNKNOWN) {
603 604 605
        cmCTestOptionalLog(this, WARNING,
                           "Model given in TAG does not match "
                           "model given in ctest_start()"
606 607 608 609
                             << std::endl,
                           quiet);
      }

Zack Galbreath's avatar
Zack Galbreath committed
610 611
      if (!this->Impl->SpecificGroup.empty() &&
          group != this->Impl->SpecificGroup) {
612
        cmCTestOptionalLog(this, WARNING,
Zack Galbreath's avatar
Zack Galbreath committed
613 614
                           "Group given in TAG does not match "
                           "group given in ctest_start()"
615 616 617
                             << std::endl,
                           quiet);
      } else {
Zack Galbreath's avatar
Zack Galbreath committed
618
        this->Impl->SpecificGroup = group;
619 620
      }

621 622 623 624
      cmCTestOptionalLog(this, OUTPUT,
                         "  Use existing tag: " << tag << " - "
                                                << this->GetTestModelString()
                                                << std::endl,
625
                         quiet);
626
    }
627

628
    this->Impl->CurrentTag = tag;
629 630
  }

631
  return 1;
632 633
}

634
bool cmCTest::InitializeFromCommand(cmCTestStartCommand* command)
635
{
636
  std::string src_dir = this->GetCTestConfiguration("SourceDirectory");
Stephen Kelly's avatar
Stephen Kelly committed
637
  std::string bld_dir = this->GetCTestConfiguration("BuildDirectory");
638
  this->Impl->BuildID = "";
639
  for (Part p = PartStart; p != PartCount; p = Part(p + 1)) {
640
    this->Impl->Parts[p].SubmitFiles.clear();
641
  }
642 643

  cmMakefile* mf = command->GetMakefile();
644 645
  std::string fname;

646
  std::string src_dir_fname = cmStrCat(src_dir, "/CTestConfig.cmake");
647 648
  cmSystemTools::ConvertToUnixSlashes(src_dir_fname);

649
  std::string bld_dir_fname = cmStrCat(bld_dir, "/CTestConfig.cmake");
650 651
  cmSystemTools::ConvertToUnixSlashes(bld_dir_fname);

652
  if (cmSystemTools::FileExists(bld_dir_fname)) {
653
    fname = bld_dir_fname;
654
  } else if (cmSystemTools::FileExists(src_dir_fname)) {
655
    fname = src_dir_fname;
656
  }
657

658
  if (!fname.empty()) {
659 660 661
    cmCTestOptionalLog(this, OUTPUT,
                       "   Reading ctest configuration file: " << fname
                                                               << std::endl,
662
                       command->ShouldBeQuiet());
663
    bool readit = mf->ReadDependentFile(fname);
664
    if (!readit) {
665
      std::string m = cmStrCat("Could not find include file: ", fname);
Stephen Kelly's avatar
Stephen Kelly committed
666
      command->SetError(m);
667 668
      return false;
    }
669
  }
670

Andy Cedilnik's avatar
Andy Cedilnik committed
671
  this->SetCTestConfigurationFromCMakeVariable(mf, "NightlyStartTime",
672 673
                                               "CTEST_NIGHTLY_START_TIME",
                                               command->ShouldBeQuiet());
Zack Galbreath's avatar
Zack Galbreath committed
674
  this->SetCTestConfigurationFromCMakeVariable(mf, "Site", "CTEST_SITE",
675 676 677
                                               command->ShouldBeQuiet());
  this->SetCTestConfigurationFromCMakeVariable(
    mf, "BuildName", "CTEST_BUILD_NAME", command->ShouldBeQuiet());
678

679
  if (!this->Initialize(bld_dir.c_str(), command)) {
680
    return false;
681
  }
682 683 684
  cmCTestOptionalLog(this, OUTPUT,
                     "   Use " << this->GetTestModelString() << " tag: "
                               << this->GetCurrentTag() << std::endl,
685
                     command->ShouldBeQuiet());
686 687 688
  return true;
}

689
bool cmCTest::UpdateCTestConfiguration()
690
{
691
  if (this->Impl->SuppressUpdatingCTestConfiguration) {
692
    return true;
693
  }
694
  std::string fileName = this->Impl->BinaryDir + "/CTestConfiguration.ini";
695
  if (!cmSystemTools::FileExists(fileName)) {
696
    fileName = this->Impl->BinaryDir + "/DartConfiguration.tcl";
697 698 699
  }
  cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
             "UpdateCTestConfiguration  from :" << fileName << "\n");
700
  if (!cmSystemTools::FileExists(fileName)) {
701
    // No need to exit if we are not producing XML
702
    if (this->Impl->ProduceXML) {
703 704
      cmCTestLog(this, ERROR_MESSAGE,
                 "Cannot find file: " << fileName << std::endl);
705
      return false;
706
    }
707
  } else {
708 709
    cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
               "Parse Config file:" << fileName << "\n");
710
    // parse the dart test file
711
    cmsys::ifstream fin(fileName.c_str());
712

713
    if (!fin) {
714
      return false;
715
    }
716 717

    char buffer[1024];
718
    while (fin) {
719 720 721
      buffer[0] = 0;
      fin.getline(buffer, 1023);
      buffer[1023] = 0;
722
      std::string line = cmCTest::CleanString(buffer);
723
      if (line.empty()) {
724
        continue;
725
      }
726
      while (fin && (line.back() == '\\')) {
727
        line = line.substr(0, line.size() - 1);
728 729 730 731
        buffer[0] = 0;
        fin.getline(buffer, 1023);
        buffer[1023] = 0;
        line += cmCTest::CleanString(buffer);
732 733
      }
      if (line[0] == '#') {
734
        continue;
735
      }
736
      std::string::size_type cpos = line.find_first_of(':');
737
      if (cpos == std::string::npos) {
738
        continue;
739
      }
740
      std::