首页 > 解决方案 > 如何使用现代 cmake 构造防止 cmake 不添加来自外部依赖项的所有目标

问题描述

我正在尝试以最干净和现代的 cmake 方式将外部依赖项添加到我的新 cmake 项目中。我想在配置期间下载这些依赖项。我的问题是始终导入来自外部依赖项的所有目标。这会在例如 Clion 中向我的配置发送垃圾邮件,默认情况下我希望更清洁,但正如您所见,我在下图中有几个来自 gtest、googlebenchmark、eigen 和 spdlog 的无用目标。该项目应在 Windows 和 Linux 上编译。

在此处输入图像描述

所以我的问题是如何防止这种情况发生?

我观看了几个现代 cmake 视频,例如 https://www.youtube.com/watch?v=eC9-iRN2b04和来自https://cliutils.gitlab.io/modern-cmake/的来源。

我的项目文件夹结构如下

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

我的顶级水平CMakeLists.txt如下

cmake_minimum_required(VERSION 3.16)
project(Ikarus)

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

Ikarus我想构建的文件夹中,Ikarus_lib应该由 和 中定义的目标使用。problemstestsbenchmarks

该文件Ikarus/CMakeLists.txt如下所示

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)

现在我想谈谈Ikarus/addexternalLibs.cmake导入外部依赖项的文件的内容。类似的困难出现在我导入 googletest 和 googlebenchmark 的基准测试和测试中,但我认为我会将自己限制在 Ikarus_lib 案例中,因为困难是相同的。

因为我想在配置期间下载我的依赖项,所以我使用https://cmake.org/cmake/help/latest/module/FetchContent.html找到了不错的解决方案

结果是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)

该解决方案有效,我设法使用选项停用了一些目标EIGEN_BUILD_DOC OFF...。但是我仍然有很多不同的无用目标,如上图所示,使用包的选项无法停用这些目标。

我尝试了其他几件事,例如使用EXCLUDE_FROM_ALL https://cmake.org/cmake/help/latest/prop_tgt/EXCLUDE_FROM_ALL.html如下

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()

这也有效,但也添加了我不想要的所有目标。因此,EXCLUDE_FROM_ALL它不像我认为的那样工作。

另一种使用方式EXCLUDE_FROM_ALL可能是

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)

这也不会改变行为。

我也尝试CPMAddPackagehttps://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"
)

这也有效,但也添加了所有目标。

所以现在我不知道还能尝试什么,我想我会保留目标并将它们从我的 Clion 配置界面中删除,并希望它们不会再次出现。但我仍然想了解我错过了什么。我认为这是一个常见问题,解决方案应该很容易?

此外,我知道我可以只下载例如 eigen 并使用它include_directories(...)来自动添加没有目标的标题。但一方面,这不是https://www.youtube.com/watch?v=eC9-iRN2b04中宣传的现代 cmake,其中使用include_directories被称为反模式,应该“在模块中思考”。另一方面,这对于不是仅标头的包没有帮助。

那么如何优雅地解决这个问题呢?此外,对于我对 Cmake 的(糟糕)使用情况的所有其他评论,我将不胜感激。谢谢!

标签: c++cmakeeigen

解决方案


推荐阅读