cmCTestLaunch.cxx 20 KB

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