Skip to content

Commit 5005b29

Browse files
committed
MAINT: runtests: enable generation of C code coverage info via gcov
1 parent a3e8c12 commit 5005b29

File tree

1 file changed

+84
-1
lines changed

1 file changed

+84
-1
lines changed

runtests.py

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@
1212
$ python runtests.py --ipython
1313
$ python runtests.py --python somescript.py
1414
15+
Run a debugger:
16+
17+
$ gdb --args python runtests.py [...other args...]
18+
19+
Generate C code coverage listing under build/lcov/:
20+
(requires http://ltp.sourceforge.net/coverage/lcov.php)
21+
22+
$ python runtests.py --gcov [...other args...]
23+
$ python runtests.py --lcov-html
24+
1525
"""
1626

1727
#
@@ -64,6 +74,13 @@ def main(argv):
6474
parser.add_argument("--coverage", action="store_true", default=False,
6575
help=("report coverage of project code. HTML output goes "
6676
"under build/coverage"))
77+
parser.add_argument("--gcov", action="store_true", default=False,
78+
help=("enable C code coverage via gcov (requires GCC). "
79+
"gcov output goes to build/**/*.gc*"))
80+
parser.add_argument("--lcov-html", action="store_true", default=False,
81+
help=("produce HTML for C code coverage information "
82+
"from a previous run with --gcov. "
83+
"HTML output goes to build/lcov/"))
6784
parser.add_argument("--mode", "-m", default="fast",
6885
help="'fast', 'full', or something that could be "
6986
"passed to nosetests -A [default: fast]")
@@ -87,10 +104,18 @@ def main(argv):
87104
help="Arguments to pass to Nose, Python or shell")
88105
args = parser.parse_args(argv)
89106

107+
if args.lcov_html:
108+
# generate C code coverage output
109+
lcov_generate()
110+
sys.exit(0)
111+
90112
if args.pythonpath:
91113
for p in reversed(args.pythonpath.split(os.pathsep)):
92114
sys.path.insert(0, p)
93115

116+
if args.gcov:
117+
gcov_reset_counters()
118+
94119
if not args.no_build:
95120
site_dir = build_project(args)
96121
sys.path.insert(0, site_dir)
@@ -194,6 +219,7 @@ def test(*a, **kw):
194219
else:
195220
sys.exit(1)
196221

222+
197223
def build_project(args):
198224
"""
199225
Build a dev version of the project.
@@ -220,10 +246,21 @@ def build_project(args):
220246
# Always use ccache, if installed
221247
env['PATH'] = os.pathsep.join(EXTRA_PATH + env.get('PATH', '').split(os.pathsep))
222248

223-
if args.debug:
249+
if args.debug or args.gcov:
224250
# assume everyone uses gcc/gfortran
225251
env['OPT'] = '-O0 -ggdb'
226252
env['FOPT'] = '-O0 -ggdb'
253+
if args.gcov:
254+
import distutils.sysconfig
255+
cvars = distutils.sysconfig.get_config_vars()
256+
env['OPT'] = '-O0 -ggdb'
257+
env['FOPT'] = '-O0 -ggdb'
258+
env['CC'] = cvars['CC'] + ' --coverage'
259+
env['CXX'] = cvars['CXX'] + ' --coverage'
260+
env['F77'] = 'gfortran --coverage '
261+
env['F90'] = 'gfortran --coverage '
262+
env['LDSHARED'] = cvars['LDSHARED'] + ' --coverage'
263+
env['LDFLAGS'] = " ".join(cvars['LDSHARED'].split()[1:]) + ' --coverage'
227264
cmd += ["build"]
228265

229266
cmd += ['install', '--prefix=' + dst_dir]
@@ -270,6 +307,52 @@ def build_project(args):
270307

271308
return site_dir
272309

310+
311+
#
312+
# GCOV support
313+
#
314+
def gcov_reset_counters():
315+
print("Removing previous GCOV .gcda files...")
316+
build_dir = os.path.join(ROOT_DIR, 'build')
317+
for dirpath, dirnames, filenames in os.walk(build_dir):
318+
for fn in filenames:
319+
if fn.endswith('.gcda') or fn.endswith('.da'):
320+
pth = os.path.join(dirpath, fn)
321+
os.unlink(pth)
322+
323+
#
324+
# LCOV support
325+
#
326+
327+
LCOV_OUTPUT_FILE = os.path.join(ROOT_DIR, 'build', 'lcov.out')
328+
LCOV_HTML_DIR = os.path.join(ROOT_DIR, 'build', 'lcov')
329+
330+
def lcov_generate():
331+
try: os.unlink(LCOV_OUTPUT_FILE)
332+
except OSError: pass
333+
try: shutil.rmtree(LCOV_HTML_DIR)
334+
except OSError: pass
335+
336+
print("Capturing lcov info...")
337+
subprocess.call(['lcov', '-q', '-c',
338+
'-d', os.path.join(ROOT_DIR, 'build'),
339+
'-b', ROOT_DIR,
340+
'--output-file', LCOV_OUTPUT_FILE])
341+
342+
print("Generating lcov HTML output...")
343+
ret = subprocess.call(['genhtml', '-q', LCOV_OUTPUT_FILE,
344+
'--output-directory', LCOV_HTML_DIR,
345+
'--legend', '--highlight'])
346+
if ret != 0:
347+
print("genhtml failed!")
348+
else:
349+
print("HTML output generated under build/lcov/")
350+
351+
352+
#
353+
# Python 3 support
354+
#
355+
273356
if sys.version_info[0] >= 3:
274357
import builtins
275358
exec_ = getattr(builtins, "exec")

0 commit comments

Comments
 (0)