Add missing cmake mocules
authorKim Kulling <kim.kulling@googlemail.com>
Wed, 16 Nov 2016 15:46:54 +0000 (16:46 +0100)
committerKim Kulling <kim.kulling@googlemail.com>
Wed, 16 Nov 2016 15:46:54 +0000 (16:46 +0100)
cmake-modules/Coveralls.cmake [new file with mode: 0644]
cmake-modules/CoverallsClear.cmake [new file with mode: 0644]
cmake-modules/CoverallsGenerateGcov.cmake [new file with mode: 0644]
test/CMakeLists.txt

diff --git a/cmake-modules/Coveralls.cmake b/cmake-modules/Coveralls.cmake
new file mode 100644 (file)
index 0000000..5343f15
--- /dev/null
@@ -0,0 +1,126 @@
+#
+# The MIT License (MIT)
+#
+# 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 <joakim.soderberg@gmail.com>
+#
+
+set(_CMAKE_SCRIPT_PATH ${CMAKE_CURRENT_LIST_DIR}) # must be outside coveralls_setup() to get correct path
+
+#
+# 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)
+
+       if (ARGC GREATER 2)
+               set(_CMAKE_SCRIPT_PATH ${ARGN})
+               message(STATUS "Coveralls: Using alternate CMake script dir: ${_CMAKE_SCRIPT_PATH}")
+       endif()
+
+       if (NOT EXISTS "${_CMAKE_SCRIPT_PATH}/CoverallsClear.cmake")
+               message(FATAL_ERROR "Coveralls: Missing ${_CMAKE_SCRIPT_PATH}/CoverallsClear.cmake")
+       endif()
+
+       if (NOT EXISTS "${_CMAKE_SCRIPT_PATH}/CoverallsGenerateGcov.cmake")
+               message(FATAL_ERROR "Coveralls: Missing ${_CMAKE_SCRIPT_PATH}/CoverallsGenerateGcov.cmake")
+       endif()
+
+       # 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} -DPROJECT_BINARY_DIR="${PROJECT_BINARY_DIR}" -P "${_CMAKE_SCRIPT_PATH}/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 "${_CMAKE_SCRIPT_PATH}/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 <options> ..")
+       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-modules/CoverallsClear.cmake b/cmake-modules/CoverallsClear.cmake
new file mode 100644 (file)
index 0000000..a9ad487
--- /dev/null
@@ -0,0 +1,31 @@
+#
+# The MIT License (MIT)
+#
+# 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 <joakim.soderberg@gmail.com>
+#
+
+# do not follow symlinks in file(GLOB_RECURSE ...)
+cmake_policy(SET CMP0009 NEW)
+
+file(GLOB_RECURSE GCDA_FILES "${PROJECT_BINARY_DIR}/*.gcda")
+if(NOT GCDA_FILES STREQUAL "")
+  file(REMOVE ${GCDA_FILES})
+endif()
\ No newline at end of file
diff --git a/cmake-modules/CoverallsGenerateGcov.cmake b/cmake-modules/CoverallsGenerateGcov.cmake
new file mode 100644 (file)
index 0000000..ba506e3
--- /dev/null
@@ -0,0 +1,482 @@
+#
+# The MIT License (MIT)
+#
+# 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 <joakim.soderberg@gmail.com>
+#
+# 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})
+
+if (NOT DEFINED ENV{GCOV})
+  find_program(GCOV_EXECUTABLE gcov)
+else()
+  find_program(GCOV_EXECUTABLE $ENV{GCOV})
+endif()
+
+# convert all paths in COVERAGE_SRCS to absolute paths
+set(COVERAGE_SRCS_TMP "")
+foreach (COVERAGE_SRC ${COVERAGE_SRCS})
+       if (NOT "${COVERAGE_SRC}" MATCHES "^/")
+               set(COVERAGE_SRC ${PROJECT_ROOT}/${COVERAGE_SRC})
+       endif()
+       list(APPEND COVERAGE_SRCS_TMP ${COVERAGE_SRC})
+endforeach()
+set(COVERAGE_SRCS ${COVERAGE_SRCS_TMP})
+unset(COVERAGE_SRCS_TMP)
+
+if (NOT GCOV_EXECUTABLE)
+       message(FATAL_ERROR "gcov not found! Aborting...")
+endif()
+
+find_package(Git)
+
+set(JSON_REPO_TEMPLATE
+  "{
+    \"head\": {
+      \"id\": \"\@GIT_COMMIT_HASH\@\",
+      \"author_name\": \"\@GIT_AUTHOR_NAME\@\",
+      \"author_email\": \"\@GIT_AUTHOR_EMAIL\@\",
+      \"committer_name\": \"\@GIT_COMMITTER_NAME\@\",
+      \"committer_email\": \"\@GIT_COMMITTER_EMAIL\@\",
+      \"message\": \"\@GIT_COMMIT_MESSAGE\@\"
+    },
+    \"branch\": \"@GIT_BRANCH@\",
+    \"remotes\": []
+  }"
+)
+
+# TODO: Fill in git remote data
+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_NAME)
+       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)
+       git_log_format(H GIT_COMMIT_HASH)
+
+       if(GIT_COMMIT_MESSAGE)
+               string(REPLACE "\n" "\\n" GIT_COMMIT_MESSAGE ${GIT_COMMIT_MESSAGE})
+       endif()
+
+       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 commiter name: ${GIT_COMMITTER_NAME}")
+       message("Git commiter e-mail: ${GIT_COMMITTER_EMAIL}")
+       message("Git commit hash: ${GIT_COMMIT_HASH}")
+       message("Git commit message: ${GIT_COMMIT_MESSAGE}")
+
+       string(CONFIGURE ${JSON_REPO_TEMPLATE} JSON_REPO_DATA)
+else()
+       set(JSON_REPO_DATA "{}")
+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})
+       file(RELATIVE_PATH GCOV_SRC_REL_PATH "${PROJECT_ROOT}" "${GCOV_SRC_PATH}")
+
+       # 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_REPO_TOKEN $ENV{COVERALLS_REPO_TOKEN})
+
+set(JSON_TEMPLATE
+"{
+  \"repo_token\": \"\@JSON_REPO_TOKEN\@\",
+  \"service_name\": \"\@JSON_SERVICE_NAME\@\",
+  \"service_job_id\": \"\@JSON_SERVICE_JOB_ID\@\",
+  \"source_files\": \@JSON_GCOV_FILES\@,
+  \"git\": \@JSON_REPO_DATA\@
+}"
+)
+
+set(SRC_FILE_TEMPLATE
+"{
+      \"name\": \"\@GCOV_SRC_REL_PATH\@\",
+      \"source_digest\": \"\@GCOV_CONTENTS_MD5\@\",
+      \"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}")
+
+       # The new coveralls API doesn't need the entire source (Yay!)
+       # However, still keeping that part for now. Will cleanup in the future.
+       file(MD5 "${GCOV_SRC_PATH}" GCOV_CONTENTS_MD5)
+       message("MD5: ${GCOV_SRC_PATH} = ${GCOV_CONTENTS_MD5}")
+
+       # Loads the gcov file as a list of lines.
+       # (We first open the file and replace all occurences of [] with _
+       #  because CMake will fail to parse a line containing unmatched brackets...
+       #  also the \ to escaped \n in macros screws up things.)
+       # https://public.kitware.com/Bug/view.php?id=15369
+       file(READ ${GCOV_FILE} GCOV_CONTENTS)
+       string(REPLACE "[" "_" GCOV_CONTENTS "${GCOV_CONTENTS}")
+       string(REPLACE "]" "_" GCOV_CONTENTS "${GCOV_CONTENTS}")
+       string(REPLACE "\\" "_" GCOV_CONTENTS "${GCOV_CONTENTS}")
+
+       # Remove file contents to avoid encoding issues (cmake 2.8 has no ENCODING option)
+       string(REGEX REPLACE "([^:]*):([^:]*):([^\n]*)\n" "\\1:\\2: \n" GCOV_CONTENTS "${GCOV_CONTENTS}")
+       file(WRITE ${GCOV_FILE}_tmp "${GCOV_CONTENTS}")
+
+       file(STRINGS ${GCOV_FILE}_tmp GCOV_LINES)
+       list(LENGTH GCOV_LINES LINE_COUNT)
+
+       # 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 delimeter).
+       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 "[")
+
+       set(GCOV_LINE_COUNT 1) # Line number for the .gcov.
+       set(DO_SKIP 0)
+       foreach (GCOV_LINE ${GCOV_LINES})
+               #message("${GCOV_LINE}")
+               # 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}")
+
+               # Check if we should exclude lines using the Lcov syntax.
+               string(REGEX MATCH "LCOV_EXCL_START" START_SKIP "${GCOV_LINE}")
+               string(REGEX MATCH "LCOV_EXCL_END" END_SKIP "${GCOV_LINE}")
+               string(REGEX MATCH "LCOV_EXCL_LINE" LINE_SKIP "${GCOV_LINE}")
+
+               set(RESET_SKIP 0)
+               if (LINE_SKIP AND NOT DO_SKIP)
+                       set(DO_SKIP 1)
+                       set(RESET_SKIP 1)
+               endif()
+
+               if (START_SKIP)
+                       set(DO_SKIP 1)
+                       message("${GCOV_LINE_COUNT}: Start skip")
+               endif()
+
+               if (END_SKIP)
+                       set(DO_SKIP 0)
+               endif()
+
+               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)
+
+                               if (DO_SKIP)
+                                       set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}null, ")
+                               else()
+                                       # Translate the hitcount into valid JSON values.
+                                       if (${HITCOUNT} STREQUAL "#####" OR ${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()
+                               endif()
+                       endif()
+               else()
+                       message(WARNING "Failed to properly parse line (RES_COUNT = ${RES_COUNT}) ${GCOV_FILE}:${GCOV_LINE_COUNT}\n-->${GCOV_LINE}")
+               endif()
+
+               if (RESET_SKIP)
+                       set(DO_SKIP 0)
+               endif()
+               math(EXPR GCOV_LINE_COUNT "${GCOV_LINE_COUNT}+1")
+       endforeach()
+
+       message("${GCOV_LINE_COUNT} of ${LINE_COUNT} lines read!")
+
+       # 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})
+
+       # Set variables for json replacement
+       set(GCOV_SRC_PATH ${NOT_COVERED_SRC})
+       file(MD5 "${GCOV_SRC_PATH}" GCOV_CONTENTS_MD5)
+       file(RELATIVE_PATH GCOV_SRC_REL_PATH "${PROJECT_ROOT}" "${GCOV_SRC_PATH}")
+
+       # 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}null, ")
+
+               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("###########################################################################")
index 48e297e..e7beee4 100644 (file)
@@ -131,6 +131,6 @@ if (ASSIMP_COVERALLS)
     coveralls_setup(
         "${COVERAGE_SRCS}" # The source files.
         ON                 # If we should upload.
-        "${PROJECT_SOURCE_DIR}/cmake/") # (Optional) Alternate project cmake module path.
+        "${PROJECT_SOURCE_DIR}/cmake-modules/") # (Optional) Alternate project cmake module path.
 endif()