Home | History | Annotate | Download | only in CMake
      1 # This file is part of CMake-codecov.
      2 #
      3 # Copyright (c)
      4 #   2015-2017 RWTH Aachen University, Federal Republic of Germany
      5 #
      6 # See the LICENSE file in the package base directory for details
      7 #
      8 # Written by Alexander Haase, alexander.haase (a] rwth-aachen.de
      9 #
     10 
     11 
     12 # Add an option to choose, if coverage should be enabled or not. If enabled
     13 # marked targets will be build with coverage support and appropriate targets
     14 # will be added. If disabled coverage will be ignored for *ALL* targets.
     15 option(ENABLE_COVERAGE "Enable coverage build." OFF)
     16 
     17 set(COVERAGE_FLAG_CANDIDATES
     18 	# gcc and clang
     19 	"-O0 -g -fprofile-arcs -ftest-coverage"
     20 
     21 	# gcc and clang fallback
     22 	"-O0 -g --coverage"
     23 )
     24 
     25 
     26 # Add coverage support for target ${TNAME} and register target for coverage
     27 # evaluation. If coverage is disabled or not supported, this function will
     28 # simply do nothing.
     29 #
     30 # Note: This function is only a wrapper to define this function always, even if
     31 #   coverage is not supported by the compiler or disabled. This function must
     32 #   be defined here, because the module will be exited, if there is no coverage
     33 #   support by the compiler or it is disabled by the user.
     34 function (add_coverage TNAME)
     35 	# only add coverage for target, if coverage is support and enabled.
     36 	if (ENABLE_COVERAGE)
     37 		foreach (TNAME ${ARGV})
     38 			add_coverage_target(${TNAME})
     39 		endforeach ()
     40 	endif ()
     41 endfunction (add_coverage)
     42 
     43 
     44 # Add global target to gather coverage information after all targets have been
     45 # added. Other evaluation functions could be added here, after checks for the
     46 # specific module have been passed.
     47 #
     48 # Note: This function is only a wrapper to define this function always, even if
     49 #   coverage is not supported by the compiler or disabled. This function must
     50 #   be defined here, because the module will be exited, if there is no coverage
     51 #   support by the compiler or it is disabled by the user.
     52 function (coverage_evaluate)
     53 	# add lcov evaluation
     54 	if (LCOV_FOUND)
     55 		lcov_capture_initial()
     56 		lcov_capture()
     57 	endif (LCOV_FOUND)
     58 endfunction ()
     59 
     60 
     61 # Exit this module, if coverage is disabled. add_coverage is defined before this
     62 # return, so this module can be exited now safely without breaking any build-
     63 # scripts.
     64 if (NOT ENABLE_COVERAGE)
     65 	return()
     66 endif ()
     67 
     68 
     69 
     70 
     71 # Find the reuired flags foreach language.
     72 set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET})
     73 set(CMAKE_REQUIRED_QUIET ${codecov_FIND_QUIETLY})
     74 
     75 get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
     76 foreach (LANG ${ENABLED_LANGUAGES})
     77 	# Coverage flags are not dependend on language, but the used compiler. So
     78 	# instead of searching flags foreach language, search flags foreach compiler
     79 	# used.
     80 	set(COMPILER ${CMAKE_${LANG}_COMPILER_ID})
     81 	if (NOT COVERAGE_${COMPILER}_FLAGS)
     82 		foreach (FLAG ${COVERAGE_FLAG_CANDIDATES})
     83 			if(NOT CMAKE_REQUIRED_QUIET)
     84 				message(STATUS "Try ${COMPILER} code coverage flag = [${FLAG}]")
     85 			endif()
     86 
     87 			set(CMAKE_REQUIRED_FLAGS "${FLAG}")
     88 			unset(COVERAGE_FLAG_DETECTED CACHE)
     89 
     90 			if (${LANG} STREQUAL "C")
     91 				include(CheckCCompilerFlag)
     92 				check_c_compiler_flag("${FLAG}" COVERAGE_FLAG_DETECTED)
     93 
     94 			elseif (${LANG} STREQUAL "CXX")
     95 				include(CheckCXXCompilerFlag)
     96 				check_cxx_compiler_flag("${FLAG}" COVERAGE_FLAG_DETECTED)
     97 
     98 			elseif (${LANG} STREQUAL "Fortran")
     99 				# CheckFortranCompilerFlag was introduced in CMake 3.x. To be
    100 				# compatible with older Cmake versions, we will check if this
    101 				# module is present before we use it. Otherwise we will define
    102 				# Fortran coverage support as not available.
    103 				include(CheckFortranCompilerFlag OPTIONAL
    104 					RESULT_VARIABLE INCLUDED)
    105 				if (INCLUDED)
    106 					check_fortran_compiler_flag("${FLAG}"
    107 						COVERAGE_FLAG_DETECTED)
    108 				elseif (NOT CMAKE_REQUIRED_QUIET)
    109 					message("-- Performing Test COVERAGE_FLAG_DETECTED")
    110 					message("-- Performing Test COVERAGE_FLAG_DETECTED - Failed"
    111 						" (Check not supported)")
    112 				endif ()
    113 			endif()
    114 
    115 			if (COVERAGE_FLAG_DETECTED)
    116 				set(COVERAGE_${COMPILER}_FLAGS "${FLAG}"
    117 					CACHE STRING "${COMPILER} flags for code coverage.")
    118 				mark_as_advanced(COVERAGE_${COMPILER}_FLAGS)
    119 				break()
    120 			else ()
    121 				message(WARNING "Code coverage is not available for ${COMPILER}"
    122 				        " compiler. Targets using this compiler will be "
    123 				        "compiled without it.")
    124 			endif ()
    125 		endforeach ()
    126 	endif ()
    127 endforeach ()
    128 
    129 set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_SAVE})
    130 
    131 
    132 
    133 
    134 # Helper function to get the language of a source file.
    135 function (codecov_lang_of_source FILE RETURN_VAR)
    136 	get_filename_component(FILE_EXT "${FILE}" EXT)
    137 	string(TOLOWER "${FILE_EXT}" FILE_EXT)
    138 	string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT)
    139 
    140 	get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
    141 	foreach (LANG ${ENABLED_LANGUAGES})
    142 		list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP)
    143 		if (NOT ${TEMP} EQUAL -1)
    144 			set(${RETURN_VAR} "${LANG}" PARENT_SCOPE)
    145 			return()
    146 		endif ()
    147 	endforeach()
    148 
    149 	set(${RETURN_VAR} "" PARENT_SCOPE)
    150 endfunction ()
    151 
    152 
    153 # Helper function to get the relative path of the source file destination path.
    154 # This path is needed by FindGcov and FindLcov cmake files to locate the
    155 # captured data.
    156 function (codecov_path_of_source FILE RETURN_VAR)
    157 	string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _source ${FILE})
    158 
    159 	# If expression was found, SOURCEFILE is a generator-expression for an
    160 	# object library. Currently we found no way to call this function automatic
    161 	# for the referenced target, so it must be called in the directoryso of the
    162 	# object library definition.
    163 	if (NOT "${_source}" STREQUAL "")
    164 		set(${RETURN_VAR} "" PARENT_SCOPE)
    165 		return()
    166 	endif ()
    167 
    168 
    169 	string(REPLACE "${CMAKE_CURRENT_BINARY_DIR}/" "" FILE "${FILE}")
    170 	if(IS_ABSOLUTE ${FILE})
    171 		file(RELATIVE_PATH FILE ${CMAKE_CURRENT_SOURCE_DIR} ${FILE})
    172 	endif()
    173 
    174 	# get the right path for file
    175 	string(REPLACE ".." "__" PATH "${FILE}")
    176 
    177 	set(${RETURN_VAR} "${PATH}" PARENT_SCOPE)
    178 endfunction()
    179 
    180 
    181 
    182 
    183 # Add coverage support for target ${TNAME} and register target for coverage
    184 # evaluation.
    185 function(add_coverage_target TNAME)
    186 	# Check if all sources for target use the same compiler. If a target uses
    187 	# e.g. C and Fortran mixed and uses different compilers (e.g. clang and
    188 	# gfortran) this can trigger huge problems, because different compilers may
    189 	# use different implementations for code coverage.
    190 	get_target_property(TSOURCES ${TNAME} SOURCES)
    191 	set(TARGET_COMPILER "")
    192 	set(ADDITIONAL_FILES "")
    193 	foreach (FILE ${TSOURCES})
    194 		# If expression was found, FILE is a generator-expression for an object
    195 		# library. Object libraries will be ignored.
    196 		string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _file ${FILE})
    197 		if ("${_file}" STREQUAL "")
    198 			codecov_lang_of_source(${FILE} LANG)
    199 			if (LANG)
    200 				list(APPEND TARGET_COMPILER ${CMAKE_${LANG}_COMPILER_ID})
    201 
    202 				list(APPEND ADDITIONAL_FILES "${FILE}.gcno")
    203 				list(APPEND ADDITIONAL_FILES "${FILE}.gcda")
    204 			endif ()
    205 		endif ()
    206 	endforeach ()
    207 
    208 	list(REMOVE_DUPLICATES TARGET_COMPILER)
    209 	list(LENGTH TARGET_COMPILER NUM_COMPILERS)
    210 
    211 	if (NUM_COMPILERS GREATER 1)
    212 		message(WARNING "Can't use code coverage for target ${TNAME}, because "
    213 		        "it will be compiled by incompatible compilers. Target will be "
    214 		        "compiled without code coverage.")
    215 		return()
    216 
    217 	elseif (NUM_COMPILERS EQUAL 0)
    218 		message(WARNING "Can't use code coverage for target ${TNAME}, because "
    219 		        "it uses an unknown compiler. Target will be compiled without "
    220 		        "code coverage.")
    221 		return()
    222 
    223 	elseif (NOT DEFINED "COVERAGE_${TARGET_COMPILER}_FLAGS")
    224 		# A warning has been printed before, so just return if flags for this
    225 		# compiler aren't available.
    226 		return()
    227 	endif()
    228 
    229 
    230 	# enable coverage for target
    231 	set_property(TARGET ${TNAME} APPEND_STRING
    232 		PROPERTY COMPILE_FLAGS " ${COVERAGE_${TARGET_COMPILER}_FLAGS}")
    233 	set_property(TARGET ${TNAME} APPEND_STRING
    234 		PROPERTY LINK_FLAGS " ${COVERAGE_${TARGET_COMPILER}_FLAGS}")
    235 
    236 
    237 	# Add gcov files generated by compiler to clean target.
    238 	set(CLEAN_FILES "")
    239 	foreach (FILE ${ADDITIONAL_FILES})
    240 		codecov_path_of_source(${FILE} FILE)
    241 		list(APPEND CLEAN_FILES "CMakeFiles/${TNAME}.dir/${FILE}")
    242 	endforeach()
    243 
    244 	set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES
    245 		"${CLEAN_FILES}")
    246 
    247 
    248 	add_gcov_target(${TNAME})
    249 	add_lcov_target(${TNAME})
    250 endfunction(add_coverage_target)
    251 
    252 
    253 
    254 
    255 # Include modules for parsing the collected data and output it in a readable
    256 # format (like gcov and lcov).
    257 find_package(Gcov)
    258 find_package(Lcov)
    259