# Copyright (C) 2018-2025 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
# LCOV_EXCLUDE test/*
# LLVM_EXCLUDE 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(
# LCOV_EXCLUDE test/*
# LLVM_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.
# ~~~
#
# Example 4: Hook all targets
#
# ~~~
# set(CCOV_TARGETS_HOOK ON) # enable 'add_executable' and 'add_library' hooks
# set(CCOV_TARGETS_HOOK_ARGS ALL AUTO) # set default arguments for coverage
#
# add_code_coverage() # Adds instrumentation to all targets
#
# add_library(theLib lib.cpp) # ccov-theLib target will be add
#
# add_executable(theExe main.cpp) # ccov-theExe target will be add
# target_link_libraries(theExe PRIVATE theLib)
# ~~~
# Options
option(
CODE_COVERAGE
"Builds targets with code coverage instrumentation. (Requires GCC or Clang)"
OFF)
option(CCOV_TARGETS_HOOK"Autocapture all new targets."OFF)
option(CCOV_TARGETS_HOOK_ARGS"Default arguments for all hooked targets.")
# Programs
find_program(LLVM_COV_PATHllvm-cov)
find_program(LLVM_PROFDATA_PATHllvm-profdata)
find_program(LCOV_PATHlcov)
find_program(GENHTML_PATHgenhtml)
# Hide behind the 'advanced' mode flag for GUI/ccmake
# 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-report : Generates HTML code coverage report for every target added with 'AUTO' parameter.
#
# Required Parameters:
# TARGET_NAME - Name of the target to generate code coverage for.
#
# Optional Parameters:
# 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-*' targets created by a prior call to `add_code_coverage_all_targets` 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}`.
# OBJECTS <TARGETS> - For executables ONLY, if the provided targets are static or shared libraries, adds coverage information to the output
# PRE_ARGS <ARGUMENTS> - For executables ONLY, prefixes given arguments to the associated ccov-run-${TARGET_NAME} executable call ($<PRE_ARGS> ccov-*)
# ARGS <ARGUMENTS> - For executables ONLY, appends the given arguments to the associated ccov-run-${TARGET_NAME} executable call (ccov-* $<ARGS>)
# EXCLUDE <PATTERNS> - Excludes files of the patterns provided from coverage. Added to any LLVM/LCOV specified excludes. (These do not copy to the 'all' targets)
#
# Optional Parameters effective with the clang/LLVM backend:
# LLVM_EXCLUDE <PATTERNS> - Excludes files that match the provided patterns, LLVM excludes by regex patterns.
# LLVM_PROFDATA_OPTIONS <OPTIONS> - Options are passed verbatim to the `llvm-profdata` call that merges/processes raw profile data. (.profraw -> .profdata)
# LLVM_COV_SHOW_OPTIONS <OPTIONS> - Options are passed verbatim to the `llvm-cov show` call for the `ccov-show-${TARGET_NAME}` target.
# LLVM_COV_REPORT_OPTIONS <OPTIONS> - Options are passed verbatim to the `llvm-cov report` call for the `ccov-report-${TARGET_NAME}` target.
# LLVM_COV_EXPORT_OPTIONS <OPTIONS> - Options are passed verbatim to the `llvm-cov export` call for the `ccov-export-${TARGET_NAME}` target.
# LLVM_COV_HTML_OPTIONS <OPTIONS> - Options are passed verbatim to the `llvm-cov show -format="html"` call for the `ccov-html-${TARGET_NAME}`/`ccov-${TARGET_NAME}` targets.
#
# Optional Parameters effective with the GCC/lcov backend:
# LCOV_EXCLUDE <PATTERNS> - Excludes files that match the provided patterns. LCOV exclude by glob patterns.
# LCOV_OPTIONS <OPTIONS> - Options are passed verbatim to the `lcov` call when capturing/filtering capture data
# GENHTML_OPTIONS <OPTIONS> - Options are passed verbatim to the `genhtml` call when generating the HTML report from lcov data for the `ccov-html-${TARGET_NAME}`/`ccov-${TARGET_NAME}` targets.
# ~~~
function(target_code_coverageTARGET_NAME)
if(NOT CODE_COVERAGE)
return()
endif()
# Argument parsing
set(options AUTO ALL EXTERNAL PUBLIC INTERFACE PLAIN)
# Adds several 'ccov-all-*' targets that operates runs all targets added via
# `target_code_coverage` with the `ALL` parameter and merges all the coverage
# data into a single large report instead of numerous smaller ones.
# ~~~
# Targets added:
# ccov-all-run : Re-runs all tagged executables, collecting fresh coverage data
# ccov-all-html : Generates an HTML report of all tagged executable coverage data merged into one
# ccov-all : Generates an HTML report of all tagged executable coverage data merged into one (same as ccov-all-html)
#
# LLVM-based coverage targets added:
# ccov-all-report : Generates an HTML report of all tagged executable coverage data merged into one and displays it in the CLI
# ccov-all-export : Exports coverage data in JSON format for use in CI environments or similar
#
# GCC-based coverage targets added:
# ccov-all-capture : Generates an all-merged.info file, for use with coverage dashboards (e.g. codecov.io, coveralls).
#
# Optional Parameters:
# EXCLUDE <PATTERNS> - Excludes files of the patterns provided from coverage. Note that GCC/lcov excludes by glob pattern, and clang/LLVM excludes via regex!
#
# Optional Parameters effective with the clang/LLVM backend:
# LLVM_EXCLUDE <PATTERNS> - Excludes files that match the provided patterns, LLVM excludes by regex patterns.
# LLVM_PROFDATA_OPTIONS <OPTIONS> - Options are passed verbatim to the `llvm-profdata` call that merges/processes raw profile data. (.profraw -> .profdata)
# LLVM_COV_REPORT_OPTIONS <OPTIONS> - Options are passed verbatim to the `llvm-cov report` call for the `ccov-report-all` target.
# LLVM_COV_EXPORT_OPTIONS <OPTIONS> - Options are passed verbatim to the `llvm-cov export` call for the `ccov-export-all` target.
# LLVM_COV_HTML_OPTIONS <OPTIONS> - Options are passed verbatim to the `llvm-cov show -format="html"` call for the `ccov-html-all`/`ccov-all` targets.
#
# Optional Parameters effective with the GCC/lcov backend:
# LCOV_EXCLUDE <PATTERNS> - Excludes files that match the provided patterns. LCOV exclude by glob patterns.
# LCOV_OPTIONS <OPTIONS> - Options are passed verbatim to the `lcov` call when capturing/filtering capture data
# GENHTML_OPTIONS <OPTIONS> - Options are passed verbatim to the `genhtml` call when generating the HTML report from lcov data for the `ccov-html-all`/`ccov-all` targets.