find_package
用于查找包(通常是使用三方库),并返回关于包的细节(使用包所依赖的头文件、库文件、编译选项、链接选项等)
与find_libaray直接在指定搜索目录下搜索库不同,find_package
命令可以获取更多的信息,那么它的搜索方式也是与find_libaray
不一样,它有两种不同的搜索方式,因此在介绍这个命令的细节之前,先简单介绍一下find_package
命令的两种搜索模式:模块模式(Module mode
)和配置模式(Config mode
)。
Module mode
)在该模式下,Cmake
会搜索一个名为Find
的文件,其中
为待搜索包的名称。
搜索路径的顺序依次是:
CMAKE_MODULE_PATH
指定的路径中进行查找Cmake
安装路径中查找。Cmake
会在其安装路径下提供很多.cmake
文件,例如/XXX/cmake/Modules/
目录下(不同的系统安装目录可能不一致)如果找到文件Find
,Cmake
会读取并处理该文件,简而言之,它负责检查一些条件(如版本号是否满足等)是否满足,并在找到包后,返回给调用者一些变量,用以获取包的详细信息。
一般来说,Find
文件不是随包本身一起提供的,更多的是外部针对已有包的重新包装,例如操作系统、Cmake
程序、甚至是调用find_package
命令的工程针对已有的包提供针对该包的.cmake
文件。
Config mode
)该模式下,CMake
会搜索
文件或
文件。如果find_package
命令中指定了具体的版本,也会搜索
或
文件,因此配置模式下通常会提供配置文件和版本文件(注意形式上要保持一致),并且作为包的一部分一起提供给使用者。
该模式下对.cmake
文件的搜索路径的顺序比较复杂,具体见本文的4.1
节。
.cmake
后缀文件的作用是什么?
find_package
的两种搜索模式都会按照一定规则从路径下搜索.cmake
后缀的文件,两种模式下的.cmake
文件作用都是为了给find_package
命令的调用方返回有关包的信息(头文件路径、库文件路径、编译连接选项、版本信息等等)
find_package
命令有两种格式,基本命令格式和完整命令格式。
- find_package(
[version] [EXACT] [QUIET] [MODULE] - [REQUIRED] [[COMPONENTS] [components...]]
- [OPTIONAL_COMPONENTS components...]
- [NO_POLICY_SCOPE])
几个重要的参数介绍:
PackageName
:待查找包的名称。此外它还决定两种搜索模下的.cmake
文件名称:例如模块模式下的名称为Find.cmake
,而配置模式下为-config.cmake
/-config-version.cmake
。MODULE
:该选项指定find_package
命令只使用模块模式
搜索方式查找。未指定该选项时,find_package
会优先使用模块模式
搜索,仍未找到包时,会切换成配置模式
搜索。version
:待查找包的版本号要求,版本号为点分格式,由四个部分组成,每个部分都是一个数字,均为可选:major[.minor[.patch[.tweak]]]
,例如1.1.1.1
、1.0
、等。同样也可以指定版本范围(CMake 3.19
及之后才支持),格式为:versionMin...[<]versionMax
,versionMin
和versionMax
均是major[.minor[.patch[.tweak]]]
形式的版本号,默认情况下会包含这个指定区间两端的版本号,但如果指定了<
,那么会排除掉versionMax
,例如1.1.1.1...1.1.2.0
、1.1.1.1...<1.1.2.0
等。EXACT
:该选项要求待查找包的版本必须与指定的版本精确匹配,因此如果指定的是一个版本范围,不能使用该参数。QUIET
:禁止输出信息,正常情况当找到包时,CMake
会打印一些信息,指定该选项时会禁止掉这些打印。例外是当同时指定QUIET
时,如果找不到包,仍然会输出错误信息并终止执行过程。REQUIRED
:当未找到满足条件的包(例如版本号不匹配,或指定组件未找到等),会终止CMake
的执行过程,并输出一条错误信息。如果未指定该选项,即使未找到满足条件的包,CMake
的执行过程也会继续。COMPONENTS
:指定要查找的组件。通常一个包可能包含多个组件(可以理解为多个库,例如把C++的std看成一个包的概念,那么vector就是std下的其中一个组件),我们的工程可能会依赖包下的具体某个组件,因此可以通过这个选项来检测这些组件是否存在。通常的约定是,该选项后的组件应该都找到时才认为包找到,否则认为未找到满足条件的包。这个约束会依赖包的.cmake
来实现,通过find_package
命令传入的COMPONENTS
可以通过_FIND_COMPONENTS
这个变量来获得。举个简单的例子: - # mymathConfig.cmake,假定它位于./mymath/mymath目录下
- # 作用就是校验COMPONENTS是否是test,只有当COMPONENTS为空或者为test时,包mymath才会被找到
-
- message(${mymath_FIND_COMPONENTS}) # `find_package`命令的`COMPONENTS`传入的
- if(${mymath_FIND_COMPONENTS} STREQUAL "")
- message("Empty comps.")
- set(mymath_INCLUDE_DIR "/XXX/mymath")
- set(mymath_LIBRARY "/XXX/mymath/libmymath.a")
- else()
- foreach(comp ${mymath_FIND_COMPONENTS})
- if (comp MATCHES "test")
- message("Find comp test")
- set(mymath_INCLUDE_DIR "/XXX/mymath")
- set(mymath_LIBRARY "/XXX/mymath/libmymath.a")
- endif()
- endforeach()
- endif()
- # 顶层目录的CMakeLists.txt
- cmake_minimum_required(VERSION 3.10.2)
- project(find_package_test)
- find_package(mymath
- CONFIG
- REQUIRED
- COMPONENTS test
- PATHS ./mymath/mymath
- )
-
- if(mymath_FOUND)
- message("Find mymath: ${mymath_INCLUDE_DIR}; ${mymath_LIBRARY};")
- endif()
- # 执行cmake .
- cmake .
-
- # 输出为
- test
- Find comp test
- Find mymath: /XXX/mymath; /XXX/mymath/libmymath.a;
OPTIONAL_COMPONENTS
:与COMPONENTS
的区别是,不强制要求这些组件必须存在。不影响CMake
的执行。- 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|NO_MODULE
:这两个选项二选一即可,表示强制find_package
命令使用配置模式
搜索,忽略模块模式
搜索。NAMES
:默认情况下find_package
命令会查找名为
的包。如果NAMES
选项后指定了名称,则会使用这些名字来查找包而忽略
参数。PATHS
/HINTS
:配置模式下指定.cmake
文件的搜索路径。NO_XXX_PATH
:配置模式下忽略指定的路径,具体的含义可以参考4.1.2
节。