gpt4 book ai didi

c++ - cmake 使用 find_package 传播依赖项

转载 作者:行者123 更新时间:2023-12-01 14:43:40 27 4
gpt4 key购买 nike

举个简单的例子:有两个库和一个可执行文件。
两个库都是 SHARED (即 .so 文件)。一种称为 libMain,一种称为 libUtil。 libMain 使用 libUtil,可执行文件也是如此。
可执行文件可能会单独使用 libUtil,但通常它会从 libMain 调用一个在其实现中使用 libUtil 的方法。

所以在阅读了一些关于 CMake 的教程和文档之后,这个例子看起来相当简单。
每个项目都有一个简单的 CMakeLists.txt,而 libUtil 链接到 libMain,可执行文件链接到 libMain。 (我省略了 target_include_directories 以节省一些行)所有项目都使用相同的 PREFIX_PATH。

库工具

include(GNUInstallDirs)

project(libUtil)
set(CMAKE_SHARED_LIBRARY_PREFIX "")
add_library(libUtil SHARED main.cpp)
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Config
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(EXPORT ${PROJECT_NAME}Config DESTINATION ${CMAKE_INSTALL_PREFIX}/cmake)

libMain
include(GNUInstallDirs)

project(libMain)
set(CMAKE_SHARED_LIBRARY_PREFIX "")
add_library(libUtil SHARED main.cpp)
find_package(libUtil REQUIRED)
target_link_libraries(${PROJECT_NAME} PUBLIC libUtil)
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Config
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(EXPORT ${PROJECT_NAME}Config DESTINATION ${CMAKE_INSTALL_PREFIX}/cmake)

可执行的
project(exe)
find_package(libMain REQUIRED)
set(CMAKE_SHARED_LIBRARY_PREFIX "")
add_executable(exe main.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE libMain)

但是,在构建可执行文件时,我遇到了链接器错误,告诉 exe 是 无法执行 -llibUtil .

我已经在这上面花了很多时间,在阅读了几篇文章后,我发现(或者至少我是这么认为的..)我需要提供一个 自定义 libMainConfig.cmake (我将生成的重命名为 libMainTargets.cmake)
include("${CMAKE_CURRENT_LIST_DIR}/libMainTargets.cmake")

find_package(libUtil REQUIRED)

链接器错误现在消失了,我可以运行我的项目了。我仍然不明白为什么我需要这样做。我知道在处理已经构建的库时会有所不同。但是,由于 CMake 始终使用基于目录的项目分离,我将如何以及为什么在同一个 CMakeLists.txt 中构建多个项目?到目前为止,我一直使用 find_package (如果我没记错的话,它本质上是 add_library IMPORTED,即搜索预构建库)并希望将其定义的依赖项传播到“消费”项目。但似乎所有 PUBLIC , INTERFACEPRIVATE从导入的目标对“消费”目标没有影响?

请在解释中详细说明和/或指出我错过的一些引用资料,并指出一些最佳实践,假设所有项目要么是我自己构建的,要么是像 boost 或 JNI 这样的系统库。

谢谢!

最佳答案

CMake 具有传播 find_package 的特定功能像这样称为 find_dependency .你可以这样使用它:

include(CMakeFindDependencyMacro)
find_dependency(libutil)

CMake 不会导出调用 find_package在其生成的文件中。您必须使用 find_package 手动完成。或 find_dependency .区别在于 find_dependency将转发 REQUIREDQUIET正确。

为什么 CMake 会尝试发送 -llibUtil到链接器?

target_link_libraries 添加内容时,有两种情况:
  • 要链接的库是目标。在这种情况下,接口(interface)属性会正确传播,包括必要时的链接。
  • 它只是一个字符串,没有目标。在这种情况下, CMake 假定它是一个系统库并添加 -l旗帜 .

  • 由于链接到 libutillibmain 的公共(public)属性(property), libmain 的所有消费者还将链接到 libutil .

    由于调用 find package 必须手动完成, libutil不是目标。假定链接到系统库,因此 CMake 将使 exe到名为 libutil 的系统库这不存在。

    调用 find_package(libUtil REQUIRED)将确保 exe将消耗 libutil 的使用要求通过链接目标而不是库。

    怎么可能 libUtil成为目标?

    CMake 目标不(完全)绑定(bind)到目录。你可以有 GLOBALIMPORTED目标,您可以按项目拥有多个目标。想象一个由多个库和可执行文件组成的项目。类比是 CMake 项目是 Visual Studio 解决方案,而 CMake 目标是 Visual Studio 项目。

    现在在导入的目标上。它们旨在表示已经由另一个项目编译的目标,您可以安装或导出构建树。导入的目标让您可以像使用普通目标一样使用来自不同项目的东西,就像您声明它一样。导入的目标比编译器标志更精确:它们当然带有库,还带有如何链接、所需的编译器标志、包含目录和其他要求。
    find_packagefind_dependency旨在查找配置文件,其中包含导入的目标信息。之后,只需链接到它就会设置包含目录、正确的链接器标志等。

    导出多个目标的项目的一个很好的例子是 SFML,它将声音、图形和窗口管理等不同的模块导出为不同的静态/动态库。

    如何避免这种愚蠢的错误?

    CMake 让我们使用命名空间作为目标。使用命名空间语法时,它不能是系统库。 CMake 将输出找不到目标的错误,而不是尝试链接到不存在的库。
    install(
    EXPORT ${PROJECT_NAME}Config
    NAMESPACE libUtil
    DESTINATION ${CMAKE_INSTALL_PREFIX}/cmake
    )

    然后将链接更改为:
    target_link_libraries(${PROJECT_NAME} PUBLIC libUtil::libUtil)

    命名空间名称的约定是使用与包相同的名称。

    关于c++ - cmake 使用 find_package 传播依赖项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59649679/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com