在cmake的世界中,进行查找库文件的方式一般就有三种
find_package(cmake原生支持)
很多著名的库在进行安装的时候都会进行带一个 库名config.cmake 的文件,通过这个 config.cmake文件 cmake就能够进行找到这个库文件以及相关依赖的路径。
使用 find_package 进行使用第三方库的一般步骤(三部曲)
bash
find /usr -name "*Config.cmake" | grep -i dbus
1. 寻找库
find_package(库的名字 REQURIED)
如果是通过 apt - get install 进行安装的库,该库的config.cmake文件一般存在的路径是:/usr/lib/架构/cmake/库名/

实质作用:
确认库是否被安装,如果库没有被安装 并且设置了REQURIED这个字段则会编译报错,防止后续的无效编译。
如果查找到库已安装,cmake会进行解析这个cmake文件,并在内存中进行创建变量,最重要的两个变量就是
- ${库名_INCLUDE_DIRS}:记录头文件在哪
- ${库名_LIBRARIES}:记录库文件在哪
2. 关联头文件
target_include_directories(程序名 PRIVATE ${库名字_INCLUDE_DIRS})
他会把**{库名_INCLUDE_DIRS}** 转化成gcc -I 后的参数,编译器默认去/usr/include/路径下进行查找代码中包含的头文件,如果没有找到则去**{库名_INCLUDE_DIRS}**的路径进行查找。
这里使用 PRIVATE 是因为只有这个程序本身需要这个头文件,如果后续有人进行链接了该程序,他们不需要去自动获取这个程序的头文件。
3. 链接库文件
target_link_libraries(程序名 PRIVATE ${库名称_LIBRARIES})
在编译的最后一步,它会将 ${库名称_LIBRARIES} 进行指向的 .so 动态库进行传给链接器
PkgConfig(Linux标准方式)
使用PkgConfig 是cmake非原生使用第三方库的工具,但是这个工具是Linux下非常古老但是健壮性和稳定性非常好的工具。
和find_package一样在使用apt-get install 进行安装开发版本的库文件时,会在 /usr/lib/架构/pkgconfig/库名称 有一个.pc 后缀的文件。

使用PkgConfig 进行使用第三方库的四部曲
1. 查找系统是否安装了PkgConfig这个工具
bash
find_package(PkgConfig REQUIRED)
cmake本身是读不懂 .pc 为后缀的文件的,需要进行使用外援工具PkgConfig 来及逆行读取
2. 寻找库
bash
pkg_check_modules(自定义变量名 REQUIRED 库名)
这里的库名可以使用
bashsudo find /usr -name "*.pc" | grep -i 库名 /usr/lib/x86_64-linux-gnu/pkgconfig/实际的库名这个命令来进行查看
例如:
寻找库 pkg_check_modules 的第一个参数 自定义变量名 是将进行查找库的相关信息都进行存到 自定义变量名_ 为前缀的变量中,以dbus库举个例子:
pkg_check_modules(DBUS REQUIRED dbus-1)
PkgConfig 进行查找到的dbus库的头文件的路径会进行存放到 DBUS_INCLUDE_DIRS 变量中, 库文件路径会进行存放到 DBUS_LIBRARIES 中
3. 关联头文件
bash
target_include_directories(程序名 PRIVATE ${库名称_INCLUDE_DIRS})
4. 链接库文件
bash
target_link_libraries(程序名 PRIVATE ${库名称_LIBRARIES})
find_path/find_libraries(手动地毯式搜索)
1. 寻找头文件
bash
find_path(库名称_INCLUD_DIRS NAMES 库名称.h PATHS 指定路径)
告诉编译器去指定路径去找一个名字为 库名称.h 的头文件,如果找到就将路径进行放到 库名称_INCLUDE_DIRS 这个变量中。
这里存到变量中的是文件夹,并不是文件本身。
2. 寻找库文件
bash
find_library(库名称_LIBRARIES NAMES 二进制库名称 PATHS 指定路径)
告诉编译器去指定路径下去找一个名字为 二进制库名称的 二进制库文件,如果找到就将路径进行放到 库名称_LIBRARIES 这个变量中。
这里变量中是存的是文件的完整路径
3. 进行安全检查
bash
if(NOT 库名称_INCLUDE_DIRS OR NOT 库名称_LIBRARUES) ... end if()
示例代码
假设你自己写了一个库叫 mymath,存放在 /opt/mymath 下:
# 1. 尝试找头文件
# 如果找到了,MYMATH_INCLUDE_DIR 就会变成 "/opt/mymath/include"
find_path(MYMATH_INCLUDE_DIR
NAMES mymath.h
PATHS /opt/mymath/include
)
# 2. 尝试找库文件
# 如果找到了,MYMATH_LIBRARY 就会变成 "/opt/mymath/lib/libmymath.so"
find_library(MYMATH_LIBRARY
NAMES mymath
PATHS /opt/mymath/lib
)
# 3. 安全检查(核心步骤)
# NOT 后面跟着变量名,意思是"如果没有找到..."
if(NOT MYMATH_INCLUDE_DIR)
message(FATAL_ERROR "错误:找不到 mymath.h!请检查 /opt/mymath/include 路径。")
endif()
if(NOT MYMATH_LIBRARY)
message(FATAL_ERROR "错误:找不到 libmymath.so!请检查 /opt/mymath/lib 路径。")
endif()
# 4. 如果程序运行到这里,说明上面都找到了,可以放心使用
add_executable(my_app main.cpp)
target_include_directories(my_app PRIVATE ${MYMATH_INCLUDE_DIR})
target_link_libraries(my_app PRIVATE ${MYMATH_LIBRARY})
这里进行message进行输出的时候,还进行设置了 FATAL_ERROR 字段 ,这是message进行输出的最高级警告:
- 如果不加:camke找不到库时也会进行尝试配置,最后在链接时报出一堆让人看不懂的底层错误
- 如果加了:cmake在编译时如果没有找到对应的文件就会直接报错,从而不在向下进行。
4. 链接头文件和库文件
bash
target_include_directories(...)
target_link_libraries(...)
这里可能都会有一个疑问:既然前面那两种方式这么好用,那这个手动式地毯搜索还有什么作用呢??
这种方主要是应对自动化工具失灵或者环境受限的场景,例如:
1. 使用第三方提供的"离线"预编译库
这是最典型的场景。比如某个硬件厂商给你提供了一个 SDK,里面只有两个文件夹:
include和lib。
原因 :这类库通常不是通过
apt install安装的,没有.pc文件,也没有Config.cmake脚本。做法 :你把这些文件夹拷贝到项目里的
3rdparty目录下,然后用find_path和find_library准确地把它们"挖"出来。2. 交叉编译(嵌入式开发)
如果你在 PC 上编写代码,但目标运行环境是 ARM 开发板(如树莓派、高性能嵌入式芯片)。
原因 :此时你不能让 CMake 去找你 PC 系统里的
/usr/lib(那是 X86 架构的库)。你需要指向你下载好的 交叉编译工具链(Toolchain) 里的库目录。做法:你会手动指定编译器去某个特定的 SDK 路径下寻找对应的头文件和二进制库。
3. 多版本库并存与切换
假设你的系统里安装了 OpenCV 4.0,但某个旧项目必须使用你手动下载并解压在
/home/ys/opencv-3.4的旧版本。
原因 :默认的
find_package可能会优先找到系统全局的 4.0 版本。做法:为了"强行"指定版本,你会直接用手动模式精准定位到那个特定路径,确保编译器不会找错人。
4. 内部自研库(非子目录模式)
有时候你的公司内部有多个独立的仓库,库 A 已经编译好了变成了
.so文件放在服务器上,你不想把库 A 的源代码通过add_subdirectory塞进你的项目里。
原因:为了保持项目整洁,你只引用库 A 的成品。
做法:通过手动模式,指定库 A 的二进制路径进行链接。
5. 高度定制或极端精简的系统
在某些金融、军事或极简的生产环境服务器上,可能连
pkg-config工具都没有安装。
原因:环境极其封闭,自动化探测工具全部失效。
做法:这是"最后的一道防线"。只要文件在硬盘上,你就能通过手动模式把它找出来并完成编译。
