Verified Commit b6361ff5 authored by Alberto Miranda's avatar Alberto Miranda ♨️
Browse files

CMake: Simplify generation of coverage reports

There are now explicit targets for generating coverage reports from
CMake itself:

- `coverage-zerocount`: Capture initial zero coverage data and write it to
  `${COVERAGE_ZEROCOUNT_TRACEFILE}`.
- `coverage-capture`: Capture coverage data from existing .gcda files and
  write it to `${COVERAGE_CAPTURE_TRACEFILE}`.
- `coverage-unified`: Merge any coverage data files found in
  `COVERAGE_OUTPUT_DIR` and generate a unified coverage trace.
- `coverage-summary`: Print a summary of the coverage data found in
  `${COVERAGE_UNIFIED_TRACEFILE}`.
- `coverage-html_report`: Write a HTML report from the coverage data
  found in `${COVERAGE_UNIFIED_TRACEFILE}`.
- `coverage-cobertura`: Write a Cobertura report from the coverage data
  found in `${COVERAGE_UNIFIED_TRACEFILE}`.
parent 8e96e023
Loading
Loading
Loading
Loading
+142 −76
Original line number Diff line number Diff line
@@ -26,85 +26,151 @@
# SPDX-License-Identifier: GPL-3.0-or-later                                    #
################################################################################

# Variables
option(GKFS_ENABLE_CODE_COVERAGE
  "Builds GekkoFS targets with code coverage instrumentation."
  OFF
  )

# Common initialization/checks
if(GKFS_ENABLE_CODE_COVERAGE AND NOT GKFS_CODE_COVERAGE_ADDED)
option(GKFS_GENERATE_COVERAGE_REPORTS "Generate coverage reports" ON)

  set(GKFS_CODE_COVERAGE_ADDED ON)
macro(gkfs_enable_coverage_reports)

  if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
     OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
  set(OPTIONS)
  set(SINGLE_VALUE)
  set(MULTI_VALUE EXCLUDE_DIRECTORIES)
  cmake_parse_arguments(
    ARGS "${OPTIONS}" "${SINGLE_VALUE}" "${MULTI_VALUE}" ${ARGN}
  )

    message(STATUS "[gekkofs] Building with LLVM Code Coverage Tools")
  find_program(COVERAGE_PY
    coverage.py
    PATHS ${CMAKE_SOURCE_DIR}/scripts/dev
    REQUIRED)

  elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES
                                              "GNU")
  if(NOT COVERAGE_OUTPUT_DIR)
    set(COVERAGE_OUTPUT_DIR "${CMAKE_BINARY_DIR}/coverage")
  endif()

    message(STATUS "[gekkofs] Building with GCC Code Coverage Tools")
  file(MAKE_DIRECTORY ${COVERAGE_OUTPUT_DIR})

    if(CMAKE_BUILD_TYPE)
      string(TOUPPER ${CMAKE_BUILD_TYPE} upper_build_type)
      if(NOT ${upper_build_type} STREQUAL "DEBUG")
        message(
          WARNING
            "Code coverage results with an optimized (non-Debug) build may be misleading"
        )
      endif()
    else()
      message(
        WARNING
          "Code coverage results with an optimized (non-Debug) build may be misleading"
      )
    endif()
  else()
    message(FATAL_ERROR "Code coverage requires Clang or GCC. Aborting.")
  if(NOT COVERAGE_ZEROCOUNT_TRACEFILE)
    set(COVERAGE_ZEROCOUNT_TRACEFILE "${COVERAGE_OUTPUT_DIR}/zerocount.info")
  endif()

  if(NOT COVERAGE_CAPTURE_TRACEFILE)
    set(COVERAGE_CAPTURE_TRACEFILE "${COVERAGE_OUTPUT_DIR}/capture.info")
  endif()

# Adds code coverage instrumentation to libraries and executable targets.
# ~~~
# Required:
# TARGET_NAME - Name of the target to generate code coverage for.
# Optional:
# PUBLIC   - Sets the visibility for added compile options to targets to PUBLIC
#            instead of the default of PRIVATE.
# PRIVATE - Sets the visibility for added compile options to targets to
#           INTERFACE instead of the default of PRIVATE.
# ~~~
function(target_code_coverage TARGET_NAME)
  # Argument parsing
  set(options PUBLIC INTERFACE)
  cmake_parse_arguments(target_code_coverage "${options}" "" "" ${ARGN})

  # Set the visibility of target functions to PUBLIC, INTERFACE or default to
  # PRIVATE.
  if(target_code_coverage_PUBLIC)
    set(TARGET_VISIBILITY PUBLIC)
  elseif(target_code_coverage_INTERFACE)
    set(TARGET_VISIBILITY INTERFACE)
  else()
    set(TARGET_VISIBILITY PRIVATE)
  if(NOT COVERAGE_UNIFIED_TRACEFILE)
    set(COVERAGE_UNIFIED_TRACEFILE "${COVERAGE_OUTPUT_DIR}/coverage.info")
  endif()

  if(GKFS_ENABLE_CODE_COVERAGE)

    # Add code coverage instrumentation to the target's linker command
    if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
       OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
      target_compile_options(${TARGET_NAME} ${TARGET_VISIBILITY}
                             -fprofile-instr-generate -fcoverage-mapping)
      target_link_options(${TARGET_NAME} ${TARGET_VISIBILITY}
                          -fprofile-instr-generate -fcoverage-mapping)
    elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES
                                                "GNU")
      target_compile_options(${TARGET_NAME} ${TARGET_VISIBILITY} -fprofile-arcs
        -ftest-coverage)
      target_link_libraries(${TARGET_NAME} ${TARGET_VISIBILITY} gcov)
  if(NOT COVERAGE_HTML_REPORT_DIRECTORY)
    set(COVERAGE_HTML_REPORT_DIRECTORY "${COVERAGE_OUTPUT_DIR}/coverage_html")
  endif()

  if(NOT COVERAGE_XML_REPORT)
    set(COVERAGE_XML_REPORT "${COVERAGE_OUTPUT_DIR}/coverage-cobertura.xml")
  endif()
endfunction()

  # add a `coverage-zerocount` target for the initial baseline gathering of
  # coverage information
  add_custom_command(
    OUTPUT "${COVERAGE_ZEROCOUNT_TRACEFILE}"
    COMMAND
      ${COVERAGE_PY}
        capture
        --initial
        --root-directory "${CMAKE_BINARY_DIR}"
        --output-file "${COVERAGE_ZEROCOUNT_TRACEFILE}"
        --sources-directory "${CMAKE_SOURCE_DIR}"
        "$<$<BOOL:${ARGS_EXCLUDE_DIRECTORIES}>:--exclude;$<JOIN:${ARGS_EXCLUDE_DIRECTORIES},;--exclude;>>"
    COMMAND_EXPAND_LISTS VERBATIM
    COMMENT "Generating zerocount coverage tracefile"
  )

  add_custom_target(coverage-zerocount
    DEPENDS ${COVERAGE_ZEROCOUNT_TRACEFILE})

  # add a `coverage-capture` target to capture coverage data from any
  # of the existing .gcda files
  add_custom_command(
    OUTPUT "${COVERAGE_CAPTURE_TRACEFILE}"
    COMMAND
    ${COVERAGE_PY}
    capture
    --root-directory "${CMAKE_BINARY_DIR}"
    --output-file "${COVERAGE_CAPTURE_TRACEFILE}"
    --sources-directory "${CMAKE_SOURCE_DIR}"
    "$<$<BOOL:${ARGS_EXCLUDE_DIRECTORIES}>:--exclude;$<JOIN:${ARGS_EXCLUDE_DIRECTORIES},;--exclude;>>"
    COMMAND_EXPAND_LISTS VERBATIM
    COMMENT "Generating capture coverage tracefile"
  )

  add_custom_target(coverage-capture DEPENDS ${COVERAGE_CAPTURE_TRACEFILE})

  # add a `coverage-unified` target to merge all coverage data available in
  # ${COVERAGE_OUTPUT_DIR} into a unified coverage trace
  add_custom_command(
    OUTPUT ${COVERAGE_UNIFIED_TRACEFILE}
    DEPENDS ${COVERAGE_CAPTURE_TRACEFILE}
    COMMAND
    ${COVERAGE_PY}
    merge
    --search-pattern "${COVERAGE_OUTPUT_DIR}/*.info"
    --output-file "${COVERAGE_UNIFIED_TRACEFILE}"
    VERBATIM
    COMMENT "Generating unified coverage tracefile"
  )

  add_custom_target(
    coverage-unified
    DEPENDS "${COVERAGE_UNIFIED_TRACEFILE}"
  )

  # add a `coverage-summary` target to print a summary of coverage data
  add_custom_target(
    coverage-summary
    COMMAND
    ${COVERAGE_PY}
    summary
    --input-tracefile ${COVERAGE_UNIFIED_TRACEFILE}
    DEPENDS ${COVERAGE_UNIFIED_TRACEFILE}
    COMMENT "Gathering coverage information"
  )

  # add a `coverage-html` target to generate a coverage HTML report
  add_custom_command(OUTPUT
    "${COVERAGE_HTML_REPORT_DIRECTORY}"
    COMMAND
    ${COVERAGE_PY}
    html_report
    --input-tracefile "${COVERAGE_UNIFIED_TRACEFILE}"
    --prefix "${CMAKE_SOURCE_DIR}"
    --output-directory "${COVERAGE_HTML_REPORT_DIRECTORY}"
    DEPENDS ${COVERAGE_UNIFIED_TRACEFILE}
    VERBATIM
    COMMENT "Generating HTML report"
    )

  add_custom_target(
    coverage-html
    DEPENDS "${COVERAGE_HTML_REPORT_DIRECTORY}")

  # add a `coverage-cobertura` target to generate a Cobertura XML report
  add_custom_command(OUTPUT
    "${COVERAGE_XML_REPORT}"
    COMMAND
    ${COVERAGE_PY}
    cobertura
    --input-tracefile "${COVERAGE_UNIFIED_TRACEFILE}"
    --base-dir "${CMAKE_SOURCE_DIR}"
    --output-file "${COVERAGE_XML_REPORT}"
    DEPENDS ${COVERAGE_UNIFIED_TRACEFILE}
    VERBATIM
    COMMENT "Generating Cobertura report"
    )

  add_custom_target(
    coverage-cobertura
    DEPENDS "${COVERAGE_XML_REPORT}"
  )
  set_target_properties(
    coverage-cobertura PROPERTIES ADDITIONAL_CLEAN_FILES ${COVERAGE_XML_REPORT}
  )
endmacro()
+30 −0
Original line number Diff line number Diff line
################################################################################
# Copyright 2018-2023, Barcelona Supercomputing Center (BSC), Spain            #
# Copyright 2015-2023, Johannes Gutenberg Universitaet Mainz, Germany          #
#                                                                              #
# This software was partially supported by the                                 #
# EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu).    #
#                                                                              #
# This software was partially supported by the                                 #
# ADA-FS project under the SPPEXA project funded by the DFG.                   #
#                                                                              #
# This file is part of GekkoFS.                                                #
#                                                                              #
# GekkoFS is free software: you can redistribute it and/or modify              #
# it under the terms of the GNU General Public License as published by         #
# the Free Software Foundation, either version 3 of the License, or            #
# (at your option) any later version.                                          #
#                                                                              #
# GekkoFS is distributed in the hope that it will be useful,                   #
# but WITHOUT ANY WARRANTY; without even the implied warranty of               #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                #
# GNU General Public License for more details.                                 #
#                                                                              #
# You should have received a copy of the GNU General Public License            #
# along with GekkoFS.  If not, see <https://www.gnu.org/licenses/>.            #
#                                                                              #
# SPDX-License-Identifier: GPL-3.0-or-later                                    #
################################################################################

include(gkfs-code-coverage)
include(GkfsPythonTesting)
 No newline at end of file
+1 −10
Original line number Diff line number Diff line
@@ -107,16 +107,6 @@ configure_file(include/version.hpp.in include/version.hpp)

set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake" ${CMAKE_MODULE_PATH})


################################################################################
## Coverage generation support:
## ============================
## 
## The `gkfs-code-coverage' module enables the GKFS_ENABLE_CODE_COVERAGE option
## as well as the target_code_coverage() function.
################################################################################
include(gkfs-code-coverage)

set(CMAKE_EXPORT_COMPILE_COMMANDS 0)


@@ -301,3 +291,4 @@ if (GKFS_BUILD_TESTS)
else()
    unset(GKFS_TESTS_INTERFACE CACHE)
endif()
+0 −8
Original line number Diff line number Diff line
@@ -101,10 +101,6 @@ target_sources(gkfs_intercept ${PRELOAD_SOURCES})
target_link_libraries(gkfs_intercept PRIVATE ${PRELOAD_LINK_LIBRARIES})
target_include_directories(gkfs_intercept PRIVATE ${PRELOAD_INCLUDE_DIRS})

if(GKFS_ENABLE_CODE_COVERAGE)
  target_code_coverage(gkfs_intercept AUTO)
endif()

install(TARGETS gkfs_intercept
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
@@ -133,10 +129,6 @@ if (GKFS_ENABLE_FORWARDING)
    target_link_libraries(gkfwd_intercept PRIVATE ${PRELOAD_LINK_LIBRARIES})
    target_include_directories(gkfwd_intercept PRIVATE ${PRELOAD_INCLUDE_DIRS})

    if(GKFS_ENABLE_CODE_COVERAGE)
      target_code_coverage(gkfwd_intercept AUTO)
    endif()

    install(TARGETS gkfwd_intercept
        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+0 −18
Original line number Diff line number Diff line
@@ -65,12 +65,6 @@ endif()
      ${PROMETHEUS_LINK_LIBRARIES}
  )


if(GKFS_ENABLE_CODE_COVERAGE)
  target_code_coverage(distributor AUTO)
  target_code_coverage(statistics AUTO)
endif()

# get spdlog
set(FETCHCONTENT_QUIET ON)

@@ -103,10 +97,6 @@ target_link_libraries(log_util
    spdlog
    )

if(GKFS_ENABLE_CODE_COVERAGE)
  target_code_coverage(log_util AUTO)
endif()

add_library(env_util STATIC)
set_property(TARGET env_util PROPERTY POSITION_INDEPENDENT_CODE ON)
target_sources(env_util
@@ -117,10 +107,6 @@ target_sources(env_util
    ${CMAKE_CURRENT_LIST_DIR}/env_util.cpp
    )

if(GKFS_ENABLE_CODE_COVERAGE)
  target_code_coverage(env_util AUTO)
endif()

add_library(metadata STATIC)
set_property(TARGET metadata PROPERTY POSITION_INDEPENDENT_CODE ON)
target_sources(metadata
@@ -134,10 +120,6 @@ target_link_libraries(metadata
    fmt::fmt
    )

if(GKFS_ENABLE_CODE_COVERAGE)
  target_code_coverage(metadata AUTO)
endif()

add_library(path_util
  STATIC
  )
Loading