diff --git a/CMakeLists.txt b/CMakeLists.txt index 44a7c278..df36a04d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,17 +12,37 @@ include(cmake/xo-bootstrap-macros.cmake) # unit test setup enable_testing() -# activate code coverage for all executables + libraries (when configured with -DCODE_COVERAGE=ON) -add_code_coverage() -# 1. assuming that /nix/store/ prefixes .hpp files belonging to gcc, catch2 etc. -# we're not interested in code coverage for these sources. -# 2. exclude the utest/ subdir, we don't need coverage on the unit tests themselves; -# rather, want coverage on the code that the unit tests exercise. +# ---------------------------------------------------------------- +# cmake -DCMAKE_BUILD_TYPE=coverage + +if (NOT DEFINED PROJECT_CXX_FLAGS_COVERAGE) + # note: for clang would use -fprofile-instr-generate -fcoverage-mapping here instead and also at link time + set(PROJECT_CXX_FLAGS_COVERAGE ${PROJECT_CXX_FLAGS} -ggdb -Og -fprofile-arcs -ftest-coverage + CACHE STRING "coverage c++ compiler flags") +endif() +message("-- PROJECT_CXX_FLAGS_COVERAGE: coverage c++ flags are [${PROJECT_CXX_FLAGS_COVERAGE}]") + +add_compile_options("$<$:${PROJECT_CXX_FLAGS_COVERAGE}>") +# when -DCMAKE_BUILD_TYPE=coverage, link executables with gcov +link_libraries("$<$:gcov>") + +find_program(LCOV_EXECUTABLE NAMES lcov) +find_program(GENHTML_EXECUTABLE NAMES genhtml) + +# with coverage build: +# 1. invoke instrumented executables for which you want coverage: +# (cd path/to/build && ctest) +# 2. post-process low-level coverage data +# (path/to/build/gen-ccov) +# 3. point browser to generated html data +# file:///path/to/build/ccov/html/index.html # -# NOTE: this seems to work only with the 'ccov-all' target. In particular, doesn't seem to do anything with the 'ccov' target -# -add_code_coverage_all_targets(EXCLUDE /nix/store/* ${PROJECT_SOURCE_DIR}/utest/* ${PROJECT_BINARY_DIR}/local/* ${PROJECT_SOURCE_DIR}/repo/*) +configure_file( + ${PROJECT_SOURCE_DIR}/cmake/gen-ccov.in + ${PROJECT_BINARY_DIR}/gen-ccov) + +file(CHMOD ${PROJECT_BINARY_DIR}/gen-ccov PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) # ---------------------------------------------------------------- # c++ settings diff --git a/README.md b/README.md index a5047565..09c36590 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,11 @@ Relative to `std::ratio`: Relative to `boost::ratio`: 1. Streamlined, assumes modern compiler support +## Documentation + +- xo-ratio documentation under construction +- unit test coverage here: [coverage](https://rconybea.github.io/web/xo-ratio/ccov/html/index.html) + ## Getting Started ### install dependencies diff --git a/cmake/gen-ccov.in b/cmake/gen-ccov.in new file mode 100644 index 00000000..e335aed4 --- /dev/null +++ b/cmake/gen-ccov.in @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +srcdir=@PROJECT_SOURCE_DIR@ +builddir=@PROJECT_BINARY_DIR@ +lcov=@LCOV_EXECUTABLE@ +genhtml=@GENHTML_EXECUTABLE@ + +if [[ $lcov == "LCOV_EXECUTABLE-NOTFOUND" ]]; then + echo "gen-ccov: lcov executable not found" + exit 1 +fi + +if [[ $genhtml == "GENHTML_EXECUTABLE-NOTFOUND" ]]; then + echo "gen-ccov: genhtml executable not found" + exit 1 +fi + +mkdir $builddir/ccov + +$srcdir/cmake/lcov-harness $srcdir $builddir $builddir/ccov/out $lcov $genhtml diff --git a/cmake/lcov-harness b/cmake/lcov-harness new file mode 100755 index 00000000..27ac8be9 --- /dev/null +++ b/cmake/lcov-harness @@ -0,0 +1,114 @@ +#!/usr/bin/env bash + +srcdir=$1 +builddir=$2 +outputstem=$3 +lcov=$4 +genhtml=$5 + +if [[ -z "${srcdir}" ]]; then + echo "lcov-harness: expected non-empty srcdir" + exit 1 +fi + +if [[ -z ${builddir} ]]; then + echo "lcov-harness: expected non-empty builddir" + exit 1 +fi + +if [[ -z ${outputstem} ]]; then + echo "lcov-harness: expected non-empty outputstem" + exit 1 +fi + +if [[ -z ${lcov} ]]; then + echo "lcov-harness: exepcted non-empty lcov" + exit 1 +fi + +if [[ -z ${genhtml} ]]; then + echo "lcov-harness: expected non-empty genhtml" + exit 1 +fi + +# 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 +# +primarydirs=$(cd ${builddir} && find -name '*.gcno' \ + | xargs --replace=xx dirname xx \ + | uniq \ + | sed -e 's:^\./::') + +#echo "primarydirs=${primarydirs}" + +cmd="${lcov} --output ${outputstem}.info --capture --ignore-errors source" + +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 + +# 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 + +# 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