A C++ library for logging very fast and without allocating.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

680 lignes
32 KiB

il y a 2 ans
  1. #
  2. # Copyright (C) 2018-2020 by George Cave - gcave@stablecoder.ca
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. # use this file except in compliance with the License. You may obtain a copy of
  6. # the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. # License for the specific language governing permissions and limitations under
  14. # the License.
  15. # USAGE: To enable any code coverage instrumentation/targets, the single CMake
  16. # option of `CODE_COVERAGE` needs to be set to 'ON', either by GUI, ccmake, or
  17. # on the command line.
  18. #
  19. # From this point, there are two primary methods for adding instrumentation to
  20. # targets: 1 - A blanket instrumentation by calling `add_code_coverage()`, where
  21. # all targets in that directory and all subdirectories are automatically
  22. # instrumented. 2 - Per-target instrumentation by calling
  23. # `target_code_coverage(<TARGET_NAME>)`, where the target is given and thus only
  24. # that target is instrumented. This applies to both libraries and executables.
  25. #
  26. # To add coverage targets, such as calling `make ccov` to generate the actual
  27. # coverage information for perusal or consumption, call
  28. # `target_code_coverage(<TARGET_NAME>)` on an *executable* target.
  29. #
  30. # Example 1: All targets instrumented
  31. #
  32. # In this case, the coverage information reported will will be that of the
  33. # `theLib` library target and `theExe` executable.
  34. #
  35. # 1a: Via global command
  36. #
  37. # ~~~
  38. # add_code_coverage() # Adds instrumentation to all targets
  39. #
  40. # add_library(theLib lib.cpp)
  41. #
  42. # add_executable(theExe main.cpp)
  43. # target_link_libraries(theExe PRIVATE theLib)
  44. # target_code_coverage(theExe) # As an executable target, adds the 'ccov-theExe' target (instrumentation already added via global anyways) for generating code coverage reports.
  45. # ~~~
  46. #
  47. # 1b: Via target commands
  48. #
  49. # ~~~
  50. # add_library(theLib lib.cpp)
  51. # target_code_coverage(theLib) # As a library target, adds coverage instrumentation but no targets.
  52. #
  53. # add_executable(theExe main.cpp)
  54. # target_link_libraries(theExe PRIVATE theLib)
  55. # target_code_coverage(theExe) # As an executable target, adds the 'ccov-theExe' target and instrumentation for generating code coverage reports.
  56. # ~~~
  57. #
  58. # Example 2: Target instrumented, but with regex pattern of files to be excluded
  59. # from report
  60. #
  61. # ~~~
  62. # add_executable(theExe main.cpp non_covered.cpp)
  63. # target_code_coverage(theExe EXCLUDE non_covered.cpp test/*) # As an executable target, the reports will exclude the non-covered.cpp file, and any files in a test/ folder.
  64. # ~~~
  65. #
  66. # Example 3: Target added to the 'ccov' and 'ccov-all' targets
  67. #
  68. # ~~~
  69. # add_code_coverage_all_targets(EXCLUDE test/*) # Adds the 'ccov-all' target set and sets it to exclude all files in test/ folders.
  70. #
  71. # add_executable(theExe main.cpp non_covered.cpp)
  72. # target_code_coverage(theExe AUTO ALL EXCLUDE non_covered.cpp test/*) # As an executable target, adds to the 'ccov' and ccov-all' targets, and the reports will exclude the non-covered.cpp file, and any files in a test/ folder.
  73. # ~~~
  74. # Options
  75. option(
  76. CODE_COVERAGE
  77. "Builds targets with code coverage instrumentation. (Requires GCC or Clang)"
  78. OFF)
  79. # Programs
  80. if(NOT LLVM_COV_PATH)
  81. find_program(LLVM_COV_PATH llvm-cov)
  82. endif()
  83. if(NOT LLVM_PROFDATA_PATH)
  84. find_program(LLVM_PROFDATA_PATH llvm-profdata)
  85. endif()
  86. if(NOT LCOV_PATH)
  87. find_program(LCOV_PATH lcov)
  88. endif()
  89. find_program(GENHTML_PATH genhtml)
  90. # Hide behind the 'advanced' mode flag for GUI/ccmake
  91. mark_as_advanced(FORCE LLVM_COV_PATH LLVM_PROFDATA_PATH LCOV_PATH GENHTML_PATH)
  92. # Variables
  93. set(CMAKE_COVERAGE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/ccov)
  94. set_property(GLOBAL PROPERTY JOB_POOLS ccov_serial_pool=1)
  95. # Common initialization/checks
  96. if(CODE_COVERAGE AND NOT CODE_COVERAGE_ADDED)
  97. set(CODE_COVERAGE_ADDED ON)
  98. # Common Targets
  99. add_custom_target(
  100. ccov-preprocessing
  101. COMMAND ${CMAKE_COMMAND} -E make_directory
  102. ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}
  103. DEPENDS ccov-clean)
  104. if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
  105. OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
  106. # Messages
  107. message(STATUS "Building with llvm Code Coverage Tools")
  108. if(NOT LLVM_COV_PATH)
  109. message(FATAL_ERROR "llvm-cov not found! Aborting.")
  110. else()
  111. # Version number checking for 'EXCLUDE' compatibility
  112. execute_process(COMMAND ${LLVM_COV_PATH} --version
  113. OUTPUT_VARIABLE LLVM_COV_VERSION_CALL_OUTPUT)
  114. string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" LLVM_COV_VERSION
  115. ${LLVM_COV_VERSION_CALL_OUTPUT})
  116. if(LLVM_COV_VERSION VERSION_LESS "7.0.0")
  117. message(
  118. WARNING
  119. "target_code_coverage()/add_code_coverage_all_targets() 'EXCLUDE' option only available on llvm-cov >= 7.0.0"
  120. )
  121. endif()
  122. endif()
  123. # Targets
  124. if(${CMAKE_VERSION} VERSION_LESS "3.17.0")
  125. add_custom_target(
  126. ccov-clean
  127. COMMAND ${CMAKE_COMMAND} -E remove -f
  128. ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list
  129. COMMAND ${CMAKE_COMMAND} -E remove -f
  130. ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list)
  131. else()
  132. add_custom_target(
  133. ccov-clean
  134. COMMAND ${CMAKE_COMMAND} -E rm -f
  135. ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list
  136. COMMAND ${CMAKE_COMMAND} -E rm -f
  137. ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list)
  138. endif()
  139. # Used to get the shared object file list before doing the main all-
  140. # processing
  141. add_custom_target(
  142. ccov-libs
  143. COMMAND ;
  144. COMMENT "libs ready for coverage report.")
  145. elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES
  146. "GNU")
  147. # Messages
  148. message(STATUS "Building with lcov Code Coverage Tools")
  149. if(CMAKE_BUILD_TYPE)
  150. string(TOUPPER ${CMAKE_BUILD_TYPE} upper_build_type)
  151. if(NOT ${upper_build_type} STREQUAL "DEBUG")
  152. message(
  153. WARNING
  154. "Code coverage results with an optimized (non-Debug) build may be misleading"
  155. )
  156. endif()
  157. else()
  158. message(
  159. WARNING
  160. "Code coverage results with an optimized (non-Debug) build may be misleading"
  161. )
  162. endif()
  163. if(NOT LCOV_PATH)
  164. message(FATAL_ERROR "lcov not found! Aborting...")
  165. endif()
  166. if(NOT GENHTML_PATH)
  167. message(FATAL_ERROR "genhtml not found! Aborting...")
  168. endif()
  169. # Targets
  170. add_custom_target(ccov-clean COMMAND ${LCOV_PATH} --directory
  171. ${CMAKE_BINARY_DIR} --zerocounters)
  172. else()
  173. message(FATAL_ERROR "Code coverage requires Clang or GCC. Aborting.")
  174. endif()
  175. endif()
  176. # Adds code coverage instrumentation to a library, or instrumentation/targets
  177. # for an executable target.
  178. # ~~~
  179. # EXECUTABLE ADDED TARGETS:
  180. # GCOV/LCOV:
  181. # ccov : Generates HTML code coverage report for every target added with 'AUTO' parameter.
  182. # ccov-${TARGET_NAME} : Generates HTML code coverage report for the associated named target.
  183. # ccov-all : Generates HTML code coverage report, merging every target added with 'ALL' parameter into a single detailed report.
  184. #
  185. # LLVM-COV:
  186. # ccov : Generates HTML code coverage report for every target added with 'AUTO' parameter.
  187. # ccov-report : Generates HTML code coverage report for every target added with 'AUTO' parameter.
  188. # ccov-${TARGET_NAME} : Generates HTML code coverage report.
  189. # ccov-report-${TARGET_NAME} : Prints to command line summary per-file coverage information.
  190. # ccov-export-${TARGET_NAME} : Exports the coverage report to a JSON file.
  191. # ccov-show-${TARGET_NAME} : Prints to command line detailed per-line coverage information.
  192. # ccov-all : Generates HTML code coverage report, merging every target added with 'ALL' parameter into a single detailed report.
  193. # ccov-all-report : Prints summary per-file coverage information for every target added with ALL' parameter to the command line.
  194. # ccov-all-export : Exports the coverage report to a JSON file.
  195. #
  196. # Required:
  197. # TARGET_NAME - Name of the target to generate code coverage for.
  198. # Optional:
  199. # PUBLIC - Sets the visibility for added compile options to targets to PUBLIC instead of the default of PRIVATE.
  200. # INTERFACE - Sets the visibility for added compile options to targets to INTERFACE instead of the default of PRIVATE.
  201. # PLAIN - Do not set any target visibility (backward compatibility with old cmake projects)
  202. # AUTO - Adds the target to the 'ccov' target so that it can be run in a batch with others easily. Effective on executable targets.
  203. # ALL - Adds the target to the 'ccov-all' and 'ccov-all-report' targets, which merge several executable targets coverage data to a single report. Effective on executable targets.
  204. # EXTERNAL - For GCC's lcov, allows the profiling of 'external' files from the processing directory
  205. # COVERAGE_TARGET_NAME - For executables ONLY, changes the outgoing target name so instead of `ccov-${TARGET_NAME}` it becomes `ccov-${COVERAGE_TARGET_NAME}`.
  206. # EXCLUDE <PATTERNS> - Excludes files of the patterns provided from coverage. Note that GCC/lcov excludes by glob pattern, and clang/LLVM excludes via regex! **These do not copy to the 'all' targets.**
  207. # OBJECTS <TARGETS> - For executables ONLY, if the provided targets are shared libraries, adds coverage information to the output
  208. # ARGS <ARGUMENTS> - For executables ONLY, appends the given arguments to the associated ccov-* executable call
  209. # ~~~
  210. function(target_code_coverage TARGET_NAME)
  211. # Argument parsing
  212. set(options AUTO ALL EXTERNAL PUBLIC INTERFACE PLAIN)
  213. set(single_value_keywords COVERAGE_TARGET_NAME)
  214. set(multi_value_keywords EXCLUDE OBJECTS ARGS)
  215. cmake_parse_arguments(
  216. target_code_coverage "${options}" "${single_value_keywords}"
  217. "${multi_value_keywords}" ${ARGN})
  218. # Set the visibility of target functions to PUBLIC, INTERFACE or default to
  219. # PRIVATE.
  220. if(target_code_coverage_PUBLIC)
  221. set(TARGET_VISIBILITY PUBLIC)
  222. set(TARGET_LINK_VISIBILITY PUBLIC)
  223. elseif(target_code_coverage_INTERFACE)
  224. set(TARGET_VISIBILITY INTERFACE)
  225. set(TARGET_LINK_VISIBILITY INTERFACE)
  226. elseif(target_code_coverage_PLAIN)
  227. set(TARGET_VISIBILITY PUBLIC)
  228. set(TARGET_LINK_VISIBILITY)
  229. else()
  230. set(TARGET_VISIBILITY PRIVATE)
  231. set(TARGET_LINK_VISIBILITY PRIVATE)
  232. endif()
  233. if(NOT target_code_coverage_COVERAGE_TARGET_NAME)
  234. # If a specific name was given, use that instead.
  235. set(target_code_coverage_COVERAGE_TARGET_NAME ${TARGET_NAME})
  236. endif()
  237. if(CODE_COVERAGE)
  238. # Add code coverage instrumentation to the target's linker command
  239. if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
  240. OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
  241. target_compile_options(${TARGET_NAME} ${TARGET_VISIBILITY}
  242. -fprofile-instr-generate -fcoverage-mapping)
  243. target_link_options(${TARGET_NAME} ${TARGET_VISIBILITY}
  244. -fprofile-instr-generate -fcoverage-mapping)
  245. elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES
  246. "GNU")
  247. target_compile_options(${TARGET_NAME} ${TARGET_VISIBILITY} -fprofile-arcs
  248. -ftest-coverage)
  249. target_link_libraries(${TARGET_NAME} ${TARGET_LINK_VISIBILITY} gcov)
  250. endif()
  251. # Targets
  252. get_target_property(target_type ${TARGET_NAME} TYPE)
  253. # Add shared library to processing for 'all' targets
  254. if(target_type STREQUAL "SHARED_LIBRARY" AND target_code_coverage_ALL)
  255. if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
  256. OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
  257. add_custom_target(
  258. ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}
  259. COMMAND
  260. ${CMAKE_COMMAND} -E echo "-object=$<TARGET_FILE:${TARGET_NAME}>" >>
  261. ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list
  262. DEPENDS ccov-preprocessing ${TARGET_NAME})
  263. if(NOT TARGET ccov-libs)
  264. message(
  265. FATAL_ERROR
  266. "Calling target_code_coverage with 'ALL' must be after a call to 'add_code_coverage_all_targets'."
  267. )
  268. endif()
  269. add_dependencies(ccov-libs
  270. ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME})
  271. endif()
  272. endif()
  273. # For executables add targets to run and produce output
  274. if(target_type STREQUAL "EXECUTABLE")
  275. if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
  276. OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
  277. # If there are shared objects to also work with, generate the string to
  278. # add them here
  279. foreach(SO_TARGET ${target_code_coverage_OBJECTS})
  280. # Check to see if the target is a shared object
  281. if(TARGET ${SO_TARGET})
  282. get_target_property(SO_TARGET_TYPE ${SO_TARGET} TYPE)
  283. if(${SO_TARGET_TYPE} STREQUAL "SHARED_LIBRARY")
  284. set(SO_OBJECTS ${SO_OBJECTS} -object=$<TARGET_FILE:${SO_TARGET}>)
  285. endif()
  286. endif()
  287. endforeach()
  288. # Run the executable, generating raw profile data Make the run data
  289. # available for further processing. Separated to allow Windows to run
  290. # this target serially.
  291. add_custom_target(
  292. ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}
  293. COMMAND
  294. ${CMAKE_COMMAND} -E env
  295. LLVM_PROFILE_FILE=${target_code_coverage_COVERAGE_TARGET_NAME}.profraw
  296. $<TARGET_FILE:${TARGET_NAME}> ${target_code_coverage_ARGS}
  297. COMMAND
  298. ${CMAKE_COMMAND} -E echo "-object=$<TARGET_FILE:${TARGET_NAME}>"
  299. ${SO_OBJECTS} >> ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list
  300. COMMAND
  301. ${CMAKE_COMMAND} -E echo
  302. "${CMAKE_CURRENT_BINARY_DIR}/${target_code_coverage_COVERAGE_TARGET_NAME}.profraw"
  303. >> ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list
  304. JOB_POOL ccov_serial_pool
  305. DEPENDS ccov-preprocessing ccov-libs ${TARGET_NAME})
  306. # Merge the generated profile data so llvm-cov can process it
  307. add_custom_target(
  308. ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}
  309. COMMAND
  310. ${LLVM_PROFDATA_PATH} merge -sparse
  311. ${target_code_coverage_COVERAGE_TARGET_NAME}.profraw -o
  312. ${target_code_coverage_COVERAGE_TARGET_NAME}.profdata
  313. DEPENDS ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME})
  314. # Ignore regex only works on LLVM >= 7
  315. if(LLVM_COV_VERSION VERSION_GREATER_EQUAL "7.0.0")
  316. foreach(EXCLUDE_ITEM ${target_code_coverage_EXCLUDE})
  317. set(EXCLUDE_REGEX ${EXCLUDE_REGEX}
  318. -ignore-filename-regex='${EXCLUDE_ITEM}')
  319. endforeach()
  320. endif()
  321. # Print out details of the coverage information to the command line
  322. add_custom_target(
  323. ccov-show-${target_code_coverage_COVERAGE_TARGET_NAME}
  324. COMMAND
  325. ${LLVM_COV_PATH} show $<TARGET_FILE:${TARGET_NAME}> ${SO_OBJECTS}
  326. -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata
  327. -show-line-counts-or-regions ${EXCLUDE_REGEX}
  328. DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME})
  329. # Print out a summary of the coverage information to the command line
  330. add_custom_target(
  331. ccov-report-${target_code_coverage_COVERAGE_TARGET_NAME}
  332. COMMAND
  333. ${LLVM_COV_PATH} report $<TARGET_FILE:${TARGET_NAME}> ${SO_OBJECTS}
  334. -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata
  335. ${EXCLUDE_REGEX}
  336. DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME})
  337. # Export coverage information so continuous integration tools (e.g.
  338. # Jenkins) can consume it
  339. add_custom_target(
  340. ccov-export-${target_code_coverage_COVERAGE_TARGET_NAME}
  341. COMMAND
  342. ${LLVM_COV_PATH} export $<TARGET_FILE:${TARGET_NAME}> ${SO_OBJECTS}
  343. -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata
  344. -format="text" ${EXCLUDE_REGEX} >
  345. ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}.json
  346. DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME})
  347. # Generates HTML output of the coverage information for perusal
  348. add_custom_target(
  349. ccov-${target_code_coverage_COVERAGE_TARGET_NAME}
  350. COMMAND
  351. ${LLVM_COV_PATH} show $<TARGET_FILE:${TARGET_NAME}> ${SO_OBJECTS}
  352. -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata
  353. -show-line-counts-or-regions
  354. -output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}
  355. -format="html" ${EXCLUDE_REGEX}
  356. DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME})
  357. elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES
  358. "GNU")
  359. set(COVERAGE_INFO
  360. "${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}.info"
  361. )
  362. # Run the executable, generating coverage information
  363. add_custom_target(
  364. ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}
  365. COMMAND $<TARGET_FILE:${TARGET_NAME}> ${target_code_coverage_ARGS}
  366. DEPENDS ccov-preprocessing ${TARGET_NAME})
  367. # Generate exclusion string for use
  368. foreach(EXCLUDE_ITEM ${target_code_coverage_EXCLUDE})
  369. set(EXCLUDE_REGEX ${EXCLUDE_REGEX} --remove ${COVERAGE_INFO}
  370. '${EXCLUDE_ITEM}')
  371. endforeach()
  372. if(EXCLUDE_REGEX)
  373. set(EXCLUDE_COMMAND ${LCOV_PATH} ${EXCLUDE_REGEX} --output-file
  374. ${COVERAGE_INFO})
  375. else()
  376. set(EXCLUDE_COMMAND ;)
  377. endif()
  378. if(NOT ${target_code_coverage_EXTERNAL})
  379. set(EXTERNAL_OPTION --no-external)
  380. endif()
  381. # Capture coverage data
  382. if(${CMAKE_VERSION} VERSION_LESS "3.17.0")
  383. add_custom_target(
  384. ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME}
  385. COMMAND ${CMAKE_COMMAND} -E remove -f ${COVERAGE_INFO}
  386. COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --zerocounters
  387. COMMAND $<TARGET_FILE:${TARGET_NAME}> ${target_code_coverage_ARGS}
  388. COMMAND
  389. ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --base-directory
  390. ${CMAKE_SOURCE_DIR} --capture ${EXTERNAL_OPTION} --output-file
  391. ${COVERAGE_INFO}
  392. COMMAND ${EXCLUDE_COMMAND}
  393. DEPENDS ccov-preprocessing ${TARGET_NAME})
  394. else()
  395. add_custom_target(
  396. ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME}
  397. COMMAND ${CMAKE_COMMAND} -E rm -f ${COVERAGE_INFO}
  398. COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --zerocounters
  399. COMMAND $<TARGET_FILE:${TARGET_NAME}> ${target_code_coverage_ARGS}
  400. COMMAND
  401. ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --base-directory
  402. ${CMAKE_SOURCE_DIR} --capture ${EXTERNAL_OPTION} --output-file
  403. ${COVERAGE_INFO}
  404. COMMAND ${EXCLUDE_COMMAND}
  405. DEPENDS ccov-preprocessing ${TARGET_NAME})
  406. endif()
  407. # Generates HTML output of the coverage information for perusal
  408. add_custom_target(
  409. ccov-${target_code_coverage_COVERAGE_TARGET_NAME}
  410. COMMAND
  411. ${GENHTML_PATH} -o
  412. ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}
  413. ${COVERAGE_INFO}
  414. DEPENDS ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME})
  415. endif()
  416. add_custom_command(
  417. TARGET ccov-${target_code_coverage_COVERAGE_TARGET_NAME}
  418. POST_BUILD
  419. COMMAND ;
  420. COMMENT
  421. "Open ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}/index.html in your browser to view the coverage report."
  422. )
  423. # AUTO
  424. if(target_code_coverage_AUTO)
  425. if(NOT TARGET ccov)
  426. add_custom_target(ccov)
  427. endif()
  428. add_dependencies(ccov ccov-${target_code_coverage_COVERAGE_TARGET_NAME})
  429. if(NOT CMAKE_C_COMPILER_ID MATCHES "GNU" AND NOT CMAKE_CXX_COMPILER_ID
  430. MATCHES "GNU")
  431. if(NOT TARGET ccov-report)
  432. add_custom_target(ccov-report)
  433. endif()
  434. add_dependencies(
  435. ccov-report
  436. ccov-report-${target_code_coverage_COVERAGE_TARGET_NAME})
  437. endif()
  438. endif()
  439. # ALL
  440. if(target_code_coverage_ALL)
  441. if(NOT TARGET ccov-all-processing)
  442. message(
  443. FATAL_ERROR
  444. "Calling target_code_coverage with 'ALL' must be after a call to 'add_code_coverage_all_targets'."
  445. )
  446. endif()
  447. add_dependencies(ccov-all-processing
  448. ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME})
  449. endif()
  450. endif()
  451. endif()
  452. endfunction()
  453. # Adds code coverage instrumentation to all targets in the current directory and
  454. # any subdirectories. To add coverage instrumentation to only specific targets,
  455. # use `target_code_coverage`.
  456. function(add_code_coverage)
  457. if(CODE_COVERAGE)
  458. if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
  459. OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
  460. add_compile_options(-fprofile-instr-generate -fcoverage-mapping)
  461. add_link_options(-fprofile-instr-generate -fcoverage-mapping)
  462. elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES
  463. "GNU")
  464. add_compile_options(-fprofile-arcs -ftest-coverage)
  465. link_libraries(gcov)
  466. endif()
  467. endif()
  468. endfunction()
  469. # Adds the 'ccov-all' type targets that calls all targets added via
  470. # `target_code_coverage` with the `ALL` parameter, but merges all the coverage
  471. # data from them into a single large report instead of the numerous smaller
  472. # reports. Also adds the ccov-all-capture Generates an all-merged.info file, for
  473. # use with coverage dashboards (e.g. codecov.io, coveralls).
  474. # ~~~
  475. # Optional:
  476. # EXCLUDE <PATTERNS> - Excludes files of the patterns provided from coverage. Note that GCC/lcov excludes by glob pattern, and clang/LLVM excludes via regex!
  477. # ~~~
  478. function(add_code_coverage_all_targets)
  479. # Argument parsing
  480. set(multi_value_keywords EXCLUDE)
  481. cmake_parse_arguments(add_code_coverage_all_targets "" ""
  482. "${multi_value_keywords}" ${ARGN})
  483. if(CODE_COVERAGE)
  484. if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
  485. OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
  486. # Merge the profile data for all of the run executables
  487. if(WIN32)
  488. add_custom_target(
  489. ccov-all-processing
  490. COMMAND
  491. powershell -Command $$FILELIST = Get-Content
  492. ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list\; llvm-profdata.exe
  493. merge -o ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
  494. -sparse $$FILELIST)
  495. else()
  496. add_custom_target(
  497. ccov-all-processing
  498. COMMAND
  499. ${LLVM_PROFDATA_PATH} merge -o
  500. ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata -sparse `cat
  501. ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list`)
  502. endif()
  503. # Regex exclude only available for LLVM >= 7
  504. if(LLVM_COV_VERSION VERSION_GREATER_EQUAL "7.0.0")
  505. foreach(EXCLUDE_ITEM ${add_code_coverage_all_targets_EXCLUDE})
  506. set(EXCLUDE_REGEX ${EXCLUDE_REGEX}
  507. -ignore-filename-regex='${EXCLUDE_ITEM}')
  508. endforeach()
  509. endif()
  510. # Print summary of the code coverage information to the command line
  511. if(WIN32)
  512. add_custom_target(
  513. ccov-all-report
  514. COMMAND
  515. powershell -Command $$FILELIST = Get-Content
  516. ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list\; llvm-cov.exe
  517. report $$FILELIST
  518. -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
  519. ${EXCLUDE_REGEX}
  520. DEPENDS ccov-all-processing)
  521. else()
  522. add_custom_target(
  523. ccov-all-report
  524. COMMAND
  525. ${LLVM_COV_PATH} report `cat
  526. ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list`
  527. -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
  528. ${EXCLUDE_REGEX}
  529. DEPENDS ccov-all-processing)
  530. endif()
  531. # Export coverage information so continuous integration tools (e.g.
  532. # Jenkins) can consume it
  533. add_custom_target(
  534. ccov-all-export
  535. COMMAND
  536. ${LLVM_COV_PATH} export `cat
  537. ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list`
  538. -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
  539. -format="text" ${EXCLUDE_REGEX} >
  540. ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/coverage.json
  541. DEPENDS ccov-all-processing)
  542. # Generate HTML output of all added targets for perusal
  543. if(WIN32)
  544. add_custom_target(
  545. ccov-all
  546. COMMAND
  547. powershell -Command $$FILELIST = Get-Content
  548. ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list\; llvm-cov.exe show
  549. $$FILELIST
  550. -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
  551. -show-line-counts-or-regions
  552. -output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged
  553. -format="html" ${EXCLUDE_REGEX}
  554. DEPENDS ccov-all-processing)
  555. else()
  556. add_custom_target(
  557. ccov-all
  558. COMMAND
  559. ${LLVM_COV_PATH} show `cat
  560. ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list`
  561. -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
  562. -show-line-counts-or-regions
  563. -output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged
  564. -format="html" ${EXCLUDE_REGEX}
  565. DEPENDS ccov-all-processing)
  566. endif()
  567. elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES
  568. "GNU")
  569. set(COVERAGE_INFO "${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.info")
  570. # Nothing required for gcov
  571. add_custom_target(ccov-all-processing COMMAND ;)
  572. # Exclusion regex string creation
  573. set(EXCLUDE_REGEX)
  574. foreach(EXCLUDE_ITEM ${add_code_coverage_all_targets_EXCLUDE})
  575. set(EXCLUDE_REGEX ${EXCLUDE_REGEX} --remove ${COVERAGE_INFO}
  576. '${EXCLUDE_ITEM}')
  577. endforeach()
  578. if(EXCLUDE_REGEX)
  579. set(EXCLUDE_COMMAND ${LCOV_PATH} ${EXCLUDE_REGEX} --output-file
  580. ${COVERAGE_INFO})
  581. else()
  582. set(EXCLUDE_COMMAND ;)
  583. endif()
  584. # Capture coverage data
  585. if(${CMAKE_VERSION} VERSION_LESS "3.17.0")
  586. add_custom_target(
  587. ccov-all-capture
  588. COMMAND ${CMAKE_COMMAND} -E remove -f ${COVERAGE_INFO}
  589. COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --capture
  590. --output-file ${COVERAGE_INFO}
  591. COMMAND ${EXCLUDE_COMMAND}
  592. DEPENDS ccov-preprocessing ccov-all-processing)
  593. else()
  594. add_custom_target(
  595. ccov-all-capture
  596. COMMAND ${CMAKE_COMMAND} -E rm -f ${COVERAGE_INFO}
  597. COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --capture
  598. --output-file ${COVERAGE_INFO}
  599. COMMAND ${EXCLUDE_COMMAND}
  600. DEPENDS ccov-preprocessing ccov-all-processing)
  601. endif()
  602. # Generates HTML output of all targets for perusal
  603. add_custom_target(
  604. ccov-all
  605. COMMAND ${GENHTML_PATH} -o ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged
  606. ${COVERAGE_INFO} -p ${CMAKE_SOURCE_DIR}
  607. DEPENDS ccov-all-capture)
  608. endif()
  609. add_custom_command(
  610. TARGET ccov-all
  611. POST_BUILD
  612. COMMAND ;
  613. COMMENT
  614. "Open ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged/index.html in your browser to view the coverage report."
  615. )
  616. endif()
  617. endfunction()