Add 'xo-cmake/' from commit 'f510700b99'

git-subtree-dir: xo-cmake
git-subtree-mainline: c88807597e
git-subtree-split: f510700b99
This commit is contained in:
Roland Conybeare 2025-05-10 16:23:49 -05:00
commit 2329d118c2
15 changed files with 5777 additions and 0 deletions

View file

@ -0,0 +1,74 @@
name: XO cmake xo-cpp-builder
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
BUILD_TYPE: Release
jobs:
build:
# The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
# You can convert this to a matrix build if you need cross-platform coverage.
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
runs-on: ubuntu-latest
container:
# custom docker image. see github.com:rconybea/docker-xo-builder for definition
image: ghcr.io/rconybea/docker-xo-builder:v1
steps:
- name: xo-cmake
run: |
# treat github.com as known host to prevent shtoopid SSL errors
mkdir -p ~/.ssh
ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts
cat ~/.ssh/known_hosts
git config --global http.sslVerify false
XO_NAME=xo-cmake
XO_SRC=repo/${XO_NAME}
XO_BUILDDIR=${{github.workspace}}/build_${XO_NAME}
PREFIX=${{github.workspace}}/local
XO_REPO=https://github.com/rconybea/xo-cmake.git
mkdir -p ${XO_SRC}
mkdir -p ${XO_BUILDDIR}
echo "::group::clone ${XO_NAME}"
export GIT_SSH_COMMAND='ssh -o StrictHostKeyChecking=no'
export GIT_SSL_NOVERIFY=true
git clone ${XO_REPO} ${XO_SRC}
echo "::endgroup"
echo "::group::configure ${XO_NAME}"
cmake -B ${XO_BUILDDIR} -DCMAKE_INSTALL_PREFIX=${PREFIX} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} ${XO_SRC}
echo "::endgroup"
echo "::group::compile ${XO_NAME}"
cmake --build ${XO_BUILDDIR} -j
echo "::endgroup"
echo "::group::local install ${XO_NAME}"
cmake --install ${XO_BUILDDIR}
echo "::endgroup"
echo "::group::local dir tree"
tree ${PREFIX}
echo "::endgroup"
echo "::group::verify"
ls -l ${PREFIX}/bin/xo-cmake-config
${PREFIX}/bin/xo-cmake-config --help
echo "$(${PREFIX}/bin/xo-cmake-config --lcov-exe)"
echo "$(${PREFIX}/bin/xo-cmake-config --genhtml-exe)"
echo "$(${PREFIX}/bin/xo-cmake-config --lcov-harness-exe)"
echo "$(${PREFIX}/bin/xo-cmake-config --gen-ccov-template)"
echo "$(${PREFIX}/bin/xo-cmake-config --cmake-module-path)"
echo "::endgroup"

3
xo-cmake/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
.projectile
.build
compile_commands.json

67
xo-cmake/CMakeLists.txt Normal file
View file

@ -0,0 +1,67 @@
cmake_minimum_required(VERSION 3.10)
project(xo_macros VERSION 1.0)
# if any are useful for this project..
include (GNUInstallDirs)
set(XO_PROJECT_NAME xo_macros)
# LCOV_EXECUTABLE,GENHTML_EXECUTABLE: needed by xo-cmake-lcov-harness.in
find_program(LCOV_EXECUTABLE NAMES lcov)
find_program(GENHTML_EXECUTABLE NAMES genhtml)
configure_file(
${PROJECT_SOURCE_DIR}/bin/xo-cmake-lcov-harness.in
${PROJECT_BINARY_DIR}/xo-cmake-lcov-harness
@ONLY
)
configure_file(
${PROJECT_SOURCE_DIR}/bin/xo-cmake-config.in
${PROJECT_BINARY_DIR}/xo-cmake-config
@ONLY
)
configure_file(
${PROJECT_SOURCE_DIR}/bin/xo-build.in
${PROJECT_BINARY_DIR}/xo-build
@ONLY
)
install(
FILES
"cmake/xo_macros/xo-project-macros.cmake"
"cmake/xo_macros/xo_cxx.cmake"
"cmake/xo_macros/code-coverage.cmake"
PERMISSIONS OWNER_READ GROUP_READ WORLD_READ
DESTINATION ${CMAKE_INSTALL_DATADIR}/cmake/xo_macros
)
install(
FILES
"${PROJECT_BINARY_DIR}/xo-cmake-lcov-harness"
"${PROJECT_BINARY_DIR}/xo-cmake-config"
"${PROJECT_BINARY_DIR}/xo-build"
PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(
FILES
"etc/xo/subsystem-list"
PERMISSIONS OWNER_READ GROUP_READ WORLD_READ
DESTINATION ${CMAKE_INSTALL_DATADIR}/etc/xo
)
# The cmake template gen-ccov.in should be expanded in downstream project;
# to pickup downstream project's PROJECT_SOURCE_DIR / PROJECT_BINARY_DIR
#
install(
FILES
"share/xo-macros/gen-ccov.in"
"share/xo-macros/Doxyfile.in"
"share/xo-macros/xo-bootstrap-macros.cmake"
PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
DESTINATION ${CMAKE_INSTALL_DATADIR}/xo-macros
)

120
xo-cmake/FAQ Normal file
View file

@ -0,0 +1,120 @@
# Unit test build can't find library:
Missing dependency at link time
```
set(SELF_EXE mumble)
set(SELF_SRCS mumble.cpp)
xo_add_executable(${SELF_EXE} ${SELF_SRCS})
xo_self_dependency(${SELF_EXE} xo_foo)
```
and build fails with `cannot find -lxo_bar`.
Possible causes:
1. missing cmake export for a dependency of `xo_foo`.
Check `xo_foo/cmake/xo_fooConfig.cmake.in`:
If `xo_foo` depends on `xo_bar`, then cmake export
needs to have `find_dependency(xo_bar)`
```
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
find_dependency(xo_bar)
find_dependency(xo_maybemoar)
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
check_required_components("@PROJECT_NAME@")
```
# Howto introduce sphinx documentation to an XO project
REMINDER: must re-run `cmake` after introducing skeleton, since build copies files from docs/ to .build directory
Minimal skeleton for docs/ directory:
```
xo-foo
+- docs
+- CMakeLists.txt
+- conf.py
+- _static
+- index.rst
```
CMakeLists.txt:
```
# xo-foo/docs/CMakeLists.txt
xo_doxygen_collect_deps()
xo_docdir_doxygen_config()
xo_docdir_sphinx_config(
index.rst
)
```
conf.py:
```
# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
project = 'xo jit documentation'
copyright = '2024, Roland Conybeare'
author = 'Roland Conybeare'
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
#extensions = []
extensions = [ "breathe",
"sphinx.ext.mathjax", # inline math
"sphinx.ext.autodoc", # generate info from docstrings
"sphinxcontrib.ditaa", # diagrams-through-ascii-art
"sphinxcontrib.plantuml" # text -> uml diagrams
]
# note: breathe requires doxygen xml output -> must have GENERATE_XML = YES in Doxyfile.in
# match project name in Doxyfile.in
breathe_default_project = "xodoxxml"
templates_path = ['_templates']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
pygments_style = 'sphinx'
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
#html_theme = 'alabaster'
html_theme = 'sphinx_rtd_theme'
html_static_path = ['_static']
html_favicon = '_static/img/favicon.ico'
```
index.rst:
```
xo-foo documentation
====================
text here
.. toctree::
:maxdepth: 2
:caption: xo-foo contents
genindex
search
```
_static:
copy from xo-unit/docs/_static. Just need .ico

61
xo-cmake/README.md Normal file
View file

@ -0,0 +1,61 @@
# XO cmake modules
Collects cmake macros to be shared across XO projects (e.g. indentlog, reflect, kalman, ..)
## Features
- support for both manyrepo and monorepo projects
- support for generating cmake `xxxConfig.cmake` files, so cmake `find_package()` works reliably
- support for header-only libraries
- support for pybind11 libraries
- documentation generation using doxygen + breathe + sphinx
- code coverage using ccov + lcov
## Getting Started
### copy repo
```
$ git clone https://github.com:rconybea/xo-cmake.git
```
### configure + install
```
$ cd xo-cmake
$ cmake -DCMAKE_INSTALL_PREFIX=/usr/local -B .build -S . # ..or desired prefix
$ cmake --install .build
```
## use from a cmake project
In some project `foo`:
```
$ cd foo
$ mkdir cmake
$ cp $PREFIX/share/xo-macros/xo-bootstrap-macros.cmake cmake/
```
`xo-bootstrap-macros-cmake` has two vital jobs:
1. set `XO_CMAKE_CONFIG_EXECUTABLE` (locate `xo-cmake-config`)
2. set `CMAKE_MODULE_PATH` (obtained from `xo-cmake-config --cmake-module-path`)
then in `foo/CMakeLists.txt`:
```
include(cmake/xo-bootstrap-macros.cmake)
xo_cxx_toplevel_options3()
```
Now as long as `$PREFIX/bin` is in `PATH`:
```
$ cd mybuild
$ cmake path/to/foo/source
```
### or set `XO_CMAKE_CONFIG_EXECUTABLE` and `CMAKE_MODULE_PATH`
In some project `foo`:
```
$ cd mybuild
$ cmake -DXO_CMAKE_CONFIG_EXECUTABLE=xo-cmake-config -DCMAKE_MODULE_PATH=$(xo-cmake-config --cmake-module-path) path/to/foo/source
```

220
xo-cmake/bin/xo-build.in Normal file
View file

@ -0,0 +1,220 @@
#!/usr/bin/env bash
usage() {
cat <<EOF
$0 [-u|--usage|-h|--help]
[--list] [-n] [--xoname=NAME]
[--repo|--clone|--configure|--build|--install]
[-S=SOURCEDIR] [-B=BUILDDIR]
EOF
}
help() {
usage
cat <<EOF
fetch and/or build xo component libraries
Options:
-u | --usage brief help message
-h | --help this help message
--list list known xo libraries
-n dry-run: don't actually invoke state-changing commands
--xoname=NAME operate on xo subsystem NAME
--repo report github repo for NAME
--clone git clone xo library NAME in current directory
--configure run cmake for xo library NAME in immediate subdir
Will use NAME/.build as build directory
--build run cmake build for xo library NAME in immediate subdir
--install run cmake install for xo library NAME in immediate subdir
-S=SOURCEDIR override path/to/source
-B=BUILDDIR override path/to/build
EOF
}
noop_flag=0
xoname=
repo_flag=0
clone_flag=0
configure_flag=0
build_flag=0
install_flag=0
pathtosource=
pathtobuild=
while [[ $# > 0 ]]; do
case "$1" in
-u | --usage)
cmd='usage'
;;
-h | --help)
cmd='help'
;;
-n)
noop_flag=1
;;
-S)
shift
pathtosource=$1
;;
-S=*)
pathtosource="${1#*=}"
;;
-B)
shift
pathtobuild=$1
;;
-B=*)
pathtobuild="${1#*=}"
;;
--list)
cmd='list'
;;
--xoname=*)
xoname="${1#*=}"
;;
--repo)
repo_flag=1
;;
--clone)
clone_flag=1
;;
--configure|--config)
configure_flag=1
;;
--build)
build_flag=1
;;
--install)
install_flag=1
;;
xo-*)
xoname="$1"
;;
*)
usage
exit 1
;;
esac
shift
done
echo xoname=$xoname pathtosource=$pathtosource pathtobuild=$pathtobuild
if [[ -z "$pathtosource" ]]; then
pathtosource=$xoname
fi
if [[ -z "$pathtobuild" ]]; then
pathtobuild=$xoname/.build
fi
SUBSYSTEMLIST_FILE=@CMAKE_INSTALL_FULL_DATADIR@/etc/xo/subsystem-list
subsystem_list() {
cat ${SUBSYSTEMLIST_FILE}
}
XO_REPO_STEM="https://github.com/Rconybea"
repo() {
xoname=$1
case "$xoname" in
xo-indentlog)
echo "${XO_REPO_STEM}/indentlog.git"
;;
xo-refcnt)
echo "${XO_REPO_STEM}/refcnt.git"
;;
xo-subsys)
echo "${XO_REPO_STEM}/subsys.git"
;;
xo-reflect)
echo "${XO_REPO_STEM}/reflect.git"
;;
*)
if grep -q $1 ${SUBSYSTEMLIST_FILE}; then
echo "${XO_REPO_STEM}/${1}.git"
else
>&2 echo "$0: unknown xo component [${xoname}]"
return 1
fi
esac
return 0
}
if [[ $cmd == 'usage' ]]; then
echo -n "usage: "
usage
exit 0
elif [[ $cmd == 'help' ]]; then
echo -n "help; "
help
exit 0
fi
set -e
if [[ $cmd == 'list' ]]; then
subsystem_list
fi
if [[ $repo_flag -eq 1 ]]; then
if [[ -n "$xoname" ]]; then
repo $xoname
fi
fi
if [[ $clone_flag -eq 1 ]]; then
if [[ -n "$xoname" ]]; then
url=$(repo $xoname)
cmd="git clone $url"
if [[ $noop_flag -eq 1 ]]; then
echo $cmd
else
$cmd
fi
fi
fi
if [[ $configure_flag -eq 1 ]]; then
if [[ -n "$xoname" ]]; then
cmd="cmake -DCMAKE_INSTALL_PREFIX=@CMAKE_INSTALL_PREFIX@ -S $pathtosource -B $pathtobuild"
if [[ $noop_flag -eq 1 ]]; then
echo $cmd
else
$cmd
fi
fi
fi
if [[ $build_flag -eq 1 ]]; then
if [[ -n "$xoname" ]]; then
cmd="cmake --build $pathtobuild -j"
if [[ $noop_flag -eq 1 ]]; then
echo $cmd
else
$cmd
fi
fi
fi
if [[ $install_flag -eq 1 ]]; then
if [[ -n "$xoname" ]]; then
cmd="cmake --install $pathtobuild"
if [[ $noop_flag -eq 1 ]]; then
echo $cmd
else
$cmd
fi
fi
fi

86
xo-cmake/bin/xo-cmake-config.in Executable file
View file

@ -0,0 +1,86 @@
#!/usr/bin/env bash
usage() {
echo "$0 [-u|--usage|-h|--help|--lcov-exe|--genhtml-exe|--lcov-harness-exe|--gen-ccov-template|--cmake-module-path|--subsystem-list]" 1>&2
}
help() {
usage
cat <<EOF
display xo-cmake configuration variables
Options:
-u | --usage brief help message
-h | --help this help message
--cmake-module-path report directory providing xo-cmake macros (will use/appear-in CMAKE_MODULE_PATH)
--lcov-exe report path to 'lcov' executable
--genhtml-exe report path to 'genhtml' executable
--lcov-harness-exe report path to 'xo-cmake-lcov-harness' executable
--gen-ccov-template report path to 'gen-ccov.in' template
--doxygen-template report path to 'Doxyfile.in' template
--subsystem-list report path to 'subsystem-ilst'
EOF
}
while [[ $# > 0 ]]; do
case "$1" in
-u | --usage)
cmd='usage'
;;
-h | --help)
cmd='help'
;;
--cmake-module-path)
cmd='cmake_module_path'
;;
--lcov-exe)
cmd='lcov_exe'
;;
--genhtml-exe)
cmd='genhtml_exe'
;;
--lcov-harness-exe)
cmd='lcov_harness_exe'
;;
--gen-ccov-template)
cmd='gen_ccov_template'
;;
--doxygen-template)
cmd='doxygen_template'
;;
--subsystem-list)
cmd='subsystem_list'
;;
*)
usage
exit 1
;;
esac
shift
done
if [[ $cmd == 'usage' ]]; then
echo -n "usage: "
usage
elif [[ $cmd == 'help' ]]; then
echo -n "help: "
help
elif [[ $cmd == 'cmake_module_path' ]]; then
echo -n @CMAKE_INSTALL_FULL_DATADIR@/cmake
elif [[ $cmd == 'lcov_exe' ]]; then
echo -n @LCOV_EXECUTABLE@
elif [[ $cmd == 'genhtml_exe' ]]; then
echo -n @GENHTML_EXECUTABLE@
elif [[ $cmd == 'lcov_harness_exe' ]]; then
echo -n @CMAKE_INSTALL_FULL_BINDIR@/xo-cmake-lcov-harness
elif [[ $cmd == 'gen_ccov_template' ]]; then
echo -n @CMAKE_INSTALL_FULL_DATADIR@/xo-macros/gen-ccov.in
elif [[ $cmd == 'doxygen_template' ]]; then
echo -n @CMAKE_INSTALL_FULL_DATADIR@/xo-macros/Doxyfile.in
elif [[ $cmd == 'subsystem_list' ]]; then
echo -n @CMAKE_INSTALL_FULL_DATADIR@/etc/xo/subsystem-list
fi

View file

@ -0,0 +1,121 @@
#!/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
#
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

View file

@ -0,0 +1,678 @@
#
# Copyright (C) 2018-2020 by George Cave - gcave@stablecoder.ca
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
# USAGE: To enable any code coverage instrumentation/targets, the single CMake
# option of `CODE_COVERAGE` needs to be set to 'ON', either by GUI, ccmake, or
# on the command line.
#
# From this point, there are two primary methods for adding instrumentation to
# targets:
#
# 1 - A blanket instrumentation by calling `add_code_coverage()`, where
# all targets in that directory and all subdirectories are automatically
# instrumented.
#
# 2 - Per-target instrumentation by calling
# `target_code_coverage(<TARGET_NAME>)`, where the target is given and thus only
# that target is instrumented. This applies to both libraries and executables.
#
# To add coverage targets, such as calling `make ccov` to generate the actual
# coverage information for perusal or consumption, call
# `target_code_coverage(<TARGET_NAME>)` on an *executable* target.
#
# Example 1: All targets instrumented
#
# In this case, the coverage information reported will will be that of the
# `theLib` library target and `theExe` executable.
#
# 1a: Via global command
#
# ~~~
# add_code_coverage() # Adds instrumentation to all targets
#
# add_library(theLib lib.cpp)
#
# add_executable(theExe main.cpp)
# target_link_libraries(theExe PRIVATE theLib)
# target_code_coverage(theExe) # As an executable target, adds the 'ccov-theExe' target
# # (instrumentation already added via global anyways)
# # for generating code coverage reports.
# ~~~
#
# 1b: Via target commands
#
# ~~~
# add_library(theLib lib.cpp)
# target_code_coverage(theLib) # As a library target, adds coverage instrumentation but no targets.
#
# add_executable(theExe main.cpp)
# target_link_libraries(theExe PRIVATE theLib)
# target_code_coverage(theExe) # As an executable target, adds the 'ccov-theExe' target and instrumentation for generating code coverage reports.
# ~~~
#
# Example 2: Target instrumented, but with regex pattern of files to be excluded
# from report
#
# ~~~
# add_executable(theExe main.cpp non_covered.cpp)
# target_code_coverage(theExe EXCLUDE non_covered.cpp test/*) # As an executable target, the reports will exclude the non-covered.cpp file, and any files in a test/ folder.
# ~~~
#
# Example 3: Target added to the 'ccov' and 'ccov-all' targets
#
# ~~~
# add_code_coverage_all_targets(EXCLUDE test/*) # Adds the 'ccov-all' target set and sets it to exclude all files in test/ folders.
#
# add_executable(theExe main.cpp non_covered.cpp)
# target_code_coverage(theExe AUTO ALL EXCLUDE non_covered.cpp test/*) # As an executable target, adds to the 'ccov' and ccov-all' targets, and the reports will exclude the non-covered.cpp file, and any files in a test/ folder.
# ~~~
# Options
option(
CODE_COVERAGE
"Builds targets with code coverage instrumentation. (Requires GCC or Clang)"
OFF)
# Programs
find_program(LLVM_COV_PATH llvm-cov)
find_program(LLVM_PROFDATA_PATH llvm-profdata)
find_program(LCOV_PATH lcov)
find_program(GENHTML_PATH genhtml)
# Hide behind the 'advanced' mode flag for GUI/ccmake
mark_as_advanced(FORCE LLVM_COV_PATH LLVM_PROFDATA_PATH LCOV_PATH GENHTML_PATH)
# Variables
set(CMAKE_COVERAGE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/ccov)
set_property(GLOBAL PROPERTY JOB_POOLS ccov_serial_pool=1)
# Common initialization/checks
if(CODE_COVERAGE AND NOT CODE_COVERAGE_ADDED)
set(CODE_COVERAGE_ADDED ON)
# Common Targets
add_custom_target(
ccov-preprocessing
COMMAND ${CMAKE_COMMAND} -E make_directory
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}
DEPENDS ccov-clean)
if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
# Messages
message(STATUS "Building with llvm Code Coverage Tools")
if(NOT LLVM_COV_PATH)
message(FATAL_ERROR "llvm-cov not found! Aborting.")
else()
# Version number checking for 'EXCLUDE' compatibility
execute_process(COMMAND ${LLVM_COV_PATH} --version
OUTPUT_VARIABLE LLVM_COV_VERSION_CALL_OUTPUT)
string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" LLVM_COV_VERSION
${LLVM_COV_VERSION_CALL_OUTPUT})
if(LLVM_COV_VERSION VERSION_LESS "7.0.0")
message(
WARNING
"target_code_coverage()/add_code_coverage_all_targets() 'EXCLUDE' option only available on llvm-cov >= 7.0.0"
)
endif()
endif()
# Targets
if(${CMAKE_VERSION} VERSION_LESS "3.17.0")
add_custom_target(
ccov-clean
COMMAND ${CMAKE_COMMAND} -E remove -f
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list
COMMAND ${CMAKE_COMMAND} -E remove -f
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list)
else()
add_custom_target(
ccov-clean
COMMAND ${CMAKE_COMMAND} -E rm -f
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list
COMMAND ${CMAKE_COMMAND} -E rm -f
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list)
endif()
# Used to get the shared object file list before doing the main all-
# processing
add_custom_target(
ccov-libs
COMMAND ;
COMMENT "libs ready for coverage report.")
elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES
"GNU")
# Messages
message(STATUS "Building with lcov Code Coverage Tools")
if(CMAKE_BUILD_TYPE)
string(TOUPPER ${CMAKE_BUILD_TYPE} upper_build_type)
if(NOT ${upper_build_type} STREQUAL "DEBUG")
message(
WARNING
"Code coverage results with an optimized (non-Debug) build may be misleading"
)
endif()
else()
message(
WARNING
"Code coverage results with an optimized (non-Debug) build may be misleading"
)
endif()
if(NOT LCOV_PATH)
message(FATAL_ERROR "lcov not found! Aborting...")
endif()
if(NOT GENHTML_PATH)
message(FATAL_ERROR "genhtml not found! Aborting...")
endif()
# Targets
add_custom_target(ccov-clean COMMAND ${LCOV_PATH} --directory
${CMAKE_BINARY_DIR} --zerocounters)
else()
message(FATAL_ERROR "Code coverage requires Clang or GCC. Aborting.")
endif()
endif()
# Adds code coverage instrumentation to a library, or instrumentation/targets
# for an executable target.
# ~~~
# EXECUTABLE ADDED TARGETS:
# GCOV/LCOV:
# ccov : Generates HTML code coverage report for every target added with 'AUTO' parameter.
# ccov-${TARGET_NAME} : Generates HTML code coverage report for the associated named target.
# ccov-all : Generates HTML code coverage report, merging every target added with 'ALL' parameter into a single detailed report.
#
# LLVM-COV:
# ccov : Generates HTML code coverage report for every target added with 'AUTO' parameter.
# ccov-report : Generates HTML code coverage report for every target added with 'AUTO' parameter.
# ccov-${TARGET_NAME} : Generates HTML code coverage report.
# ccov-report-${TARGET_NAME} : Prints to command line summary per-file coverage information.
# ccov-export-${TARGET_NAME} : Exports the coverage report to a JSON file.
# ccov-show-${TARGET_NAME} : Prints to command line detailed per-line coverage information.
# ccov-all : Generates HTML code coverage report, merging every target added with 'ALL' parameter into a single detailed report.
# ccov-all-report : Prints summary per-file coverage information for every target added with ALL' parameter to the command line.
# ccov-all-export : Exports the coverage report to a JSON file.
#
# Required:
# TARGET_NAME - Name of the target to generate code coverage for.
# Optional:
# PUBLIC - Sets the visibility for added compile options to targets to PUBLIC instead of the default of PRIVATE.
# INTERFACE - Sets the visibility for added compile options to targets to INTERFACE instead of the default of PRIVATE.
# PLAIN - Do not set any target visibility (backward compatibility with old cmake projects)
# AUTO - Adds the target to the 'ccov' target so that it can be run in a batch with others easily. Effective on executable targets.
# ALL - Adds the target to the 'ccov-all' and 'ccov-all-report' targets, which merge several executable targets coverage data to a single report. Effective on executable targets.
# EXTERNAL - For GCC's lcov, allows the profiling of 'external' files from the processing directory
# COVERAGE_TARGET_NAME - For executables ONLY, changes the outgoing target name so instead of `ccov-${TARGET_NAME}` it becomes `ccov-${COVERAGE_TARGET_NAME}`.
# EXCLUDE <PATTERNS> - Excludes files of the patterns provided from coverage. Note that GCC/lcov excludes by glob pattern, and clang/LLVM excludes via regex! **These do not copy to the 'all' targets.**
# OBJECTS <TARGETS> - For executables ONLY, if the provided targets are shared libraries, adds coverage information to the output
# ARGS <ARGUMENTS> - For executables ONLY, appends the given arguments to the associated ccov-* executable call
# ~~~
function(target_code_coverage TARGET_NAME)
# Argument parsing
set(options AUTO ALL EXTERNAL PUBLIC INTERFACE PLAIN)
set(single_value_keywords COVERAGE_TARGET_NAME)
set(multi_value_keywords EXCLUDE OBJECTS ARGS)
cmake_parse_arguments(
target_code_coverage "${options}" "${single_value_keywords}"
"${multi_value_keywords}" ${ARGN})
# Set the visibility of target functions to PUBLIC, INTERFACE or default to
# PRIVATE.
if(target_code_coverage_PUBLIC)
set(TARGET_VISIBILITY PUBLIC)
set(TARGET_LINK_VISIBILITY PUBLIC)
elseif(target_code_coverage_INTERFACE)
set(TARGET_VISIBILITY INTERFACE)
set(TARGET_LINK_VISIBILITY INTERFACE)
elseif(target_code_coverage_PLAIN)
set(TARGET_VISIBILITY PUBLIC)
set(TARGET_LINK_VISIBILITY)
else()
set(TARGET_VISIBILITY PRIVATE)
set(TARGET_LINK_VISIBILITY PRIVATE)
endif()
if(NOT target_code_coverage_COVERAGE_TARGET_NAME)
# If a specific name was given, use that instead.
set(target_code_coverage_COVERAGE_TARGET_NAME ${TARGET_NAME})
endif()
if(CODE_COVERAGE)
# Add code coverage instrumentation to the target's linker command
if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
target_compile_options(${TARGET_NAME} ${TARGET_VISIBILITY}
-fprofile-instr-generate -fcoverage-mapping)
target_link_options(${TARGET_NAME} ${TARGET_VISIBILITY}
-fprofile-instr-generate -fcoverage-mapping)
elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES
"GNU")
target_compile_options(${TARGET_NAME} ${TARGET_VISIBILITY} -fprofile-arcs
-ftest-coverage)
target_link_libraries(${TARGET_NAME} ${TARGET_LINK_VISIBILITY} gcov)
endif()
# Targets
get_target_property(target_type ${TARGET_NAME} TYPE)
# Add shared library to processing for 'all' targets
if(target_type STREQUAL "SHARED_LIBRARY" AND target_code_coverage_ALL)
if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
add_custom_target(
ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}
COMMAND
${CMAKE_COMMAND} -E echo "-object=$<TARGET_FILE:${TARGET_NAME}>" >>
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list
DEPENDS ccov-preprocessing ${TARGET_NAME})
if(NOT TARGET ccov-libs)
message(
FATAL_ERROR
"Calling target_code_coverage with 'ALL' must be after a call to 'add_code_coverage_all_targets'."
)
endif()
add_dependencies(ccov-libs
ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME})
endif()
endif()
# For executables add targets to run and produce output
if(target_type STREQUAL "EXECUTABLE")
if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
# If there are shared objects to also work with, generate the string to
# add them here
foreach(SO_TARGET ${target_code_coverage_OBJECTS})
# Check to see if the target is a shared object
if(TARGET ${SO_TARGET})
get_target_property(SO_TARGET_TYPE ${SO_TARGET} TYPE)
if(${SO_TARGET_TYPE} STREQUAL "SHARED_LIBRARY")
set(SO_OBJECTS ${SO_OBJECTS} -object=$<TARGET_FILE:${SO_TARGET}>)
endif()
endif()
endforeach()
# Run the executable, generating raw profile data Make the run data
# available for further processing. Separated to allow Windows to run
# this target serially.
add_custom_target(
ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}
COMMAND
${CMAKE_COMMAND} -E env
LLVM_PROFILE_FILE=${target_code_coverage_COVERAGE_TARGET_NAME}.profraw
$<TARGET_FILE:${TARGET_NAME}> ${target_code_coverage_ARGS}
COMMAND
${CMAKE_COMMAND} -E echo "-object=$<TARGET_FILE:${TARGET_NAME}>"
${SO_OBJECTS} >> ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list
COMMAND
${CMAKE_COMMAND} -E echo
"${CMAKE_CURRENT_BINARY_DIR}/${target_code_coverage_COVERAGE_TARGET_NAME}.profraw"
>> ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list
JOB_POOL ccov_serial_pool
DEPENDS ccov-preprocessing ccov-libs ${TARGET_NAME})
# Merge the generated profile data so llvm-cov can process it
add_custom_target(
ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}
COMMAND
${LLVM_PROFDATA_PATH} merge -sparse
${target_code_coverage_COVERAGE_TARGET_NAME}.profraw -o
${target_code_coverage_COVERAGE_TARGET_NAME}.profdata
DEPENDS ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME})
# Ignore regex only works on LLVM >= 7
if(LLVM_COV_VERSION VERSION_GREATER_EQUAL "7.0.0")
foreach(EXCLUDE_ITEM ${target_code_coverage_EXCLUDE})
set(EXCLUDE_REGEX ${EXCLUDE_REGEX}
-ignore-filename-regex='${EXCLUDE_ITEM}')
endforeach()
endif()
# Print out details of the coverage information to the command line
add_custom_target(
ccov-show-${target_code_coverage_COVERAGE_TARGET_NAME}
COMMAND
${LLVM_COV_PATH} show $<TARGET_FILE:${TARGET_NAME}> ${SO_OBJECTS}
-instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata
-show-line-counts-or-regions ${EXCLUDE_REGEX}
DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME})
# Print out a summary of the coverage information to the command line
add_custom_target(
ccov-report-${target_code_coverage_COVERAGE_TARGET_NAME}
COMMAND
${LLVM_COV_PATH} report $<TARGET_FILE:${TARGET_NAME}> ${SO_OBJECTS}
-instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata
${EXCLUDE_REGEX}
DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME})
# Export coverage information so continuous integration tools (e.g.
# Jenkins) can consume it
add_custom_target(
ccov-export-${target_code_coverage_COVERAGE_TARGET_NAME}
COMMAND
${LLVM_COV_PATH} export $<TARGET_FILE:${TARGET_NAME}> ${SO_OBJECTS}
-instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata
-format="text" ${EXCLUDE_REGEX} >
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}.json
DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME})
# Generates HTML output of the coverage information for perusal
add_custom_target(
ccov-${target_code_coverage_COVERAGE_TARGET_NAME}
COMMAND
${LLVM_COV_PATH} show $<TARGET_FILE:${TARGET_NAME}> ${SO_OBJECTS}
-instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata
-show-line-counts-or-regions
-output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}
-format="html" ${EXCLUDE_REGEX}
DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME})
elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES
"GNU")
set(COVERAGE_INFO
"${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}.info"
)
# Run the executable, generating coverage information
add_custom_target(
ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}
COMMAND $<TARGET_FILE:${TARGET_NAME}> ${target_code_coverage_ARGS}
DEPENDS ccov-preprocessing ${TARGET_NAME})
# Generate exclusion string for use
foreach(EXCLUDE_ITEM ${target_code_coverage_EXCLUDE})
set(EXCLUDE_REGEX ${EXCLUDE_REGEX} --remove ${COVERAGE_INFO}
'${EXCLUDE_ITEM}')
endforeach()
if(EXCLUDE_REGEX)
set(EXCLUDE_COMMAND ${LCOV_PATH} ${EXCLUDE_REGEX} --output-file
${COVERAGE_INFO})
else()
set(EXCLUDE_COMMAND ;)
endif()
if(NOT ${target_code_coverage_EXTERNAL})
set(EXTERNAL_OPTION --no-external)
endif()
# Capture coverage data
if(${CMAKE_VERSION} VERSION_LESS "3.17.0")
add_custom_target(
ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME}
COMMAND ${CMAKE_COMMAND} -E remove -f ${COVERAGE_INFO}
COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --zerocounters
COMMAND $<TARGET_FILE:${TARGET_NAME}> ${target_code_coverage_ARGS}
COMMAND
${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --base-directory
${CMAKE_SOURCE_DIR} --capture ${EXTERNAL_OPTION} --output-file
${COVERAGE_INFO}
COMMAND ${EXCLUDE_COMMAND}
DEPENDS ccov-preprocessing ${TARGET_NAME})
else()
add_custom_target(
ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME}
COMMAND ${CMAKE_COMMAND} -E rm -f ${COVERAGE_INFO}
COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --zerocounters
COMMAND $<TARGET_FILE:${TARGET_NAME}> ${target_code_coverage_ARGS}
COMMAND
${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --base-directory
${CMAKE_SOURCE_DIR} --capture ${EXTERNAL_OPTION} --output-file
${COVERAGE_INFO}
COMMAND ${EXCLUDE_COMMAND}
DEPENDS ccov-preprocessing ${TARGET_NAME})
endif()
# Generates HTML output of the coverage information for perusal
add_custom_target(
ccov-${target_code_coverage_COVERAGE_TARGET_NAME}
COMMAND
${GENHTML_PATH} -o
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}
${COVERAGE_INFO}
DEPENDS ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME})
endif()
add_custom_command(
TARGET ccov-${target_code_coverage_COVERAGE_TARGET_NAME}
POST_BUILD
COMMAND ;
COMMENT
"Open ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}/index.html in your browser to view the coverage report."
)
# AUTO
if(target_code_coverage_AUTO)
if(NOT TARGET ccov)
add_custom_target(ccov)
endif()
add_dependencies(ccov ccov-${target_code_coverage_COVERAGE_TARGET_NAME})
if(NOT CMAKE_C_COMPILER_ID MATCHES "GNU" AND NOT CMAKE_CXX_COMPILER_ID
MATCHES "GNU")
if(NOT TARGET ccov-report)
add_custom_target(ccov-report)
endif()
add_dependencies(
ccov-report
ccov-report-${target_code_coverage_COVERAGE_TARGET_NAME})
endif()
endif()
# ALL
if(target_code_coverage_ALL)
if(NOT TARGET ccov-all-processing)
message(
FATAL_ERROR
"Calling target_code_coverage with 'ALL' must be after a call to 'add_code_coverage_all_targets'."
)
endif()
add_dependencies(ccov-all-processing
ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME})
endif()
endif()
endif()
endfunction()
# Adds code coverage instrumentation to all targets in the current directory and
# any subdirectories. To add coverage instrumentation to only specific targets,
# use `target_code_coverage`.
function(add_code_coverage)
if(CODE_COVERAGE)
if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
add_compile_options(-fprofile-instr-generate -fcoverage-mapping)
add_link_options(-fprofile-instr-generate -fcoverage-mapping)
elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES
"GNU")
add_compile_options(-fprofile-arcs -ftest-coverage)
link_libraries(gcov)
endif()
endif()
endfunction()
# Adds the 'ccov-all' type targets that calls all targets added via
# `target_code_coverage` with the `ALL` parameter, but merges all the coverage
# data from them into a single large report instead of the numerous smaller
# reports. Also adds the ccov-all-capture Generates an all-merged.info file, for
# use with coverage dashboards (e.g. codecov.io, coveralls).
# ~~~
# Optional:
# EXCLUDE <PATTERNS> - Excludes files of the patterns provided from coverage. Note that GCC/lcov excludes by glob pattern, and clang/LLVM excludes via regex!
# ~~~
function(add_code_coverage_all_targets)
# Argument parsing
set(multi_value_keywords EXCLUDE)
cmake_parse_arguments(add_code_coverage_all_targets "" ""
"${multi_value_keywords}" ${ARGN})
if(CODE_COVERAGE)
if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
# Merge the profile data for all of the run executables
if(WIN32)
add_custom_target(
ccov-all-processing
COMMAND
powershell -Command $$FILELIST = Get-Content
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list\; llvm-profdata.exe
merge -o ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
-sparse $$FILELIST)
else()
add_custom_target(
ccov-all-processing
COMMAND
${LLVM_PROFDATA_PATH} merge -o
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata -sparse `cat
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list`)
endif()
# Regex exclude only available for LLVM >= 7
if(LLVM_COV_VERSION VERSION_GREATER_EQUAL "7.0.0")
foreach(EXCLUDE_ITEM ${add_code_coverage_all_targets_EXCLUDE})
set(EXCLUDE_REGEX ${EXCLUDE_REGEX}
-ignore-filename-regex='${EXCLUDE_ITEM}')
endforeach()
endif()
# Print summary of the code coverage information to the command line
if(WIN32)
add_custom_target(
ccov-all-report
COMMAND
powershell -Command $$FILELIST = Get-Content
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list\; llvm-cov.exe
report $$FILELIST
-instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
${EXCLUDE_REGEX}
DEPENDS ccov-all-processing)
else()
add_custom_target(
ccov-all-report
COMMAND
${LLVM_COV_PATH} report `cat
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list`
-instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
${EXCLUDE_REGEX}
DEPENDS ccov-all-processing)
endif()
# Export coverage information so continuous integration tools (e.g.
# Jenkins) can consume it
add_custom_target(
ccov-all-export
COMMAND
${LLVM_COV_PATH} export `cat
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list`
-instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
-format="text" ${EXCLUDE_REGEX} >
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/coverage.json
DEPENDS ccov-all-processing)
# Generate HTML output of all added targets for perusal
if(WIN32)
add_custom_target(
ccov-all
COMMAND
powershell -Command $$FILELIST = Get-Content
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list\; llvm-cov.exe show
$$FILELIST
-instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
-show-line-counts-or-regions
-output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged
-format="html" ${EXCLUDE_REGEX}
DEPENDS ccov-all-processing)
else()
add_custom_target(
ccov-all
COMMAND
${LLVM_COV_PATH} show `cat
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list`
-instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
-show-line-counts-or-regions
-output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged
-format="html" ${EXCLUDE_REGEX}
DEPENDS ccov-all-processing)
endif()
elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES
"GNU")
set(COVERAGE_INFO "${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.info")
# Nothing required for gcov
add_custom_target(ccov-all-processing COMMAND ;)
# Exclusion regex string creation
set(EXCLUDE_REGEX)
foreach(EXCLUDE_ITEM ${add_code_coverage_all_targets_EXCLUDE})
set(EXCLUDE_REGEX ${EXCLUDE_REGEX} --remove ${COVERAGE_INFO}
'${EXCLUDE_ITEM}')
endforeach()
if(EXCLUDE_REGEX)
set(EXCLUDE_COMMAND ${LCOV_PATH} ${EXCLUDE_REGEX} --output-file
${COVERAGE_INFO})
else()
set(EXCLUDE_COMMAND ;)
endif()
# Capture coverage data
if(${CMAKE_VERSION} VERSION_LESS "3.17.0")
add_custom_target(
ccov-all-capture
COMMAND ${CMAKE_COMMAND} -E remove -f ${COVERAGE_INFO}
COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --capture
--output-file ${COVERAGE_INFO}
COMMAND ${EXCLUDE_COMMAND}
DEPENDS ccov-preprocessing ccov-all-processing)
else()
add_custom_target(
ccov-all-capture
COMMAND ${CMAKE_COMMAND} -E rm -f ${COVERAGE_INFO}
COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --capture
--output-file ${COVERAGE_INFO}
COMMAND ${EXCLUDE_COMMAND}
DEPENDS ccov-preprocessing ccov-all-processing)
endif()
# Generates HTML output of all targets for perusal
add_custom_target(
ccov-all
COMMAND ${GENHTML_PATH} -o ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged
${COVERAGE_INFO} -p ${CMAKE_SOURCE_DIR}
DEPENDS ccov-all-capture)
endif()
add_custom_command(
TARGET ccov-all
POST_BUILD
COMMAND ;
COMMENT
"Open ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged/index.html in your browser to view the coverage report."
)
endif()
endfunction()

View file

@ -0,0 +1,2 @@
include(xo_macros/xo_cxx)
include(xo_macros/code-coverage)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,38 @@
xo-cmake
xo-indentlog
xo-refcnt
xo-subsys
xo-randomgen
xo-ordinaltree
xo-pyutil
xo-flatstring
xo-reflectutil
xo-reflect
xo-pyreflect
xo-ratio
xo-unit
xo-pyunit
xo-expression
xo-pyexpression
xo-tokenizer
xo-reader
xo-jit
xo-pyjit
xo-callback
xo-webutil
xo-pywebutil
xo-printjson
xo-pyprintjson
xo-reactor
xo-pyreactor
xo-websock
xo-pywebsock
xo-statistics
xo-distribution
xo-pydistribution
xo-simulator
xo-pysimulator
xo-process
xo-pyprocess
xo-kalmanfilter
xo-pykalmanfilter

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,30 @@
#!/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
lcovharness=@XO_CMAKE_LCOV_HARNESS_EXECUTABLE@
if [[ -z $lcovharness ]]; then
echo "gen-ccov: lcov-harness executable (XO_CMAKE_LCOV_HARNESS_EXECUTABLE) not configured"
echo "gen-ccov: expect value of path/to/xo-cmake-config --lcov-harness-exe"
echo "gen-ccov: stored in XO_CMAKE_LCOV_HARNESS_EXECUTABLE by xo_toplevel_testing_options2()"
exit 1
fi
# TODO: allow providing LCOV_EXECUTABLE GENHTML_EXECUTABLE here
$lcovharness $srcdir $builddir

View file

@ -0,0 +1,35 @@
# ----------------------------------------------------------------
# for example:
# $ PREFIX=/usr/local # for example
# $ cmake -DCMAKE_MODULE_PATH=prefix -DCMAKE_INSTALL_PREFIX=$PREFIX -B .build
#
# will get
# CMAKE_MODULE_PATH
# from xo-cmake-config --cmake-module-path
#
# and expect .cmake macros in
# CMAKE_MODULE_PATH/xo_macros/xo_cxx.cmake
# ----------------------------------------------------------------
find_program(XO_CMAKE_CONFIG_EXECUTABLE NAMES xo-cmake-config REQUIRED)
if ("${XO_CMAKE_CONFIG_EXECUTABLE}" STREQUAL "XO_CMAKE_CONFIG_EXECUTABLE-NOT_FOUND")
message(FATAL "could not find xo-cmake-config executable")
endif()
message(STATUS "XO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE}")
if (NOT XO_SUBMODULE_BUILD)
if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL prefix))
# default to typical install location for xo-project-macros
execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --cmake-module-path OUTPUT_VARIABLE CMAKE_MODULE_PATH)
message(STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}")
endif()
endif()
# needs to have been installed somewhere on CMAKE_MODULE_PATH,
# (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX)
#
include(xo_macros/xo_cxx)
xo_cxx_bootstrap_message()