CMake find_package 找不到依赖包速查
更新时间:2026-10-24。FAQ 速查页,面向用 CMake 生成构建系统的同学。CMake 找不到依赖包指
cmake ..配置阶段find_package(Xxx REQUIRED)直接中止,打出Could NOT find Xxx (missing: XXX_DIR)的红字错误。CMake 找包有两条完全不同的路:Config 模式(找包自带的XxxConfig.cmake)与 Module 模式(找 CMake 自带或工程手写的FindXxx.cmake),两条路各有自己的搜索路径与失败原因——搞不清楚包走的是哪条路,就永远在错误的方向上折腾。本篇以Could NOT find原文为锚逐条定位;如果是 CMake 版本本身不够,请转 CMake 版本不兼容报错速查;如果是找不到编译器,请转 CMake 找不到编译器速查。
本文要回答的问题
Could NOT find Xxx (missing: XXX_DIR)里的XXX_DIR该填什么?- 装了库(
libxxx)为什么还找不到?-dev/-devel包差在哪? - Config 模式和 Module 模式怎么区分,搜索路径各是什么?
CMAKE_PREFIX_PATH、PKG_CONFIG_PATH各管哪条路?- 手写
FindXxx.cmake放在哪、为什么失效?
一、报错原文与典型现象
典型现场(工程路径以 /proj/demo 占位),配置阶段中止:
$ cmake -B build
-- The CXX compiler identification is GNU 12.3.0
-- Detecting CXX compiler ABI info - done
CMake Error at /usr/share/cmake-3.25/Modules/FindPackageHandleStandardArgs.cmake:230 (message):
Could NOT find ZLIB (missing: ZLIB_LIBRARY ZLIB_INCLUDE_DIR)
Call Stack (most recent call first):
CMakeLists.txt:12 (find_package)Config 模式失败则是另一种措辞,直接点名 XXX_DIR:
CMake Error at CMakeLists.txt:15 (find_package):
Could not find a package configuration file provided by "Foo" with any
of the following names:
FooConfig.cmake
foo-config.cmake
Add the installation prefix of "Foo" to CMAKE_PREFIX_PATH or set
"Foo_DIR" to a directory containing one of the above files.读懂现场抓住两件东西:模式——报 FooConfig.cmake / foo-config.cmake 并提示 Foo_DIR 的是 Config 模式,报 (missing: XXX_LIBRARY XXX_INCLUDE_DIR) 且调用栈里有 FindPackageHandleStandardArgs.cmake 的是 Module 模式;缺的是头文件还是库——missing: 后面列出 XXX_INCLUDE_DIR 说明头文件没找到,列出 XXX_LIBRARY 说明库文件没找到,两个都列说明大概率整个开发包没装。
二、病因处置速查表
| 报错现象 / 场景 | 可能原因 | 快速验证 | 处置 |
|---|---|---|---|
Could NOT find Xxx,系统里只装了运行时库 | 只装了 libxxx 没装 libxxx-dev(缺头文件与 .cmake/.pc) | dpkg -l | grep libxxx 看有没有 -dev 后缀包 | 装开发包:apt install libxxx-dev / dnf install xxx-devel |
| 库装在非标准前缀(如 /opt/foo) | 前缀不在 CMake 默认搜索路径 | ls /opt/foo/lib/cmake/ 确认 Config 文件存在 | cmake -DCMAKE_PREFIX_PATH=/opt/foo .. 或 -DFoo_DIR=/opt/foo/lib/cmake/Foo |
| Module 模式缺库/头 | 头文件与库在非系统目录 | find / -name "xxx.h" 2>/dev/null 定位 | 传 -DXXX_INCLUDE_DIR=... -DXXX_LIBRARY=... 显式指定 |
| 走 pkg-config 的查找失败 | .pc 文件不在 PKG_CONFIG_PATH | pkg-config --cflags --libs xxx 是否报错 | export PKG_CONFIG_PATH=/opt/foo/lib/pkgconfig 后重配 |
| 报错名与安装的 Config 文件名大小写不符 | find_package 名字与 xxx-config.cmake 不匹配 | ls 库目录看实际 Config 文件名 | 改 find_package 的名字或 -D<实际名>_DIR=... |
版本不满足:Could NOT find Xxx: Found unsuitable version | 已装版本低于 find_package(Xxx 2.0 REQUIRED) 要求 | 报错行会打印 found version | 升级库版本,或放宽版本要求(确认 API 兼容) |
手写 FindXxx.cmake 不生效 | 模块目录没加进 CMAKE_MODULE_PATH | message(${CMAKE_MODULE_PATH}) 打印确认 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") |
| 交叉编译时找到宿主机库 | 搜索路径没限定到 sysroot | 配置日志里找到的库路径在宿主机 /usr | 设 CMAKE_FIND_ROOT_PATH 指向目标 sysroot |
三、查找链流程图
下面这张图按 CMake 找包的决策顺序展开:红是报错入口与命中的根因,蓝是排查动作,绿是查找通过,橙是修复动作,紫是防复发沉淀。CMake 实际就是按「Module 模式先试(除非禁用)→ Config 模式兜底」这条链走的,跟着链查一遍必能找到断点。
读这张图抓一条主线:先判模式再动手。报 FooConfig.cmake 字样的是 Config 模式,答案永远是「把含该文件的前缀喂给 CMAKE_PREFIX_PATH 或 Foo_DIR」;报 (missing: XXX_LIBRARY XXX_INCLUDE_DIR) 的是 Module 模式,先查 -dev 包是否装了,再决定要不要显式传路径。修完务必删掉 build/CMakeCache.txt 再重配——CMake 会把失败结果缓存,不清缓存重跑等于白修。
四、逐条排查展开
运行时库与开发包是两件事。 发行版把库拆成两个包:libz1 只含 libz.so.1 运行时(够程序跑),libz-dev/zlib-devel 才含头文件、.so 软链、.pc 与 .cmake 文件(够编译用)。CMake 找的是后者:
dpkg -l | grep zlib # 只看 libz1 没有 zlib1g-dev 就是病根
apt install zlib1g-dev # Debian/Ubuntu
dnf install zlib-devel # RHEL/Fedora这是 Could NOT find 报错里占比最大的病因,先排它。
Config 模式的搜索规则。 find_package(Foo) 在 Config 模式下找 <前缀>/lib/cmake/Foo/FooConfig.cmake(也试 share/、<前缀>/lib/cmake/foo/ 等变体)。库装在 /opt/foo 这类非标前缀时,两个喂法:
cmake -B build -DCMAKE_PREFIX_PATH=/opt/foo
# 或精准直指:
cmake -B build -DFoo_DIR=/opt/foo/lib/cmake/FooFoo_DIR 优先级最高、一次只治一个包;CMAKE_PREFIX_PATH 是分号分隔的前缀列表,一批包都在同一前缀时用它最省事。
Module 模式的搜索规则。 报错调用栈里有 FindPackageHandleStandardArgs.cmake 时走的是 Find 模块:它用 find_path(XXX_INCLUDE_DIR xxx.h) 与 find_library(XXX_LIBRARY xxx) 在系统路径里搜。装在非标位置时显式喂:
cmake -B build -DXXX_INCLUDE_DIR=/opt/foo/include -DXXX_LIBRARY=/opt/foo/lib/libfoo.soCMake 自带的 Find 模块清单用 cmake --help-module-list 查;工程手写模块放在如 cmake/FindXxx.cmake,必须 list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") 才会被发现——忘了这行是手写模块「写了却不生效」的唯一原因。
pkg-config 这条平行宇宙。 很多 Find 模块(和 pkg_check_modules)底层调 pkg-config,它只认 PKG_CONFIG_PATH 里的 .pc 文件。排查先绕开 CMake 单测:
pkg-config --cflags --libs foo # 报 not found 就是 .pc 路径问题
export PKG_CONFIG_PATH=/opt/foo/lib/pkgconfig:$PKG_CONFIG_PATH注意 PKG_CONFIG_PATH 指到含 .pc 文件的目录(通常是 lib/pkgconfig 或 share/pkgconfig),不是前缀本身。
名字与版本两个细节坑。 Config 模式对包名大小写敏感:find_package(foo) 能匹配 foo-config.cmake 和 FooConfig.cmake,但 find_package(Foo) 只匹配后者写法族——安装的 Config 文件名与 find_package 实参要对得上。版本不满足时报错会带 Found unsuitable version "1.2" required is "2.0",这不算「找不到」而是「找到但太旧」,处置是升级库或与上游确认 API 兼容后放宽版本约束。
用 CMake 自己的诊断开关看清搜索过程。 cmake --debug-find -B build 会把每个 find_package/find_path/find_library 实际尝试过哪些路径逐条打出,断点在哪一步一目了然;更轻量的做法是给单次查找加日志:find_package(Foo CONFIG QUIET) 后 message(STATUS "Foo_DIR=${Foo_DIR}"),看变量最终是路径还是 Foo_DIR-NOTFOUND。Config 模式还有一个常被忽略的回退顺序:先查 Foo_DIR 变量,再查 CMAKE_PREFIX_PATH,再查环境变量 Foo_DIR,最后才扫系统默认前缀——变量被旧缓存值占住时,后面的路径根本不会被走到。
交叉编译场景的查找隔离。 交叉时最经典的故障是「找到了宿主机 /usr/lib 里的同名库,链接期才炸」。toolchain 文件里三件套缺一不可:CMAKE_FIND_ROOT_PATH 指向目标 sysroot、CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY 与 CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY 把库与头文件的搜索限制在 sysroot 内、CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH 让构建期工具(如代码生成器)仍可用宿主机的。漏掉 ONLY 限定,宿主机的库就会混进搜索结果,报错的形态反而变成链接期的架构不匹配,排查方向完全被带偏。
清缓存重配是硬动作。 CMake 把每次查找结果写进 build/CMakeCache.txt(XXX_DIR:PATH=XXX_DIR-NOTFOUND 这种失败值也会被缓存)。修复环境后必须 rm -rf build/CMakeCache.txt(或整个删 build 目录)再跑 cmake,否则旧的 NOTFOUND 缓存值让修复看起来无效——这是「明明装好了还报错」的头号假相。
五、规范做法与预防
- CI 镜像里把开发包装齐:构建镜像的 Dockerfile 列全
libxxx-dev清单,构建日志开头dpkg -l | grep dev留指纹。 - 第三方库统一前缀管理:手工编译的库一律装到
/opt/<name>-<version>并写进工具链文件(toolchain file)的CMAKE_PREFIX_PATH,不散落/usr/local。 - 工程 README 写清依赖矩阵:每个
find_package对应发行版包名、最低版本、获取方式三列,新机器照单装。 - 交叉编译用 toolchain file:
CMAKE_FIND_ROOT_PATH、sysroot、查找模式(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY等)写进 toolchain 文件,不靠手传参。 - 失败时先看 CMakeError.log 与缓存:
build/CMakeFiles/CMakeError.log记录试编译失败细节,CMakeCache.txt记录每个变量的最终取值,是定位断点的两份原始档案。
这一篇在知识体系里的位置
本篇是 Makefile/CMake 排错速查簇的 CMake 依赖查找篇:CMake 版本不够的看 CMake 版本不兼容报错速查;找不到编译器的看 CMake 找不到编译器速查;联网拉依赖失败的看 CMake FetchContent 拉取失败速查;找到包之后链接期符号缺失的看 undefined reference 链接错误速查、符号重复的看 multiple definition 重复定义速查;编译期资源与崩溃类问题见 make 编译内存超限速查 与 make 编译器内部崩溃速查;语法、路径、权限、规则、卡死、乱码、类型等其它岔路见 Makefile missing separator 速查、fatal error 头文件找不到速查、构建 Permission denied 速查、make: No rule to make target 速查、make 构建卡死无响应速查、make 构建输出乱码速查、error: cannot convert 类型不匹配速查。「先判 Config/Module 模式、再清缓存重配」这对动作对一切 CMake 查找类故障通用。
一句话总结
CMake 找不到依赖包先按报错措辞判模式:报 FooConfig.cmake 是 Config 模式,把含该文件的前缀喂给 CMAKE_PREFIX_PATH 或 Foo_DIR;报 (missing: XXX_LIBRARY XXX_INCLUDE_DIR) 是 Module 模式,先查 -dev/-devel 开发包装没装(最大病因),没装装之、装了在非标前缀就显式传 XXX_INCLUDE_DIR/XXX_LIBRARY;走 pkg-config 的模块先 pkg-config --cflags --libs foo 单测并补 PKG_CONFIG_PATH;手写 Find 模块确认 CMAKE_MODULE_PATH 已追加。修复后删 CMakeCache.txt 重配,看到 -- Found Xxx 才算闭环。