Commit 6d4b20f2 authored by Alberto Miranda's avatar Alberto Miranda ♨️
Browse files

Merge branch 'amiranda/252-ci-simplify-coverage-scripts' into 'master'

Resolve "CI: Simplify coverage scripts"

This MR simplifies coverage generation in the following ways:

- Replaces the old `scripts/ci/coverage.sh` script with a new
 `scripts/dev/coverage.py` written in Python, making it more robust 
 and simpler to modify if needed.
- Adds specific CMake targets for generating coverage reports 
  directly from CMake.
- Adds specialized CMake presets to make it simpler to configure
  the different builds required to generate coverage information.
- Replaces `gcovr` with `lcov` + `lcov_cobertura`, since `gcovr` 
  exhibited some errors that were difficult to track down and 
  `lcov` worked out of the box.
- Adds HTML documentation about the coverage generation in 
  `docs/sphinx/devs/coverage.md`

This MR also updates the `v0.9.2` Docker images to update CMake
and include coverage-related packages. The `coverage` image in 
particular is no longer needed and has been removed.

Closes #252 #256 #257 #258

Closes #258, #257, #256, and #252

See merge request !163
parents 7fe3ab07 bc235cf5
Loading
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -86,4 +86,8 @@ playground
.hidden_playground/
test/build/
build/
builds/
.run/

# Allow users to provide their own CMake presets
CMakeUserPresets.json
+96 −84
Original line number Diff line number Diff line
@@ -12,7 +12,7 @@ variables:
  BUILD_PATH:                   "${CI_PROJECT_DIR}/gkfs/build"
  INSTALL_PATH:                 "${CI_PROJECT_DIR}/gkfs/install"
  INTEGRATION_TESTS_BIN_PATH:   "${CI_PROJECT_DIR}/gkfs/install/share/gkfs/tests/integration"
  COVERAGE_PATH:                "${CI_PROJECT_DIR}/gkfs/build/.coverage"
  COVERAGE_PATH:                "${CI_PROJECT_DIR}/gkfs/build/coverage"
  PYTEST:                       "${CI_PROJECT_DIR}/gkfs/install/share/gkfs/tests/integration/pytest-venv/bin/py.test"
  BATS:                         "${CI_PROJECT_DIR}/tests/scripts/bats/bin/bats"
  LD_LIBRARY_PATH:              "${CI_PROJECT_DIR}/deps/install/lib:${CI_PROJECT_DIR}/deps/install/lib64"
@@ -55,23 +55,8 @@ gkfs:
    - sed -i 's/constexpr auto use_mtime = false;/constexpr auto use_mtime = true;/g' "${CI_PROJECT_DIR}/include/config.hpp"
    - sed -i 's/constexpr auto use_link_cnt = false;/constexpr auto use_link_cnt = true;/g' "${CI_PROJECT_DIR}/include/config.hpp"
    - sed -i 's/constexpr auto use_blocks = false;/constexpr auto use_blocks = true;/g' "${CI_PROJECT_DIR}/include/config.hpp"
    - mkdir -p ${BUILD_PATH} && cd ${BUILD_PATH}
    - cmake
      -Wdev
      -Wdeprecate
      -DCMAKE_BUILD_TYPE=Debug
      -DGKFS_ENABLE_CODE_COVERAGE:BOOL=ON
      -DGKFS_BUILD_TESTS:BOOL=ON
      -DGKFS_INSTALL_TESTS:BOOL=ON
      -DCMAKE_INSTALL_PREFIX=${INSTALL_PATH}
      -DGKFS_USE_GUIDED_DISTRIBUTION:BOOL=ON
      -DGKFS_ENABLE_PARALLAX:BOOL=ON
      -DGKFS_ENABLE_ROCKSDB:BOOL=ON
      -DGKFS_CHUNK_STATS:BOOL=ON
      -DGKFS_ENABLE_PROMETHEUS:BOOL=ON
      -DGKFS_RENAME_SUPPORT:BOOL=ON
      ${CI_PROJECT_DIR}
    - make -j$(nproc) install
    - cmake --preset ci-coverage -DCOVERAGE_OUTPUT_DIR=${COVERAGE_PATH}
    - cmake --build ${BUILD_PATH} -j $(nproc) --target install
    # reduce artifacts size
    - ${CI_SCRIPTS_DIR}/trim_build_artifacts.sh ${BUILD_PATH}
  artifacts:
@@ -86,21 +71,8 @@ gkfwd:
  interruptible: true
  needs: []
  script:
    - mkdir -p ${BUILD_PATH} && cd ${BUILD_PATH}
    - cmake
      -Wdev
      -Wdeprecate
      -DCMAKE_BUILD_TYPE=Debug
      -DGKFS_ENABLE_CODE_COVERAGE:BOOL=ON
      -DGKFS_BUILD_TESTS:BOOL=ON
      -DGKFS_INSTALL_TESTS:BOOL=ON
      -DGKFS_ENABLE_FORWARDING:BOOL=ON
      -DGKFS_ENABLE_AGIOS:BOOL=ON
      -DGKFS_ENABLE_PARALLAX:BOOL=OFF
      -DGKFS_ENABLE_ROCKSDB:BOOL=ON
      -DCMAKE_INSTALL_PREFIX=${INSTALL_PATH}
      ${CI_PROJECT_DIR}
    - make -j$(nproc) install
    - cmake --preset ci-forwarding-coverage
    - cmake --build ${BUILD_PATH} -j $(nproc) --target install
    # reduce artifacts size
    - ${CI_SCRIPTS_DIR}/trim_build_artifacts.sh ${BUILD_PATH}
  artifacts:
@@ -150,15 +122,18 @@ gkfs:integration:
          --basetemp=${BUILD_PATH}/tests/run/${SUBTEST}
          --junit-xml=report.xml

    ## capture coverage information
    - cd ${BUILD_PATH}
    - ${CI_SCRIPTS_DIR}/coverage.sh
          --verbose
          --capture integration_${SUBTEST}
          --root-dir ${CI_PROJECT_DIR}
          --build-dir ${BUILD_PATH}
          --exclusions "${CI_SCRIPTS_DIR}/.coverage-exclusions"
          --log-file "${COVERAGE_PATH}/partial/integration_${SUBTEST}/capture.log"
    ## capture coverage information for this test and write it to
    ## $COVERAGE_PATH/$SUBTEST.info
    - cd ${CI_PROJECT_DIR}
    - cmake --preset ci-coverage
          -DCOVERAGE_OUTPUT_DIR=${COVERAGE_PATH}
          -DCOVERAGE_CAPTURE_TRACEFILE=${COVERAGE_PATH}/${SUBTEST}.info
    ## Since the pipeline recreates the source tree, the access times for .gcno
    ## files are newer than those of .gcda files. This makes gcov emit a
    ## warning for each file which slows it down. Updating the timestamps
    ## avoids this
    - find ${BUILD_PATH} -name "*.gcno" -exec touch {} \;
    - cmake --build ${BUILD_PATH} --target coverage-capture

  # fix relative paths so that GitLab can find the correct files
  after_script:
@@ -195,15 +170,18 @@ gkfwd:integration:
          --basetemp=${BUILD_PATH}/tests/run/${SUBTEST}
          --junit-xml=report.xml

    ## capture coverage information
    - cd ${BUILD_PATH}
    - ${CI_SCRIPTS_DIR}/coverage.sh
          --verbose
          --capture integration_${SUBTEST}
          --root-dir ${CI_PROJECT_DIR}
          --build-dir ${BUILD_PATH}
          --exclusions "${CI_SCRIPTS_DIR}/.coverage-exclusions"
          --log-file "${COVERAGE_PATH}/partial/integration_${SUBTEST}/capture.log"
    ## capture coverage information for this test and write it to
    ## $COVERAGE_PATH/$SUBTEST.info
    - cd ${CI_PROJECT_DIR}
    - cmake --preset ci-coverage
          -DCOVERAGE_OUTPUT_DIR=${COVERAGE_PATH}
          -DCOVERAGE_CAPTURE_TRACEFILE=${COVERAGE_PATH}/${SUBTEST}.info
    ## Since the pipeline recreates the source tree, the access times for .gcno
    ## files are newer than those of .gcda files. This makes gcov emit a
    ## warning for each file which slows it down. Updating the timestamps
    ## avoids this
    - find ${BUILD_PATH} -name "*.gcno" -exec touch {} \;
    - cmake --build ${BUILD_PATH} --target coverage-capture

  # fix relative paths so that GitLab can find the correct files
  after_script:
@@ -230,18 +208,25 @@ gkfs:unit:
    ## Add path to mkfs.kreon
    - export PATH=${PATH}:/usr/local/bin
    ## run actual tests
    - cd ${BUILD_PATH}/tests/unit
    - ctest -j $(nproc) -L unit::all --output-junit report.xml

    ## capture coverage information
    - cd ${BUILD_PATH}
    - ${CI_SCRIPTS_DIR}/coverage.sh
          --verbose
          --capture unit
          --root-dir ${CI_PROJECT_DIR}
          --build-dir ${BUILD_PATH}
          --exclusions "${CI_SCRIPTS_DIR}/.coverage-exclusions"
          --log-file "${COVERAGE_PATH}/partial/unit/capture.log"
    - cd ${CI_PROJECT_DIR}
    - ctest --test-dir ${BUILD_PATH}
          -j $(nproc)
          -L unit::all
          --output-junit ${BUILD_PATH}/tests/unit/report.xml

    ## capture coverage information for this test and write it to
    ## $COVERAGE_PATH/unit.info
    - cd ${CI_PROJECT_DIR}
    - cmake --preset ci-coverage
          -DCOVERAGE_OUTPUT_DIR=${COVERAGE_PATH}
          -DCOVERAGE_CAPTURE_TRACEFILE=${COVERAGE_PATH}/unit.info
    ## Since the pipeline recreates the source tree, the access times for .gcno
    ## files are newer than those of .gcda files. This makes gcov emit a
    ## warning for each file which slows it down. Updating the timestamps
    ## avoids this
    - find ${BUILD_PATH} -name "*.gcno" -exec touch {} \;
    - cmake --build ${BUILD_PATH} --target coverage-capture

  artifacts:
    expire_in: 1 week
    paths:
@@ -270,16 +255,8 @@ documentation:
      when: always

  script:
    - mkdir -p ${BUILD_PATH} && cd ${BUILD_PATH}
    - cmake
      -Wdev
      -Wdeprecate
      -DCMAKE_BUILD_TYPE=Debug
      -DGKFS_BUILD_DOCUMENTATION:BOOL=ON
      -DCMAKE_PREFIX_PATH=${DEPS_INSTALL_PATH}
      -DCMAKE_INSTALL_PREFIX=${INSTALL_PATH}
      ${CI_PROJECT_DIR}
    - make docs
    - cmake --preset ci-docs
    - cmake --build ${BUILD_PATH} --target docs
  artifacts:
    paths:
      - ${BUILD_PATH}/docs
@@ -289,25 +266,60 @@ documentation:
################################################################################
## Generation of code coverage reports
################################################################################

## == coverage baseline ====================
coverage:baseline:
  stage: report
  image: gekkofs/testing:0.9.2
  interruptible: true
  needs: ['gkfs', 'gkfwd']

  script:
    ## capture initial coverage information to establish a baseline
    ## and write it to $COVERAGE_PATH/zerocount.info
    - cd ${CI_PROJECT_DIR}
    - cmake --preset ci-coverage
      -DCOVERAGE_OUTPUT_DIR=${COVERAGE_PATH}
      -DCOVERAGE_ZEROCOUNT_TRACEFILE=${COVERAGE_PATH}/zerocount.info
    ## Since the pipeline recreates the source tree, the access times for .gcno
    ## files are newer than those of .gcda files. This makes gcov emit a
    ## warning for each file which slows it down. Updating the timestamps
    ## avoids this
    - find ${BUILD_PATH} -name "*.gcno" -exec touch {} \;
    - cmake --build ${BUILD_PATH} --target coverage-zerocount

  artifacts:
    expire_in: 1 week
    when: always
    paths:
      - ${COVERAGE_PATH}/zerocount.info

coverage:
  stage: report
  image: gekkofs/coverage:0.9.2
  needs: [ 'gkfs:integration', 'gkfwd:integration', 'gkfs:unit' ]
  image: gekkofs/testing:0.9.2
  needs: [ 'coverage:baseline', 'gkfs:integration', 'gkfwd:integration',
           'gkfs:unit' ]
  script:
    - cd ${BUILD_PATH}
    ## merge the partial coverage files from each test in the pipeline
    - ${CI_SCRIPTS_DIR}/coverage.sh
          --verbose
          --merge
          --root-dir ${CI_PROJECT_DIR}
          --build-dir ${BUILD_PATH}
    - cd ${CI_PROJECT_DIR}
    - cmake
          --preset ci-coverage
          -DCOVERAGE_OUTPUT_DIR=${COVERAGE_PATH}
          -DCOVERAGE_UNIFIED_TRACEFILE=${COVERAGE_PATH}/coverage.info
          -DCOVERAGE_HTML_REPORT_DIRECTORY=${COVERAGE_PATH}/coverage_html
          -DCOVERAGE_XML_REPORT=${COVERAGE_PATH}/coverage-cobertura.xml
    - cmake
          --build ${BUILD_PATH}
          --target coverage-html
          --target coverage-cobertura
          --target coverage-summary
  coverage: '/lines[\.]+\: (\d+\.\d+)\%/'
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: ${BUILD_PATH}/.coverage/coverage-cobertura.xml
        path: ${COVERAGE_PATH}/coverage-cobertura.xml
    paths:
      - ${BUILD_PATH}/.coverage
      - ${COVERAGE_PATH}
    expire_in: 2 weeks


+4 −0
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- Added PowerPC support ([!151](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/151)).
- GKFS_RENAME_SUPPORT adds support for renaming files. It includes the use case of renaming opened files using the fd
- FLOCK and fcntl functions for locks, are not supported, but they are available.
- Added support for [CMake presets](https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html) to simplify build 
  configurations ([!163](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/163#note_8179)).

### Changed

@@ -26,6 +28,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
  tests ([!145](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/145)).
- Support parallelism for symlink tests ([!147](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/147)).
- Update Parallax release (PARALLAX-exp) ([!158](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/158)
- Improved and simplified coverage generation procedures for developers with
  specific CMake targets ([!163](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/163#note_8179)).

### Removed

+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()
Loading