diff options
Diffstat (limited to 'Modules/CTestCoverageCollectGCOV.cmake')
-rw-r--r-- | Modules/CTestCoverageCollectGCOV.cmake | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/Modules/CTestCoverageCollectGCOV.cmake b/Modules/CTestCoverageCollectGCOV.cmake new file mode 100644 index 0000000..ef3aa76 --- /dev/null +++ b/Modules/CTestCoverageCollectGCOV.cmake @@ -0,0 +1,204 @@ +#.rst: +# CTestCoverageCollectGCOV +# ------------------------ +# +# This module provides the function ``ctest_coverage_collect_gcov``. +# The function will run gcov on the .gcda files in a binary tree and then +# package all of the .gcov files into a tar file with a data.json that +# contains the source and build directories for CDash to use in parsing +# the coverage data. In addtion the Labels.json files for targets that +# have coverage information are also put in the tar file for CDash to +# asign the correct labels. This file can be sent to a CDash server for +# display with the +# :command:`ctest_submit(CDASH_UPLOAD)` command. +# +# .. command:: cdash_coverage_collect_gcov +# +# :: +# +# ctest_coverage_collect_gcov(TARBALL <tarfile> +# [SOURCE <source_dir>][BUILD <build_dir>] +# [GCOV_COMMAND <gcov_command>] +# [GCOV_OPTIONS <options>...] +# ) +# +# Run gcov and package a tar file for CDash. The options are: +# +# ``TARBALL <tarfile>`` +# Specify the location of the ``.tar`` file to be created for later +# upload to CDash. Relative paths will be interpreted with respect +# to the top-level build directory. +# +# ``SOURCE <source_dir>`` +# Specify the top-level source directory for the build. +# Default is the value of :variable:`CTEST_SOURCE_DIRECTORY`. +# +# ``BUILD <build_dir>`` +# Specify the top-level build directory for the build. +# Default is the value of :variable:`CTEST_BINARY_DIRECTORY`. +# +# ``GCOV_COMMAND <gcov_command>`` +# Specify the full path to the ``gcov`` command on the machine. +# Default is the value of :variable:`CTEST_COVERAGE_COMMAND`. +# +# ``GCOV_OPTIONS <options>...`` +# Specify options to be passed to gcov. The ``gcov`` command +# is run as ``gcov <options>... -o <gcov-dir> <file>.gcda``. +# If not specified, the default option is just ``-b``. +# +# ``QUIET`` +# Suppress non-error messages that otherwise would have been +# printed out by this function. + +#============================================================================= +# Copyright 2014-2015 Kitware, Inc. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) +include(CMakeParseArguments) +function(ctest_coverage_collect_gcov) + set(options QUIET) + set(oneValueArgs TARBALL SOURCE BUILD GCOV_COMMAND) + set(multiValueArgs GCOV_OPTIONS) + cmake_parse_arguments(GCOV "${options}" "${oneValueArgs}" + "${multiValueArgs}" "" ${ARGN} ) + if(NOT DEFINED GCOV_TARBALL) + message(FATAL_ERROR + "TARBALL must be specified. for ctest_coverage_collect_gcov") + endif() + if(NOT DEFINED GCOV_SOURCE) + set(source_dir "${CTEST_SOURCE_DIRECTORY}") + else() + set(source_dir "${GCOV_SOURCE}") + endif() + if(NOT DEFINED GCOV_BUILD) + set(binary_dir "${CTEST_BINARY_DIRECTORY}") + else() + set(binary_dir "${GCOV_BUILD}") + endif() + if(NOT DEFINED GCOV_GCOV_COMMAND) + set(gcov_command "${CTEST_COVERAGE_COMMAND}") + else() + set(gcov_command "${GCOV_GCOV_COMMAND}") + endif() + # run gcov on each gcda file in the binary tree + set(gcda_files) + set(label_files) + # look for gcda files in the target directories + # could do a glob from the top of the binary tree but + # this will be faster and only look where the files will be + file(STRINGS "${binary_dir}/CMakeFiles/TargetDirectories.txt" target_dirs + ENCODING UTF-8) + foreach(target_dir ${target_dirs}) + file(GLOB_RECURSE gfiles RELATIVE ${binary_dir} "${target_dir}/*.gcda") + list(LENGTH gfiles len) + # if we have gcda files then also grab the labels file for that target + if(${len} GREATER 0) + file(GLOB_RECURSE lfiles RELATIVE ${binary_dir} + "${target_dir}/Labels.json") + list(APPEND gcda_files ${gfiles}) + list(APPEND label_files ${lfiles}) + endif() + endforeach() + # return early if no coverage files were found + list(LENGTH gcda_files len) + if(len EQUAL 0) + if (NOT GCOV_QUIET) + message("ctest_coverage_collect_gcov: No .gcda files found, " + "ignoring coverage request.") + endif() + return() + endif() + # setup the dir for the coverage files + set(coverage_dir "${binary_dir}/Testing/CoverageInfo") + file(MAKE_DIRECTORY "${coverage_dir}") + # call gcov on each .gcda file + foreach (gcda_file ${gcda_files}) + # get the directory of the gcda file + get_filename_component(gcda_file ${binary_dir}/${gcda_file} ABSOLUTE) + get_filename_component(gcov_dir ${gcda_file} DIRECTORY) + # run gcov, this will produce the .gcov file in the current + # working directory + if(NOT DEFINED GCOV_GCOV_OPTIONS) + set(GCOV_GCOV_OPTIONS -b) + endif() + execute_process(COMMAND + ${gcov_command} ${GCOV_GCOV_OPTIONS} -o ${gcov_dir} ${gcda_file} + OUTPUT_VARIABLE out + RESULT_VARIABLE res + WORKING_DIRECTORY ${coverage_dir}) + endforeach() + if(NOT "${res}" EQUAL 0) + if (NOT GCOV_QUIET) + message(STATUS "Error running gcov: ${res} ${out}") + endif() + endif() + # create json file with project information + file(WRITE ${coverage_dir}/data.json + "{ + \"Source\": \"${source_dir}\", + \"Binary\": \"${binary_dir}\" +}") + # collect the gcov files + set(unfiltered_gcov_files) + file(GLOB_RECURSE unfiltered_gcov_files RELATIVE ${binary_dir} "${coverage_dir}/*.gcov") + + set(gcov_files) + foreach(gcov_file ${unfiltered_gcov_files}) + file(STRINGS ${binary_dir}/${gcov_file} first_line LIMIT_COUNT 1 ENCODING UTF-8) + + set(is_excluded false) + if(first_line MATCHES "^ -: 0:Source:(.*)$") + set(source_file ${CMAKE_MATCH_1}) + elseif(NOT GCOV_QUIET) + message(STATUS "Could not determine source file corresponding to: ${gcov_file}") + endif() + + foreach(exclude_entry IN LISTS CTEST_CUSTOM_COVERAGE_EXCLUDE) + if(source_file MATCHES "${exclude_entry}") + set(is_excluded true) + + if(NOT GCOV_QUIET) + message("Excluding coverage for: ${source_file} which matches ${exclude_entry}") + endif() + + break() + endif() + endforeach() + + if(NOT is_excluded) + list(APPEND gcov_files ${gcov_file}) + endif() + endforeach() + + # tar up the coverage info with the same date so that the md5 + # sum will be the same for the tar file independent of file time + # stamps + string(REPLACE ";" "\n" gcov_files "${gcov_files}") + string(REPLACE ";" "\n" label_files "${label_files}") + file(WRITE "${coverage_dir}/coverage_file_list.txt" + "${gcov_files} +${coverage_dir}/data.json +${label_files} +") + + if (GCOV_QUIET) + set(tar_opts "cfj") + else() + set(tar_opts "cvfj") + endif() + + execute_process(COMMAND + ${CMAKE_COMMAND} -E tar ${tar_opts} ${GCOV_TARBALL} + "--mtime=1970-01-01 0:0:0 UTC" + "--format=gnutar" + --files-from=${coverage_dir}/coverage_file_list.txt + WORKING_DIRECTORY ${binary_dir}) +endfunction() |