首页 > 极客资料 博客日记
find_package()使用指南
2025-01-12 21:30:05极客资料围观4次
关于find_package()
在使用cmake引用第三方库(比如OpenCV
)时,我们总是使用find_package()这个指令来实现对包的查找(比如find_package(OpenCV)
)。调用完后就可以使用一些似乎凭空出现的变量如${OpenCV_INCLUDE_DIRS}
以及${OpenCV_LIBS}
,分别指示了OpenCV库的头文件路径以及各个库文件位置。
find_package(OpenCV)
include_directories(${OpenCV_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBS})
# 这样就可以使用到OpenCV了
cmake官方文档对find_package()的解释是这样的:
find_package(<PackageName> [version] [EXACT] [QUIET] [MODULE]
[REQUIRED] [[COMPONENTS] [components...]]
[OPTIONAL_COMPONENTS components...]
[REGISTRY_VIEW (64|32|64_32|32_64|HOST|TARGET|BOTH)]
[GLOBAL]
[NO_POLICY_SCOPE]
[BYPASS_PROVIDER])
其中[]
中的内容表示为可选项。下面我们来解释find_package()是如何工作的。
find_package()的工作原理
实际上,find_package(<PackageName>)
运行时,会去指定路径查找一些名字为
Find<PackageName>.cmake
<PackageName>Config.cmake
<lowercasePackageName>-config.cmake
<lowercasePackageName>-config-version.cmake
# 指定版本信息
<PackageName>ConfigVersion.cmake
# 指定版本信息
的文件,注意这里的命名格式是非常的固定的,基本都是FindXXX.cmake
或者XXXConfig.cmake
。这些后缀为.cmake
的文件本质上也是使用cmake
语言编写的脚本文件,它们会定义一些变量,比如<PackageName>_INCLUDE_DIRS
和<PackageName>_LIBS
等。当find_package()
找到这些文件时,会执行这些文件并将其中定义的变量引入到当前的cmake
环境中。
对于搜索这些后缀为.cmake
的文件,find_package()采用两种策略来实现
find_package()的模块模式
这里引用官方文档的解释:
在这种模式下,CMake 搜索名为
Find<PackageName>.cmake
的文件,首先在 CMAKE_MODULE_PATH 中列出的位置中查找,然后在 CMake 提供的 Find Modules 中查找安装。如果找到该文件,CMake 将读取并处理该文件。它负责查找包、检查版本并生成任何需要的消息。一些 Find 模块对版本控制提供有限支持或不支持;检查查找模块的文档。
一般来说,FindXXX.cmake并非为包所提供,大多数包提供的是更为严谨的XXXConfig.cmake,这将在后面说到。也就是说,FindXXX.cmake是一个较为简单的查找模块,大多为用户或者cmake本身自行编写或者提供。查找时,cmake会优先查找cmake环境变量的CMAKE_MODULE_PATH
中的路径(这个变量默认为空),然后再查找cmake自带的Find Modules中的路径,这部分可以使用cmake --help-module-list
查看cmake自带的模块列表,可以看到很多FindXXX.cmake文件。
或者对于一些轻量级的包,本身并不提供XXXConfig.cmake或者FindXXX.cmake,而是使用其他包管理器(比如package-config
,使用后缀为.pc的文件来管理),若想使用find_package()引用这些包,就需要自行编写FindXXX.cmake文件。(当然也可以直接使用cmake中的pkg_check_modules()
来引用这些包)。下面给出一个FindXXX.cmake的例子,引用的是ffmpeg这个包。
#FindFFMEPG.cmake
set(FFMPEG_SOURCE /home/ruby/ffmpeg_loc) #指定ffmpeg位置
set(FFMPEG_INCLUDE_DIRS ${FFMPEG_SOURCE}/include)
set(FFMPEG_LIBDIRS_DIRS ${FFMPEG_SOURCE}/lib)
find_library(FFMPEG_AVCODEC_LIBRARY avcodec ${FFMPEG_LIBDIRS_DIR})
find_library(FFMPEG_AVFORMAT_LIBRARY avformat ${FFMPEG_LIBDIRS_DIR})
find_library(FFMPEG_AVUTIL_LIBRARY avutil ${FFMPEG_LIBDIRS_DIR})
find_library(FFMPEG_SWSCALE_LIBRARY swscale ${FFMPEG_LIBDIRS_DIR})
find_library(FFMPEG_SWRESAMPLE_LIBRARY swresample ${FFMPEG_LIBDIRS_DIR})
set(FFMPEG_LIBS ${FFMPEG_AVCODEC_LIBRARY} ${FFMPEG_AVFORMAT_LIBRARY} ${FFMPEG_AVUTIL_LIBRARY} ${FFMPEG_SWSCALE_LIBRARY} ${FFMPEG_SWRESAMPLE_LIBRARY})
可以看到,我们做到工作无非是设置一些变量,这些变量指向了ffmpeg的头文件路径以及库文件路径,然后使用find_library()
来查找对应的库文件。
在使用时,我们可以使用set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} /path/to/FindFFMPEG.cmake)
来指定FindFFMPEG.cmake的位置,然后使用find_package(FFMPEG)
来引用ffmpeg这个包,然后就可以使用${FFMPEG_INCLUDE_DIRS}
和${FFMPEG_LIBS}
了。若这个脚本就在当前目录下,可以直接使用set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR})
来指定。
find_package()的配置模式
这里引用官方文档的解释:
在这种模式下,CMake 搜索名为“<lowercasePackageName>-config.cmake”或“<PackageName>Config.cmake”的文件。如果指定了版本详细信息,它还将查找``<lowercasePackageName>-config-version.cmake`` 或``<PackageName>ConfigVersion.cmake``(请参阅:ref:version selection 以了解如何将它们分开使用版本文件)。
在配置模式下,可以为该命令提供一个名称列表,以作为包名称进行搜索。 CMake 搜索配置和版本文件的位置比模块模式复杂得多。
配置和版本文件通常作为包的一部分安装,因此它们往往比查找模块更可靠。它们通常包含包内容的直接知识,因此不需要在配置或版本文件本身中进行搜索或试探。
对于大多数的第三方包,都会提供XXXConfig.cmake
文件,这个文件会定义一些变量,比如XXX_INCLUDE_DIRS
和XXX_LIBS/XXX_LIBRARIS
等。当模块模式搜索不到时,自动切换到配置模式进行搜索。配置模式的搜索非常繁琐,会尽一切可能去搜索。有一些我也看不懂,这里挑几个比较易懂且常用的来说。
- 在
XXX_DIR
指定的路径下搜索XXXConfig.cmake
文件,XXX_DIR
为变量或者环境变量,指定到配置文件所在路径 - 在
CMAKE_PREFIX_PATH
指定的路径下搜索XXXConfig.cmake
文件 - 在环境变量
PATH
下搜索XXXConfig.cmake
文件 - .....
其中,第2和第3种方式提供了一种以前缀路径的方式来指定包的位置,当本级路径搜索不到时,cmake会将本级路径作为前缀去搜索该路径下其他文件中是否有配置文件。匹配规则如下:
其中的<prefix>
或者<前缀>即为上述指定的根路径,以根路径为前缀,一直去搜索,直到找到配置文件为止。例如find_package(OpenCV)
,其中PATH
指定有一个路径为/usr/lib/x86_64-linux-gnu/
,查找时便会找
- /usr/lib/x86_64-linux-gnu/
- /usr/lib/x86_64-linux-gnu/cmake/
- /usr/lib/x86_64-linux-gnu/OpenCV(opencv)/
- /usr/lib/x86_64-linux-gnu/cmake/OpenCV(opencv)/ ....
等能匹配上的路径,注意,这里的<name>*
中的name对应于find_package()中的参数,即find_package(OpenCV)
中的OpenCV
。但是在作为前缀路径时,name参数不区分大小写,且允许有后缀,如opencv4.5
。规则中的|
代表选其一,<arch>
为系统架构,比如x86,64位架构下就会搜索/lib/x86_64-linux-gnu
而arm64
架构下则会搜索/lib/aarch64-linux-gnu
等。
如何灵活使用?
对于自己写的FindXXX.cmake
,在使用时用
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR})
# or不在本级目录
set(MY_FINDXXX "path/to/FindXXX.cmake")
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${MY_FINDXXX})
对于三方库,可以直接用find_package()
find_package(OpenCV)
倘若找不到,可以用locate命令查找一下,然后手动指定路径
locate OpenCVConfig.cmake
然后将配置文件所在路径赋值给XXX_DIR
set(OpenCV_DIR /path/to/opencv)
对于有多个配置文件的项目(如Qt),使用CMAKE_PREFIX_PATH
来指定路径
set(CMAKE_PREFIX_PATH /path/to/qt)
给出一个例子
进入到可以匹配到路径的路径,我这里是/home/ruby/Qt5.14.0/5.14.0/gcc_64
,里面的格式为
也就是最好找到带有lib字眼或者cmake字眼的那一级目录即可,然后将这个路径赋值给CMAKE_PREFIX_PATH
set(CMAKE_PREFIX_PATH /home/ruby/Qt5.14.0/5.14.0/gcc_64)
即可。使用这个方法可以简便的使用不同版本的Qt,比如我这里有5.14.0和5.15.0两个版本,只需要将CMAKE_PREFIX_PATH
指定到对应的路径即可。
set(CMAKE_PREFIX_PATH /home/ruby/Qt5.15.0/5.15.0/gcc_64)
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} /home/ruby/Qt5.14.0/5.14.0/gcc_64)
# 注意,这两个路径正常直接find是find不到的,只能加入CMAKE_PREFIX_PATH中
#find_package(Qt5 <version> COMPONENTS Core Widgets REQUIRED), version填版本号,如
find_package(Qt5 5.15.0 COMPONENTS Core Widgets REQUIRED)
# or
find_package(Qt5 5.14.0 COMPONENTS Core Widgets REQUIRED)
可以加入EXACT来精确匹配,当匹配不到时会报错
find_package(Qt5 5.14.0 EXACT COMPONENTS Core Widgets REQUIRED)
标签:
相关文章
最新发布
- .NET 开发的分流抢票软件,不做广告、不收集隐私
- 深入理解ASP.NET Core 管道的工作原理
- DataV Note:让Jupyter Notebook绽放新活力
- Mysql身份认证过程
- .NET 9 new features-Microsoft.ML.Tokenizers 库
- 掌握设计模式--代理模式
- 前端实现 HTML 网页转 PDF 并导出
- 回顾 2024 年 12 个月的C#/.NET/.NET Core优秀项目和框架简报
- 《深入理解Mybatis原理》Mybatis中的缓存实现原理
- 互联网不景气了那就玩玩嵌入式吧,用纯.NET开发并制作一个智能桌面机器人(一):从.NET IoT入门开始