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:
committed by
Ray molenkamp
parent
dd47ee9e25
commit
2c5b9e182b
@@ -1033,25 +1033,43 @@ Build and link with code coverage support (only for Debug targets)."
|
|||||||
mark_as_advanced(WITH_COMPILER_CODE_COVERAGE)
|
mark_as_advanced(WITH_COMPILER_CODE_COVERAGE)
|
||||||
|
|
||||||
if(WITH_COMPILER_CODE_COVERAGE)
|
if(WITH_COMPILER_CODE_COVERAGE)
|
||||||
if(NOT CMAKE_COMPILER_IS_GNUCC)
|
if(CMAKE_COMPILER_IS_GNUCC)
|
||||||
message(WARNING "WITH_COMPILER_CODE_COVERAGE only works with GCC currently.")
|
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)
|
set(WITH_COMPILER_CODE_COVERAGE OFF)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
|
||||||
|
|
||||||
if(WITH_COMPILER_CODE_COVERAGE)
|
# The code above could have disabled the feature, so check again.
|
||||||
set(_code_coverage_defaults "--coverage")
|
if(WITH_COMPILER_CODE_COVERAGE)
|
||||||
set(COMPILER_CODE_COVERAGE_CFLAGS
|
set(COMPILER_CODE_COVERAGE_CFLAGS
|
||||||
${_code_coverage_defaults} CACHE STRING
|
${_code_coverage_defaults} CACHE STRING
|
||||||
"C flags for code coverage"
|
"C flags for code coverage"
|
||||||
)
|
)
|
||||||
mark_as_advanced(COMPILER_CODE_COVERAGE_CFLAGS)
|
mark_as_advanced(COMPILER_CODE_COVERAGE_CFLAGS)
|
||||||
set(COMPILER_CODE_COVERAGE_CXXFLAGS
|
set(COMPILER_CODE_COVERAGE_CXXFLAGS
|
||||||
${_code_coverage_defaults} CACHE STRING
|
${_code_coverage_defaults} CACHE STRING
|
||||||
"C++ flags for code coverage"
|
"C++ flags for code coverage"
|
||||||
)
|
)
|
||||||
mark_as_advanced(COMPILER_CODE_COVERAGE_CXXFLAGS)
|
mark_as_advanced(COMPILER_CODE_COVERAGE_CXXFLAGS)
|
||||||
unset(_code_coverage_defaults)
|
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()
|
endif()
|
||||||
|
|
||||||
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang")
|
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||||
@@ -1463,8 +1481,15 @@ set(PLATFORM_LINKFLAGS_RELEASE "")
|
|||||||
set(PLATFORM_LINKFLAGS_EXECUTABLE "")
|
set(PLATFORM_LINKFLAGS_EXECUTABLE "")
|
||||||
|
|
||||||
if(WITH_COMPILER_CODE_COVERAGE)
|
if(WITH_COMPILER_CODE_COVERAGE)
|
||||||
string(APPEND CMAKE_C_FLAGS_DEBUG " ${COMPILER_CODE_COVERAGE_CFLAGS}")
|
if(CMAKE_COMPILER_IS_GNUCC)
|
||||||
string(APPEND CMAKE_CXX_FLAGS_DEBUG " ${COMPILER_CODE_COVERAGE_CXXFLAGS}")
|
# 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()
|
endif()
|
||||||
|
|
||||||
if(NOT CMAKE_BUILD_TYPE MATCHES "Release")
|
if(NOT CMAKE_BUILD_TYPE MATCHES "Release")
|
||||||
@@ -2509,10 +2534,10 @@ if(WITH_PYTHON)
|
|||||||
set(_numpy_include "_core/include")
|
set(_numpy_include "_core/include")
|
||||||
if(PYTHON_VERSION VERSION_LESS "3.13")
|
if(PYTHON_VERSION VERSION_LESS "3.13")
|
||||||
set(_numpy_include "core/include")
|
set(_numpy_include "core/include")
|
||||||
endif()
|
endif()
|
||||||
find_python_package(numpy "${_numpy_include}")
|
find_python_package(numpy "${_numpy_include}")
|
||||||
unset(_numpy_include)
|
unset(_numpy_include)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WIN32 OR APPLE)
|
if(WIN32 OR APPLE)
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ function(blender_test_set_envvars testname envvar_list)
|
|||||||
list(APPEND envvar_list "${_lsan_options}" "${_asan_options}")
|
list(APPEND envvar_list "${_lsan_options}" "${_asan_options}")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
if(WITH_COMPILER_CODE_COVERAGE AND CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||||
|
list(APPEND envvar_list "LLVM_PROFILE_FILE=${COMPILER_CODE_COVERAGE_DATA_DIR}/raw/blender_%p.profraw")
|
||||||
|
endif()
|
||||||
# Can only be called once per test to define its custom environment variables.
|
# Can only be called once per test to define its custom environment variables.
|
||||||
set_tests_properties(${testname} PROPERTIES ENVIRONMENT "${envvar_list}")
|
set_tests_properties(${testname} PROPERTIES ENVIRONMENT "${envvar_list}")
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|||||||
Submodule lib/macos_arm64 updated: 61f8c3337c...e07a7e9ab9
Submodule lib/windows_x64 updated: 9bad6ade51...cdef40845c
@@ -71,15 +71,70 @@ endif()
|
|||||||
add_subdirectory(gtests)
|
add_subdirectory(gtests)
|
||||||
|
|
||||||
if(WITH_COMPILER_CODE_COVERAGE)
|
if(WITH_COMPILER_CODE_COVERAGE)
|
||||||
set(COVERAGE_SCRIPT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/coverage/coverage.py)
|
if(CMAKE_COMPILER_IS_GNUCC)
|
||||||
|
set(COVERAGE_SCRIPT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/coverage/coverage.py)
|
||||||
|
|
||||||
add_custom_target(coverage-report
|
add_custom_target(coverage-report
|
||||||
${PYTHON_EXECUTABLE} ${COVERAGE_SCRIPT_PATH} report --build-directory ${CMAKE_BINARY_DIR}
|
${PYTHON_EXECUTABLE} ${COVERAGE_SCRIPT_PATH} report --build-directory ${CMAKE_BINARY_DIR} --no-browser
|
||||||
USES_TERMINAL
|
USES_TERMINAL
|
||||||
)
|
)
|
||||||
|
|
||||||
add_custom_target(coverage-reset
|
add_custom_target(coverage-show
|
||||||
${PYTHON_EXECUTABLE} ${COVERAGE_SCRIPT_PATH} reset --build-directory ${CMAKE_BINARY_DIR}
|
${PYTHON_EXECUTABLE} ${COVERAGE_SCRIPT_PATH} report --build-directory ${CMAKE_BINARY_DIR}
|
||||||
USES_TERMINAL
|
USES_TERMINAL
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_custom_target(coverage-reset
|
||||||
|
${PYTHON_EXECUTABLE} ${COVERAGE_SCRIPT_PATH} reset --build-directory ${CMAKE_BINARY_DIR}
|
||||||
|
USES_TERMINAL
|
||||||
|
)
|
||||||
|
elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||||
|
add_custom_target(coverage-reset
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E
|
||||||
|
remove_directory ${COMPILER_CODE_COVERAGE_DATA_DIR}
|
||||||
|
USES_TERMINAL
|
||||||
|
)
|
||||||
|
add_custom_target(coverage-merge
|
||||||
|
COMMENT "Merging code-coverage data."
|
||||||
|
BYPRODUCTS "${COMPILER_CODE_COVERAGE_DATA_DIR}/blender.profdata"
|
||||||
|
COMMAND "${LLVM_PROFDATA}"
|
||||||
|
merge
|
||||||
|
-sparse
|
||||||
|
${COMPILER_CODE_COVERAGE_DATA_DIR}/raw/*.profraw
|
||||||
|
-o ${COMPILER_CODE_COVERAGE_DATA_DIR}/blender.profdata
|
||||||
|
)
|
||||||
|
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 18.1 OR
|
||||||
|
(CMAKE_C_COMPILER_ID MATCHES "AppleClang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 17.0)
|
||||||
|
)
|
||||||
|
# Available clang on 18.1+ only , Apple's compiler doesn't map 1:1 to clang
|
||||||
|
# versions so on apple we check for version 17+ which maps to clang
|
||||||
|
# version 19.1.4, given this is somewhat unintuitive there is a handy table on
|
||||||
|
# https://en.wikipedia.org/wiki/Xcode#Xcode_15.0_-_(since_visionOS_support)_2
|
||||||
|
set(_cov_extra_args -show-directory-coverage)
|
||||||
|
endif()
|
||||||
|
add_custom_target(coverage-report
|
||||||
|
BYPRODUCTS "${COMPILER_CODE_COVERAGE_DATA_DIR}/report/index.html"
|
||||||
|
SOURCES "${COMPILER_CODE_COVERAGE_DATA_DIR}/blender.profdata"
|
||||||
|
COMMENT "Generating code-coverage report."
|
||||||
|
COMMAND "${LLVM_COV}" show
|
||||||
|
-format=html
|
||||||
|
-show-instantiations=true
|
||||||
|
-show-line-counts-or-regions
|
||||||
|
${_cov_extra_args}
|
||||||
|
-instr-profile=${COMPILER_CODE_COVERAGE_DATA_DIR}/blender.profdata
|
||||||
|
--output-dir=${COMPILER_CODE_COVERAGE_DATA_DIR}/report
|
||||||
|
$<TARGET_FILE:blender>
|
||||||
|
USES_TERMINAL
|
||||||
|
)
|
||||||
|
if(NOT WIN32)
|
||||||
|
set(_open_cmd "open")
|
||||||
|
endif()
|
||||||
|
add_custom_target(coverage-show
|
||||||
|
COMMENT "Starting browser for ${COMPILER_CODE_COVERAGE_DATA_DIR}/report/index.html"
|
||||||
|
COMMAND ${_open_cmd} "${COMPILER_CODE_COVERAGE_DATA_DIR}/report/index.html"
|
||||||
|
SOURCES "${COMPILER_CODE_COVERAGE_DATA_DIR}/report/index.html"
|
||||||
|
)
|
||||||
|
unset(_cov_extra_args)
|
||||||
|
unset(_open_cmd)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
Reference in New Issue
Block a user