find_package

注意

使用依赖项指南》提供了一个关于此通用主题的高层介绍。它更广泛地概述了 find_package() 命令在整体图景中的位置,包括它与 FetchContent 模块的关系。建议在深入研究以下细节之前先阅读本指南。

查找包(通常由项目外部的某个东西提供),并加载其特定于包的详细信息。对此命令的调用也可以被 依赖项提供程序 拦截。

典型用法

大多数 find_package() 的调用通常具有以下形式

find_package(<PackageName> [<version>] [REQUIRED] [COMPONENTS <components>...])

唯一的强制参数是 <PackageName><version> 经常被省略,如果项目没有此包就无法成功配置,则应指定 REQUIRED。一些更复杂的包支持组件,可以使用 COMPONENTS 关键字进行选择,但大多数包没有这么复杂的级别。

以上是 基本签名 的简化形式。如果可能,项目应使用此形式查找包。这可以减少复杂性,并最大化查找或提供包的方式。

理解 基本签名 应该足以用于 find_package() 的一般用法。打算提供包配置文件(package configuration file)的项目维护者应理解更全面的情况,如 完整签名 和本页面后续所有部分所述。

搜索模式

该命令有几种搜索包的模式

模块模式

在此模式下,CMake 搜索名为 Find<PackageName>.cmake 的文件,首先在 CMAKE_MODULE_PATH 中列出的位置进行搜索,然后搜索 CMake 安装提供的 查找模块。如果找到文件,CMake 将读取并处理它。它负责查找包、检查版本并生成任何必要的通知。某些查找模块对版本支持有限或不支持;请检查查找模块的文档。

通常情况下,包本身不会提供 Find<PackageName>.cmake 文件。相反,它通常由包外部的某个东西提供,例如操作系统、CMake 本身,甚至是调用 find_package() 命令的项目。由于是外部提供的,查找模块 通常是启发式的,并且容易过时。它们通常搜索特定的库、文件和其他包的产物。

模块模式仅由 基本命令签名 支持。

Config 模式

在此模式下,CMake 搜索名为 <lowercasePackageName>-config.cmake<PackageName>Config.cmake 的文件。如果指定了版本详细信息(请参见 Config 模式版本选择 以了解这些单独的版本文件如何使用),它还会查找 <lowercasePackageName>-config-version.cmake<PackageName>ConfigVersion.cmake

注意

如果启用了实验性的 CMAKE_EXPERIMENTAL_FIND_CPS_PACKAGES,还会考虑名为 <PackageName>.cps<lowercasePackageName>.cps 的文件。这些文件根据 通用包规范 (CPS) 提供包信息,这比 CMake 脚本更具可移植性。除了任何明确指出的例外情况,任何对“配置文件”、“Config 模式”、“包配置文件”等的引用都同样适用于 CPS 和 CMake 脚本文件。此功能尚在开发中,可能缺少某些功能。

搜索的实现方式倾向于在大多数情况下优先考虑通用包规范文件而不是 CMake 脚本配置文件。指定 CONFIGS 会抑制对 CPS 文件的考虑。

在 Config 模式下,可以为命令提供一组名称作为包名称进行搜索。CMake 搜索 Config 和版本文件的位置比模块模式复杂得多(请参见 Config 模式搜索过程)。

Config 和版本文件通常作为包的一部分安装,因此它们比查找模块更可靠。它们通常包含对包内容的直接了解,因此在 Config 或版本文件本身不需要搜索或启发式方法。

Config 模式支持 基本完整 命令签名。

FetchContent 重定向模式

版本 3.24 中新增: find_package() 的调用可以在内部重定向到由 FetchContent 模块提供的包。对于调用者来说,行为将类似于 Config 模式,但搜索逻辑会被绕过,组件信息也不会被使用。有关详细信息,请参阅 FetchContent_Declare()FetchContent_MakeAvailable()

当不重定向到由 FetchContent 提供的包时,命令参数决定使用模块模式还是 Config 模式。当使用 基本签名 时,命令首先以模块模式搜索。如果找不到包,则回退到 Config 模式。用户可以设置 CMAKE_FIND_PACKAGE_PREFER_CONFIG 变量为 true,以反转优先级,并指示 CMake 先使用 Config 模式搜索,然后再回退到模块模式。基本签名也可以使用 MODULE 关键字强制仅使用模块模式。如果使用 完整签名,则命令仅以 Config 模式搜索。

基本签名

find_package(<PackageName> [version] [EXACT] [QUIET] [MODULE]
             [REQUIRED|OPTIONAL] [[COMPONENTS] [components...]]
             [OPTIONAL_COMPONENTS components...]
             [REGISTRY_VIEW  (64|32|64_32|32_64|HOST|TARGET|BOTH)]
             [GLOBAL]
             [NO_POLICY_SCOPE]
             [BYPASS_PROVIDER]
             [UNWIND_INCLUDE])

基本签名同时支持模块模式和 Config 模式。MODULE 关键字表示只能使用模块模式查找包,没有回退到 Config 模式。

无论使用哪种模式,都会设置一个 <PackageName>_FOUND 变量来指示是否找到包。当找到包时,可以通过包本身文档化的其他变量和 导入目标 提供特定于包的信息。QUIET 选项禁用信息性消息,包括未找到包但又不 REQUIRED 的消息。REQUIRED 选项在找不到包时会停止处理并显示错误消息。

COMPONENTS 关键字之后,可以列出一个特定于包的必需组件列表。如果这些组件中的任何一个无法满足,则整个包将被视为未找到。如果同时存在 REQUIRED 选项,这将视为致命错误,否则执行仍将继续。作为一种简写方式,如果存在 REQUIRED 选项,则可以省略 COMPONENTS 关键字,并将必需的组件直接列在 REQUIRED 之后。

可以使用 CMAKE_FIND_REQUIRED 变量来使此调用默认 REQUIRED。可以通过提供 OPTIONAL 关键字来覆盖此行为。与 REQUIRED 选项一样,可以在 OPTIONAL 之后直接列出组件列表,这等同于将其列在 COMPONENTS 关键字之后。当给出 OPTIONAL 关键字时,当找不到包时出现的警告输出将被抑制。

其他可选组件可以在 OPTIONAL_COMPONENTS 之后列出。如果无法满足这些组件,只要所有必需的组件都已满足,整个包仍然可以被视为已找到。

可用组件的集合及其含义由目标包定义。

  • 对于 CMake 脚本包配置文件,目标包如何解释提供给它的组件信息是形式上的,但它应该遵循上述期望。对于没有指定组件的调用,没有单一的预期行为,目标包应清楚地定义在这种情况下会发生什么。常见的安排包括假定它应该找到所有组件、不查找组件或查找一些明确定义的可用组件子集。

  • 通用包规范包由一个根配置文件和零个或多个附录组成,每个附录提供组件并可能具有依赖项。CMake 始终尝试加载根配置文件。只有当附录的依赖项能够满足,并且它们提供请求的组件,或者在没有请求任何组件时,才会加载附录。如果提供必需组件的附录的依赖项无法满足,则该包被视为未找到。否则,该附录将被忽略。

版本 3.24 中新增: REGISTRY_VIEW 关键字指定应查询哪些注册表视图。此关键字仅在 Windows 平台上有意义,在所有其他平台上将被忽略。在形式上,目标包如何解释提供给它的注册表视图信息是其自身的责任。

版本 3.24 中新增: 指定 GLOBAL 关键字会将所有导入的目标提升到导入项目中全局范围。或者,可以通过设置 CMAKE_FIND_PACKAGE_TARGETS_GLOBAL 变量来实现此功能。

有关 [version] 参数,它请求一个包找到的版本应与之兼容。它可能有两种指定形式

  • 单个版本,格式为 major[.minor[.patch[.tweak]]],其中每个组件都是一个数字值。

  • 版本范围,格式为 versionMin...[<]versionMax,其中 versionMinversionMax 具有与单个版本相同的格式和组件为整数的约束。默认情况下,两个端点都包含在内。通过指定 <,上限端点将被排除。版本范围仅在 CMake 3.19 或更高版本中支持。

注意

除了 CPS 包之外,版本支持目前仅按包提供。当指定版本范围但包仅设计为期望单个版本时,包将忽略范围的上限,并且仅考虑范围下限处的单个版本。支持版本范围的非 CPS 包以由各个包确定的方式支持。有关详细信息和重要注意事项,请参阅下面的 版本选择 部分。

EXACT 选项要求精确匹配版本。此选项与版本范围的指定不兼容。

如果在查找模块的递归调用中没有给出 [version] 和/或组件列表,则相应参数将自动从外部调用转发(包括 [version]EXACT 标志)。

有关 NO_POLICY_SCOPE 选项的讨论,请参阅 cmake_policy() 命令文档。

版本 3.24 中新增: BYPASS_PROVIDER 关键字仅允许在 find_package()依赖项提供程序 调用时使用。提供程序可以使用它直接调用内置的 find_package() 实现,并防止该调用被重新路由回自身。CMake 的未来版本可能会检测到尝试从依赖项提供程序以外的地方使用此关键字,并以致命错误停止。

版本 4.2 中新增: UNWIND_INCLUDE 关键字仅允许在 find_package() 在对 find_package() 的父调用中调用时使用。当调用 find_package(UNWIND_INCLUDE) 无法找到所需的包时,它将开始“解开”(unwind)状态。在此状态下,进一步调用 find_package()include() 是被禁止的,并且所有父 include() 命令在其作用域到达时将立即调用 return()。这种“解开”将一直持续到返回到父 find_package()

UNWIND_INCLUDE 仅供 install(EXPORT_PACKAGE_DEPENDENCIES) 生成的 find_package() 调用使用,但可能对那些希望以类似方式手动管理其依赖项的人有用。

完整签名

find_package(<PackageName> [version] [EXACT] [QUIET]
             [REQUIRED|OPTIONAL] [[COMPONENTS] [components...]]
             [OPTIONAL_COMPONENTS components...]
             [CONFIG|NO_MODULE]
             [GLOBAL]
             [NO_POLICY_SCOPE]
             [BYPASS_PROVIDER]
             [NAMES name1 [name2 ...]]
             [CONFIGS config1 [config2 ...]]
             [HINTS path1 [path2 ...]]
             [PATHS path1 [path2 ...]]
             [REGISTRY_VIEW  (64|32|64_32|32_64|HOST|TARGET|BOTH)]
             [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_INSTALL_PREFIX]
             [NO_CMAKE_SYSTEM_PACKAGE_REGISTRY]
             [CMAKE_FIND_ROOT_PATH_BOTH |
              ONLY_CMAKE_FIND_ROOT_PATH |
              NO_CMAKE_FIND_ROOT_PATH])

CONFIG 选项、同义的 NO_MODULE 选项,或使用 基本签名 中未指定的选项,都会强制纯 Config 模式。在纯 Config 模式下,命令会跳过模块模式搜索,并立即进行 Config 模式搜索。

Config 模式搜索尝试定位要查找的包提供的配置文件。将创建一个名为 <PackageName>_DIR 的缓存条目,用于存储包含该文件的目录。默认情况下,命令搜索名为 <PackageName> 的包。如果给出了 NAMES 选项,则使用后面的名称而不是 <PackageName>。在确定是否将调用重定向到 FetchContent 提供的包时,也会考虑这些名称。

该命令搜索名为 <PackageName>Config.cmake<lowercasePackageName>-config.cmake 的文件,针对指定的每个名称。可以使用 CONFIGS 选项提供一组替代的可能配置文件名称。Config 模式搜索过程 如下所述。找到后,将检查任何 版本约束,如果满足,CMake 将读取并处理配置文件。由于该文件由包提供,它已经知道包内容的目录。配置文件的完整路径存储在 CMake 变量 <PackageName>_CONFIG 中。

注意

如果启用了实验性的 CMAKE_EXPERIMENTAL_FIND_CPS_PACKAGES,还会考虑名为 <PackageName>.cps<lowercasePackageName>.cps 的文件,除非给出了 CONFIGS

所有 CMake 在搜索具有适当版本的包时考虑过的配置文件都存储在 <PackageName>_CONSIDERED_CONFIGS 变量中,相关的版本存储在 <PackageName>_CONSIDERED_VERSIONS 变量中。

如果找不到包配置文件,CMake 将生成一个描述问题的错误,除非指定了 QUIET 参数。如果指定了 REQUIRED 且找不到包,则会生成一个致命错误,配置步骤将停止执行。如果 <PackageName>_DIR 已设置为不包含配置文件的目录,或者请求的版本与在该目录中找到的包不兼容(请参阅 Config 模式版本选择),CMake 将忽略它并从头开始搜索。

鼓励提供包配置文件的包维护者以这样的方式命名和安装它们,以便下面概述的 Config 模式搜索过程 能够找到它们,而无需使用额外的选项。

Config 模式搜索过程

注意

当使用 Config 模式时,无论给出的是 完整 签名还是 基本 签名,都会应用此搜索过程。

版本 3.24 中新增: 所有 find_package() 的调用(即使是模块模式)都会首先在 CMAKE_FIND_PACKAGE_REDIRECTS_DIR 目录中查找 Config 包文件。FetchContent 模块,甚至项目本身,都可以向该位置写入文件,以将 find_package() 调用重定向到项目已提供的的内容。如果在该位置未找到 Config 包文件,搜索将继续执行下面的逻辑。

CMake 构建一组可能的包安装前缀。在每个前缀下,会搜索几个目录以查找配置文件。下表显示了搜索的目录。每个条目都旨在遵循 Windows (W)、UNIX (U) 或 Apple (A) 约定的安装树。

条目

约定

<prefix>/<name>/cps/ [2]

W

<prefix>/<name>/*/cps/ [2]

W

<prefix>/cps/<name>/ [2]

W

<prefix>/cps/<name>/*/ [2]

W

<prefix>/cps/ [2]

W

<prefix>/

W

<prefix>/(cmake|CMake)/

W

<prefix>/<name>*/

W

<prefix>/<name>*/(cmake|CMake)/

W

<prefix>/<name>*/(cmake|CMake)/<name>*/ [1]

W

<prefix>/(lib/<arch>|lib*|share)/cps/<name>/ [2]

U

<prefix>/(lib/<arch>|lib*|share)/cps/<name>/*/ [2]

U

<prefix>/(lib/<arch>|lib*|share)/cps/ [2]

U

<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

在支持 macOS FRAMEWORKBUNDLE 的系统上,会搜索以下目录以查找包含配置文件的 Framework 或应用程序束。

条目

约定

<prefix>/<name>.framework/Versions/*/Resources/CPS/ [3]

A

<prefix>/<name>.framework/Resources/CPS/ [3]

A

<prefix>/<name>.framework/Resources/

A

<prefix>/<name>.framework/Resources/CMake/

A

<prefix>/<name>.framework/Versions/*/Resources/

A

<prefix>/<name>.framework/Versions/*/Resources/CMake/

A

<prefix>/<name>.app/Contents/Resources/CPS/ [3]

A

<prefix>/<name>.app/Contents/Resources/

A

<prefix>/<name>.app/Contents/Resources/CMake/

A

在搜索上述路径时,find_package 只会在包含 /cps/ 的搜索路径中查找 .cps 文件,而在其他情况下只查找 .cmake 文件。(这仅适用于指定的路径,不考虑 <prefix><name> 的内容。)

在所有情况下,<name> 都被视为大小写不敏感,并对应于指定的任何名称(<PackageName>NAMES 提供的名称)。

如果启用了至少一种编译语言,则可能会根据编译器的目标架构搜索特定于架构的 lib/<arch>lib* 目录,顺序如下:

lib/<arch>

如果设置了 CMAKE_LIBRARY_ARCHITECTURE 变量,则搜索此目录。

lib64

在 64 位平台上搜索(CMAKE_SIZEOF_VOID_P 为 8),并且 FIND_LIBRARY_USE_LIB64_PATHS 属性设置为 TRUE

lib32

在 32 位平台上搜索(CMAKE_SIZEOF_VOID_P 为 4),并且 FIND_LIBRARY_USE_LIB32_PATHS 属性设置为 TRUE

libx32

在使用 x32 ABI 的平台上搜索,如果 FIND_LIBRARY_USE_LIBX32_PATHS 属性设置为 TRUE

lib

始终搜索。

版本 3.24 中更改: Windows 平台上,可以通过 HINTSPATHS 关键字包含注册表查询作为指定目录的一部分,使用 专用语法。这些指定在所有其他平台上都将被忽略。

版本 3.24 中新增: 可以指定 REGISTRY_VIEW 来管理作为 PATHSHINTS 指定的 Windows 注册表查询。

指定必须查询的注册表视图。此选项仅在 Windows 平台上才有意义,并在其他平台上被忽略。未指定时,当 CMP0134 策略为 NEW 时使用 TARGET 视图。有关策略为 OLD 时的默认视图,请参阅 CMP0134

64

查询64位注册表。在32位Windows上,它始终返回字符串 /REGISTRY-NOTFOUND

32

查询32位注册表。

64_32

查询两个视图(6432)并为每个视图生成一个路径。

32_64

查询两个视图(3264)并为每个视图生成一个路径。

HOST

查询与主机架构匹配的注册表:64 位 Windows 上为 64,32 位 Windows 上为 32

TARGET

查询与 CMAKE_SIZEOF_VOID_P 变量指定的架构匹配的注册表。如果未定义,则回退到 HOST 视图。

BOTH

查询两个视图(3264)。顺序取决于以下规则:如果 CMAKE_SIZEOF_VOID_P 变量已定义,则根据此变量的内容使用以下视图:

  • 8: 64_32

  • 4: 32_64

如果 CMAKE_SIZEOF_VOID_P 变量未定义,则依赖于主机架构。

  • 64位: 64_32

  • 32位: 32

如果指定了 PATH_SUFFIXES,则后缀将逐个附加到每个(W)或(U)目录条目。

这组目录旨在与在安装树中提供配置文件的项目协同工作。上面标有(W)的目录适用于 Windows 上的安装,其中前缀可能指向应用程序安装目录的顶部。标有(U)的目录适用于 UNIX 平台上的安装,其中前缀由多个包共享。这仅仅是一个约定,因此所有(W)和(U)目录仍会在所有平台上进行搜索。标有(A)的目录适用于 Apple 平台上的安装。CMAKE_FIND_FRAMEWORKCMAKE_FIND_APPBUNDLE 变量决定了搜索顺序。

警告

CMAKE_FIND_FRAMEWORKCMAKE_FIND_APPBUNDLE 设置为 FIRST(默认值)以外的值,将导致 CMake 以不同于规范中设定的顺序搜索通用包规范文件。

安装前缀集是通过以下步骤构建的。如果指定了 NO_DEFAULT_PATH,则启用所有 NO_* 选项。

  1. 搜索当前正在查找的 <PackageName> 独有的前缀。请参阅策略 CMP0074

    3.12 版本新增。

    具体来说,按顺序搜索以下变量指定的查找前缀:

    1. <PackageName>_ROOT CMake 变量,其中 <PackageName> 是保留大小写的包名。

    2. CMake 变量 <PACKAGENAME>_ROOT,其中 <PACKAGENAME> 是大写的包名称。请参阅策略 CMP0144

      在 3.27 版本中新增。

    3. <PackageName>_ROOT 环境变量,其中 <PackageName> 是保留大小写的包名。

    4. 环境变量 <PACKAGENAME>_ROOT,其中 <PACKAGENAME> 是大写的包名称。请参阅策略 CMP0144

      在 3.27 版本中新增。

    包根变量被维护为一个堆栈,因此如果从查找模块内部调用,在当前包的路径之后也会搜索父查找模块的根路径。如果传递了 NO_PACKAGE_ROOT_PATH 或将 CMAKE_FIND_USE_PACKAGE_ROOT_PATH 设置为 FALSE,则可以跳过此步骤。

  2. 在 CMake 特定缓存变量中指定的查找路径。这些变量旨在通过命令行使用 -DVAR=VALUE 进行设置。这些值被解释为 分号分隔的列表。如果传递了 NO_CMAKE_PATH 或将 CMAKE_FIND_USE_CMAKE_PATH 设置为 FALSE,则可以跳过此步骤。

  3. 在 CMake 特定环境变量中指定的查找路径。这些变量旨在在用户 shell 配置中设置,因此使用主机的本机路径分隔符(Windows 上为 ;,UNIX 上为 :)。如果传递了 NO_CMAKE_ENVIRONMENT_PATH 或将 CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH 设置为 FALSE,则可以跳过此步骤。

  4. HINTS 选项中指定的查找路径。这些通常是系统内省计算出的路径,例如另一个已找到项的位置提供的提示。硬编码的猜测应使用 PATHS 选项指定。

  5. 搜索标准的系统环境变量。如果传递了 NO_SYSTEM_ENVIRONMENT_PATH 或将 CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH 设置为 FALSE,则可以跳过此步骤。以 /bin/sbin 结尾的路径条目会自动转换为它们的父目录。

    • PATH

  6. 在 CMake 用户包注册表 中存储的查找路径。如果传递了 NO_CMAKE_PACKAGE_REGISTRY 或将变量 CMAKE_FIND_USE_PACKAGE_REGISTRY 设置为 FALSE 或已弃用的变量 CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY 设置为 TRUE,则可以跳过此步骤。

    有关用户包注册表的详细信息,请参阅 cmake-packages(7) 手册。

  7. 在当前系统的平台文件中定义的 CMake 变量中指定的查找路径。可以跳过对 CMAKE_INSTALL_PREFIXCMAKE_STAGING_PREFIX 的搜索,如果传递了 NO_CMAKE_INSTALL_PREFIX 或将 CMAKE_FIND_USE_INSTALL_PREFIX 设置为 FALSE。如果传递了 NO_CMAKE_SYSTEM_PATH 或将 CMAKE_FIND_USE_CMAKE_SYSTEM_PATH 设置为 FALSE,则可以跳过所有这些位置。

    这些变量包含的平台路径通常包括已安装的软件位置。例如,UNIX 平台上的 /usr/local

  8. 在 CMake 系统包注册表 中存储的查找路径。如果传递了 NO_CMAKE_SYSTEM_PACKAGE_REGISTRY 或将 CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY 变量设置为 FALSE 或已弃用的变量 CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY 设置为 TRUE,则可以跳过此步骤。

    有关系统包注册表的详细信息,请参阅 cmake-packages(7) 手册。

  9. PATHS 选项中指定的查找路径。这些通常是硬编码的猜测。

CMAKE_IGNORE_PATHCMAKE_IGNORE_PREFIX_PATHCMAKE_SYSTEM_IGNORE_PATHCMAKE_SYSTEM_IGNORE_PREFIX_PATH 变量也可以导致上面的一些位置被忽略。

路径按照上述顺序搜索。找到的第一个可行的包配置文件将被使用,即使该包的较新版本位于搜索路径列表的后面。

对于包含 glob 表达式(*)的搜索路径,默认情况下,匹配 glob 的目录按自然顺序倒序搜索。此行为可以通过设置变量 CMAKE_FIND_PACKAGE_SORT_ORDERCMAKE_FIND_PACKAGE_SORT_DIRECTION 来覆盖。这些变量决定了 CMake 考虑 glob 匹配的顺序。例如,如果文件系统中存在包配置文件

<prefix>/example-1.2/example-config.cmake
<prefix>/example-1.10/example-config.cmake
<prefix>/share/example-2.0/example-config.cmake

那么 find_package(example) 将(当上述变量未设置时)选择 example-1.10(假设 example-1.2example-1.10 都可行)。但请注意,find_package **不会** 找到 example-2.0,因为其他两个之一会先被找到。

要控制 find_package 搜索匹配 glob 表达式的目录的顺序,请使用 CMAKE_FIND_PACKAGE_SORT_ORDERCMAKE_FIND_PACKAGE_SORT_DIRECTION。例如,要使上述示例选择 example-1.2,可以在调用 find_package 之前设置

set(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL)
set(CMAKE_FIND_PACKAGE_SORT_DIRECTION ASC)

版本 3.16 中新增: 添加了 CMAKE_FIND_USE_<CATEGORY> 变量以全局禁用各种搜索位置。

版本 4.0 中更改: 变量 CMAKE_FIND_PACKAGE_SORT_ORDERCMAKE_FIND_PACKAGE_SORT_DIRECTION 现在也控制 find_package 搜索匹配搜索路径 <prefix>/<name>.framework/Versions/*/Resources/<prefix>/<name>.framework/Versions/*/Resources/CMake 中 glob 表达式的目录的顺序。在以前的 CMake 版本中,此顺序是不确定的。

版本 4.2 中更改: 遇到多个可行的匹配项时,find_package 现在默认选择版本最新的那个。在以前的 CMake 版本中,结果是不确定的。因此,CMAKE_FIND_PACKAGE_SORT_ORDER 的默认值已从 NONE 更改为 NATURAL,而 CMAKE_FIND_PACKAGE_SORT_DIRECTION 现在默认为 DEC(降序),而不是 ASC(升序)。

CMake 变量 CMAKE_FIND_ROOT_PATH 指定一个或多个目录,这些目录将被添加到所有其他搜索目录的前面。这有效地将整个搜索“重新根化”到给定的位置。位于 CMAKE_STAGING_PREFIX 下面的路径将被排除在此重新根化之外,因为该变量始终是主机系统上的一个路径。默认情况下,CMAKE_FIND_ROOT_PATH 为空。

变量 CMAKE_SYSROOT 也可用于指定仅一个目录作为前缀。设置 CMAKE_SYSROOT 也有其他影响。有关更多信息,请参阅该变量的文档。

这些变量在交叉编译时特别有用,可以将它们指向目标环境的根目录,CMake 也会在那里进行搜索。默认情况下,首先搜索 CMAKE_FIND_ROOT_PATH 中列出的目录,然后搜索 CMAKE_SYSROOT 目录,然后搜索非根目录。可以通过设置 CMAKE_FIND_ROOT_PATH_MODE_PACKAGE 来调整默认行为。可以使用选项在每次调用时手动覆盖此行为:

CMAKE_FIND_ROOT_PATH_BOTH

按上述顺序搜索。

NO_CMAKE_FIND_ROOT_PATH

不要使用 CMAKE_FIND_ROOT_PATH 变量。

ONLY_CMAKE_FIND_ROOT_PATH

仅搜索重新根化的目录和 CMAKE_STAGING_PREFIX 下的目录。

默认搜索顺序旨在为常见用例实现从最具体到最不具体的排序。项目可以通过多次调用命令并使用 NO_* 选项来覆盖此顺序。

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

一旦其中一个调用成功,结果变量将被设置并存储在缓存中,这样就不会再次搜索。

默认情况下,结果变量中存储的值将是找到文件的路径。可以通过将 CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS 变量设置为 TRUE 来解析符号链接并存储文件的真实路径,然后再调用 find_package

每个非 REQUIREDfind_package 调用都可以被禁用或设置为 REQUIRED

同时将这两个变量都设置为 TRUE 是一个错误。

CMAKE_REQUIRE_FIND_PACKAGE_<PackageName> 变量在确定包是否必需时优先于 OPTIONAL 关键字。

配置模式版本选择

注意

当使用配置模式时,无论提供的是完整签名还是基本签名,都会应用此版本选择过程。

当给出 [version] 参数时,配置模式只会查找声称与请求版本兼容的包版本(请参见格式规范)。如果给出 EXACT 选项,则只能找到声称与请求版本完全匹配的包版本。CMake 不会为版本号的含义建立任何约定。

CMake 脚本

对于 CMake 脚本包配置文件,包版本号由包本身提供的“version”文件或 FetchContent 进行检查。对于候选包配置文件 <config-file>.cmake,相应的版本文件位于其旁边,命名为 <config-file>-version.cmake<config-file>Version.cmake。如果不存在这样的版本文件,则假定配置文件与任何请求的版本都不兼容。可以使用 CMakePackageConfigHelpers 模块创建一个包含通用版本匹配代码的基本版本文件。找到版本文件后,会加载它以检查请求的版本号。版本文件在以下变量已定义的嵌套作用域中加载

PACKAGE_FIND_NAME

<PackageName>

PACKAGE_FIND_VERSION

完整的请求版本字符串

PACKAGE_FIND_VERSION_MAJOR

如果请求了主版本号,则为该值,否则为 0

PACKAGE_FIND_VERSION_MINOR

如果请求了次版本号,则为该值,否则为 0

PACKAGE_FIND_VERSION_PATCH

如果请求了补丁版本号,则为该值,否则为 0

PACKAGE_FIND_VERSION_TWEAK

如果请求了微调版本号,则为该值,否则为 0

PACKAGE_FIND_VERSION_COUNT

版本组件的数量,0 到 4

当指定版本范围时,上述版本变量将持有基于版本范围下限的值。这是为了保持与未实现期望版本范围的包的兼容性。此外,版本范围将通过以下变量进行描述

PACKAGE_FIND_VERSION_RANGE

完整的请求版本范围字符串

PACKAGE_FIND_VERSION_RANGE_MIN

这指定版本范围的下限点是包含还是排除。目前,此变量的唯一支持值是 INCLUDE

PACKAGE_FIND_VERSION_RANGE_MAX

这指定版本范围的上限点是包含还是排除。此变量的受支持值是 INCLUDEEXCLUDE

PACKAGE_FIND_VERSION_MIN

范围下限点的完整请求版本字符串

PACKAGE_FIND_VERSION_MIN_MAJOR

如果请求了范围下限点的主版本号,则为该值,否则为 0

PACKAGE_FIND_VERSION_MIN_MINOR

如果请求了范围下限点的次版本号,则为该值,否则为 0

PACKAGE_FIND_VERSION_MIN_PATCH

如果请求了范围下限点的补丁版本号,则为该值,否则为 0

PACKAGE_FIND_VERSION_MIN_TWEAK

如果请求了范围下限点的微调版本号,则为该值,否则为 0

PACKAGE_FIND_VERSION_MIN_COUNT

范围下限点的版本组件数量,0 到 4

PACKAGE_FIND_VERSION_MAX

范围上限点的完整请求版本字符串

PACKAGE_FIND_VERSION_MAX_MAJOR

如果请求了范围上限点的主版本号,则为该值,否则为 0

PACKAGE_FIND_VERSION_MAX_MINOR

如果请求了范围上限点的次版本号,则为该值,否则为 0

PACKAGE_FIND_VERSION_MAX_PATCH

如果请求了范围上限点的补丁版本号,则为该值,否则为 0

PACKAGE_FIND_VERSION_MAX_TWEAK

如果请求了范围上限点的微调版本号,则为该值,否则为 0

PACKAGE_FIND_VERSION_MAX_COUNT

范围上限点的版本组件数量,0 到 4

无论指定的是单个版本还是版本范围,变量 PACKAGE_FIND_VERSION_COMPLETE 都将被定义,并包含指定的完整请求版本字符串。

版本文件检查它是否满足请求的版本并设置这些变量

PACKAGE_VERSION

提供的完整版本字符串

PACKAGE_VERSION_EXACT

如果版本是精确匹配,则为 True

PACKAGE_VERSION_COMPATIBLE

如果版本是兼容的,则为 True

PACKAGE_VERSION_UNSUITABLE

如果作为任何版本都不合适,则为 True

这些变量由 find_package 命令检查,以确定配置文件是否提供了可接受的版本。它们在 find_package 调用返回后不可用。如果版本可接受,则设置以下变量

<PackageName>_VERSION

提供的完整版本字符串

<PackageName>_VERSION_MAJOR

如果提供了主版本号,则为该值,否则为 0

<PackageName>_VERSION_MINOR

如果提供了次版本号,则为该值,否则为 0

<PackageName>_VERSION_PATCH

如果提供了补丁版本号,则为该值,否则为 0

<PackageName>_VERSION_TWEAK

如果提供了微调版本号,则为该值,否则为 0

<PackageName>_VERSION_COUNT

版本组件的数量,0 到 4

并加载相应的包配置文件。

注意

虽然版本匹配的确切行为由各个包决定,但许多包使用 write_basic_package_version_file() 来提供此逻辑。它们生成的版本检查脚本在版本范围方面有一些值得注意的注意事项

  • 版本范围的上限是接受版本的一个硬限制。因此,虽然请求版本 1.4.0 可能会被版本为 1.6.0 且声明“相同主版本号”兼容性的包满足,但如果请求的版本范围是 1.4.0...1.5.0,则该包将被拒绝。

  • 版本范围的两端都必须匹配包声明的兼容性级别。例如,如果一个包声明“相同主版本号和次版本号”兼容性,请求版本范围 1.4.0...<1.5.51.4.0...1.5.0 将导致该包被拒绝,即使包版本是 1.4.1

因此,无法使用版本范围来扩展可接受的兼容包版本的范围。

通用包规范 (CPS)

对于通用包规范 (CPS) 包配置文件,包版本号由 CMake 根据一组已识别的版本模式进行检查。目前,识别以下模式

simple

版本号是整数元组,后跟一个可选的尾部段,该段在版本比较中被忽略。

自定义

解释版本号的机制是未指定的。版本字符串必须完全匹配才能接受该包。

请参阅 version_schema 以更详细地解释每种模式及其比较方式。请注意,该规范可能包含 CMake 不支持的模式。

除了包的 version 之外,CPS 还允许包选择性地指定 compat_version,这是包提供兼容性的最旧版本。也就是说,包保证期望 compat_version 的消费者应该能够使用该包,即使包的实际版本更新。如果未指定 compat_version,则它隐式等于包的版本,即不提供向后兼容性。

当包使用已识别的模式时,CMake 将根据以下规则确定包的可接受性

  • 如果指定了 EXACT,或者包未提供 compat_version,则包的 version 必须等于请求的版本。

  • 否则

    • 包的 version 必须大于或等于请求的(最小)版本,并且

    • 包的 compat_version 必须小于或等于请求的(最小)版本,并且

    • 如果给出了请求的最大版本,则它必须大于(或等于,取决于最大版本是指定为包含还是排除)包的 version

注意

此范围匹配实现的选择是为了最接近地匹配 write_basic_package_version_file() 的行为,尽管没有出现范围过宽而什么都匹配不到的情况。

对于使用 simple 版本模式的包,如果版本可接受,则设置以下变量

<PackageName>_VERSION

提供的完整版本字符串

<PackageName>_VERSION_MAJOR

如果提供了主版本号,则为该值,否则为 0

<PackageName>_VERSION_MINOR

如果提供了次版本号,则为该值,否则为 0

<PackageName>_VERSION_PATCH

如果提供了补丁版本号,则为该值,否则为 0

<PackageName>_VERSION_TWEAK

如果提供了微调版本号,则为该值,否则为 0

<PackageName>_VERSION_COUNT

版本组件的数量,非负

包文件接口变量

在加载查找模块或 CMake 脚本包配置文件时,find_package 会定义变量来提供有关调用参数的信息(并在返回前恢复其原始状态)

CMAKE_FIND_PACKAGE_NAME

正在搜索的 <PackageName>

<PackageName>_FIND_REQUIRED

如果给出了 REQUIRED 选项,则为 True

<PackageName>_FIND_QUIETLY

如果给出了 QUIET 选项,则为 True

<PackageName>_FIND_REGISTRY_VIEW

如果给出了 REGISTRY_VIEW 选项,则为请求的视图

<PackageName>_FIND_VERSION

完整的请求版本字符串

<PackageName>_FIND_VERSION_MAJOR

如果请求了主版本号,则为该值,否则为 0

<PackageName>_FIND_VERSION_MINOR

如果请求了次版本号,则为该值,否则为 0

<PackageName>_FIND_VERSION_PATCH

如果请求了补丁版本号,则为该值,否则为 0

<PackageName>_FIND_VERSION_TWEAK

如果请求了微调版本号,则为该值,否则为 0

<PackageName>_FIND_VERSION_COUNT

版本组件的数量,0 到 4

<PackageName>_FIND_VERSION_EXACT

如果给出了 EXACT 选项,则为 True

<PackageName>_FIND_COMPONENTS

指定的组件列表(必需和可选)

<PackageName>_FIND_REQUIRED_<c>

如果组件 <c> 是必需的,则为 True,如果组件 <c> 是可选的,则为 False

当指定版本范围时,上述版本变量将持有基于版本范围下限的值。这是为了保持与未实现期望版本范围的包的兼容性。此外,版本范围将通过以下变量进行描述

<PackageName>_FIND_VERSION_RANGE

完整的请求版本范围字符串

<PackageName>_FIND_VERSION_RANGE_MIN

这指定版本范围的下限点是包含还是排除。目前,INCLUDE 是唯一支持的值。

<PackageName>_FIND_VERSION_RANGE_MAX

这指定版本范围的上限点是包含还是排除。此变量的可能值为 INCLUDEEXCLUDE

<PackageName>_FIND_VERSION_MIN

范围下限点的完整请求版本字符串

<PackageName>_FIND_VERSION_MIN_MAJOR

如果请求了范围下限点的主版本号,则为该值,否则为 0

<PackageName>_FIND_VERSION_MIN_MINOR

如果请求了范围下限点的次版本号,则为该值,否则为 0

<PackageName>_FIND_VERSION_MIN_PATCH

如果请求了范围下限点的补丁版本号,则为该值,否则为 0

<PackageName>_FIND_VERSION_MIN_TWEAK

如果请求了范围下限点的微调版本号,则为该值,否则为 0

<PackageName>_FIND_VERSION_MIN_COUNT

范围下限点的版本组件数量,0 到 4

<PackageName>_FIND_VERSION_MAX

范围上限点的完整请求版本字符串

<PackageName>_FIND_VERSION_MAX_MAJOR

如果请求了范围上限点的主版本号,则为该值,否则为 0

<PackageName>_FIND_VERSION_MAX_MINOR

如果请求了范围上限点的次版本号,则为该值,否则为 0

<PackageName>_FIND_VERSION_MAX_PATCH

如果请求了范围上限点的补丁版本号,则为该值,否则为 0

<PackageName>_FIND_VERSION_MAX_TWEAK

如果请求了范围上限点的微调版本号,则为该值,否则为 0

<PackageName>_FIND_VERSION_MAX_COUNT

范围上限点的版本组件数量,0 到 4

无论指定的是单个版本还是版本范围,变量 <PackageName>_FIND_VERSION_COMPLETE 都将被定义,并包含指定的完整请求版本字符串。

在模块模式下,加载的查找模块负责响应这些变量中详细说明的请求;有关详细信息,请参阅查找模块。在配置模式下,find_package 会自动处理 REQUIREDQUIET[version] 选项,但将组件的处理方式留给包配置文件来决定。包配置文件可以将 <PackageName>_FOUND 设置为 false,以告知 find_package 组件要求未满足。

CPS 传递性要求

通用包规范 (CPS) 包描述由一个或多个组件组成,这些组件可能又依赖于包内部或外部的其他组件。当需要外部组件时,提供包被视为该包的包级别要求。此外,所需组件集通常会在所述外部包要求中注明。

CMake 脚本包描述会使用 find_dependency() 命令来处理传递性依赖项,而 CMake 则使用内部嵌套的 find_package 调用来为 CPS 本身处理传递性依赖项。此调用可以通过另一个 CPS 包或通过 CMake 脚本包来解析 CPS 包依赖项。CPS 组件依赖项的处理方式有一些注意事项。

当解析传递性依赖项的候选是另一个 CPS 包时,情况很简单;COMPONENTS 和 CPS“组件”是直接可比的(并且实际上与 CMake“导入目标”同义)。然而,CMake 脚本包通常会(并且经常)检查是否找到了必需的组件,而不管包是否描述了单独的组件。此外,即使描述了组件的包,通常也与 CPS 的正常情况不同,与导入目标的关联性也不强。因此,将 CPS 包声明的必需组件集传递给 COMPONENTS 将导致解析依赖项的意外失败。

为了解决这个问题,如果解析 CPS 传递性依赖项的候选是 CMake 脚本包,CMake 会将消耗该 CPS 包声明的必需组件作为 OPTIONAL_COMPONENTS 传递,并进行单独的内部检查,以确保候选包提供了必需的导入目标。这些目标必须命名为 <PackageName>::<ComponentName>,以符合 CPS 约定,否则检查将认为该包未找到。