cmCTestLaunch.cxx 20 KB

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