第六章:CMake 找库文件的两种方式

在上一小节当中,查找第三方库用到一个新语法 find_package,这个语法往往被人误解,以为只要是找第三方库就可以用,但如果你看我第三章的那种方式构建库文件,你就无法用 find_package。

我们是官方的第三方库,我们可以用 find_package,因为人家已经整理好相关的文件到一个变量中,我们才可以非常方便简单设置几个参数值快速找到库文件。但如果说我们自己的或不规范的第三方库,即没提过规范的语法设置快速寻找,就只能先添加库文件的寻找路径,再一个一个寻找,这就是 find_library 命令。

总之,find_package 和 find_library 都可以用于在 CMake 中查找和链接库,但 find_package 更适用于具有CMake 配置文件的库,而 find_library 则适用于没有 CMake 配置文件的库。

find_package

典型用法

1
find_package(<PackageName> [<version>] [REQUIRED] [COMPONENTS <components>...])

<PackageName> 是唯一的必选参数。

<version> 通常会被省略,如果没有软件包就无法成功配置项目,则应给出 REQUIRED。

一些更复杂的软件包支持组件,可以使用 COMPONENTS 关键字来选择组件,但大多数软件包都没有这种复杂程度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#查找名为 OpenCV 的包,找不到不报错,事后可以通过 ${OpenCV_FOUND} 查询是否找到
find_package(OpenCV)

#查找名为 OpenCV 的包,找不到不报错,也不打印任何信息
find_package(OpenCV QUIET)

#查找名为 OpenCV 的包,找不到就报错(并终止 cmake 进程,不再继续往下执行)
find_package(OpenCV REQUIRED) # 最常见用法

#查找名为 OpenCV 的包,找不到就报错,且必须具有 OpenCV::core 和 OpenCV::videoio 这两个组件,如果没有这两个组件也会报错
find_package(OpenCV REQUIRED COMPONENTS core videoio)

#查找名为 OpenCV 的包,找不到就报错,可具有 OpenCV::core 和 OpenCV::videoio 这两个组件,没有这两组件不会报错,通过 ${OpenCV_core_FOUND} 查询是否找到 core 组件
find_package(OpenCV REQUIRED OPTIONAL_COMPONENTS core videoio)

搜索包的模式

Module 模式:在这种模式下,CMake 会搜索名为 Find<PackageName>.cmake 的文件,首先在 CMAKE_MODULE_PATH 指定的路径中查找,则在CMake安装目录(即CMAKE_ROOT变量)下的Modules目录下查找。找到文件后,CMake 会读取并处理它,负责查找包、检查版本,并生成任何必要的消息。

1
2
3
#查找顺序
CMAKE_MODULE_PATH
CMAKE_ROOT

Config 模式:在这种模式下,CMake 会搜索名为 <lowercasePackageName>-config.cmake<PackageName>Config.cmake 的文件。如果指定了版本信息,还会查找 <lowercasePackageName>-config-version.cmake<PackageName>ConfigVersion.cmake。Config 模式下,可以指定一个包名列表来搜索。CMake 搜索配置和版本文件的位置比 Module 模式复杂得多。

1
2
3
4
#查找文件
<lowercasePackageName>-config.cmake 或 <PackageName>Config.cmake
#如果指定版本
<lowercasePackageName>-config-version.cmake` 或 `<PackageName>ConfigVersion.cmake

默认情况下是先 Module 模式,如果查找失败就采用 Config 模式。

OpenCV 是 Config 模式,见下:

findpackage.png

如果想让CMake找到<PackageName>Config.cmake文件,需要在CMakeLists.txt中设置参数<PackageName>_DIR来设置路径。

比方说OpenCV的设置情况:

1
set(OpenCV_DIR "${CMAKE_CURRENT_SOURCE_DIR}/thirdpart/opencv_x64")

然后你接着去设置头文件路径、库文件路径,最后把库文件链接到可执行程序中

1
2
3
4
5
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})

add_executable(YourProjectName main.cpp)
target_link_libraries(YourProjectName ${OpenCV_LIBS})

因此 find_package不是直接去找具体的动态库文件和头文件。而是去找包配置文件,这个配置文件里包含了包的具体信息,包括动态库文件的位置头文件的目录链接时需要开启的编译选项等等。

再者,不同的操作系统平台,CMake 都会有默认的搜索路径,再结合官方提供的CMake信息,以及你的 CMakeLists.txt 文件就能找到库文件。那如果你没有把这些库文件安装到 CMake 默认搜索路径呢?

默认的搜索路径

Windows 和 Linux 都有自己的标准路径,这里就以 Linux 作为探讨对象。

1
2
3
4
5
6
<prefix>/(lib/<arch>|lib*|share)/cmake/<name>*/
<prefix>/(lib/<arch>|lib*|share)/<name>*/
<prefix>/(lib/<arch>|lib*|share)/<name>*/cmake/
<prefix>/<name>*/(lib/<arch>|lib*|share)/cmake/<name>*/
<prefix>/<name>*/(lib/<arch>|lib*|share)/<name>*/
<prefix>/<name>*/(lib/<arch>|lib*|share)/<name>*/cmake/
  • <prefix> 是变量 ${CMAKE_PREFIX_PATH},Unix 平台默认为 /usr
  • <name> 是你在 find_package(<name> REQUIRED) 命令中指定的包名。
  • <arch> 是系统的架构,例如 x86_64-linux-gnui386-linux-gnu
    • ( Ubuntu 喜欢把库文件套娃在 /usr/lib/x86_64-linux-gnu 目录下)

例如你是 64 位的 Linux 系统,find_package(Qt5 REQUIRED) 会依次搜索:

1
2
3
4
5
6
7
8
9
10
11
12
/usr/lib/cmake/Qt5/Qt5Config.cmake
/usr/lib/x86_64-linux-gnu/cmake/Qt5/Qt5Config.cmake
/usr/share/cmake/Qt5/Qt5Config.cmake
/usr/lib/Qt5/Qt5Config.cmake
/usr/lib/x86_64-linux-gnu/Qt5/Qt5Config.cmake
/usr/share/Qt5/Qt5Config.cmake
/usr/Qt5/lib/cmake/Qt5/Qt5Config.cmake
/usr/Qt5/lib/x86_64-linux-gnu/cmake/Qt5/Qt5Config.cmake
/usr/Qt5/share/cmake/Qt5/Qt5Config.cmake
/usr/Qt5/lib/Qt5/Qt5Config.cmake
/usr/Qt5/lib/x86_64-linux-gnu/Qt5/Qt5Config.cmake
/usr/Qt5/share/Qt5/Qt5Config.cmake

非标准路径下的搜索

如果你没有在前面介绍的标准路径下,你在其他地方下载并编译安装了,也没有把库文件和头文件移动到标准路径中,那该怎么办?这时你需要手动指定一个变量告诉它在哪儿。我们依旧还是以 Linux 举例。

例如Qt5 安装到了/opt/Qt5.12.1。 首先找到它里面的 Qt5Config.cmake 文件所在位置。假如你找到该文件的位置是 /opt/Qt5.12.1/lib/cmake/Qt5/Qt5Config.cmake,那么请你设置变量 Qt5_DIR 为 /opt/Qt5.12.1/lib/cmake/Qt5。有三种设置方法:

  1. 单次有效:在 configure 阶段,可以从命令行设置: cmake -B build -DQt5_DIR="/opt/Qt5.12.1/lib/cmake/Qt5"
  2. 全局启用:修改你的 ~/.bashrc 文件添加环境变量: export Qt5_DIR="/opt/Qt5.12.1/lib/cmake/Qt5",然后重启终端。这样以后你每次构建任何项目,find_package 都能自动找到这个路径的 Qt5 包了。
  3. 单项目有效:直接在你自己项目的 CMakeLists.txt 最开头写一行: set(Qt5_DIR ""/opt/Qt5.12.1/lib/cmake/Qt5")。但是一定要写在最前面。

利弊.png

只要不删除 build ,单次有效的方式就会一直存在,因为 CMake 会有缓存,但如果你删除 build 了,下次你还得再设置一次。

find_library

在 CMake 中,find_library 用于查找特定的库文件(如 .lib.a.so 等),并将该库的路径存储到变量中。它的功能类似于手动指定库文件路径,但通过 find_library 可以让 CMake 自动在系统的标准库路径或用户指定的路径中查找库。

1
find_library(<VAR> name [PATHS paths...])

<VAR>: 将找到的库的完整路径存储到这个变量中。

name: 要查找的库的名称,不需要后缀(如 .a.so)。

PATHS: 可选,指定额外的搜索路径,CMake 会在这些路径中查找库。

如果库文件已经在标准路径中,无需指定。否则,需要通过 PATH 参数指定。

1
2
3
4
5
6
7
8
find_library(MYLIB_LIB NAMES mylib PATHS /usr/local/lib /opt/libs)

if(MYLIB_LIB)
message(STATUS "Found mylib: ${MYLIB_LIB}")
target_link_libraries(MyExecutable ${MYLIB_LIB})
else()
message(FATAL_ERROR "Could not find mylib!")
endif()

find_library(MYLIB_LIB NAMES mylib): 这是查找名为 mylib 的库文件,查找到的路径会存储在 MYLIB_LIB 变量中。

PATHS /usr/local/lib /opt/libs: 在这些路径下查找库文件。如果没有指定 PATHS,CMake 会默认查找系统标准的库路径,例如 /usr/lib/lib 等。

target_link_libraries(MyExecutable ${MYLIB_LIB}): 如果找到了库文件,将其链接到目标 MyExecutable