blob: e5fae00f6f65495c2e32e8974931f299a311c84e [file] [log] [blame]
#
# Copyright (c) 2020, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
#[===[.rst:
Coverage CMake module
---------------------
Control flow
^^^^^^^^^^^^
Using the code coverage feature of the system starts with including
``Coverage`` module. This will implicitly check if all the requirements for
generating coverage are fulfilled. This includes checking the following
conditions.
- Compiler is GCC
- lcov executables exist
- ``c-picker-coverage-mapper`` is available
As the next step it sets the compiler flags to make GCC to generate binaries
with coverage information.
Variables
^^^^^^^^^
The module sets the following variables while it's checking its prerequisites.
.. cmake:variable:: LCOV_COMMAND
Path of lcov executable
.. cmake:variable:: GENHTML_COMMAND
Path of genhtml executable which is part of the lcov package.
.. cmake:variable:: CPICKER_COVERAGE_MAPPER_COMMAND
Path of ``c-picker-coverage-mapper`` executable which is provided by c-picker
pip package.
Functions
^^^^^^^^^
The module also contains functions for setting up the coverage feature.
#]===]
include_guard(DIRECTORY)
# Checking GCC
if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
message(FATAL_ERROR "Coverage measurement is only supported when using GCC")
endif()
# Checking lcov
find_program(LCOV_COMMAND "lcov")
if (NOT LCOV_COMMAND)
message(FATAL_ERROR "Please install lcov")
endif()
# Checking c-picker-coverage-mapper
find_program(CPICKER_COVERAGE_MAPPER_COMMAND "c-picker-coverage-mapper")
if (NOT CPICKER_COVERAGE_MAPPER_COMMAND)
message(FATAL_ERROR "Please install c-picker-coverage-mapper using pip (part of c-picker)")
endif()
# Checking genhtml
find_program(GENHTML_COMMAND "genhtml")
if (NOT GENHTML_COMMAND)
message(FATAL_ERROR "Please install genhtml with genhtml (part of lcov)")
endif()
# Including this file enables code coverage measurement by adding the necessary compiler and
# linker flags.
set(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
set(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage -fno-exceptions")
set(CMAKE_EXE_LINKER_FLAGS "-fprofile-arcs -ftest-coverage")
# Adding custom targets
add_custom_target(coverage)
add_custom_target(coverage_report)
# Adds a file to the dependency list of the target by inserting an accessory
# custom target. The name of the custom target is properly escaped.
function(add_coverage_dependency)
set(_OPTIONS_ARGS)
set(_ONE_VALUE_ARGS TARGET DEPENDS)
set(_MULTI_VALUE_ARGS)
cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
set(TARGET ${_MY_PARAMS_TARGET})
set(DEPENDS ${_MY_PARAMS_DEPENDS})
string(REGEX REPLACE "\\/" "_" CUSTOM_TARGET_SUFFIX ${DEPENDS})
add_custom_target(${TARGET}_target_${CUSTOM_TARGET_SUFFIX} DEPENDS ${DEPENDS})
add_dependencies(${TARGET} ${TARGET}_target_${CUSTOM_TARGET_SUFFIX})
endfunction()
#[===[.rst:
.. cmake:command:: coverage_generate
.. code-block:: cmake
coverage_generate(
NAME test_name
SOURCE_DIR source_directory
BINARY_DIR binary_directory
CPICKER_MAPPING_PATH c_picker_mapping_path
OUTPUT_FILE output_file
)
The function outputs an lcov info file for further processing. It also handles
the remapping of the coverage of the c-picker generated files.
Control flow:
1. Running the ``lcov`` command for collecting the coverage data from the
available ``.gcda`` and ``.gcno`` files in the ``BINARY_DIR``.
2. The output of previous step is processed by ``c-picker-coverage-mapper``.
This will remap the coverage of files in ``CPICKER_MAPPING_PATH`` to the
original source files.
3. Adds the output file to the ``coverage`` target's dependency list.
Inputs:
``NAME``
Test name included in lcov info file
``SOURCE_DIR``
Directory of source files
``BINARY_DIR``
Directory of the ``.gcda`` and ``.gcno`` files
``CPICKER_MAPPING_PATH``
Path of c-picker generated files
``OUTPUT_FILE``
Output lcov coverage info file
#]===]
function(coverage_generate)
set(_OPTIONS_ARGS)
set(_ONE_VALUE_ARGS NAME SOURCE_DIR BINARY_DIR CPICKER_MAPPING_PATH OUTPUT_FILE)
set(_MULTI_VALUE_ARGS)
cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
set(TEST_NAME ${_MY_PARAMS_NAME})
set(SOURCE_DIR ${_MY_PARAMS_SOURCE_DIR})
set(BINARY_DIR ${_MY_PARAMS_BINARY_DIR})
set(CPICKER_MAPPING_PATH ${_MY_PARAMS_CPICKER_MAPPING_PATH})
set(TEMP_FILE ${_MY_PARAMS_OUTPUT_FILE}_temp)
set(OUTPUT_FILE ${_MY_PARAMS_OUTPUT_FILE})
# Collecting information from .gcda and .gcno files into an lcov .info file
# Mapping c-picker generated files' coverage info to the original source lines
add_custom_command(
OUTPUT ${TEMP_FILE} ${OUTPUT_FILE}
COMMAND ${LCOV_COMMAND}
--capture
--test-name ${TEST_NAME}
--directory ${BINARY_DIR}
--base-directory ${SOURCE_DIR}
--output-file ${TEMP_FILE}
COMMAND ${CPICKER_COVERAGE_MAPPER_COMMAND}
--input ${TEMP_FILE}
--output ${OUTPUT_FILE}
--mapping-path ${CPICKER_MAPPING_PATH}
)
add_coverage_dependency(
TARGET coverage
DEPENDS ${OUTPUT_FILE}
)
endfunction()
#[===[.rst:
.. cmake:command:: coverage_filter
.. code-block:: cmake
coverage_filter(
INPUT_FILE input_file
OUTPUT_FILE output_file
INCLUDE_DIRECTORY include_directory
)
The function filters the coverage data by including only the coverage of the
files of ``INCLUDE_DIRECTORY`` or its subdirectories. It adds the filtered
output file to the ``coverage`` target's dependency list.
Inputs:
``INPUT_FILE``
Input lcov coverage info file
``OUTPUT_FILE``
Output lcov coverage info file
``INCLUDE_DIRECTORY``
Root directory of included files
#]===]
function(coverage_filter)
set(_OPTIONS_ARGS)
set(_ONE_VALUE_ARGS INPUT_FILE OUTPUT_FILE INCLUDE_DIRECTORY)
set(_MULTI_VALUE_ARGS)
cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
set(INPUT_FILE ${_MY_PARAMS_INPUT_FILE})
set(OUTPUT_FILE ${_MY_PARAMS_OUTPUT_FILE})
set(INCLUDE_DIRECTORY ${_MY_PARAMS_INCLUDE_DIRECTORY})
# The pattern must be an absolute path ending with an asterisk
get_filename_component(INCLUDE_DIRECTORY_ABSPATH "${INCLUDE_DIRECTORY}" ABSOLUTE)
set(INCLUDE_DIRECTORY_ABSPATH "${INCLUDE_DIRECTORY_ABSPATH}/*")
add_custom_command(
OUTPUT ${OUTPUT_FILE}
COMMAND ${LCOV_COMMAND}
--extract ${INPUT_FILE} \"${INCLUDE_DIRECTORY_ABSPATH}\"
--output-file ${OUTPUT_FILE}
DEPENDS ${INPUT_FILE}
)
add_coverage_dependency(
TARGET coverage
DEPENDS ${OUTPUT_FILE}
)
endfunction()
#[===[.rst:
.. cmake:command:: coverage_generate_report
.. code-block:: cmake
coverage_generate_report(
INPUT_FILE input_file
OUTPUT_DIRECTORY output_directory
)
The function generates a HTML coverage report from the lcov info file into
the ``OUTPUT_DIRECTORY``. It adds the output directory to the
``coverage_report`` target's dependency list.
Inputs:
``INPUT_FILE``
Input lcov coverage info file
``OUTPUT_DIRECTORY``
Output directory of the coverage report where the ``index.html`` is the
root file of the report.
#]===]
function(coverage_generate_report)
set(_OPTIONS_ARGS)
set(_ONE_VALUE_ARGS INPUT_FILE OUTPUT_DIRECTORY)
set(_MULTI_VALUE_ARGS)
cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
set(INPUT_FILE ${_MY_PARAMS_INPUT_FILE})
set(OUTPUT_DIRECTORY ${_MY_PARAMS_OUTPUT_DIRECTORY})
add_custom_command(
OUTPUT ${OUTPUT_DIRECTORY}
COMMAND genhtml ${INPUT_FILE}
--show-details
--output-directory ${OUTPUT_DIRECTORY}
DEPENDS ${INPUT_FILE}
)
add_coverage_dependency(
TARGET coverage_report
DEPENDS ${OUTPUT_DIRECTORY}
)
endfunction()