cmCTestLaunch.cxx 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717
  1. /*=========================================================================
  2. Program: CMake - Cross-Platform Makefile Generator
  3. Module: $RCSfile$
  4. Language: C++
  5. Date: $Date$
  6. Version: $Revision$
  7. Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
  8. See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
  9. This software is distributed WITHOUT ANY WARRANTY; without even
  10. the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  11. PURPOSE. See the above copyright notices for more information.
  12. =========================================================================*/
  13. #include "cmCTestLaunch.h"
  14. #include "cmGeneratedFileStream.h"
  15. #include "cmSystemTools.h"
  16. #include "cmXMLSafe.h"
  17. #include "cmake.h"
  18. #include <cmsys/MD5.h>
  19. #include <cmsys/Process.h>
  20. #include <cmsys/RegularExpression.hxx>
  21. //----------------------------------------------------------------------------
  22. cmCTestLaunch::cmCTestLaunch(int argc, const char* const* argv)
  23. {
  24. this->Passthru = true;
  25. this->Process = 0;
  26. this->ExitCode = 1;
  27. this->CWD = cmSystemTools::GetCurrentWorkingDirectory();
  28. if(!this->ParseArguments(argc, argv))
  29. {
  30. return;
  31. }
  32. this->ComputeFileNames();
  33. this->ScrapeRulesLoaded = false;
  34. this->HaveOut = false;
  35. this->HaveErr = false;
  36. this->Process = cmsysProcess_New();
  37. }
  38. //----------------------------------------------------------------------------
  39. cmCTestLaunch::~cmCTestLaunch()
  40. {
  41. cmsysProcess_Delete(this->Process);
  42. if(!this->Passthru)
  43. {
  44. cmSystemTools::RemoveFile(this->LogOut.c_str());
  45. cmSystemTools::RemoveFile(this->LogErr.c_str());
  46. }
  47. }
  48. //----------------------------------------------------------------------------
  49. bool cmCTestLaunch::ParseArguments(int argc, const char* const* argv)
  50. {
  51. // Launcher options occur first and are separated from the real
  52. // command line by a '--' option.
  53. enum Doing { DoingNone,
  54. DoingOutput,
  55. DoingSource,
  56. DoingLanguage,
  57. DoingTargetName,
  58. DoingTargetType,
  59. DoingBuildDir,
  60. DoingCount };
  61. Doing doing = DoingNone;
  62. int arg0 = 0;
  63. for(int i=1; !arg0 && i < argc; ++i)
  64. {
  65. const char* arg = argv[i];
  66. if(strcmp(arg, "--") == 0)
  67. {
  68. arg0 = i+1;
  69. }
  70. else if(strcmp(arg, "--output") == 0)
  71. {
  72. doing = DoingOutput;
  73. }
  74. else if(strcmp(arg, "--source") == 0)
  75. {
  76. doing = DoingSource;
  77. }
  78. else if(strcmp(arg, "--language") == 0)
  79. {
  80. doing = DoingLanguage;
  81. }
  82. else if(strcmp(arg, "--target-name") == 0)
  83. {
  84. doing = DoingTargetName;
  85. }
  86. else if(strcmp(arg, "--target-type") == 0)
  87. {
  88. doing = DoingTargetType;
  89. }
  90. else if(strcmp(arg, "--build-dir") == 0)
  91. {
  92. doing = DoingBuildDir;
  93. }
  94. else if(doing == DoingOutput)
  95. {
  96. this->OptionOutput = arg;
  97. doing = DoingNone;
  98. }
  99. else if(doing == DoingSource)
  100. {
  101. this->OptionSource = arg;
  102. doing = DoingNone;
  103. }
  104. else if(doing == DoingLanguage)
  105. {
  106. this->OptionLanguage = arg;
  107. if(this->OptionLanguage == "CXX")
  108. {
  109. this->OptionLanguage = "C++";
  110. }
  111. doing = DoingNone;
  112. }
  113. else if(doing == DoingTargetName)
  114. {
  115. this->OptionTargetName = arg;
  116. doing = DoingNone;
  117. }
  118. else if(doing == DoingTargetType)
  119. {
  120. this->OptionTargetType = arg;
  121. doing = DoingNone;
  122. }
  123. else if(doing == DoingBuildDir)
  124. {
  125. this->OptionBuildDir = arg;
  126. doing = DoingNone;
  127. }
  128. }
  129. // Extract the real command line.
  130. if(arg0)
  131. {
  132. this->RealArgC = argc - arg0;
  133. this->RealArgV = argv + arg0;
  134. for(int i=0; i < this->RealArgC; ++i)
  135. {
  136. this->HandleRealArg(this->RealArgV[i]);
  137. }
  138. return true;
  139. }
  140. else
  141. {
  142. this->RealArgC = 0;
  143. this->RealArgV = 0;
  144. std::cerr << "No launch/command separator ('--') found!\n";
  145. return false;
  146. }
  147. }
  148. //----------------------------------------------------------------------------
  149. void cmCTestLaunch::HandleRealArg(const char* arg)
  150. {
  151. #ifdef _WIN32
  152. // Expand response file arguments.
  153. if(arg[0] == '@' && cmSystemTools::FileExists(arg+1))
  154. {
  155. std::ifstream fin(arg+1);
  156. std::string line;
  157. while(cmSystemTools::GetLineFromStream(fin, line))
  158. {
  159. cmSystemTools::ParseWindowsCommandLine(line.c_str(), this->RealArgs);
  160. }
  161. return;
  162. }
  163. #endif
  164. this->RealArgs.push_back(arg);
  165. }
  166. //----------------------------------------------------------------------------
  167. void cmCTestLaunch::ComputeFileNames()
  168. {
  169. // We just passthru the behavior of the real command unless the
  170. // CTEST_LAUNCH_LOGS environment variable is set.
  171. const char* d = getenv("CTEST_LAUNCH_LOGS");
  172. if(!(d && *d))
  173. {
  174. return;
  175. }
  176. this->Passthru = false;
  177. // The environment variable specifies the directory into which we
  178. // generate build logs.
  179. this->LogDir = d;
  180. cmSystemTools::ConvertToUnixSlashes(this->LogDir);
  181. this->LogDir += "/";
  182. // We hash the input command working dir and command line to obtain
  183. // a repeatable and (probably) unique name for log files.
  184. char hash[32];
  185. cmsysMD5* md5 = cmsysMD5_New();
  186. cmsysMD5_Initialize(md5);
  187. cmsysMD5_Append(md5, (unsigned char const*)(this->CWD.c_str()), -1);
  188. for(std::vector<std::string>::const_iterator ai = this->RealArgs.begin();
  189. ai != this->RealArgs.end(); ++ai)
  190. {
  191. cmsysMD5_Append(md5, (unsigned char const*)ai->c_str(), -1);
  192. }
  193. cmsysMD5_FinalizeHex(md5, hash);
  194. cmsysMD5_Delete(md5);
  195. this->LogHash.assign(hash, 32);
  196. // We store stdout and stderr in temporary log files.
  197. this->LogOut = this->LogDir;
  198. this->LogOut += "launch-";
  199. this->LogOut += this->LogHash;
  200. this->LogOut += "-out.txt";
  201. this->LogErr = this->LogDir;
  202. this->LogErr += "launch-";
  203. this->LogErr += this->LogHash;
  204. this->LogErr += "-err.txt";
  205. }
  206. //----------------------------------------------------------------------------
  207. void cmCTestLaunch::RunChild()
  208. {
  209. // Prepare to run the real command.
  210. cmsysProcess* cp = this->Process;
  211. cmsysProcess_SetCommand(cp, this->RealArgV);
  212. std::ofstream fout;
  213. std::ofstream ferr;
  214. if(this->Passthru)
  215. {
  216. // In passthru mode we just share the output pipes.
  217. cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1);
  218. cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1);
  219. }
  220. else
  221. {
  222. // In full mode we record the child output pipes to log files.
  223. fout.open(this->LogOut.c_str(),
  224. std::ios::out | std::ios::binary);
  225. ferr.open(this->LogErr.c_str(),
  226. std::ios::out | std::ios::binary);
  227. }
  228. // Run the real command.
  229. cmsysProcess_Execute(cp);
  230. // Record child stdout and stderr if necessary.
  231. if(!this->Passthru)
  232. {
  233. char* data = 0;
  234. int length = 0;
  235. while(int p = cmsysProcess_WaitForData(cp, &data, &length, 0))
  236. {
  237. if(p == cmsysProcess_Pipe_STDOUT)
  238. {
  239. fout.write(data, length);
  240. std::cout.write(data, length);
  241. this->HaveOut = true;
  242. }
  243. else if(p == cmsysProcess_Pipe_STDERR)
  244. {
  245. ferr.write(data, length);
  246. std::cerr.write(data, length);
  247. this->HaveErr = true;
  248. }
  249. }
  250. }
  251. // Wait for the real command to finish.
  252. cmsysProcess_WaitForExit(cp, 0);
  253. this->ExitCode = cmsysProcess_GetExitValue(cp);
  254. }
  255. //----------------------------------------------------------------------------
  256. int cmCTestLaunch::Run()
  257. {
  258. if(!this->Process)
  259. {
  260. std::cerr << "Could not allocate cmsysProcess instance!\n";
  261. return -1;
  262. }
  263. this->RunChild();
  264. if(this->CheckResults())
  265. {
  266. return this->ExitCode;
  267. }
  268. this->LoadConfig();
  269. this->WriteXML();
  270. return this->ExitCode;
  271. }
  272. //----------------------------------------------------------------------------
  273. void cmCTestLaunch::LoadLabels()
  274. {
  275. if(this->OptionBuildDir.empty() || this->OptionTargetName.empty())
  276. {
  277. return;
  278. }
  279. // Labels are listed in per-target files.
  280. std::string fname = this->OptionBuildDir;
  281. fname += cmake::GetCMakeFilesDirectory();
  282. fname += "/";
  283. fname += this->OptionTargetName;
  284. fname += ".dir/Labels.txt";
  285. // We are interested in per-target labels for this source file.
  286. std::string source = this->OptionSource;
  287. cmSystemTools::ConvertToUnixSlashes(source);
  288. // Load the labels file.
  289. std::ifstream fin(fname.c_str(), std::ios::in | std::ios::binary);
  290. if(!fin) { return; }
  291. bool inTarget = true;
  292. bool inSource = false;
  293. std::string line;
  294. while(cmSystemTools::GetLineFromStream(fin, line))
  295. {
  296. if(line.empty() || line[0] == '#')
  297. {
  298. // Ignore blank and comment lines.
  299. continue;
  300. }
  301. else if(line[0] == ' ')
  302. {
  303. // Label lines appear indented by one space.
  304. if(inTarget || inSource)
  305. {
  306. this->Labels.insert(line.c_str()+1);
  307. }
  308. }
  309. else if(!this->OptionSource.empty() && !inSource)
  310. {
  311. // Non-indented lines specify a source file name. The first one
  312. // is the end of the target-wide labels. Use labels following a
  313. // matching source.
  314. inTarget = false;
  315. inSource = this->SourceMatches(line, source);
  316. }
  317. else
  318. {
  319. return;
  320. }
  321. }
  322. }
  323. //----------------------------------------------------------------------------
  324. bool cmCTestLaunch::SourceMatches(std::string const& lhs,
  325. std::string const& rhs)
  326. {
  327. // TODO: Case sensitivity, UseRelativePaths, etc. Note that both
  328. // paths in the comparison get generated by CMake. This is done for
  329. // every source in the target, so it should be efficient (cannot use
  330. // cmSystemTools::IsSameFile).
  331. return lhs == rhs;
  332. }
  333. //----------------------------------------------------------------------------
  334. bool cmCTestLaunch::IsError() const
  335. {
  336. return this->ExitCode != 0;
  337. }
  338. //----------------------------------------------------------------------------
  339. void cmCTestLaunch::WriteXML()
  340. {
  341. // Name the xml file.
  342. std::string logXML = this->LogDir;
  343. logXML += this->IsError()? "error-" : "warning-";
  344. logXML += this->LogHash;
  345. logXML += ".xml";
  346. // Use cmGeneratedFileStream to atomically create the report file.
  347. cmGeneratedFileStream fxml(logXML.c_str());
  348. fxml << "\t<Failure type=\""
  349. << (this->IsError()? "Error" : "Warning") << "\">\n";
  350. this->WriteXMLAction(fxml);
  351. this->WriteXMLCommand(fxml);
  352. this->WriteXMLResult(fxml);
  353. this->WriteXMLLabels(fxml);
  354. fxml << "\t</Failure>\n";
  355. }
  356. //----------------------------------------------------------------------------
  357. void cmCTestLaunch::WriteXMLAction(std::ostream& fxml)
  358. {
  359. fxml << "\t\t<!-- Meta-information about the build action -->\n";
  360. fxml << "\t\t<Action>\n";
  361. // TargetName
  362. if(!this->OptionTargetName.empty())
  363. {
  364. fxml << "\t\t\t<TargetName>"
  365. << cmXMLSafe(this->OptionTargetName)
  366. << "</TargetName>\n";
  367. }
  368. // Language
  369. if(!this->OptionLanguage.empty())
  370. {
  371. fxml << "\t\t\t<Language>"
  372. << cmXMLSafe(this->OptionLanguage)
  373. << "</Language>\n";
  374. }
  375. // SourceFile
  376. if(!this->OptionSource.empty())
  377. {
  378. std::string source = this->OptionSource;
  379. cmSystemTools::ConvertToUnixSlashes(source);
  380. // If file is in source tree use its relative location.
  381. if(cmSystemTools::FileIsFullPath(this->SourceDir.c_str()) &&
  382. cmSystemTools::FileIsFullPath(source.c_str()) &&
  383. cmSystemTools::IsSubDirectory(source.c_str(),
  384. this->SourceDir.c_str()))
  385. {
  386. source = cmSystemTools::RelativePath(this->SourceDir.c_str(),
  387. source.c_str());
  388. }
  389. fxml << "\t\t\t<SourceFile>"
  390. << cmXMLSafe(source)
  391. << "</SourceFile>\n";
  392. }
  393. // OutputFile
  394. if(!this->OptionOutput.empty())
  395. {
  396. fxml << "\t\t\t<OutputFile>"
  397. << cmXMLSafe(this->OptionOutput)
  398. << "</OutputFile>\n";
  399. }
  400. // OutputType
  401. const char* outputType = 0;
  402. if(!this->OptionTargetType.empty())
  403. {
  404. if(this->OptionTargetType == "EXECUTABLE")
  405. {
  406. outputType = "executable";
  407. }
  408. else if(this->OptionTargetType == "SHARED_LIBRARY")
  409. {
  410. outputType = "shared library";
  411. }
  412. else if(this->OptionTargetType == "MODULE_LIBRARY")
  413. {
  414. outputType = "module library";
  415. }
  416. else if(this->OptionTargetType == "STATIC_LIBRARY")
  417. {
  418. outputType = "static library";
  419. }
  420. }
  421. else if(!this->OptionSource.empty())
  422. {
  423. outputType = "object file";
  424. }
  425. if(outputType)
  426. {
  427. fxml << "\t\t\t<OutputType>"
  428. << cmXMLSafe(outputType)
  429. << "</OutputType>\n";
  430. }
  431. fxml << "\t\t</Action>\n";
  432. }
  433. //----------------------------------------------------------------------------
  434. void cmCTestLaunch::WriteXMLCommand(std::ostream& fxml)
  435. {
  436. fxml << "\n";
  437. fxml << "\t\t<!-- Details of command -->\n";
  438. fxml << "\t\t<Command>\n";
  439. if(!this->CWD.empty())
  440. {
  441. fxml << "\t\t\t<WorkingDirectory>"
  442. << cmXMLSafe(this->CWD)
  443. << "</WorkingDirectory>\n";
  444. }
  445. for(std::vector<std::string>::const_iterator ai = this->RealArgs.begin();
  446. ai != this->RealArgs.end(); ++ai)
  447. {
  448. fxml << "\t\t\t<Argument>"
  449. << cmXMLSafe(ai->c_str())
  450. << "</Argument>\n";
  451. }
  452. fxml << "\t\t</Command>\n";
  453. }
  454. //----------------------------------------------------------------------------
  455. void cmCTestLaunch::WriteXMLResult(std::ostream& fxml)
  456. {
  457. fxml << "\n";
  458. fxml << "\t\t<!-- Result of command -->\n";
  459. fxml << "\t\t<Result>\n";
  460. // StdOut
  461. fxml << "\t\t\t<StdOut>";
  462. this->DumpFileToXML(fxml, this->LogOut);
  463. fxml << "</StdOut>\n";
  464. // StdErr
  465. fxml << "\t\t\t<StdErr>";
  466. this->DumpFileToXML(fxml, this->LogErr);
  467. fxml << "</StdErr>\n";
  468. // ExitCondition
  469. fxml << "\t\t\t<ExitCondition>";
  470. cmsysProcess* cp = this->Process;
  471. switch (cmsysProcess_GetState(cp))
  472. {
  473. case cmsysProcess_State_Starting:
  474. fxml << "No process has been executed"; break;
  475. case cmsysProcess_State_Executing:
  476. fxml << "The process is still executing"; break;
  477. case cmsysProcess_State_Disowned:
  478. fxml << "Disowned"; break;
  479. case cmsysProcess_State_Killed:
  480. fxml << "Killed by parent"; break;
  481. case cmsysProcess_State_Expired:
  482. fxml << "Killed when timeout expired"; break;
  483. case cmsysProcess_State_Exited:
  484. fxml << this->ExitCode; break;
  485. case cmsysProcess_State_Exception:
  486. fxml << "Terminated abnormally: "
  487. << cmXMLSafe(cmsysProcess_GetExceptionString(cp)); break;
  488. case cmsysProcess_State_Error:
  489. fxml << "Error administrating child process: "
  490. << cmXMLSafe(cmsysProcess_GetErrorString(cp)); break;
  491. };
  492. fxml << "</ExitCondition>\n";
  493. fxml << "\t\t</Result>\n";
  494. }
  495. //----------------------------------------------------------------------------
  496. void cmCTestLaunch::WriteXMLLabels(std::ostream& fxml)
  497. {
  498. this->LoadLabels();
  499. if(!this->Labels.empty())
  500. {
  501. fxml << "\n";
  502. fxml << "\t\t<!-- Interested parties -->\n";
  503. fxml << "\t\t<Labels>\n";
  504. for(std::set<cmStdString>::const_iterator li = this->Labels.begin();
  505. li != this->Labels.end(); ++li)
  506. {
  507. fxml << "\t\t\t<Label>" << cmXMLSafe(*li) << "</Label>\n";
  508. }
  509. fxml << "\t\t</Labels>\n";
  510. }
  511. }
  512. //----------------------------------------------------------------------------
  513. void cmCTestLaunch::DumpFileToXML(std::ostream& fxml,
  514. std::string const& fname)
  515. {
  516. std::ifstream fin(fname.c_str(), std::ios::in | std::ios::binary);
  517. std::string line;
  518. const char* sep = "";
  519. while(cmSystemTools::GetLineFromStream(fin, line))
  520. {
  521. fxml << sep << cmXMLSafe(line).Quotes(false);
  522. sep = "\n";
  523. }
  524. }
  525. //----------------------------------------------------------------------------
  526. bool cmCTestLaunch::CheckResults()
  527. {
  528. // Skip XML in passthru mode.
  529. if(this->Passthru)
  530. {
  531. return true;
  532. }
  533. // We always report failure for error conditions.
  534. if(this->IsError())
  535. {
  536. return false;
  537. }
  538. // Scrape the output logs to look for warnings.
  539. if((this->HaveErr && this->ScrapeLog(this->LogErr)) ||
  540. (this->HaveOut && this->ScrapeLog(this->LogOut)))
  541. {
  542. return false;
  543. }
  544. return true;
  545. }
  546. //----------------------------------------------------------------------------
  547. void cmCTestLaunch::LoadScrapeRules()
  548. {
  549. if(this->ScrapeRulesLoaded)
  550. {
  551. return;
  552. }
  553. this->ScrapeRulesLoaded = true;
  554. // Common compiler warning formats. These are much simpler than the
  555. // full log-scraping expressions because we do not need to extract
  556. // file and line information.
  557. this->RegexWarning.push_back("(^|[ :])[Ww][Aa][Rr][Nn][Ii][Nn][Gg]");
  558. this->RegexWarning.push_back("(^|[ :])[Rr][Ee][Mm][Aa][Rr][Kk]");
  559. this->RegexWarning.push_back("(^|[ :])[Nn][Oo][Tt][Ee]");
  560. // Load custom match rules given to us by CTest.
  561. this->LoadScrapeRules("Warning", this->RegexWarning);
  562. this->LoadScrapeRules("WarningSuppress", this->RegexWarningSuppress);
  563. }
  564. //----------------------------------------------------------------------------
  565. void
  566. cmCTestLaunch
  567. ::LoadScrapeRules(const char* purpose,
  568. std::vector<cmsys::RegularExpression>& regexps)
  569. {
  570. std::string fname = this->LogDir;
  571. fname += "Custom";
  572. fname += purpose;
  573. fname += ".txt";
  574. std::ifstream fin(fname.c_str(), std::ios::in | std::ios::binary);
  575. std::string line;
  576. cmsys::RegularExpression rex;
  577. while(cmSystemTools::GetLineFromStream(fin, line))
  578. {
  579. if(rex.compile(line.c_str()))
  580. {
  581. regexps.push_back(rex);
  582. }
  583. }
  584. }
  585. //----------------------------------------------------------------------------
  586. bool cmCTestLaunch::ScrapeLog(std::string const& fname)
  587. {
  588. this->LoadScrapeRules();
  589. // Look for log file lines matching warning expressions but not
  590. // suppression expressions.
  591. std::ifstream fin(fname.c_str(), std::ios::in | std::ios::binary);
  592. std::string line;
  593. while(cmSystemTools::GetLineFromStream(fin, line))
  594. {
  595. if(this->Match(line.c_str(), this->RegexWarning) &&
  596. !this->Match(line.c_str(), this->RegexWarningSuppress))
  597. {
  598. return true;
  599. }
  600. }
  601. return false;
  602. }
  603. //----------------------------------------------------------------------------
  604. bool cmCTestLaunch::Match(std::string const& line,
  605. std::vector<cmsys::RegularExpression>& regexps)
  606. {
  607. for(std::vector<cmsys::RegularExpression>::iterator ri = regexps.begin();
  608. ri != regexps.end(); ++ri)
  609. {
  610. if(ri->find(line.c_str()))
  611. {
  612. return true;
  613. }
  614. }
  615. return false;
  616. }
  617. //----------------------------------------------------------------------------
  618. int cmCTestLaunch::Main(int argc, const char* const argv[])
  619. {
  620. if(argc == 2)
  621. {
  622. std::cerr << "ctest --launch: this mode is for internal CTest use only"
  623. << std::endl;
  624. return 1;
  625. }
  626. cmCTestLaunch self(argc, argv);
  627. return self.Run();
  628. }
  629. //----------------------------------------------------------------------------
  630. #include "cmGlobalGenerator.h"
  631. #include "cmLocalGenerator.h"
  632. #include "cmMakefile.h"
  633. #include "cmake.h"
  634. #include <cmsys/auto_ptr.hxx>
  635. void cmCTestLaunch::LoadConfig()
  636. {
  637. cmake cm;
  638. cmGlobalGenerator gg;
  639. gg.SetCMakeInstance(&cm);
  640. cmsys::auto_ptr<cmLocalGenerator> lg(gg.CreateLocalGenerator());
  641. cmMakefile* mf = lg->GetMakefile();
  642. std::string fname = this->LogDir;
  643. fname += "CTestLaunchConfig.cmake";
  644. if(cmSystemTools::FileExists(fname.c_str()) &&
  645. mf->ReadListFile(0, fname.c_str()))
  646. {
  647. this->SourceDir = mf->GetSafeDefinition("CTEST_SOURCE_DIRECTORY");
  648. cmSystemTools::ConvertToUnixSlashes(this->SourceDir);
  649. }
  650. }