Findcodecov.cmake 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  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. # Add an option to choose, if coverage should be enabled or not. If enabled
  11. # marked targets will be build with coverage support and appropriate targets
  12. # will be added. If disabled coverage will be ignored for *ALL* targets.
  13. option(ENABLE_COVERAGE "Enable coverage build." OFF)
  14. set(COVERAGE_FLAG_CANDIDATES
  15. # gcc and clang
  16. "-O0 -g -fprofile-arcs -ftest-coverage"
  17. # gcc and clang fallback
  18. "-O0 -g --coverage"
  19. )
  20. # Add coverage support for target ${TNAME} and register target for coverage
  21. # evaluation. If coverage is disabled or not supported, this function will
  22. # simply do nothing.
  23. #
  24. # Note: This function is only a wrapper to define this function always, even if
  25. # coverage is not supported by the compiler or disabled. This function must
  26. # be defined here, because the module will be exited, if there is no coverage
  27. # support by the compiler or it is disabled by the user.
  28. function (add_coverage TNAME)
  29. # only add coverage for target, if coverage is support and enabled.
  30. if (ENABLE_COVERAGE)
  31. foreach (TNAME ${ARGV})
  32. add_coverage_target(${TNAME})
  33. endforeach ()
  34. endif ()
  35. endfunction (add_coverage)
  36. # Add global target to gather coverage information after all targets have been
  37. # added. Other evaluation functions could be added here, after checks for the
  38. # specific module have been passed.
  39. #
  40. # Note: This function is only a wrapper to define this function always, even if
  41. # coverage is not supported by the compiler or disabled. This function must
  42. # be defined here, because the module will be exited, if there is no coverage
  43. # support by the compiler or it is disabled by the user.
  44. function (coverage_evaluate)
  45. # add lcov evaluation
  46. if (LCOV_FOUND)
  47. lcov_capture_initial()
  48. lcov_capture()
  49. endif (LCOV_FOUND)
  50. endfunction ()
  51. # Exit this module, if coverage is disabled. add_coverage is defined before this
  52. # return, so this module can be exited now safely without breaking any build-
  53. # scripts.
  54. if (NOT ENABLE_COVERAGE)
  55. return()
  56. endif ()
  57. # Find the reuired flags foreach language.
  58. set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET})
  59. set(CMAKE_REQUIRED_QUIET ${codecov_FIND_QUIETLY})
  60. get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
  61. foreach (LANG ${ENABLED_LANGUAGES})
  62. # Coverage flags are not dependent on language, but the used compiler. So
  63. # instead of searching flags foreach language, search flags foreach compiler
  64. # used.
  65. set(COMPILER ${CMAKE_${LANG}_COMPILER_ID})
  66. if (NOT COVERAGE_${COMPILER}_FLAGS)
  67. foreach (FLAG ${COVERAGE_FLAG_CANDIDATES})
  68. if(NOT CMAKE_REQUIRED_QUIET)
  69. message(STATUS "Try ${COMPILER} code coverage flag = [${FLAG}]")
  70. endif()
  71. set(CMAKE_REQUIRED_FLAGS "${FLAG}")
  72. unset(COVERAGE_FLAG_DETECTED CACHE)
  73. if (${LANG} STREQUAL "C")
  74. include(CheckCCompilerFlag)
  75. check_c_compiler_flag("${FLAG}" COVERAGE_FLAG_DETECTED)
  76. elseif (${LANG} STREQUAL "CXX")
  77. include(CheckCXXCompilerFlag)
  78. check_cxx_compiler_flag("${FLAG}" COVERAGE_FLAG_DETECTED)
  79. elseif (${LANG} STREQUAL "Fortran")
  80. # CheckFortranCompilerFlag was introduced in CMake 3.x. To be
  81. # compatible with older Cmake versions, we will check if this
  82. # module is present before we use it. Otherwise we will define
  83. # Fortran coverage support as not available.
  84. include(CheckFortranCompilerFlag OPTIONAL
  85. RESULT_VARIABLE INCLUDED)
  86. if (INCLUDED)
  87. check_fortran_compiler_flag("${FLAG}"
  88. COVERAGE_FLAG_DETECTED)
  89. elseif (NOT CMAKE_REQUIRED_QUIET)
  90. message("-- Performing Test COVERAGE_FLAG_DETECTED")
  91. message("-- Performing Test COVERAGE_FLAG_DETECTED - Failed"
  92. " (Check not supported)")
  93. endif ()
  94. endif()
  95. if (COVERAGE_FLAG_DETECTED)
  96. set(COVERAGE_${COMPILER}_FLAGS "${FLAG}"
  97. CACHE STRING "${COMPILER} flags for code coverage.")
  98. mark_as_advanced(COVERAGE_${COMPILER}_FLAGS)
  99. break()
  100. else ()
  101. message(WARNING "Code coverage is not available for ${COMPILER}"
  102. " compiler. Targets using this compiler will be "
  103. "compiled without it.")
  104. endif ()
  105. endforeach ()
  106. endif ()
  107. endforeach ()
  108. set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_SAVE})
  109. # Helper function to get the language of a source file.
  110. function (codecov_lang_of_source FILE RETURN_VAR)
  111. get_filename_component(FILE_EXT "${FILE}" EXT)
  112. string(TOLOWER "${FILE_EXT}" FILE_EXT)
  113. string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT)
  114. get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
  115. foreach (LANG ${ENABLED_LANGUAGES})
  116. list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP)
  117. if (NOT ${TEMP} EQUAL -1)
  118. set(${RETURN_VAR} "${LANG}" PARENT_SCOPE)
  119. return()
  120. endif ()
  121. endforeach()
  122. set(${RETURN_VAR} "" PARENT_SCOPE)
  123. endfunction ()
  124. # Helper function to get the relative path of the source file destination path.
  125. # This path is needed by FindGcov and FindLcov cmake files to locate the
  126. # captured data.
  127. function (codecov_path_of_source FILE RETURN_VAR)
  128. string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _source ${FILE})
  129. # If expression was found, SOURCEFILE is a generator-expression for an
  130. # object library. Currently we found no way to call this function automatic
  131. # for the referenced target, so it must be called in the directoryso of the
  132. # object library definition.
  133. if (NOT "${_source}" STREQUAL "")
  134. set(${RETURN_VAR} "" PARENT_SCOPE)
  135. return()
  136. endif ()
  137. string(REPLACE "${CMAKE_CURRENT_BINARY_DIR}/" "" FILE "${FILE}")
  138. if(IS_ABSOLUTE ${FILE})
  139. file(RELATIVE_PATH FILE ${CMAKE_CURRENT_SOURCE_DIR} ${FILE})
  140. endif()
  141. # get the right path for file
  142. string(REPLACE ".." "__" PATH "${FILE}")
  143. set(${RETURN_VAR} "${PATH}" PARENT_SCOPE)
  144. endfunction()
  145. # Add coverage support for target ${TNAME} and register target for coverage
  146. # evaluation.
  147. function(add_coverage_target TNAME)
  148. # Check if all sources for target use the same compiler. If a target uses
  149. # e.g. C and Fortran mixed and uses different compilers (e.g. clang and
  150. # gfortran) this can trigger huge problems, because different compilers may
  151. # use different implementations for code coverage.
  152. get_target_property(TSOURCES ${TNAME} SOURCES)
  153. set(TARGET_COMPILER "")
  154. set(ADDITIONAL_FILES "")
  155. foreach (FILE ${TSOURCES})
  156. # If expression was found, FILE is a generator-expression for an object
  157. # library. Object libraries will be ignored.
  158. string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _file ${FILE})
  159. if ("${_file}" STREQUAL "")
  160. codecov_lang_of_source(${FILE} LANG)
  161. if (LANG)
  162. list(APPEND TARGET_COMPILER ${CMAKE_${LANG}_COMPILER_ID})
  163. list(APPEND ADDITIONAL_FILES "${FILE}.gcno")
  164. list(APPEND ADDITIONAL_FILES "${FILE}.gcda")
  165. endif ()
  166. endif ()
  167. endforeach ()
  168. list(REMOVE_DUPLICATES TARGET_COMPILER)
  169. list(LENGTH TARGET_COMPILER NUM_COMPILERS)
  170. if (NUM_COMPILERS GREATER 1)
  171. message(WARNING "Can't use code coverage for target ${TNAME}, because "
  172. "it will be compiled by incompatible compilers. Target will be "
  173. "compiled without code coverage.")
  174. return()
  175. elseif (NUM_COMPILERS EQUAL 0)
  176. message(WARNING "Can't use code coverage for target ${TNAME}, because "
  177. "it uses an unknown compiler. Target will be compiled without "
  178. "code coverage.")
  179. return()
  180. elseif (NOT DEFINED "COVERAGE_${TARGET_COMPILER}_FLAGS")
  181. # A warning has been printed before, so just return if flags for this
  182. # compiler aren't available.
  183. return()
  184. endif()
  185. # enable coverage for target
  186. set_property(TARGET ${TNAME} APPEND_STRING
  187. PROPERTY COMPILE_FLAGS " ${COVERAGE_${TARGET_COMPILER}_FLAGS}")
  188. set_property(TARGET ${TNAME} APPEND_STRING
  189. PROPERTY LINK_FLAGS " ${COVERAGE_${TARGET_COMPILER}_FLAGS}")
  190. # Add gcov files generated by compiler to clean target.
  191. set(CLEAN_FILES "")
  192. foreach (FILE ${ADDITIONAL_FILES})
  193. codecov_path_of_source(${FILE} FILE)
  194. list(APPEND CLEAN_FILES "CMakeFiles/${TNAME}.dir/${FILE}")
  195. endforeach()
  196. set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES
  197. "${CLEAN_FILES}")
  198. add_gcov_target(${TNAME})
  199. add_lcov_target(${TNAME})
  200. endfunction(add_coverage_target)
  201. # Include modules for parsing the collected data and output it in a readable
  202. # format (like gcov and lcov).
  203. find_package(Gcov)
  204. find_package(Lcov)