A C++ library for logging very fast and without allocating.
Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

680 linhas
32 KiB

  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()