Search code examples
cmakegcovgcovr

GCOVR how to cover shared library, no GCNO file generated


I'm trying to get code coverage from a simple test project using a shared library, but for some reason, my XML file is empty, and no .gcno file is generated for the shared library. The only .gcno file I have is from the unit test executable. Also, no .gcda file will be generated at all. I have the following folder structure:

ProjectRoot/
|- src/
|  |- calculator/
|  |  |- CMakeLists.txt
|  |  |- Calculator.cpp
|  |  |- Calculator.h
|  |- CMakeLists.txt
|  |- sources.cpp
|  |- sources.h
|- tests/
|  |- CMakelists.txt
|  |- unit-test1.cpp
|- CMakeLists.txt

Root CMakeLists.txt file:

cmake_minimum_required (VERSION 3.14)
enable_testing()
project("testProject")

add_subdirectory("src")

if (CMAKE_BUILD_TYPE MATCHES Debug)
    add_subdirectory("tests")
endif()

src CMakeLists.txt file:

project ("main")
add_subdirectory("calculator")

add_executable(${PROJECT_NAME} "sources.cpp" "sources.h")
target_link_libraries(${PROJECT_NAME} "calculator")

if (CMAKE_VERSION VERSION_GREATER 3.12)
    set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20)
endif()

Calculator CMakeLists.txt file:

project("calculator")

add_library(${PROJECT_NAME} SHARED "Calculator.h" "Calculator.cpp")

if (CMAKE_VERSION VERSION_GREATER 3.12)
    set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20)
endif()

(unit)test CMakeLists.txt file

set(UNIT-TESTS "unit-tests")
include(CTest)

include(FetchContent)
FetchContent_Declare(
    googletest
    GIT_REPOSITORY https://github.com/google/googletest.git
    GIT_TAG v1.14.0)

set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)

# This will generate an empty code coverage result and only a unit-test1.cpp.gcno file
add_executable(${UNIT-TESTS} unit-test1.cpp)
target_link_libraries(${UNIT-TESTS} GTest::gtest_main calculator)

# if I change the executable to this I will have the correct code coverage result 
# and 2 .gcno files: unit-test1.cpp.gcno and Calculator.cpp.gcno
# But no .gcda file is generated (unsure if that is significant).
# add_executable(${UNIT-TESTS} unit-test1.cpp "../src/calculator/Calculator.h" "../src/calculator/Calculator.cpp")
# target_link_libraries(${UNIT-TESTS} GTest::gtest_main)

include(GoogleTest)
gtest_discover_tests(${UNIT-TESTS})

set(CMAKE_MODULE_PATH /usr/src/CMake)
include(CodeCoverage)
append_coverage_compiler_flags(${UNIT-TESTS})

set(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} -O0")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0")

set(GCOVR_PATH "/usr/bin/gcovr")
setup_target_for_coverage_gcovr_xml(
        NAME CoverageXml
        EXECUTABLE ${UNIT-TESTS}
        DEPENDENCIES ${UNIT-TESTS}
        EXECUTABLE_ARGS "--gtest_output=xml:report/"
        EXCLUDE "out/" "tests/")

For the functions append_coverage_compiler_flags() and setup_target_for_coverage_gcovr_xml() please look here https://github.com/bilke/cmake-modules/blob/master/CodeCoverage.cmake. This module has already been loaded into the docker image I'm using. To compile and run the coverageXml and unit-tests target I use the following commands:

cmake -S ./ -B ./out/build/ -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=clang-16 -DCMAKE_CXX_COMPILER=clang++-16
cmake --build ./out/build/ --parallel 2 --target CoverageXml unit-tests

Any ideas to solve the issue I'm facing and still be able to use a shared library are greatly appreciated!


Solution

  • Thanks to Tsyvarev for pushing me the right way. With the current cmake files and cmake command. the flags -g and --coverage are not given to the calculator library. I found 2 possible fixes either adding this to the library:

    set(CMAKE_MODULE_PATH /usr/src/CMake)
    include(CodeCoverage)
    append_coverage_compiler_flags(${PROJECT_NAME})
    

    Which definitely works for this project, but I have another project much larger. The fix I use there is to add the -g and --coverage flags to the command as such:

    -DCMAKE_CXX_FLAGS="-g --coverage"
    

    When testing this solution (second option) out on the other project where I had the same issue, I noticed that using the project() function in the CMakeLists.txt file in the unit-tests folder was causing another problem.

    The .gcno files would generate (for executable and libraries), but the XML file would only contain the unit-tests files it self and not the executable of library. Simply removing this line from the CMakeLists.txt file resolved the issue. If someone could explain why this causes this issue, please tell me as I have no clue.