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.
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!