转载总结自深入理解CMake(3):find_package()的使用
CMake学习日志之find_package
一、首先明确一下默认查找的路径
<package>_DIR
CMAKE_PREFIX_PATH
CMAKE_FRAMEWORK_PATH
CMAKE_APPBUNDLE_PATH
PATH
二、了解一下find_package()的两种模式
find_package()有Module模式(基本用法,basic signature)和Config模式(full signature,完全用法)
在以下三种情况下为Config模式
- find_package()中指定CONFIG关键字
- find_package()中指定NO_MODULE关键字
- find_package()中使用了不在Module模式下支持的配置关键字
三、明确find_package()下的所有关键字
3.1 Module模式
find_package(<PackageName> [version] [EXACT] [QUIET] [MODULE]
[REQUIRED] [[COMPONENTS] [components...]]
[OPTIONAL_COMPONENTS components...]
[NO_POLICY_SCOPE])
注意,在没设置MOUDULE选项的情况下,如果Module模式找不到包则会自动进入Config模式
version和EXACT: 都是可选的,version指定的是版本,如果指定就必须检查找到的包的版本是否和version兼容。如果指定EXACT则表示必须完全匹配的版本而不是兼容版本就可以。
QUIET 可选字段,表示如果查找失败,不会在屏幕进行输出(但是如果指定了REQUIRED字段,则QUIET无效,仍然会输出查找失败提示语)。
MODULE 可选字段。前面提到说“如果Module模式查找失败则回退到Config模式进行查找”,但是假如设定了MODULE选项,那么就只在Module模式查找,如果Module模式下查找失败并不回落到Config模式查找。
REQUIRED可选字段。表示一定要找到包,找不到的话就立即停掉整个cmake。而如果不指定REQUIRED则cmake会继续执行。
COMPONENTS components :可选字段,表示查找的包中必须要找到的组件(components),如果有任何一个找不到就算失败,类似于REQUIRED,导致cmake停止执行。
OPTIONAL_COMPONENTS和components:可选的模块,找不到也不会让cmake停止执行。
3.2 Module模式查找顺序
Module模式下是要查找到名为Find.cmake的文件。
先在CMAKE_MODULE_PATH变量对应的路径中查找。如果路径为空,或者路径中查找失败,则在cmake module directory(cmake安装时的Modules目录,比如/usr/local/share/cmake/Modules)查找。
不管使用哪一种模式,只要找到包,就会定义下面这些变量:
<NAME>_FOUND
<NAME>_INCLUDE_DIRS or <NAME>_INCLUDES
<NAME>_LIBRARIES or <NAME>_LIBRARIES or <NAME>_LIBS
<NAME>_DEFINITIONS
这些都在Find.cmake文件中。
现在,在你的代码(要使用库 的代码)的顶层目录中的CMakeLists.txt文件中,我们检查变量<NAME>_FOUND
来确定包是否被找到。大部分包的这些变量中的包名是全大写的,如 LIBFOO_FOUND ,有些包则使用包的实际大小写,如 LibFoo_FOUND 。如果找到这个包,我们用 _INCLUDE_DIRS 调用 include_directories() 命令,用 _LIBRARIES 调用 target_link_libraries() 命令。
3.3 Config模式下find_package()的用法
find_package(<PackageName> [version] [EXACT] [QUIET]
[REQUIRED] [[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模式下的查找顺序,比Module模式下要多得多。而且,新版本的CMake比老版本的有更多的查找顺序(新增的在最优先的查找顺序)。它要找的文件名字也不一样,Config模式要找<PackageName>Config.cmake
或<lower-case-package-name>-config.cmake
。查找顺序为:
3.3.1 <PackageName>_ROOT
的cmake变量或环境变量
名为<PackageName>_ROOT
的cmake变量或环境变量
。CMake3.12新增。设定CMP0074 Policy来关闭。
注意:如果定义了<PackageName>_DIR cmake变量
,那么<PackageName>_ROOT
不起作用。举例:
cmake_minimum_required(VERSION 3.13)
project(fk_cmk)
set(OpenCV_ROOT "F:/zhangzhuo/lib/opencv_249/build")
set(OpenCV_DIR "F:/zhangzhuo/lib/opencv_300/build")
find_package(OpenCV QUIET
NO_MODULE
NO_DEFAULT_PATH
NO_CMAKE_PATH
NO_CMAKE_ENVIRONMENT_PATH
NO_SYSTEM_ENVIRONMENT_PATH
NO_CMAKE_PACKAGE_REGISTRY
NO_CMAKE_BUILDS_PATH
NO_CMAKE_SYSTEM_PATH
NO_CMAKE_SYSTEM_PACKAGE_REGISTRY
)
message(STATUS "OpenCV library status:")
message(STATUS " version: ${OpenCV_VERSION}")
message(STATUS " libraries: ${OpenCV_LIBS}")
message(STATUS " include path: ${OpenCV_INCLUDE_DIRS}")
实际上会找到opencv300,也就是OpenCV_DIR这一cmake变量的值最先起作用。
3.3.2 cmake特定的缓存变量:
CMAKE_PREFIX_PATH
CMAKE_FRAMEWORK_PATH
CMAKE_APPBUNDLE_PATH
可以通过设定NO_CMAKE_PATH来关闭这一查找顺序
3.3.3 cmake特定的环境变量
<PackageName>_DIR
CMAKE_PREFIX_PATH
CMAKE_FRAMEWORK_PATH
CMAKE_APPBUNDLE_PATH
3.3.4 HINT字段指定的路径
3.3.5 搜索标准的系统环境变量PATH
。
其中如果是以/bin或者/sbin结尾的,会自动转化为其父目录。通过指定NO_SYSTEM_ENVIRONMENT_PATH来跳过。
3.3.6 其他
四、思路总结
4.1 判断find_package()实际执行的是module模式还是config模式
-
find_package()这样的用法并不能看出是module模式还是config模式。要看CMAKE_MODULE_PATH或cmake安装路径下是否有
Find<PackageName>.cmake
脚本存在,并且这个脚本是否能正确的找到包。如果上述两个位置不存在Find.cmake,或者这个Find.cmake执行失败,则进入config模式。 -
可以通过CONFIG、NO_MODULE、CONFIG模式特有字段,来设定为config模式
-
明确
<PackageName>_DIR
是config模式特有的缓存变量
可以在find_package()前设定<PackageName>_DIR
,指向包含<PackageName>Config.cmake
或<lower-case-package-name>-config.cmake
的目录。
<PackageName>_ROOT
先设定,再设定<PackageName>_DIR
,最后find_package(<PackageName>)
;并且两个都能找到包,则<PackageName>_DIR
起作用。
<PackageName>_DIR
也可在find_package()后使用例如打印。
module模式下在find_package()前使用==_DIR==,并不能用来帮助find_package()找到包;并且在find_package()后,也并没有_DIR缓存变量自动存在。
- 明确
<PackageName>_ROOT
是cmake3.12起支持的变量
<PackageName>_ROOT
变量被find_package, find_library, find_path, find_program, find_file支持。因此,尽管从find_package()文档页看会以为<PackageName>_ROOT
只被config模式支持而不被module模式支持,但是module模式下通过另外4个find命令会间接的使用到<PackageName>_ROOT
,从而find_package命令的module模式间接的支持<PackageName>_ROOT
变量。
<PackageName>_ROOT
设定后,find_package()的config模式会在<PackageName>_ROOT
目录及其子目录下寻找cmake的config文件;而_DIR则很傻,不会在子目录中寻找。