├── src ├── libfoo │ ├── foo.h │ ├── CMakeLists.txt │ ├── bar.c │ └── foo.c ├── fortran │ ├── hello.f │ └── CMakeLists.txt ├── libheader │ ├── CMakeLists.txt │ └── header.h ├── CMakeLists.txt └── bar │ ├── bar.c │ └── CMakeLists.txt ├── CMakeLists.txt ├── cmake ├── llvm-cov-wrapper ├── FindGcov.cmake ├── Findcodecov.cmake └── FindLcov.cmake ├── .travis.yml ├── LICENSE └── README.md /src/libfoo/foo.h: -------------------------------------------------------------------------------- 1 | /* This file is part of CMake-codecov. 2 | * 3 | * SPDX-FileCopyrightText: RWTH Aachen University, Federal Republic of Germany 4 | * SPDX-FileContributor: Alexander Haase, alexander.haase@rwth-aachen.de 5 | * 6 | * SPDX-License-Identifier: BSD-3-Clause 7 | */ 8 | 9 | 10 | int foo(); 11 | int bar(); 12 | -------------------------------------------------------------------------------- /src/libfoo/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of CMake-codecov. 2 | # 3 | # SPDX-FileCopyrightText: RWTH Aachen University, Federal Republic of Germany 4 | # SPDX-FileContributor: Alexander Haase, alexander.haase@rwth-aachen.de 5 | # 6 | # SPDX-License-Identifier: BSD-3-Clause 7 | 8 | 9 | add_library(foo SHARED foo.c bar.c) 10 | add_coverage(foo) 11 | -------------------------------------------------------------------------------- /src/fortran/hello.f: -------------------------------------------------------------------------------- 1 | C This file is part of CMake-codecov. 2 | C SPDX-FileCopyrightText: RWTH Aachen University, Federal Republic of Germany 3 | C SPDX-FileContributor: Alexander Haase, alexander.haase@rwth-aachen.de 4 | C 5 | C SPDX-License-Identifier: BSD-3-Clause 6 | 7 | program hello 8 | print *, "Hello World!" 9 | end program hello 10 | -------------------------------------------------------------------------------- /src/libfoo/bar.c: -------------------------------------------------------------------------------- 1 | /* This file is part of CMake-codecov. 2 | * 3 | * SPDX-FileCopyrightText: RWTH Aachen University, Federal Republic of Germany 4 | * SPDX-FileContributor: Alexander Haase, alexander.haase@rwth-aachen.de 5 | * 6 | * SPDX-License-Identifier: BSD-3-Clause 7 | */ 8 | 9 | 10 | #include "foo.h" 11 | 12 | int 13 | bar () 14 | { 15 | return 42; 16 | } 17 | -------------------------------------------------------------------------------- /src/fortran/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of CMake-codecov. 2 | # 3 | # SPDX-FileCopyrightText: RWTH Aachen University, Federal Republic of Germany 4 | # SPDX-FileContributor: Alexander Haase, alexander.haase@rwth-aachen.de 5 | # 6 | # SPDX-License-Identifier: BSD-3-Clause 7 | 8 | add_executable(hello_fortran hello.f) 9 | add_coverage(hello_fortran) 10 | add_test(test_fortran hello_fortran) 11 | -------------------------------------------------------------------------------- /src/libheader/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of CMake-codecov. 2 | # 3 | # SPDX-FileCopyrightText: RWTH Aachen University, Federal Republic of Germany 4 | # SPDX-FileContributor: Alexander Haase, alexander.haase@rwth-aachen.de 5 | # 6 | # SPDX-License-Identifier: BSD-3-Clause 7 | 8 | add_library(header INTERFACE) 9 | target_include_directories(header INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) 10 | -------------------------------------------------------------------------------- /src/libheader/header.h: -------------------------------------------------------------------------------- 1 | /* This file is part of CMake-codecov. 2 | * 3 | * SPDX-FileCopyrightText: RWTH Aachen University, Federal Republic of Germany 4 | * SPDX-FileContributor: Alexander Haase, alexander.haase@rwth-aachen.de 5 | * 6 | * SPDX-License-Identifier: BSD-3-Clause 7 | */ 8 | 9 | 10 | #include 11 | 12 | void 13 | header_func () 14 | { 15 | printf("Hello World\n"); 16 | } 17 | -------------------------------------------------------------------------------- /src/libfoo/foo.c: -------------------------------------------------------------------------------- 1 | /* This file is part of CMake-codecov. 2 | * 3 | * SPDX-FileCopyrightText: RWTH Aachen University, Federal Republic of Germany 4 | * SPDX-FileContributor: Alexander Haase, alexander.haase@rwth-aachen.de 5 | * 6 | * SPDX-License-Identifier: BSD-3-Clause 7 | */ 8 | 9 | 10 | #include "foo.h" 11 | 12 | int 13 | foo () 14 | { 15 | int i = 0; 16 | while (i < 10) 17 | i++; 18 | 19 | return i; 20 | } 21 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of CMake-codecov. 2 | # 3 | # SPDX-FileCopyrightText: RWTH Aachen University, Federal Republic of Germany 4 | # SPDX-FileContributor: Alexander Haase, alexander.haase@rwth-aachen.de 5 | # 6 | # SPDX-License-Identifier: BSD-3-Clause 7 | 8 | 9 | # recurse into subdirectories 10 | add_subdirectory(libfoo) 11 | add_subdirectory(bar) 12 | add_subdirectory(libheader) 13 | 14 | if(CMAKE_Fortran_COMPILER) 15 | add_subdirectory(fortran) 16 | endif() 17 | -------------------------------------------------------------------------------- /src/bar/bar.c: -------------------------------------------------------------------------------- 1 | /* This file is part of CMake-codecov. 2 | * 3 | * SPDX-FileCopyrightText: RWTH Aachen University, Federal Republic of Germany 4 | * SPDX-FileContributor: Alexander Haase, alexander.haase@rwth-aachen.de 5 | * 6 | * SPDX-License-Identifier: BSD-3-Clause 7 | */ 8 | 9 | #include 10 | 11 | #include "foo.h" 12 | #include 13 | 14 | int 15 | main (int argc, char** argv) 16 | { 17 | if (*argv[1] == '1') 18 | printf("%d\n", foo()); 19 | else if (*argv[1] == '2') 20 | printf("%d\n", bar()); 21 | else if (*argv[1] == '3') 22 | header_func(); 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /src/bar/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of CMake-codecov. 2 | # 3 | # SPDX-FileCopyrightText: RWTH Aachen University, Federal Republic of Germany 4 | # SPDX-FileContributor: Alexander Haase, alexander.haase@rwth-aachen.de 5 | # 6 | # SPDX-License-Identifier: BSD-3-Clause 7 | 8 | 9 | include_directories(../libfoo ../libheader) 10 | add_executable(bar bar.c) 11 | target_link_libraries(bar foo) 12 | target_link_libraries(bar header) 13 | 14 | add_test(test1 ${CMAKE_CURRENT_BINARY_DIR}/bar 1) 15 | add_test(test2 ${CMAKE_CURRENT_BINARY_DIR}/bar 2) 16 | add_test(test3 ${CMAKE_CURRENT_BINARY_DIR}/bar 3) 17 | 18 | find_package(codecov) 19 | add_coverage(bar) 20 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of CMake-codecov. 2 | # 3 | # SPDX-FileCopyrightText: RWTH Aachen University, Federal Republic of Germany 4 | # SPDX-FileContributor: Alexander Haase, alexander.haase@rwth-aachen.de 5 | # 6 | # SPDX-License-Identifier: BSD-3-Clause 7 | 8 | 9 | # minimum required cmake version 10 | cmake_minimum_required(VERSION 3.10) 11 | 12 | # 13 | # project information 14 | # 15 | project("test" LANGUAGES C) 16 | 17 | # 18 | # check for Fortran support 19 | # 20 | include(CheckLanguage) 21 | check_language(Fortran) 22 | if(CMAKE_Fortran_COMPILER) 23 | enable_language(Fortran) 24 | endif() 25 | 26 | 27 | # 28 | # cmake configuration 29 | # 30 | set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) 31 | 32 | enable_testing() 33 | 34 | 35 | # enable code coverage 36 | find_package(codecov) 37 | 38 | 39 | # 40 | # recurse into subdirectories 41 | # 42 | add_subdirectory(src) 43 | 44 | 45 | # evaluate coverage 46 | coverage_evaluate() 47 | -------------------------------------------------------------------------------- /cmake/llvm-cov-wrapper: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This file is part of CMake-codecov. 4 | # 5 | # SPDX-FileCopyrightText: RWTH Aachen University, Federal Republic of Germany 6 | # SPDX-FileContributor: Alexander Haase, alexander.haase@rwth-aachen.de 7 | # 8 | # SPDX-License-Identifier: BSD-3-Clause 9 | 10 | if [ -z "$LLVM_COV_BIN" ] 11 | then 12 | echo "LLVM_COV_BIN not set!" >& 2 13 | exit 1 14 | fi 15 | 16 | 17 | # Get LLVM version to find out. 18 | LLVM_VERSION=$($LLVM_COV_BIN -version | grep -i "LLVM version" \ 19 | | sed "s/^\([A-Za-z ]*\)\([0-9]*\).\([0-9]*\).*$/\2.\3/g") 20 | 21 | if [ "$1" = "-v" ] 22 | then 23 | echo "llvm-cov-wrapper $LLVM_VERSION" 24 | exit 0 25 | fi 26 | 27 | 28 | if [ -n "$LLVM_VERSION" ] 29 | then 30 | MAJOR=$(echo $LLVM_VERSION | cut -d'.' -f1) 31 | MINOR=$(echo $LLVM_VERSION | cut -d'.' -f2) 32 | 33 | if [ $MAJOR -eq 3 ] && [ $MINOR -le 4 ] 34 | then 35 | if [ -f "$1" ] 36 | then 37 | filename=$(basename "$1") 38 | extension="${filename##*.}" 39 | 40 | case "$extension" in 41 | "gcno") exec $LLVM_COV_BIN --gcno="$1" ;; 42 | "gcda") exec $LLVM_COV_BIN --gcda="$1" ;; 43 | esac 44 | fi 45 | fi 46 | 47 | if [ $MAJOR -eq 3 ] && [ $MINOR -le 5 ] 48 | then 49 | exec $LLVM_COV_BIN $@ 50 | fi 51 | fi 52 | 53 | exec $LLVM_COV_BIN gcov $@ 54 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # This file is part of CMake-codecov. 2 | # 3 | # SPDX-FileCopyrightText: RWTH Aachen University, Federal Republic of Germany 4 | # SPDX-FileContributor: Alexander Haase, alexander.haase@rwth-aachen.de 5 | # 6 | # SPDX-License-Identifier: BSD-3-Clause 7 | 8 | language: c 9 | 10 | matrix: 11 | include: 12 | - os: linux 13 | dist: xenial 14 | compiler: gcc 15 | - os: linux 16 | dist: xenial 17 | compiler: clang 18 | 19 | - os: linux 20 | dist: bionic 21 | compiler: gcc 22 | - os: linux 23 | dist: bionic 24 | compiler: clang 25 | 26 | - os: osx 27 | compiler: clang 28 | 29 | 30 | addons: 31 | apt: 32 | packages: 33 | - cmake 34 | - lcov 35 | - gfortran 36 | 37 | 38 | before_install: 39 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; 40 | then 41 | brew update --quiet >& /dev/null; 42 | fi 43 | # The following solution was inspired by http://stackoverflow.com/a/20802381 44 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; 45 | then 46 | for pkg in gcc cmake lcov; do 47 | if brew list -1 | grep -q "^${pkg}\$"; then 48 | brew outdated $pkg || brew upgrade $pkg; 49 | else 50 | brew install $pkg; 51 | fi 52 | done 53 | fi 54 | 55 | script: 56 | - mkdir build && cd build 57 | - cmake .. -DENABLE_COVERAGE=On && make && make test gcov lcov 58 | 59 | after_success: 60 | - bash <(curl -s https://codecov.io/bash) -X gcov 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright 2015-2025 RWTH Aachen University, Federal Republic of Germany 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, this 13 | list of conditions and the following disclaimer in the documentation and/or 14 | other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its contributors may 17 | be used to endorse or promote products derived from this software without 18 | specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 21 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 27 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /cmake/FindGcov.cmake: -------------------------------------------------------------------------------- 1 | # This file is part of CMake-codecov. 2 | # 3 | # SPDX-FileCopyrightText: RWTH Aachen University, Federal Republic of Germany 4 | # SPDX-FileContributor: Alexander Haase, alexander.haase@rwth-aachen.de 5 | # 6 | # SPDX-License-Identifier: BSD-3-Clause 7 | 8 | # include required Modules 9 | include(FindPackageHandleStandardArgs) 10 | 11 | 12 | # Search for gcov binary. 13 | set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET}) 14 | set(CMAKE_REQUIRED_QUIET ${codecov_FIND_QUIETLY}) 15 | 16 | get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) 17 | foreach (LANG ${ENABLED_LANGUAGES}) 18 | # Gcov evaluation is dependent on the used compiler. Check gcov support for 19 | # each compiler that is used. If gcov binary was already found for this 20 | # compiler, do not try to find it again. 21 | if (NOT GCOV_${CMAKE_${LANG}_COMPILER_ID}_BIN) 22 | get_filename_component(COMPILER_PATH "${CMAKE_${LANG}_COMPILER}" PATH) 23 | 24 | if ("${CMAKE_${LANG}_COMPILER_ID}" STREQUAL "GNU") 25 | # Some distributions like OSX (homebrew) ship gcov with the compiler 26 | # version appended as gcov-x. To find this binary we'll build the 27 | # suggested binary name with the compiler version. 28 | string(REGEX MATCH "^[0-9]+" GCC_VERSION 29 | "${CMAKE_${LANG}_COMPILER_VERSION}") 30 | 31 | find_program(GCOV_BIN NAMES gcov-${GCC_VERSION} gcov 32 | HINTS ${COMPILER_PATH}) 33 | 34 | elseif ("${CMAKE_${LANG}_COMPILER_ID}" MATCHES "^(Apple)?Clang$") 35 | # Some distributions like Debian ship llvm-cov with the compiler 36 | # version appended as llvm-cov-x.y or just llvm-cov-x. To find this binary we'll build 37 | # the suggested binary name with the compiler version. 38 | string(REGEX MATCH "^[0-9]+\.[0-9]+" LLVM_FULL_VERSION 39 | "${CMAKE_${LANG}_COMPILER_VERSION}") 40 | string(REGEX MATCH "^[0-9]+" LLVM_MAJOR_VERSION 41 | "${CMAKE_${LANG}_COMPILER_VERSION}") 42 | 43 | # llvm-cov prior version 3.5 seems to be not working with coverage 44 | # evaluation tools, but these versions are compatible with the gcc 45 | # gcov tool. 46 | if(LLVM_FULL_VERSION VERSION_GREATER 3.4) 47 | find_program(LLVM_COV_BIN NAMES "llvm-cov-${LLVM_FULL_VERSION}" "llvm-cov-${LLVM_MAJOR_VERSION}" 48 | "llvm-cov" HINTS ${COMPILER_PATH}) 49 | mark_as_advanced(LLVM_COV_BIN) 50 | 51 | if (LLVM_COV_BIN) 52 | find_program(LLVM_COV_WRAPPER "llvm-cov-wrapper" PATHS 53 | ${CMAKE_MODULE_PATH}) 54 | if (LLVM_COV_WRAPPER) 55 | set(GCOV_BIN "${LLVM_COV_WRAPPER}" CACHE FILEPATH "") 56 | 57 | # set additional parameters 58 | set(GCOV_${CMAKE_${LANG}_COMPILER_ID}_ENV 59 | "LLVM_COV_BIN=${LLVM_COV_BIN}" CACHE STRING 60 | "Environment variables for llvm-cov-wrapper.") 61 | mark_as_advanced(GCOV_${CMAKE_${LANG}_COMPILER_ID}_ENV) 62 | endif () 63 | endif () 64 | endif () 65 | 66 | if (NOT GCOV_BIN) 67 | # Fall back to gcov binary if llvm-cov was not found or is 68 | # incompatible. This is the default on OSX, but may crash on 69 | # recent Linux versions. 70 | find_program(GCOV_BIN gcov HINTS ${COMPILER_PATH}) 71 | endif () 72 | endif () 73 | 74 | 75 | if (GCOV_BIN) 76 | set(GCOV_${CMAKE_${LANG}_COMPILER_ID}_BIN "${GCOV_BIN}" CACHE STRING 77 | "${LANG} gcov binary.") 78 | 79 | if (NOT CMAKE_REQUIRED_QUIET) 80 | message("-- Found gcov evaluation for " 81 | "${CMAKE_${LANG}_COMPILER_ID}: ${GCOV_BIN}") 82 | endif() 83 | 84 | unset(GCOV_BIN CACHE) 85 | endif () 86 | endif () 87 | endforeach () 88 | 89 | 90 | 91 | 92 | # Add a new global target for all gcov targets. This target could be used to 93 | # generate the gcov files for the whole project instead of calling -gcov 94 | # for each target. 95 | if (NOT TARGET gcov) 96 | add_custom_target(gcov) 97 | endif (NOT TARGET gcov) 98 | 99 | 100 | 101 | # This function will add gcov evaluation for target . Only sources of 102 | # this target will be evaluated and no dependencies will be added. It will call 103 | # Gcov on any source file of once and store the gcov file in the same 104 | # directory. 105 | function (add_gcov_target TNAME) 106 | get_target_property(TBIN_DIR ${TNAME} BINARY_DIR) 107 | set(TDIR ${TBIN_DIR}/CMakeFiles/${TNAME}.dir) 108 | 109 | # We don't have to check, if the target has support for coverage, thus this 110 | # will be checked by add_coverage_target in Findcoverage.cmake. Instead we 111 | # have to determine which gcov binary to use. 112 | get_target_property(TSOURCES ${TNAME} SOURCES) 113 | set(SOURCES "") 114 | set(TCOMPILER "") 115 | foreach (FILE ${TSOURCES}) 116 | codecov_path_of_source(${FILE} FILE) 117 | if (NOT "${FILE}" STREQUAL "") 118 | codecov_lang_of_source(${FILE} LANG) 119 | if (NOT "${LANG}" STREQUAL "") 120 | list(APPEND SOURCES "${FILE}") 121 | set(TCOMPILER ${CMAKE_${LANG}_COMPILER_ID}) 122 | endif () 123 | endif () 124 | endforeach () 125 | 126 | # If no gcov binary was found, coverage data can't be evaluated. 127 | if (NOT GCOV_${TCOMPILER}_BIN) 128 | message(WARNING "No coverage evaluation binary found for ${TCOMPILER}.") 129 | return() 130 | endif () 131 | 132 | set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}") 133 | set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}") 134 | 135 | 136 | set(BUFFER "") 137 | set(NULL_DEVICE "/dev/null") 138 | if(WIN32) 139 | set(NULL_DEVICE "NUL") 140 | endif() 141 | foreach(FILE ${SOURCES}) 142 | get_filename_component(FILE_PATH "${TDIR}/${FILE}" PATH) 143 | 144 | # call gcov 145 | add_custom_command(OUTPUT ${TDIR}/${FILE}.gcov 146 | COMMAND ${GCOV_ENV} ${GCOV_BIN} -p ${TDIR}/${FILE}.gcno > ${NULL_DEVICE} 147 | DEPENDS ${TNAME} ${TDIR}/${FILE}.gcno 148 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 149 | ) 150 | 151 | list(APPEND BUFFER ${TDIR}/${FILE}.gcov) 152 | endforeach() 153 | 154 | 155 | # add target for gcov evaluation of 156 | add_custom_target(${TNAME}-gcov DEPENDS ${BUFFER}) 157 | 158 | # add evaluation target to the global gcov target. 159 | add_dependencies(gcov ${TNAME}-gcov) 160 | endfunction (add_gcov_target) 161 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # CMake-codecov 9 | 10 | [![Travis](https://img.shields.io/travis/RWTH-HPC/CMake-codecov/master.svg?style=flat-square)](https://travis-ci.org/RWTH-HPC/CMake-codecov) 11 | [![Codecov](https://img.shields.io/codecov/c/github/RWTH-HPC/CMake-codecov.svg?style=flat-square)](https://codecov.io/github/RWTH-HPC/CMake-codecov?branch=master) 12 | [![](https://img.shields.io/github/issues-raw/RWTH-HPC/CMake-codecov.svg?style=flat-square)](https://github.com/RWTH-HPC/CMake-codecov/issues) 13 | [![](http://img.shields.io/badge/license-3--clause_BSD-blue.svg?style=flat-square)](LICENSE) 14 | 15 | CMake module to enable code coverage easily and generate coverage reports with CMake targets. 16 | 17 | 18 | 19 | ## Include into your project 20 | 21 | To use [Findcodecov.cmake](cmake/Findcodecov.cmake), simply add this repository as git submodule into your own repository 22 | ```Shell 23 | mkdir externals 24 | git submodule add git://github.com/RWTH-HPC/CMake-codecov.git externals/CMake-codecov 25 | ``` 26 | and adding ```externals/cmake-codecov/cmake``` to your ```CMAKE_MODULE_PATH``` 27 | ```CMake 28 | set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/externals/CMake-codecov/cmake" ${CMAKE_MODULE_PATH}) 29 | ``` 30 | 31 | If you don't use git or dislike submodules you can copy the [Findcodecov.cmake](cmake/Findcodecov.cmake), [FindGcov.cmake](cmake/FindGcov.cmake) and [FindLcov.cmake](cmake/FindLcov.cmake) files into your repository. *Be careful when there are version updates of this repository!* 32 | 33 | Next you have to include the codecov package. This can be done in your root CMake file, or in the file were your targets will be defined: 34 | ```CMake 35 | # enable code coverage 36 | find_package(codecov) 37 | ``` 38 | 39 | For coverage evaluation you have to add ```coverage_evaluate()``` after all other targets have been defined. A good place for this is your root CMakeLists.txt in the last lines after you included your sub-directories and added targets. 40 | 41 | 42 | ## Usage 43 | 44 | To enable coverage support in general, you have to enable ```ENABLE_COVERAGE``` option in your CMake configuration. You can do this by passing ```-DENABLE_COVERAGE=On``` on your command line or with your graphical interface. 45 | 46 | If coverage is supported by your compiler, the specified targets will be build with coverage support. If your compiler has no coverage capabilities (I asume intel compiler doesn't) you'll get a warning but CMake will continue processing and coverage will simply just be ignored. 47 | 48 | #### Compiler issues 49 | 50 | Different compilers may be using different implementations for code coverage. If you'll try to cover targets with C and Fortran code but don't use gcc & gfortran but clang & gfortran, this will cause linking problems. To avoid this, such problems will be detected and coverage will be disabled for such targets. 51 | 52 | Even C only targets may cause problems, if e.g. clang compiles the coverage for an older gcov version than the one is shipped with your distribution. [FindGcov.cmake](cmake/FindGcov.cmake) tries to find a compatible coverage evaluation tool to avoid this issue, but may fail. In this case you should check coverage with a different compiler or install a compatible coverage tool. 53 | 54 | #### File extensions 55 | 56 | Starting with CMake `3.14`, this module will use the last file extension only (i.e. `.c` for `a.b.c`). Prior versions will use the full file extension starting with the first dot in the file name. 57 | 58 | ### Build targets with coverage support 59 | 60 | To enable coverage support you have two options: You can mark targets explictly for coverage by adding your target with ```add_coverage()```. This call must be done in the same directory as your ```add_executable()```or ```add_library()``` call: 61 | ```CMake 62 | add_executable(some_exe foo.c bar.c) 63 | add_coverage(some_exe) 64 | 65 | add_library(some_lib foo.c bar.c) 66 | add_coverage(some_lib) 67 | ``` 68 | 69 | 70 | ### Executing your program 71 | 72 | To be able to evaluate your coverage data, you have to run your application first. Some projects include CMake tests - it might me a good idea to execute them now by ```make test```, but you can run your application however you want (e.g. by running ```./a.out```). 73 | 74 | 75 | ### Evaluating coverage data 76 | 77 | #### Gcov 78 | 79 | Gcov is a console program to evaluate the generated coverage data. You can evaluate the data by calling the following targets: 80 | 81 | | target | description | 82 | |---------|-------------| 83 | |```-gcov```|Evaluate coverage data for target ``````.| 84 | |```gcov```|Evaluate the coverage data of all your targets. **Warning:** You have to run programs generated by every target before you can call this target without any error. Otherwise you might get errors, if ```*.gcda``` files will not be found.| 85 | 86 | The files generated by Gcov reside in the binary directory of the target `````` you're evaluating the coverage data for (e.g. for target ```bar``` in this repository it'll be ```${CMAKE_BINARY_DIR}/src/bar/CMakeFiles/bar.dir/```). 87 | 88 | 89 | #### Lcov 90 | 91 | Lcov is a console program to evaluate the generate coverage data, but instead of writing the results into a copy of the original source file (like Gcov does) a HTML report will be generated, which is much easier to read than several gcov files. 92 | 93 | | target | description | 94 | |---------|-------------| 95 | |```-geninfo```|Evaluate coverage data for target ``````.| 96 | |```-genhtml```|Generate a report for a specific target *(and only this one, even if it has dependencies!)*. This target will call ```-geninfo``` before. Reports will be generated in ```${CMAKE_BINARY_DIR}/lcov/html/```.| 97 | |```lcov-geninfo```|Evaluate the coverage data of all your targets.| 98 | |```lcov-genhtml```|Generate a *single* report for all evaluated data that is available now. **Note:** You have to call ```-geninfo``` for all targets you want to have in this report before calling this target or ```lcov-geninfo```. You can use this option, if you like to have a single report for the targets ```foo``` and ```bar``` together, but without all the other targets. Reports will be generated in ```${CMAKE_BINARY_DIR}/lcov/html/selected_targets```.| 99 | |```lcov```|Generate a *single* report for all targets. This target will call ```lcov-geninfo``` before. Reports will be generated in ```${CMAKE_BINARY_DIR}/lcov/html/all_targets```.| 100 | 101 | ##### Excluding files from coverage reports 102 | If you want to exclude some files from your coverage reports by ```lcov --remove``` subcommand, you can append their path patterns to ```LCOV_REMOVE_PATTERNS``` in your CMakeLists.txt like a following example. 103 | ```CMake 104 | list(APPEND LCOV_REMOVE_PATTERNS "'${PROJECT_SOURCE_DIR}/extlib/*'") 105 | ``` 106 | Note that asterisks in patterns should not be expanded by the shell interpreter. 107 | 108 | 109 | ## Contribute 110 | 111 | Anyone is welcome to contribute. Simply fork this repository, make your changes **in an own branch** and create a pull-request for your change. Please do only one change per pull-request. 112 | 113 | You found a bug? Please fill out an issue and include any data to reproduce the bug. 114 | 115 | #### Contributors 116 | 117 | [Alexander Haase](https://github.com/alehaa) 118 | 119 | 120 | ## License 121 | 122 | CMake-codecov is released under the 3-clause BSD license. See the [LICENSE](LICENSE) file for more information. 123 | 124 | Copyright © 2015-2025 RWTH Aachen University, Federal Republic of Germany. 125 | -------------------------------------------------------------------------------- /cmake/Findcodecov.cmake: -------------------------------------------------------------------------------- 1 | # This file is part of CMake-codecov. 2 | # 3 | # SPDX-FileCopyrightText: RWTH Aachen University, Federal Republic of Germany 4 | # SPDX-FileContributor: Alexander Haase, alexander.haase@rwth-aachen.de 5 | # 6 | # SPDX-License-Identifier: BSD-3-Clause 7 | 8 | 9 | # Add an option to choose, if coverage should be enabled or not. If enabled 10 | # marked targets will be build with coverage support and appropriate targets 11 | # will be added. If disabled coverage will be ignored for *ALL* targets. 12 | option(ENABLE_COVERAGE "Enable coverage build." OFF) 13 | 14 | set(COVERAGE_FLAG_CANDIDATES 15 | # gcc and clang 16 | "-O0 -g -fprofile-arcs -ftest-coverage" 17 | 18 | # gcc and clang fallback 19 | "-O0 -g --coverage" 20 | ) 21 | 22 | 23 | # Add coverage support for target ${TNAME} and register target for coverage 24 | # evaluation. If coverage is disabled or not supported, this function will 25 | # simply do nothing. 26 | # 27 | # Note: This function is only a wrapper to define this function always, even if 28 | # coverage is not supported by the compiler or disabled. This function must 29 | # be defined here, because the module will be exited, if there is no coverage 30 | # support by the compiler or it is disabled by the user. 31 | function (add_coverage TNAME) 32 | # only add coverage for target, if coverage is support and enabled. 33 | if (ENABLE_COVERAGE) 34 | foreach (TNAME ${ARGV}) 35 | add_coverage_target(${TNAME}) 36 | endforeach () 37 | endif () 38 | endfunction (add_coverage) 39 | 40 | 41 | # Add global target to gather coverage information after all targets have been 42 | # added. Other evaluation functions could be added here, after checks for the 43 | # specific module have been passed. 44 | # 45 | # Note: This function is only a wrapper to define this function always, even if 46 | # coverage is not supported by the compiler or disabled. This function must 47 | # be defined here, because the module will be exited, if there is no coverage 48 | # support by the compiler or it is disabled by the user. 49 | function (coverage_evaluate) 50 | # add lcov evaluation 51 | if (LCOV_FOUND) 52 | lcov_capture_initial() 53 | lcov_capture() 54 | endif (LCOV_FOUND) 55 | endfunction () 56 | 57 | 58 | # Exit this module, if coverage is disabled. add_coverage is defined before this 59 | # return, so this module can be exited now safely without breaking any build- 60 | # scripts. 61 | if (NOT ENABLE_COVERAGE) 62 | return() 63 | endif () 64 | 65 | 66 | 67 | 68 | # Find the required flags foreach language. 69 | set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET}) 70 | set(CMAKE_REQUIRED_QUIET ${codecov_FIND_QUIETLY}) 71 | 72 | get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) 73 | foreach (LANG ${ENABLED_LANGUAGES}) 74 | if (NOT ${LANG} MATCHES "^(C|CXX|Fortran)$") 75 | message(STATUS "Skipping coverage for unsupported language: ${LANG}") 76 | continue() 77 | endif () 78 | 79 | # Coverage flags are not dependent on language, but the used compiler. So 80 | # instead of searching flags foreach language, search flags foreach compiler 81 | # used. 82 | set(COMPILER ${CMAKE_${LANG}_COMPILER_ID}) 83 | if (NOT COVERAGE_${COMPILER}_FLAGS) 84 | foreach (FLAG ${COVERAGE_FLAG_CANDIDATES}) 85 | if(NOT CMAKE_REQUIRED_QUIET) 86 | message(STATUS "Try ${COMPILER} code coverage flag = [${FLAG}]") 87 | endif() 88 | 89 | set(CMAKE_REQUIRED_FLAGS "${FLAG}") 90 | unset(COVERAGE_FLAG_DETECTED CACHE) 91 | 92 | if (${LANG} STREQUAL "C") 93 | include(CheckCCompilerFlag) 94 | check_c_compiler_flag("${FLAG}" COVERAGE_FLAG_DETECTED) 95 | 96 | elseif (${LANG} STREQUAL "CXX") 97 | include(CheckCXXCompilerFlag) 98 | check_cxx_compiler_flag("${FLAG}" COVERAGE_FLAG_DETECTED) 99 | 100 | elseif (${LANG} STREQUAL "Fortran") 101 | # CheckFortranCompilerFlag was introduced in CMake 3.x. To be 102 | # compatible with older Cmake versions, we will check if this 103 | # module is present before we use it. Otherwise we will define 104 | # Fortran coverage support as not available. 105 | include(CheckFortranCompilerFlag OPTIONAL 106 | RESULT_VARIABLE INCLUDED) 107 | if (INCLUDED) 108 | check_fortran_compiler_flag("${FLAG}" 109 | COVERAGE_FLAG_DETECTED) 110 | elseif (NOT CMAKE_REQUIRED_QUIET) 111 | message("-- Performing Test COVERAGE_FLAG_DETECTED") 112 | message("-- Performing Test COVERAGE_FLAG_DETECTED - Failed" 113 | " (Check not supported)") 114 | endif () 115 | endif() 116 | 117 | unset(CMAKE_REQUIRED_FLAGS) 118 | 119 | if (COVERAGE_FLAG_DETECTED) 120 | set(COVERAGE_${COMPILER}_FLAGS "${FLAG}" 121 | CACHE STRING "${COMPILER} flags for code coverage.") 122 | mark_as_advanced(COVERAGE_${COMPILER}_FLAGS) 123 | break() 124 | elseif (NOT CMAKE_REQUIRED_QUIET) 125 | message(WARNING "Code coverage is not available for ${COMPILER}" 126 | " compiler. Targets using this compiler will be " 127 | "compiled without it.") 128 | endif () 129 | endforeach () 130 | endif () 131 | endforeach () 132 | 133 | set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_SAVE}) 134 | 135 | 136 | 137 | 138 | # Helper function to get the language of a source file. 139 | function (codecov_lang_of_source FILE RETURN_VAR) 140 | # Usually, only the last extension of the file should be checked, to avoid 141 | # template files (i.e. *.t.cpp) are checked with the full file extension. 142 | # However, this feature requires CMake 3.14 or later. 143 | set(EXT_COMP "LAST_EXT") 144 | if(${CMAKE_VERSION} VERSION_LESS "3.14.0") 145 | set(EXT_COMP "EXT") 146 | endif() 147 | 148 | get_filename_component(FILE_EXT "${FILE}" ${EXT_COMP}) 149 | string(TOLOWER "${FILE_EXT}" FILE_EXT) 150 | string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT) 151 | 152 | get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) 153 | foreach (LANG ${ENABLED_LANGUAGES}) 154 | list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP) 155 | if (NOT ${TEMP} EQUAL -1) 156 | set(${RETURN_VAR} "${LANG}" PARENT_SCOPE) 157 | return() 158 | endif () 159 | endforeach() 160 | 161 | set(${RETURN_VAR} "" PARENT_SCOPE) 162 | endfunction () 163 | 164 | 165 | # Helper function to get the relative path of the source file destination path. 166 | # This path is needed by FindGcov and FindLcov cmake files to locate the 167 | # captured data. 168 | function (codecov_path_of_source FILE RETURN_VAR) 169 | string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _source ${FILE}) 170 | 171 | # If expression was found, SOURCEFILE is a generator-expression for an 172 | # object library. Currently we found no way to call this function automatic 173 | # for the referenced target, so it must be called in the directoryso of the 174 | # object library definition. 175 | if (NOT "${_source}" STREQUAL "") 176 | set(${RETURN_VAR} "" PARENT_SCOPE) 177 | return() 178 | endif () 179 | 180 | 181 | string(REPLACE "${CMAKE_CURRENT_BINARY_DIR}/" "" FILE "${FILE}") 182 | if(IS_ABSOLUTE ${FILE}) 183 | file(RELATIVE_PATH FILE ${CMAKE_CURRENT_SOURCE_DIR} ${FILE}) 184 | endif() 185 | 186 | # get the right path for file 187 | string(REPLACE ".." "__" PATH "${FILE}") 188 | 189 | set(${RETURN_VAR} "${PATH}" PARENT_SCOPE) 190 | endfunction() 191 | 192 | 193 | 194 | 195 | # Add coverage support for target ${TNAME} and register target for coverage 196 | # evaluation. 197 | function(add_coverage_target TNAME) 198 | # Check if all sources for target use the same compiler. If a target uses 199 | # e.g. C and Fortran mixed and uses different compilers (e.g. clang and 200 | # gfortran) this can trigger huge problems, because different compilers may 201 | # use different implementations for code coverage. 202 | get_target_property(TSOURCES ${TNAME} SOURCES) 203 | set(TARGET_COMPILER "") 204 | set(ADDITIONAL_FILES "") 205 | foreach (FILE ${TSOURCES}) 206 | # If expression was found, FILE is a generator-expression for an object 207 | # library. Object libraries will be ignored. 208 | string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _file ${FILE}) 209 | if ("${_file}" STREQUAL "") 210 | codecov_lang_of_source(${FILE} LANG) 211 | if (LANG) 212 | list(APPEND TARGET_COMPILER ${CMAKE_${LANG}_COMPILER_ID}) 213 | 214 | list(APPEND ADDITIONAL_FILES "${FILE}.gcno") 215 | list(APPEND ADDITIONAL_FILES "${FILE}.gcda") 216 | endif () 217 | endif () 218 | endforeach () 219 | 220 | list(REMOVE_DUPLICATES TARGET_COMPILER) 221 | list(LENGTH TARGET_COMPILER NUM_COMPILERS) 222 | 223 | if (NUM_COMPILERS GREATER 1) 224 | message(WARNING "Can't use code coverage for target ${TNAME}, because " 225 | "it will be compiled by incompatible compilers. Target will be " 226 | "compiled without code coverage.") 227 | return() 228 | 229 | elseif (NUM_COMPILERS EQUAL 0) 230 | message(WARNING "Can't use code coverage for target ${TNAME}, because " 231 | "it uses an unknown compiler. Target will be compiled without " 232 | "code coverage.") 233 | return() 234 | 235 | elseif (NOT DEFINED "COVERAGE_${TARGET_COMPILER}_FLAGS") 236 | # A warning has been printed before, so just return if flags for this 237 | # compiler aren't available. 238 | return() 239 | endif() 240 | 241 | 242 | # enable coverage for target 243 | set_property(TARGET ${TNAME} APPEND_STRING 244 | PROPERTY COMPILE_FLAGS " ${COVERAGE_${TARGET_COMPILER}_FLAGS}") 245 | set_property(TARGET ${TNAME} APPEND_STRING 246 | PROPERTY LINK_FLAGS " ${COVERAGE_${TARGET_COMPILER}_FLAGS}") 247 | 248 | 249 | # Add gcov files generated by compiler to clean target. 250 | set(CLEAN_FILES "") 251 | foreach (FILE ${ADDITIONAL_FILES}) 252 | codecov_path_of_source(${FILE} FILE) 253 | list(APPEND CLEAN_FILES "CMakeFiles/${TNAME}.dir/${FILE}") 254 | endforeach() 255 | 256 | if(${CMAKE_VERSION} VERSION_LESS "3.15.0") 257 | set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES 258 | "${CLEAN_FILES}") 259 | else() 260 | set_directory_properties(PROPERTIES ADDITIONAL_CLEAN_FILES 261 | "${CLEAN_FILES}") 262 | endif() 263 | 264 | 265 | add_gcov_target(${TNAME}) 266 | add_lcov_target(${TNAME}) 267 | endfunction(add_coverage_target) 268 | 269 | 270 | 271 | 272 | # Include modules for parsing the collected data and output it in a readable 273 | # format (like gcov and lcov). 274 | find_package(Gcov) 275 | find_package(Lcov) 276 | -------------------------------------------------------------------------------- /cmake/FindLcov.cmake: -------------------------------------------------------------------------------- 1 | # This file is part of CMake-codecov. 2 | # 3 | # SPDX-FileCopyrightText: RWTH Aachen University, Federal Republic of Germany 4 | # SPDX-FileContributor: Alexander Haase, alexander.haase@rwth-aachen.de 5 | # 6 | # SPDX-License-Identifier: BSD-3-Clause 7 | 8 | 9 | # configuration 10 | set(LCOV_DATA_PATH "${CMAKE_BINARY_DIR}/lcov/data") 11 | set(LCOV_DATA_PATH_INIT "${LCOV_DATA_PATH}/init") 12 | set(LCOV_DATA_PATH_CAPTURE "${LCOV_DATA_PATH}/capture") 13 | set(LCOV_HTML_PATH "${CMAKE_BINARY_DIR}/lcov/html") 14 | 15 | set(GENINFO_EXTRA_FLAGS "" CACHE STRING "Additional flags to pass to geninfo.") 16 | string(REPLACE " " ";" GENINFO_EXTRA_FLAGS "${GENINFO_EXTRA_FLAGS}") 17 | set(LCOV_EXTRA_FLAGS "" CACHE STRING "Additional flags to pass to lcov.") 18 | string(REPLACE " " ";" LCOV_EXTRA_FLAGS "${LCOV_EXTRA_FLAGS}") 19 | 20 | 21 | # Search for Gcov which is used by Lcov. 22 | find_package(Gcov) 23 | 24 | 25 | 26 | 27 | # This function will add lcov evaluation for target . Only sources of 28 | # this target will be evaluated and no dependencies will be added. It will call 29 | # geninfo on any source file of once and store the info file in the same 30 | # directory. 31 | # 32 | # Note: This function is only a wrapper to define this function always, even if 33 | # coverage is not supported by the compiler or disabled. This function must 34 | # be defined here, because the module will be exited, if there is no coverage 35 | # support by the compiler or it is disabled by the user. 36 | function (add_lcov_target TNAME) 37 | if (LCOV_FOUND) 38 | # capture initial coverage data 39 | lcov_capture_initial_tgt(${TNAME}) 40 | 41 | # capture coverage data after execution 42 | lcov_capture_tgt(${TNAME}) 43 | endif () 44 | endfunction (add_lcov_target) 45 | 46 | 47 | 48 | 49 | # include required Modules 50 | include(FindPackageHandleStandardArgs) 51 | 52 | # Search for required lcov binaries. 53 | find_program(LCOV_BIN lcov) 54 | find_program(GENINFO_BIN geninfo) 55 | find_program(GENHTML_BIN genhtml) 56 | find_package_handle_standard_args(Lcov 57 | REQUIRED_VARS LCOV_BIN GENINFO_BIN GENHTML_BIN 58 | ) 59 | 60 | # enable genhtml C++ demangeling, if c++filt is found. 61 | set(GENHTML_CPPFILT_FLAG "") 62 | find_program(CPPFILT_BIN c++filt) 63 | if (NOT CPPFILT_BIN STREQUAL "") 64 | set(GENHTML_CPPFILT_FLAG "--demangle-cpp") 65 | endif (NOT CPPFILT_BIN STREQUAL "") 66 | 67 | # enable no-external flag for lcov, if available. 68 | if (GENINFO_BIN AND NOT DEFINED GENINFO_EXTERN_FLAG) 69 | set(FLAG "") 70 | execute_process(COMMAND ${GENINFO_BIN} --help OUTPUT_VARIABLE GENINFO_HELP) 71 | string(REGEX MATCH "external" GENINFO_RES "${GENINFO_HELP}") 72 | if (GENINFO_RES) 73 | set(FLAG "--no-external") 74 | endif () 75 | 76 | set(GENINFO_EXTERN_FLAG "${FLAG}" 77 | CACHE STRING "Geninfo flag to exclude system sources.") 78 | endif () 79 | 80 | # If Lcov was not found, exit module now. 81 | if (NOT LCOV_FOUND) 82 | return() 83 | endif (NOT LCOV_FOUND) 84 | 85 | 86 | 87 | 88 | # Create directories to be used. 89 | file(MAKE_DIRECTORY ${LCOV_DATA_PATH_INIT}) 90 | file(MAKE_DIRECTORY ${LCOV_DATA_PATH_CAPTURE}) 91 | 92 | set(LCOV_REMOVE_PATTERNS "") 93 | 94 | # This function will merge lcov files to a single target file. Additional lcov 95 | # flags may be set with setting LCOV_EXTRA_FLAGS before calling this function. 96 | function (lcov_merge_files OUTFILE ...) 97 | # Remove ${OUTFILE} from ${ARGV} and generate lcov parameters with files. 98 | list(REMOVE_AT ARGV 0) 99 | 100 | # Generate merged file. 101 | string(REPLACE "${CMAKE_BINARY_DIR}/" "" FILE_REL "${OUTFILE}") 102 | add_custom_command(OUTPUT "${OUTFILE}.raw" 103 | COMMAND cat ${ARGV} > ${OUTFILE}.raw 104 | DEPENDS ${ARGV} 105 | COMMENT "Generating ${FILE_REL}" 106 | ) 107 | 108 | add_custom_command(OUTPUT "${OUTFILE}" 109 | COMMAND ${LCOV_BIN} --quiet -a ${OUTFILE}.raw --output-file ${OUTFILE} 110 | --base-directory ${PROJECT_SOURCE_DIR} ${LCOV_EXTRA_FLAGS} 111 | COMMAND ${LCOV_BIN} --quiet -r ${OUTFILE} ${LCOV_REMOVE_PATTERNS} 112 | --output-file ${OUTFILE} ${LCOV_EXTRA_FLAGS} 113 | DEPENDS ${OUTFILE}.raw 114 | COMMENT "Post-processing ${FILE_REL}" 115 | ) 116 | endfunction () 117 | 118 | 119 | 120 | 121 | # Add a new global target to generate initial coverage reports for all targets. 122 | # This target will be used to generate the global initial info file, which is 123 | # used to gather even empty report data. 124 | if (NOT TARGET lcov-capture-init) 125 | add_custom_target(lcov-capture-init) 126 | set(LCOV_CAPTURE_INIT_FILES "" CACHE INTERNAL "") 127 | endif (NOT TARGET lcov-capture-init) 128 | 129 | 130 | # This function will add initial capture of coverage data for target , 131 | # which is needed to get also data for objects, which were not loaded at 132 | # execution time. It will call geninfo for every source file of once and 133 | # store the info file in the same directory. 134 | function (lcov_capture_initial_tgt TNAME) 135 | # We don't have to check, if the target has support for coverage, thus this 136 | # will be checked by add_coverage_target in Findcoverage.cmake. Instead we 137 | # have to determine which gcov binary to use. 138 | get_target_property(TSOURCES ${TNAME} SOURCES) 139 | set(SOURCES "") 140 | set(TCOMPILER "") 141 | foreach (FILE ${TSOURCES}) 142 | codecov_path_of_source(${FILE} FILE) 143 | if (NOT "${FILE}" STREQUAL "") 144 | codecov_lang_of_source(${FILE} LANG) 145 | if (NOT "${LANG}" STREQUAL "") 146 | list(APPEND SOURCES "${FILE}") 147 | set(TCOMPILER ${CMAKE_${LANG}_COMPILER_ID}) 148 | endif () 149 | endif () 150 | endforeach () 151 | 152 | # If no gcov binary was found, coverage data can't be evaluated. 153 | if (NOT GCOV_${TCOMPILER}_BIN) 154 | message(WARNING "No coverage evaluation binary found for ${TCOMPILER}.") 155 | return() 156 | endif () 157 | 158 | set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}") 159 | set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}") 160 | 161 | 162 | get_target_property(TBIN_DIR ${TNAME} BINARY_DIR) 163 | set(TDIR ${TBIN_DIR}/CMakeFiles/${TNAME}.dir) 164 | 165 | set(GENINFO_FILES "") 166 | foreach(FILE ${SOURCES}) 167 | # generate empty coverage files 168 | set(OUTFILE "${TDIR}/${FILE}.info.init") 169 | list(APPEND GENINFO_FILES ${OUTFILE}) 170 | 171 | add_custom_command(OUTPUT ${OUTFILE} COMMAND ${GCOV_ENV} ${GENINFO_BIN} 172 | --quiet --base-directory ${PROJECT_SOURCE_DIR} --initial 173 | --gcov-tool ${GCOV_BIN} --output-filename ${OUTFILE} 174 | ${GENINFO_EXTERN_FLAG} ${TDIR}/${FILE}.gcno 175 | ${GENINFO_EXTRA_FLAGS} 176 | DEPENDS ${TNAME} 177 | COMMENT "Capturing initial coverage data for ${FILE}" 178 | ) 179 | endforeach() 180 | 181 | # Concatenate all files generated by geninfo to a single file per target. 182 | set(OUTFILE "${LCOV_DATA_PATH_INIT}/${TNAME}.info") 183 | list(APPEND LCOV_EXTRA_FLAGS "--initial") 184 | lcov_merge_files("${OUTFILE}" ${GENINFO_FILES}) 185 | add_custom_target(${TNAME}-capture-init ALL DEPENDS ${OUTFILE}) 186 | 187 | # add geninfo file generation to global lcov-geninfo target 188 | add_dependencies(lcov-capture-init ${TNAME}-capture-init) 189 | set(LCOV_CAPTURE_INIT_FILES "${LCOV_CAPTURE_INIT_FILES}" 190 | "${OUTFILE}" CACHE INTERNAL "" 191 | ) 192 | endfunction (lcov_capture_initial_tgt) 193 | 194 | 195 | # This function will generate the global info file for all targets. It has to be 196 | # called after all other CMake functions in the root CMakeLists.txt file, to get 197 | # a full list of all targets that generate coverage data. 198 | function (lcov_capture_initial) 199 | # Skip this function (and do not create the following targets), if there are 200 | # no input files. 201 | if ("${LCOV_CAPTURE_INIT_FILES}" STREQUAL "") 202 | return() 203 | endif () 204 | 205 | # Add a new target to merge the files of all targets. 206 | set(OUTFILE "${LCOV_DATA_PATH_INIT}/all_targets.info") 207 | lcov_merge_files("${OUTFILE}" ${LCOV_CAPTURE_INIT_FILES}) 208 | add_custom_target(lcov-geninfo-init ALL DEPENDS ${OUTFILE} 209 | lcov-capture-init 210 | ) 211 | endfunction (lcov_capture_initial) 212 | 213 | 214 | 215 | 216 | # Add a new global target to generate coverage reports for all targets. This 217 | # target will be used to generate the global info file. 218 | if (NOT TARGET lcov-capture) 219 | add_custom_target(lcov-capture) 220 | set(LCOV_CAPTURE_FILES "" CACHE INTERNAL "") 221 | endif (NOT TARGET lcov-capture) 222 | 223 | 224 | # This function will add capture of coverage data for target , which is 225 | # needed to get also data for objects, which were not loaded at execution time. 226 | # It will call geninfo for every source file of once and store the info 227 | # file in the same directory. 228 | function (lcov_capture_tgt TNAME) 229 | # We don't have to check, if the target has support for coverage, thus this 230 | # will be checked by add_coverage_target in Findcoverage.cmake. Instead we 231 | # have to determine which gcov binary to use. 232 | get_target_property(TSOURCES ${TNAME} SOURCES) 233 | set(SOURCES "") 234 | set(TCOMPILER "") 235 | foreach (FILE ${TSOURCES}) 236 | codecov_path_of_source(${FILE} FILE) 237 | if (NOT "${FILE}" STREQUAL "") 238 | codecov_lang_of_source(${FILE} LANG) 239 | if (NOT "${LANG}" STREQUAL "") 240 | list(APPEND SOURCES "${FILE}") 241 | set(TCOMPILER ${CMAKE_${LANG}_COMPILER_ID}) 242 | endif () 243 | endif () 244 | endforeach () 245 | 246 | # If no gcov binary was found, coverage data can't be evaluated. 247 | if (NOT GCOV_${TCOMPILER}_BIN) 248 | message(WARNING "No coverage evaluation binary found for ${TCOMPILER}.") 249 | return() 250 | endif () 251 | 252 | set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}") 253 | set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}") 254 | 255 | get_target_property(TBIN_DIR ${TNAME} BINARY_DIR) 256 | set(TDIR ${TBIN_DIR}/CMakeFiles/${TNAME}.dir) 257 | 258 | set(GENINFO_FILES "") 259 | foreach(FILE ${SOURCES}) 260 | # Generate coverage files. If no .gcda file was generated during 261 | # execution, the empty coverage file will be used instead. 262 | set(OUTFILE "${TDIR}/${FILE}.info") 263 | list(APPEND GENINFO_FILES ${OUTFILE}) 264 | 265 | # Create an empty .gcda file, so the target capture file can have a dependency on it. 266 | # The capture file will only use this .gcda if it has a non-zero size (test -s). 267 | add_custom_command(OUTPUT "${TDIR}/${FILE}.gcda" 268 | COMMAND "${CMAKE_COMMAND}" -E touch "${TDIR}/${FILE}.gcda" 269 | ) 270 | 271 | add_custom_command(OUTPUT ${OUTFILE} 272 | COMMAND test -s "${TDIR}/${FILE}.gcda" 273 | && ${GCOV_ENV} ${GENINFO_BIN} --quiet --base-directory 274 | ${PROJECT_SOURCE_DIR} --gcov-tool ${GCOV_BIN} 275 | --output-filename ${OUTFILE} ${GENINFO_EXTERN_FLAG} 276 | ${TDIR}/${FILE}.gcda ${GENINFO_EXTRA_FLAGS} 277 | || cp ${OUTFILE}.init ${OUTFILE} 278 | DEPENDS ${TNAME} ${TNAME}-capture-init "${TDIR}/${FILE}.gcda" 279 | COMMENT "Capturing coverage data for ${FILE}" 280 | ) 281 | endforeach() 282 | 283 | # Concatenate all files generated by geninfo to a single file per target. 284 | set(OUTFILE "${LCOV_DATA_PATH_CAPTURE}/${TNAME}.info") 285 | lcov_merge_files("${OUTFILE}" ${GENINFO_FILES}) 286 | add_custom_target(${TNAME}-geninfo DEPENDS ${OUTFILE}) 287 | 288 | # add geninfo file generation to global lcov-capture target 289 | add_dependencies(lcov-capture ${TNAME}-geninfo) 290 | set(LCOV_CAPTURE_FILES "${LCOV_CAPTURE_FILES}" "${OUTFILE}" CACHE INTERNAL 291 | "" 292 | ) 293 | 294 | # Add target for generating html output for this target only. 295 | file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/${TNAME}) 296 | add_custom_target(${TNAME}-genhtml 297 | COMMAND ${GENHTML_BIN} --quiet --sort ${GENHTML_CPPFILT_FLAG} 298 | --prefix ${PROJECT_SOURCE_DIR} 299 | --baseline-file ${LCOV_DATA_PATH_INIT}/${TNAME}.info 300 | --output-directory ${LCOV_HTML_PATH}/${TNAME} 301 | --title "${CMAKE_PROJECT_NAME} - target ${TNAME}" 302 | ${OUTFILE} 303 | DEPENDS ${TNAME}-geninfo ${TNAME}-capture-init 304 | ) 305 | endfunction (lcov_capture_tgt) 306 | 307 | 308 | # This function will generate the global info file for all targets. It has to be 309 | # called after all other CMake functions in the root CMakeLists.txt file, to get 310 | # a full list of all targets that generate coverage data. 311 | function (lcov_capture) 312 | # Skip this function (and do not create the following targets), if there are 313 | # no input files. 314 | if ("${LCOV_CAPTURE_FILES}" STREQUAL "") 315 | return() 316 | endif () 317 | 318 | # Add a new target to merge the files of all targets. 319 | set(OUTFILE "${LCOV_DATA_PATH_CAPTURE}/all_targets.info") 320 | lcov_merge_files("${OUTFILE}" ${LCOV_CAPTURE_FILES}) 321 | add_custom_target(lcov-geninfo DEPENDS ${OUTFILE} lcov-capture) 322 | 323 | # Add a new global target for all lcov targets. This target could be used to 324 | # generate the lcov html output for the whole project instead of calling 325 | # -geninfo and -genhtml for each target. It will also be 326 | # used to generate a html site for all project data together instead of one 327 | # for each target. 328 | if (NOT TARGET lcov) 329 | file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/all_targets) 330 | add_custom_target(lcov 331 | COMMAND ${GENHTML_BIN} --quiet --sort ${GENHTML_CPPFILT_FLAG} 332 | --baseline-file ${LCOV_DATA_PATH_INIT}/all_targets.info 333 | --output-directory ${LCOV_HTML_PATH}/all_targets 334 | --title "${CMAKE_PROJECT_NAME}" --prefix "${PROJECT_SOURCE_DIR}" 335 | ${OUTFILE} 336 | DEPENDS lcov-geninfo-init lcov-geninfo 337 | ) 338 | endif () 339 | endfunction (lcov_capture) 340 | 341 | 342 | 343 | 344 | # Add a new global target to generate the lcov html report for the whole project 345 | # instead of calling -genhtml for each target (to create an own report 346 | # for each target). Instead of the lcov target it does not require geninfo for 347 | # all targets, so you have to call -geninfo to generate the info files 348 | # the targets you'd like to have in your report or lcov-geninfo for generating 349 | # info files for all targets before calling lcov-genhtml. 350 | file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/selected_targets) 351 | if (NOT TARGET lcov-genhtml) 352 | add_custom_target(lcov-genhtml 353 | COMMAND ${GENHTML_BIN} 354 | --quiet 355 | --output-directory ${LCOV_HTML_PATH}/selected_targets 356 | --title \"${CMAKE_PROJECT_NAME} - targets `find 357 | ${LCOV_DATA_PATH_CAPTURE} -name \"*.info\" ! -name 358 | \"all_targets.info\" -exec basename {} .info \\\;`\" 359 | --prefix ${PROJECT_SOURCE_DIR} 360 | --sort 361 | ${GENHTML_CPPFILT_FLAG} 362 | `find ${LCOV_DATA_PATH_CAPTURE} -name \"*.info\" ! -name 363 | \"all_targets.info\"` 364 | ) 365 | endif (NOT TARGET lcov-genhtml) 366 | --------------------------------------------------------------------------------