1. 检索模式

1.1 module模式

        在这个模式下会查找一个名为find.cmake的文件,首先去CMAKE_MODULE_PATH指定的路径下去查找,然后去cmake安装提供的查找模块中查找(安装cmake时生成的一些cmake文件)。找到之后会检查版本,生成一些需要的信息。

1.2 config模式

        在这个模式下会查找一个名为-config.cmake(<小写包名>-config.cmake)或者Config.cmake 的文件,如果指定了版本信息也会搜索名为-config-version.cmake 或者 ConfigVersion.cmake的文件。

1.2.1 搜索路径

        该模式搜索配置和版本文件的路径比module模式复杂的多:

        首先在CMAKE_FIND_PACKAGE_REDIRECTS_DIR指定的路径下查找。如果没有找到配置文件,则按照下面的逻辑进行查找:

        cmake会为包构建可能的前缀,然后再前缀目录下搜索多个可能的目录,cmake指定的安装目录肯定是构建的前缀之一。

find_package深度解析及实例应用-LMLPHP

        简单解释一下,prefix就是我们cmake编译的时候指定的安装路径(CMAKE_STAGING_PREFIX的路径)(cmake指定的安装目录肯定是搜索路径之一,还可能有其他可能的目录);

        <prefix>/<name>*/(lib/<arch>|lib*|share)/cmake/<name>*/就表示可能在/lib/cmake/或者/lib*/cmake/或者/share/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,则可以跳过此操作:

<PackageName>_DIR

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本身不会对版本号做任何转换,而是通过查找到包的版本校验文件(包自身提供的)<PackageName>ConfigVersion.cmake(或<PackageName>-config-version.cmake),调用版本配置文件做校验,版本配置文件可以通过CMakePackageConfigHelpers模块来辅助创建。

        当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

2. 命令格式

2.1 基础命令

find_package(<PackageName> [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变量来启用此功能。

2.2 完整命令

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模式下忽略指定的路径。

3. find_package实例用法

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 <stdio.h>

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模式的查找测试方案。

 

05-29 06:28