blob: b7a643931797e46d1fa780471619c957cee5e76f [file] [log] [blame]
Imre Kisa21712e2019-10-08 12:56:59 +02001#
2# Copyright (c) 2020-2021, Arm Limited. All rights reserved.
3#
4# SPDX-License-Identifier: BSD-3-Clause
5#
6
7#[===[.rst:
8Coverage CMake module
9---------------------
10
11Control flow
12^^^^^^^^^^^^
13
14Using the code coverage feature of the system starts with including
15``Coverage`` module. This will implicitly check if all the requirements for
16generating coverage are fulfilled. This includes checking the following
17conditions.
18
19- Compiler is GCC
20- lcov executables exist
21- ``c-picker-coverage-mapper`` is available
22
23As the next step it sets the compiler flags to make GCC to generate binaries
24with coverage information.
25
26
27Variables
28^^^^^^^^^
29
30The module sets the following variables while it's checking its prerequisites.
31
32.. cmake:variable:: LCOV_COMMAND
33
34Path of lcov executable
35
36.. cmake:variable:: GENHTML_COMMAND
37
38Path of genhtml executable which is part of the lcov package.
39
40.. cmake:variable:: CPICKER_COVERAGE_MAPPER_COMMAND
41
42Path of ``c-picker-coverage-mapper`` executable which is provided by c-picker
43pip package.
44
45
46Functions
47^^^^^^^^^
48
49The module also contains functions for setting up the coverage feature.
50
51#]===]
52
53include_guard(DIRECTORY)
54
55# Checking GCC
56if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
57 message(FATAL_ERROR "Coverage measurement is only supported when using GCC")
58endif()
59
60# Checking lcov
61find_program(LCOV_COMMAND "lcov")
62if (NOT LCOV_COMMAND)
63 message(FATAL_ERROR "Please install lcov")
64endif()
65
66# Checking c-picker-coverage-mapper
67find_program(CPICKER_COVERAGE_MAPPER_COMMAND "c-picker-coverage-mapper")
68if (NOT CPICKER_COVERAGE_MAPPER_COMMAND)
69 message(FATAL_ERROR "Please install c-picker-coverage-mapper using pip (part of c-picker)")
70endif()
71
72# Checking genhtml
73find_program(GENHTML_COMMAND "genhtml")
74if (NOT GENHTML_COMMAND)
75 message(FATAL_ERROR "Please install genhtml with genhtml (part of lcov)")
76endif()
77
78# Including this file enables code coverage measurement by adding the necessary compiler and
79# linker flags.
80set(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
81set(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage -fno-exceptions")
82set(CMAKE_EXE_LINKER_FLAGS "-fprofile-arcs -ftest-coverage")
83
84# Adding custom targets
85add_custom_target(coverage)
86add_custom_target(coverage_report)
87
88# Adds a file to the dependency list of the target by inserting an accessory
89# custom target. The name of the custom target is properly escaped.
90function(add_coverage_dependency)
91 set(_OPTIONS_ARGS)
92 set(_ONE_VALUE_ARGS TARGET DEPENDS)
93 set(_MULTI_VALUE_ARGS)
94 cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
95
96 set(TARGET ${_MY_PARAMS_TARGET})
97 set(DEPENDS ${_MY_PARAMS_DEPENDS})
98
99 string(REGEX REPLACE "\\/" "_" CUSTOM_TARGET_SUFFIX ${DEPENDS})
100
101 add_custom_target(${TARGET}_target_${CUSTOM_TARGET_SUFFIX} DEPENDS ${DEPENDS})
102 add_dependencies(${TARGET} ${TARGET}_target_${CUSTOM_TARGET_SUFFIX})
103endfunction()
104
105#[===[.rst:
106.. cmake:command:: coverage_generate
107
108 .. code-block:: cmake
109
110 coverage_generate(
111 NAME test_name
112 SOURCE_DIR source_directory
113 BINARY_DIR binary_directory
114 OUTPUT_FILE output_file
115 )
116
117 The function outputs an lcov info file for further processing. It also handles
118 the remapping of the coverage of the c-picker generated files.
119
120 Control flow:
121
122 1. Running the ``lcov`` command for collecting the coverage data from the
123 available ``.gcda`` and ``.gcno`` files in the ``BINARY_DIR``.
124
125 2. The output of previous step is processed by ``c-picker-coverage-mapper``.
126 This will remap the coverage of files in ``CPICKER_CACHE_PATH`` to the
127 original source files.
128
129 3. Adds the output file to the ``coverage`` target's dependency list.
130
131 Inputs:
132
133 ``NAME``
134 Test name included in lcov info file
135
136 ``SOURCE_DIR``
137 Directory of source files
138
139 ``BINARY_DIR``
140 Directory of the ``.gcda`` and ``.gcno`` files
141
142 ``OUTPUT_FILE``
143 Output lcov coverage info file
144
145 Global dependencies:
146
147 ``CPICKER_CACHE_PATH``
148 Root directory of the c-picker generated files
149
150
151#]===]
152function(coverage_generate)
153 set(_OPTIONS_ARGS)
154 set(_ONE_VALUE_ARGS NAME SOURCE_DIR BINARY_DIR OUTPUT_FILE)
155 set(_MULTI_VALUE_ARGS)
156 cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
157
158 set(TEST_NAME ${_MY_PARAMS_NAME})
159 set(SOURCE_DIR ${_MY_PARAMS_SOURCE_DIR})
160 set(BINARY_DIR ${_MY_PARAMS_BINARY_DIR})
161 set(TEMP_FILE ${_MY_PARAMS_OUTPUT_FILE}_temp)
162 set(OUTPUT_FILE ${_MY_PARAMS_OUTPUT_FILE})
163
164 # Collecting information from .gcda and .gcno files into an lcov .info file
165 # Mapping c-picker generated files' coverage info to the original source lines
166 add_custom_command(
167 OUTPUT ${TEMP_FILE} ${OUTPUT_FILE}
168 COMMAND ${LCOV_COMMAND}
169 --capture
170 --test-name ${TEST_NAME}
171 --directory ${BINARY_DIR}
172 --base-directory ${SOURCE_DIR}
173 --output-file ${TEMP_FILE}
174 COMMAND ${CPICKER_COVERAGE_MAPPER_COMMAND}
175 --input ${TEMP_FILE}
176 --output ${OUTPUT_FILE}
177 --mapping-path ${CPICKER_CACHE_PATH}
178 )
179
180 add_coverage_dependency(
181 TARGET coverage
182 DEPENDS ${OUTPUT_FILE}
183 )
184endfunction()
185
186#[===[.rst:
187.. cmake:command:: coverage_filter
188
189 .. code-block:: cmake
190
191 coverage_filter(
192 INPUT_FILE input_file
193 OUTPUT_FILE output_file
194 INCLUDE_DIRECTORY include_directory
195 )
196
197 The function filters the coverage data by including only the coverage of the
198 files of ``INCLUDE_DIRECTORY`` or its subdirectories. It adds the filtered
199 output file to the ``coverage`` target's dependency list.
200
201 Inputs:
202
203 ``INPUT_FILE``
204 Input lcov coverage info file
205
206 ``OUTPUT_FILE``
207 Output lcov coverage info file
208
209 ``INCLUDE_DIRECTORY``
210 Root directory of included files
211
212#]===]
213function(coverage_filter)
214 set(_OPTIONS_ARGS)
215 set(_ONE_VALUE_ARGS INPUT_FILE OUTPUT_FILE INCLUDE_DIRECTORY)
216 set(_MULTI_VALUE_ARGS)
217 cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
218
219 set(INPUT_FILE ${_MY_PARAMS_INPUT_FILE})
220 set(OUTPUT_FILE ${_MY_PARAMS_OUTPUT_FILE})
221 set(INCLUDE_DIRECTORY ${_MY_PARAMS_INCLUDE_DIRECTORY})
222
223 # The pattern must be an absolute path ending with an asterisk
224 get_filename_component(INCLUDE_DIRECTORY_ABSPATH "${INCLUDE_DIRECTORY}" ABSOLUTE)
225 set(INCLUDE_DIRECTORY_ABSPATH "${INCLUDE_DIRECTORY_ABSPATH}/*")
226
227 add_custom_command(
228 OUTPUT ${OUTPUT_FILE}
229 COMMAND ${LCOV_COMMAND}
230 --extract ${INPUT_FILE} \"${INCLUDE_DIRECTORY_ABSPATH}\"
231 --output-file ${OUTPUT_FILE}
232 DEPENDS ${INPUT_FILE}
233 )
234
235 add_coverage_dependency(
236 TARGET coverage
237 DEPENDS ${OUTPUT_FILE}
238 )
239endfunction()
240
241#[===[.rst:
242.. cmake:command:: coverage_generate_report
243
244 .. code-block:: cmake
245
246 coverage_generate_report(
247 INPUT_FILE input_file
248 OUTPUT_DIRECTORY output_directory
249 )
250
251 The function generates a HTML coverage report from the lcov info file into
252 the ``OUTPUT_DIRECTORY``. It adds the output directory to the
253 ``coverage_report`` target's dependency list.
254
255 Inputs:
256
257 ``INPUT_FILE``
258 Input lcov coverage info file
259
260 ``OUTPUT_DIRECTORY``
261 Output directory of the coverage report where the ``index.html`` is the
262 root file of the report.
263
264#]===]
265function(coverage_generate_report)
266 set(_OPTIONS_ARGS)
267 set(_ONE_VALUE_ARGS INPUT_FILE OUTPUT_DIRECTORY)
268 set(_MULTI_VALUE_ARGS)
269 cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
270
271 set(INPUT_FILE ${_MY_PARAMS_INPUT_FILE})
272 set(OUTPUT_DIRECTORY ${_MY_PARAMS_OUTPUT_DIRECTORY})
273
274 add_custom_command(
275 OUTPUT ${OUTPUT_DIRECTORY}
276 COMMAND genhtml ${INPUT_FILE}
277 --show-details
278 --output-directory ${OUTPUT_DIRECTORY}
279 DEPENDS ${INPUT_FILE}
280 )
281
282 add_coverage_dependency(
283 TARGET coverage_report
284 DEPENDS ${OUTPUT_DIRECTORY}
285 )
286endfunction()