现代CMake高级教程 - 第 5 章:链接第三方库

这篇具有很好参考价值的文章主要介绍了现代CMake高级教程 - 第 5 章:链接第三方库。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

双笙子佯谬老师的【公开课】现代CMake高级教程课程笔记

第 5 章:链接第三方库

案例

使用 tbb 库

main.cpp

#include <tbb/parallel_for.h>
int main()
{
	tbb::parallel_for(0, 4, [&] (int i)
	{
		printf("hello, %d!\n", i);
	})
}
直接链接 tbb

CMakeLists.txt

add_executable(main main.cpp)

target_link_libraries(main PUBLIC tbb)

直接链接 tbb 的缺点:

如果这样直接指定 tbb,CMake 会让链接器在系统的库目录里查找 tbb,他会找到 /usr/lib/libtbb.so 这个系统自带的,但这对于没有一个固定库安装位置的 Windows 系统并不适用。

此外,他还要求 tbb 的头文件就在 /usr/include 这个系统默认的头文件目录,这样才能 #include <tbb/parallel_for.h> 不出错,如果 tbb 的头文件在其他地方就需要再加一个 target_include_directories 设置额外的头文件查找目录。

也可以直接写出全部路径,这样也可以让没有默认系统路径的 Windows 找到安装在奇怪位置的 tbb… 不过这样根本不跨平台,你这样改了别人如果装在不同地方就出错了。

顺便一提,CMake 的路径分割符始终是 /。即使在 Windows 上,也要把所有的 \ 改成 /,这是出于跨平台的考虑。请放心,CMake 会自动在调用 MSVC 的时候转换成 \,你可以放心的用 ${x}/bin 来实现和 Python 的 os.path.join(x, ‘bin’) 一样的效果。

CMakeLists.txt

add_executable(main main.cpp)
target_link_libraries(main PUBLIC c:/Users/archibate/installed/tbb/tbb.dll)

毕竟大多数操作系统都是 Unix-like,就 Windows 喜欢搞特殊。cd /d C:\Program\ Files\ (x86)\Microsoft\ Visual\ Studio\2019\ 路径里动不动夹杂几个转移符、空格、特殊符号,这谁顶得住啊。

高情商:Windows 是最适合练习你 C 语言转义符使用水平的地方。

更通用的方式:find_package

更好的做法是用 CMake 的 find_package 命令。
find_package(TBB REQUIRED) 会查找 /usr/lib/cmake/TBB/TBBConfig.cmake 这个配置文件,并根据里面的配置信息创建 TBB::tbb 这个伪对象(他实际指向真正的 tbb 库文件路径 /usr/lib/libtbb.so),之后通过 target_link_libraries 链接 TBB::tbb 就可以正常工作了。

CMakeLists.txt

add_executable(main main.cpp)
find_package(TBB REQUIRED)
target_link_libraries(main PUBLIC TBB::tbb)

TBB::tbb 的秘密:自带了一些 PUBLIC 属性

TBB::tbb 是一个伪对象(imported),除了他会指向 /usr/lib/libtbb.so 之外,TBBConfig.cmake 还会给 TBB::tbb 添加一些 PUBLIC 属性,用于让链接了他的对象带上一些 flag 之类的。

比如,TBB 安装在 /opt/tbb 目录下,头文件在 /opt/tbb/include 里,那么这时 TBBConfig.cmake 里就会有:
target_include_directories(TBB::tbb PUBLIC /opt/tbb/include)
这样 main 在链接了 TBB::tbb 的时候也会被“传染”上 /opt/tbb/include 这个目录,不用调用者手动添加了。

再比如,TBB::tbb 链接了另一个库 Blosc::blosc,那这个库也会自动链接到 main 上,无需调用者手动添加。

比如 spdlog 的 spdlog-config.cmake 就会定义 SPDLOG_NOT_HEADER_ONLY 这个宏为 PUBLIC。从而实现直接 #include <spdlog/spdlog.h> 时候是纯头文件,而 find_package(spdlog REQUIRED) 时却变成预编译链接库的版本。(嗯,其实不是 PUBLIC 而是 INTERFACE,因为伪对象没有实体)

和古代 CMake 做对比:为什么 PUBLIC 属性的传播机制如此便利

古代 CMake

cmake_minimum_required(VERSION 2.8)
project(MyProject)

list(APPEND CMAKE_MODULE_PATH "<path to FindTBB module>")

find_package(TBB COMPONENTS tbb tbbmalloc)
if (NOT TBB_FOUND)
	message(FATAL_ERROR "TBB not found")
endif()

add_executable(myapp myapp)
target_include_directories(myapp ${TBB_INCLUDE_DIRS})
target_compile_definitions(myapp ${TBB_DEFINITIONS})
target_link_libraries(myapp ${TBB_LIBRARIES})

现代 CMake

cmake_minimum_required(VERSION 3.12)
project(MyProject LANGUAGES CXX)

find_package(TBB COMPONENTS tbb tbbmalloc REQUIRED)

add_executable(myapp myapp)
target_link_libraries(myapp TBB::tbb TBB::tbbmalloc)
和 find_package(TBB CONFIG REQUIRED) 的区别

其实更好的是通过 find_package(TBB CONFIG REQUIRED),添加了一个 CONFIG 选项。这样他会优先查找 TBBConfig.cmake(系统自带的)而不是 FindTBB.cmake(项目作者常把他塞在 cmake/ 目录里并添加到 CMAKE_MODULE_PATH)。这样能保证寻找包的这个 .cmake 脚本是和系统自带的 tbb 版本是适配的,而不是项目作者当年下载的那个版本的 .cmake 脚本。

CMakeLists.txt

add_executable(main main.cpp)

find_package(TBB CONFIG REQUIRED)
target_link_libraries(main PUBLIC TBB::tbb)

当然,如果你坚持要用 find_package(TBB REQUIRED) 也是可以的。

  • 没有 CONFIG 选项:先找 FindTBB.cmake,再找 TBBConfig.cmake,找不到则报错
  • 有 CONFIG 选项:只会找 TBBConfig.cmake,找不到则报错

此外,一些老年项目(例如 OpenVDB)只提供 Find 而没有 Config 文件,这时候就必须用 find_package(OpenVDB REQUIRED) 而不能带 CONFIG 选项。

/usr/lib/cmake/TBB/TBBConfig.cmake

不论是 TBBConfig.cmake 还是 FindTBB.cmake,这个文件通常由库的作者提供,在 Linux 的包管理器安装 tbb 后也会自动安装这个文件。少部分对 CMake 不友好的第三方库,需要自己写 FindXXX.cmake 才能使用。

# Create imported target TBB::tbb
add_library(TBB::tbb SHARED IMPORTED)

set_target_properties(TBB::tbb PROPERTIES
	INTERFACE_COMPILE_DEFINITIONS "\$<\$<CONFIG:DEBUG>:TBB_USE_DEBUG>"
	INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
)

# Create imported target TBB::tbbmalloc
add_library(TBB::tbbmalloc SHARED IMPORTED)

set_target_properties(TBB::tbbmalloc PROPERTIES
	INTERFACE_COMPILE_DEFINITIONS "\$<\$<CONFIG:DEBUG>:TBB_USE_DEBUG>"
	INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
)

# Create imported target TBB::tbbmalloc_proxy
add_library(TBB::tbbmalloc_proxy SHARED IMPORTED)

set_target_properties(TBB::tbbmalloc_proxy PROPERTIES
	INTERFACE_COMPILE_DEFINITIONS "\$<\$<CONFIG:DEBUG>:TBB_USE_DEBUG>"
	INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
)
老年项目案例:OpenVDB(反面教材)

一些老年项目作者喜欢在项目里自己塞几个 FindXXX.cmake,然而版本可能和系统里的不一样,比如用 3.0 的 finder 去找 2.0 的包,容易出现一些奇奇怪怪的错误。

不建议大家这样用自己创建一个 cmake/ 目录来存用到的所有库的 finder,尽量用系统自带的,可以保证用的是系统自带库的那个配置。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n37qGqZL-1683089549162)(./images/1677557642969.png)]

find_package(Qt5 REQUIRED) 出错了
find_package(Qt5 REQUIRED)
target_link_libraries(main PUBLIC Qt5::Widgets Qt5::Gui)
CMake Error at CMakeLists.txt:6 (find_package):
	Found package configuration file:
	
		/usr/lib64/cmake/Qt5/Qt5Config.cmake

but it set Qt5_FOUND to FALSE so package "Qt5" is considered to be NOT FOUND. Reason given by package:
The 0t5 package requires atleast onecomponent

原因:Qt5 具有多个组件,必须指定你需要哪些组件。

find_package 生成的伪对象(imported target)都按照“包名::组件名”的格式命名。你可以在 find_package 中通过 COMPONENTS 选项,后面跟随一个列表表示需要用的组件。

find_package(Qt5 COMPONENTS Widgets Gui REQUIRED)
target_link_libraries(main PUBLIC Qt5::Widgets Qt5::Gui)
find_package(TBB COMPONETS tbb tbbmalloc tbbmalloc_proxy REQUIRED)
target_link_libraries(main PUBLIC TBB::tbb TBB::tbbmalloc TBB::tbbmalloc_proxy)

测试一下能否找到 Qt 的头文件并编译通过:
main.cpp

#include <QDebug>
#include <QString>

int main()
{
	QString hello = "hello, world!\n";
	qInfo() << hello;
	return 0;
}

常见问题:Windows 上找不到 Qt5 包怎么办?我明明安装了!

你是 Windows 系统,可能你安装了 Qt5,但是因为 Windows 系统的安装路径非常混乱,没有固定的 /usr/lib 之类的默认路径可以搜索,所以出错了。

CMake Error at CMakeLists.txt:6 (find_package):
	Could not find a package configuration file provided by "Qt5" with any ofthe following names:
		Qt5Config.cmake
		qt5-config.cmake
	
Add the installation prefix of "Qt5" to CMAKE_PREFIX_PATH or set "Qt5_DIR" to a directory containing one of the above files. If "Qt5" provides a separate development package or SDK, be sure it has been installed.

假设你的 Qt5 安装在 C:\Qt\Qt5.14.2,则你去找找这个目录:

C:\Qt\Qt5.14.2\msvc2019_64\lib\cmake\

你会看到他里面有个 Qt5Config.cmake 对吧。现在,有四种方法让 CMake 找得到他。

  • 第一种是设置 CMAKE_MODULE_PATH 变量,添加一下包含 Qt5Config.cmake 这个文件的目录路径 C:\Qt\Qt5.14.2\msvc2019_64\lib\cmake,当然刚刚说了尽管你是 Windows 还是要把 \ 全部换成 /,因为 CMake 是“亲 Unix”的构建系统。

    set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} C:/Qt/Qt5.14.2/msvc2019_64/lib/cmake)
    
    find_package(Qt5 REQUIRED COMPONENTS Widgets Gui REQUIRED)
    target_link_libraries(main PUBLIC Qt5::Widgets Qt5::Gui)
    
  • 更好的方法:设置 <包名>_DIR 变量指向 <包名>Config.cmake 所在位置
    第二种是设置 Qt5_DIR 这个变量为 C:\Qt\Qt5.14.2\msvc2019_64\lib\cmake。这样只有 Qt5 这个包会去这个目录里搜索 Qt5Config.cmake,更有针对性。

    set(Qt5_DIR C:/Qt/Qt5.14.2/msvc2019_64/lib/cmake)
    
    find_package(Qt5 REQUIRED COMPONENTS Widgets Gui REQUIRED)
    target_link_libraries(main PUBLIC Qt5::Widgets Qt5::Gui)
    
  • 更好的方法:设置 <包名>_DIR 变量指向 <包名>Config.cmake 所在位置
    第三种(推荐),直接在命令行通过 -DQt5_DIR=”xxx” 指定,这样不用修改 CMakeLists.txt。

    cmake -B build -DQt5_DIR="C:/Qt/Qt5.14.2/msvc2019_64/lib/cmake"
    
  • 更好的方法:设置 <包名>_DIR 变量指向 <包名>Config.cmake 所在位置
    第四种,还可以通过设置环境变量 Qt5_DIR 也是可以的,就是对 Windows 用户比较困难。

    export Qt5_DIR="/opt/Qt5.14.2/lib/cmkae"
    cmake -B build
    
不指定 REQUIRED

不指定 REQUIRED,找不到时不报错,只会设置 TBB_FOUND 为 FALSE

find_package(TBB)
if (TBB_FOUND)
	message(STATUS "TBB found at: ${TBB_DIR}")
	target_link_libraries(main PUBLIC TBB::tbb)
	target_compile_definitions(main PUBLIC WITH_TBB)
else()
	message(WARNING "TBB not found! using serial for")
endif()
-- TBB found at: /usr/lib64/cmake/TBB

如果没有 REQUIRED 选项,找不到时将不会报错。这样可以用于添加一些可选的依赖,如果没有也不要紧的那种,这时我们可以抛出一个警告。

找到了会把 TBB_FOUND 设为 TRUE,TBB_DIR 设为 TBBConfig.cmake 所在路径。
找不到会把 TBB_FOUND 设为 FALSE,TBB_DIR 为空。

这里我们在找到 TBB 时定义 WITH_TBB 宏,稍后 .cpp 里就可以根据这个判断。如果找不到 TBB 可以 fallback 到保守的实现。

这样在 .cpp 里可以判断 WITH_TBB 宏,找不到 TBB 时退化到串行 for 循环

#include <cstdio>
#ifdef WITH_TBB
#include <tbb/parallel_for.h>
#endif

int main()
{
#ifdef WITH_TBB
	tbb::parallel_for(0, 4, [&](int i)
	{
#else
	for (int i = 0; i < 4; i++)
	{
#endif
		printf("hello, %d!\n", i);
#ifdef WITH_TBB
	});
#else
	}
#endif
}

也可以用 TARGET 判断是否存在 TBB::tbb 这个伪对象,实现同样效果

find_package(TBB)
if (TARGET TBB::tbb)
	message(STATUS "TBB found at: ${TBB_DIR}")
	target_link_libraries(main PUBLIC TBB::tbb)
	target_compile_definitions(main PUBLIC WITH_TBB)
else()
	message(WARNING "TBB not found! using serial for")
endif()

也可以复合 if 的各种判断语句,例如 NOT TARGET TBB::tbb AND TARGET Eigen3::eigen 表示找得到 TBB 但是找不到 Eigen3 的情况。文章来源地址https://www.toymoban.com/news/detail-433259.html

到了这里,关于现代CMake高级教程 - 第 5 章:链接第三方库的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • cmake-动态库和静态库及使用OpenCV第三方库

     项目中会有单个源文件构建的多个可执行文件的可能。项目中有多个源文件,通常分布在不同子目录中。这种实践有助于项目的源代码结构,而且支持模块化、代码重用和关注点分离。同时,这种分离可以简化并加速项目的重新编译。 add.h add.cpp main.cpp  首先这是我们的目

    2024年01月24日
    浏览(55)
  • 鸿蒙加载第三方动态链接库(.so)文件

    在没有正确引用so文件情况下会报出上述错误,正确姿势应该如下 在Android的写法是在main目录下创建jniLibs,然后.so文件复制进去即可,但是在鸿蒙里面要在libs/内放置arm64-v8a和armeabi-v7a两个文件夹的so文件,然后在build.gradle文件 加多一个.so的描述 即可使用

    2024年02月13日
    浏览(37)
  • ESP32工程中CMake使用及加入第三方SDK库文件

            本文中使用的是乐鑫官方推出的 ESP-IDF v5.1 对 ESP32S3 设备开发,并非是Arduino、Micro-python等第三方工具开发。在ESP-IDF框架中,乐鑫官方已经将 CMake 和 Ninja 编译构建工具 集成到了ESP-IDF中。         ESP-IDF 即乐鑫物联网开发框架,可为在 Windows、Linux 和 macOS 系统平台

    2024年02月20日
    浏览(55)
  • CMake学习笔记:搜索第三方库及头文件路径 find_package()命令

    在实际开发过程中,经常不可避免的会使用到第三方开源库,这些开源库可能是通过apt-get install 命令自动安装到系统目录,也可能是由我们自己下载库的源码然后通过编译安装到指定目录的。 不管哪种方式安装的库文件,如果我们需要在自己的项目中使用这些库,首先面临

    2024年02月06日
    浏览(104)
  • vue 微软插件实现根据第三方网站链接预览word、pd、excelf等文件

    一开始做的时候没想到会预览不了,报错 如下: 我在微软官网查到: 官网地址:https://learn.microsoft.com/en-us/webappsserver/configure-office-web-apps-for-sharepoint-2013#problem-you-receive-a-file-not-found-the-url-of-the-original-file-is-not-valid-or-the-document-is-not-publicly-accessible-verify-the-url-is-correct-then-conta

    2024年02月04日
    浏览(33)
  • Python第三方库安装教程、什么是第三方库

    Python有一个全球社区:https://pypi.org/,在这里我们可以搜索任何主题的Python第三方库。PyPI全称是Python Package Index,指的是Python包的索引,它由PSF(Python Software Foundation)来维护,并且展示全球Python计算生态。 我们需要学会利用PyPI的主站检索,找到我们使用和关心的Python第三方

    2024年02月03日
    浏览(108)
  • windows下qt使用第三方库(静态链接库lib)、静态库和动态库区别、动态库的使用。

    这里主要是讲述windows下qt使用第三方库。 windows下qt使用第三方库(静态链接库lib)。 为了能够使测试更准确,首先自己用qt创建了一个已经封装好的lib库。 然后让另一个项目去链接这个lib静态库,并且去调用里面的方法。 目的: 使 untitled项目 能够使用 myllplib.lib 静态库,这

    2023年04月17日
    浏览(96)
  • 现代CMake高级教程 - 第 7 章:变量与缓存

    双笙子佯谬老师的【公开课】现代CMake高级教程课程笔记 重复执行 cmake -B build 会有什么区别? 可以看到第二次的输出少了很多,这是因为 CMake 第一遍需要检测编译器和 C++ 特性等比较耗时,检测完会把结果存储到缓存中,这样第二遍运行 cmake -B build 时就可以直接用缓存的值

    2024年02月02日
    浏览(43)
  • 微信分享第三方连接(H5页面)自定义缩略图、标题、描述(显示分享框,而不是链接)(微信JS-SDK)

    首先要说明几个分享相关的问题: 现在微信不支持自定义按钮分享组件(也就是说不能点击某个按钮分享),只能通过微信右上角的三个小点,点击后选择分享给朋友,朋友圈等。 当前从企业微信分享到微信好友和微信朋友圈是有问题的,一些手机(有些是因为app版本,企

    2024年02月09日
    浏览(53)
  • 73. python第三方库安装教程(超详细)

    Python 的库分为2类。 标准库:不需要安装,需要导入。 第三库:需要安装、需要导入。 【导入语法】 import + 库名 【示例】 openpyxl = open + python + xlsx 【简写如下】 openpyxl = open +py + xl open 是打开的意思。 py 指 python 。 xl 是 xlsx/xlsm/xltx/xltm 的缩写。 xlsx/xlsm/xltx/xltm 是电子表格的

    2024年02月09日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包