/ externals / catch / CMake / Findcodecov.cmake
Findcodecov.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@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 dependent 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)