diff options
-rw-r--r-- | mesonbuild/backend/ninjabackend.py | 110 | ||||
-rw-r--r-- | mesonbuild/scripts/coverage.py | 194 |
2 files changed, 152 insertions, 152 deletions
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 2eff796..cee1434 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -613,13 +613,19 @@ int dummy; self.create_target_alias(target_name, outfile) self.processed_targets[target.get_id()] = True + def generate_coverage_command(self, elem, outputs): + elem.add_item('COMMAND', self.environment.get_build_command() + + ['--internal', 'coverage'] + + outputs + + [self.environment.get_source_dir(), + os.path.join(self.environment.get_source_dir(), + self.build.get_subproject_dir()), + self.environment.get_build_dir(), + self.environment.get_log_dir()]) + def generate_coverage_rules(self, outfile): e = NinjaBuildElement(self.all_outputs, 'meson-coverage', 'CUSTOM_COMMAND', 'PHONY') - e.add_item('COMMAND', self.environment.get_build_command() + - ['--internal', 'coverage', - self.environment.get_source_dir(), - self.environment.get_build_dir(), - self.environment.get_log_dir()]) + self.generate_coverage_command(e, []) e.add_item('description', 'Generates coverage reports.') e.write(outfile) # Alias that runs the target defined above @@ -627,80 +633,26 @@ int dummy; self.generate_coverage_legacy_rules(outfile) def generate_coverage_legacy_rules(self, outfile): - (gcovr_exe, gcovr_new_rootdir, lcov_exe, genhtml_exe) = environment.find_coverage_tools() - added_rule = False - if gcovr_exe: - # gcovr >= 3.1 interprets rootdir differently - if gcovr_new_rootdir: - rootdir = self.environment.get_build_dir() - else: - rootdir = self.environment.get_source_dir(), - added_rule = True - elem = NinjaBuildElement(self.all_outputs, 'meson-coverage-xml', 'CUSTOM_COMMAND', '') - elem.add_item('COMMAND', [gcovr_exe, '-x', '-r', rootdir, - '-o', os.path.join(self.environment.get_log_dir(), 'coverage.xml')]) - elem.add_item('DESC', 'Generating XML coverage report.') - elem.write(outfile) - # Alias that runs the target defined above - self.create_target_alias('meson-coverage-xml', outfile) - elem = NinjaBuildElement(self.all_outputs, 'meson-coverage-text', 'CUSTOM_COMMAND', '') - elem.add_item('COMMAND', [gcovr_exe, '-r', rootdir, - '-o', os.path.join(self.environment.get_log_dir(), 'coverage.txt')]) - elem.add_item('DESC', 'Generating text coverage report.') - elem.write(outfile) - # Alias that runs the target defined above - self.create_target_alias('meson-coverage-text', outfile) - if lcov_exe and genhtml_exe: - added_rule = True - htmloutdir = os.path.join(self.environment.get_log_dir(), 'coveragereport') - covinfo = os.path.join(self.environment.get_log_dir(), 'coverage.info') - phony_elem = NinjaBuildElement(self.all_outputs, 'meson-coverage-html', 'phony', os.path.join(htmloutdir, 'index.html')) - phony_elem.write(outfile) - # Alias that runs the target defined above - self.create_target_alias('meson-coverage-html', outfile) - elem = NinjaBuildElement(self.all_outputs, os.path.join(htmloutdir, 'index.html'), 'CUSTOM_COMMAND', '') - - subproject_dir = self.build.get_subproject_dir() - command = [lcov_exe, - '--directory', self.environment.get_build_dir(), - '--capture', - '--output-file', covinfo, - '--no-checksum', - '&&', lcov_exe, - '--extract', - covinfo, - os.path.join(self.environment.get_source_dir(), '*'), - '--output-file', covinfo, - '&&', lcov_exe, - '--remove', - covinfo, - os.path.join(self.environment.get_source_dir(), subproject_dir, '*'), - '--output-file', covinfo, - '&&', genhtml_exe, - '--prefix', self.environment.get_build_dir(), - '--output-directory', htmloutdir, - '--title', 'Code coverage', - '--legend', - '--show-details', - covinfo] - elem.add_item('COMMAND', command) - elem.add_item('DESC', 'Generating HTML coverage report.') - elem.write(outfile) - elif gcovr_exe and gcovr_new_rootdir: - added_rule = True - htmloutdir = os.path.join(self.environment.get_log_dir(), 'coveragereport') - phony_elem = NinjaBuildElement(self.all_outputs, 'meson-coverage-html', 'phony', os.path.join(htmloutdir, 'index.html')) - phony_elem.write(outfile) - # Alias that runs the target defined above - self.create_target_alias('meson-coverage-html', outfile) - elem = NinjaBuildElement(self.all_outputs, os.path.join(htmloutdir, 'index.html'), 'CUSTOM_COMMAND', '') - command = [gcovr_exe, '--html', '--html-details', '-r', self.environment.get_build_dir(), - '-o', os.path.join(htmloutdir, 'index.html')] - elem.add_item('COMMAND', command) - elem.add_item('DESC', 'Generating HTML coverage report.') - elem.write(outfile) - if not added_rule: - mlog.warning('coverage requested but neither gcovr nor lcov/genhtml found.') + e = NinjaBuildElement(self.all_outputs, 'meson-coverage-xml', 'CUSTOM_COMMAND', 'PHONY') + self.generate_coverage_command(e, ['--xml']) + e.add_item('description', 'Generates XML coverage report.') + e.write(outfile) + # Alias that runs the target defined above + self.create_target_alias('meson-coverage-xml', outfile) + + e = NinjaBuildElement(self.all_outputs, 'meson-coverage-text', 'CUSTOM_COMMAND', 'PHONY') + self.generate_coverage_command(e, ['--text']) + e.add_item('description', 'Generates text coverage report.') + e.write(outfile) + # Alias that runs the target defined above + self.create_target_alias('meson-coverage-text', outfile) + + e = NinjaBuildElement(self.all_outputs, 'meson-coverage-html', 'CUSTOM_COMMAND', 'PHONY') + self.generate_coverage_command(e, ['--html']) + e.add_item('description', 'Generates HTML coverage report.') + e.write(outfile) + # Alias that runs the target defined above + self.create_target_alias('meson-coverage-html', outfile) def generate_install(self, outfile): install_data_file = os.path.join(self.environment.get_scratch_dir(), 'install.dat') diff --git a/mesonbuild/scripts/coverage.py b/mesonbuild/scripts/coverage.py index 2d1f8c3..916c84f 100644 --- a/mesonbuild/scripts/coverage.py +++ b/mesonbuild/scripts/coverage.py @@ -14,87 +14,135 @@ from mesonbuild import environment -import sys, os, subprocess, pathlib +import argparse, sys, os, subprocess, pathlib + +def coverage(outputs, source_root, subproject_root, build_root, log_dir): + outfiles = [] + exitcode = 0 -def coverage(source_root, build_root, log_dir): (gcovr_exe, gcovr_new_rootdir, lcov_exe, genhtml_exe) = environment.find_coverage_tools() - if gcovr_exe: - # gcovr >= 3.1 interprets rootdir differently - if gcovr_new_rootdir: - rootdir = build_root - else: - rootdir = source_root - subprocess.check_call([gcovr_exe, - '-x', - '-r', rootdir, - '-o', os.path.join(log_dir, 'coverage.xml'), - ]) - subprocess.check_call([gcovr_exe, - '-r', rootdir, - '-o', os.path.join(log_dir, 'coverage.txt'), - ]) - if lcov_exe and genhtml_exe: - htmloutdir = os.path.join(log_dir, 'coveragereport') - covinfo = os.path.join(log_dir, 'coverage.info') - initial_tracefile = covinfo + '.initial' - run_tracefile = covinfo + '.run' - raw_tracefile = covinfo + '.raw' - subprocess.check_call([lcov_exe, - '--directory', build_root, - '--capture', - '--initial', - '--output-file', - initial_tracefile]) - subprocess.check_call([lcov_exe, - '--directory', build_root, - '--capture', - '--output-file', run_tracefile, - '--no-checksum', - '--rc', 'lcov_branch_coverage=1', - ]) - # Join initial and test results. - subprocess.check_call([lcov_exe, - '-a', initial_tracefile, - '-a', run_tracefile, - '-o', raw_tracefile]) - # Remove all directories outside the source_root from the covinfo - subprocess.check_call([lcov_exe, - '--extract', raw_tracefile, - os.path.join(source_root, '*'), - '--output-file', covinfo]) - subprocess.check_call([genhtml_exe, - '--prefix', build_root, - '--output-directory', htmloutdir, - '--title', 'Code coverage', - '--legend', - '--show-details', - '--branch-coverage', - covinfo]) - elif gcovr_exe and gcovr_new_rootdir: - htmloutdir = os.path.join(log_dir, 'coveragereport') - subprocess.check_call([gcovr_exe, - '--html', - '--html-details', - '-r', build_root, - '-o', os.path.join(htmloutdir, 'index.html'), - ]) - if gcovr_exe: + + # gcovr >= 3.1 interprets rootdir differently + if gcovr_new_rootdir: + gcovr_rootdir = build_root + else: + gcovr_rootdir = source_root + + if not outputs or 'xml' in outputs: + if gcovr_exe: + subprocess.check_call([gcovr_exe, + '-x', + '-r', gcovr_rootdir, + '-e', subproject_root, + '-o', os.path.join(log_dir, 'coverage.xml'), + ]) + outfiles.append(('Xml', pathlib.Path(log_dir, 'coverage.xml'))) + elif outputs: + print('gcovr needed to generate Xml coverage report') + exitcode = 1 + + if not outputs or 'text' in outputs: + if gcovr_exe: + subprocess.check_call([gcovr_exe, + '-r', gcovr_rootdir, + '-e', subproject_root, + '-o', os.path.join(log_dir, 'coverage.txt'), + ]) + outfiles.append(('Text', pathlib.Path(log_dir, 'coverage.txt'))) + elif outputs: + print('gcovr needed to generate text coverage report') + exitcode = 1 + + if not outputs or 'html' in outputs: + if lcov_exe and genhtml_exe: + htmloutdir = os.path.join(log_dir, 'coveragereport') + covinfo = os.path.join(log_dir, 'coverage.info') + initial_tracefile = covinfo + '.initial' + run_tracefile = covinfo + '.run' + raw_tracefile = covinfo + '.raw' + subprocess.check_call([lcov_exe, + '--directory', build_root, + '--capture', + '--initial', + '--output-file', + initial_tracefile]) + subprocess.check_call([lcov_exe, + '--directory', build_root, + '--capture', + '--output-file', run_tracefile, + '--no-checksum', + '--rc', 'lcov_branch_coverage=1', + ]) + # Join initial and test results. + subprocess.check_call([lcov_exe, + '-a', initial_tracefile, + '-a', run_tracefile, + '-o', raw_tracefile]) + # Remove all directories outside the source_root from the covinfo + subprocess.check_call([lcov_exe, + '--extract', raw_tracefile, + os.path.join(source_root, '*'), + '--output-file', covinfo]) + # Remove all directories inside subproject dir + subprocess.check_call([lcov_exe, + '--remove', covinfo, + os.path.join(subproject_root, '*'), + '--output-file', covinfo]) + subprocess.check_call([genhtml_exe, + '--prefix', build_root, + '--output-directory', htmloutdir, + '--title', 'Code coverage', + '--legend', + '--show-details', + '--branch-coverage', + covinfo]) + outfiles.append(('Html', pathlib.Path(htmloutdir, 'index.html'))) + elif gcovr_exe and gcovr_new_rootdir: + htmloutdir = os.path.join(log_dir, 'coveragereport') + if not os.path.isdir(htmloutdir): + os.mkdir(htmloutdir) + subprocess.check_call([gcovr_exe, + '--html', + '--html-details', + '-r', build_root, + '-e', subproject_root, + '-o', os.path.join(htmloutdir, 'index.html'), + ]) + outfiles.append(('Html', pathlib.Path(htmloutdir, 'index.html'))) + elif outputs: + print('lcov/genhtml or gcovr >= 3.1 needed to generate Html coverage report') + exitcode = 1 + + if not outputs and not outfiles: + print('Need gcovr or lcov/genhtml to generate any coverage reports') + exitcode = 1 + + if outfiles: print('') - print('XML coverage report can be found at', - pathlib.Path(log_dir, 'coverage.xml').as_uri()) - print('Text coverage report can be found at', - pathlib.Path(log_dir, 'coverage.txt').as_uri()) - if (lcov_exe and genhtml_exe) or (gcovr_exe and gcovr_new_rootdir): - print('Html coverage report can be found at', - pathlib.Path(htmloutdir, 'index.html').as_uri()) - return 0 + for (filetype, path) in outfiles: + print(filetype + ' coverage report can be found at', path.as_uri()) + + return exitcode def run(args): if not os.path.isfile('build.ninja'): print('Coverage currently only works with the Ninja backend.') return 1 - source_root, build_root, log_dir = args[:] - return coverage(source_root, build_root, log_dir) + parser = argparse.ArgumentParser(description='Generate coverage reports') + parser.add_argument('--text', dest='outputs', action='append_const', + const='text', help='generate Text report') + parser.add_argument('--xml', dest='outputs', action='append_const', + const='xml', help='generate Xml report') + parser.add_argument('--html', dest='outputs', action='append_const', + const='html', help='generate Html report') + parser.add_argument('source_root') + parser.add_argument('subproject_root') + parser.add_argument('build_root') + parser.add_argument('log_dir') + options = parser.parse_args(args) + return coverage(options.outputs, options.source_root, + options.subproject_root, options.build_root, + options.log_dir) if __name__ == '__main__': sys.exit(run(sys.argv[1:])) |