举一个简单的例子:有两个库和一个可执行文件。这两个库都是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)
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)
project(exe)
find_package(libMain REQUIRED)
set(CMAKE_SHARED_LIBRARY_PREFIX "")
add_executable(exe main.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE libMain)
Run Code Online (Sandbox Code Playgroud)
然而,在构建可执行文件时,我最终遇到了链接器错误,告诉 exe 无法执行 -llibUtil。
我已经花了相当多的时间在这上面,在阅读了几篇文章后,我发现(或者至少我这么认为......)我需要提供一个自定义的 libMainConfig.cmake(我将生成的一个重命名为 libMainTargets.cmake)
include("${CMAKE_CURRENT_LIST_DIR}/libMainTargets.cmake")
find_package(libUtil REQUIRED)
Run Code Online (Sandbox Code Playgroud)
链接器错误现在消失了,我可以运行我的项目了。我仍然不明白为什么我需要这样做。我知道处理已构建的库时存在差异。但是,由于 CMake 始终使用基于目录的项目分离,我如何以及为什么要在同一个 CMakeLists.txt 中构建多个项目?到目前为止,我一直使用find_package
(本质上是 add_library IMPORTED,即搜索预构建库,如果我没有记错的话),并希望将其定义的依赖项传播到“消费”项目。但似乎导入的目标中的所有PUBLIC
和INTERFACE
都PRIVATE
对“消耗”目标没有影响?
请在解释中详细说明和/或指出我错过的一些参考资料,并指出一些最佳实践,假设所有项目要么是我自己构建的,要么是 boost 或 JNI 等系统库。
谢谢!
CMake 有一个特定的函数find_package
可以像这样传播,称为find_dependency
. 你可以这样使用它:
include(CMakeFindDependencyMacro)
find_dependency(libutil)
Run Code Online (Sandbox Code Playgroud)
CMake 不会导出find_package
其生成文件中的调用。您必须使用find_package
或手动执行此操作find_dependency
。区别在于find_dependency
会转发REQUIRED
且QUIET
正确。
-llibUtil
到链接器?添加内容时target_link_libraries
,有两种情况:
-l
标志。由于链接到libutil
是 的公共财产libmain
,因此所有消费者libmain
也将链接到libutil
。
由于调用 find package 必须手动完成,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
)
Run Code Online (Sandbox Code Playgroud)
然后更改链接到此:
target_link_libraries(${PROJECT_NAME} PUBLIC libUtil::libUtil)
Run Code Online (Sandbox Code Playgroud)
命名空间名称的约定是使用与包相同的名称。
归档时间: |
|
查看次数: |
4102 次 |
最近记录: |