summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMAKE/FindGcov.cmake157
-rw-r--r--CMAKE/Findcodecov.cmake256
2 files changed, 413 insertions, 0 deletions
diff --git a/CMAKE/FindGcov.cmake b/CMAKE/FindGcov.cmake
new file mode 100644
index 00000000..715da6ed
--- /dev/null
+++ b/CMAKE/FindGcov.cmake
@@ -0,0 +1,157 @@
+# This file is part of CMake-codecov.
+#
+# Copyright (c)
+# 2015-2016 RWTH Aachen University, Federal Republic of Germany
+#
+# See the LICENSE file in the package base directory for details
+#
+# Written by Alexander Haase, alexander.haase@rwth-aachen.de
+#
+
+
+# include required Modules
+include(FindPackageHandleStandardArgs)
+
+
+# Search for gcov binary.
+set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET})
+set(CMAKE_REQUIRED_QUIET ${codecov_FIND_QUIETLY})
+
+get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
+foreach (LANG ${ENABLED_LANGUAGES})
+ # Gcov evaluation is dependend on the used compiler. Check gcov support for
+ # each compiler that is used. If gcov binary was already found for this
+ # compiler, do not try to find it again.
+ if (NOT GCOV_${CMAKE_${LANG}_COMPILER_ID}_BIN)
+ get_filename_component(COMPILER_PATH "${CMAKE_${LANG}_COMPILER}" PATH)
+
+ if ("${CMAKE_${LANG}_COMPILER_ID}" STREQUAL "GNU")
+ # Some distributions like OSX (homebrew) ship gcov with the compiler
+ # version appended as gcov-x. To find this binary we'll build the
+ # suggested binary name with the compiler version.
+ string(REGEX MATCH "^[0-9]+" GCC_VERSION
+ "${CMAKE_${LANG}_COMPILER_VERSION}")
+
+ find_program(GCOV_BIN NAMES gcov-${GCC_VERSION} gcov
+ HINTS ${COMPILER_PATH})
+
+ elseif ("${CMAKE_${LANG}_COMPILER_ID}" STREQUAL "Clang")
+ # Some distributions like Debian ship llvm-cov with the compiler
+ # version appended as llvm-cov-x.y. To find this binary we'll build
+ # the suggested binary name with the compiler version.
+ string(REGEX MATCH "^[0-9]+.[0-9]+" LLVM_VERSION
+ "${CMAKE_${LANG}_COMPILER_VERSION}")
+
+ # llvm-cov prior version 3.5 seems to be not working with coverage
+ # evaluation tools, but these versions are compatible with the gcc
+ # gcov tool.
+ if(LLVM_VERSION VERSION_GREATER 3.4)
+ find_program(LLVM_COV_BIN NAMES "llvm-cov-${LLVM_VERSION}"
+ "llvm-cov" HINTS ${COMPILER_PATH})
+ mark_as_advanced(LLVM_COV_BIN)
+
+ if (LLVM_COV_BIN)
+ find_program(LLVM_COV_WRAPPER "llvm-cov-wrapper" PATHS
+ ${CMAKE_MODULE_PATH})
+ if (LLVM_COV_WRAPPER)
+ set(GCOV_BIN "${LLVM_COV_WRAPPER}" CACHE FILEPATH "")
+
+ # set additional parameters
+ set(GCOV_${CMAKE_${LANG}_COMPILER_ID}_ENV
+ "LLVM_COV_BIN=${LLVM_COV_BIN}" CACHE STRING
+ "Environment variables for llvm-cov-wrapper.")
+ mark_as_advanced(GCOV_${CMAKE_${LANG}_COMPILER_ID}_ENV)
+ endif ()
+ endif ()
+ endif ()
+
+ if (NOT GCOV_BIN)
+ # Fall back to gcov binary if llvm-cov was not found or is
+ # incompatible. This is the default on OSX, but may crash on
+ # recent Linux versions.
+ find_program(GCOV_BIN gcov HINTS ${COMPILER_PATH})
+ endif ()
+ endif ()
+
+
+ if (GCOV_BIN)
+ set(GCOV_${CMAKE_${LANG}_COMPILER_ID}_BIN "${GCOV_BIN}" CACHE STRING
+ "${LANG} gcov binary.")
+
+ if (NOT CMAKE_REQUIRED_QUIET)
+ message("-- Found gcov evaluation for "
+ "${CMAKE_${LANG}_COMPILER_ID}: ${GCOV_BIN}")
+ endif()
+
+ unset(GCOV_BIN CACHE)
+ endif ()
+ endif ()
+endforeach ()
+
+
+
+
+# Add a new global target for all gcov targets. This target could be used to
+# generate the gcov files for the whole project instead of calling <TARGET>-gcov
+# for each target.
+if (NOT TARGET gcov)
+ add_custom_target(gcov)
+endif (NOT TARGET gcov)
+
+
+
+# This function will add gcov evaluation for target <TNAME>. Only sources of
+# this target will be evaluated and no dependencies will be added. It will call
+# Gcov on any source file of <TNAME> once and store the gcov file in the same
+# directory.
+function (add_gcov_target TNAME)
+ set(TDIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TNAME}.dir)
+
+ # We don't have to check, if the target has support for coverage, thus this
+ # will be checked by add_coverage_target in Findcoverage.cmake. Instead we
+ # have to determine which gcov binary to use.
+ get_target_property(TSOURCES ${TNAME} SOURCES)
+ set(SOURCES "")
+ set(TCOMPILER "")
+ foreach (FILE ${TSOURCES})
+ codecov_path_of_source(${FILE} FILE)
+ if (NOT "${FILE}" STREQUAL "")
+ codecov_lang_of_source(${FILE} LANG)
+ if (NOT "${LANG}" STREQUAL "")
+ list(APPEND SOURCES "${FILE}")
+ set(TCOMPILER ${CMAKE_${LANG}_COMPILER_ID})
+ endif ()
+ endif ()
+ endforeach ()
+
+ # If no gcov binary was found, coverage data can't be evaluated.
+ if (NOT GCOV_${TCOMPILER}_BIN)
+ message(WARNING "No coverage evaluation binary found for ${TCOMPILER}.")
+ return()
+ endif ()
+
+ set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}")
+ set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}")
+
+
+ set(BUFFER "")
+ foreach(FILE ${SOURCES})
+ get_filename_component(FILE_PATH "${TDIR}/${FILE}" PATH)
+
+ # call gcov
+ add_custom_command(OUTPUT ${TDIR}/${FILE}.gcov
+ COMMAND ${GCOV_ENV} ${GCOV_BIN} ${TDIR}/${FILE}.gcno > /dev/null
+ DEPENDS ${TNAME} ${TDIR}/${FILE}.gcno
+ WORKING_DIRECTORY ${FILE_PATH}
+ )
+
+ list(APPEND BUFFER ${TDIR}/${FILE}.gcov)
+ endforeach()
+
+
+ # add target for gcov evaluation of <TNAME>
+ add_custom_target(${TNAME}-gcov DEPENDS ${BUFFER})
+
+ # add evaluation target to the global gcov target.
+ add_dependencies(gcov ${TNAME}-gcov)
+endfunction (add_gcov_target)
diff --git a/CMAKE/Findcodecov.cmake b/CMAKE/Findcodecov.cmake
new file mode 100644
index 00000000..11250784
--- /dev/null
+++ b/CMAKE/Findcodecov.cmake
@@ -0,0 +1,256 @@
+# This file is part of CMake-codecov.
+#
+# Copyright (c)
+# 2015-2016 RWTH Aachen University, Federal Republic of Germany
+#
+# See the LICENSE file in the package base directory for details
+#
+# Written by Alexander Haase, alexander.haase@rwth-aachen.de
+#
+
+
+# Add an option to choose, if coverage should be enabled or not. If enabled
+# marked targets will be build with coverage support and appropriate targets
+# will be added. If disabled coverage will be ignored for *ALL* targets.
+option(ENABLE_COVERAGE "Enable coverage build." OFF)
+
+set(COVERAGE_FLAG_CANDIDATES
+ # gcc and clang
+ "-O0 -g -fprofile-arcs -ftest-coverage"
+
+ # gcc and clang fallback
+ "-O0 -g --coverage"
+)
+
+
+# To avoid error messages about CMP0051, this policy will be set to new. There
+# will be no problem, as TARGET_OBJECTS generator expressions will be filtered
+# with a regular expression from the sources.
+if (POLICY CMP0051)
+ cmake_policy(SET CMP0051 NEW)
+endif()
+
+
+# Add coverage support for target ${TNAME} and register target for coverage
+# evaluation. If coverage is disabled or not supported, this function will
+# simply do nothing.
+#
+# Note: This function is only a wrapper to define this function always, even if
+# coverage is not supported by the compiler or disabled. This function must
+# be defined here, because the module will be exited, if there is no coverage
+# support by the compiler or it is disabled by the user.
+function (add_coverage TNAME)
+ # only add coverage for target, if coverage is support and enabled.
+ if (ENABLE_COVERAGE)
+ foreach (TNAME ${ARGV})
+ add_coverage_target(${TNAME})
+ endforeach ()
+ endif ()
+endfunction (add_coverage)
+
+
+# Add global target to gather coverage information after all targets have been
+# added. Other evaluation functions could be added here, after checks for the
+# specific module have been passed.
+#
+# Note: This function is only a wrapper to define this function always, even if
+# coverage is not supported by the compiler or disabled. This function must
+# be defined here, because the module will be exited, if there is no coverage
+# support by the compiler or it is disabled by the user.
+function (coverage_evaluate)
+ # add lcov evaluation
+ if (LCOV_FOUND)
+ lcov_capture_initial()
+ lcov_capture()
+ endif (LCOV_FOUND)
+endfunction ()
+
+
+# Exit this module, if coverage is disabled. add_coverage is defined before this
+# return, so this module can be exited now safely without breaking any build-
+# scripts.
+if (NOT ENABLE_COVERAGE)
+ return()
+endif ()
+
+
+
+
+# Find the reuired flags foreach language.
+set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET})
+set(CMAKE_REQUIRED_QUIET ${codecov_FIND_QUIETLY})
+
+get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
+foreach (LANG ${ENABLED_LANGUAGES})
+ # Coverage flags are not dependend on language, but the used compiler. So
+ # instead of searching flags foreach language, search flags foreach compiler
+ # used.
+ set(COMPILER ${CMAKE_${LANG}_COMPILER_ID})
+ if (NOT COVERAGE_${COMPILER}_FLAGS)
+ foreach (FLAG ${COVERAGE_FLAG_CANDIDATES})
+ if(NOT CMAKE_REQUIRED_QUIET)
+ message(STATUS "Try ${COMPILER} code coverage flag = [${FLAG}]")
+ endif()
+
+ set(CMAKE_REQUIRED_FLAGS "${FLAG}")
+ unset(COVERAGE_FLAG_DETECTED CACHE)
+
+ if (${LANG} STREQUAL "C")
+ include(CheckCCompilerFlag)
+ check_c_compiler_flag("${FLAG}" COVERAGE_FLAG_DETECTED)
+
+ elseif (${LANG} STREQUAL "CXX")
+ include(CheckCXXCompilerFlag)
+ check_cxx_compiler_flag("${FLAG}" COVERAGE_FLAG_DETECTED)
+
+ elseif (${LANG} STREQUAL "Fortran")
+ # CheckFortranCompilerFlag was introduced in CMake 3.x. To be
+ # compatible with older Cmake versions, we will check if this
+ # module is present before we use it. Otherwise we will define
+ # Fortran coverage support as not available.
+ include(CheckFortranCompilerFlag OPTIONAL
+ RESULT_VARIABLE INCLUDED)
+ if (INCLUDED)
+ check_fortran_compiler_flag("${FLAG}"
+ COVERAGE_FLAG_DETECTED)
+ elseif (NOT CMAKE_REQUIRED_QUIET)
+ message("-- Performing Test COVERAGE_FLAG_DETECTED")
+ message("-- Performing Test COVERAGE_FLAG_DETECTED - Failed"
+ " (Check not supported)")
+ endif ()
+ endif()
+
+ if (COVERAGE_FLAG_DETECTED)
+ set(COVERAGE_${COMPILER}_FLAGS "${FLAG}"
+ CACHE STRING "${COMPILER} flags for code coverage.")
+ mark_as_advanced(COVERAGE_${COMPILER}_FLAGS)
+ break()
+ endif ()
+ endforeach ()
+ endif ()
+endforeach ()
+
+set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_SAVE})
+
+
+
+
+# Helper function to get the language of a source file.
+function (codecov_lang_of_source FILE RETURN_VAR)
+ get_filename_component(FILE_EXT "${FILE}" EXT)
+ string(TOLOWER "${FILE_EXT}" FILE_EXT)
+ string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT)
+
+ get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
+ foreach (LANG ${ENABLED_LANGUAGES})
+ list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP)
+ if (NOT ${TEMP} EQUAL -1)
+ set(${RETURN_VAR} "${LANG}" PARENT_SCOPE)
+ return()
+ endif ()
+ endforeach()
+
+ set(${RETURN_VAR} "" PARENT_SCOPE)
+endfunction ()
+
+
+# Helper function to get the relative path of the source file destination path.
+# This path is needed by FindGcov and FindLcov cmake files to locate the
+# captured data.
+function (codecov_path_of_source FILE RETURN_VAR)
+ string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _source ${FILE})
+
+ # If expression was found, SOURCEFILE is a generator-expression for an
+ # object library. Currently we found no way to call this function automatic
+ # for the referenced target, so it must be called in the directoryso of the
+ # object library definition.
+ if (NOT "${_source}" STREQUAL "")
+ set(${RETURN_VAR} "" PARENT_SCOPE)
+ return()
+ endif ()
+
+
+ string(REPLACE "${CMAKE_CURRENT_BINARY_DIR}/" "" FILE "${FILE}")
+ if(IS_ABSOLUTE ${FILE})
+ file(RELATIVE_PATH FILE ${CMAKE_CURRENT_SOURCE_DIR} ${FILE})
+ endif()
+
+ # get the right path for file
+ string(REPLACE ".." "__" PATH "${FILE}")
+
+ set(${RETURN_VAR} "${PATH}" PARENT_SCOPE)
+endfunction()
+
+
+
+
+# Add coverage support for target ${TNAME} and register target for coverage
+# evaluation.
+function(add_coverage_target TNAME)
+ # Check if all sources for target use the same compiler. If a target uses
+ # e.g. C and Fortran mixed and uses different compilers (e.g. clang and
+ # gfortran) this can trigger huge problems, because different compilers may
+ # use different implementations for code coverage.
+ get_target_property(TSOURCES ${TNAME} SOURCES)
+ set(TARGET_COMPILER "")
+ set(ADDITIONAL_FILES "")
+ foreach (FILE ${TSOURCES})
+ # If expression was found, FILE is a generator-expression for an object
+ # library. Object libraries will be ignored.
+ string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _file ${FILE})
+ if ("${_file}" STREQUAL "")
+ codecov_lang_of_source(${FILE} LANG)
+ if (LANG)
+ list(APPEND TARGET_COMPILER ${CMAKE_${LANG}_COMPILER_ID})
+
+ list(APPEND ADDITIONAL_FILES "${FILE}.gcno")
+ list(APPEND ADDITIONAL_FILES "${FILE}.gcda")
+ endif ()
+ endif ()
+ endforeach ()
+
+ list(REMOVE_DUPLICATES TARGET_COMPILER)
+ list(LENGTH TARGET_COMPILER NUM_COMPILERS)
+
+ if (NUM_COMPILERS GREATER 1)
+ message(AUTHOR_WARNING "Coverage disabled for target ${TNAME} because "
+ "it will be compiled by different compilers.")
+ return()
+
+ elseif ((NUM_COMPILERS EQUAL 0) OR
+ (NOT DEFINED "COVERAGE_${TARGET_COMPILER}_FLAGS"))
+ message(AUTHOR_WARNING "Coverage disabled for target ${TNAME} "
+ "because there is no sanitizer available for target sources.")
+ return()
+ endif()
+
+
+ # enable coverage for target
+ set_property(TARGET ${TNAME} APPEND_STRING
+ PROPERTY COMPILE_FLAGS " ${COVERAGE_${TARGET_COMPILER}_FLAGS}")
+ set_property(TARGET ${TNAME} APPEND_STRING
+ PROPERTY LINK_FLAGS " ${COVERAGE_${TARGET_COMPILER}_FLAGS}")
+
+
+ # Add gcov files generated by compiler to clean target.
+ set(CLEAN_FILES "")
+ foreach (FILE ${ADDITIONAL_FILES})
+ codecov_path_of_source(${FILE} FILE)
+ list(APPEND CLEAN_FILES "CMakeFiles/${TNAME}.dir/${FILE}")
+ endforeach()
+
+ set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES
+ "${CLEAN_FILES}")
+
+
+ add_gcov_target(${TNAME})
+ add_lcov_target(${TNAME})
+endfunction(add_coverage_target)
+
+
+
+
+# Include modules for parsing the collected data and output it in a readable
+# format (like gcov and lcov).
+find_package(Gcov)
+find_package(Lcov)