cmCTestCoverageHandler.cxx 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061
  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 "cmCTestCoverageHandler.h"
  14. #include "cmCTest.h"
  15. #include "cmake.h"
  16. #include "cmSystemTools.h"
  17. #include "cmGeneratedFileStream.h"
  18. #include <cmsys/Process.h>
  19. #include <cmsys/RegularExpression.hxx>
  20. #include <cmsys/Glob.hxx>
  21. #include <stdlib.h>
  22. #include <math.h>
  23. #include <float.h>
  24. #define SAFEDIV(x,y) (((y)!=0)?((x)/(y)):(0))
  25. //----------------------------------------------------------------------
  26. //**********************************************************************
  27. class cmCTestCoverageHandlerContainer
  28. {
  29. public:
  30. int Error;
  31. std::string SourceDir;
  32. std::string BinaryDir;
  33. typedef std::vector<int> SingleFileCoverageVector;
  34. typedef std::map<std::string, SingleFileCoverageVector> TotalCoverageMap;
  35. TotalCoverageMap TotalCoverage;
  36. std::ostream* OFS;
  37. };
  38. //**********************************************************************
  39. //----------------------------------------------------------------------
  40. //----------------------------------------------------------------------
  41. cmCTestCoverageHandler::cmCTestCoverageHandler()
  42. {
  43. }
  44. //----------------------------------------------------------------------
  45. void cmCTestCoverageHandler::Initialize()
  46. {
  47. this->Superclass::Initialize();
  48. this->CustomCoverageExclude.empty();
  49. }
  50. //----------------------------------------------------------------------
  51. bool cmCTestCoverageHandler::StartCoverageLogFile(
  52. cmGeneratedFileStream& covLogFile, int logFileCount)
  53. {
  54. char covLogFilename[1024];
  55. sprintf(covLogFilename, "CoverageLog-%d", logFileCount);
  56. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Open file: "
  57. << covLogFilename << std::endl);
  58. if (!this->StartResultingXML(covLogFilename, covLogFile) )
  59. {
  60. cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot open log file: "
  61. << covLogFilename << std::endl);
  62. return false;
  63. }
  64. std::string local_start_time = this->CTest->CurrentTime();
  65. this->CTest->StartXML(covLogFile);
  66. covLogFile << "<CoverageLog>" << std::endl
  67. << "\t<StartDateTime>" << local_start_time << "</StartDateTime>"
  68. << std::endl;
  69. return true;
  70. }
  71. //----------------------------------------------------------------------
  72. void cmCTestCoverageHandler::EndCoverageLogFile(cmGeneratedFileStream& ostr,
  73. int logFileCount)
  74. {
  75. std::string local_end_time = this->CTest->CurrentTime();
  76. ostr << "\t<EndDateTime>" << local_end_time << "</EndDateTime>" << std::endl
  77. << "</CoverageLog>" << std::endl;
  78. this->CTest->EndXML(ostr);
  79. char covLogFilename[1024];
  80. sprintf(covLogFilename, "CoverageLog-%d.xml", logFileCount);
  81. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Close file: "
  82. << covLogFilename << std::endl);
  83. ostr.Close();
  84. }
  85. //----------------------------------------------------------------------
  86. bool cmCTestCoverageHandler::ShouldIDoCoverage(const char* file,
  87. const char* srcDir,
  88. const char* binDir)
  89. {
  90. std::vector<cmsys::RegularExpression>::iterator sit;
  91. for ( sit = this->CustomCoverageExcludeRegex.begin();
  92. sit != this->CustomCoverageExcludeRegex.end(); ++ sit )
  93. {
  94. if ( sit->find(file) )
  95. {
  96. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " File " << file
  97. << " is excluded in CTestCustom.ctest" << std::endl;);
  98. return false;
  99. }
  100. }
  101. std::string fSrcDir = cmSystemTools::CollapseFullPath(srcDir);
  102. std::string fBinDir = cmSystemTools::CollapseFullPath(binDir);
  103. std::string fFile = cmSystemTools::CollapseFullPath(file);
  104. bool sourceSubDir = cmSystemTools::IsSubDirectory(fFile.c_str(),
  105. fSrcDir.c_str());
  106. bool buildSubDir = cmSystemTools::IsSubDirectory(fFile.c_str(),
  107. fBinDir.c_str());
  108. // Always check parent directory of the file.
  109. std::string fileDir = cmSystemTools::GetFilenamePath(fFile.c_str());
  110. std::string checkDir;
  111. // We also need to check the binary/source directory pair.
  112. if ( sourceSubDir && buildSubDir )
  113. {
  114. if ( fSrcDir.size() > fBinDir.size() )
  115. {
  116. checkDir = fSrcDir;
  117. }
  118. else
  119. {
  120. checkDir = fBinDir;
  121. }
  122. }
  123. else if ( sourceSubDir )
  124. {
  125. checkDir = fSrcDir;
  126. }
  127. else if ( buildSubDir )
  128. {
  129. checkDir = fBinDir;
  130. }
  131. std::string ndc
  132. = cmSystemTools::FileExistsInParentDirectories(".NoDartCoverage",
  133. fFile.c_str(), checkDir.c_str());
  134. if ( ndc.size() )
  135. {
  136. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Found: " << ndc.c_str()
  137. << " so skip coverage of " << file << std::endl);
  138. return false;
  139. }
  140. // By now checkDir should be set to parent directory of the file.
  141. // Get the relative path to the file an apply it to the opposite directory.
  142. // If it is the same as fileDir, then ignore, otherwise check.
  143. std::string relPath = cmSystemTools::RelativePath(checkDir.c_str(),
  144. fFile.c_str());
  145. if ( checkDir == fSrcDir )
  146. {
  147. checkDir = fBinDir;
  148. }
  149. else
  150. {
  151. checkDir = fSrcDir;
  152. }
  153. fFile = checkDir + "/" + relPath;
  154. fFile = cmSystemTools::GetFilenamePath(fFile.c_str());
  155. if ( fileDir == fFile )
  156. {
  157. // This is in-source build, so we trust the previous check.
  158. return true;
  159. }
  160. ndc = cmSystemTools::FileExistsInParentDirectories(".NoDartCoverage",
  161. fFile.c_str(), checkDir.c_str());
  162. if ( ndc.size() )
  163. {
  164. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Found: " << ndc.c_str()
  165. << " so skip coverage of: " << file << std::endl);
  166. return false;
  167. }
  168. // Ok, nothing in source tree, nothing in binary tree
  169. return true;
  170. }
  171. //----------------------------------------------------------------------
  172. //clearly it would be nice if this were broken up into a few smaller
  173. //functions and commented...
  174. int cmCTestCoverageHandler::ProcessHandler()
  175. {
  176. int error = 0;
  177. // do we have time for this
  178. if (this->CTest->GetRemainingTimeAllowed() < 120)
  179. {
  180. return error;
  181. }
  182. std::string coverage_start_time = this->CTest->CurrentTime();
  183. std::string sourceDir
  184. = this->CTest->GetCTestConfiguration("SourceDirectory");
  185. std::string binaryDir
  186. = this->CTest->GetCTestConfiguration("BuildDirectory");
  187. cmGeneratedFileStream ofs;
  188. double elapsed_time_start = cmSystemTools::GetTime();
  189. if ( !this->StartLogFile("Coverage", ofs) )
  190. {
  191. cmCTestLog(this->CTest, ERROR_MESSAGE,
  192. "Cannot create LastCoverage.log file" << std::endl);
  193. }
  194. ofs << "Performing coverage: " << elapsed_time_start << std::endl;
  195. cmSystemTools::ConvertToUnixSlashes(sourceDir);
  196. cmSystemTools::ConvertToUnixSlashes(binaryDir);
  197. std::string asfGlob = sourceDir + "/*";
  198. std::string abfGlob = binaryDir + "/*";
  199. cmCTestLog(this->CTest, HANDLER_OUTPUT, "Performing coverage" << std::endl);
  200. cmCTestCoverageHandlerContainer cont;
  201. cont.Error = error;
  202. cont.SourceDir = sourceDir;
  203. cont.BinaryDir = binaryDir;
  204. cont.OFS = &ofs;
  205. int file_count = 0;
  206. file_count += this->HandleGCovCoverage(&cont);
  207. if ( file_count < 0 )
  208. {
  209. return error;
  210. }
  211. file_count += this->HandleTracePyCoverage(&cont);
  212. if ( file_count < 0 )
  213. {
  214. return error;
  215. }
  216. error = cont.Error;
  217. if ( file_count == 0 )
  218. {
  219. cmCTestLog(this->CTest, WARNING,
  220. " Cannot find any coverage files. Ignoring Coverage request."
  221. << std::endl);
  222. return error;
  223. }
  224. cmGeneratedFileStream covSumFile;
  225. cmGeneratedFileStream covLogFile;
  226. if (!this->StartResultingXML("Coverage", covSumFile))
  227. {
  228. cmCTestLog(this->CTest, ERROR_MESSAGE,
  229. "Cannot open coverage summary file." << std::endl);
  230. return -1;
  231. }
  232. this->CTest->StartXML(covSumFile);
  233. // Produce output xml files
  234. covSumFile << "<Coverage>" << std::endl
  235. << "\t<StartDateTime>" << coverage_start_time << "</StartDateTime>"
  236. << std::endl;
  237. int logFileCount = 0;
  238. if ( !this->StartCoverageLogFile(covLogFile, logFileCount) )
  239. {
  240. return -1;
  241. }
  242. cmCTestCoverageHandlerContainer::TotalCoverageMap::iterator fileIterator;
  243. int cnt = 0;
  244. long total_tested = 0;
  245. long total_untested = 0;
  246. //std::string fullSourceDir = sourceDir + "/";
  247. //std::string fullBinaryDir = binaryDir + "/";
  248. cmCTestLog(this->CTest, HANDLER_OUTPUT, std::endl);
  249. cmCTestLog(this->CTest, HANDLER_OUTPUT,
  250. " Acumulating results (each . represents one file):" << std::endl);
  251. cmCTestLog(this->CTest, HANDLER_OUTPUT, " ");
  252. std::vector<std::string> errorsWhileAccumulating;
  253. file_count = 0;
  254. for ( fileIterator = cont.TotalCoverage.begin();
  255. fileIterator != cont.TotalCoverage.end();
  256. ++fileIterator )
  257. {
  258. cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush);
  259. file_count ++;
  260. if ( file_count % 50 == 0 )
  261. {
  262. cmCTestLog(this->CTest, HANDLER_OUTPUT, " processed: " << file_count
  263. << " out of "
  264. << cont.TotalCoverage.size() << std::endl);
  265. cmCTestLog(this->CTest, HANDLER_OUTPUT, " ");
  266. }
  267. if ( cnt % 100 == 0 )
  268. {
  269. this->EndCoverageLogFile(covLogFile, logFileCount);
  270. logFileCount ++;
  271. if ( !this->StartCoverageLogFile(covLogFile, logFileCount) )
  272. {
  273. return -1;
  274. }
  275. }
  276. const std::string fullFileName = fileIterator->first;
  277. const std::string fileName
  278. = cmSystemTools::GetFilenameName(fullFileName.c_str());
  279. std::string fullFilePath
  280. = cmSystemTools::GetFilenamePath(fullFileName.c_str());
  281. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  282. "Process file: " << fullFileName << std::endl);
  283. cmSystemTools::ConvertToUnixSlashes(fullFilePath);
  284. if ( !cmSystemTools::FileExists(fullFileName.c_str()) )
  285. {
  286. cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot find file: "
  287. << fullFileName.c_str() << std::endl);
  288. continue;
  289. }
  290. bool shouldIDoCoverage
  291. = this->ShouldIDoCoverage(fullFileName.c_str(),
  292. sourceDir.c_str(), binaryDir.c_str());
  293. if ( !shouldIDoCoverage )
  294. {
  295. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  296. ".NoDartCoverage found, so skip coverage check for: "
  297. << fullFileName.c_str()
  298. << std::endl);
  299. continue;
  300. }
  301. const cmCTestCoverageHandlerContainer::SingleFileCoverageVector& fcov
  302. = fileIterator->second;
  303. covLogFile << "\t<File Name=\""
  304. << this->CTest->MakeXMLSafe(fileName.c_str())
  305. << "\" FullPath=\"" << this->CTest->MakeXMLSafe(
  306. this->CTest->GetShortPathToFile(
  307. fileIterator->first.c_str())) << "\">" << std::endl
  308. << "\t\t<Report>" << std::endl;
  309. std::ifstream ifs(fullFileName.c_str());
  310. if ( !ifs)
  311. {
  312. cmOStringStream ostr;
  313. ostr << "Cannot open source file: " << fullFileName.c_str();
  314. errorsWhileAccumulating.push_back(ostr.str());
  315. error ++;
  316. continue;
  317. }
  318. int tested = 0;
  319. int untested = 0;
  320. cmCTestCoverageHandlerContainer::SingleFileCoverageVector::size_type cc;
  321. std::string line;
  322. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  323. "Actually perfoming coverage for: " << fullFileName << std::endl);
  324. for ( cc= 0; cc < fcov.size(); cc ++ )
  325. {
  326. if ( !cmSystemTools::GetLineFromStream(ifs, line) &&
  327. cc != fcov.size() -1 )
  328. {
  329. cmOStringStream ostr;
  330. ostr << "Problem reading source file: " << fullFileName.c_str()
  331. << " line:" << cc;
  332. errorsWhileAccumulating.push_back(ostr.str());
  333. error ++;
  334. break;
  335. }
  336. covLogFile << "\t\t<Line Number=\"" << cc << "\" Count=\"" << fcov[cc]
  337. << "\">"
  338. << this->CTest->MakeXMLSafe(line.c_str()) << "</Line>" << std::endl;
  339. if ( fcov[cc] == 0 )
  340. {
  341. untested ++;
  342. }
  343. else if ( fcov[cc] > 0 )
  344. {
  345. tested ++;
  346. }
  347. }
  348. if ( cmSystemTools::GetLineFromStream(ifs, line) )
  349. {
  350. cmOStringStream ostr;
  351. ostr << "Looks like there are more lines in the file: " << line;
  352. errorsWhileAccumulating.push_back(ostr.str());
  353. }
  354. float cper = 0;
  355. float cmet = 0;
  356. if ( tested + untested > 0 )
  357. {
  358. cper = (100 * SAFEDIV(static_cast<float>(tested),
  359. static_cast<float>(tested + untested)));
  360. cmet = ( SAFEDIV(static_cast<float>(tested + 10),
  361. static_cast<float>(tested + untested + 10)));
  362. }
  363. total_tested += tested;
  364. total_untested += untested;
  365. covLogFile << "\t\t</Report>" << std::endl
  366. << "\t</File>" << std::endl;
  367. covSumFile << "\t<File Name=\"" << this->CTest->MakeXMLSafe(fileName)
  368. << "\" FullPath=\"" << this->CTest->MakeXMLSafe(
  369. this->CTest->GetShortPathToFile(fullFileName.c_str()))
  370. << "\" Covered=\"" << (cmet>0?"true":"false") << "\">\n"
  371. << "\t\t<LOCTested>" << tested << "</LOCTested>\n"
  372. << "\t\t<LOCUnTested>" << untested << "</LOCUnTested>\n"
  373. << "\t\t<PercentCoverage>";
  374. covSumFile.setf(std::ios::fixed, std::ios::floatfield);
  375. covSumFile.precision(2);
  376. covSumFile << (cper) << "</PercentCoverage>\n"
  377. << "\t\t<CoverageMetric>";
  378. covSumFile.setf(std::ios::fixed, std::ios::floatfield);
  379. covSumFile.precision(2);
  380. covSumFile << (cmet) << "</CoverageMetric>\n"
  381. << "\t</File>" << std::endl;
  382. cnt ++;
  383. }
  384. this->EndCoverageLogFile(covLogFile, logFileCount);
  385. if ( errorsWhileAccumulating.size() > 0 )
  386. {
  387. cmCTestLog(this->CTest, ERROR_MESSAGE, std::endl);
  388. cmCTestLog(this->CTest, ERROR_MESSAGE,
  389. "Error(s) while acumulating results:" << std::endl);
  390. std::vector<std::string>::iterator erIt;
  391. for ( erIt = errorsWhileAccumulating.begin();
  392. erIt != errorsWhileAccumulating.end();
  393. ++ erIt )
  394. {
  395. cmCTestLog(this->CTest, ERROR_MESSAGE,
  396. " " << erIt->c_str() << std::endl);
  397. }
  398. }
  399. int total_lines = total_tested + total_untested;
  400. float percent_coverage = 100 * SAFEDIV(static_cast<float>(total_tested),
  401. static_cast<float>(total_lines));
  402. if ( total_lines == 0 )
  403. {
  404. percent_coverage = 0;
  405. }
  406. std::string end_time = this->CTest->CurrentTime();
  407. covSumFile << "\t<LOCTested>" << total_tested << "</LOCTested>\n"
  408. << "\t<LOCUntested>" << total_untested << "</LOCUntested>\n"
  409. << "\t<LOC>" << total_lines << "</LOC>\n"
  410. << "\t<PercentCoverage>";
  411. covSumFile.setf(std::ios::fixed, std::ios::floatfield);
  412. covSumFile.precision(2);
  413. covSumFile << (percent_coverage)<< "</PercentCoverage>\n"
  414. << "\t<EndDateTime>" << end_time << "</EndDateTime>\n";
  415. covSumFile << "<ElapsedMinutes>" <<
  416. static_cast<int>((cmSystemTools::GetTime() - elapsed_time_start)/6)/10.0
  417. << "</ElapsedMinutes>"
  418. << "</Coverage>" << std::endl;
  419. this->CTest->EndXML(covSumFile);
  420. cmCTestLog(this->CTest, HANDLER_OUTPUT, "" << std::endl
  421. << "\tCovered LOC: "
  422. << total_tested << std::endl
  423. << "\tNot covered LOC: " << total_untested << std::endl
  424. << "\tTotal LOC: " << total_lines << std::endl
  425. << "\tPercentage Coverage: "
  426. << std::setiosflags(std::ios::fixed)
  427. << std::setprecision(2)
  428. << (percent_coverage) << "%" << std::endl);
  429. ofs << "\tCovered LOC: " << total_tested << std::endl
  430. << "\tNot covered LOC: " << total_untested << std::endl
  431. << "\tTotal LOC: " << total_lines << std::endl
  432. << "\tPercentage Coverage: "
  433. << std::setiosflags(std::ios::fixed)
  434. << std::setprecision(2)
  435. << (percent_coverage) << "%" << std::endl;
  436. if ( error )
  437. {
  438. return -1;
  439. }
  440. return 0;
  441. }
  442. //----------------------------------------------------------------------
  443. void cmCTestCoverageHandler::PopulateCustomVectors(cmMakefile *mf)
  444. {
  445. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  446. " Add coverage exclude regular expressions." << std::endl);
  447. this->CTest->PopulateCustomVector(mf, "CTEST_CUSTOM_COVERAGE_EXCLUDE",
  448. this->CustomCoverageExclude);
  449. std::vector<cmStdString>::iterator it;
  450. for ( it = this->CustomCoverageExclude.begin();
  451. it != this->CustomCoverageExclude.end();
  452. ++ it )
  453. {
  454. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " Add coverage exclude: "
  455. << it->c_str() << std::endl);
  456. }
  457. }
  458. //----------------------------------------------------------------------
  459. int cmCTestCoverageHandler::HandleGCovCoverage(
  460. cmCTestCoverageHandlerContainer* cont)
  461. {
  462. std::string gcovCommand
  463. = this->CTest->GetCTestConfiguration("CoverageCommand");
  464. // Style 1
  465. std::string st1gcovOutputRex1
  466. = "[0-9]+\\.[0-9]+% of [0-9]+ (source |)lines executed in file (.*)$";
  467. std::string st1gcovOutputRex2 = "^Creating (.*\\.gcov)\\.";
  468. cmsys::RegularExpression st1re1(st1gcovOutputRex1.c_str());
  469. cmsys::RegularExpression st1re2(st1gcovOutputRex2.c_str());
  470. // Style 2
  471. std::string st2gcovOutputRex1 = "^File *[`'](.*)'$";
  472. std::string st2gcovOutputRex2
  473. = "Lines executed: *[0-9]+\\.[0-9]+% of [0-9]+$";
  474. std::string st2gcovOutputRex3 = "^(.*):creating [`'](.*\\.gcov)'";
  475. std::string st2gcovOutputRex4 = "^(.*):unexpected EOF *$";
  476. std::string st2gcovOutputRex5 = "^(.*):cannot open source file*$";
  477. std::string st2gcovOutputRex6
  478. = "^(.*):source file is newer than graph file `(.*)'$";
  479. cmsys::RegularExpression st2re1(st2gcovOutputRex1.c_str());
  480. cmsys::RegularExpression st2re2(st2gcovOutputRex2.c_str());
  481. cmsys::RegularExpression st2re3(st2gcovOutputRex3.c_str());
  482. cmsys::RegularExpression st2re4(st2gcovOutputRex4.c_str());
  483. cmsys::RegularExpression st2re5(st2gcovOutputRex5.c_str());
  484. cmsys::RegularExpression st2re6(st2gcovOutputRex6.c_str());
  485. cmsys::Glob gl;
  486. gl.RecurseOn();
  487. std::string daGlob = cont->BinaryDir + "/*.da";
  488. gl.FindFiles(daGlob);
  489. std::vector<std::string> files = gl.GetFiles();
  490. daGlob = cont->BinaryDir + "/*.gcda";
  491. gl.FindFiles(daGlob);
  492. std::vector<std::string>& moreFiles = gl.GetFiles();
  493. files.insert(files.end(), moreFiles.begin(), moreFiles.end());
  494. std::vector<std::string>::iterator it;
  495. if ( files.size() == 0 )
  496. {
  497. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  498. " Cannot find any GCov coverage files."
  499. << std::endl);
  500. // No coverage files is a valid thing, so the exit code is 0
  501. return 0;
  502. }
  503. std::string testingDir = this->CTest->GetBinaryDir() + "/Testing";
  504. std::string tempDir = testingDir + "/CoverageInfo";
  505. std::string currentDirectory = cmSystemTools::GetCurrentWorkingDirectory();
  506. cmSystemTools::MakeDirectory(tempDir.c_str());
  507. cmSystemTools::ChangeDirectory(tempDir.c_str());
  508. this->CustomCoverageExcludeRegex.empty();
  509. std::vector<cmStdString>::iterator rexIt;
  510. for ( rexIt = this->CustomCoverageExclude.begin();
  511. rexIt != this->CustomCoverageExclude.end();
  512. ++ rexIt )
  513. {
  514. this->CustomCoverageExcludeRegex.push_back(
  515. cmsys::RegularExpression(rexIt->c_str()));
  516. }
  517. int gcovStyle = 0;
  518. std::set<std::string> missingFiles;
  519. std::string actualSourceFile = "";
  520. cmCTestLog(this->CTest, HANDLER_OUTPUT,
  521. " Processing coverage (each . represents one file):" << std::endl);
  522. cmCTestLog(this->CTest, HANDLER_OUTPUT, " ");
  523. int file_count = 0;
  524. for ( it = files.begin(); it != files.end(); ++ it )
  525. {
  526. cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush);
  527. std::string fileDir = cmSystemTools::GetFilenamePath(it->c_str());
  528. std::string command = "\"" + gcovCommand + "\" -l -o \"" + fileDir
  529. + "\" \"" + *it + "\"";
  530. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, command.c_str()
  531. << std::endl);
  532. std::string output = "";
  533. std::string errors = "";
  534. int retVal = 0;
  535. *cont->OFS << "* Run coverage for: " << fileDir.c_str() << std::endl;
  536. *cont->OFS << " Command: " << command.c_str() << std::endl;
  537. int res = this->CTest->RunCommand(command.c_str(), &output, &errors,
  538. &retVal, tempDir.c_str(), 0 /*this->TimeOut*/);
  539. *cont->OFS << " Output: " << output.c_str() << std::endl;
  540. *cont->OFS << " Errors: " << errors.c_str() << std::endl;
  541. if ( ! res )
  542. {
  543. cmCTestLog(this->CTest, ERROR_MESSAGE,
  544. "Problem running coverage on file: " << it->c_str() << std::endl);
  545. cmCTestLog(this->CTest, ERROR_MESSAGE,
  546. "Command produced error: " << errors << std::endl);
  547. cont->Error ++;
  548. continue;
  549. }
  550. if ( retVal != 0 )
  551. {
  552. cmCTestLog(this->CTest, ERROR_MESSAGE, "Coverage command returned: "
  553. << retVal << " while processing: " << it->c_str() << std::endl);
  554. cmCTestLog(this->CTest, ERROR_MESSAGE,
  555. "Command produced error: " << cont->Error << std::endl);
  556. }
  557. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  558. "--------------------------------------------------------------"
  559. << std::endl
  560. << output << std::endl
  561. << "--------------------------------------------------------------"
  562. << std::endl);
  563. std::vector<cmStdString> lines;
  564. std::vector<cmStdString>::iterator line;
  565. // Globals for storing current source file and current gcov file;
  566. cmSystemTools::Split(output.c_str(), lines);
  567. for ( line = lines.begin(); line != lines.end(); ++line)
  568. {
  569. std::string sourceFile;
  570. std::string gcovFile;
  571. cmCTestLog(this->CTest, DEBUG, "Line: [" << line->c_str() << "]"
  572. << std::endl);
  573. if ( line->size() == 0 )
  574. {
  575. // Ignore empty line; probably style 2
  576. }
  577. else if ( st1re1.find(line->c_str()) )
  578. {
  579. if ( gcovStyle != 0 )
  580. {
  581. if ( gcovStyle != 1 )
  582. {
  583. cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
  584. << std::endl);
  585. cont->Error ++;
  586. break;
  587. }
  588. gcovStyle = 1;
  589. }
  590. actualSourceFile = "";
  591. sourceFile = st1re1.match(2);
  592. }
  593. else if ( st1re2.find(line->c_str() ) )
  594. {
  595. if ( gcovStyle != 0 )
  596. {
  597. if ( gcovStyle != 1 )
  598. {
  599. cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
  600. << std::endl);
  601. cont->Error ++;
  602. break;
  603. }
  604. gcovStyle = 1;
  605. }
  606. gcovFile = st1re2.match(1);
  607. }
  608. else if ( st2re1.find(line->c_str() ) )
  609. {
  610. if ( gcovStyle != 0 )
  611. {
  612. if ( gcovStyle != 2 )
  613. {
  614. cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
  615. << std::endl);
  616. cont->Error ++;
  617. break;
  618. }
  619. gcovStyle = 2;
  620. }
  621. actualSourceFile = "";
  622. sourceFile = st2re1.match(1);
  623. }
  624. else if ( st2re2.find(line->c_str() ) )
  625. {
  626. if ( gcovStyle != 0 )
  627. {
  628. if ( gcovStyle != 2 )
  629. {
  630. cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
  631. << std::endl);
  632. cont->Error ++;
  633. break;
  634. }
  635. gcovStyle = 2;
  636. }
  637. }
  638. else if ( st2re3.find(line->c_str() ) )
  639. {
  640. if ( gcovStyle != 0 )
  641. {
  642. if ( gcovStyle != 2 )
  643. {
  644. cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
  645. << std::endl);
  646. cont->Error ++;
  647. break;
  648. }
  649. gcovStyle = 2;
  650. }
  651. gcovFile = st2re3.match(2);
  652. }
  653. else if ( st2re4.find(line->c_str() ) )
  654. {
  655. if ( gcovStyle != 0 )
  656. {
  657. if ( gcovStyle != 2 )
  658. {
  659. cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
  660. << std::endl);
  661. cont->Error ++;
  662. break;
  663. }
  664. gcovStyle = 2;
  665. }
  666. cmCTestLog(this->CTest, WARNING, "Warning: " << st2re4.match(1)
  667. << " had unexpected EOF" << std::endl);
  668. }
  669. else if ( st2re5.find(line->c_str() ) )
  670. {
  671. if ( gcovStyle != 0 )
  672. {
  673. if ( gcovStyle != 2 )
  674. {
  675. cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
  676. << std::endl);
  677. cont->Error ++;
  678. break;
  679. }
  680. gcovStyle = 2;
  681. }
  682. cmCTestLog(this->CTest, WARNING, "Warning: Cannot open file: "
  683. << st2re5.match(1) << std::endl);
  684. }
  685. else if ( st2re6.find(line->c_str() ) )
  686. {
  687. if ( gcovStyle != 0 )
  688. {
  689. if ( gcovStyle != 2 )
  690. {
  691. cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style"
  692. << std::endl);
  693. cont->Error ++;
  694. break;
  695. }
  696. gcovStyle = 2;
  697. }
  698. cmCTestLog(this->CTest, WARNING, "Warning: File: " << st2re6.match(1)
  699. << " is newer than " << st2re6.match(2) << std::endl);
  700. }
  701. else
  702. {
  703. cmCTestLog(this->CTest, ERROR_MESSAGE,
  704. "Unknown line: [" << line->c_str() << "]" << std::endl);
  705. cont->Error ++;
  706. //abort();
  707. }
  708. if ( !gcovFile.empty() && actualSourceFile.size() )
  709. {
  710. cmCTestCoverageHandlerContainer::SingleFileCoverageVector* vec
  711. = &cont->TotalCoverage[actualSourceFile];
  712. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " in file: "
  713. << gcovFile << std::endl);
  714. std::ifstream ifile(gcovFile.c_str());
  715. if ( ! ifile )
  716. {
  717. cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot open file: "
  718. << gcovFile << std::endl);
  719. }
  720. else
  721. {
  722. long cnt = -1;
  723. std::string nl;
  724. while ( cmSystemTools::GetLineFromStream(ifile, nl) )
  725. {
  726. cnt ++;
  727. //TODO: Handle gcov 3.0 non-coverage lines
  728. // Skip empty lines
  729. if ( !nl.size() )
  730. {
  731. continue;
  732. }
  733. // Skip unused lines
  734. if ( nl.size() < 12 )
  735. {
  736. continue;
  737. }
  738. // Read the coverage count from the beginning of the gcov output
  739. // line
  740. std::string prefix = nl.substr(0, 12);
  741. int cov = atoi(prefix.c_str());
  742. // Read the line number starting at the 10th character of the gcov
  743. // output line
  744. std::string lineNumber = nl.substr(10, 5);
  745. int lineIdx = atoi(lineNumber.c_str())-1;
  746. if ( lineIdx >= 0 )
  747. {
  748. while ( vec->size() <=
  749. static_cast<size_t>(lineIdx) )
  750. {
  751. vec->push_back(-1);
  752. }
  753. // Initially all entries are -1 (not used). If we get coverage
  754. // information, increment it to 0 first.
  755. if ( (*vec)[lineIdx] < 0 )
  756. {
  757. if ( cov > 0 || prefix.find("#") != prefix.npos )
  758. {
  759. (*vec)[lineIdx] = 0;
  760. }
  761. }
  762. (*vec)[lineIdx] += cov;
  763. }
  764. }
  765. }
  766. actualSourceFile = "";
  767. }
  768. if ( !sourceFile.empty() && actualSourceFile.empty() )
  769. {
  770. gcovFile = "";
  771. // Is it in the source dir?
  772. if ( sourceFile.size() > cont->SourceDir.size() &&
  773. sourceFile.substr(0, cont->SourceDir.size()) == cont->SourceDir &&
  774. sourceFile[cont->SourceDir.size()] == '/' )
  775. {
  776. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " produced s: "
  777. << sourceFile.c_str() << std::endl);
  778. *cont->OFS << " produced in source dir: " << sourceFile.c_str()
  779. << std::endl;
  780. actualSourceFile
  781. = cmSystemTools::CollapseFullPath(sourceFile.c_str());
  782. }
  783. // Binary dir?
  784. if ( sourceFile.size() > cont->BinaryDir.size() &&
  785. sourceFile.substr(0, cont->BinaryDir.size()) == cont->BinaryDir &&
  786. sourceFile[cont->BinaryDir.size()] == '/' )
  787. {
  788. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " produced b: "
  789. << sourceFile.c_str() << std::endl);
  790. *cont->OFS << " produced in binary dir: " << sourceFile.c_str()
  791. << std::endl;
  792. actualSourceFile
  793. = cmSystemTools::CollapseFullPath(sourceFile.c_str());
  794. }
  795. if ( actualSourceFile.empty() )
  796. {
  797. if ( missingFiles.find(actualSourceFile) == missingFiles.end() )
  798. {
  799. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  800. "Something went wrong" << std::endl);
  801. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "File: ["
  802. << sourceFile.c_str() << "]" << std::endl);
  803. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "s: ["
  804. << sourceFile.substr(0, cont->SourceDir.size()) << "]" << std::endl);
  805. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "b: ["
  806. << sourceFile.substr(0, cont->BinaryDir.size()) << "]" << std::endl);
  807. *cont->OFS << " Something went wrong. Cannot find: "
  808. << sourceFile.c_str()
  809. << " in source dir: " << cont->SourceDir.c_str()
  810. << " or binary dir: " << cont->BinaryDir.c_str() << std::endl;
  811. missingFiles.insert(actualSourceFile);
  812. }
  813. }
  814. }
  815. }
  816. file_count ++;
  817. if ( file_count % 50 == 0 )
  818. {
  819. cmCTestLog(this->CTest, HANDLER_OUTPUT, " processed: " << file_count
  820. << " out of " << files.size() << std::endl);
  821. cmCTestLog(this->CTest, HANDLER_OUTPUT, " ");
  822. }
  823. }
  824. cmSystemTools::ChangeDirectory(currentDirectory.c_str());
  825. return file_count;
  826. }
  827. //----------------------------------------------------------------------
  828. int cmCTestCoverageHandler::HandleTracePyCoverage(
  829. cmCTestCoverageHandlerContainer* cont)
  830. {
  831. cmsys::Glob gl;
  832. gl.RecurseOn();
  833. std::string daGlob = cont->BinaryDir + "/*.cover";
  834. gl.FindFiles(daGlob);
  835. std::vector<std::string> files = gl.GetFiles();
  836. if ( files.size() == 0 )
  837. {
  838. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  839. " Cannot find any Python Trace.py coverage files."
  840. << std::endl);
  841. // No coverage files is a valid thing, so the exit code is 0
  842. return 0;
  843. }
  844. std::string testingDir = this->CTest->GetBinaryDir() + "/Testing";
  845. std::string tempDir = testingDir + "/CoverageInfo";
  846. std::string currentDirectory = cmSystemTools::GetCurrentWorkingDirectory();
  847. cmSystemTools::MakeDirectory(tempDir.c_str());
  848. cmSystemTools::ChangeDirectory(tempDir.c_str());
  849. cmSystemTools::ChangeDirectory(currentDirectory.c_str());
  850. std::vector<std::string>::iterator fileIt;
  851. int file_count = 0;
  852. for ( fileIt = files.begin(); fileIt != files.end(); ++ fileIt )
  853. {
  854. std::string fileName = this->FindFile(cont, *fileIt);
  855. if ( fileName.empty() )
  856. {
  857. cmCTestLog(this->CTest, ERROR_MESSAGE,
  858. "Cannot find source Python file corresponding to: "
  859. << fileIt->c_str() << std::endl);
  860. continue;
  861. }
  862. std::string actualSourceFile
  863. = cmSystemTools::CollapseFullPath(fileName.c_str());
  864. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  865. " Check coverage for file: " << actualSourceFile.c_str()
  866. << std::endl);
  867. cmCTestCoverageHandlerContainer::SingleFileCoverageVector* vec
  868. = &cont->TotalCoverage[actualSourceFile];
  869. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  870. " in file: " << fileIt->c_str() << std::endl);
  871. std::ifstream ifile(fileIt->c_str());
  872. if ( ! ifile )
  873. {
  874. cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot open file: "
  875. << fileIt->c_str() << std::endl);
  876. }
  877. else
  878. {
  879. long cnt = -1;
  880. std::string nl;
  881. while ( cmSystemTools::GetLineFromStream(ifile, nl) )
  882. {
  883. cnt ++;
  884. // Skip empty lines
  885. if ( !nl.size() )
  886. {
  887. continue;
  888. }
  889. // Skip unused lines
  890. if ( nl.size() < 12 )
  891. {
  892. continue;
  893. }
  894. // Read the coverage count from the beginning of the Trace.py output
  895. // line
  896. std::string prefix = nl.substr(0, 6);
  897. if ( prefix[5] != ' ' && prefix[5] != ':' )
  898. {
  899. // This is a hack. We should really do something more elaborate
  900. prefix = nl.substr(0, 7);
  901. if ( prefix[6] != ' ' && prefix[6] != ':' )
  902. {
  903. prefix = nl.substr(0, 8);
  904. if ( prefix[7] != ' ' && prefix[7] != ':' )
  905. {
  906. cmCTestLog(this->CTest, ERROR_MESSAGE,
  907. "Currently the limit is maximum coverage of 999999"
  908. << std::endl);
  909. }
  910. }
  911. }
  912. int cov = atoi(prefix.c_str());
  913. if ( prefix[prefix.size()-1] != ':' )
  914. {
  915. // This line does not have ':' so no coverage here. That said,
  916. // Trace.py does not handle not covered lines versus comments etc.
  917. // So, this will be set to 0.
  918. cov = 0;
  919. }
  920. cmCTestLog(this->CTest, DEBUG, "Prefix: " << prefix.c_str()
  921. << " cov: " << cov
  922. << std::endl);
  923. // Read the line number starting at the 10th character of the gcov
  924. // output line
  925. int lineIdx = cnt;
  926. if ( lineIdx >= 0 )
  927. {
  928. while ( vec->size() <=
  929. static_cast<size_t>(lineIdx) )
  930. {
  931. vec->push_back(-1);
  932. }
  933. // Initially all entries are -1 (not used). If we get coverage
  934. // information, increment it to 0 first.
  935. if ( (*vec)[lineIdx] < 0 )
  936. {
  937. if ( cov >= 0 )
  938. {
  939. (*vec)[lineIdx] = 0;
  940. }
  941. }
  942. (*vec)[lineIdx] += cov;
  943. }
  944. }
  945. }
  946. ++ file_count;
  947. }
  948. cmSystemTools::ChangeDirectory(currentDirectory.c_str());
  949. return file_count;
  950. }
  951. //----------------------------------------------------------------------
  952. std::string cmCTestCoverageHandler::FindFile(
  953. cmCTestCoverageHandlerContainer* cont,
  954. std::string fileName)
  955. {
  956. std::string fileNameNoE
  957. = cmSystemTools::GetFilenameWithoutLastExtension(fileName);
  958. // First check in source and binary directory
  959. std::string fullName = cont->SourceDir + "/" + fileNameNoE + ".py";
  960. if ( cmSystemTools::FileExists(fullName.c_str()) )
  961. {
  962. return fullName;
  963. }
  964. fullName = cont->BinaryDir + "/" + fileNameNoE + ".py";
  965. if ( cmSystemTools::FileExists(fullName.c_str()) )
  966. {
  967. return fullName;
  968. }
  969. return "";
  970. }