-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAddCoverage.cmake
More file actions
181 lines (159 loc) · 5.1 KB
/
AddCoverage.cmake
File metadata and controls
181 lines (159 loc) · 5.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# newest features used: TBD
cmake_minimum_required(VERSION 3.15)
include_guard(DIRECTORY)
if(COMMAND add_coverage_instrumentation AND
COMMAND add_coverage_report)
return()
endif()
if(NOT COMMAND get_all_target_dependencies)
include(GetAllTargetDependencies)
endif()
find_program(GCOV_BINARY
gcov
REQUIRED
)
find_program(LCOV_BINARY
lcov
REQUIRED
)
find_program(GENHTML_BINARY
genhtml
REQUIRED
)
# define_property calls should be idempotent
define_property(TARGET PROPERTY
COVERAGE_INSTRUMENTATION
BRIEF_DOCS "boolean indicating code coverage instrumentation has been added \
to target"
)
define_property(TARGET PROPERTY
COVERAGE_REPORT
BRIEF_DOCS "boolean indicating code coverage report generation target has \
been added for target"
)
# add_coverage_instrumentation(target)
# When building in Debug mode, turns on coverage instrumentation for a
# given target, generating files for use in later generating coverage
# reports:
# - .gcno, or GNU Coverage Notes
# - .gcda, or GNU Coverage Data
# Should support both GCC and Clang, provided their respective report
# generation tools (gcov and llvm-cov) are available.
#
# target (string): target to which to add instrumentation
#
function(add_coverage_instrumentation target)
if (NOT TARGET ${target})
return()
endif()
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
message(WARNING "add_coverage_instrumentation(${target}): coverage only \
supported when compiling in Debug mode")
return()
endif()
get_target_property(coverage_instrumentation ${target}
COVERAGE_INSTRUMENTATION
)
if(coverage_instrumentation)
return()
endif()
# clang should match gcc support of --coverage flags (enabling -fprofile-arcs
# and -ftest-coverage)
target_compile_options(${target} PRIVATE
--coverage
)
target_link_options(${target} PUBLIC
--coverage
)
# .gcno files are updated at every recompilation, but .gcda files are not,
# potentially causing a mismatch that can result in SEGFAULTs, so here
# we clean any stale .gcda files
add_custom_command(TARGET ${target}
PRE_BUILD COMMAND
find ${CMAKE_BINARY_DIR} -type f -name '*.gcda' -exec rm {} +
)
set_target_properties(${target} PROPERTIES
COVERAGE_INSTRUMENTATION TRUE
)
endfunction()
# add_coverage_report(target)
# Sets up an optional target `${target}_coverage` that generates an HTML
# code coverage report for the given target. All coverage report targets
# will also be linked to a catchall `${PROJECT_NAME}_coverage` target.
#
# target (string): target for which to generate report
#
function(add_coverage_report target)
if (NOT TARGET ${target})
return()
endif()
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
message(WARNING "add_coverage_report(${target}): coverage only supported when \
compiling in Debug mode")
return()
endif()
get_target_property(coverage_report ${target}
COVERAGE_REPORT
)
if(coverage_report)
return()
endif()
get_target_property(coverage_instrumentation ${target}
COVERAGE_INSTRUMENTATION
)
if(NOT coverage_instrumentation)
get_all_target_dependencies(${target} ${target}_deps)
foreach(dep ${${target}_deps})
get_target_property(coverage_instrumentation ${dep}
COVERAGE_INSTRUMENTATION
)
if(coverage_instrumentation)
break()
endif()
endforeach()
endif()
if(NOT coverage_instrumentation)
message(FATAL_ERROR "add_coverage_report(${target}): coverage report \
generation requires target or at least one dependency to have coverage \
instrumentation")
endif()
set(LCOV_BASE_CMD "${LCOV_BINARY}")
if(CMAKE_C_COMPILER MATCHES "clang" OR CMAKE_CXX_COMPILER MATCHES "clang")
find_program(LLVM-COV_BINARY
llvm-cov
)
if(NOT LLVM-COV_BINARY)
message(FATAL_ERROR "add_coverage_report(${target}): coverage report \
generation when compiling with clang requires llvm-cov")
endif()
# man lcov(1) -> man geninfo(1): second use of --gcov-tool adds params to
# command named in first use, so equivalent to `llvm-cov gcov`
string(APPEND LCOV_BASE_CMD
" --gcov-tool ${LLVM-COV_BINARY} --gcov-tool gcov")
endif()
if(NOT TARGET ${PROJECT_NAME}_coverage)
add_custom_target(${PROJECT_NAME}_coverage)
endif()
add_custom_target(${target}_coverage
COMMENT "Running coverage for ${target}..."
# reset counters
COMMAND ${LCOV_BASE_CMD} -d . --zerocounters
# run target executable
COMMAND $<TARGET_FILE:${target}>
# collect metrics from current directory and output to file
COMMAND ${LCOV_BASE_CMD} -d . --capture -o coverage.info
# filter out coverage on system headers
COMMAND ${LCOV_BASE_CMD} -r coverage.info '/usr/include/*' -o filtered.info
# generate HTML report in coverage directory, with legend color
COMMAND ${GENHTML_BINARY} -o coverage filtered.info --legend
# cleanup
COMMAND rm -rf coverage.info filtered.info
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
add_dependencies(${PROJECT_NAME}_coverage
${target}_coverage
)
set_target_properties(${target} PROPERTIES
COVERAGE_REPORT TRUE
)
endfunction()