在 CMake 中,find_package() 是一个核心函数,用于查找并加载外部依赖库的配置。它的主要作用是定位头文件、库文件,并设置相关变量,以便后续编译和链接。以下是详细解析:


1. 基本语法

find_package(<PackageName> [version] [REQUIRED] [COMPONENTS components...] [OPTIONAL_COMPONENTS components...] [NO_MODULE] [QUIET] [CONFIG])
  • <PackageName>:要查找的包名(如 BoostEigen3)。
  • version:可选,指定最低版本(如 3.3)。
  • REQUIRED:可选,如果找不到包,则报错并终止构建。
  • COMPONENTS:指定包的子组件(如 Boost 的 systemfilesystem)。
  • NO_MODULE:跳过模块模式,直接使用配置模式(Config Mode)。
  • QUIET:静默模式,不输出警告信息。

find_package() 中的 REQUIRED 是可选的。如果不指定 REQUIRED,CMake 会尝试查找包,但即使找不到也不会报错,而是继续执行后续的 CMake 脚本。此时,你需要通过检查 <PackageName>_FOUND 变量来判断是否成功找到该包,并决定后续逻辑。


1.1 REQUIRED关键字

1.1.1 没有REQUIRED关键字的情况
find_package(<PackageName> [version] [COMPONENTS components...])
  • 如果找到包,CMake 会设置 <PackageName>_FOUND=TRUE 和相关变量(如 _INCLUDE_DIRS_LIBRARIES)。
  • 如果未找到包,<PackageName>_FOUND=FALSE,但不会中断构建。
1.1.2 典型场景
1.1.2.1 可选依赖库

某些库可能是可选的(例如,用于增强功能但不影响核心功能):

find_package(OpenCV)  # 不强制要求 OpenCVif(OpenCV_FOUND)message(STATUS "OpenCV found, enabling advanced features")add_definitions(-DUSE_OPENCV)target_link_libraries(my_app PRIVATE ${OpenCV_LIBRARIES})
else()message(WARNING "OpenCV not found, some features will be disabled")
endif()
1.1.2.2 多版本兼容

尝试查找高版本库,失败时回退到低版本或默认路径:

find_package(Python 3.8)  # 首选 Python 3.8
if(NOT Python_FOUND)find_package(Python 3.6)  # 回退到 Python 3.6
endif()
1.1.2.3 平台特定依赖

某些库仅在特定平台需要(如 Linux 的 libudev):

if(UNIX AND NOT APPLE)find_package(UDEV)  # 仅在 Linux 下查找if(UDEV_FOUND)target_link_libraries(my_app PRIVATE udev)endif()
endif()

1.1.2.4 关键注意事项

1.1.2.4.1 必须检查 _FOUND 变量
如果不检查,后续使用未找到的库会导致编译或链接错误:

find_package(CURL)
# 错误:直接使用 ${CURL_LIBRARIES} 而不检查 CURL_FOUND

1.1.2.4.2 REQUIRED 的对比

行为REQUIRED 模式REQUIRED 模式
找不到包时报错并终止 CMake 配置继续执行,<PackageName>_FOUND=FALSE
适用场景核心依赖(如 ROS、Eigen)可选功能或平台特定依赖
代码复杂度无需额外判断需手动检查 _FOUND 变量

1.1.2.4.3 组件(COMPONENTS)的非必需性
即使包支持组件,也可以不标记 REQUIRED

find_package(Boost COMPONENTS system)
if(Boost_FOUND AND Boost_SYSTEM_FOUND)target_link_libraries(my_app PRIVATE Boost::system)
endif()
1.1.3 实际案例
1.1.3.1 ROS 中的可选消息依赖
find_package(catkin COMPONENTSroscppsensor_msgs  # 可选依赖
)if(catkin_FOUND AND sensor_msgs_FOUND)add_definitions(-DUSE_SENSOR_MSGS)
endif()
1.1.3.2 多图形后端支持
find_package(OpenGL)
find_package(Vulkan)if(OpenGL_FOUND)target_link_libraries(my_engine PRIVATE OpenGL::GL)
elseif(Vulkan_FOUND)target_link_libraries(my_engine PRIVATE Vulkan::Vulkan)
else()message(FATAL_ERROR "No supported graphics API found!")
endif()

1.2 COMPONENTS:指定子组件

1.2.1 作用
  • 用于声明需要查找的包的子模块或组件(例如 Boost 的 systemfilesystem,或 ROS 的 roscpptf)。
  • 如果某个组件未找到,且标记了 REQUIRED,CMake 会报错。
1.2.2 示例
find_package(Boost REQUIRED COMPONENTS system filesystem)
  • 查找 Boost 库,并明确要求 systemfilesystem 两个组件。
  • 成功后,变量 Boost_SYSTEM_FOUNDBoost_FILESYSTEM_FOUND 会被设为 TRUE
1.2.3 关键点
  • 需要包本身支持组件化(如 Boost、Qt、ROS)。
  • 每个组件可能有独立的变量(如 Boost_SYSTEM_LIBRARY)。

1.3 NO_MODULE:强制跳过模块模式

1.3.1 作用
  • 强制 CMake 跳过传统的 Find<Package>.cmake 模块模式,直接使用包的现代配置模式(即查找 <Package>Config.cmake 文件)。
  • 适用于明确知道包提供了 Config.cmake 文件的情况(如 Eigen3、现代 Qt)。
1.3.2 示例
find_package(Eigen3 3.3 REQUIRED NO_MODULE)
  • 跳过 FindEigen3.cmake,直接查找 Eigen3Config.cmake
  • 避免因旧版模块文件与新版本库不兼容导致的问题。
1.3.3 关键点
  • 通常用于现代 CMake 兼容的库(如 Eigen3、VTK)。
  • 如果包没有提供 Config.cmake 文件,使用 NO_MODULE 会导致查找失败。

1.3.4 COMPONENTSNO_MODULE两者关系总结
特性COMPONENTSNO_MODULE
用途指定包的子组件强制使用配置模式(跳过模块模式)
依赖包的支持包需支持组件化(如 Boost、Qt)包需提供 Config.cmake 文件
典型场景find_package(Boost COMPONENTS system)find_package(Eigen3 NO_MODULE)
是否互斥可与 NO_MODULE 同时使用可与 COMPONENTS 同时使用
1.3.5 同时使用的例子
find_package(Qt6 REQUIRED COMPONENTS Core Gui NO_MODULE)
  • 强制使用 Qt6Config.cmake(跳过 FindQt6.cmake),并指定需要 CoreGui 组件。
1.3.6 为什么有些包需要同时用 COMPONENTSNO_MODULE
  • 例如 Qt6 既提供了 Config.cmake 文件,又将功能拆分为多个组件(Core、Gui、Widgets 等)。此时:
    find_package(Qt6 REQUIRED COMPONENTS Core NO_MODULE)
    
    • NO_MODULE:确保使用 Qt6Config.cmake(现代方式)。
    • COMPONENTS:明确要求 Core 组件。
1.3.7 如果包不支持组件,但用了 COMPONENTS 会怎样?
  • CMake 会报错,例如:
    find_package(Eigen3 COMPONENTS Core)  # Eigen3 无组件,会报错
    
    错误信息类似:
    Could not find a configuration file for package "Eigen3" that specifies component "Core".
1.3.8 如何知道一个包是否支持组件?
  • 查看官方文档或包的 Config.cmake 文件。
  • 例如 Boost 的组件列表见:Boost Libraries。

1.3.9 对比示例
案例 1:仅用 COMPONENTS
find_package(Boost REQUIRED COMPONENTS system)
  • 查找 Boost 的 system 组件,使用默认的模块模式(FindBoost.cmake)。
案例 2:仅用 NO_MODULE
find_package(Eigen3 NO_MODULE)
  • 跳过 FindEigen3.cmake,直接查找 Eigen3Config.cmake
案例 3:同时使用
find_package(Qt5 COMPONENTS Widgets NO_MODULE)
  • 强制使用 Qt5Config.cmake,并指定 Widgets 组件。

1.3.10 总结
  • COMPONENTS:用于指定包的子模块,与包的功能拆分相关
  • NO_MODULE:用于控制查找模式,与包的配置方式相关
  • 两者可独立或组合使用,具体取决于包的支持情况。

2. 工作模式

find_package 有两种查找模式:

(1) 模块模式(Module Mode)

  • 查找 <PackageName>Config.cmakeFind<PackageName>.cmake 文件。
  • 通常用于传统库(如 FindBoost.cmake)。
  • 优先级低于配置模式

(2) 配置模式(Config Mode)

  • 查找 <PackageName>Config.cmake 文件(通常由库的开发者提供)。
  • 现代库(如 Eigen3Qt5)优先使用此模式。
  • 通过 NO_MODULE 强制启用。

3. 关键变量

成功调用 find_package 后,CMake 会设置以下变量(以 Eigen3 为例):

变量名作用示例值
<PackageName>_FOUND是否找到包Eigen3_FOUND = TRUE
<PackageName>_INCLUDE_DIR头文件目录Eigen3_INCLUDE_DIRS = /usr/include/eigen3
<PackageName>_LIBRARIES库文件路径Boost_LIBRARIES = /usr/lib/libboost_system.so
<PackageName>_VERSION版本号Eigen3_VERSION = 3.4.0

4. 具体示例解析

(1) ROS 的 catkin

find_package(catkin REQUIRED COMPONENTSroscpp tf
)
  • 作用:查找 ROS 的 catkin 构建系统,并加载 roscpptf 的依赖。
  • 生成的变量
    • catkin_INCLUDE_DIRS:ROS 包的头文件路径。
    • catkin_LIBRARIES:ROS 包的库文件路径。
  • 后续用法
    include_directories(${catkin_INCLUDE_DIRS})
    target_link_libraries(my_node ${catkin_LIBRARIES})
    

(2) Boost 库

find_package(Boost REQUIRED COMPONENTSsystem filesystem
)
  • 作用:查找 Boost 库,并指定需要 systemfilesystem 组件。
  • 生成的变量
    • Boost_INCLUDE_DIRS:Boost 头文件路径(如 /usr/include)。
    • Boost_LIBRARIES:组件库路径(如 boost_systemboost_filesystem)。
  • 后续用法
    target_include_directories(my_app PRIVATE ${Boost_INCLUDE_DIRS})
    target_link_libraries(my_app ${Boost_LIBRARIES})
    

(3) Eigen3 线性代数库

find_package(Eigen3 3.3 REQUIRED NO_MODULE)
  • 作用:查找 Eigen3 库,要求版本 ≥ 3.3,并强制使用配置模式(NO_MODULE)。
  • 生成的变量
    • Eigen3_INCLUDE_DIRS:Eigen 头文件路径(如 /usr/include/eigen3)。
    • Eigen3_VERSION:版本号。
  • 后续用法
    target_include_directories(my_app PRIVATE ${Eigen3_INCLUDE_DIRS})
    

(4) PkgConfig 工具

find_package(PkgConfig REQUIRED)
  • 作用:启用 pkg-config 支持(用于查找没有 CMake 配置文件的库)。
  • 后续用法
    pkg_search_module(GLIB REQUIRED glib-2.0)
    include_directories(${GLIB_INCLUDE_DIRS})
    target_link_libraries(my_app ${GLIB_LIBRARIES})
    

5. 常见问题

(1) 为什么有些包需要 NO_MODULE

  • 例如 Eigen3 只有 Eigen3Config.cmake,没有 FindEigen3.cmake,因此需强制使用配置模式。

(2) REQUIRED 的作用是什么?

  • 如果找不到包,CMake 会报错并停止构建。避免后续链接时出现未定义错误。

(3) 可以没有REQUIRED关键字吗

  • 是的,



5. 调试技巧

  • 查看查找结果
    find_package(Foo)
    message(STATUS "Foo_FOUND = ${Foo_FOUND}, Foo_INCLUDE_DIRS = ${Foo_INCLUDE_DIRS}")
    
  • 手动指定路径(用于调试):
    set(Foo_DIR "/path/to/FooConfig.cmake")  # 提示 CMake 查找路径
    

总结

  • **省略 REQUIRED** 时,find_package()变为“尝试查找”,需配合_FOUND` 变量使用。
  • 适用场景:可选功能、多版本回退、平台特定依赖。
  • 优势:灵活控制构建流程,避免因非核心依赖缺失导致构建失败。

(4) 如何调试 find_package 失败?

  • 检查路径是否在 CMAKE_PREFIX_PATH 中:
    message(STATUS "CMAKE_PREFIX_PATH = ${CMAKE_PREFIX_PATH}")
    
  • 手动指定路径:
    set(Eigen3_DIR "/path/to/eigen3/share/eigen3/cmake")
    

6. 总结

场景示例命令关键变量
ROS 包依赖find_package(catkin REQUIRED COMPONENTS roscpp)catkin_INCLUDE_DIRS
Boost 组件find_package(Boost REQUIRED COMPONENTS system)Boost_LIBRARIES
强制配置模式find_package(Eigen3 NO_MODULE)Eigen3_INCLUDE_DIRS
使用 pkg-configfind_package(PkgConfig)PKG_CONFIG_FOUND

通过 find_package,CMake 可以灵活地集成第三方库,而 ROS 的 catkin 进一步扩展了这一机制,使其支持 ROS 特有的依赖管理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/92121.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/92121.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/92121.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring--BeanFactoryPostProcessor的用法

原文网址&#xff1a;Spring--BeanFactoryPostProcessor的用法_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍Spring的BeanFactoryPostProcessor的用法。 BeanPostProcessor和BeanFactoryPostProcessor的区别 项BeanPostProcessorBeanFactoryPostProcessor处理的对象处理…

了解类加载器吗?类加载器的类型有哪些?

一、什么是类加载器&#xff08;ClassLoader&#xff09; 类加载器是 Java 虚拟机中的一部分&#xff0c;负责将 .class 文件加载到 JVM 内存中&#xff0c;生成对应的 Class 对象。 Java 程序中所有的类在使用前都必须通过类加载器加载进 JVM&#xff0c;才能被执行。二、类加…

PHP面向对象高级特性:魔术方法、对象迭代器与设计模式应用

引言 在前一篇文章中,我们探讨了PHP的Traits、匿名类和对象比较机制。本文将深入PHP面向对象编程的更多高级特性,包括魔术方法、对象迭代器以及常用设计模式的实际应用,这些特性能够帮助开发者构建更加灵活和强大的面向对象系统。 魔术方法深度解析 魔术方法是PHP中一组以…

【Java基础】一个月教你轻松掌握Java——第三篇Git

一、Java概述&#xff08;之前的文章&#xff09;二、版本控制工具Git其实这个与Java基础关系不大&#xff0c;但是这个工具还是很重要的&#xff0c;不管是团队之间打比赛还是就业都应该学会它&#xff0c;秉持着学的早一些&#xff0c;用的时间长一点&#xff0c;会更熟练。&…

【C# in .NET】16. 探秘类成员-索引器:通过索引访问对象

探秘类成员-索引器:通过索引访问对象 在 C# 中,索引器(Indexer)是一种独特的类成员,它允许类或结构的实例像数组一样被索引访问,为数据访问提供了极大的灵活性。本文将从基础概念出发,深入.NET 框架底层,剖析索引器的实现机制,并通过实战案例展示其强大的应用价值。 …

idea出现:java: Target level ‘1.7‘ is incompatible with source level ‘1.8‘.解决办法

在文件->设置->java编译器&#xff0c;把这里版本对应上。这里用的是8版本

ssms(SQL 查询编辑器) 添加快捷键 Ctrl+D(功能等于Ctrl+C + Ctrl+V),一步到位

1,打开ssms 工具&#xff0c;打开对应添加快捷键得地方2&#xff0c;分配 快捷键3&#xff0c;看效果

数学建模--层次分析法

层次分析法&#xff08;AHP&#xff09;笔记 一、核心概念 &#xff08;一&#xff09;问题本质 面对多方案、多准则决策&#xff0c;将复杂问题分层拆解&#xff0c;通过定性与定量结合&#xff0c;确定各因素权重&#xff0c;选出最优方案&#xff0c;比如选“微博之星”时综…

人工智能教研室暑期培训flask全栈开发培训

人工智能教研室暑期培训flask全栈开发培训第一天&#xff1a;Flask 基础入门与环境搭建实践项目&#xff1a;搭建个人博客首页&#xff0c;包含文章列表与详情页上午&#xff1a;环境搭建与 Flask 基础1. 安装 Python 与虚拟环境配置2. Flask 框架简介与第一个 "Hello Wor…

MySQL(141)如何处理重复数据问题?

处理重复数据问题是数据管理中的一个常见挑战。重复数据会影响数据库的性能、占用资源&#xff0c;并且可能导致数据分析结果的偏差。以下是处理重复数据问题的详细步骤以及结合代码的示例。 一、识别重复数据 首先&#xff0c;需要识别数据库中的重复数据。可以使用 SQL 查询来…

MySQL 核心知识点梳理(3)

目录 SQL优化 23什么是慢SQL 如何优化呢? 如何利于覆盖索引 如何使用联合索引 如何进行分页优化 Join代替子查询 为什么要小表驱动大表? 为什么避免join太多的表? 如何进行排序优化 什么是filesort 全字段排序和rowid排序 条件下推 索引 索引为什么能提高MyS…

关于注册登录功能制作的步骤(文件IO存储+LVGL弹窗提示)

按你的需求&#xff08;文件IO存储LVGL弹窗提示&#xff09;&#xff0c;工程需创建以下文件&#xff0c;代码按功能模块化存放&#xff0c;清晰明了&#xff1a;一、需要创建的文件清单 文件名 作用 存放内容 main.c 程序入口 主函数、硬件初始化、LVGL初始化、启动界面 ui.…

自媒体端后台设计指南:从注册认证到内容管理的全流程搭建

自媒体端后台设计指南&#xff1a;从注册认证到内容管理的全流程搭建自媒体端后台是专业创作者管理内容、粉丝和数据的核心阵地&#xff0c;其设计直接影响创作效率和平台运营质量。一个功能清晰、操作便捷的后台系统&#xff0c;能让创作者专注于内容生产&#xff0c;而非被复…

uniapp扫描二维码反色处理

在开发扫描二维码过程中&#xff0c;发现白底黑码可以直接用uni.scanCode扫描出来&#xff0c;但是黑底白码就扫不出来&#xff0c;于是就试试反色后的二维码能不能扫描出来&#xff0c;没想到真的可以&#xff0c;下面附上完整代码&#xff1a; <u-icon name"scan&quo…

C语言定义fixed_t什么意思

在 C 语言中&#xff0c;fixed_t 通常是一个自定义的类型别名&#xff08;typedef&#xff09;&#xff0c;用于表示固定点数&#xff08;Fixed-Point Number&#xff09;&#xff0c;而非 C 语言标准库中的原生类型。它主要用于需要高效实数运算但无法使用浮点数的场景&#x…

音频3A处理简介之ANS(自动噪声抑制)

我们常用的手机、消费类摄像头等产品的麦克风所采集的原始声音信号中往往包含了比较多的背景噪音&#xff0c;不仅影响用户录音和回放的使用体验&#xff0c;而且这些噪声数据还会降低音频编码的压缩效率&#xff0c;因此有必要对音频底噪进行抑制处理&#xff0c;这就是ANS&am…

Python 使用期物处理并发(使用concurrent.futures模块启动 进程)

使用concurrent.futures模块启动进程 concurrent.futures 模块的文档 &#xff08;https://docs.python.org/3/library/concurrent.futures.html&#xff09;副标题 是“Launching parallel tasks”&#xff08;执行并行任务&#xff09;。这个模块实现的是真正 的并行计算&…

【系统全面】Linux内核原理——基础知识介绍

理解内核&#xff1a;内核原理 计算机系统的软件分层 不同于单片机中使用代码直接与硬件交互&#xff0c;对于这种方式的缺点深有&#xff1a; &#xff08;1&#xff09;复杂度高&#xff0c;调用难度高&#xff0c;需要深入理解硬件的工作原理和细节。 &#xff08;2&#xf…

Oracle自治事务——从问题到实践的深度解析

一、引言&#xff1a;当“关键操作”遇上主事务的“生死绑定”​先问大家一个问题&#xff1a;假设你在开发一个用户管理系统&#xff0c;核心功能是“用户注册”&#xff0c;同时需要记录“操作日志”。某天&#xff0c;用户提交注册信息时&#xff0c;数据库突然因磁盘空间不…

广播(Broadcast)和组播(Multicast)对比

概述 广播&#xff08;Broadcast&#xff09;和组播&#xff08;Multicast&#xff09;是计算机网络中两种重要的一对多通信方式&#xff0c;用于高效地将数据同时分发给多个接收者&#xff0c;它们的核心区别在于目标接收者的范围和控制精度&#xff0c;基于业务对效率、规模和…