From c688502ff583f5b1b3e4d7c4061992bbbb8d96d0 Mon Sep 17 00:00:00 2001 From: "Yury V. Zaytsev" Date: Mon, 11 Jan 2016 15:23:29 +0100 Subject: [PATCH] Add support for demangling C++ function names using c++filt Signed-off-by: Yury V. Zaytsev --- README.md | 4 +++- lcov_cobertura/lcov_cobertura.py | 32 ++++++++++++++++++++++++++++---- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 21b4b0b..1e79e5d 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,10 @@ python lcov_cobertura.py lcov-file.dat - `-b/--base-dir` - (Optional) Directory where source files are located. Defaults to the current directory - `-e/--excludes` - (Optional) Comma-separated list of regexes of packages to exclude - `-o/--output` - (Optional) Path to store cobertura xml file. _Defaults to ./coverage.xml_ + - `-d/--demangle` - (Optional) Demangle C++ function names. _Requires c++filt_ ```bash -python lcov_cobertura.py lcov-file.dat --base-dir src/dir --excludes test.lib --output build/coverage.xml +python lcov_cobertura.py lcov-file.dat --base-dir src/dir --excludes test.lib --output build/coverage.xml --demangle ``` ## Usage as a Python module @@ -56,6 +57,7 @@ This project is made possible due to the efforts of these fine people: - [Eric Wendelin](http://eriwen.com) - [Björge Dijkstra](https://github.com/bjd) - [Jon Schewe](http://mtu.net/~jpschewe) + - [Yury V. Zaytsev](http://yury.zaytsev.net) ## License This project is provided under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0). diff --git a/lcov_cobertura/lcov_cobertura.py b/lcov_cobertura/lcov_cobertura.py index 6753e25..37fb5b3 100755 --- a/lcov_cobertura/lcov_cobertura.py +++ b/lcov_cobertura/lcov_cobertura.py @@ -13,13 +13,28 @@ import sys import os import time +import subprocess from xml.dom import minidom from optparse import OptionParser +from distutils.spawn import find_executable + +CPPFILT = "c++filt" +HAVE_CPPFILT = False + +if find_executable(CPPFILT) is not None: + HAVE_CPPFILT = True + VERSION = '1.5' __all__ = ['LcovCobertura'] +def demangle(name): + pipe = subprocess.Popen([CPPFILT, name], stdin=subprocess.PIPE, stdout=subprocess.PIPE) + stdout, _ = pipe.communicate() + return stdout.split("\n")[0] + + class LcovCobertura(object): """ Converts code coverage report files in lcov format to Cobertura's XML @@ -33,7 +48,7 @@ class LcovCobertura(object): >>> print(cobertura_xml) """ - def __init__(self, lcov_data, base_dir='.', excludes=None): + def __init__(self, lcov_data, base_dir='.', excludes=None, demangle=False): """ Create a new :class:`LcovCobertura` object using the given `lcov_data` and `options`. @@ -44,6 +59,8 @@ def __init__(self, lcov_data, base_dir='.', excludes=None): :type base_dir: string :param excludes: list of regexes to packages as excluded :type excludes: [string] + :param demangle: whether to demangle function names using c++filt + :type demangle: bool """ if not excludes: @@ -51,6 +68,7 @@ def __init__(self, lcov_data, base_dir='.', excludes=None): self.lcov_data = lcov_data self.base_dir = base_dir self.excludes = excludes + self.demangle = demangle def convert(self): """ @@ -253,7 +271,7 @@ def generate_cobertura_xml(self, coverage_data): methods_el = self._el(document, 'methods', {}) for method_name, (line, hits) in list(class_data['methods'].items()): method_el = self._el(document, 'method', { - 'name': method_name, + 'name': demangle(method_name) if self.demangle else method_name, 'signature': '()V', 'line-rate': '1.0' if int(hits) > 0 else '0.0', 'branch-rate': '1.0' if int(hits) > 0 else '0.0', @@ -349,7 +367,7 @@ def main(argv): """ parser = OptionParser() - parser.usage = 'lcov_cobertura.py lcov-file.dat [-b source/dir] [-e ] [-o output.xml]' + parser.usage = 'lcov_cobertura.py lcov-file.dat [-b source/dir] [-e ] [-o output.xml] [-d]' parser.description = 'Converts lcov output to cobertura-compatible XML' parser.add_option('-b', '--base-dir', action='store', help='Directory where source files are located', @@ -360,8 +378,14 @@ def main(argv): parser.add_option('-o', '--output', help='Path to store cobertura xml file', action='store', dest='output', default='coverage.xml') + parser.add_option('-d', '--demangle', + help='Demangle C++ function names using %s' % CPPFILT, + action='store_true', dest='demangle', default=False) (options, args) = parser.parse_args(args=argv) + if options.demangle and not HAVE_CPPFILT: + raise RuntimeError("C++ filter executable (%s) not found!" % CPPFILT) + if len(args) != 2: print(main.__doc__) sys.exit(1) @@ -369,7 +393,7 @@ def main(argv): try: with open(args[1], 'r') as lcov_file: lcov_data = lcov_file.read() - lcov_cobertura = LcovCobertura(lcov_data, options.base_dir, options.excludes) + lcov_cobertura = LcovCobertura(lcov_data, options.base_dir, options.excludes, options.demangle) cobertura_xml = lcov_cobertura.convert() with open(options.output, mode='wt') as output_file: output_file.write(cobertura_xml)