1# check-sources:disable-copyright-check 2 3# Copyright (c) 2012 - 2017, Lars Bilke All rights reserved. 4# 5# Redistribution and use in source and binary forms, with or without 6# modification, are permitted provided that the following conditions are met: 7# 8# 1. Redistributions of source code must retain the above copyright notice, this 9# list of conditions and the following disclaimer. 10# 11# 1. Redistributions in binary form must reproduce the above copyright notice, 12# this list of conditions and the following disclaimer in the documentation 13# and/or other materials provided with the distribution. 14# 15# 1. Neither the name of the copyright holder nor the names of its contributors 16# may be used to endorse or promote products derived from this software 17# without specific prior written permission. 18# 19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29# 30# CHANGES: 31# 32# 2012-01-31, Lars Bilke - Enable Code Coverage 33# 34# 2013-09-17, Joakim Söderberg - Added support for Clang. - Some additional 35# usage instructions. 36# 37# 2016-02-03, Lars Bilke - Refactored functions to use named parameters 38# 39# 2017-06-02, Lars Bilke - Merged with modified version from github.com/ufz/ogs 40# 41# USAGE: 42# 43# 1. Copy this file into your cmake modules path. 44# 45# 1. Add the following line to your CMakeLists.txt: include(CodeCoverage) 46# 47# 1. Append necessary compiler flags: APPEND_COVERAGE_COMPILER_FLAGS() 48# 49# 1. If you need to exclude additional directories from the report, specify them 50# using the COVERAGE_EXCLUDES variable before calling 51# SETUP_TARGET_FOR_COVERAGE. Example: set(COVERAGE_EXCLUDES 'dir1/*' 'dir2/*') 52# 53# 1. Use the functions described below to create a custom make target which runs 54# your test executable and produces a code coverage report. 55# 56# 1. Build a Debug build: cmake -DCMAKE_BUILD_TYPE=Debug .. make make 57# my_coverage_target 58# 59 60include(CMakeParseArguments) 61 62# Check prereqs 63find_program(GCOV_PATH gcov) 64find_program(LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl) 65find_program(GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat) 66find_program(GCOVR_PATH gcovr PATHS ${PROJECT_SOURCE_DIR}/scripts/test) 67find_program(SIMPLE_PYTHON_EXECUTABLE python) 68 69if(NOT GCOV_PATH) 70 message(FATAL_ERROR "gcov not found! Aborting...") 71endif() # NOT GCOV_PATH 72 73if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang") 74 if("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 3) 75 message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...") 76 endif() 77elseif(NOT CMAKE_COMPILER_IS_GNUCXX) 78 message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...") 79endif() 80 81set(COVERAGE_COMPILER_FLAGS 82 "-g -O0 --coverage -fprofile-arcs -ftest-coverage" 83 CACHE INTERNAL "" 84) 85 86set(CMAKE_CXX_FLAGS_COVERAGE 87 ${COVERAGE_COMPILER_FLAGS} 88 CACHE STRING "Flags used by the C++ compiler during coverage builds." FORCE 89) 90set(CMAKE_C_FLAGS_COVERAGE 91 ${COVERAGE_COMPILER_FLAGS} 92 CACHE STRING "Flags used by the C compiler during coverage builds." FORCE 93) 94set(CMAKE_EXE_LINKER_FLAGS_COVERAGE 95 "" 96 CACHE STRING "Flags used for linking binaries during coverage builds." 97 FORCE 98) 99set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE 100 "" 101 CACHE STRING 102 "Flags used by the shared libraries linker during coverage builds." 103 FORCE 104) 105mark_as_advanced( 106 CMAKE_CXX_FLAGS_COVERAGE CMAKE_C_FLAGS_COVERAGE 107 CMAKE_EXE_LINKER_FLAGS_COVERAGE CMAKE_SHARED_LINKER_FLAGS_COVERAGE 108) 109 110if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") 111 message( 112 WARNING 113 "Code coverage results with an optimised (non-Debug) build may be misleading" 114 ) 115endif() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug" 116 117if(CMAKE_C_COMPILER_ID STREQUAL "GNU") 118 link_libraries(gcov) 119else() 120 set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") 121endif() 122 123# Defines a target for running and collection code coverage information Builds 124# dependencies, runs the given executable and outputs reports. NOTE! The 125# executable should always have a ZERO as exit code otherwise the coverage 126# generation will not complete. 127# 128# SETUP_TARGET_FOR_COVERAGE( NAME testrunner_coverage # New 129# target name EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in 130# PROJECT_BINARY_DIR DEPENDENCIES testrunner # Dependencies 131# to build first ) 132function(SETUP_TARGET_FOR_COVERAGE) 133 134 set(options NONE) 135 set(oneValueArgs NAME) 136 set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) 137 cmake_parse_arguments( 138 Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} 139 ) 140 141 if(NOT LCOV_PATH) 142 message(FATAL_ERROR "lcov not found! Aborting...") 143 endif() # NOT LCOV_PATH 144 145 if(NOT GENHTML_PATH) 146 message(FATAL_ERROR "genhtml not found! Aborting...") 147 endif() # NOT GENHTML_PATH 148 149 # Setup target 150 add_custom_target( 151 ${Coverage_NAME} 152 # Cleanup lcov 153 COMMAND ${LCOV_PATH} --directory . --zerocounters 154 # Create baseline to make sure untouched files show up in the report 155 COMMAND ${LCOV_PATH} -c -i -d . -o ${Coverage_NAME}.base 156 # Run tests 157 COMMAND ${Coverage_EXECUTABLE} 158 # Capturing lcov counters and generating report 159 COMMAND 160 ${LCOV_PATH} --directory . --capture --output-file ${Coverage_NAME}.info 161 # add baseline counters 162 COMMAND ${LCOV_PATH} -a ${Coverage_NAME}.base -a ${Coverage_NAME}.info 163 --output-file ${Coverage_NAME}.total 164 COMMAND ${LCOV_PATH} --remove ${Coverage_NAME}.total ${COVERAGE_EXCLUDES} 165 --output-file ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned 166 COMMAND ${GENHTML_PATH} -o ${Coverage_NAME} 167 ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned 168 COMMAND 169 ${CMAKE_COMMAND} -E remove ${Coverage_NAME}.base ${Coverage_NAME}.info 170 ${Coverage_NAME}.total ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned 171 WORKING_DIRECTORY ${PROJECT_BINARY_DIR} 172 DEPENDS ${Coverage_DEPENDENCIES} 173 COMMENT 174 "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report." 175 ) 176 177 # Show info where to find the report 178 add_custom_command( 179 TARGET ${Coverage_NAME} 180 POST_BUILD 181 COMMAND ; 182 COMMENT 183 "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." 184 ) 185 186endfunction() # SETUP_TARGET_FOR_COVERAGE 187 188# Defines a target for running and collection code coverage information Builds 189# dependencies, runs the given executable and outputs reports. NOTE! The 190# executable should always have a ZERO as exit code otherwise the coverage 191# generation will not complete. 192# 193# SETUP_TARGET_FOR_COVERAGE_COBERTURA( NAME ctest_coverage # 194# New target name EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in 195# PROJECT_BINARY_DIR DEPENDENCIES executable_target # Dependencies to 196# build first ) 197function(SETUP_TARGET_FOR_COVERAGE_COBERTURA) 198 199 set(options NONE) 200 set(oneValueArgs NAME) 201 set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) 202 cmake_parse_arguments( 203 Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} 204 ) 205 206 if(NOT SIMPLE_PYTHON_EXECUTABLE) 207 message(FATAL_ERROR "python not found! Aborting...") 208 endif() # NOT SIMPLE_PYTHON_EXECUTABLE 209 210 if(NOT GCOVR_PATH) 211 message(FATAL_ERROR "gcovr not found! Aborting...") 212 endif() # NOT GCOVR_PATH 213 214 # Combine excludes to several -e arguments 215 set(COBERTURA_EXCLUDES "") 216 foreach(EXCLUDE ${COVERAGE_EXCLUDES}) 217 set(COBERTURA_EXCLUDES "-e ${EXCLUDE} ${COBERTURA_EXCLUDES}") 218 endforeach() 219 220 add_custom_target( 221 ${Coverage_NAME} 222 # Run tests 223 ${Coverage_EXECUTABLE} 224 # Running gcovr 225 COMMAND ${GCOVR_PATH} -x -r ${PROJECT_SOURCE_DIR} ${COBERTURA_EXCLUDES} -o 226 ${Coverage_NAME}.xml 227 WORKING_DIRECTORY ${PROJECT_BINARY_DIR} 228 DEPENDS ${Coverage_DEPENDENCIES} 229 COMMENT "Running gcovr to produce Cobertura code coverage report." 230 ) 231 232 # Show info where to find the report 233 add_custom_command( 234 TARGET ${Coverage_NAME} 235 POST_BUILD 236 COMMAND ; 237 COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml." 238 ) 239 240endfunction() # SETUP_TARGET_FOR_COVERAGE_COBERTURA 241 242function(APPEND_COVERAGE_COMPILER_FLAGS) 243 set(CMAKE_C_FLAGS 244 "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" 245 PARENT_SCOPE 246 ) 247 set(CMAKE_CXX_FLAGS 248 "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" 249 PARENT_SCOPE 250 ) 251 message( 252 STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}" 253 ) 254endfunction() # APPEND_COVERAGE_COMPILER_FLAGS 255