blob: e5fae00f6f65495c2e32e8974931f299a311c84e [file] [log] [blame]
Imre Kise6bf9e62020-01-20 11:34:24 +01001#
2# Copyright (c) 2020, Arm Limited. All rights reserved.
3#
4# SPDX-License-Identifier: BSD-3-Clause
5#
6
Imre Kis1d2fbdd2019-12-13 11:42:08 +01007#[===[.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
Imre Kise6bf9e62020-01-20 11:34:24 +010053include_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
Imre Kisc73346f2020-02-07 15:26:27 +010088# 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
Imre Kis1d2fbdd2019-12-13 11:42:08 +0100105#[===[.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 CPICKER_MAPPING_PATH c_picker_mapping_path
115 OUTPUT_FILE output_file
116 )
117
118 The function outputs an lcov info file for further processing. It also handles
119 the remapping of the coverage of the c-picker generated files.
120
121 Control flow:
122
123 1. Running the ``lcov`` command for collecting the coverage data from the
124 available ``.gcda`` and ``.gcno`` files in the ``BINARY_DIR``.
125
126 2. The output of previous step is processed by ``c-picker-coverage-mapper``.
127 This will remap the coverage of files in ``CPICKER_MAPPING_PATH`` to the
128 original source files.
129
130 3. Adds the output file to the ``coverage`` target's dependency list.
131
132 Inputs:
133
134 ``NAME``
135 Test name included in lcov info file
136
137 ``SOURCE_DIR``
138 Directory of source files
139
140 ``BINARY_DIR``
141 Directory of the ``.gcda`` and ``.gcno`` files
142
143 ``CPICKER_MAPPING_PATH``
144 Path of c-picker generated files
145
146 ``OUTPUT_FILE``
147 Output lcov coverage info file
148
149#]===]
Imre Kise6bf9e62020-01-20 11:34:24 +0100150function(coverage_generate)
151 set(_OPTIONS_ARGS)
Imre Kis335834e2020-02-07 15:44:38 +0100152 set(_ONE_VALUE_ARGS NAME SOURCE_DIR BINARY_DIR CPICKER_MAPPING_PATH OUTPUT_FILE)
Imre Kise6bf9e62020-01-20 11:34:24 +0100153 set(_MULTI_VALUE_ARGS)
154 cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
155
156 set(TEST_NAME ${_MY_PARAMS_NAME})
Imre Kis335834e2020-02-07 15:44:38 +0100157 set(SOURCE_DIR ${_MY_PARAMS_SOURCE_DIR})
158 set(BINARY_DIR ${_MY_PARAMS_BINARY_DIR})
159 set(CPICKER_MAPPING_PATH ${_MY_PARAMS_CPICKER_MAPPING_PATH})
Imre Kisc73346f2020-02-07 15:26:27 +0100160 set(TEMP_FILE ${_MY_PARAMS_OUTPUT_FILE}_temp)
Imre Kise6bf9e62020-01-20 11:34:24 +0100161 set(OUTPUT_FILE ${_MY_PARAMS_OUTPUT_FILE})
162
Imre Kise6bf9e62020-01-20 11:34:24 +0100163 # Collecting information from .gcda and .gcno files into an lcov .info file
164 # Mapping c-picker generated files' coverage info to the original source lines
165 add_custom_command(
166 OUTPUT ${TEMP_FILE} ${OUTPUT_FILE}
167 COMMAND ${LCOV_COMMAND}
168 --capture
169 --test-name ${TEST_NAME}
Imre Kis335834e2020-02-07 15:44:38 +0100170 --directory ${BINARY_DIR}
171 --base-directory ${SOURCE_DIR}
Imre Kise6bf9e62020-01-20 11:34:24 +0100172 --output-file ${TEMP_FILE}
173 COMMAND ${CPICKER_COVERAGE_MAPPER_COMMAND}
174 --input ${TEMP_FILE}
175 --output ${OUTPUT_FILE}
Imre Kis335834e2020-02-07 15:44:38 +0100176 --mapping-path ${CPICKER_MAPPING_PATH}
Imre Kise6bf9e62020-01-20 11:34:24 +0100177 )
Imre Kisc73346f2020-02-07 15:26:27 +0100178
179 add_coverage_dependency(
180 TARGET coverage
181 DEPENDS ${OUTPUT_FILE}
182 )
Imre Kise6bf9e62020-01-20 11:34:24 +0100183endfunction()
184
Imre Kis1d2fbdd2019-12-13 11:42:08 +0100185#[===[.rst:
186.. cmake:command:: coverage_filter
187
188 .. code-block:: cmake
189
190 coverage_filter(
191 INPUT_FILE input_file
192 OUTPUT_FILE output_file
193 INCLUDE_DIRECTORY include_directory
194 )
195
196 The function filters the coverage data by including only the coverage of the
197 files of ``INCLUDE_DIRECTORY`` or its subdirectories. It adds the filtered
198 output file to the ``coverage`` target's dependency list.
199
200 Inputs:
201
202 ``INPUT_FILE``
203 Input lcov coverage info file
204
205 ``OUTPUT_FILE``
206 Output lcov coverage info file
207
208 ``INCLUDE_DIRECTORY``
209 Root directory of included files
210
211#]===]
Imre Kise6bf9e62020-01-20 11:34:24 +0100212function(coverage_filter)
213 set(_OPTIONS_ARGS)
214 set(_ONE_VALUE_ARGS INPUT_FILE OUTPUT_FILE INCLUDE_DIRECTORY)
215 set(_MULTI_VALUE_ARGS)
216 cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
217
Imre Kisc73346f2020-02-07 15:26:27 +0100218 set(INPUT_FILE ${_MY_PARAMS_INPUT_FILE})
219 set(OUTPUT_FILE ${_MY_PARAMS_OUTPUT_FILE})
220 set(INCLUDE_DIRECTORY ${_MY_PARAMS_INCLUDE_DIRECTORY})
221
Imre Kise6bf9e62020-01-20 11:34:24 +0100222 # The pattern must be an absolute path ending with an asterisk
Imre Kisc73346f2020-02-07 15:26:27 +0100223 get_filename_component(INCLUDE_DIRECTORY_ABSPATH "${INCLUDE_DIRECTORY}" ABSOLUTE)
Imre Kise6bf9e62020-01-20 11:34:24 +0100224 set(INCLUDE_DIRECTORY_ABSPATH "${INCLUDE_DIRECTORY_ABSPATH}/*")
225
226 add_custom_command(
Imre Kisc73346f2020-02-07 15:26:27 +0100227 OUTPUT ${OUTPUT_FILE}
Imre Kise6bf9e62020-01-20 11:34:24 +0100228 COMMAND ${LCOV_COMMAND}
Imre Kisc73346f2020-02-07 15:26:27 +0100229 --extract ${INPUT_FILE} \"${INCLUDE_DIRECTORY_ABSPATH}\"
230 --output-file ${OUTPUT_FILE}
231 DEPENDS ${INPUT_FILE}
Imre Kise6bf9e62020-01-20 11:34:24 +0100232 )
233
Imre Kisc73346f2020-02-07 15:26:27 +0100234 add_coverage_dependency(
235 TARGET coverage
236 DEPENDS ${OUTPUT_FILE}
237 )
Imre Kise6bf9e62020-01-20 11:34:24 +0100238endfunction()
239
Imre Kis1d2fbdd2019-12-13 11:42:08 +0100240#[===[.rst:
241.. cmake:command:: coverage_generate_report
242
243 .. code-block:: cmake
244
245 coverage_generate_report(
246 INPUT_FILE input_file
247 OUTPUT_DIRECTORY output_directory
248 )
249
250 The function generates a HTML coverage report from the lcov info file into
251 the ``OUTPUT_DIRECTORY``. It adds the output directory to the
252 ``coverage_report`` target's dependency list.
253
254 Inputs:
255
256 ``INPUT_FILE``
257 Input lcov coverage info file
258
259 ``OUTPUT_DIRECTORY``
260 Output directory of the coverage report where the ``index.html`` is the
261 root file of the report.
262
263#]===]
Imre Kise6bf9e62020-01-20 11:34:24 +0100264function(coverage_generate_report)
265 set(_OPTIONS_ARGS)
266 set(_ONE_VALUE_ARGS INPUT_FILE OUTPUT_DIRECTORY)
267 set(_MULTI_VALUE_ARGS)
268 cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
269
Imre Kisc73346f2020-02-07 15:26:27 +0100270 set(INPUT_FILE ${_MY_PARAMS_INPUT_FILE})
271 set(OUTPUT_DIRECTORY ${_MY_PARAMS_OUTPUT_DIRECTORY})
272
Imre Kise6bf9e62020-01-20 11:34:24 +0100273 add_custom_command(
Imre Kisc73346f2020-02-07 15:26:27 +0100274 OUTPUT ${OUTPUT_DIRECTORY}
275 COMMAND genhtml ${INPUT_FILE}
Imre Kise6bf9e62020-01-20 11:34:24 +0100276 --show-details
Imre Kisc73346f2020-02-07 15:26:27 +0100277 --output-directory ${OUTPUT_DIRECTORY}
278 DEPENDS ${INPUT_FILE}
Imre Kise6bf9e62020-01-20 11:34:24 +0100279 )
280
Imre Kisc73346f2020-02-07 15:26:27 +0100281 add_coverage_dependency(
282 TARGET coverage_report
283 DEPENDS ${OUTPUT_DIRECTORY}
284 )
Imre Kise6bf9e62020-01-20 11:34:24 +0100285endfunction()