• find_package深度解析及实例应用


    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指定的安装目录肯定是构建的前缀之一。

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

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

    _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本身不会对版本号做任何转换,而是通过查找到包的版本校验文件(包自身提供的)ConfigVersion.cmake(或-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 基础命令

    1. find_package( [version] [EXACT] [QUIET] [MODULE]
    2. [REQUIRED] [[COMPONENTS] [components...]]
    3. [OPTIONAL_COMPONENTS components...]
    4. [REGISTRY_VIEW (64|32|64_32|32_64|HOST|TARGET|BOTH)]
    5. [GLOBAL]
    6. [NO_POLICY_SCOPE]
    7. [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 完整命令

    1. find_package(<PackageName> [version] [EXACT] [QUIET]
    2. [REQUIRED] [[COMPONENTS] [components...]]
    3. [OPTIONAL_COMPONENTS components...]
    4. [CONFIG|NO_MODULE]
    5. [GLOBAL]
    6. [NO_POLICY_SCOPE]
    7. [BYPASS_PROVIDER]
    8. [NAMES name1 [name2 ...]]
    9. [CONFIGS config1 [config2 ...]]
    10. [HINTS path1 [path2 ... ]]
    11. [PATHS path1 [path2 ... ]]
    12. [REGISTRY_VIEW (64|32|64_32|32_64|HOST|TARGET|BOTH)]
    13. [PATH_SUFFIXES suffix1 [suffix2 ...]]
    14. [NO_DEFAULT_PATH]
    15. [NO_PACKAGE_ROOT_PATH]
    16. [NO_CMAKE_PATH]
    17. [NO_CMAKE_ENVIRONMENT_PATH]
    18. [NO_SYSTEM_ENVIRONMENT_PATH]
    19. [NO_CMAKE_PACKAGE_REGISTRY]
    20. [NO_CMAKE_BUILDS_PATH] # Deprecated; does nothing.
    21. [NO_CMAKE_SYSTEM_PATH]
    22. [NO_CMAKE_INSTALL_PREFIX]
    23. [NO_CMAKE_SYSTEM_PACKAGE_REGISTRY]
    24. [CMAKE_FIND_ROOT_PATH_BOTH |
    25. ONLY_CMAKE_FIND_ROOT_PATH |
    26. 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

    1. #include "./include/add.h"
    2. int add(int first, int second)
    3. {
    4. int val = 0;
    5. val = first+second;
    6. return val;
    7. }

    add.h

    1. #ifndef _ADD_H_
    2. #define _ADD_H_
    3. #include
    4. int add(int first, int second);
    5. #endif

    main.c

    1. #include "add.h"
    2. int main(void)
    3. {
    4. int result = 0;
    5. result = add(1, 1);
    6. printf("result = %d\n", result);
    7. return 0;
    8. }

    执行: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函数查找,内容如下:

    1. //CMAKE_SOURCE_DIR:CMakeLists.txt所在的目录
    2. //在指定目录(${CMAKE_SOURCE_DIR}/include)下查找add.h文件,如果找到将路径返回给ADD_INCLUDE_DIR
    3. find_path(ADD_INCLUDE_DIR add.h ${CMAKE_SOURCE_DIR}/include)
    4. //在指定目录(${CMAKE_SOURCE_DIR}/lib)下查找add.h文件,如果找到将路径返回给ADD_LIBRARY,
    5. //NAMES : 要查找的库名字,可以有多个
    6. //PATHS:查找的路径,可以有多个
    7. find_library(ADD_LIBRARY NAMES add PATHS ${CMAKE_SOURCE_DIR}/lib)
    8. //如果ADD_INCLUDE_DIR ADD_LIBRARY都有结果,设置ADD_FOUND为真,作为find_package的返回结果
    9. if (ADD_INCLUDE_DIR AND ADD_LIBRARY)
    10. set(ADD_FOUND TRUE)
    11. endif (ADD_INCLUDE_DIR AND ADD_LIBRARY)

    然后返回我们上层目录,编辑我们的CMakeLists.txt:

    1. cmake_minimum_required(VERSION 1.0)
    2. project(TEST)
    3. //指定find_package查找的路径,CMAKE_MODULE_PATH为module模式下邮箱查找的路径
    4. set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
    5. add_executable(main main.c)
    6. find_package(Add)
    7. //如果查找完成,找到相应的库就进行头文件和库的引用
    8. if(ADD_FOUND)
    9. target_include_directories(main PRIVATE ${ADD_INCLUDE_DIR})
    10. target_link_libraries(main ${ADD_LIBRARY})
    11. endif(ADD_FOUND)

    这个时候整个查找流程就完成了,创建build,进入该目录进行cmake编译,执行是没有问题的。

    另外cmake有自带的内部依赖库,我们直接find_package(库名),即可;如果想要引用非内部库,例如grpc的库,需要将grpc使用cmake方式进行编译安装,然后find_package直接查找即可。

    以上是find_package的module模式的测试方案,感兴趣的话也可以按照我上面的描述自己写一个config模式的查找测试方案。

     

  • 相关阅读:
    八大排序(二)--------冒泡排序
    R绘制箱线图
    AFL源码分析(一)
    照身帖、密钥,看古代人做实名认证有哪些招数?
    最小栈 与 栈的压入、弹出序列
    【现代密码学原理实验】——CrypTool2与OpenSSL的使用(学习笔记)
    Spring的循环依赖
    网络通信深入解析:探索TCP/IP模型
    计算机毕业设计springboot+vue基本微信小程序的云宠物小程序-宠物领养
    Rust 赋能前端: 视频抽帧
  • 原文地址:https://blog.csdn.net/qq_39466755/article/details/130912344