CMake指令:find_package()
目录
1.简介
2.搜索模式
3.常用参数
4.工作流程
5.内置模块示例:FindBoost.cmake
6.自定义模块文件(Find.cmake)
7.模块模式 vs 配置模式
8.总结
1.简介
查找模块(find module)是一系列用于搜索第三方依赖软件包(包括库或可执行文件)的模块。对查找模块的引用一般不使用include命令,而是使用find_package命令。
基本语法
find_package(<PackageName> [version] [EXACT] [QUIET] [MODULE][REQUIRED] [[COMPONENTS] [components...]][OPTIONAL_COMPONENTS components...][CONFIG|NO_MODULE][HINTS path1 [path2 ... ]][PATHS path1 [path2 ... ]][NO_DEFAULT_PATH][NO_PACKAGE_ROOT_PATH][NO_CMAKE_PATH][NO_CMAKE_ENVIRONMENT_PATH][NO_SYSTEM_ENVIRONMENT_PATH][NO_CMAKE_PACKAGE_REGISTRY][NO_CMAKE_BUILDS_PATH][NO_CMAKE_SYSTEM_PATH][CMAKE_FIND_ROOT_PATH_BOTH|ONLY_CMAKE_FIND_ROOT_PATH|NO_CMAKE_FIND_ROOT_PATH])
常用简化形式:
find_package(Boost 1.70 REQUIRED COMPONENTS system filesystem)
find_package(OpenCV REQUIRED)
2.搜索模式
find_package
支持两种搜索模式:
1. 模块模式(Module Mode)
- 使用 CMake 内置的模块文件(位于
Modules/Find<PackageName>.cmake
)来完成对软件包的搜索。它首先在CMAKE_MODULE_PATH变量定义的路径列表中搜索查找模块,若找不到,则从CMake安装目录中搜索符合该名称的CMake预制的查找模块。如果任未找到对应的查找模块,该命令会切换到配置模式再进行处理。 - 适用于没有提供 CMake 配置文件的旧库(如 OpenGL、Boost 部分组件)。
2.配置模式(Config Mode)
- 查找库自带的 CMake 配置文件(如
<PackageName>Config.cmake
或<PackageName>ConfigVersion.cmake
)。 - 适用于现代库(如 OpenCV、Qt、Eigen)。
模式选择规则:
- 默认优先尝试 配置模式,失败后尝试 模块模式。
- 通过
CONFIG
或NO_MODULE
参数强制使用配置模式。 - 通过
MODULE
参数强制使用模块模式。如:
find_package(PackageName MODULE) # 强制使用模块模式
3.常用参数
参数 | 作用 |
---|---|
REQUIRED | 表示该软件包是构建过程中所必须的,找不到包时终止配置并报错。 |
QUIET | 用于启用静默模式,找不到包时不显示警告(默认会显示警告)。 |
EXACT | 要求版本严格匹配(如 3.14.1 )。 |
COMPONENTS | 指定需要的组件(如 Boost 的 system 、filesystem )。 |
HINTS | 手动指定可能的搜索路径(优先级高于默认路径)。 |
PATHS | 强制搜索特定路径(优先级最高)。 |
NO_DEFAULT_PATH | 不搜索任何默认路径(仅使用 HINTS 和 PATHS )。 |
4.工作流程
1.确定搜索路径
- 系统默认路径(如
/usr/lib/cmake
、C:/Program Files/<PackageName>
)。 CMAKE_PREFIX_PATH
环境变量指定的路径。HINTS
和PATHS
参数指定的路径。
2.查找配置文件
- 配置模式:查找
<PackageName>Config.cmake
或<lowercase-package-name>-config.cmake
。 - 模块模式:查找 CMake 内置的
Find<PackageName>.cmake
模块。
这里也可以自定义搜索路径:
find_package(MyLib REQUIREDHINTS ${CMAKE_SOURCE_DIR}/../mylib/install # 优先搜索此路径PATHS /opt/mylib /usr/local/mylib # 备选路径
)
3.验证版本(若指定)
- 检查库版本是否满足要求(如
>=3.10
或EXACT 3.14.1
)。
4.导入目标
成功后,CMake 会定义一系列变量和导入目标(如 <PackageName>::<Component>
)。
设置结果变量
通过 find_package_handle_standard_args
命令,根据搜索结果设置以下关键变量:
<PackageName>_FOUND
:是否找到库(TRUE
/FALSE
)。<PackageName>_INCLUDE_DIRS
或<PackageName>_INCLUDES
:头文件路径。<PackageName>_LIBRARIES
或<PackageName>_LIBS
:库文件路径。<PackageName>_VERSION
:库版本号。
创建导入目标(配置模式推荐)
现代模块文件(如 FindBoost.cmake
)会额外创建导入目标(如 Boost::system
),允许通过 target_link_libraries
直接链接。
find_package(OpenCV REQUIRED)
target_link_libraries(myapp PRIVATE ${OpenCV_LIBS}) # 模块模式
# 或
target_link_libraries(myapp PRIVATE OpenCV::opencv_core) # 配置模式
5.内置模块示例:FindBoost.cmake
以查找 Boost 库为例,模块模式的典型用法如下:
1. 调用 find_package
find_package(Boost 1.70 REQUIRED COMPONENTS system filesystem)
2. 模块文件的行为
FindBoost.cmake
会:
- 搜索 Boost 的头文件路径(如
/usr/include/boost
)。 - 搜索指定组件的库文件(如
libboost_system.so
、libboost_filesystem.so
)。 - 设置变量:
Boost_FOUND # 是否找到所有必需组件
Boost_INCLUDE_DIRS # 头文件路径
Boost_LIBRARIES # 库文件列表(如 boost_system;boost_filesystem)
Boost_VERSION # 版本号(如 1.70.0)
3.在项目中使用结果
if(Boost_FOUND)include_directories(${Boost_INCLUDE_DIRS})target_link_libraries(myapp PRIVATE ${Boost_LIBRARIES})# 或使用导入目标(若模块支持)# target_link_libraries(myapp PRIVATE Boost::system Boost::filesystem)
endif()
6.自定义模块文件(Find<PackageName>.cmake)
若依赖库没有内置的 Find<PackageName>.cmake
,可手动编写模块文件。以下是一个简化的 FindMyLib.cmake
示例:
# 1. 定义缓存变量,允许用户手动指定路径
set(MYLIB_ROOT "" CACHE PATH "MyLib installation root")# 2. 查找头文件
find_path(MYLIB_INCLUDE_DIRNAMES mylib.hHINTS ${MYLIB_ROOT}/includePATHS /usr/local/include /opt/mylib/include
)# 3. 查找库文件(静态库)
find_library(MYLIB_LIBRARYNAMES mylib mylib_staticHINTS ${MYLIB_ROOT}/libPATHS /usr/local/lib /opt/mylib/lib
)# 4. 验证版本(示例:从头文件中提取版本)
if(MYLIB_INCLUDE_DIR)file(STRINGS "${MYLIB_INCLUDE_DIR}/mylib.h" MYLIB_VERSION_LINEREGEX "#define MYLIB_VERSION \"[0-9.]+\"")string(REGEX REPLACE "#define MYLIB_VERSION \"([0-9.]+)\"" "\\1"MYLIB_VERSION "${MYLIB_VERSION_LINE}")
endif()# 5. 设置标准结果变量
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(MyLibREQUIRED_VARS MYLIB_LIBRARY MYLIB_INCLUDE_DIRVERSION_VAR MYLIB_VERSION
)# 6. 可选:创建导入目标(现代 CMake 推荐)
if(MYLIB_FOUND)add_library(MyLib::MyLib UNKNOWN IMPORTED)set_target_properties(MyLib::MyLib PROPERTIESIMPORTED_LOCATION "${MYLIB_LIBRARY}"INTERFACE_INCLUDE_DIRECTORIES "${MYLIB_INCLUDE_DIR}")
endif()
使用自定义模块:
# 添加模块路径到 CMAKE_MODULE_PATH
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules")# 调用 find_package
find_package(MyLib 2.0 REQUIRED)# 链接导入目标(或使用变量)
target_link_libraries(myapp PRIVATE MyLib::MyLib)
7.模块模式 vs 配置模式
特性 | 模块模式 | 配置模式 |
---|---|---|
依赖文件 | CMake 内置 / 用户自定义的 Find<>.cmake | 库自身提供的 <>.cmake 或 <>.Config.cmake |
维护者 | CMake 社区或用户 | 库开发者 |
变量命名 | 不统一(如 Boost_LIBRARIES vs OpenCV_LIBS ) | 统一(通过导入目标) |
推荐场景 | 旧库、无 CMake 支持的库 | 现代库(如 Qt、Eigen) |
集成度 | 较低(需手动处理变量) | 较高(自动生成导入目标) |
8.总结
1.优先使用配置模式:现代库通常提供自己的 CMake 配置文件(如 Qt5Config.cmake
),通过导入目标(如 Qt5::Core
)可自动处理头文件路径和链接依赖,避免变量污染。
2.模块模式的局限性:模块文件由第三方维护(如 CMake 社区),可能存在版本滞后或配置不完整的问题(如缺少某些组件)。
3.自定义模块的注意事项
- 使用
find_package_handle_standard_args
统一结果变量。 - 为库创建导入目标(
IMPORTED
目标),与现代 CMake 风格兼容。 - 通过
CACHE
变量允许用户手动指定路径(如MYLIB_ROOT
)。
模块模式是 CMake 兼容旧库或无 CMake 支持库的重要机制,通过 Find<PackageName>.cmake
模块文件实现依赖查找。尽管配置模式更现代,但模块模式在兼容传统项目时仍不可替代。在实际开发中,建议优先使用配置模式,仅在必要时通过自定义模块支持旧库。
相关链接
- CMake 官网 CMake - Upgrade Your Software Build System
- CMake 官方文档:CMake Tutorial — CMake 4.0.2 Documentation
- CMake 源码:https://github.com/Kitware/CMake
- CMake 源码:Sign in · GitLab