#!/usr/bin/env bash srcdir=$1 builddir=$2 outputstem=$3 # optional lcov=$4 # optional genhtml=$5 # optional if [[ -z "${srcdir}" ]]; then echo "xo-cmake-lcov-harness: expected non-empty srcdir" exit 1 fi if [[ -z "${builddir}" ]]; then echo "xo-cmake-lcov-harness: expected non-empty builddir" exit 1 fi if [[ -z ${outputstem} ]]; then outputstem=$builddir/ccov/out fi if [[ -z ${lcov} ]]; then lcov=@LCOV_EXECUTABLE@ if [[ $lcov == "LCOV_EXECUTABLE-NOTFOUND" ]]; then echo "xo-cmake-lcov-harness: lcov executable not found during xo-cmake build/install" exit 1 fi fi if [[ -z ${genhtml} ]]; then genhtml=@GENHTML_EXECUTABLE@ if [[ $genhtml == "GENHTML_EXECUTABLE-NOTFOUND" ]]; then echo "xo-cmake-lcov-harness: genhtml executable not found during xo-cmake build/install" exit 1 fi fi mkdir -p $builddir/ccov # directory stems for location of {.gcda, gcno} coverage information, # # if we have source tree: # # ${srcdir} # +- foo # | \- foo.cpp # \- bar # \- quux # +- quux.cpp # \- quux_main.cpp # # then we expect build tree: # # ${builddir} # +- foo # | \- CMakeFiles # | \- foo_target.dir # | +- foo.cpp.gcda # | \- foo.cpp.gcno # +- bar # \- quux # \- CMakeFiles # \- target4quux.dir # +- quux.cpp.gcda # +- quux.cpp.gcno # +- quux_main.cpp.gcda # \- quux_main.cpp.gcno # # in which case will have cmd_body: # # ${primarydirs} # ./foo/CMakeFiles/foo_target.dir # ./bar/quux/CMakeFiles/target4quux.dir # # here foo_target, quux_target are whatever build is using for corresponding cmake target names. # # We want to invoke lcov like: # # lcov --capture \ # --output ${builddir}/ccov \ # --exclude /utest/ \ # --base-directory ${srcdir}/foo --directory ${builddir}/foo/CMakeFiles/foo_target.dir \ # --base-directory ${srcdir}/bar/quux --directory ${builddir}/bar/quux/CMakeFiles/target4quux.dir # # collect unique directories containing .gcno files, # then remove any that are subdirectories of another in the list. # lcov scans recursively, so passing both parent/ and parent/facet/ # causes "duplicate file" errors. _alldirs=$(cd ${builddir} && find -name '*.gcno' \ | xargs --replace=xx dirname xx \ | sort -u \ | sed -e 's:^\./::') primarydirs="" for d in ${_alldirs}; do # check if any existing entry is a prefix of d is_nested=false for p in ${primarydirs}; do case "${d}" in "${p}"/*) is_nested=true; break ;; esac done if ! ${is_nested}; then primarydirs="${primarydirs} ${d}" fi done #echo "primarydirs=${primarydirs}" cmd="${lcov} --output ${outputstem}.info --capture --ignore-errors source --ignore-errors empty --ignore-errors inconsistent,inconsistent" for bdir in ${primarydirs}; do sdir=$(dirname $(dirname ${bdir})) cmd="${cmd} --base-directory ${srcdir}/${sdir} --directory ${builddir}/${bdir}" done #echo cmd=${cmd} set -x # capture ${cmd} # keep only files with paths under source tree # (don't want coverage for external libraries such as libstdc++ etc) ${lcov} --extract ${outputstem}.info "${srcdir}/*" --output ${outputstem}2.info --ignore-errors inconsistent,inconsistent # remove unit test dirs # (we're interested in coverage of our installed code, not of the unit tests that exercise it) ${lcov} --remove ${outputstem}2.info '*/utest/*' --output ${outputstem}3.info --ignore-errors inconsistent,inconsistent # generate .html tree mkdir -p ${builddir}/ccov/html ${genhtml} --ignore-errors source --show-details --prefix ${srcdir} --output-directory ${builddir}/ccov/html ${outputstem}3.info # also send report to stdout ${lcov} --list ${outputstem}3.info