繁体   English   中英

cmake 使用 find_package 传播依赖项

[英]cmake propagate dependencies using find_package

举一个简单的例子:有两个库和一个可执行文件。 两个库都是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)

主库

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,即搜索预构建库,如果我没记错的话)并希望将其定义的依赖项传播到“消费”项目。 但似乎来自导入目标的所有PUBLICINTERFACEPRIVATE对“消费”目标没有影响?

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

谢谢!

CMake 有一个特定的函数来传播find_package这样的称为find_dependency 你可以这样使用它:

include(CMakeFindDependencyMacro)
find_dependency(libutil)

CMake 不会在其生成的文件中导出调用find_package 您必须使用find_packagefind_dependency手动执行此操作。 不同之处在于find_dependency将正确转发REQUIREDQUIET

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

target_link_libraries添加内容时,有两种情况:

  1. 要链接的库是目标。 在这种情况下,接口属性会正确传播,包括需要时的链接。
  2. 它只是一个字符串,没有目标。 在这种情况下, CMake 假定它是一个系统库并添加-l标志

由于链接到libutil是一个公共财产libmain ,所有消费者libmain也将链接到libutil

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

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

libUtil如何成为目标?

CMake 目标不(完全)绑定到目录。 您可以拥有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)

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM