CMake中find_package指令的实现

 更新时间:2026年07月02日 08:23:32   作者:流星雨爱编程  
CMake查找模块(findmodule)提供了两种搜索第三方依赖的模式,模块模式和配置模式,本文就来详细的介绍一下CMake中find_package指令的实现,感兴趣的可以了解一下

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 部分组件)。
  • 模块文件由用户或 CMake 官方编写,通过手动逻辑查找库的头文件目录(find_path)、库文件(find_library),并定义 <PackageName>_FOUND、<PackageName>_INCLUDE_DIRS、<PackageName>_LIBRARIES 等变量。

2.配置模式(Config Mode)

  • 查找库自带的 CMake 配置文件(如 <PackageName>Config.cmake 或<PackageName>-config.cmake 或 <PackageName>ConfigVersion.cmake)。
  • 适用于现代库(如 OpenCV、Qt、Eigen)。
  • 配置文件由库的编译安装流程生成,会自动导出导入目标(如 <PackageName>::<TargetName>),并封装头文件、库文件、编译选项等信息,无需手动定义变量。

模式选择规则:

  • 默认优先尝试 配置模式,失败后尝试 模块模式。
  • 通过 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 REQUIRED
    HINTS ${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_DIR
    NAMES mylib.h
    HINTS ${MYLIB_ROOT}/include
    PATHS /usr/local/include /opt/mylib/include
)

# 3. 查找库文件(静态库)
find_library(MYLIB_LIBRARY
    NAMES mylib mylib_static
    HINTS ${MYLIB_ROOT}/lib
    PATHS /usr/local/lib /opt/mylib/lib
)

# 4. 验证版本(示例:从头文件中提取版本)
if(MYLIB_INCLUDE_DIR)
    file(STRINGS "${MYLIB_INCLUDE_DIR}/mylib.h" MYLIB_VERSION_LINE
         REGEX "#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(MyLib
    REQUIRED_VARS MYLIB_LIBRARY MYLIB_INCLUDE_DIR
    VERSION_VAR MYLIB_VERSION
)

# 6. 可选:创建导入目标(现代 CMake 推荐)
if(MYLIB_FOUND)
    add_library(MyLib::MyLib UNKNOWN IMPORTED)
    set_target_properties(MyLib::MyLib PROPERTIES
        IMPORTED_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中find_package指令的实现的文章就介绍到这了,更多相关CMake find_package指令内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:

相关文章

  • C语言链表实现学生信息管理系统程序设计

    C语言链表实现学生信息管理系统程序设计

    这篇文章主要为大家详细介绍了C语言链表实现学生信息管理系统程序设计,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-07-07
  • 手把手带你学习C++的数据类型

    手把手带你学习C++的数据类型

    这篇文章主要为大家介绍了C++的数据类型,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助,希望能够给你带来帮助
    2021-11-11
  • C语言实现全排列算法模板的方法

    C语言实现全排列算法模板的方法

    这篇文章主要介绍了C语言实现全排列算法模板的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-02-02
  • Qt实现小功能之复杂抽屉效果详解

    Qt实现小功能之复杂抽屉效果详解

    在Qt自带的控件中,也存在抽屉控件:QToolBar。但是,该控件有个缺点:一次只能展开一个抽屉信息,无法实现多个展开。所以本文将自定义实现复杂抽屉效果,需要的可以参考一下
    2022-10-10
  • QT+OpenCV实现录屏功能

    QT+OpenCV实现录屏功能

    这篇文章主要为大家详细介绍了QT+OpenCV实现录屏功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • VC++中图像处理类CBitmap的用法

    VC++中图像处理类CBitmap的用法

    使用VC进行图像处理的时候,CBitmap类为我们提供了丰富的位图处理函数,本文总结了该类的相关函数和常用使用方法,包括加载位图,显示位图,析构CBitmap资源以及在内存中保存位图等内容。
    2015-11-11
  • C++实现的大数相乘算法示例

    C++实现的大数相乘算法示例

    这篇文章主要介绍了C++实现的大数相乘算法,结合实例形式分析了C++大数相乘的概念、原理及代码实现技巧,需要的朋友可以参考下
    2017-08-08
  • VC程序设计小技巧20例

    VC程序设计小技巧20例

    这篇文章主要介绍了VC程序设计小技巧20例,需要的朋友可以参考下
    2014-07-07
  • c语言实现数组循环左移m位

    c语言实现数组循环左移m位

    这篇文章主要介绍了c语言实现数组循环左移m位,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • Qt实现简单的TCP通信

    Qt实现简单的TCP通信

    这篇文章主要为大家详细介绍了Qt实现简单的TCP通信,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08

最新评论