[英]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,即搜索预构建库,如果我没记错的话)并希望将其定义的依赖项传播到“消费”项目。 但似乎来自导入目标的所有PUBLIC
、 INTERFACE
和PRIVATE
对“消费”目标没有影响?
请在解释中详细说明和/或指出我遗漏的一些参考资料,并指出一些最佳实践,假设所有项目要么由我自己构建,要么是像 boost 或 JNI 这样的系统库。
谢谢!
CMake 有一个特定的函数来传播find_package
这样的称为find_dependency
。 你可以这样使用它:
include(CMakeFindDependencyMacro)
find_dependency(libutil)
CMake 不会在其生成的文件中导出调用find_package
。 您必须使用find_package
或find_dependency
手动执行此操作。 不同之处在于find_dependency
将正确转发REQUIRED
和QUIET
。
-llibUtil
发送到链接器? 向target_link_libraries
添加内容时,有两种情况:
-l
标志。 由于链接到libutil
是一个公共财产libmain
,所有消费者libmain
也将链接到libutil
。
由于调用 find 包必须手动完成,因此libutil
不是目标。 假设链接到系统库,因此 CMake 会将exe
到名为libutil
的系统库,该库不存在。
调用find_package(libUtil REQUIRED)
将确保exe
通过链接目标而不是库来消耗libutil
的使用要求。
libUtil
如何成为目标? CMake 目标不(完全)绑定到目录。 您可以拥有GLOBAL
和IMPORTED
目标,也可以按项目拥有多个目标。 想象一个由多个库和可执行文件组成的项目。 一个类比是 CMake 项目是 Visual Studio 解决方案,而 CMake 目标是 Visual Studio 项目。
现在在导入的目标上。 它们旨在表示已由另一个项目编译的目标,您安装或导出了构建树。 导入的目标让你可以像普通目标一样使用来自不同项目的东西,就好像你声明了它一样。 导入的目标比编译器标志更精确:它们当然携带库,但也携带如何链接、所需的编译器标志、包含目录和其他要求。
find_package
和find_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.