背景
最近根据 【eBPF 入门实践教程十二:使用 eBPF 程序 profile 进行性能分析】 这篇文章,写了一个性能分析的小工具。不过文章中的实现是 rust,但我还不怎么熟悉 rust,因而打算参考其代码使用 c++ 来实现相同的功能。项目地址:profiler。
rust 中使用 cargo 就能非常方便的管理依赖与构建项目,c++ 中我使用 CMake 来构建项目,Conan 来管理依赖。在使用期间,我对于这二者的协同工作原理感到好奇,因此有了这篇笔记。
文中的出现的“依赖”、“包”、“库”等名词都是指代同一个概念,只是不同的说法而已~
CMake 查找依赖
在 CMake 中查找和管理项目依赖,主要是通过内置的 find_package 命令完成,其会在系统中寻找指定的库。
| |
- PackageName: 库的名称(区分大小写,如
OpenCV、Qt5)。 - REQUIRED: 如果找不到该库,CMake 会直接报错并停止配置。
- COMPONENTS: 查找库中的特定模块(例如
Qt5里的Widgets)。
CMake 有两种查找库的逻辑:
Module 模式 (查找
Find<PackageName>.cmake)CMake 预置了一系列脚本(在
/usr/share/cmake/Modules下)。它会寻找名为FindGSL.cmake这样的文件。- 适用场景: 较老或不直接支持 CMake 的库(如
CURL,ZLIB)。
- 适用场景: 较老或不直接支持 CMake 的库(如
Config 模式 (查找
<PackageName>Config.cmake或<lower-case-pkg>-config.cmake)这是现代库推荐的方式。库在安装时会自带一个配置文件。
- 适用场景:
OpenCV,Qt,Protobuf等现代库。 - 查找路径: 库的安装路径、
CMAKE_PREFIX_PATH或环境变量。
- 适用场景:
CMAKE_PREFIX_PATH
当在 CMake 中使用 find_package、find_library 或 find_path 等指令去寻找一个外部库时,CMake 并不知道这个库安装在哪里。CMAKE_PREFIX_PATH 就是用来告诉 CMake 去这些目录下找找看。
这里有三种设置的方法:
命令行设置
1cmake -DCMAKE_PREFIX_PATH="/path/to"CMakeLists.txt
1list(APPEND CMAKE_PREFIX_PATH "/path/to")环境变量
1export CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/path/to
Conan Generator
在一个比较简单的项目里,通常使用 conanfile.txt 来管理依赖即可,例如:
| |
注意 [generators] 中的 CMakeDeps 和 CMakeToolchain,他们在 Conan 与 CMake 集成使用中起了至关重要的作用。
CMakeDeps
当我们安装了 spdlog 这样的库后,需要 CMake 能看到它。CMakeDeps 就是这样一个依赖关系生成器,它会为每一个依赖库生成一个标准的 spdlog-config.cmake 文件。
将 CMakeDeps 生成的 spdlog-config.cmake 目录位置加入 CMAKE_PREFIX_PATH 后,就可以在 CMakeLists.txt 中写 find_package(spdlog REQUIRED) 来查找依赖了。
CMakeToolchain
CMakeToolchain 负责将你在 Conan Profile(配置文件)中定义的设置(如 compiler=gcc, build_type=Release)注入给 CMake。同时,其生成 conan_toolchain.cmake,并自动设置 CMAKE_PREFIX_PATH。
重要的一点就是:它会自动把 CMakeDeps 生成的配置文件目录加入搜索路径。
当执行 conan install 后,这两个生成器会接力完成任务:
- Conan 下载库到缓存。
CMakeDeps生成 spdlog-config.cmake 等文件,描述如何链接这些库。CMakeToolchain生成conan_toolchain.cmake,里面有一行代码告诉 CMake:“去CMakeDeps生成的那个文件夹里找包”。
例如我的项目里, ~/Workspace/profiler-cpp/build/Debug/generators/conan_toolchain.cmake 中的一部分如下:
| |
CMake 工具链文件
工具链文件是一个以 .cmake 结尾的脚本,通过在配置阶段指定变量 CMAKE_TOOLCHAIN_FILE 来加载:
| |
它通常在 project() 指令执行 之前 被加载,用于初始化编译器路径、目标平台信息等关键底层变量。
现在通过 Conan 的 CMakeDeps 和 CMakeToolchain,直接引入 Conan 自动生成的工具链文件,就能无感地使用下载的依赖了。
例如直接在命令里使用:
| |
或者通过 CMakePresets(CMake >= 3.19)来使用:
| |
总结
理解了 CMake 是如何查找依赖包后,二者的协同工作原理就明了了:Conan 通过自动生成的配置能告诉 CMake 在哪里找依赖包后,就能直接在 CMakeLists.txt 中使用 find_package 来查找对应的依赖了。