1
# Copyright (c) 2012 - 2017, Lars Bilke
2
# Copyright (c) 2021 KeePassXC Team
5
# Redistribution and use in source and binary forms, with or without modification,
6
# are permitted provided that the following conditions are met:
8
# 1. Redistributions of source code must retain the above copyright notice, this
9
# list of conditions and the following disclaimer.
11
# 2. Redistributions in binary form must reproduce the above copyright notice,
12
# this list of conditions and the following disclaimer in the documentation
13
# and/or other materials provided with the distribution.
15
# 3. Neither the name of the copyright holder nor the names of its contributors
16
# may be used to endorse or promote products derived from this software without
17
# specific prior written permission.
19
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
23
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
include(CMakeParseArguments)
33
find_program(GCOV_PATH gcov)
34
find_program(LLVM_COV_PATH llvm-cov)
35
find_program(LLVM_PROFDATA_PATH llvm-profdata)
36
find_program(XCRUN_PATH xcrun)
37
find_program(GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat)
38
find_program(GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test)
40
set(COVERAGE_COMPILER_FLAGS "-g -O0" CACHE INTERNAL "")
41
if(CMAKE_COMPILER_IS_GNUCXX)
42
set(COVERAGE_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} --coverage -fprofile-arcs -ftest-coverage")
43
elseif(CMAKE_COMPILER_IS_CLANGXX)
44
set(COVERAGE_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-instr-generate -fcoverage-mapping")
47
set(CMAKE_COVERAGE_FORMAT
49
CACHE STRING "Coverage report output format.")
50
set_property(CACHE CMAKE_COVERAGE_FORMAT PROPERTY STRINGS "html" "txt")
52
set(CMAKE_CXX_FLAGS_COVERAGE
53
${COVERAGE_COMPILER_FLAGS}
54
CACHE STRING "Flags used by the C++ compiler during coverage builds.")
55
set(CMAKE_C_FLAGS_COVERAGE
56
${COVERAGE_COMPILER_FLAGS}
57
CACHE STRING "Flags used by the C compiler during coverage builds.")
58
set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
60
CACHE STRING "Flags used for linking binaries during coverage builds.")
61
set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
63
CACHE STRING "Flags used by the shared libraries linker during coverage builds.")
66
CMAKE_CXX_FLAGS_COVERAGE
67
CMAKE_C_FLAGS_COVERAGE
68
CMAKE_EXE_LINKER_FLAGS_COVERAGE
69
CMAKE_SHARED_LINKER_FLAGS_COVERAGE)
71
if(NOT CMAKE_BUILD_TYPE_LOWER STREQUAL "debug")
72
message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading")
73
endif() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug"
75
if(CMAKE_COMPILER_IS_GNUCXX)
77
message(FATAL_ERROR "gcov not found! Aborting...")
78
endif() # NOT GCOV_PATH
83
# Defines a target for running and collection code coverage information
84
# Builds dependencies, runs the given executable and outputs reports.
85
# NOTE! The executable should always have a ZERO as exit code otherwise
86
# the coverage generation will not complete.
88
# SETUP_TARGET_FOR_COVERAGE_GCOVR(
89
# NAME ctest_coverage # New target name
90
# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
91
# DEPENDENCIES executable_target # Dependencies to build first
93
function(SETUP_TARGET_FOR_COVERAGE_GCOVR)
96
set(oneValueArgs NAME SOURCES_ROOT)
97
set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
98
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
101
message(FATAL_ERROR "gcovr not found! Aborting...")
102
endif() # NOT GCOVR_PATH
104
# Combine excludes to several -e arguments
105
set(GCOVR_EXCLUDES "")
106
foreach(EXCLUDE ${COVERAGE_EXCLUDES})
107
list(APPEND GCOVR_EXCLUDES "-e")
108
list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
111
add_custom_target(${Coverage_NAME}
113
COMMAND ctest -C $<CONFIG> $ENV{ARGS} $$ARGS
115
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
116
DEPENDS ${Coverage_DEPENDENCIES}
119
if("html" IN_LIST CMAKE_COVERAGE_FORMAT)
120
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
122
COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${Coverage_NAME}-html
125
COMMAND ${GCOVR_PATH} --html --html-details
126
-r ${Coverage_SOURCES_ROOT} ${GCOVR_EXCLUDES}
127
--object-directory=${PROJECT_BINARY_DIR}
128
--exclude-unreachable-branches --exclude-throw-branches
129
-o ${Coverage_NAME}-html/index.html
130
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
131
COMMENT "Running gcovr to produce HTML code coverage report ${Coverage_NAME}-html."
135
if("xml" IN_LIST CMAKE_COVERAGE_FORMAT)
136
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
138
COMMAND ${GCOVR_PATH} --xml
139
-r ${Coverage_SOURCES_ROOT} ${GCOVR_EXCLUDES}
140
--object-directory=${PROJECT_BINARY_DIR}
141
--exclude-unreachable-branches --exclude-throw-branches
142
-o ${Coverage_NAME}.xml
143
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
144
COMMENT "Running gcovr to produce XML code coverage report ${Coverage_NAME}.xml."
148
if("txt" IN_LIST CMAKE_COVERAGE_FORMAT)
149
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
151
COMMAND ${GCOVR_PATH}
152
-r ${Coverage_SOURCES_ROOT} ${GCOVR_EXCLUDES}
153
--object-directory=${PROJECT_BINARY_DIR}
154
--exclude-unreachable-branches --exclude-throw-branches
155
-o ${Coverage_NAME}.txt
156
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
157
COMMENT "Running gcovr to produce TXT code coverage report ${Coverage_NAME}.txt."
161
endfunction() # SETUP_TARGET_FOR_COVERAGE_GCOVR
163
# Defines a target for running and collection code coverage information
164
# Builds dependencies, runs the given executable and outputs reports.
165
# NOTE! The executable should always have a ZERO as exit code otherwise
166
# the coverage generation will not complete.
168
# SETUP_TARGET_FOR_COVERAGE_LLVM(
169
# NAME ctest_coverage # New target name
170
# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
171
# DEPENDENCIES executable_target # Dependencies to build first
173
function(SETUP_TARGET_FOR_COVERAGE_LLVM)
176
set(oneValueArgs NAME SOURCES_ROOT PROF_FILE)
177
set(multiValueArgs EXECUTABLE BINARY EXECUTABLE_ARGS DEPENDENCIES)
178
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
181
set(LLVM_COV_PATH ${XCRUN_PATH} llvm-cov)
182
set(LLVM_PROFDATA_PATH ${XCRUN_PATH} llvm-profdata)
184
if(NOT LLVM_COV_PATH)
185
message(FATAL_ERROR "llvm-cov not found! Aborting...")
186
endif() # NOT LLVM_COV_PATH
187
if(NOT LLVM_PROFDATA_PATH)
188
message(FATAL_ERROR "llvm-profdata not found! Aborting...")
189
endif() # NOT LLVM_PROFDATA_PATH
192
set(LLVM_PROFILE_DIR ${PROJECT_BINARY_DIR}/llvm_profile)
193
file(REMOVE_RECURSE ${LLVM_PROFILE_DIR})
196
foreach(EXCLUDE ${COVERAGE_EXCLUDES})
197
list(APPEND COV_EXCLUDES "-ignore-filename-regex=${EXCLUDE}")
200
list(GET Coverage_BINARY 0 COV_BINARY)
202
list(REMOVE_AT Coverage_BINARY 0)
203
foreach(BIN ${Coverage_BINARY})
204
list(APPEND COV_BINARY -object ${BIN})
208
add_custom_target(${Coverage_NAME}
209
COMMAND ${CMAKE_COMMAND} -E env LLVM_PROFILE_FILE=${LLVM_PROFILE_DIR}/profile-%p.profraw ctest -C $<CONFIG> $$ARGS
211
COMMAND ${LLVM_PROFDATA_PATH} merge -sparse ${LLVM_PROFILE_DIR}/* -o coverage.profdata
212
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
213
DEPENDS ${Coverage_DEPENDENCIES})
215
if("html" IN_LIST CMAKE_COVERAGE_FORMAT)
216
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
217
COMMAND ${LLVM_COV_PATH} show -instr-profile=coverage.profdata ${COV_BINARY}
218
--format=html --output-dir=${Coverage_NAME}-html ${COV_EXCLUDES} ${Coverage_SOURCES_ROOT}
219
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
220
COMMENT "Running llvm-cov to produce HTML code coverage report ${Coverage_NAME}-html")
223
if("xml" IN_LIST CMAKE_COVERAGE_FORMAT)
224
message(WARNING "XML coverage report format not supported for llvm-cov")
227
if("txt" IN_LIST CMAKE_COVERAGE_FORMAT)
228
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
229
COMMAND ${LLVM_COV_PATH} show -instr-profile=coverage.profdata ${COV_BINARY}
230
--format=text ${COV_EXCLUDES} ${Coverage_SOURCES_ROOT} > ${Coverage_NAME}.txt
232
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
233
COMMENT "Running llvm-cov to produce TXT code coverage report ${Coverage_NAME}.txt.")
236
endfunction() # SETUP_TARGET_FOR_COVERAGE_LLVM
239
function(APPEND_COVERAGE_COMPILER_FLAGS)
240
message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}")
241
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
242
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
243
endfunction() # APPEND_COVERAGE_COMPILER_FLAGS