CoverallsGenerateGcov.cmake 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. #
  2. # The MIT License (MIT)
  3. #
  4. # Permission is hereby granted, free of charge, to any person obtaining a copy
  5. # of this software and associated documentation files (the "Software"), to deal
  6. # in the Software without restriction, including without limitation the rights
  7. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. # copies of the Software, and to permit persons to whom the Software is
  9. # furnished to do so, subject to the following conditions:
  10. #
  11. # The above copyright notice and this permission notice shall be included in all
  12. # copies or substantial portions of the Software.
  13. #
  14. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. # SOFTWARE.
  21. #
  22. # Copyright (C) 2014 Joakim Söderberg <[email protected]>
  23. #
  24. # This is intended to be run by a custom target in a CMake project like this.
  25. # 0. Compile program with coverage support.
  26. # 1. Clear coverage data. (Recursively delete *.gcda in build dir)
  27. # 2. Run the unit tests.
  28. # 3. Run this script specifying which source files the coverage should be performed on.
  29. #
  30. # This script will then use gcov to generate .gcov files in the directory specified
  31. # via the COV_PATH var. This should probably be the same as your cmake build dir.
  32. #
  33. # It then parses the .gcov files to convert them into the Coveralls JSON format:
  34. # https://coveralls.io/docs/api
  35. #
  36. # Example for running as standalone CMake script from the command line:
  37. # (Note it is important the -P is at the end...)
  38. # $ cmake -DCOV_PATH=$(pwd)
  39. # -DCOVERAGE_SRCS="catcierge_rfid.c;catcierge_timer.c"
  40. # -P ../cmake/CoverallsGcovUpload.cmake
  41. #
  42. CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
  43. #
  44. # Make sure we have the needed arguments.
  45. #
  46. if (NOT COVERALLS_OUTPUT_FILE)
  47. message(FATAL_ERROR "Coveralls: No coveralls output file specified. Please set COVERALLS_OUTPUT_FILE")
  48. endif()
  49. if (NOT COV_PATH)
  50. message(FATAL_ERROR "Coveralls: Missing coverage directory path where gcov files will be generated. Please set COV_PATH")
  51. endif()
  52. if (NOT COVERAGE_SRCS)
  53. message(FATAL_ERROR "Coveralls: Missing the list of source files that we should get the coverage data for COVERAGE_SRCS")
  54. endif()
  55. if (NOT PROJECT_ROOT)
  56. message(FATAL_ERROR "Coveralls: Missing PROJECT_ROOT.")
  57. endif()
  58. # Since it's not possible to pass a CMake list properly in the
  59. # "1;2;3" format to an external process, we have replaced the
  60. # ";" with "*", so reverse that here so we get it back into the
  61. # CMake list format.
  62. string(REGEX REPLACE "\\*" ";" COVERAGE_SRCS ${COVERAGE_SRCS})
  63. if (NOT DEFINED ENV{GCOV})
  64. find_program(GCOV_EXECUTABLE gcov)
  65. else()
  66. find_program(GCOV_EXECUTABLE $ENV{GCOV})
  67. endif()
  68. # convert all paths in COVERAGE_SRCS to absolute paths
  69. set(COVERAGE_SRCS_TMP "")
  70. foreach (COVERAGE_SRC ${COVERAGE_SRCS})
  71. if (NOT "${COVERAGE_SRC}" MATCHES "^/")
  72. set(COVERAGE_SRC ${PROJECT_ROOT}/${COVERAGE_SRC})
  73. endif()
  74. list(APPEND COVERAGE_SRCS_TMP ${COVERAGE_SRC})
  75. endforeach()
  76. set(COVERAGE_SRCS ${COVERAGE_SRCS_TMP})
  77. unset(COVERAGE_SRCS_TMP)
  78. if (NOT GCOV_EXECUTABLE)
  79. message(FATAL_ERROR "gcov not found! Aborting...")
  80. endif()
  81. find_package(Git)
  82. set(JSON_REPO_TEMPLATE
  83. "{
  84. \"head\": {
  85. \"id\": \"\@GIT_COMMIT_HASH\@\",
  86. \"author_name\": \"\@GIT_AUTHOR_NAME\@\",
  87. \"author_email\": \"\@GIT_AUTHOR_EMAIL\@\",
  88. \"committer_name\": \"\@GIT_COMMITTER_NAME\@\",
  89. \"committer_email\": \"\@GIT_COMMITTER_EMAIL\@\",
  90. \"message\": \"\@GIT_COMMIT_MESSAGE\@\"
  91. },
  92. \"branch\": \"@GIT_BRANCH@\",
  93. \"remotes\": []
  94. }"
  95. )
  96. # TODO: Fill in git remote data
  97. if (GIT_FOUND)
  98. # Branch.
  99. execute_process(
  100. COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD
  101. WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
  102. OUTPUT_VARIABLE GIT_BRANCH
  103. OUTPUT_STRIP_TRAILING_WHITESPACE
  104. )
  105. macro (git_log_format FORMAT_CHARS VAR_NAME)
  106. execute_process(
  107. COMMAND ${GIT_EXECUTABLE} log -1 --pretty=format:%${FORMAT_CHARS}
  108. WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
  109. OUTPUT_VARIABLE ${VAR_NAME}
  110. OUTPUT_STRIP_TRAILING_WHITESPACE
  111. )
  112. endmacro()
  113. git_log_format(an GIT_AUTHOR_NAME)
  114. git_log_format(ae GIT_AUTHOR_EMAIL)
  115. git_log_format(cn GIT_COMMITTER_NAME)
  116. git_log_format(ce GIT_COMMITTER_EMAIL)
  117. git_log_format(B GIT_COMMIT_MESSAGE)
  118. git_log_format(H GIT_COMMIT_HASH)
  119. if(GIT_COMMIT_MESSAGE)
  120. string(REPLACE "\n" "\\n" GIT_COMMIT_MESSAGE ${GIT_COMMIT_MESSAGE})
  121. endif()
  122. message("Git exe: ${GIT_EXECUTABLE}")
  123. message("Git branch: ${GIT_BRANCH}")
  124. message("Git author: ${GIT_AUTHOR_NAME}")
  125. message("Git e-mail: ${GIT_AUTHOR_EMAIL}")
  126. message("Git commiter name: ${GIT_COMMITTER_NAME}")
  127. message("Git commiter e-mail: ${GIT_COMMITTER_EMAIL}")
  128. message("Git commit hash: ${GIT_COMMIT_HASH}")
  129. message("Git commit message: ${GIT_COMMIT_MESSAGE}")
  130. string(CONFIGURE ${JSON_REPO_TEMPLATE} JSON_REPO_DATA)
  131. else()
  132. set(JSON_REPO_DATA "{}")
  133. endif()
  134. ############################# Macros #########################################
  135. #
  136. # This macro converts from the full path format gcov outputs:
  137. #
  138. # /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
  139. #
  140. # to the original source file path the .gcov is for:
  141. #
  142. # /path/to/project/root/subdir/the_file.c
  143. #
  144. macro(get_source_path_from_gcov_filename _SRC_FILENAME _GCOV_FILENAME)
  145. # /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
  146. # ->
  147. # #path#to#project#root#subdir#the_file.c.gcov
  148. get_filename_component(_GCOV_FILENAME_WEXT ${_GCOV_FILENAME} NAME)
  149. # #path#to#project#root#subdir#the_file.c.gcov -> /path/to/project/root/subdir/the_file.c
  150. string(REGEX REPLACE "\\.gcov$" "" SRC_FILENAME_TMP ${_GCOV_FILENAME_WEXT})
  151. string(REGEX REPLACE "\#" "/" SRC_FILENAME_TMP ${SRC_FILENAME_TMP})
  152. set(${_SRC_FILENAME} "${SRC_FILENAME_TMP}")
  153. endmacro()
  154. ##############################################################################
  155. # Get the coverage data.
  156. file(GLOB_RECURSE GCDA_FILES "${COV_PATH}/*.gcda")
  157. message("GCDA files:")
  158. # Get a list of all the object directories needed by gcov
  159. # (The directories the .gcda files and .o files are found in)
  160. # and run gcov on those.
  161. foreach(GCDA ${GCDA_FILES})
  162. message("Process: ${GCDA}")
  163. message("------------------------------------------------------------------------------")
  164. get_filename_component(GCDA_DIR ${GCDA} PATH)
  165. #
  166. # The -p below refers to "Preserve path components",
  167. # This means that the generated gcov filename of a source file will
  168. # keep the original files entire filepath, but / is replaced with #.
  169. # Example:
  170. #
  171. # /path/to/project/root/build/CMakeFiles/the_file.dir/subdir/the_file.c.gcda
  172. # ------------------------------------------------------------------------------
  173. # File '/path/to/project/root/subdir/the_file.c'
  174. # Lines executed:68.34% of 199
  175. # /path/to/project/root/subdir/the_file.c:creating '#path#to#project#root#subdir#the_file.c.gcov'
  176. #
  177. # If -p is not specified then the file is named only "the_file.c.gcov"
  178. #
  179. execute_process(
  180. COMMAND ${GCOV_EXECUTABLE} -p -o ${GCDA_DIR} ${GCDA}
  181. WORKING_DIRECTORY ${COV_PATH}
  182. )
  183. endforeach()
  184. # TODO: Make these be absolute path
  185. file(GLOB ALL_GCOV_FILES ${COV_PATH}/*.gcov)
  186. # Get only the filenames to use for filtering.
  187. #set(COVERAGE_SRCS_NAMES "")
  188. #foreach (COVSRC ${COVERAGE_SRCS})
  189. # get_filename_component(COVSRC_NAME ${COVSRC} NAME)
  190. # message("${COVSRC} -> ${COVSRC_NAME}")
  191. # list(APPEND COVERAGE_SRCS_NAMES "${COVSRC_NAME}")
  192. #endforeach()
  193. #
  194. # Filter out all but the gcov files we want.
  195. #
  196. # We do this by comparing the list of COVERAGE_SRCS filepaths that the
  197. # user wants the coverage data for with the paths of the generated .gcov files,
  198. # so that we only keep the relevant gcov files.
  199. #
  200. # Example:
  201. # COVERAGE_SRCS =
  202. # /path/to/project/root/subdir/the_file.c
  203. #
  204. # ALL_GCOV_FILES =
  205. # /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
  206. # /path/to/project/root/build/#path#to#project#root#subdir#other_file.c.gcov
  207. #
  208. # Result should be:
  209. # GCOV_FILES =
  210. # /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
  211. #
  212. set(GCOV_FILES "")
  213. #message("Look in coverage sources: ${COVERAGE_SRCS}")
  214. message("\nFilter out unwanted GCOV files:")
  215. message("===============================")
  216. set(COVERAGE_SRCS_REMAINING ${COVERAGE_SRCS})
  217. foreach (GCOV_FILE ${ALL_GCOV_FILES})
  218. #
  219. # /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
  220. # ->
  221. # /path/to/project/root/subdir/the_file.c
  222. get_source_path_from_gcov_filename(GCOV_SRC_PATH ${GCOV_FILE})
  223. file(RELATIVE_PATH GCOV_SRC_REL_PATH "${PROJECT_ROOT}" "${GCOV_SRC_PATH}")
  224. # Is this in the list of source files?
  225. # TODO: We want to match against relative path filenames from the source file root...
  226. list(FIND COVERAGE_SRCS ${GCOV_SRC_PATH} WAS_FOUND)
  227. if (NOT WAS_FOUND EQUAL -1)
  228. message("YES: ${GCOV_FILE}")
  229. list(APPEND GCOV_FILES ${GCOV_FILE})
  230. # We remove it from the list, so we don't bother searching for it again.
  231. # Also files left in COVERAGE_SRCS_REMAINING after this loop ends should
  232. # have coverage data generated from them (no lines are covered).
  233. list(REMOVE_ITEM COVERAGE_SRCS_REMAINING ${GCOV_SRC_PATH})
  234. else()
  235. message("NO: ${GCOV_FILE}")
  236. endif()
  237. endforeach()
  238. # TODO: Enable setting these
  239. set(JSON_SERVICE_NAME "travis-ci")
  240. set(JSON_SERVICE_JOB_ID $ENV{TRAVIS_JOB_ID})
  241. set(JSON_REPO_TOKEN $ENV{COVERALLS_REPO_TOKEN})
  242. set(JSON_TEMPLATE
  243. "{
  244. \"repo_token\": \"\@JSON_REPO_TOKEN\@\",
  245. \"service_name\": \"\@JSON_SERVICE_NAME\@\",
  246. \"service_job_id\": \"\@JSON_SERVICE_JOB_ID\@\",
  247. \"source_files\": \@JSON_GCOV_FILES\@,
  248. \"git\": \@JSON_REPO_DATA\@
  249. }"
  250. )
  251. set(SRC_FILE_TEMPLATE
  252. "{
  253. \"name\": \"\@GCOV_SRC_REL_PATH\@\",
  254. \"source_digest\": \"\@GCOV_CONTENTS_MD5\@\",
  255. \"coverage\": \@GCOV_FILE_COVERAGE\@
  256. }"
  257. )
  258. message("\nGenerate JSON for files:")
  259. message("=========================")
  260. set(JSON_GCOV_FILES "[")
  261. # Read the GCOV files line by line and get the coverage data.
  262. foreach (GCOV_FILE ${GCOV_FILES})
  263. get_source_path_from_gcov_filename(GCOV_SRC_PATH ${GCOV_FILE})
  264. file(RELATIVE_PATH GCOV_SRC_REL_PATH "${PROJECT_ROOT}" "${GCOV_SRC_PATH}")
  265. # The new coveralls API doesn't need the entire source (Yay!)
  266. # However, still keeping that part for now. Will cleanup in the future.
  267. file(MD5 "${GCOV_SRC_PATH}" GCOV_CONTENTS_MD5)
  268. message("MD5: ${GCOV_SRC_PATH} = ${GCOV_CONTENTS_MD5}")
  269. # Loads the gcov file as a list of lines.
  270. # (We first open the file and replace all occurrences of [] with _
  271. # because CMake will fail to parse a line containing unmatched brackets...
  272. # also the \ to escaped \n in macros screws up things.)
  273. # https://public.kitware.com/Bug/view.php?id=15369
  274. file(READ ${GCOV_FILE} GCOV_CONTENTS)
  275. string(REPLACE "[" "_" GCOV_CONTENTS "${GCOV_CONTENTS}")
  276. string(REPLACE "]" "_" GCOV_CONTENTS "${GCOV_CONTENTS}")
  277. string(REPLACE "\\" "_" GCOV_CONTENTS "${GCOV_CONTENTS}")
  278. # Remove file contents to avoid encoding issues (cmake 2.8 has no ENCODING option)
  279. string(REGEX REPLACE "([^:]*):([^:]*):([^\n]*)\n" "\\1:\\2: \n" GCOV_CONTENTS "${GCOV_CONTENTS}")
  280. file(WRITE ${GCOV_FILE}_tmp "${GCOV_CONTENTS}")
  281. file(STRINGS ${GCOV_FILE}_tmp GCOV_LINES)
  282. list(LENGTH GCOV_LINES LINE_COUNT)
  283. # Instead of trying to parse the source from the
  284. # gcov file, simply read the file contents from the source file.
  285. # (Parsing it from the gcov is hard because C-code uses ; in many places
  286. # which also happens to be the same as the CMake list delimiter).
  287. file(READ ${GCOV_SRC_PATH} GCOV_FILE_SOURCE)
  288. string(REPLACE "\\" "\\\\" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
  289. string(REGEX REPLACE "\"" "\\\\\"" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
  290. string(REPLACE "\t" "\\\\t" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
  291. string(REPLACE "\r" "\\\\r" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
  292. string(REPLACE "\n" "\\\\n" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
  293. # According to http://json.org/ these should be escaped as well.
  294. # Don't know how to do that in CMake however...
  295. #string(REPLACE "\b" "\\\\b" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
  296. #string(REPLACE "\f" "\\\\f" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
  297. #string(REGEX REPLACE "\u([a-fA-F0-9]{4})" "\\\\u\\1" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
  298. # We want a json array of coverage data as a single string
  299. # start building them from the contents of the .gcov
  300. set(GCOV_FILE_COVERAGE "[")
  301. set(GCOV_LINE_COUNT 1) # Line number for the .gcov.
  302. set(DO_SKIP 0)
  303. foreach (GCOV_LINE ${GCOV_LINES})
  304. #message("${GCOV_LINE}")
  305. # Example of what we're parsing:
  306. # Hitcount |Line | Source
  307. # " 8: 26: if (!allowed || (strlen(allowed) == 0))"
  308. string(REGEX REPLACE
  309. "^([^:]*):([^:]*):(.*)$"
  310. "\\1;\\2;\\3"
  311. RES
  312. "${GCOV_LINE}")
  313. # Check if we should exclude lines using the Lcov syntax.
  314. string(REGEX MATCH "LCOV_EXCL_START" START_SKIP "${GCOV_LINE}")
  315. string(REGEX MATCH "LCOV_EXCL_END" END_SKIP "${GCOV_LINE}")
  316. string(REGEX MATCH "LCOV_EXCL_LINE" LINE_SKIP "${GCOV_LINE}")
  317. set(RESET_SKIP 0)
  318. if (LINE_SKIP AND NOT DO_SKIP)
  319. set(DO_SKIP 1)
  320. set(RESET_SKIP 1)
  321. endif()
  322. if (START_SKIP)
  323. set(DO_SKIP 1)
  324. message("${GCOV_LINE_COUNT}: Start skip")
  325. endif()
  326. if (END_SKIP)
  327. set(DO_SKIP 0)
  328. endif()
  329. list(LENGTH RES RES_COUNT)
  330. if (RES_COUNT GREATER 2)
  331. list(GET RES 0 HITCOUNT)
  332. list(GET RES 1 LINE)
  333. list(GET RES 2 SOURCE)
  334. string(STRIP ${HITCOUNT} HITCOUNT)
  335. string(STRIP ${LINE} LINE)
  336. # Lines with 0 line numbers are metadata and can be ignored.
  337. if (NOT ${LINE} EQUAL 0)
  338. if (DO_SKIP)
  339. set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}null, ")
  340. else()
  341. # Translate the hitcount into valid JSON values.
  342. if (${HITCOUNT} STREQUAL "#####" OR ${HITCOUNT} STREQUAL "=====")
  343. set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}0, ")
  344. elseif (${HITCOUNT} STREQUAL "-")
  345. set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}null, ")
  346. else()
  347. set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}${HITCOUNT}, ")
  348. endif()
  349. endif()
  350. endif()
  351. else()
  352. message(WARNING "Failed to properly parse line (RES_COUNT = ${RES_COUNT}) ${GCOV_FILE}:${GCOV_LINE_COUNT}\n-->${GCOV_LINE}")
  353. endif()
  354. if (RESET_SKIP)
  355. set(DO_SKIP 0)
  356. endif()
  357. math(EXPR GCOV_LINE_COUNT "${GCOV_LINE_COUNT}+1")
  358. endforeach()
  359. message("${GCOV_LINE_COUNT} of ${LINE_COUNT} lines read!")
  360. # Advanced way of removing the trailing comma in the JSON array.
  361. # "[1, 2, 3, " -> "[1, 2, 3"
  362. string(REGEX REPLACE ",[ ]*$" "" GCOV_FILE_COVERAGE ${GCOV_FILE_COVERAGE})
  363. # Append the trailing ] to complete the JSON array.
  364. set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}]")
  365. # Generate the final JSON for this file.
  366. message("Generate JSON for file: ${GCOV_SRC_REL_PATH}...")
  367. string(CONFIGURE ${SRC_FILE_TEMPLATE} FILE_JSON)
  368. set(JSON_GCOV_FILES "${JSON_GCOV_FILES}${FILE_JSON}, ")
  369. endforeach()
  370. # Loop through all files we couldn't find any coverage for
  371. # as well, and generate JSON for those as well with 0% coverage.
  372. foreach(NOT_COVERED_SRC ${COVERAGE_SRCS_REMAINING})
  373. # Set variables for json replacement
  374. set(GCOV_SRC_PATH ${NOT_COVERED_SRC})
  375. file(MD5 "${GCOV_SRC_PATH}" GCOV_CONTENTS_MD5)
  376. file(RELATIVE_PATH GCOV_SRC_REL_PATH "${PROJECT_ROOT}" "${GCOV_SRC_PATH}")
  377. # Loads the source file as a list of lines.
  378. file(STRINGS ${NOT_COVERED_SRC} SRC_LINES)
  379. set(GCOV_FILE_COVERAGE "[")
  380. set(GCOV_FILE_SOURCE "")
  381. foreach (SOURCE ${SRC_LINES})
  382. set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}null, ")
  383. string(REPLACE "\\" "\\\\" SOURCE "${SOURCE}")
  384. string(REGEX REPLACE "\"" "\\\\\"" SOURCE "${SOURCE}")
  385. string(REPLACE "\t" "\\\\t" SOURCE "${SOURCE}")
  386. string(REPLACE "\r" "\\\\r" SOURCE "${SOURCE}")
  387. set(GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}${SOURCE}\\n")
  388. endforeach()
  389. # Remove trailing comma, and complete JSON array with ]
  390. string(REGEX REPLACE ",[ ]*$" "" GCOV_FILE_COVERAGE ${GCOV_FILE_COVERAGE})
  391. set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}]")
  392. # Generate the final JSON for this file.
  393. message("Generate JSON for non-gcov file: ${NOT_COVERED_SRC}...")
  394. string(CONFIGURE ${SRC_FILE_TEMPLATE} FILE_JSON)
  395. set(JSON_GCOV_FILES "${JSON_GCOV_FILES}${FILE_JSON}, ")
  396. endforeach()
  397. # Get rid of trailing comma.
  398. string(REGEX REPLACE ",[ ]*$" "" JSON_GCOV_FILES ${JSON_GCOV_FILES})
  399. set(JSON_GCOV_FILES "${JSON_GCOV_FILES}]")
  400. # Generate the final complete JSON!
  401. message("Generate final JSON...")
  402. string(CONFIGURE ${JSON_TEMPLATE} JSON)
  403. file(WRITE "${COVERALLS_OUTPUT_FILE}" "${JSON}")
  404. message("###########################################################################")
  405. message("Generated coveralls JSON containing coverage data:")
  406. message("${COVERALLS_OUTPUT_FILE}")
  407. message("###########################################################################")