bilke / cmake-modules

Additional CMake functionality. Most of the modules are from Ryan Pavlik (https://github.com/rpavlik/cmake-modules)
Boost Software License 1.0
542 stars 215 forks source link

EXCLUDEs additional directories which starts with the same name #68

Open sakthi-vivek opened 2 years ago

sakthi-vivek commented 2 years ago

Hello,

# Folder structure
.
|-- src/
|-- test/
|-- testmodules/
|-- CMakeLists.txt
setup_target_for_coverage_gcovr_html(
        NAME ${PROJECT_NAME}_coverage_html
        EXECUTABLE ${PROJECT_NAME} --gtest_output=xml:report.xml
        BASE_DIRECTORY ${CMAKE_SOURCE_DIR}
        EXCLUDE "test/*")

In the above example, the testmodules also get excluded even if it was just the test folder that I wanted to exclude.

Any help is appreciated. Thank you.

qgymib commented 2 years ago

Checkout append_coverage_compiler_flags_to_target(). If your cmake rule was target-based, you can generate report for specific target.

sakthi-vivek commented 2 years ago

Thank you for your input. But could you expand on how this would help in the above context? I was having the trouble of excluding a directory (test) whose name was the initial part of another directory (testmodules) that I wanted to be included as part of the report.

qgymib commented 2 years ago

A minimal example:

CMakeLists.txt for src:

add_library(foo
    "src/foo1.c"
    "src/foo2.c")

# Only setup coverage for your target `foo`
include(CodeCoverage)
append_coverage_compiler_flags_to_target(foo)

CMakeLists.txt for test:

# This is your test code
add_executable(bar
    "test/test1.c"
    "test/test2.c")
target_link_libraries(bar PRIVATE foo)
add_test(bar bar)

# Here we generate coverage report
setup_target_for_coverage_lcov(
    NAME coverage
    EXECUTABLE $<TARGET_FILE:bar>
)

In the above example, the following files will have report:

And following files does not generate report:

sakthi-vivek commented 2 years ago

The issue is actually with the keyword EXCLUDE. The example I provided above was just to provide some context to the issue. I shall try to explain better with a bit more elaborately another example.

# Folder structure
.
|-- CMakeLists.txt
|-- CodeCoverage.cmake
|-- src/
    |-- cpp/
    |-- core.hpp
    |-- cppmodules/
    |-- module_a/
             |-- module_a.hpp
         |-- module_a.cpp
    |-- module_b/
|-- test/
    |-- src/
    |-- cppmodules/
        |-- module_a/
            |-- module_a_test.cpp

So under the src folder I have placed a cpp folder which can contains core header files included by classes in cppmodules/module_*. The test folder contains *_test.cpp files for testing the corresponding module.

Now, when I perform a coverage test for every module I do not want to include the files under src/cpp/*. However, they naturally are included when building the test executable. So as seen in the CMakeLists.txt file I need to EXCLUDE the src/cpp/*.

This is where I face the issue where EXCLUDE also removes the needed src/cppmodules/module_a/module_a.cpp from the report. As of now, I have to perform a hack like excluding src/cpp/c* or renaming the cpp folder, in-order to get the coverage report of the module source file.

I have pasted below the code that was used to recreate the issue.

# CMakeLists.txt
cmake_minimum_required(VERSION 3.16)

project(module_a_test LANGUAGES CXX)

set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS_DEBUG "-Og -g3")

set(CMAKE_MODULE_PATH ${module_a_test_SOURCE_DIR})

include(FetchContent)
FetchContent_Declare(
    googletest
    URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip
)
set(BUILD_GMOCK OFF CACHE BOOL "" FORCE)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)

if(CMAKE_BUILD_TYPE STREQUAL "Debug")
    include(CodeCoverage)
    add_definitions("-DPFTP_TRACE_OUTPUT")
    set(GCOVR_ADDITIONAL_ARGS -s --xml coverage.xml --exclude-throw-branches ${PROJECT_BINARY_DIR})
    setup_target_for_coverage_gcovr_html(
        NAME ${PROJECT_NAME}_coverage_html
        EXECUTABLE ${PROJECT_NAME} --gtest_output=xml:report.xml
        BASE_DIRECTORY ${module_a_test_SOURCE_DIR}
        EXCLUDE "build/*" "Debug/*" "src/cpp/*") # !!! This exlcudes src/cppmodules/* aswell. I lose the coverage report of module_a.cpp
    append_coverage_compiler_flags()
endif()

include_directories(
        test/src/cppmodules/module_a
        src/cppmodules/module_a
        src/cpp
)

add_executable( ${PROJECT_NAME}
        src/cppmodules/module_a/module_a.cpp
        test/src/cppmodules/module_a/module_a_test.cpp
)

target_link_libraries(${PROJECT_NAME} gtest_main)
// module_a_test.cpp
#include <gtest/gtest.h>
#include "module_a.hpp"

TEST(ModuleA, IsCorrectModuleType)
{
    ModuleA a;
    EXPECT_TRUE(a.type == ModuleType::A);
}
// module_a.hpp
#ifndef MODULE_A_HPP_
#define MODULE_A_HPP_
#include "core.hpp"

class ModuleA {
public:
    ModuleA();
    ~ModuleA() = default;

    ModuleType type = ModuleType::A;
};

#endif
// module_a.cpp
#include "module_a.hpp"

ModuleA::ModuleA()
{

}
// core.hpp
#ifndef CORE_HPP_
#define CORE_HPP_
enum class ModuleType {
    NONE = -1,
    A = 0,
    B = 1
};
#endif

Run the following after recreating the above setup to generate the coverage report,

# Install cmake >=3.16 and gcovr.
cmake -DCMAKE_BUILD_TYPE=Debug -B Debug
make -C Debug/ module_a_test_coverage_html
# See ./Debug/module_a_test_coverage_html