FindLcov.cmake 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  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. # configuration
  11. set(LCOV_DATA_PATH "${CMAKE_BINARY_DIR}/lcov/data")
  12. set(LCOV_DATA_PATH_INIT "${LCOV_DATA_PATH}/init")
  13. set(LCOV_DATA_PATH_CAPTURE "${LCOV_DATA_PATH}/capture")
  14. set(LCOV_HTML_PATH "${CMAKE_BINARY_DIR}/lcov/html")
  15. # Search for Gcov which is used by Lcov.
  16. find_package(Gcov)
  17. # This function will add lcov evaluation for target <TNAME>. Only sources of
  18. # this target will be evaluated and no dependencies will be added. It will call
  19. # geninfo on any source file of <TNAME> once and store the info file in the same
  20. # directory.
  21. #
  22. # Note: This function is only a wrapper to define this function always, even if
  23. # coverage is not supported by the compiler or disabled. This function must
  24. # be defined here, because the module will be exited, if there is no coverage
  25. # support by the compiler or it is disabled by the user.
  26. function (add_lcov_target TNAME)
  27. if (LCOV_FOUND)
  28. # capture initial coverage data
  29. lcov_capture_initial_tgt(${TNAME})
  30. # capture coverage data after execution
  31. lcov_capture_tgt(${TNAME})
  32. endif ()
  33. endfunction (add_lcov_target)
  34. # include required Modules
  35. include(FindPackageHandleStandardArgs)
  36. # Search for required lcov binaries.
  37. find_program(LCOV_BIN lcov)
  38. find_program(GENINFO_BIN geninfo)
  39. find_program(GENHTML_BIN genhtml)
  40. find_package_handle_standard_args(lcov
  41. REQUIRED_VARS LCOV_BIN GENINFO_BIN GENHTML_BIN
  42. )
  43. # enable genhtml C++ demangeling, if c++filt is found.
  44. set(GENHTML_CPPFILT_FLAG "")
  45. find_program(CPPFILT_BIN c++filt)
  46. if (NOT CPPFILT_BIN STREQUAL "")
  47. set(GENHTML_CPPFILT_FLAG "--demangle-cpp")
  48. endif (NOT CPPFILT_BIN STREQUAL "")
  49. # enable no-external flag for lcov, if available.
  50. if (GENINFO_BIN AND NOT DEFINED GENINFO_EXTERN_FLAG)
  51. set(FLAG "")
  52. execute_process(COMMAND ${GENINFO_BIN} --help OUTPUT_VARIABLE GENINFO_HELP)
  53. string(REGEX MATCH "external" GENINFO_RES "${GENINFO_HELP}")
  54. if (GENINFO_RES)
  55. set(FLAG "--no-external")
  56. endif ()
  57. set(GENINFO_EXTERN_FLAG "${FLAG}"
  58. CACHE STRING "Geninfo flag to exclude system sources.")
  59. endif ()
  60. # If Lcov was not found, exit module now.
  61. if (NOT LCOV_FOUND)
  62. return()
  63. endif (NOT LCOV_FOUND)
  64. # Create directories to be used.
  65. file(MAKE_DIRECTORY ${LCOV_DATA_PATH_INIT})
  66. file(MAKE_DIRECTORY ${LCOV_DATA_PATH_CAPTURE})
  67. set(LCOV_REMOVE_PATTERNS "")
  68. # This function will merge lcov files to a single target file. Additional lcov
  69. # flags may be set with setting LCOV_EXTRA_FLAGS before calling this function.
  70. function (lcov_merge_files OUTFILE ...)
  71. # Remove ${OUTFILE} from ${ARGV} and generate lcov parameters with files.
  72. list(REMOVE_AT ARGV 0)
  73. # Generate merged file.
  74. string(REPLACE "${CMAKE_BINARY_DIR}/" "" FILE_REL "${OUTFILE}")
  75. add_custom_command(OUTPUT "${OUTFILE}.raw"
  76. COMMAND cat ${ARGV} > ${OUTFILE}.raw
  77. DEPENDS ${ARGV}
  78. COMMENT "Generating ${FILE_REL}"
  79. )
  80. add_custom_command(OUTPUT "${OUTFILE}"
  81. COMMAND ${LCOV_BIN} --quiet -a ${OUTFILE}.raw --output-file ${OUTFILE}
  82. --base-directory ${PROJECT_SOURCE_DIR} ${LCOV_EXTRA_FLAGS}
  83. COMMAND ${LCOV_BIN} --quiet -r ${OUTFILE} ${LCOV_REMOVE_PATTERNS}
  84. --output-file ${OUTFILE} ${LCOV_EXTRA_FLAGS}
  85. DEPENDS ${OUTFILE}.raw
  86. COMMENT "Post-processing ${FILE_REL}"
  87. )
  88. endfunction ()
  89. # Add a new global target to generate initial coverage reports for all targets.
  90. # This target will be used to generate the global initial info file, which is
  91. # used to gather even empty report data.
  92. if (NOT TARGET lcov-capture-init)
  93. add_custom_target(lcov-capture-init)
  94. set(LCOV_CAPTURE_INIT_FILES "" CACHE INTERNAL "")
  95. endif (NOT TARGET lcov-capture-init)
  96. # This function will add initial capture of coverage data for target <TNAME>,
  97. # which is needed to get also data for objects, which were not loaded at
  98. # execution time. It will call geninfo for every source file of <TNAME> once and
  99. # store the info file in the same directory.
  100. function (lcov_capture_initial_tgt TNAME)
  101. # We don't have to check, if the target has support for coverage, thus this
  102. # will be checked by add_coverage_target in Findcoverage.cmake. Instead we
  103. # have to determine which gcov binary to use.
  104. get_target_property(TSOURCES ${TNAME} SOURCES)
  105. set(SOURCES "")
  106. set(TCOMPILER "")
  107. foreach (FILE ${TSOURCES})
  108. codecov_path_of_source(${FILE} FILE)
  109. if (NOT "${FILE}" STREQUAL "")
  110. codecov_lang_of_source(${FILE} LANG)
  111. if (NOT "${LANG}" STREQUAL "")
  112. list(APPEND SOURCES "${FILE}")
  113. set(TCOMPILER ${CMAKE_${LANG}_COMPILER_ID})
  114. endif ()
  115. endif ()
  116. endforeach ()
  117. # If no gcov binary was found, coverage data can't be evaluated.
  118. if (NOT GCOV_${TCOMPILER}_BIN)
  119. message(WARNING "No coverage evaluation binary found for ${TCOMPILER}.")
  120. return()
  121. endif ()
  122. set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}")
  123. set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}")
  124. set(TDIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TNAME}.dir)
  125. set(GENINFO_FILES "")
  126. foreach(FILE ${SOURCES})
  127. # generate empty coverage files
  128. set(OUTFILE "${TDIR}/${FILE}.info.init")
  129. list(APPEND GENINFO_FILES ${OUTFILE})
  130. add_custom_command(OUTPUT ${OUTFILE} COMMAND ${GCOV_ENV} ${GENINFO_BIN}
  131. --quiet --base-directory ${PROJECT_SOURCE_DIR} --initial
  132. --gcov-tool ${GCOV_BIN} --output-filename ${OUTFILE}
  133. ${GENINFO_EXTERN_FLAG} ${TDIR}/${FILE}.gcno
  134. DEPENDS ${TNAME}
  135. COMMENT "Capturing initial coverage data for ${FILE}"
  136. )
  137. endforeach()
  138. # Concatenate all files generated by geninfo to a single file per target.
  139. set(OUTFILE "${LCOV_DATA_PATH_INIT}/${TNAME}.info")
  140. set(LCOV_EXTRA_FLAGS "--initial")
  141. lcov_merge_files("${OUTFILE}" ${GENINFO_FILES})
  142. add_custom_target(${TNAME}-capture-init ALL DEPENDS ${OUTFILE})
  143. # add geninfo file generation to global lcov-geninfo target
  144. add_dependencies(lcov-capture-init ${TNAME}-capture-init)
  145. set(LCOV_CAPTURE_INIT_FILES "${LCOV_CAPTURE_INIT_FILES}"
  146. "${OUTFILE}" CACHE INTERNAL ""
  147. )
  148. endfunction (lcov_capture_initial_tgt)
  149. # This function will generate the global info file for all targets. It has to be
  150. # called after all other CMake functions in the root CMakeLists.txt file, to get
  151. # a full list of all targets that generate coverage data.
  152. function (lcov_capture_initial)
  153. # Skip this function (and do not create the following targets), if there are
  154. # no input files.
  155. if ("${LCOV_CAPTURE_INIT_FILES}" STREQUAL "")
  156. return()
  157. endif ()
  158. # Add a new target to merge the files of all targets.
  159. set(OUTFILE "${LCOV_DATA_PATH_INIT}/all_targets.info")
  160. lcov_merge_files("${OUTFILE}" ${LCOV_CAPTURE_INIT_FILES})
  161. add_custom_target(lcov-geninfo-init ALL DEPENDS ${OUTFILE}
  162. lcov-capture-init
  163. )
  164. endfunction (lcov_capture_initial)
  165. # Add a new global target to generate coverage reports for all targets. This
  166. # target will be used to generate the global info file.
  167. if (NOT TARGET lcov-capture)
  168. add_custom_target(lcov-capture)
  169. set(LCOV_CAPTURE_FILES "" CACHE INTERNAL "")
  170. endif (NOT TARGET lcov-capture)
  171. # This function will add capture of coverage data for target <TNAME>, which is
  172. # needed to get also data for objects, which were not loaded at execution time.
  173. # It will call geninfo for every source file of <TNAME> once and store the info
  174. # file in the same directory.
  175. function (lcov_capture_tgt TNAME)
  176. # We don't have to check, if the target has support for coverage, thus this
  177. # will be checked by add_coverage_target in Findcoverage.cmake. Instead we
  178. # have to determine which gcov binary to use.
  179. get_target_property(TSOURCES ${TNAME} SOURCES)
  180. set(SOURCES "")
  181. set(TCOMPILER "")
  182. foreach (FILE ${TSOURCES})
  183. codecov_path_of_source(${FILE} FILE)
  184. if (NOT "${FILE}" STREQUAL "")
  185. codecov_lang_of_source(${FILE} LANG)
  186. if (NOT "${LANG}" STREQUAL "")
  187. list(APPEND SOURCES "${FILE}")
  188. set(TCOMPILER ${CMAKE_${LANG}_COMPILER_ID})
  189. endif ()
  190. endif ()
  191. endforeach ()
  192. # If no gcov binary was found, coverage data can't be evaluated.
  193. if (NOT GCOV_${TCOMPILER}_BIN)
  194. message(WARNING "No coverage evaluation binary found for ${TCOMPILER}.")
  195. return()
  196. endif ()
  197. set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}")
  198. set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}")
  199. set(TDIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TNAME}.dir)
  200. set(GENINFO_FILES "")
  201. foreach(FILE ${SOURCES})
  202. # Generate coverage files. If no .gcda file was generated during
  203. # execution, the empty coverage file will be used instead.
  204. set(OUTFILE "${TDIR}/${FILE}.info")
  205. list(APPEND GENINFO_FILES ${OUTFILE})
  206. add_custom_command(OUTPUT ${OUTFILE}
  207. COMMAND test -f "${TDIR}/${FILE}.gcda"
  208. && ${GCOV_ENV} ${GENINFO_BIN} --quiet --base-directory
  209. ${PROJECT_SOURCE_DIR} --gcov-tool ${GCOV_BIN}
  210. --output-filename ${OUTFILE} ${GENINFO_EXTERN_FLAG}
  211. ${TDIR}/${FILE}.gcda
  212. || cp ${OUTFILE}.init ${OUTFILE}
  213. DEPENDS ${TNAME} ${TNAME}-capture-init
  214. COMMENT "Capturing coverage data for ${FILE}"
  215. )
  216. endforeach()
  217. # Concatenate all files generated by geninfo to a single file per target.
  218. set(OUTFILE "${LCOV_DATA_PATH_CAPTURE}/${TNAME}.info")
  219. lcov_merge_files("${OUTFILE}" ${GENINFO_FILES})
  220. add_custom_target(${TNAME}-geninfo DEPENDS ${OUTFILE})
  221. # add geninfo file generation to global lcov-capture target
  222. add_dependencies(lcov-capture ${TNAME}-geninfo)
  223. set(LCOV_CAPTURE_FILES "${LCOV_CAPTURE_FILES}" "${OUTFILE}" CACHE INTERNAL
  224. ""
  225. )
  226. # Add target for generating html output for this target only.
  227. file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/${TNAME})
  228. add_custom_target(${TNAME}-genhtml
  229. COMMAND ${GENHTML_BIN} --quiet --sort --prefix ${PROJECT_SOURCE_DIR}
  230. --baseline-file ${LCOV_DATA_PATH_INIT}/${TNAME}.info
  231. --output-directory ${LCOV_HTML_PATH}/${TNAME}
  232. --title "${CMAKE_PROJECT_NAME} - target ${TNAME}"
  233. ${GENHTML_CPPFILT_FLAG} ${OUTFILE}
  234. DEPENDS ${TNAME}-geninfo ${TNAME}-capture-init
  235. )
  236. endfunction (lcov_capture_tgt)
  237. # This function will generate the global info file for all targets. It has to be
  238. # called after all other CMake functions in the root CMakeLists.txt file, to get
  239. # a full list of all targets that generate coverage data.
  240. function (lcov_capture)
  241. # Skip this function (and do not create the following targets), if there are
  242. # no input files.
  243. if ("${LCOV_CAPTURE_FILES}" STREQUAL "")
  244. return()
  245. endif ()
  246. # Add a new target to merge the files of all targets.
  247. set(OUTFILE "${LCOV_DATA_PATH_CAPTURE}/all_targets.info")
  248. lcov_merge_files("${OUTFILE}" ${LCOV_CAPTURE_FILES})
  249. add_custom_target(lcov-geninfo DEPENDS ${OUTFILE} lcov-capture)
  250. # Add a new global target for all lcov targets. This target could be used to
  251. # generate the lcov html output for the whole project instead of calling
  252. # <TARGET>-geninfo and <TARGET>-genhtml for each target. It will also be
  253. # used to generate a html site for all project data together instead of one
  254. # for each target.
  255. if (NOT TARGET lcov)
  256. file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/all_targets)
  257. add_custom_target(lcov
  258. COMMAND ${GENHTML_BIN} --quiet --sort
  259. --baseline-file ${LCOV_DATA_PATH_INIT}/all_targets.info
  260. --output-directory ${LCOV_HTML_PATH}/all_targets
  261. --title "${CMAKE_PROJECT_NAME}" --prefix "${PROJECT_SOURCE_DIR}"
  262. ${GENHTML_CPPFILT_FLAG} ${OUTFILE}
  263. DEPENDS lcov-geninfo-init lcov-geninfo
  264. )
  265. endif ()
  266. endfunction (lcov_capture)
  267. # Add a new global target to generate the lcov html report for the whole project
  268. # instead of calling <TARGET>-genhtml for each target (to create an own report
  269. # for each target). Instead of the lcov target it does not require geninfo for
  270. # all targets, so you have to call <TARGET>-geninfo to generate the info files
  271. # the targets you'd like to have in your report or lcov-geninfo for generating
  272. # info files for all targets before calling lcov-genhtml.
  273. file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/selected_targets)
  274. if (NOT TARGET lcov-genhtml)
  275. add_custom_target(lcov-genhtml
  276. COMMAND ${GENHTML_BIN}
  277. --quiet
  278. --output-directory ${LCOV_HTML_PATH}/selected_targets
  279. --title \"${CMAKE_PROJECT_NAME} - targets `find
  280. ${LCOV_DATA_PATH_CAPTURE} -name \"*.info\" ! -name
  281. \"all_targets.info\" -exec basename {} .info \\\;`\"
  282. --prefix ${PROJECT_SOURCE_DIR}
  283. --sort
  284. ${GENHTML_CPPFILT_FLAG}
  285. `find ${LCOV_DATA_PATH_CAPTURE} -name \"*.info\" ! -name
  286. \"all_targets.info\"`
  287. )
  288. endif (NOT TARGET lcov-genhtml)