Cmake中寻库文件的路径

在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 库名)

这里的库名可以使用

bash 复制代码
sudo 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,里面只有两个文件夹:includelib

  • 原因 :这类库通常不是通过 apt install 安装的,没有 .pc 文件,也没有 Config.cmake 脚本。

  • 做法 :你把这些文件夹拷贝到项目里的 3rdparty 目录下,然后用 find_pathfind_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 工具都没有安装。

  • 原因:环境极其封闭,自动化探测工具全部失效。

  • 做法:这是"最后的一道防线"。只要文件在硬盘上,你就能通过手动模式把它找出来并完成编译。

相关推荐
李慕婉学姐2 小时前
【开题答辩过程】以《基于Java的周边游优选推荐网站的设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
java·开发语言
良木生香2 小时前
【C语言进阶】文件操作的相关详解(1):
c语言·数据结构·c++
Larry_Yanan2 小时前
Qt安卓开发(三)双摄像头内嵌布局
android·开发语言·c++·qt·ui
wjs20242 小时前
Kotlin 条件控制
开发语言
我命由我123452 小时前
Kotlin 开发 - Kotlin Lambda 表达式返回值
android·java·开发语言·java-ee·kotlin·android studio·android-studio
玖釉-2 小时前
[Vulkan 学习之路] 01 - 迈入高性能图形开发的大门 (Windows 环境搭建)
c++·windows·图形渲染
雨中散步撒哈拉2 小时前
22、做中学 | 高一下期 | Golang反射
开发语言·golang·状态模式
a努力。2 小时前
中国电网Java面试被问:Dubbo的服务目录和路由链实现
java·开发语言·jvm·后端·面试·职场和发展·dubbo
itwangyang5202 小时前
人工智能药物设计和生信常用 R 包一键全自动安装脚本
开发语言·人工智能·r语言