在这个模式下会查找一个名为find.cmake的文件,首先去CMAKE_MODULE_PATH指定的路径下去查找,然后去cmake安装提供的查找模块中查找(安装cmake时生成的一些cmake文件)。找到之后会检查版本,生成一些需要的信息。
在这个模式下会查找一个名为-config.cmake(<小写包名>-config.cmake)或者Config.cmake 的文件,如果指定了版本信息也会搜索名为-config-version.cmake 或者 ConfigVersion.cmake的文件。
1.2.1 搜索路径
该模式搜索配置和版本文件的路径比module模式复杂的多:
首先在CMAKE_FIND_PACKAGE_REDIRECTS_DIR指定的路径下查找。如果没有找到配置文件,则按照下面的逻辑进行查找:
cmake会为包构建可能的前缀,然后再前缀目录下搜索多个可能的目录,cmake指定的安装目录肯定是构建的前缀之一。
简单解释一下,prefix就是我们cmake编译的时候指定的安装路径(CMAKE_STAGING_PREFIX的路径)(cmake指定的安装目录肯定是搜索路径之一,还可能有其他可能的目录);
如果FIND_LIBRARY_USE_LIB64_PATHS被置为true,则lib64的路径将再64位平台上被搜索。
如果FIND_LIBRARY_USE_LIB32_PATHS被置为true,则lib32的路径将再32位平台上被搜索。
lib路径始终被搜索。
使用以下步骤构建安装前缀集(搜索路径的prefix字段)。如果指定NO_DEFAULT_PATH,则启用所有NO_*选项:
(1). _ROOT环境变量中指定的搜索路径,其中是要查找的包(find_package的第一个参数保留大小写)。如果传递了NO_PACKAGE_ROOT_PATH,或者将CMAKE_FIND_USE_PACKAGE_ROOT_PATH设置为FALSE,则可以跳过此操作。
(2). 搜索cmake特定缓存变量中指定的路径。通过命令行-DVAR=value传递进来的路径,多个路径需要以分号隔开。包含
CMAKE_PFEFIX_PATH、
CMAKE_FRAMEWORK_PATH、
CMAKE_APPBUNDLE_PATH三个变量;
例如
cmake -DCMAKE_PREFIX_PATH=/usr/local/lib;/lib。可以通过NO_CMAKE_PATH选项或将CMAKE_FIND_USE_CMAKE_PATH设置为FALSE来跳过
(3). 搜索特定于cmake的环境变量中指定的路径。如果传递了NO_CMAKE_ENVIRONMENT_PATH,或者将CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH设置为FALSE,则可以跳过此操作:
CMAKE_PREFIX_PATH
CMAKE_FRAMEWORK_PATH
CMAKE_APPBUNDLE_PATH
(4). HINTS指定的路径或者标准系统环境变量指定的路径,例如:PATH,LD_LIBRARY_PATH;在~/.bashrc或者/etc/profile文件中指定
(5). 搜索当前系统的平台文件中定义的cmake变量(就是当前系统定义的一些cmake相关的变量)
例如:
CMAKE_INSTALL_PREFIX
CMAKE_STAGING_PREFIX
但是可以被传递NO_CMAKE_INSTALL_PREFIX或者CMAKE_FIND_USE_INSTALL_PREFIX设置为FALSE跳过。
如果传递了NO_CMAKE_SYSTEM_PATH,或者将CMAKE_FIND_USE_CMAKE_SYSTEM_PATH设置为FALSE,则可以跳过所有这些位置:
CMAKE_SYSTEM_PREFIX_PATH
CMAKE_SYSTEM_FRAMEWORK_PATH
CMAKE_SYSTEM_APPBUNDLE_PATH
这些变量包含的路径一般是系统默认安装路径:/usr/local等。
(6). PATHS指定的路径
1.2.2 版本配置文件
当指定version参数,配置模式将仅会查找能兼容指定版本的包,如果指定了EXACT,则会查找精确匹配指定版本的包。
CMake本身不会对版本号做任何转换,而是通过查找到包的版本校验文件(包自身提供的)
当find_package命令中指定version参数后,会把version参数解析出来,赋值到PACKAGE_FIND_XXX中,定义了以下变量:
单一版本号:
PACKAGE_FIND_NAME:包名
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:表示是否包含高版本,当前支持INCLUDE和EXCLUDE
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_VERSION_XXX的变量供find_package使用,具体的变量如下:
PACKAGE_VERSION:版本文件中提供的全版本字符串
PACKAGE_VERSION_EXACT:如果精确匹配,那么该变量为True
PACKAGE_VERSION_COMPATIBLE:如果版本兼容,那么该变量为True
PACKAGE_VERSION_UNSUITABLE:如果未找到合适的版本,该变量为True
上面的PACKAGE_VERSION_XXX几个变量仅用于find_package命令检查配置文件是否提供了一个可接受的版本,一旦find_package命令返回后,这些变量就失效了。
如果版本校验通过,那么如下_VERSION_XXX变量会被设置,供find_package调用者使用:
_VERSION:包的全版本字符串
_VERSION_MAJOR:主版本
_VERSION_MINOR:此版本
_VERSION_PATCH:补丁版本
_VERSION_TWEAK:小版本
_VERSION_COUNT:点分版本组成的数量,范围0~4
- find_package(
[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])
module模式和config模式都支持该命令格式。
version:请求与该包兼容的版本。有两种可能的形式可以指定它:
(1):格式为major[.minor[.patch]的单一版本,例如:每个部分都是一个数字,均为可选:0.0.0.0或0.0.0或0.0.
(2):格式为versionMin…[<]versionMax,其中versionMin和versionMax具有与单个版本相同的格式和对整数组件的约束(例如:0.0.0.0...1.1.1.1)。默认情况下,包括两个端点。通过指定<,将排除上结束点。版本范围仅支持CMake 3.19或更高版本。
EXACT: 选项要求精确匹配版本。此选项与版本范围的规格不兼容(版本的第二种格式)。
MODULE:只是用module模式进行查找,不进行config模式查找,如果没指定的话,优先使用module模式搜索,如果未找到在进行config模式搜索。
QUIET:禁用信息消息。正常情况当找到包时,CMake会打印一些信息,指定该选项时会禁止掉这些打印。如果找不到包,仍然会输出错误信息并终止执行过程。
REQUIRED:当包没有被找到时,停止处理,及cmake停止继续运行。
COMPONENTS:指定要查找的组件列表,一般一个包可能有很多组件组成(例如一个库生成需要很多其他的依赖库),通常找到这些全部组件才认为包被找到。
OPTIONAL_COMPONENTS:指定要查找的组件列表,与COMPONENTS的区别是,不要求这些组件必须存在。不影响CMake的执行。
REGISTRY_VIEW:用于windows,在这不做考虑。
GLOBAL:将把所有导入的目标提升到导入项目中的全局作用域。可以通过设置CMAKE_FIND_PACKAGE_TARGETS_GLOBAL变量来启用此功能。
- find_package(<PackageName> [version] [EXACT] [QUIET]
- [REQUIRED] [[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:这两个选项二选一即可,表示强制find_package命令使用config模式搜索,忽略module模式搜索。
NAMES:默认情况下find_package命令会查找名为的包。如果NAMES指定了名称,则会使用这些名字来查找包而忽略参数。
PATHS/HINTS:config模式下指定.cmake文件的搜索路径。
NO_XXX_PATH:config模式下忽略指定的路径。
cmake引用外部自定义库的测试案例:
首先写个加法器,用于生成库文件供其他程序调用,在/home/findpack下创建三个文件:
add.c
- #include "./include/add.h"
-
- int add(int first, int second)
- {
- int val = 0;
- val = first+second;
-
- return val;
- }
add.h
- #ifndef _ADD_H_
- #define _ADD_H_
-
- #include
-
- int add(int first, int second);
-
- #endif
main.c
- #include "add.h"
-
- int main(void)
- {
- int result = 0;
-
- result = add(1, 1);
-
- printf("result = %d\n", result);
-
- return 0;
- }
执行:gcc -shared -fPIC -o libadd.so add.c 生成libadd.so库文件,在该目录下创建 include, lib两个文件,include用于存放add.h,lib用于存放libadd.so,此时当前目录结构如下:
add.c main.c include lib CMakeLists.txt
然后创建cmake目录,cmake目录下创建FindAdd.cmake文件,用于find_package函数查找,内容如下:
- //CMAKE_SOURCE_DIR:CMakeLists.txt所在的目录
- //在指定目录(${CMAKE_SOURCE_DIR}/include)下查找add.h文件,如果找到将路径返回给ADD_INCLUDE_DIR
- find_path(ADD_INCLUDE_DIR add.h ${CMAKE_SOURCE_DIR}/include)
- //在指定目录(${CMAKE_SOURCE_DIR}/lib)下查找add.h文件,如果找到将路径返回给ADD_LIBRARY,
- //NAMES : 要查找的库名字,可以有多个
- //PATHS:查找的路径,可以有多个
- find_library(ADD_LIBRARY NAMES add PATHS ${CMAKE_SOURCE_DIR}/lib)
-
- //如果ADD_INCLUDE_DIR ADD_LIBRARY都有结果,设置ADD_FOUND为真,作为find_package的返回结果
- if (ADD_INCLUDE_DIR AND ADD_LIBRARY)
- set(ADD_FOUND TRUE)
- endif (ADD_INCLUDE_DIR AND ADD_LIBRARY)
然后返回我们上层目录,编辑我们的CMakeLists.txt:
- cmake_minimum_required(VERSION 1.0)
- project(TEST)
- //指定find_package查找的路径,CMAKE_MODULE_PATH为module模式下邮箱查找的路径
- set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
- add_executable(main main.c)
- find_package(Add)
-
- //如果查找完成,找到相应的库就进行头文件和库的引用
- if(ADD_FOUND)
- target_include_directories(main PRIVATE ${ADD_INCLUDE_DIR})
- target_link_libraries(main ${ADD_LIBRARY})
- endif(ADD_FOUND)
这个时候整个查找流程就完成了,创建build,进入该目录进行cmake编译,执行是没有问题的。
另外cmake有自带的内部依赖库,我们直接find_package(库名),即可;如果想要引用非内部库,例如grpc的库,需要将grpc使用cmake方式进行编译安装,然后find_package直接查找即可。
以上是find_package的module模式的测试方案,感兴趣的话也可以按照我上面的描述自己写一个config模式的查找测试方案。