Use standalone coverage script for legacy targets

ninja coverage -> generate all possible reports (text, xml, html)
                  depending on gcovr and/or lcov/genhtml availability.
ninja coverage-html -> generate only html report
ninja coverage-xml -> generate only xml report
ninja coverage-text -> generate only text report

Make all targets phony, the old legacy rules where just annoying as
you would have to remove the old report before being able to generate
a new one.

ninja coverage succeeds if it can generate at least one report.
ninja coverage-* only succeeds if it can generate the requested report
pull/3242/head
Joel Klinghed 7 years ago
parent 50c66f1f5c
commit 79bb1df04f
  1. 108
      mesonbuild/backend/ninjabackend.py
  2. 182
      mesonbuild/scripts/coverage.py

@ -613,13 +613,17 @@ 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(),
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 +631,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')

@ -14,87 +14,123 @@
from mesonbuild import environment
import sys, os, subprocess, pathlib
import argparse, sys, os, subprocess, pathlib
def coverage(outputs, source_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,
'-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,
'-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])
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')
subprocess.check_call([gcovr_exe,
'--html',
'--html-details',
'-r', build_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('build_root')
parser.add_argument('log_dir')
options = parser.parse_args(args)
return coverage(options.outputs, options.source_root,
options.build_root, options.log_dir)
if __name__ == '__main__':
sys.exit(run(sys.argv[1:]))

Loading…
Cancel
Save