@@ -70,6 +70,8 @@ def create_args(params):
7070 default = None , metavar = 'FILE' )
7171 parser .add_argument ('--follow-symlinks' , action = 'store_true' ,
7272 help = 'Follow symlinks (default off)' )
73+ parser .add_argument ('-l' , '--lcov-file' , metavar = 'FILE' ,
74+ help = 'Upload lcov generated info file' )
7375
7476 return parser .parse_args (params )
7577
@@ -278,6 +280,31 @@ def parse_gcov_file(fobj, filename):
278280 coverage .append (int (cov_num ))
279281 return coverage
280282
283+ def parse_lcov_file_info (args , filepath , line_iter , line_coverage_re , file_end_string ):
284+ """ Parse the file content in lcov info file
285+ """
286+ coverage = []
287+ lines_covered = []
288+ while True :
289+ try :
290+ line = line_iter .next ()
291+ if line != "end_of_record" :
292+ line_coverage_match = line_coverage_re .match (line )
293+ if line_coverage_match :
294+ line_no = line_coverage_match .group (1 )
295+ cov_count = int (line_coverage_match .group (2 ))
296+ lines_covered .append ((line_no , cov_count ))
297+ else :
298+ break
299+ except StopIteration :
300+ break
301+
302+ num_code_lines = len ([line .rstrip ('\n ' ) for line in open (filepath , 'r' )])
303+ coverage = [None ] * num_code_lines
304+ for line_covered in lines_covered :
305+ coverage [int (line_covered [0 ]) - 1 ] = line_covered [1 ]
306+
307+ return coverage
281308
282309def combine_reports (original , new ):
283310 """Combines two gcov reports for a file into one by adding the number of hits on each line
@@ -299,7 +326,6 @@ def combine_reports(original, new):
299326 report ['coverage' ] = coverage
300327 return report
301328
302-
303329def collect_non_report_files (args , discovered_files ):
304330 """Collects the source files that have no coverage reports.
305331 """
@@ -344,54 +370,80 @@ def collect(args):
344370 discovered_files = set ()
345371 src_files = {}
346372 abs_root = os .path .abspath (args .root )
347- for root , dirs , files in os .walk (args .root , followlinks = args .follow_symlinks ):
348- dirs [:] = filter_dirs (root , dirs , excl_paths )
349-
350- root_is_libtool_dir = is_libtool_dir (root )
351- for filepath in files :
352- if os .path .splitext (filepath )[1 ] == '.gcov' :
353- gcov_path = os .path .join (os .path .join (root , filepath ))
354- with open (gcov_path , mode = 'rb' ) as fobj :
355- source_file_line = fobj .readline ().decode ('utf-8' , 'replace' )
356- source_file_path = source_file_line .split (':' )[- 1 ].strip ()
357- if not os .path .isabs (source_file_path ):
358- if args .build_root :
359- source_file_path = os .path .join (
360- args .build_root , source_file_path )
361- elif root_is_libtool_dir :
362- source_file_path = os .path .abspath (
363- libtool_source_file_path (
364- root , source_file_path ))
365- else :
366- if not source_file_path .startswith (os .path .pardir + os .path .sep ) and \
367- os .path .dirname (source_file_path ):
368- the_root = abs_root
369- else :
370- the_root = root
371- source_file_path = os .path .abspath (
372- os .path .join (the_root , source_file_path ))
373- src_path = os .path .relpath (source_file_path , abs_root )
374- if src_path .startswith (os .path .pardir + os .path .sep ):
375- continue
376- if is_excluded_path (args , source_file_path ):
377- continue
378-
373+ if args .lcov_file :
374+ info_lines = [line .rstrip ('\n ' ) for line in open (args .lcov_file , 'r' )]
375+ line_iter = iter (info_lines )
376+ new_file_re = re .compile ('SF:(.*)' )
377+ line_coverage_re = re .compile ('DA:(\d+),(\d+)' );
378+ while True :
379+ try :
380+ line = line_iter .next ()
381+ new_file_match = new_file_re .match (line )
382+ if new_file_match :
379383 src_report = {}
380- src_report ['name' ] = posix_path (src_path )
381- discovered_files .add (src_path )
382- with io .open (source_file_path , mode = 'rb' ) as src_file :
384+ filepath = new_file_match .group (1 )
385+ if args .build_root :
386+ filepath = os .path .relpath (filepath , args .build_root )
387+ abs_filepath = os .path .join (abs_root , filepath )
388+ src_report ['name' ] = unicode (posix_path (filepath ))
389+ with io .open (abs_filepath , mode = 'rb' ) as src_file :
383390 src_report ['source_digest' ] = hashlib .md5 (src_file .read ()).hexdigest ()
384-
385- src_report ['coverage' ] = parse_gcov_file (fobj , gcov_path )
386- if src_path in src_files :
387- src_files [src_path ] = combine_reports (src_files [src_path ], src_report )
388- else :
389- src_files [src_path ] = src_report
391+ src_report ['coverage' ] = parse_lcov_file_info (abs_filepath , line_iter , line_coverage_re , "end_of_record" )
392+ src_files [filepath ] = src_report
393+ elif line != "TN:" :
394+ print "invalid info file"
395+ except StopIteration :
396+ break
397+ else :
398+ for root , dirs , files in os .walk (args .root , followlinks = args .follow_symlinks ):
399+ dirs [:] = filter_dirs (root , dirs , excl_paths )
400+
401+ root_is_libtool_dir = is_libtool_dir (root )
402+ for filepath in files :
403+ if os .path .splitext (filepath )[1 ] == '.gcov' :
404+ gcov_path = os .path .join (os .path .join (root , filepath ))
405+ with open (gcov_path , mode = 'rb' ) as fobj :
406+ source_file_line = fobj .readline ().decode ('utf-8' , 'replace' )
407+ source_file_path = source_file_line .split (':' )[- 1 ].strip ()
408+ if not os .path .isabs (source_file_path ):
409+ if args .build_root :
410+ source_file_path = os .path .join (
411+ args .build_root , source_file_path )
412+ elif root_is_libtool_dir :
413+ source_file_path = os .path .abspath (
414+ libtool_source_file_path (
415+ root , source_file_path ))
416+ else :
417+ if not source_file_path .startswith (os .path .pardir + os .path .sep ) and \
418+ os .path .dirname (source_file_path ):
419+ the_root = abs_root
420+ else :
421+ the_root = root
422+ source_file_path = os .path .abspath (
423+ os .path .join (the_root , source_file_path ))
424+ src_path = os .path .relpath (source_file_path , abs_root )
425+ if src_path .startswith (os .path .pardir + os .path .sep ):
426+ continue
427+ if is_excluded_path (args , source_file_path ):
428+ continue
429+
430+ src_report = {}
431+ src_report ['name' ] = posix_path (src_path )
432+ discovered_files .add (src_path )
433+ with io .open (source_file_path , mode = 'rb' ) as src_file :
434+ src_report ['source_digest' ] = hashlib .md5 (src_file .read ()).hexdigest ()
435+
436+ src_report ['coverage' ] = parse_gcov_file (fobj , gcov_path )
437+ if src_path in src_files :
438+ src_files [src_path ] = combine_reports (src_files [src_path ], src_report )
439+ else :
440+ src_files [src_path ] = src_report
390441
391442 report ['source_files' ] = list (src_files .values ())
392443 # Also collects the source files that have no coverage reports.
393- report ['source_files' ].extend (
394- collect_non_report_files (args , discovered_files ))
444+ if not args .lcov_file :
445+ report ['source_files' ].extend (
446+ collect_non_report_files (args , discovered_files ))
395447
396448 # Use the root directory to get information on the Git repository
397449 report ['git' ] = gitrepo .gitrepo (abs_root )
0 commit comments