How to prevent cmake to not add all targets from external dependencies using modern cmake constructs

Viewed 458

I am trying to add external dependencies to my new cmake project in the cleanest and modern cmake way possible. I want to download these dependencies during configuration time. My problem is that all the targets from external dependencies are always imported. This spams my configurations in e.g. Clion which i want to be much cleaner by default but as you can see i have several useless targets from gtest, googlebenchmark, eigen and spdlog in the figure below. The project should compile on Windows and Linux.

enter image description here

So my question is how to prevent this?

I watched several modern cmake videos, e.g. https://www.youtube.com/watch?v=eC9-iRN2b04 and the sources from https://cliutils.gitlab.io/modern-cmake/.

My project folder structure looks as follows

.
├── CMakeLists.txt
├── Ikarus
|   └── CMakeLists.txt
|   └── addexternalLibs.cmake
│   └── src
│       └── Ikarus
│           └── [...]
├── problems
|   └── CMakeLists.txt
|   └── [.cpp files]
├── tests
|   └── CMakeLists.txt
|   └── [.cpp files]
├── benchmarks
|   └── CMakeLists.txt
|   └── [.cpp files]

I have a top level CMakeLists.txt as follows

cmake_minimum_required(VERSION 3.16)
project(Ikarus)

add_subdirectory(Ikarus)
add_subdirectory(problems)
add_subdirectory(tests)
add_subdirectory(benchmarks)

In the folder Ikarus i want to build my Ikarus_lib which should be used by the targets defined in problems, tests and benchmarks.

The file Ikarus/CMakeLists.txt looks as follows

file(GLOB_RECURSE IKARUS_CPP_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} CONFIGURE_DEPENDS  *.cpp )


add_library(${PROJECT_NAME}_lib STATIC)
target_sources(${PROJECT_NAME}_lib PRIVATE ${IKARUS_CPP_SOURCES})

include(addexternalLibs.cmake)

target_include_directories(${PROJECT_NAME}_lib PUBLIC src)

target_link_libraries(${PROJECT_NAME}_lib PUBLIC Eigen3::Eigen
                                          PUBLIC spdlog::spdlog)

Now i want to talk about the contents of the file Ikarus/addexternalLibs.cmake where the external depencies are imported. The difficulties similar arise in benchmark and tests where i import googletest and googlebenchmark but i think i will restrict myself to the Ikarus_lib case since the difficulties are the same.

Since i want to download my dependencies during configuration time i found the nice solution using https://cmake.org/cmake/help/latest/module/FetchContent.html

The result is Ikarus/addexternalLibs.cmake:

include(FetchContent)

message("Configure Eigen: ")

FetchContent_Declare(
        eigen
        GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
        GIT_TAG 3.4
        GIT_SHALLOW TRUE
        GIT_PROGRESS TRUE
)
option(EIGEN_BUILD_DOC OFF)
option(BUILD_TESTING OFF)
option(EIGEN_LEAVE_TEST_IN_ALL_TARGET OFF)
option(EIGEN_BUILD_PKGCONFIG OFF)
FetchContent_MakeAvailable(eigen)

message("====================================")
message("Configure spdlog: ")

FetchContent_Declare(
        spdlog
        GIT_REPOSITORY https://github.com/gabime/spdlog.git
        GIT_TAG v1.8.5
        GIT_SHALLOW TRUE
        GIT_PROGRESS TRUE
)
option(SPDLOG_BUILD_SHARED OFF)
option(SPDLOG_BUILD_ALL OFF)
FetchContent_MakeAvailable(spdlog)

This solution works and i managed to deactivate some targets using the options EIGEN_BUILD_DOC OFF.... But still i have lots of different useless targets as seen in the image above which cannot be deactivated using options of the package.

I tried several other things, e.g. using EXCLUDE_FROM_ALL https://cmake.org/cmake/help/latest/prop_tgt/EXCLUDE_FROM_ALL.html as follows

FetchContent_Declare(
        eigen
        GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
        GIT_TAG 3.4
        GIT_SHALLOW TRUE
        GIT_PROGRESS TRUE
)
option(EIGEN_BUILD_DOC OFF)
option(BUILD_TESTING OFF)
option(EIGEN_LEAVE_TEST_IN_ALL_TARGET OFF)
option(EIGEN_BUILD_PKGCONFIG OFF)

FetchContent_GetProperties(eigen)
if(NOT eigen_POPULATED)
   FetchContent_Populate(Eigen)
    add_subdirectory(${eigen_SOURCE_DIR} ${eigen_BINARY_DIR} EXCLUDE_FROM_ALL)
endif()

which also works but does also adds all that targets which i don't want. Therefore, EXCLUDE_FROM_ALL does not work the way i think it works.

Another way of using EXCLUDE_FROM_ALL could be

include(FetchContent)

message("Configure Eigen: ")

FetchContent_Declare(
        eigen
        GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
        GIT_TAG 3.4
        GIT_SHALLOW TRUE
        GIT_PROGRESS TRUE
)
option(EIGEN_BUILD_DOC OFF)
option(BUILD_TESTING OFF)
option(EIGEN_LEAVE_TEST_IN_ALL_TARGET OFF)
option(EIGEN_BUILD_PKGCONFIG OFF)
FetchContent_MakeAvailable(eigen)
set_target_properties(eigen PROPERTIES EXCLUDE_FROM_ALL TRUE)

which also does not change the behaviour.

I also tried CPMAddPackage from https://github.com/cpm-cmake/CPM.cmake

CPMAddPackage(
        GITLAB_REPOSITORY libeigen/eigen
        GIT_TAG 3.4
        OPTIONS "EIGEN_BUILD_DOC NO"
                "BUILD_TESTING NO"
                "EIGEN_LEAVE_TEST_IN_ALL_TARGET YES"
                "EIGEN_BUILD_PKGCONFIG NO"
)

which also works but also adds all that targets.

So now i don't know what else to try and i think i will just keep the targets and delete them from my Clion configuration interface and hope they don't reappear. But still i want to understand what am i missing. I think this is a common problem and the solution should be very easy?

Furthermore, i know that i can just download e.g. eigen and use include_directories(...) to automatically add the headers without the targets. But on one hand this is not modern cmake as advertised in https://www.youtube.com/watch?v=eC9-iRN2b04 where using include_directories is called an anti-pattern and one should "think in modules". On the other hand this does not help for packages which are not header-only.

So how can this problem be solved elegantly? Additionally, i would appreciate every other comment to my (bad) usage of Cmake. Thanks!

0 Answers
Related