Blame CMake/FindLcov.cmake

rpm-build a7f80b
# This file is part of CMake-codecov.
rpm-build a7f80b
#
rpm-build a7f80b
# Copyright (c)
rpm-build a7f80b
#   2015-2017 RWTH Aachen University, Federal Republic of Germany
rpm-build a7f80b
#
rpm-build a7f80b
# See the LICENSE file in the package base directory for details
rpm-build a7f80b
#
rpm-build a7f80b
# Written by Alexander Haase, alexander.haase@rwth-aachen.de
rpm-build a7f80b
#
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
# configuration
rpm-build a7f80b
set(LCOV_DATA_PATH "${CMAKE_BINARY_DIR}/lcov/data")
rpm-build a7f80b
set(LCOV_DATA_PATH_INIT "${LCOV_DATA_PATH}/init")
rpm-build a7f80b
set(LCOV_DATA_PATH_CAPTURE "${LCOV_DATA_PATH}/capture")
rpm-build a7f80b
set(LCOV_HTML_PATH "${CMAKE_BINARY_DIR}/lcov/html")
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
# Search for Gcov which is used by Lcov.
rpm-build a7f80b
find_package(Gcov)
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
# This function will add lcov evaluation for target <TNAME>. Only sources of
rpm-build a7f80b
# this target will be evaluated and no dependencies will be added. It will call
rpm-build a7f80b
# geninfo on any source file of <TNAME> once and store the info file in the same
rpm-build a7f80b
# directory.
rpm-build a7f80b
#
rpm-build a7f80b
# Note: This function is only a wrapper to define this function always, even if
rpm-build a7f80b
#   coverage is not supported by the compiler or disabled. This function must
rpm-build a7f80b
#   be defined here, because the module will be exited, if there is no coverage
rpm-build a7f80b
#   support by the compiler or it is disabled by the user.
rpm-build a7f80b
function (add_lcov_target TNAME)
rpm-build a7f80b
	if (LCOV_FOUND)
rpm-build a7f80b
		# capture initial coverage data
rpm-build a7f80b
		lcov_capture_initial_tgt(${TNAME})
rpm-build a7f80b
rpm-build a7f80b
		# capture coverage data after execution
rpm-build a7f80b
		lcov_capture_tgt(${TNAME})
rpm-build a7f80b
	endif ()
rpm-build a7f80b
endfunction (add_lcov_target)
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
# include required Modules
rpm-build a7f80b
include(FindPackageHandleStandardArgs)
rpm-build a7f80b
rpm-build a7f80b
# Search for required lcov binaries.
rpm-build a7f80b
find_program(LCOV_BIN lcov)
rpm-build a7f80b
find_program(GENINFO_BIN geninfo)
rpm-build a7f80b
find_program(GENHTML_BIN genhtml)
rpm-build a7f80b
find_package_handle_standard_args(lcov
rpm-build a7f80b
	REQUIRED_VARS LCOV_BIN GENINFO_BIN GENHTML_BIN
rpm-build a7f80b
)
rpm-build a7f80b
rpm-build a7f80b
# enable genhtml C++ demangeling, if c++filt is found.
rpm-build a7f80b
set(GENHTML_CPPFILT_FLAG "")
rpm-build a7f80b
find_program(CPPFILT_BIN c++filt)
rpm-build a7f80b
if (NOT CPPFILT_BIN STREQUAL "")
rpm-build a7f80b
	set(GENHTML_CPPFILT_FLAG "--demangle-cpp")
rpm-build a7f80b
endif (NOT CPPFILT_BIN STREQUAL "")
rpm-build a7f80b
rpm-build a7f80b
# enable no-external flag for lcov, if available.
rpm-build a7f80b
if (GENINFO_BIN AND NOT DEFINED GENINFO_EXTERN_FLAG)
rpm-build a7f80b
	set(FLAG "")
rpm-build a7f80b
	execute_process(COMMAND ${GENINFO_BIN} --help OUTPUT_VARIABLE GENINFO_HELP)
rpm-build a7f80b
	string(REGEX MATCH "external" GENINFO_RES "${GENINFO_HELP}")
rpm-build a7f80b
	if (GENINFO_RES)
rpm-build a7f80b
		set(FLAG "--no-external")
rpm-build a7f80b
	endif ()
rpm-build a7f80b
rpm-build a7f80b
	set(GENINFO_EXTERN_FLAG "${FLAG}"
rpm-build a7f80b
		CACHE STRING "Geninfo flag to exclude system sources.")
rpm-build a7f80b
endif ()
rpm-build a7f80b
rpm-build a7f80b
# If Lcov was not found, exit module now.
rpm-build a7f80b
if (NOT LCOV_FOUND)
rpm-build a7f80b
	return()
rpm-build a7f80b
endif (NOT LCOV_FOUND)
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
# Create directories to be used.
rpm-build a7f80b
file(MAKE_DIRECTORY ${LCOV_DATA_PATH_INIT})
rpm-build a7f80b
file(MAKE_DIRECTORY ${LCOV_DATA_PATH_CAPTURE})
rpm-build a7f80b
rpm-build a7f80b
set(LCOV_REMOVE_PATTERNS "")
rpm-build a7f80b
rpm-build a7f80b
# This function will merge lcov files to a single target file. Additional lcov
rpm-build a7f80b
# flags may be set with setting LCOV_EXTRA_FLAGS before calling this function.
rpm-build a7f80b
function (lcov_merge_files OUTFILE ...)
rpm-build a7f80b
	# Remove ${OUTFILE} from ${ARGV} and generate lcov parameters with files.
rpm-build a7f80b
	list(REMOVE_AT ARGV 0)
rpm-build a7f80b
rpm-build a7f80b
	# Generate merged file.
rpm-build a7f80b
	string(REPLACE "${CMAKE_BINARY_DIR}/" "" FILE_REL "${OUTFILE}")
rpm-build a7f80b
	add_custom_command(OUTPUT "${OUTFILE}.raw"
rpm-build a7f80b
		COMMAND cat ${ARGV} > ${OUTFILE}.raw
rpm-build a7f80b
		DEPENDS ${ARGV}
rpm-build a7f80b
		COMMENT "Generating ${FILE_REL}"
rpm-build a7f80b
	)
rpm-build a7f80b
rpm-build a7f80b
	add_custom_command(OUTPUT "${OUTFILE}"
rpm-build a7f80b
		COMMAND ${LCOV_BIN} --quiet -a ${OUTFILE}.raw --output-file ${OUTFILE}
rpm-build a7f80b
			--base-directory ${PROJECT_SOURCE_DIR} ${LCOV_EXTRA_FLAGS}
rpm-build a7f80b
		COMMAND ${LCOV_BIN} --quiet -r ${OUTFILE} ${LCOV_REMOVE_PATTERNS}
rpm-build a7f80b
			--output-file ${OUTFILE} ${LCOV_EXTRA_FLAGS}
rpm-build a7f80b
		DEPENDS ${OUTFILE}.raw
rpm-build a7f80b
		COMMENT "Post-processing ${FILE_REL}"
rpm-build a7f80b
	)
rpm-build a7f80b
endfunction ()
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
# Add a new global target to generate initial coverage reports for all targets.
rpm-build a7f80b
# This target will be used to generate the global initial info file, which is
rpm-build a7f80b
# used to gather even empty report data.
rpm-build a7f80b
if (NOT TARGET lcov-capture-init)
rpm-build a7f80b
	add_custom_target(lcov-capture-init)
rpm-build a7f80b
	set(LCOV_CAPTURE_INIT_FILES "" CACHE INTERNAL "")
rpm-build a7f80b
endif (NOT TARGET lcov-capture-init)
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
# This function will add initial capture of coverage data for target <TNAME>,
rpm-build a7f80b
# which is needed to get also data for objects, which were not loaded at
rpm-build a7f80b
# execution time. It will call geninfo for every source file of <TNAME> once and
rpm-build a7f80b
# store the info file in the same directory.
rpm-build a7f80b
function (lcov_capture_initial_tgt TNAME)
rpm-build a7f80b
	# We don't have to check, if the target has support for coverage, thus this
rpm-build a7f80b
	# will be checked by add_coverage_target in Findcoverage.cmake. Instead we
rpm-build a7f80b
	# have to determine which gcov binary to use.
rpm-build a7f80b
	get_target_property(TSOURCES ${TNAME} SOURCES)
rpm-build a7f80b
	set(SOURCES "")
rpm-build a7f80b
	set(TCOMPILER "")
rpm-build a7f80b
	foreach (FILE ${TSOURCES})
rpm-build a7f80b
		codecov_path_of_source(${FILE} FILE)
rpm-build a7f80b
		if (NOT "${FILE}" STREQUAL "")
rpm-build a7f80b
			codecov_lang_of_source(${FILE} LANG)
rpm-build a7f80b
			if (NOT "${LANG}" STREQUAL "")
rpm-build a7f80b
				list(APPEND SOURCES "${FILE}")
rpm-build a7f80b
				set(TCOMPILER ${CMAKE_${LANG}_COMPILER_ID})
rpm-build a7f80b
			endif ()
rpm-build a7f80b
		endif ()
rpm-build a7f80b
	endforeach ()
rpm-build a7f80b
rpm-build a7f80b
	# If no gcov binary was found, coverage data can't be evaluated.
rpm-build a7f80b
	if (NOT GCOV_${TCOMPILER}_BIN)
rpm-build a7f80b
		message(WARNING "No coverage evaluation binary found for ${TCOMPILER}.")
rpm-build a7f80b
		return()
rpm-build a7f80b
	endif ()
rpm-build a7f80b
rpm-build a7f80b
	set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}")
rpm-build a7f80b
	set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}")
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
	set(TDIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TNAME}.dir)
rpm-build a7f80b
	set(GENINFO_FILES "")
rpm-build a7f80b
	foreach(FILE ${SOURCES})
rpm-build a7f80b
		# generate empty coverage files
rpm-build a7f80b
		set(OUTFILE "${TDIR}/${FILE}.info.init")
rpm-build a7f80b
		list(APPEND GENINFO_FILES ${OUTFILE})
rpm-build a7f80b
rpm-build a7f80b
		add_custom_command(OUTPUT ${OUTFILE} COMMAND ${GCOV_ENV} ${GENINFO_BIN}
rpm-build a7f80b
				--quiet --base-directory ${PROJECT_SOURCE_DIR} --initial
rpm-build a7f80b
				--gcov-tool ${GCOV_BIN} --output-filename ${OUTFILE}
rpm-build a7f80b
				${GENINFO_EXTERN_FLAG} ${TDIR}/${FILE}.gcno
rpm-build a7f80b
			DEPENDS ${TNAME}
rpm-build a7f80b
			COMMENT "Capturing initial coverage data for ${FILE}"
rpm-build a7f80b
		)
rpm-build a7f80b
	endforeach()
rpm-build a7f80b
rpm-build a7f80b
	# Concatenate all files generated by geninfo to a single file per target.
rpm-build a7f80b
	set(OUTFILE "${LCOV_DATA_PATH_INIT}/${TNAME}.info")
rpm-build a7f80b
	set(LCOV_EXTRA_FLAGS "--initial")
rpm-build a7f80b
	lcov_merge_files("${OUTFILE}" ${GENINFO_FILES})
rpm-build a7f80b
	add_custom_target(${TNAME}-capture-init ALL DEPENDS ${OUTFILE})
rpm-build a7f80b
rpm-build a7f80b
	# add geninfo file generation to global lcov-geninfo target
rpm-build a7f80b
	add_dependencies(lcov-capture-init ${TNAME}-capture-init)
rpm-build a7f80b
	set(LCOV_CAPTURE_INIT_FILES "${LCOV_CAPTURE_INIT_FILES}"
rpm-build a7f80b
		"${OUTFILE}" CACHE INTERNAL ""
rpm-build a7f80b
	)
rpm-build a7f80b
endfunction (lcov_capture_initial_tgt)
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
# This function will generate the global info file for all targets. It has to be
rpm-build a7f80b
# called after all other CMake functions in the root CMakeLists.txt file, to get
rpm-build a7f80b
# a full list of all targets that generate coverage data.
rpm-build a7f80b
function (lcov_capture_initial)
rpm-build a7f80b
	# Skip this function (and do not create the following targets), if there are
rpm-build a7f80b
	# no input files.
rpm-build a7f80b
	if ("${LCOV_CAPTURE_INIT_FILES}" STREQUAL "")
rpm-build a7f80b
		return()
rpm-build a7f80b
	endif ()
rpm-build a7f80b
rpm-build a7f80b
	# Add a new target to merge the files of all targets.
rpm-build a7f80b
	set(OUTFILE "${LCOV_DATA_PATH_INIT}/all_targets.info")
rpm-build a7f80b
	lcov_merge_files("${OUTFILE}" ${LCOV_CAPTURE_INIT_FILES})
rpm-build a7f80b
	add_custom_target(lcov-geninfo-init ALL	DEPENDS ${OUTFILE}
rpm-build a7f80b
		lcov-capture-init
rpm-build a7f80b
	)
rpm-build a7f80b
endfunction (lcov_capture_initial)
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
# Add a new global target to generate coverage reports for all targets. This
rpm-build a7f80b
# target will be used to generate the global info file.
rpm-build a7f80b
if (NOT TARGET lcov-capture)
rpm-build a7f80b
	add_custom_target(lcov-capture)
rpm-build a7f80b
	set(LCOV_CAPTURE_FILES "" CACHE INTERNAL "")
rpm-build a7f80b
endif (NOT TARGET lcov-capture)
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
# This function will add capture of coverage data for target <TNAME>, which is
rpm-build a7f80b
# needed to get also data for objects, which were not loaded at execution time.
rpm-build a7f80b
# It will call geninfo for every source file of <TNAME> once and store the info
rpm-build a7f80b
# file in the same directory.
rpm-build a7f80b
function (lcov_capture_tgt TNAME)
rpm-build a7f80b
	# We don't have to check, if the target has support for coverage, thus this
rpm-build a7f80b
	# will be checked by add_coverage_target in Findcoverage.cmake. Instead we
rpm-build a7f80b
	# have to determine which gcov binary to use.
rpm-build a7f80b
	get_target_property(TSOURCES ${TNAME} SOURCES)
rpm-build a7f80b
	set(SOURCES "")
rpm-build a7f80b
	set(TCOMPILER "")
rpm-build a7f80b
	foreach (FILE ${TSOURCES})
rpm-build a7f80b
		codecov_path_of_source(${FILE} FILE)
rpm-build a7f80b
		if (NOT "${FILE}" STREQUAL "")
rpm-build a7f80b
			codecov_lang_of_source(${FILE} LANG)
rpm-build a7f80b
			if (NOT "${LANG}" STREQUAL "")
rpm-build a7f80b
				list(APPEND SOURCES "${FILE}")
rpm-build a7f80b
				set(TCOMPILER ${CMAKE_${LANG}_COMPILER_ID})
rpm-build a7f80b
			endif ()
rpm-build a7f80b
		endif ()
rpm-build a7f80b
	endforeach ()
rpm-build a7f80b
rpm-build a7f80b
	# If no gcov binary was found, coverage data can't be evaluated.
rpm-build a7f80b
	if (NOT GCOV_${TCOMPILER}_BIN)
rpm-build a7f80b
		message(WARNING "No coverage evaluation binary found for ${TCOMPILER}.")
rpm-build a7f80b
		return()
rpm-build a7f80b
	endif ()
rpm-build a7f80b
rpm-build a7f80b
	set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}")
rpm-build a7f80b
	set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}")
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
	set(TDIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TNAME}.dir)
rpm-build a7f80b
	set(GENINFO_FILES "")
rpm-build a7f80b
	foreach(FILE ${SOURCES})
rpm-build a7f80b
		# Generate coverage files. If no .gcda file was generated during
rpm-build a7f80b
		# execution, the empty coverage file will be used instead.
rpm-build a7f80b
		set(OUTFILE "${TDIR}/${FILE}.info")
rpm-build a7f80b
		list(APPEND GENINFO_FILES ${OUTFILE})
rpm-build a7f80b
rpm-build a7f80b
		add_custom_command(OUTPUT ${OUTFILE}
rpm-build a7f80b
			COMMAND test -f "${TDIR}/${FILE}.gcda"
rpm-build a7f80b
				&& ${GCOV_ENV} ${GENINFO_BIN} --quiet --base-directory
rpm-build a7f80b
					${PROJECT_SOURCE_DIR} --gcov-tool ${GCOV_BIN}
rpm-build a7f80b
					--output-filename ${OUTFILE} ${GENINFO_EXTERN_FLAG}
rpm-build a7f80b
					${TDIR}/${FILE}.gcda
rpm-build a7f80b
				|| cp ${OUTFILE}.init ${OUTFILE}
rpm-build a7f80b
			DEPENDS ${TNAME} ${TNAME}-capture-init
rpm-build a7f80b
			COMMENT "Capturing coverage data for ${FILE}"
rpm-build a7f80b
		)
rpm-build a7f80b
	endforeach()
rpm-build a7f80b
rpm-build a7f80b
	# Concatenate all files generated by geninfo to a single file per target.
rpm-build a7f80b
	set(OUTFILE "${LCOV_DATA_PATH_CAPTURE}/${TNAME}.info")
rpm-build a7f80b
	lcov_merge_files("${OUTFILE}" ${GENINFO_FILES})
rpm-build a7f80b
	add_custom_target(${TNAME}-geninfo DEPENDS ${OUTFILE})
rpm-build a7f80b
rpm-build a7f80b
	# add geninfo file generation to global lcov-capture target
rpm-build a7f80b
	add_dependencies(lcov-capture ${TNAME}-geninfo)
rpm-build a7f80b
	set(LCOV_CAPTURE_FILES "${LCOV_CAPTURE_FILES}" "${OUTFILE}" CACHE INTERNAL
rpm-build a7f80b
		""
rpm-build a7f80b
	)
rpm-build a7f80b
rpm-build a7f80b
	# Add target for generating html output for this target only.
rpm-build a7f80b
	file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/${TNAME})
rpm-build a7f80b
	add_custom_target(${TNAME}-genhtml
rpm-build a7f80b
		COMMAND ${GENHTML_BIN} --quiet --sort --prefix ${PROJECT_SOURCE_DIR}
rpm-build a7f80b
			--baseline-file ${LCOV_DATA_PATH_INIT}/${TNAME}.info
rpm-build a7f80b
			--output-directory ${LCOV_HTML_PATH}/${TNAME}
rpm-build a7f80b
			--title "${CMAKE_PROJECT_NAME} - target ${TNAME}"
rpm-build a7f80b
			${GENHTML_CPPFILT_FLAG} ${OUTFILE}
rpm-build a7f80b
		DEPENDS ${TNAME}-geninfo ${TNAME}-capture-init
rpm-build a7f80b
	)
rpm-build a7f80b
endfunction (lcov_capture_tgt)
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
# This function will generate the global info file for all targets. It has to be
rpm-build a7f80b
# called after all other CMake functions in the root CMakeLists.txt file, to get
rpm-build a7f80b
# a full list of all targets that generate coverage data.
rpm-build a7f80b
function (lcov_capture)
rpm-build a7f80b
	# Skip this function (and do not create the following targets), if there are
rpm-build a7f80b
	# no input files.
rpm-build a7f80b
	if ("${LCOV_CAPTURE_FILES}" STREQUAL "")
rpm-build a7f80b
		return()
rpm-build a7f80b
	endif ()
rpm-build a7f80b
rpm-build a7f80b
	# Add a new target to merge the files of all targets.
rpm-build a7f80b
	set(OUTFILE "${LCOV_DATA_PATH_CAPTURE}/all_targets.info")
rpm-build a7f80b
	lcov_merge_files("${OUTFILE}" ${LCOV_CAPTURE_FILES})
rpm-build a7f80b
	add_custom_target(lcov-geninfo DEPENDS ${OUTFILE} lcov-capture)
rpm-build a7f80b
rpm-build a7f80b
	# Add a new global target for all lcov targets. This target could be used to
rpm-build a7f80b
	# generate the lcov html output for the whole project instead of calling
rpm-build a7f80b
	# <TARGET>-geninfo and <TARGET>-genhtml for each target. It will also be
rpm-build a7f80b
	# used to generate a html site for all project data together instead of one
rpm-build a7f80b
	# for each target.
rpm-build a7f80b
	if (NOT TARGET lcov)
rpm-build a7f80b
		file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/all_targets)
rpm-build a7f80b
		add_custom_target(lcov
rpm-build a7f80b
			COMMAND ${GENHTML_BIN} --quiet --sort
rpm-build a7f80b
				--baseline-file ${LCOV_DATA_PATH_INIT}/all_targets.info
rpm-build a7f80b
				--output-directory ${LCOV_HTML_PATH}/all_targets
rpm-build a7f80b
				--title "${CMAKE_PROJECT_NAME}" --prefix "${PROJECT_SOURCE_DIR}"
rpm-build a7f80b
				${GENHTML_CPPFILT_FLAG} ${OUTFILE}
rpm-build a7f80b
			DEPENDS lcov-geninfo-init lcov-geninfo
rpm-build a7f80b
		)
rpm-build a7f80b
	endif ()
rpm-build a7f80b
endfunction (lcov_capture)
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
# Add a new global target to generate the lcov html report for the whole project
rpm-build a7f80b
# instead of calling <TARGET>-genhtml for each target (to create an own report
rpm-build a7f80b
# for each target). Instead of the lcov target it does not require geninfo for
rpm-build a7f80b
# all targets, so you have to call <TARGET>-geninfo to generate the info files
rpm-build a7f80b
# the targets you'd like to have in your report or lcov-geninfo for generating
rpm-build a7f80b
# info files for all targets before calling lcov-genhtml.
rpm-build a7f80b
file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/selected_targets)
rpm-build a7f80b
if (NOT TARGET lcov-genhtml)
rpm-build a7f80b
	add_custom_target(lcov-genhtml
rpm-build a7f80b
		COMMAND ${GENHTML_BIN}
rpm-build a7f80b
			--quiet
rpm-build a7f80b
			--output-directory ${LCOV_HTML_PATH}/selected_targets
rpm-build a7f80b
			--title \"${CMAKE_PROJECT_NAME} - targets  `find
rpm-build a7f80b
				${LCOV_DATA_PATH_CAPTURE} -name \"*.info\" ! -name
rpm-build a7f80b
				\"all_targets.info\" -exec basename {} .info \\\;`\"
rpm-build a7f80b
			--prefix ${PROJECT_SOURCE_DIR}
rpm-build a7f80b
			--sort
rpm-build a7f80b
			${GENHTML_CPPFILT_FLAG}
rpm-build a7f80b
			`find ${LCOV_DATA_PATH_CAPTURE} -name \"*.info\" ! -name
rpm-build a7f80b
				\"all_targets.info\"`
rpm-build a7f80b
	)
rpm-build a7f80b
endif (NOT TARGET lcov-genhtml)