首页 > 极客资料 博客日记

find_package()使用指南

2025-01-12 21:30:05极客资料围观4

极客之家推荐find_package()使用指南这篇文章给大家,欢迎收藏极客之家享受知识的乐趣

关于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_DIRSXXX_LIBS/XXX_LIBRARIS等。当模块模式搜索不到时,自动切换到配置模式进行搜索。配置模式的搜索非常繁琐,会尽一切可能去搜索。有一些我也看不懂,这里挑几个比较易懂且常用的来说。

  • XXX_DIR指定的路径下搜索XXXConfig.cmake文件,XXX_DIR为变量或者环境变量,指定到配置文件所在路径
  • CMAKE_PREFIX_PATH指定的路径下搜索XXXConfig.cmake文件
  • 在环境变量PATH下搜索XXXConfig.cmake文件
  • .....

其中,第2和第3种方式提供了一种以前缀路径的方式来指定包的位置,当本级路径搜索不到时,cmake会将本级路径作为前缀去搜索该路径下其他文件中是否有配置文件。匹配规则如下:

image

其中的<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-gnuarm64架构下则会搜索/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,里面的格式为
image

也就是最好找到带有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)


版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!

标签:

相关文章

本站推荐

标签云