CMake: find_package

  • Post author:
  • Post category:其他




查找外部项目, 并加载它的设置



基本标志与模块模式

find_package(<PackageName> [version] [EXACT] [QUIET] [MODULE]
             [REQUIRED] [[COMPONENTS] [components...]]
             [OPTIONAL_COMPONENTS components...]
             [NO_POLICY_SCOPE])

执行该命令之后, 可以通过查询混_Found这个变量来确定是否找到的对应的包。QUIET选项的设置可以用来关闭相关消息的输出, 包括非必须包未能找到的消息。REQUIRED选项设置后,当所查找的包未能找到之后会输出一个错误信息并终止处理过程。指定包所需的特定部件的列表可以跟在COMPONENTS之后。额外的可选部件列表可以跟在OPTIONAL_COMPONENTS后面。

[version] 给出了所找包的版本兼容性要求。版本一般的格式是:major[.minor[.patch[.tweak]]]。 其中major是一个大版本,这种版本变化一般伴随着API的修改之类的变化,可能会存在一些兼容性问题;minor一般是指API增加一类的修改;path通常指的是功能实现、函数实现方式的修改之类的。选项EXACT 指明了版本必须精确匹配。

这个命令用来搜索包有两个模式:模块模式(Module)和配置模式(Config)。默认模式是模块模式,如果在模块模式下没有找到包,则该命令会自动切换到配置模式下进行包的搜索, 可以通过设置标志CMAKE_FIND_PACKAGE_PREFER_CONFIG来确定优先使用Config模式。如果指定了搜索模式,则不会发生这种自动切换模式的搜索策略。在Module模式下,CMake会去搜索一个名为Find.cmake的文件。这个文件会先在CMAKE_MODULE_PATH下搜索, 然后再在CMake安装路径下的Find Module下搜索。如果找到了对应的文件,它会被读取并进行处理。 处理过程中包括查找相应的文件、核验版本和生成所需的信息。



全部标志与配置模式

find_package(<PackageName> [version] [EXACT] [QUIET]
             [REQUIRED] [[COMPONENTS] [components...]]
             [OPTIONAL_COMPONENTS components...]
             [CONFIG|NO_MODULE]
             [NO_POLICY_SCOPE]
             [NAMES name1 [name2 ...]]
             [CONFIGS config1 [config2 ...]]
             [HINTS path1 [path2 ... ]]
             [PATHS path1 [path2 ... ]]
             [PATH_SUFFIXES suffix1 [suffix2 ...]]
             [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] # Deprecated; does nothing.
             [NO_CMAKE_SYSTEM_PATH]
             [NO_CMAKE_SYSTEM_PACKAGE_REGISTRY]
             [CMAKE_FIND_ROOT_PATH_BOTH |
              ONLY_CMAKE_FIND_ROOT_PATH |
              NO_CMAKE_FIND_ROOT_PATH])

通常来说, 第一部分中的简化版的命令接口用来查找包就可以满足大部分实际项目需求。



版本选择

当在Config模式下指定了[version]参数的时候, 只会寻找与给定版本需求相兼容的包。如果EXACT选项给出了,则表明只查找确定版本的包, 而不是兼容包。CMake没有建立版本号的协议,包的版本号校验需要利用包中的版本文件来进行校验。对于一个候选包的配置文件.cmake, 对应的版本文件存在与之同样的位置,名字通常为-version.cmake 或者Version.cmake。 如果没有这样的版本文件,则认为该包不与任何版本兼容。一个基本的版本文件可以使用CMakePackageConfigHelpers模块来进行生成。当一个版本文件被找到之后,则会被加载以用来进行版本校验。版本文件被加载进入一个内嵌的作用域内,其内定义了一些变量。

变量 描述
PACKAGE_FIND_NAME PackageName
PACKAGE_FIND_VERSION full requested version string
PACKAGE_FIND_VERSION_MAJOR major version if requested, else 0
PACKAGE_FIND_VERSION_MINOR minor version if requested, else 0
PACKAGE_FIND_VERSION_PATCH patch version if requested, else 0
PACKAGE_FIND_VERSION_TWEAK tweak version if requested, else 0
PACKAGE_FIND_VERSION_COUNT number of version components, 0 to 4

版本文件经过核验之后,如果满足了相应的版本要求则会设置以下几个变量。

变量 描述
PACKAGE_VERSION full provided version string
PACKAGE_VERSION_EXACT true if version is exact match
PACKAGE_VERSION_COMPATIBLE true if version is compatible
PACKAGE_VERSION_UNSUITABLE true if unsuitable as any version

以下几个变量会被find_package命令进行校验,并决定配置文件是否提供了一个可以接受的版本。如果包满足需求,则以下几个变量会被设置且配置文件会被加载。当多个包的配置文件是可用且未指明具体那个包被选择的时候:除非设置了变量CMAKE_FIND_PACKAGE_SORT_ORDER, 则会选择一个最高或者最相近的版本。

变量 描述
_VERSION full requested version string
_VERSION_MAJOR major version if requested, else 0
_VERSION_MINOR minor version if requested, else 0
_VERSION_PATCH patch version if requested, else 0
_VERSION_TWEAK tweak version if requested, else 0
_VERSION_COUNT number of version components, 0 to 4

为了控制find_package命令校验包的兼容性顺序,会用到两个变量:CMAKE_FIND_PACKAGE_SORT_ORDER 和 CMAKE_FIND_PACKAGE_SORT_DIRECTION。例如,如果需要选定最高版本的兼容包,可以在使用find_package命令之前这样设置:

SET(CMAKE_FIND_PACKAGE_SORT_ORDER  NATURAL)
SET(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)



查找过程

CMake构建了一组可能的包安装路径前缀。在每个前缀路径之下,含有多个目录可以用来搜索配置文件。下表列出了一些可以搜索的文件夹。每个条目都对应各自可能的系统环境(W:windows, U:UNIX; A:Apple)。

路径 系统
<prefix>/ (W)
<prefix>/(cmake|CMake)/ (W)
<prefix>/<name>*/ (W)
<prefix>/<name>*/(cmake|CMake)/ (W)
<prefix>/(lib/<arch>|lib*|share)/cmake/<name>*/ (U)
<prefix>/(lib/<arch>|lib*|share)/<name>*/ (U)
<prefix>/(lib/<arch>|lib*|share)/<name>*/(cmake|CMake)/ (U)
<prefix>/<name>*/(lib/<arch>|lib*|share)/cmake/<name>*/ (W/U)
<prefix>/<name>*/(lib/<arch>|lib*|share)/<name>*/ (W/U)
<prefix>/<name>*/(lib/<arch>|lib*|share)/<name>*/(cmake|CMake)/ (W/U)

在上述表中,是大小写敏感的并对应于任意的或者NAEMS所指定的名称。

如果CMAKE_LIBRARY_ARCHITECTURE变量被设置后, lib/这个路径可以被使用。lib* 包括了一个或者多个值,如lib64, lib32, libx32, 或者lib。其中:

  • lib64:如果FIND_LIBRARY_USE_LIB64_PATH属性被设置为TRUE时, 会在64位平台的该路径下搜索的库;
  • lib32:如果FIND_LIBRARY_USE_LIB32_PATH属性被设置为TRUE时, 会在32位平台的该路径下搜索的库;
  • libx32:如果FIND_LIBRARY_USE_LIBX32_PATHS属性被设置为TRUE时, 会在使用x32 ABI 的平台该路径下搜索库;
  • lib: 这个路径使用会被搜索。

如果PATH_SUFFIXES被设定了,这这些后缀会在上标中每个条目的后面进行一一追加。

这一组安装前缀是通过以下步骤进行构建起来的。如果NO_DEFAULT_PATH被指定,那么所有的NO_* 选项将会被激活。

  1. 搜索路径是被CMake变量或者系统环境变量_ROOT 所指定了包的查找路径。包的根目录变量被保存为堆栈,所以在一个查找模块中调用,那么其父查找模块中的路径也会被搜索。通过传递参数CMAKE_PACKAGE_ROOT_PACKAGE或者设置CMAKE_FIND_PACKAGE_ROOT_PATH为FALSE来跳过这个查找。
  2. 搜索路径在CMAKE缓存变量中被指定。这种设计可以在命令行中添加-DVAR=value来进行。可以被设置的缓存变量有:CMAKE_PREFIX_PATH、CMAKE_FRAMEWORK_PATH、CMAKE_APPBUNDLE_PATH。
  3. 搜索路径可以通过cmake指定的系统环境变量中进行设置。这种设计可以使得用户可以在shell文件中进行设置。设定的环境变量有:<PackageName>_DIR、CMAKE_PREFIX_PATH、CMAKE_FRAMEWORK_PATH、CMAKE_APPBUNDLE_PATH。
  4. 所有路径通过HINTS选项指定。此时可以通过其已经找到的库的路径来进行计算提示。如果采用硬编码的方式指定路径,可以参考使用PATHS选项来进行设置。
  5. 搜索标准的系统环境变量。如果传递了参数NO_SYSTEM_ENVIRONMENT_PATH或者将变量CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH设置为FALSE时,这步将会跳过不执行。含有/bin或者/sbin关键项目结尾的路径将会自动转换为其父目录。
  6. 搜索通过CMAKE User Package Registry 进行存储的路径。这一步可以通过已经方式进行跳过:传递参数NO_CMAKE_PACKAGE_REGISTRY; 设置变量CMAKE_FIND_USE_PACKAGE_REGISTRY为FALSE; 设置变量CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY为TRUE。
  7. 搜索当前系统平台文件中所定义的cmake变量:CMAKE_SYSTEM_PREFIX_PATH、CMAKE_SYSTEM_FRAMEWORK_PATH、CMAKE_SYSTEM_APPBUNDLE_PATH。这一步可以通过传递参数NO_CMAKE_SYSTEM_PATH或者设置变量CMAKE_FIND_USE_CMAKE_SYSTEM_PATH为FALSE。
  8. 搜索通过CMAKE System Package Registry 存储的路径。这一步可以通过已经方式进行跳过:传递参数NO_CMAKE_SYSTEM_PACKAGE_REGISTRY; 设置变量CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY为FALSE; 设置变量CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY为TRUE。
  9. 搜索通过PATH选项设置的路径。这一步主要用于硬编码。

CMake 变量CMAKE_FIND_ROOT_PATH指定了一个或者多个目录, 则会预置多个其他的搜索目录(这里应该是将这个变量作为上述表格中的prefix)。默认情况是这个变量值是空的。

变量CMAKE_SYSROOT也可以被用来指明一个目录作为prefix。 设置CMAKE_SYSROOT同时也会带来其他影响。

这些变量在跨平台编译时指定目标环境根目录的时候非常有用。默认情况下是先搜索CMAKE_FIND_ROOT_PATH,然后再搜索CMAKE_SYSROOT目录,最后是搜索非根目录。这种默认的行为可以通过设CMAKE_FIND_ROOT_PATH_MODE_PACKAGE来进行调整。通过手动设置下列选项可以进行调整:

变量 功能
CMAKE_FIND_ROOT_PATH_BOTH Search in the order described above
NO_CMAKE_FIND_ROOT_PATH Do not use the CMAKE_FIND_ROOT_PATH variable
ONLY_CMAKE_FIND_ROOT_PATH Search only the re-rooted directories and directories below CMAKE_STAGING_PREFIX

大多数情况下默认的搜索路径设计一般是从细到宽的设计。项目可以重写顺序通过多次调用带有NO_*的命令。

find_package (<PackageName> PATHS paths... NO_DEFAULT_PATH)
find_package (<PackageName>)

一旦某个调用成功了则会对相应的变量进行设置并存储在缓存中,之后的调用则不会再进行搜索。



参考


[1] CMake 官方文档



[2] find_package与CMake如何查找链接库详解



版权声明:本文为xholes原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。