Skip to content

Commit

Permalink
Add support for demangling C++ function names using c++filt
Browse files Browse the repository at this point in the history
Signed-off-by: Yury V. Zaytsev <yury.zaytsev@traveltainment.de>
  • Loading branch information
zyv committed Jan 11, 2016
1 parent fd2b9be commit c688502
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 5 deletions.
4 changes: 3 additions & 1 deletion README.md
Expand Up @@ -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
Expand Down Expand Up @@ -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).
Expand Down
32 changes: 28 additions & 4 deletions lcov_cobertura/lcov_cobertura.py
Expand Up @@ -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
Expand All @@ -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`.
Expand All @@ -44,13 +59,16 @@ 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:
excludes = []
self.lcov_data = lcov_data
self.base_dir = base_dir
self.excludes = excludes
self.demangle = demangle

def convert(self):
"""
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -349,7 +367,7 @@ def main(argv):
"""

parser = OptionParser()
parser.usage = 'lcov_cobertura.py lcov-file.dat [-b source/dir] [-e <exclude packages regex>] [-o output.xml]'
parser.usage = 'lcov_cobertura.py lcov-file.dat [-b source/dir] [-e <exclude packages regex>] [-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',
Expand All @@ -360,16 +378,22 @@ 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)

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)
Expand Down

0 comments on commit c688502

Please sign in to comment.