1# This file is part of CMake-codecov. 2# 3# https://github.com/RWTH-ELP/CMake-codecov 4# 5# Copyright (c) 6# 2015-2016 RWTH Aachen University, Federal Republic of Germany 7# 8# LICENSE : BSD 3-Clause License 9# 10# Written by Alexander Haase, alexander.haase@rwth-aachen.de 11# Updated by Guillaume Jacquenot, guillaume.jacquenot@gmail.com 12 13set(COVERAGE_FLAG_CANDIDATES 14 # gcc and clang 15 "-O0 -g -fprofile-arcs -ftest-coverage" 16 17 # gcc and clang fallback 18 "-O0 -g --coverage" 19) 20 21 22# To avoid error messages about CMP0051, this policy will be set to new. There 23# will be no problem, as TARGET_OBJECTS generator expressions will be filtered 24# with a regular expression from the sources. 25if(POLICY CMP0051) 26 cmake_policy(SET CMP0051 NEW) 27endif() 28 29 30# Add coverage support for target ${TNAME} and register target for coverage 31# evaluation. 32function(add_coverage TNAME) 33 foreach (TNAME ${ARGV}) 34 add_coverage_target(${TNAME}) 35 endforeach() 36endfunction() 37 38 39# Find the reuired flags foreach language. 40set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET}) 41set(CMAKE_REQUIRED_QUIET ${codecov_FIND_QUIETLY}) 42 43get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) 44foreach (LANG ${ENABLED_LANGUAGES}) 45 # Coverage flags are not dependent on language, but the used compiler. So 46 # instead of searching flags foreach language, search flags foreach compiler 47 # used. 48 set(COMPILER ${CMAKE_${LANG}_COMPILER_ID}) 49 if(NOT COVERAGE_${COMPILER}_FLAGS) 50 foreach (FLAG ${COVERAGE_FLAG_CANDIDATES}) 51 if(NOT CMAKE_REQUIRED_QUIET) 52 message(STATUS "Try ${COMPILER} code coverage flag = [${FLAG}]") 53 endif() 54 55 set(CMAKE_REQUIRED_FLAGS "${FLAG}") 56 unset(COVERAGE_FLAG_DETECTED CACHE) 57 58 if(${LANG} STREQUAL "C") 59 include(CheckCCompilerFlag) 60 check_c_compiler_flag("${FLAG}" COVERAGE_FLAG_DETECTED) 61 62 elseif(${LANG} STREQUAL "CXX") 63 include(CheckCXXCompilerFlag) 64 check_cxx_compiler_flag("${FLAG}" COVERAGE_FLAG_DETECTED) 65 66 elseif(${LANG} STREQUAL "Fortran") 67 # CheckFortranCompilerFlag was introduced in CMake 3.x. To be 68 # compatible with older Cmake versions, we will check if this 69 # module is present before we use it. Otherwise we will define 70 # Fortran coverage support as not available. 71 include(CheckFortranCompilerFlag OPTIONAL 72 RESULT_VARIABLE INCLUDED) 73 if(INCLUDED) 74 check_fortran_compiler_flag("${FLAG}" 75 COVERAGE_FLAG_DETECTED) 76 elseif(NOT CMAKE_REQUIRED_QUIET) 77 message("-- Performing Test COVERAGE_FLAG_DETECTED") 78 message("-- Performing Test COVERAGE_FLAG_DETECTED - Failed" 79 " (Check not supported)") 80 endif() 81 endif() 82 83 if(COVERAGE_FLAG_DETECTED) 84 set(COVERAGE_${COMPILER}_FLAGS "${FLAG}" 85 CACHE STRING "${COMPILER} flags for code coverage.") 86 mark_as_advanced(COVERAGE_${COMPILER}_FLAGS) 87 break() 88 endif() 89 endforeach() 90 endif() 91endforeach() 92 93set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_SAVE}) 94 95# Helper function to get the language of a source file. 96function (codecov_lang_of_source FILE RETURN_VAR) 97 get_filename_component(FILE_EXT "${FILE}" EXT) 98 string(TOLOWER "${FILE_EXT}" FILE_EXT) 99 string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT) 100 101 get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) 102 foreach (LANG ${ENABLED_LANGUAGES}) 103 list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP) 104 if(NOT ${TEMP} EQUAL -1) 105 set(${RETURN_VAR} "${LANG}" PARENT_SCOPE) 106 return() 107 endif() 108 endforeach() 109 110 set(${RETURN_VAR} "" PARENT_SCOPE) 111endfunction() 112 113# Helper function to get the relative path of the source file destination path. 114# This path is needed by FindGcov and FindLcov cmake files to locate the 115# captured data. 116function (codecov_path_of_source FILE RETURN_VAR) 117 string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _source ${FILE}) 118 119 # If expression was found, SOURCEFILE is a generator-expression for an 120 # object library. Currently we found no way to call this function automatic 121 # for the referenced target, so it must be called in the directoryso of the 122 # object library definition. 123 if(NOT "${_source}" STREQUAL "") 124 set(${RETURN_VAR} "" PARENT_SCOPE) 125 return() 126 endif() 127 128 string(REPLACE "${CMAKE_CURRENT_BINARY_DIR}/" "" FILE "${FILE}") 129 if(IS_ABSOLUTE ${FILE}) 130 file(RELATIVE_PATH FILE ${CMAKE_CURRENT_SOURCE_DIR} ${FILE}) 131 endif() 132 133 # get the right path for file 134 string(REPLACE ".." "__" PATH "${FILE}") 135 136 set(${RETURN_VAR} "${PATH}" PARENT_SCOPE) 137endfunction() 138 139# Add coverage support for target ${TNAME} and register target for coverage 140# evaluation. 141function(add_coverage_target TNAME) 142 # Check if all sources for target use the same compiler. If a target uses 143 # e.g. C and Fortran mixed and uses different compilers (e.g. clang and 144 # gfortran) this can trigger huge problems, because different compilers may 145 # use different implementations for code coverage. 146 get_target_property(TSOURCES ${TNAME} SOURCES) 147 set(TARGET_COMPILER "") 148 set(ADDITIONAL_FILES "") 149 foreach (FILE ${TSOURCES}) 150 # If expression was found, FILE is a generator-expression for an object 151 # library. Object libraries will be ignored. 152 string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _file ${FILE}) 153 if("${_file}" STREQUAL "") 154 codecov_lang_of_source(${FILE} LANG) 155 if(LANG) 156 list(APPEND TARGET_COMPILER ${CMAKE_${LANG}_COMPILER_ID}) 157 158 list(APPEND ADDITIONAL_FILES "${FILE}.gcno") 159 list(APPEND ADDITIONAL_FILES "${FILE}.gcda") 160 endif() 161 endif() 162 endforeach () 163 164 list(REMOVE_DUPLICATES TARGET_COMPILER) 165 list(LENGTH TARGET_COMPILER NUM_COMPILERS) 166 167 if(NUM_COMPILERS GREATER 1) 168 message(AUTHOR_WARNING "Coverage disabled for target ${TNAME} because " 169 "it will be compiled by different compilers.") 170 return() 171 172 elseif((NUM_COMPILERS EQUAL 0) OR 173 (NOT DEFINED "COVERAGE_${TARGET_COMPILER}_FLAGS")) 174 message(AUTHOR_WARNING "Coverage disabled for target ${TNAME} " 175 "because there is no sanitizer available for target sources.") 176 return() 177 endif() 178 179 180 # enable coverage for target 181 set_property(TARGET ${TNAME} APPEND_STRING 182 PROPERTY COMPILE_FLAGS " ${COVERAGE_${TARGET_COMPILER}_FLAGS}") 183 set_property(TARGET ${TNAME} APPEND_STRING 184 PROPERTY LINK_FLAGS " ${COVERAGE_${TARGET_COMPILER}_FLAGS}") 185 186 187 # Add gcov files generated by compiler to clean target. 188 set(CLEAN_FILES "") 189 foreach (FILE ${ADDITIONAL_FILES}) 190 codecov_path_of_source(${FILE} FILE) 191 list(APPEND CLEAN_FILES "CMakeFiles/${TNAME}.dir/${FILE}") 192 endforeach() 193 194 set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES 195 "${CLEAN_FILES}") 196 197 add_gcov_target(${TNAME}) 198endfunction() 199 200# Include modules for parsing the collected data and output it in a readable 201# format (like gcov). 202find_package(Gcov) 203