CMake: Add code coverage support for clang

Pretty bare bones but gets the job done, unlike the gcc
tooling, this will work for release builds, the performance cost
of it is on the high side of things, the full test suite tests take over
an hour for me with code coverage support enabled on a release build.
I have not timed a debug build. Given developers can just run their
tests to get coverage data over what they are working on, I feel this
is still useful tooling to have.

This adds the 3 targets for clang and adds a single gcc target

coverage-reset - this removes the collected code coverage data and
report

coverage-report - This merges the collected data and generates the
report (new for gcc)

coverage-show - This merges the collected data and generates the report
and opens it in the browser

This relies on llvm-cov and llvm-profdata being available if not found
code coverage is disabled.

Note: A full test run requires an obscene amount of disk space, a
complete test run takes about 125GB and takes 12 minutes to merge, so
provision the COMPILER_CODE_COVERAGE_DATA_DIR folder accordingly

Example report in PR
This commit is contained in:
Ray Molenkamp
2025-05-12 16:28:41 +02:00
committed by Ray molenkamp
parent dd47ee9e25
commit 2c5b9e182b
5 changed files with 106 additions and 24 deletions

View File

@@ -1033,25 +1033,43 @@ Build and link with code coverage support (only for Debug targets)."
mark_as_advanced(WITH_COMPILER_CODE_COVERAGE)
if(WITH_COMPILER_CODE_COVERAGE)
if(NOT CMAKE_COMPILER_IS_GNUCC)
message(WARNING "WITH_COMPILER_CODE_COVERAGE only works with GCC currently.")
if(CMAKE_COMPILER_IS_GNUCC)
set(_code_coverage_defaults "--coverage")
elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
get_filename_component(COMPILER_DIRECTORY ${CMAKE_CXX_COMPILER} DIRECTORY)
find_program(LLVM_COV "llvm-cov" ${COMPILER_DIRECTORY} NO_DEFAULT_PATH)
find_program(LLVM_PROFDATA "llvm-profdata" ${COMPILER_DIRECTORY} NO_DEFAULT_PATH)
if(NOT LLVM_COV OR NOT LLVM_PROFDATA)
message(WARNING "Could not find code coverage tools, disabling code coverage. You may explicitly specify LLVM_COV and LLVM_PROFDATA to work around this warning.")
set(WITH_COMPILER_CODE_COVERAGE OFF)
else()
set(_code_coverage_defaults "-fprofile-instr-generate -fcoverage-mapping")
endif()
else()
message("unsupported compiler ${CMAKE_C_COMPILER_ID} for WITH_COMPILER_CODE_COVERAGE, disabling.")
set(WITH_COMPILER_CODE_COVERAGE OFF)
endif()
endif()
if(WITH_COMPILER_CODE_COVERAGE)
set(_code_coverage_defaults "--coverage")
# The code above could have disabled the feature, so check again.
if(WITH_COMPILER_CODE_COVERAGE)
set(COMPILER_CODE_COVERAGE_CFLAGS
${_code_coverage_defaults} CACHE STRING
"C flags for code coverage"
)
mark_as_advanced(COMPILER_CODE_COVERAGE_CFLAGS)
mark_as_advanced(COMPILER_CODE_COVERAGE_CFLAGS)
set(COMPILER_CODE_COVERAGE_CXXFLAGS
${_code_coverage_defaults} CACHE STRING
"C++ flags for code coverage"
)
mark_as_advanced(COMPILER_CODE_COVERAGE_CXXFLAGS)
unset(_code_coverage_defaults)
mark_as_advanced(COMPILER_CODE_COVERAGE_CXXFLAGS)
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
set(_code_coverage_dir_default "${CMAKE_BINARY_DIR}/coverage")
set(COMPILER_CODE_COVERAGE_DATA_DIR ${_code_coverage_dir_default} CACHE STRING "Directory for code coverage data")
mark_as_advanced(COMPILER_CODE_COVERAGE_DATA_DIR)
unset(_code_coverage_dir_default)
endif()
unset(_code_coverage_defaults)
endif()
endif()
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang")
@@ -1463,8 +1481,15 @@ set(PLATFORM_LINKFLAGS_RELEASE "")
set(PLATFORM_LINKFLAGS_EXECUTABLE "")
if(WITH_COMPILER_CODE_COVERAGE)
string(APPEND CMAKE_C_FLAGS_DEBUG " ${COMPILER_CODE_COVERAGE_CFLAGS}")
string(APPEND CMAKE_CXX_FLAGS_DEBUG " ${COMPILER_CODE_COVERAGE_CXXFLAGS}")
if(CMAKE_COMPILER_IS_GNUCC)
# GCC only supports this on debug builds.
string(APPEND CMAKE_C_FLAGS_DEBUG " ${COMPILER_CODE_COVERAGE_CFLAGS}")
string(APPEND CMAKE_CXX_FLAGS_DEBUG " ${COMPILER_CODE_COVERAGE_CXXFLAGS}")
elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
# Clang will allow it on all builds.
string(APPEND CMAKE_C_FLAGS " ${COMPILER_CODE_COVERAGE_CFLAGS}")
string(APPEND CMAKE_CXX_FLAGS " ${COMPILER_CODE_COVERAGE_CXXFLAGS}")
endif()
endif()
if(NOT CMAKE_BUILD_TYPE MATCHES "Release")
@@ -2509,10 +2534,10 @@ if(WITH_PYTHON)
set(_numpy_include "_core/include")
if(PYTHON_VERSION VERSION_LESS "3.13")
set(_numpy_include "core/include")
endif()
endif()
find_python_package(numpy "${_numpy_include}")
unset(_numpy_include)
endif()
endif()
endif()
if(WIN32 OR APPLE)