• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
15option(ENABLE_COVERAGE "Enable coverage build." OFF)
16
17set(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.
34function (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 ()
41endfunction (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.
52function (coverage_evaluate)
53	# add lcov evaluation
54	if (LCOV_FOUND)
55		lcov_capture_initial()
56		lcov_capture()
57	endif (LCOV_FOUND)
58endfunction ()
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.
64if (NOT ENABLE_COVERAGE)
65	return()
66endif ()
67
68
69
70
71# Find the reuired flags foreach language.
72set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET})
73set(CMAKE_REQUIRED_QUIET ${codecov_FIND_QUIETLY})
74
75get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
76foreach (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 ()
127endforeach ()
128
129set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_SAVE})
130
131
132
133
134# Helper function to get the language of a source file.
135function (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)
150endfunction ()
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.
156function (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)
178endfunction()
179
180
181
182
183# Add coverage support for target ${TNAME} and register target for coverage
184# evaluation.
185function(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})
250endfunction(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).
257find_package(Gcov)
258find_package(Lcov)
259