I want to set up my project with CMake such that it can be developed using TDD, which means testing internal headers. But setting up the library correctly with CMake hides these implementation details (and correctly so for external use) from my unit tests.
Given a file and folder structure like this:
Foo
|-- include
| `-- Foo
| `-- Foo.h
|-- CmakeLists.txt
|-- src
| |-- Bar
| | |-- Bar.h
| | `-- Bar.cpp
| |-- Baz
| | |-- Baz.h
| | `-- Baz.cpp
| |-- Foo.cpp
| `-- CMakeLists.txt
`-- test
|-- Bar
| `-- BarTest.cpp
|-- Baz
| `-- BazTest.cpp
|-- FooTest.cpp
`-- CMakeLists.txt
Foo/CMakeLists.txt
project(Foo)
include(CTest)
add_subdirectory(src)
if(BUILD_TESTING)
add_subdirectory(test)
endif()
Foo/src/CMakeLists.txt
add_library(Foo)
target_include_directories(Foo
PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE
${CMAKE_CURRENT_LIST_DIR}
)
target_sources(Foo
PRIVATE
Foo.cpp
Bar/Bar.cpp
Baz/Baz.cpp
)
Foo/test/CMakeLists.txt
find_package(Catch2 REQUIRED)
include(Catch)
add_executable(FooTest
FooTest.cpp
Bar/BarTest.cpp
Baz/BazTest.cpp
)
target_link_libraries(FooTest
PRIVATE
Foo
Catch2::Catch2
)
The problem now is that FooTest links against Foo in a way that other projects do when they need the Foo library as a dependency. This creates the problem that I can't run unit tests in Bar/BarTest.cpp and Baz/BazTest.cpp which includes the files #include "Bar/Bar.h" and #include "Baz/Baz.h. Ideally in my mind a solution would be able to get an internal shortcut target to Foo which includes everything as PUBLIC which then I can run tests on. But when installed the internal headers are private and only files in include/Foo/ is public.
Solutions I've seen is either to create many sub targets and include just what you need for each test. But this seems cumbersome and fits not very good with package managers such as Conan in a very modular setup.
Other solution is to create a duplicate Foo target where everything is public, but this requires me to write everything twice. Sounds like a dirty way to me.
A final solution I thought about is to create an internal target FooInternal with every headers and sources set to public which then FooTest can link against. And then create the wrapping library as Foo which links against FooInternal as private, but sets the include/Foo folder as public headers. But this requires either Foo to be an interface target, which then will not have any libFoo to export, having me to create custom logic to rename or somehow setup CMake to use the libFooInternal as the correct lib file. Again this sounds dirty and I'm not sure how this would work in practice.
Are there any obvious solutions I am being oblivious about, or does anyone have a good solution to this problem?
Aucun commentaire:
Enregistrer un commentaire