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# include required Modules
13include(FindPackageHandleStandardArgs)
14
15
16# Search for gcov binary.
17set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET})
18set(CMAKE_REQUIRED_QUIET ${codecov_FIND_QUIETLY})
19
20get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
21foreach (LANG ${ENABLED_LANGUAGES})
22	# Gcov evaluation is dependent on the used compiler. Check gcov support for
23	# each compiler that is used. If gcov binary was already found for this
24	# compiler, do not try to find it again.
25	if (NOT GCOV_${CMAKE_${LANG}_COMPILER_ID}_BIN)
26		get_filename_component(COMPILER_PATH "${CMAKE_${LANG}_COMPILER}" PATH)
27
28		if ("${CMAKE_${LANG}_COMPILER_ID}" STREQUAL "GNU")
29			# Some distributions like OSX (homebrew) ship gcov with the compiler
30			# version appended as gcov-x. To find this binary we'll build the
31			# suggested binary name with the compiler version.
32			string(REGEX MATCH "^[0-9]+" GCC_VERSION
33				"${CMAKE_${LANG}_COMPILER_VERSION}")
34
35			find_program(GCOV_BIN NAMES gcov-${GCC_VERSION} gcov
36				HINTS ${COMPILER_PATH})
37
38		elseif ("${CMAKE_${LANG}_COMPILER_ID}" STREQUAL "Clang")
39			# Some distributions like Debian ship llvm-cov with the compiler
40			# version appended as llvm-cov-x.y. To find this binary we'll build
41			# the suggested binary name with the compiler version.
42			string(REGEX MATCH "^[0-9]+.[0-9]+" LLVM_VERSION
43				"${CMAKE_${LANG}_COMPILER_VERSION}")
44
45			# llvm-cov prior version 3.5 seems to be not working with coverage
46			# evaluation tools, but these versions are compatible with the gcc
47			# gcov tool.
48			if(LLVM_VERSION VERSION_GREATER 3.4)
49				find_program(LLVM_COV_BIN NAMES "llvm-cov-${LLVM_VERSION}"
50					"llvm-cov" HINTS ${COMPILER_PATH})
51				mark_as_advanced(LLVM_COV_BIN)
52
53				if (LLVM_COV_BIN)
54					find_program(LLVM_COV_WRAPPER "llvm-cov-wrapper" PATHS
55						${CMAKE_MODULE_PATH})
56					if (LLVM_COV_WRAPPER)
57						set(GCOV_BIN "${LLVM_COV_WRAPPER}" CACHE FILEPATH "")
58
59						# set additional parameters
60						set(GCOV_${CMAKE_${LANG}_COMPILER_ID}_ENV
61							"LLVM_COV_BIN=${LLVM_COV_BIN}" CACHE STRING
62							"Environment variables for llvm-cov-wrapper.")
63						mark_as_advanced(GCOV_${CMAKE_${LANG}_COMPILER_ID}_ENV)
64					endif ()
65				endif ()
66			endif ()
67
68			if (NOT GCOV_BIN)
69				# Fall back to gcov binary if llvm-cov was not found or is
70				# incompatible. This is the default on OSX, but may crash on
71				# recent Linux versions.
72				find_program(GCOV_BIN gcov HINTS ${COMPILER_PATH})
73			endif ()
74		endif ()
75
76
77		if (GCOV_BIN)
78			set(GCOV_${CMAKE_${LANG}_COMPILER_ID}_BIN "${GCOV_BIN}" CACHE STRING
79				"${LANG} gcov binary.")
80
81			if (NOT CMAKE_REQUIRED_QUIET)
82				message("-- Found gcov evaluation for "
83				"${CMAKE_${LANG}_COMPILER_ID}: ${GCOV_BIN}")
84			endif()
85
86			unset(GCOV_BIN CACHE)
87		endif ()
88	endif ()
89endforeach ()
90
91
92
93
94# Add a new global target for all gcov targets. This target could be used to
95# generate the gcov files for the whole project instead of calling <TARGET>-gcov
96# for each target.
97if (NOT TARGET gcov)
98	add_custom_target(gcov)
99endif (NOT TARGET gcov)
100
101
102
103# This function will add gcov evaluation for target <TNAME>. Only sources of
104# this target will be evaluated and no dependencies will be added. It will call
105# Gcov on any source file of <TNAME> once and store the gcov file in the same
106# directory.
107function (add_gcov_target TNAME)
108	set(TDIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TNAME}.dir)
109
110	# We don't have to check, if the target has support for coverage, thus this
111	# will be checked by add_coverage_target in Findcoverage.cmake. Instead we
112	# have to determine which gcov binary to use.
113	get_target_property(TSOURCES ${TNAME} SOURCES)
114	set(SOURCES "")
115	set(TCOMPILER "")
116	foreach (FILE ${TSOURCES})
117		codecov_path_of_source(${FILE} FILE)
118		if (NOT "${FILE}" STREQUAL "")
119			codecov_lang_of_source(${FILE} LANG)
120			if (NOT "${LANG}" STREQUAL "")
121				list(APPEND SOURCES "${FILE}")
122				set(TCOMPILER ${CMAKE_${LANG}_COMPILER_ID})
123			endif ()
124		endif ()
125	endforeach ()
126
127	# If no gcov binary was found, coverage data can't be evaluated.
128	if (NOT GCOV_${TCOMPILER}_BIN)
129		message(WARNING "No coverage evaluation binary found for ${TCOMPILER}.")
130		return()
131	endif ()
132
133	set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}")
134	set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}")
135
136
137	set(BUFFER "")
138	foreach(FILE ${SOURCES})
139		get_filename_component(FILE_PATH "${TDIR}/${FILE}" PATH)
140
141		# call gcov
142		add_custom_command(OUTPUT ${TDIR}/${FILE}.gcov
143			COMMAND ${GCOV_ENV} ${GCOV_BIN} ${TDIR}/${FILE}.gcno > /dev/null
144			DEPENDS ${TNAME} ${TDIR}/${FILE}.gcno
145			WORKING_DIRECTORY ${FILE_PATH}
146		)
147
148		list(APPEND BUFFER ${TDIR}/${FILE}.gcov)
149	endforeach()
150
151
152	# add target for gcov evaluation of <TNAME>
153	add_custom_target(${TNAME}-gcov DEPENDS ${BUFFER})
154
155	# add evaluation target to the global gcov target.
156	add_dependencies(gcov ${TNAME}-gcov)
157endfunction (add_gcov_target)
158