From 2de2c3d5fc702396a2a5ae8f70ab98a0011d486e Mon Sep 17 00:00:00 2001 From: Petri Lehtinen Date: Tue, 26 Jan 2021 21:59:23 +0200 Subject: [PATCH] Fix and simplify coveralls reporting --- .github/workflows/tests.yml | 9 +- CMakeLists.txt | 71 +----- cmake/CodeCoverage.cmake | 6 +- cmake/Coveralls.cmake | 111 --------- cmake/CoverallsClear.cmake | 24 -- cmake/CoverallsGenerateGcov.cmake | 380 ------------------------------ 6 files changed, 12 insertions(+), 589 deletions(-) delete mode 100644 cmake/Coveralls.cmake delete mode 100644 cmake/CoverallsClear.cmake delete mode 100644 cmake/CoverallsGenerateGcov.cmake diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c19e3d8..b7ec74a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -70,6 +70,11 @@ jobs: steps: - uses: actions/checkout@v2 - run: sudo apt update && sudo apt install curl lcov - - run: cmake -DJANSSON_COVERAGE=ON -DJANSSON_COVERALLS=ON -DCMAKE_BUILD_TYPE=Debug + - run: cmake -DJANSSON_COVERAGE=ON -DCMAKE_BUILD_TYPE=Debug . - run: cmake --build . - - run: cmake --build . --target coveralls + - run: cmake --build . --target coverage + - name: Coveralls + uses: coverallsapp/github-action@master + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + path-to-lcov: coverage.info diff --git a/CMakeLists.txt b/CMakeLists.txt index 7956fc3..1867b52 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,51 +1,3 @@ -# Notes: -# -# Author: Paul Harris, June 2012 -# Additions: Joakim Soderberg, February 2013 -# -# Supports: building static/shared, release/debug/etc, can also build html docs -# and some of the tests. -# Note that its designed for out-of-tree builds, so it will not pollute your -# source tree. -# -# TODO 1: Finish implementing tests. api tests are working, but the valgrind -# variants are not flagging problems. -# -# TODO 2: There is a check_exports script that would try and incorporate. -# -# TODO 3: Consolidate version numbers, currently the version number is written -# into: * cmake (here) * autotools (the configure) * source code header files. -# Should not be written directly into header files, autotools/cmake can do -# that job. -# -# Brief intro on how to use cmake: -# > mkdir build (somewhere - we do out-of-tree builds) -# > use cmake, ccmake, or cmake-gui to configure the project. for linux, you -# can only choose one variant: release,debug,etc... and static or shared. -# >> example: -# >> cd build -# >> ccmake -i ../path_to_jansson_dir -# >> inside, configure your options. press C until there are no lines -# with * next to them. -# >> note, I like to configure the 'install' path to ../install, so I get -# self-contained clean installs I can point other projects to. -# >> press G to 'generate' the project files. -# >> make (to build the project) -# >> make install -# >> make test (to run the tests, if you enabled them) -# -# Brief description on how it works: -# There is a small hierarchy of CMakeLists.txt files which define how the -# project is built. -# Header file detection etc is done, and the results are written into config.h -# and jansson_config.h, which are generated from the corresponding -# config.h.cmake and jansson_config.h.cmake template files. -# The generated header files end up in the build directory - not in -# the source directory. -# The rest is down to the usual make process. - - - cmake_minimum_required (VERSION 3.1) project(jansson C) @@ -64,8 +16,6 @@ option(JANSSON_EXAMPLES "Compile example applications" ON) if (UNIX) option(JANSSON_COVERAGE "(GCC Only! Requires gcov/lcov to be installed). Include target for doing coverage analysis for the test suite. Note that -DCMAKE_BUILD_TYPE=Debug must be set" OFF) - option(JANSSON_COVERALLS "Generate coverage info for Coveralls" OFF) - option(JANSSON_COVERALLS_UPLOAD "Upload coverage info to Coveralls (Only works via Travis)" ON) endif () # Set some nicer output dirs. @@ -122,17 +72,9 @@ endif() message("C compiler: ${CMAKE_C_COMPILER_ID}") -# Coverage only works with GCC for a debug build. -if (JANSSON_COVERALLS) - set(JANSSON_COVERAGE ON) -endif() - if (JANSSON_COVERAGE) include(CodeCoverage) - include(Coveralls) - - # This adds coverage arguments to gcc/clang. - coveralls_turn_on_coverage() + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") endif() check_include_files (endian.h HAVE_ENDIAN_H) @@ -586,16 +528,7 @@ if (NOT JANSSON_WITHOUT_TESTS) endforeach () if (JANSSON_COVERAGE) - setup_target_for_coverage( - coverage # Coverage make target "make coverage". - coverage # Name of output directory. - make # Name of test runner executable. - test) # Arguments to the test runner above (make test). - - if (JANSSON_COVERALLS) - set(COVERAGE_SRCS ${JANSSON_SRC}) - coveralls_setup("${COVERAGE_SRCS}" ${JANSSON_COVERALLS_UPLOAD}) - endif () + SETUP_TARGET_FOR_COVERAGE(coverage coverage ctest) endif () # Enable using "make check" just like the autotools project. diff --git a/cmake/CodeCoverage.cmake b/cmake/CodeCoverage.cmake index 79ec8ac..3a21d3d 100644 --- a/cmake/CodeCoverage.cmake +++ b/cmake/CodeCoverage.cmake @@ -111,9 +111,9 @@ FUNCTION(SETUP_TARGET_FOR_COVERAGE _targetname _outputname _testrunner) # Capturing lcov counters and generating report COMMAND ${LCOV_PATH} --directory . --capture --output-file ${_outputname}.info --rc lcov_branch_coverage=1 - COMMAND ${LCOV_PATH} --remove ${_outputname}.info '*/build/include/*' '*/test/*' '/usr/include/*' --output-file ${_outputname}.info.cleaned --rc lcov_branch_coverage=1 - COMMAND ${GENHTML_PATH} --branch-coverage -o ${_outputname} ${_outputname}.info.cleaned - COMMAND ${CMAKE_COMMAND} -E remove ${_outputname}.info ${_outputname}.info.cleaned + COMMAND ${LCOV_PATH} --remove ${_outputname}.info '*/build/include/*' '*/test/*' '/usr/include/*' --output-file ${_outputname}.info --rc lcov_branch_coverage=1 + # COMMAND ${GENHTML_PATH} --branch-coverage -o ${_outputname} ${_outputname}.info.cleaned + # COMMAND ${CMAKE_COMMAND} -E remove ${_outputname}.info ${_outputname}.info.cleaned WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report." diff --git a/cmake/Coveralls.cmake b/cmake/Coveralls.cmake deleted file mode 100644 index 29221c9..0000000 --- a/cmake/Coveralls.cmake +++ /dev/null @@ -1,111 +0,0 @@ -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# -# Copyright (C) 2014 Joakim Söderberg -# - - -# -# Param _COVERAGE_SRCS A list of source files that coverage should be collected for. -# Param _COVERALLS_UPLOAD Upload the result to coveralls? -# -function(coveralls_setup _COVERAGE_SRCS _COVERALLS_UPLOAD) - # When passing a CMake list to an external process, the list - # will be converted from the format "1;2;3" to "1 2 3". - # This means the script we're calling won't see it as a list - # of sources, but rather just one long path. We remedy this - # by replacing ";" with "*" and then reversing that in the script - # that we're calling. - # http://cmake.3232098.n2.nabble.com/Passing-a-CMake-list-quot-as-is-quot-to-a-custom-target-td6505681.html - set(COVERAGE_SRCS_TMP ${_COVERAGE_SRCS}) - set(COVERAGE_SRCS "") - foreach (COVERAGE_SRC ${COVERAGE_SRCS_TMP}) - set(COVERAGE_SRCS "${COVERAGE_SRCS}*${COVERAGE_SRC}") - endforeach() - - #message("Coverage sources: ${COVERAGE_SRCS}") - set(COVERALLS_FILE ${PROJECT_BINARY_DIR}/coveralls.json) - - add_custom_target(coveralls_generate - - # Zero the coverage counters. - COMMAND ${CMAKE_COMMAND} - -P "${PROJECT_SOURCE_DIR}/cmake/CoverallsClear.cmake" - - # Run regress tests. - COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure - - # Generate Gcov and translate it into coveralls JSON. - # We do this by executing an external CMake script. - # (We don't want this to run at CMake generation time, but after compilation and everything has run). - COMMAND ${CMAKE_COMMAND} - -DCOVERAGE_SRCS="${COVERAGE_SRCS}" # TODO: This is passed like: "a b c", not "a;b;c" - -DCOVERALLS_OUTPUT_FILE="${COVERALLS_FILE}" - -DCOV_PATH="${PROJECT_BINARY_DIR}" - -DPROJECT_ROOT="${PROJECT_SOURCE_DIR}" - -P "${PROJECT_SOURCE_DIR}/cmake/CoverallsGenerateGcov.cmake" - - WORKING_DIRECTORY ${PROJECT_BINARY_DIR} - COMMENT "Generating coveralls output..." - ) - - if (_COVERALLS_UPLOAD) - message("COVERALLS UPLOAD: ON") - - find_program(CURL_EXECUTABLE curl) - - if (NOT CURL_EXECUTABLE) - message(FATAL_ERROR "Coveralls: curl not found! Aborting") - endif() - - add_custom_target(coveralls_upload - # Upload the JSON to coveralls. - COMMAND ${CURL_EXECUTABLE} - -S -F json_file=@${COVERALLS_FILE} - https://coveralls.io/api/v1/jobs - - DEPENDS coveralls_generate - - WORKING_DIRECTORY ${PROJECT_BINARY_DIR} - COMMENT "Uploading coveralls output...") - - add_custom_target(coveralls DEPENDS coveralls_upload) - else() - message("COVERALLS UPLOAD: OFF") - add_custom_target(coveralls DEPENDS coveralls_generate) - endif() - -endfunction() - -macro(coveralls_turn_on_coverage) - if(NOT (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) - AND (NOT "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")) - message(FATAL_ERROR "Coveralls: Compiler ${CMAKE_C_COMPILER_ID} is not GNU gcc! Aborting... You can set this on the command line using CC=/usr/bin/gcc CXX=/usr/bin/g++ cmake ..") - endif() - - if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") - message(FATAL_ERROR "Coveralls: Code coverage results with an optimised (non-Debug) build may be misleading! Add -DCMAKE_BUILD_TYPE=Debug") - endif() - - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") -endmacro() - - - diff --git a/cmake/CoverallsClear.cmake b/cmake/CoverallsClear.cmake deleted file mode 100644 index eb68695..0000000 --- a/cmake/CoverallsClear.cmake +++ /dev/null @@ -1,24 +0,0 @@ -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# -# Copyright (C) 2014 Joakim Söderberg -# - -file(REMOVE_RECURSE ${PROJECT_BINARY_DIR}/*.gcda) - diff --git a/cmake/CoverallsGenerateGcov.cmake b/cmake/CoverallsGenerateGcov.cmake deleted file mode 100644 index c4da8fb..0000000 --- a/cmake/CoverallsGenerateGcov.cmake +++ /dev/null @@ -1,380 +0,0 @@ -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# -# Copyright (C) 2014 Joakim Söderberg -# -# This is intended to be run by a custom target in a CMake project like this. -# 0. Compile program with coverage support. -# 1. Clear coverage data. (Recursively delete *.gcda in build dir) -# 2. Run the unit tests. -# 3. Run this script specifying which source files the coverage should be performed on. -# -# This script will then use gcov to generate .gcov files in the directory specified -# via the COV_PATH var. This should probably be the same as your cmake build dir. -# -# It then parses the .gcov files to convert them into the Coveralls JSON format: -# https://coveralls.io/docs/api -# -# Example for running as standalone CMake script from the command line: -# (Note it is important the -P is at the end...) -# $ cmake -DCOV_PATH=$(pwd) -# -DCOVERAGE_SRCS="catcierge_rfid.c;catcierge_timer.c" -# -P ../cmake/CoverallsGcovUpload.cmake -# -CMAKE_MINIMUM_REQUIRED(VERSION 2.8) - - -# -# Make sure we have the needed arguments. -# -if (NOT COVERALLS_OUTPUT_FILE) - message(FATAL_ERROR "Coveralls: No coveralls output file specified. Please set COVERALLS_OUTPUT_FILE") -endif() - -if (NOT COV_PATH) - message(FATAL_ERROR "Coveralls: Missing coverage directory path where gcov files will be generated. Please set COV_PATH") -endif() - -if (NOT COVERAGE_SRCS) - message(FATAL_ERROR "Coveralls: Missing the list of source files that we should get the coverage data for COVERAGE_SRCS") -endif() - -if (NOT PROJECT_ROOT) - message(FATAL_ERROR "Coveralls: Missing PROJECT_ROOT.") -endif() - -# Since it's not possible to pass a CMake list properly in the -# "1;2;3" format to an external process, we have replaced the -# ";" with "*", so reverse that here so we get it back into the -# CMake list format. -string(REGEX REPLACE "\\*" ";" COVERAGE_SRCS ${COVERAGE_SRCS}) - -find_program(GCOV_EXECUTABLE gcov) - -if (NOT GCOV_EXECUTABLE) - message(FATAL_ERROR "gcov not found! Aborting...") -endif() - -find_package(Git) - -# TODO: Add these git things to the coveralls json. -if (GIT_FOUND) - # Branch. - execute_process( - COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - OUTPUT_VARIABLE GIT_BRANCH - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - - macro (git_log_format FORMAT_CHARS VAR_NAME) - execute_process( - COMMAND ${GIT_EXECUTABLE} log -1 --pretty=format:%${FORMAT_CHARS} - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - OUTPUT_VARIABLE ${VAR_NAME} - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - endmacro() - - git_log_format(an GIT_AUTHOR_EMAIL) - git_log_format(ae GIT_AUTHOR_EMAIL) - git_log_format(cn GIT_COMMITTER_NAME) - git_log_format(ce GIT_COMMITTER_EMAIL) - git_log_format(B GIT_COMMIT_MESSAGE) - - message("Git exe: ${GIT_EXECUTABLE}") - message("Git branch: ${GIT_BRANCH}") - message("Git author: ${GIT_AUTHOR_NAME}") - message("Git e-mail: ${GIT_AUTHOR_EMAIL}") - message("Git committer name: ${GIT_COMMITTER_NAME}") - message("Git committer e-mail: ${GIT_COMMITTER_EMAIL}") - message("Git commit message: ${GIT_COMMIT_MESSAGE}") - -endif() - -############################# Macros ######################################### - -# -# This macro converts from the full path format gcov outputs: -# -# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov -# -# to the original source file path the .gcov is for: -# -# /path/to/project/root/subdir/the_file.c -# -macro(get_source_path_from_gcov_filename _SRC_FILENAME _GCOV_FILENAME) - - # /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov - # -> - # #path#to#project#root#subdir#the_file.c.gcov - get_filename_component(_GCOV_FILENAME_WEXT ${_GCOV_FILENAME} NAME) - - # #path#to#project#root#subdir#the_file.c.gcov -> /path/to/project/root/subdir/the_file.c - string(REGEX REPLACE "\\.gcov$" "" SRC_FILENAME_TMP ${_GCOV_FILENAME_WEXT}) - string(REGEX REPLACE "\#" "/" SRC_FILENAME_TMP ${SRC_FILENAME_TMP}) - set(${_SRC_FILENAME} "${SRC_FILENAME_TMP}") -endmacro() - -############################################################################## - -# Get the coverage data. -file(GLOB_RECURSE GCDA_FILES "${COV_PATH}/*.gcda") -message("GCDA files:") - -# Get a list of all the object directories needed by gcov -# (The directories the .gcda files and .o files are found in) -# and run gcov on those. -foreach(GCDA ${GCDA_FILES}) - message("Process: ${GCDA}") - message("------------------------------------------------------------------------------") - get_filename_component(GCDA_DIR ${GCDA} PATH) - - # - # The -p below refers to "Preserve path components", - # This means that the generated gcov filename of a source file will - # keep the original files entire filepath, but / is replaced with #. - # Example: - # - # /path/to/project/root/build/CMakeFiles/the_file.dir/subdir/the_file.c.gcda - # ------------------------------------------------------------------------------ - # File '/path/to/project/root/subdir/the_file.c' - # Lines executed:68.34% of 199 - # /path/to/project/root/subdir/the_file.c:creating '#path#to#project#root#subdir#the_file.c.gcov' - # - # If -p is not specified then the file is named only "the_file.c.gcov" - # - execute_process( - COMMAND ${GCOV_EXECUTABLE} -p -o ${GCDA_DIR} ${GCDA} - WORKING_DIRECTORY ${COV_PATH} - ) -endforeach() - -# TODO: Make these be absolute path -file(GLOB ALL_GCOV_FILES ${COV_PATH}/*.gcov) - -# Get only the filenames to use for filtering. -#set(COVERAGE_SRCS_NAMES "") -#foreach (COVSRC ${COVERAGE_SRCS}) -# get_filename_component(COVSRC_NAME ${COVSRC} NAME) -# message("${COVSRC} -> ${COVSRC_NAME}") -# list(APPEND COVERAGE_SRCS_NAMES "${COVSRC_NAME}") -#endforeach() - -# -# Filter out all but the gcov files we want. -# -# We do this by comparing the list of COVERAGE_SRCS filepaths that the -# user wants the coverage data for with the paths of the generated .gcov files, -# so that we only keep the relevant gcov files. -# -# Example: -# COVERAGE_SRCS = -# /path/to/project/root/subdir/the_file.c -# -# ALL_GCOV_FILES = -# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov -# /path/to/project/root/build/#path#to#project#root#subdir#other_file.c.gcov -# -# Result should be: -# GCOV_FILES = -# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov -# -set(GCOV_FILES "") -#message("Look in coverage sources: ${COVERAGE_SRCS}") -message("\nFilter out unwanted GCOV files:") -message("===============================") - -set(COVERAGE_SRCS_REMAINING ${COVERAGE_SRCS}) - -foreach (GCOV_FILE ${ALL_GCOV_FILES}) - - # - # /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov - # -> - # /path/to/project/root/subdir/the_file.c - get_source_path_from_gcov_filename(GCOV_SRC_PATH ${GCOV_FILE}) - - # Is this in the list of source files? - # TODO: We want to match against relative path filenames from the source file root... - list(FIND COVERAGE_SRCS ${GCOV_SRC_PATH} WAS_FOUND) - - if (NOT WAS_FOUND EQUAL -1) - message("YES: ${GCOV_FILE}") - list(APPEND GCOV_FILES ${GCOV_FILE}) - - # We remove it from the list, so we don't bother searching for it again. - # Also files left in COVERAGE_SRCS_REMAINING after this loop ends should - # have coverage data generated from them (no lines are covered). - list(REMOVE_ITEM COVERAGE_SRCS_REMAINING ${GCOV_SRC_PATH}) - else() - message("NO: ${GCOV_FILE}") - endif() -endforeach() - -# TODO: Enable setting these -set(JSON_SERVICE_NAME "travis-ci") -set(JSON_SERVICE_JOB_ID $ENV{TRAVIS_JOB_ID}) - -set(JSON_TEMPLATE -"{ - \"service_name\": \"\@JSON_SERVICE_NAME\@\", - \"service_job_id\": \"\@JSON_SERVICE_JOB_ID\@\", - \"source_files\": \@JSON_GCOV_FILES\@ -}" -) - -set(SRC_FILE_TEMPLATE -"{ - \"name\": \"\@GCOV_SRC_REL_PATH\@\", - \"source\": \"\@GCOV_FILE_SOURCE\@\", - \"coverage\": \@GCOV_FILE_COVERAGE\@ -}" -) - -message("\nGenerate JSON for files:") -message("=========================") - -set(JSON_GCOV_FILES "[") - -# Read the GCOV files line by line and get the coverage data. -foreach (GCOV_FILE ${GCOV_FILES}) - - get_source_path_from_gcov_filename(GCOV_SRC_PATH ${GCOV_FILE}) - file(RELATIVE_PATH GCOV_SRC_REL_PATH "${PROJECT_ROOT}" "${GCOV_SRC_PATH}") - - # Loads the gcov file as a list of lines. - file(STRINGS ${GCOV_FILE} GCOV_LINES) - - # Instead of trying to parse the source from the - # gcov file, simply read the file contents from the source file. - # (Parsing it from the gcov is hard because C-code uses ; in many places - # which also happens to be the same as the CMake list delimiter). - file(READ ${GCOV_SRC_PATH} GCOV_FILE_SOURCE) - - string(REPLACE "\\" "\\\\" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}") - string(REGEX REPLACE "\"" "\\\\\"" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}") - string(REPLACE "\t" "\\\\t" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}") - string(REPLACE "\r" "\\\\r" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}") - string(REPLACE "\n" "\\\\n" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}") - # According to http://json.org/ these should be escaped as well. - # Don't know how to do that in CMake however... - #string(REPLACE "\b" "\\\\b" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}") - #string(REPLACE "\f" "\\\\f" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}") - #string(REGEX REPLACE "\u([a-fA-F0-9]{4})" "\\\\u\\1" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}") - - # We want a json array of coverage data as a single string - # start building them from the contents of the .gcov - set(GCOV_FILE_COVERAGE "[") - - foreach (GCOV_LINE ${GCOV_LINES}) - # Example of what we're parsing: - # Hitcount |Line | Source - # " 8: 26: if (!allowed || (strlen(allowed) == 0))" - string(REGEX REPLACE - "^([^:]*):([^:]*):(.*)$" - "\\1;\\2;\\3" - RES - "${GCOV_LINE}") - - list(LENGTH RES RES_COUNT) - if (RES_COUNT GREATER 2) - list(GET RES 0 HITCOUNT) - list(GET RES 1 LINE) - list(GET RES 2 SOURCE) - - string(STRIP ${HITCOUNT} HITCOUNT) - string(STRIP ${LINE} LINE) - - # Lines with 0 line numbers are metadata and can be ignored. - if (NOT ${LINE} EQUAL 0) - - # Translate the hitcount into valid JSON values. - if (${HITCOUNT} STREQUAL "#####") - set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}0, ") - elseif (${HITCOUNT} STREQUAL "-") - set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}null, ") - else() - set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}${HITCOUNT}, ") - endif() - # TODO: Look for LCOV_EXCL_LINE in SOURCE to get rid of false positives. - endif() - else() - message(WARNING "Failed to properly parse line --> ${GCOV_LINE}") - endif() - endforeach() - - # Advanced way of removing the trailing comma in the JSON array. - # "[1, 2, 3, " -> "[1, 2, 3" - string(REGEX REPLACE ",[ ]*$" "" GCOV_FILE_COVERAGE ${GCOV_FILE_COVERAGE}) - - # Append the trailing ] to complete the JSON array. - set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}]") - - # Generate the final JSON for this file. - message("Generate JSON for file: ${GCOV_SRC_REL_PATH}...") - string(CONFIGURE ${SRC_FILE_TEMPLATE} FILE_JSON) - - set(JSON_GCOV_FILES "${JSON_GCOV_FILES}${FILE_JSON}, ") -endforeach() - -# Loop through all files we couldn't find any coverage for -# as well, and generate JSON for those as well with 0% coverage. -foreach(NOT_COVERED_SRC ${COVERAGE_SRCS_REMAINING}) - - # Loads the source file as a list of lines. - file(STRINGS ${NOT_COVERED_SRC} SRC_LINES) - - set(GCOV_FILE_COVERAGE "[") - set(GCOV_FILE_SOURCE "") - - foreach (SOURCE ${SRC_LINES}) - set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}0, ") - - string(REPLACE "\\" "\\\\" SOURCE "${SOURCE}") - string(REGEX REPLACE "\"" "\\\\\"" SOURCE "${SOURCE}") - string(REPLACE "\t" "\\\\t" SOURCE "${SOURCE}") - string(REPLACE "\r" "\\\\r" SOURCE "${SOURCE}") - set(GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}${SOURCE}\\n") - endforeach() - - # Remove trailing comma, and complete JSON array with ] - string(REGEX REPLACE ",[ ]*$" "" GCOV_FILE_COVERAGE ${GCOV_FILE_COVERAGE}) - set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}]") - - # Generate the final JSON for this file. - message("Generate JSON for non-gcov file: ${NOT_COVERED_SRC}...") - string(CONFIGURE ${SRC_FILE_TEMPLATE} FILE_JSON) - set(JSON_GCOV_FILES "${JSON_GCOV_FILES}${FILE_JSON}, ") -endforeach() - -# Get rid of trailing comma. -string(REGEX REPLACE ",[ ]*$" "" JSON_GCOV_FILES ${JSON_GCOV_FILES}) -set(JSON_GCOV_FILES "${JSON_GCOV_FILES}]") - -# Generate the final complete JSON! -message("Generate final JSON...") -string(CONFIGURE ${JSON_TEMPLATE} JSON) - -file(WRITE "${COVERALLS_OUTPUT_FILE}" "${JSON}") -message("###########################################################################") -message("Generated coveralls JSON containing coverage data:") -message("${COVERALLS_OUTPUT_FILE}") -message("###########################################################################") -