Merge pull request #3145 from thejk/gcovr

Support gcovr >= 3.1 and add gcovr html report as fallback
pull/3219/head
Jussi Pakkanen 7 years ago committed by GitHub
commit ae0e469b06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      docs/markdown/Feature-autodetection.md
  2. 2
      docs/markdown/Unit-tests.md
  3. 24
      mesonbuild/backend/ninjabackend.py
  4. 21
      mesonbuild/environment.py
  5. 21
      mesonbuild/scripts/coverage.py
  6. 7
      run_unittests.py

@ -16,4 +16,4 @@ If you do not wish to use CCache for some reason, just specify your compiler wit
Coverage
--
When doing a code coverage build, Meson will check the existence of binaries `gcovr`, `lcov` and `genhtml`. If the first one is found, it will create targets called *coverage-text* and *coverage-xml*. If the latter two are found, it generates the target *coverage-html*. You can then generate coverage reports just by calling e.g. `ninja coverage-xml`.
When doing a code coverage build, Meson will check the existence of binaries `gcovr`, `lcov` and `genhtml`. If the first one is found, it will create targets called *coverage-text* and *coverage-xml*. If the latter two or a new enough `gcovr` is found, it generates the target *coverage-html*. You can then generate coverage reports just by calling e.g. `ninja coverage-xml`.

@ -30,7 +30,7 @@ Note how you need to specify multiple values as an array.
Coverage
--
If you enable coverage measurements by giving Meson the command line flag `-Db_coverage=true`, you can generate coverage reports. Meson will autodetect what coverage generator tools you have installed and will generate the corresponding targets. These targets are `coverage-xml` and `coverage-text` which are both provided by [Gcovr](http://gcovr.com) and `coverage-html`, which requires [Lcov](https://ltp.sourceforge.io/coverage/lcov.php) and [GenHTML](https://linux.die.net/man/1/genhtml).
If you enable coverage measurements by giving Meson the command line flag `-Db_coverage=true`, you can generate coverage reports. Meson will autodetect what coverage generator tools you have installed and will generate the corresponding targets. These targets are `coverage-xml` and `coverage-text` which are both provided by [Gcovr](http://gcovr.com) and `coverage-html`, which requires [Lcov](https://ltp.sourceforge.io/coverage/lcov.php) and [GenHTML](https://linux.die.net/man/1/genhtml) or [Gcovr](http://gcovr.com) with html support.
The output of these commands is written to the log directory `meson-logs` in your build directory.

@ -628,19 +628,24 @@ int dummy;
self.generate_coverage_legacy_rules(outfile)
def generate_coverage_legacy_rules(self, outfile):
(gcovr_exe, lcov_exe, genhtml_exe) = environment.find_coverage_tools()
(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', self.environment.get_source_dir(),
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', self.environment.get_source_dir(),
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)
@ -682,6 +687,19 @@ int dummy;
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.')

@ -76,19 +76,32 @@ cflags_mapping = {'c': 'CFLAGS',
'd': 'DFLAGS',
'vala': 'VALAFLAGS'}
def detect_gcovr(version='3.1', log=False):
gcovr_exe = 'gcovr'
try:
p, found = Popen_safe([gcovr_exe, '--version'])[0:2]
except (FileNotFoundError, PermissionError):
# Doesn't exist in PATH or isn't executable
return None, None
found = search_version(found)
if p.returncode == 0:
if log:
mlog.log('Found gcovr-{} at {}'.format(found, shlex.quote(shutil.which(gcovr_exe))))
return gcovr_exe, mesonlib.version_compare(found, '>=' + version)
return None, None
def find_coverage_tools():
gcovr_exe = 'gcovr'
gcovr_exe, gcovr_new_rootdir = detect_gcovr()
lcov_exe = 'lcov'
genhtml_exe = 'genhtml'
if not mesonlib.exe_exists([gcovr_exe, '--version']):
gcovr_exe = None
if not mesonlib.exe_exists([lcov_exe, '--version']):
lcov_exe = None
if not mesonlib.exe_exists([genhtml_exe, '--version']):
genhtml_exe = None
return gcovr_exe, lcov_exe, genhtml_exe
return gcovr_exe, gcovr_new_rootdir, lcov_exe, genhtml_exe
def detect_ninja(version='1.5', log=False):
for n in ['ninja', 'ninja-build']:

@ -17,15 +17,20 @@ from mesonbuild import environment
import sys, os, subprocess, pathlib
def coverage(source_root, build_root, log_dir):
(gcovr_exe, lcov_exe, genhtml_exe) = environment.find_coverage_tools()
(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', source_root,
'-r', rootdir,
'-o', os.path.join(log_dir, 'coverage.xml'),
])
subprocess.check_call([gcovr_exe,
'-r', source_root,
'-r', rootdir,
'-o', os.path.join(log_dir, 'coverage.txt'),
])
if lcov_exe and genhtml_exe:
@ -65,13 +70,21 @@ def coverage(source_root, build_root, log_dir):
'--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:
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:
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

@ -2639,10 +2639,11 @@ class LinuxlikeTests(BasePlatformTests):
self.assertIn("-fsanitize=address", i["command"])
def test_coverage(self):
if not shutil.which('gcovr'):
gcovr_exe, gcovr_new_rootdir = mesonbuild.environment.detect_gcovr()
if not gcovr_exe:
raise unittest.SkipTest('gcovr not found')
if not shutil.which('genhtml'):
raise unittest.SkipTest('genhtml not found')
if not shutil.which('genhtml') and not gcovr_new_rootdir:
raise unittest.SkipTest('genhtml not found and gcovr is too old')
if 'clang' in os.environ.get('CC', ''):
# We need to use llvm-cov instead of gcovr with clang
raise unittest.SkipTest('Coverage does not work with clang right now, help wanted!')

Loading…
Cancel
Save