【问题标题】:Add *.gcno and *.gcda files to the make clean rule using CMake使用 CMake 将 *.gcno 和 *.gcda 文件添加到 make clean 规则
【发布时间】:2014-11-15 19:10:20
【问题描述】:

我的项目结构类似:

├── CMakeLists.txt
├── src
│   ├── logic.cpp
│   └── logic.h
└── test
    ├── CMakeLists.txt
    └── logic_test.cpp

主要的CMakeLists.txt文件是:

cmake_minimum_required (VERSION 2.8)
project (Logic)
set (Logic_SOURCES ${PROJECT_SOURCE_DIR}/src/logic.cpp)
include_directories (${PROJECT_SOURCE_DIR}/src)
add_library (logic SHARED ${Logic_SOURCES})
add_subdirectory (test)

而用于测试的CMakeLists.txt 是:

find_package (GTest)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g -march=native -mtune=native -fprofile-arcs -ftest-coverage")
set (CMAKE_EXE_LINKER_FLAGS "-fprofile-arcs -ftest-coverage")
set (Test_SOURCES ${Logic_SOURCES} ${PROJECT_SOURCE_DIR}/test/logic_test.cpp)
add_executable (logic_test ${Test_SOURCES})
target_link_libraries (${TestName} gtest gtest_main gcov pthread)

为了处理测试覆盖率报告,我将自定义目标添加到 test/CMakeLists.txt

set (Coverage_REPORT ${PROJECT_BINARY_DIR}/coverage.info)
set (Coverage_DIR    ${PROJECT_BINARY_DIR}/coverage)
add_custom_command (
    OUTPUT  ${Coverage_REPORT}
    COMMAND lcov -q -c -f -b . -d ${PROJECT_BINARY_DIR}/test -o ${Coverage_REPORT}
    COMMAND lcov -e ${Coverage_REPORT} '${PROJECT_SOURCE_DIR}/src/*' -o ${Coverage_REPORT}
    COMMAND genhtml ${Coverage_REPORT} --legend --demangle-cpp -f -q -o ${Coverage_DIR}
    DEPENDS logic_test
)
add_custom_target (coverage DEPENDS ${Coverage_REPORT})

所有这些代码都按预期正常工作。工作流程如下所示:

mkdir build
cd build
cmake ..
make
./test/logictest
make coverage

但现在我想将测试覆盖率工件添加到 make clean 规则中。我已经尝试将此代码添加到test/CMakeLists.txt:

file (GLOB_RECURSE Test_GCNOS ${PROJECT_BINARY_DIR}/*.gcno)
file (GLOB_RECURSE Test_GCDAS ${PROJECT_BINARY_DIR}/*.gcda)
list (APPEND Test_COVERAGE_DATA "${Coverage_REPORT}")
list (APPEND Test_COVERAGE_DATA "${Coverage_DIR}")
list (APPEND Test_COVERAGE_DATA "${Coverage_GCNO}")
list (APPEND Test_COVERAGE_DATA "${Coverage_GCDA}")
set_directory_properties (PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${Test_COVERAGE_DATA}")

但是这种方法不能按预期工作(对我来说)。调用cmake .. 时工件尚不存在,因此变量Coverage_DATA 为空,这需要在运行测试后调用cmake ..。这看起来很难看(对我来说)。

所以我的问题是:如何将测试覆盖率工件添加到 make clean 规则中?

【问题讨论】:

  • 你不应该更新你的问题并包括它的答案。相反,您可以为自己的问题添加答案。让事情变得更加清晰(您甚至会为此获得徽章)。

标签: makefile cmake lcov test-coverage


【解决方案1】:

如果您正在做一个开源项目,我已经编写了一个代码覆盖率 cmake 脚本,它很容易与http://coveralls.io/ 一起使用:

https://github.com/JoakimSoderberg/coveralls-cmake https://github.com/JoakimSoderberg/coveralls-cmake-example

但是,如果您想要一个简单地在本地生成覆盖数据的脚本,则可以使用以下项目:

https://github.com/bilke/cmake-modules/blob/master/CodeCoverage.cmake

我解决了清理覆盖数据的问题,方法是始终先在覆盖目标中删除它,然后再生成新数据。这不是一个干净的解决方案,但工作正常:

file(REMOVE_RECURSE ${PROJECT_BINARY_DIR}/*.gcda)

(我几乎从不使用 make clean,而是使用多个构建目录和偶尔的rm -rf *。在我看来,make clean 的整个概念被打破了,将构建和源分开可以让事情变得更干净)

【讨论】:

  • I have written a code coverage cmake script ,我从未听说过coveralls.io,但它看起来很有趣,谢谢。 I solved the problem with cleaning the coverage data by always,优雅,但这并不是我想要的。 if you want a script that simply generates the coverage data locally there is this project,谢谢你的建议我会尝试使用它。
【解决方案2】:

这不能称为优雅的解决方案,但在我看来它比cmakerecall 更好。 我已经尝试创建宏,它通过源文件列表确定覆盖工件(*.gcno*.gcda 文件)的名称:

macro (determine_coverage_data Sources TestName Artifacts Suffix)
set (CoverageDirectory "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${TestName}.dir")
foreach (File ${Sources})
    string (REGEX MATCH "^${CMAKE_CURRENT_SOURCE_DIR}*" Directory "${File}")
    if    (Directory STREQUAL CMAKE_CURRENT_SOURCE_DIR)
        string (REGEX REPLACE "^${CMAKE_CURRENT_SOURCE_DIR}*" "${CoverageDirectory}" File "${File}")
    else  (Directory STREQUAL CMAKE_CURRENT_SOURCE_DIR)
        string (REGEX REPLACE "/" ";" A "${CMAKE_CURRENT_SOURCE_DIR}")
        string (REGEX REPLACE "/" ";" B "${File}")
        list (LENGTH A DeepDirectory)
        list (LENGTH B DeepFile)
        set (File "${CoverageDirectory}")
        set (I 1)
        while    (I LESS DeepDirectory)
            list (GET A ${I} AI)
            list (GET B ${I} BI)
            if    (AI STREQUAL BI)
                math (EXPR I "${I} + 1")
            else  (AI STREQUAL BI)
                math (EXPR DeepDiff "${DeepFile} - ${I} - 1")
                while    (DeepDiff GREATER 0)
                    set (File "${File}/__")
                    math (EXPR DeepDiff "${DeepDiff} - 1")
                endwhile (DeepDiff GREATER 0)
                while    (I LESS DeepFile)
                    list (GET B ${I} BI)
                    set (File "${File}/${BI}")
                    math (EXPR I "${I} + 1")
                endwhile (I LESS DeepFile)
            endif (AI STREQUAL BI)
        endwhile (I LESS DeepDirectory)
    endif (Directory STREQUAL CMAKE_CURRENT_SOURCE_DIR)
    set (${Artifacts} ${${Artifacts}} "${File}${Suffix}")
endforeach (File)
endmacro (determine_coverage_data)

所以完整的解决方案是这样的:

├── cmake
│   └── UseGCov.cmake
├── CMakeLists.txt
├── src
│   ├── logic.cpp
│   └── logic.h
└── test
    ├── CMakeLists.txt
    └── logic_test.cpp

主要的CMakeLists.txt文件是:

cmake_minimum_required (VERSION 2.8)
project (Logic)
set (Logic_SOURCES ${PROJECT_SOURCE_DIR}/src/logic.cpp)
# Include macro.
include ("${PROJECT_SOURCE_DIR}/cmake/UseGCov.cmake")
include_directories (${PROJECT_SOURCE_DIR}/src)
add_library (logic SHARED ${Logic_SOURCES})
add_subdirectory (test)

而用于测试的CMakeLists.txt 是:

find_package (GTest)

set (Test_SOURCES ${Logic_SOURCES} ${PROJECT_SOURCE_DIR}/test/logic_test.cpp)
set (TestName logic_test)

set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g -march=native -mtune=native -fprofile-arcs -ftest-coverage")
set (CMAKE_EXE_LINKER_FLAGS "-fprofile-arcs -ftest-coverage")

# Call macro.
determine_coverage_data ("${Test_SOURCES}" "${TestName}" Test_GCNOS ".gcno")
determine_coverage_data ("${Test_SOURCES}" "${TestName}" Test_GCDAS ".gcda")

set (Coverage_REPORT ${PROJECT_BINARY_DIR}/coverage.info)
set (Coverage_DIR    ${PROJECT_BINARY_DIR}/coverage)
add_custom_command (
    OUTPUT  ${Coverage_REPORT}
    COMMAND lcov -q -c -f -b . -d ${PROJECT_BINARY_DIR}/test -o ${Coverage_REPORT}
    COMMAND lcov -e ${Coverage_REPORT} '${PROJECT_SOURCE_DIR}/src/*' -o ${Coverage_REPORT}
    COMMAND genhtml ${Coverage_REPORT} --legend --demangle-cpp -f -q -o ${Coverage_DIR}
    DEPENDS logic_test
)
add_custom_target (coverage DEPENDS ${Coverage_REPORT})

set (Test_COVERAGE_DATA ${Coverage_REPORT} ${Coverage_DIR} ${Test_GCNOS} ${Test_GCDAS})
set_directory_properties (PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${Test_COVERAGE_DATA}")
add_executable (${TestName} ${Test_SOURCES})
target_link_libraries (${TestName} gtest gtest_main gcov pthread)

【讨论】: