cmake find

tech2025-10-25  2

一、find_package可以解决的问题

当构建一个依赖第三库或外部库的project时(即:project需要链接第三方库或外部库),我们需要知道以下信息:

去哪儿找第三 方库的头文件 .h对比GCC的 -I 参数去哪儿找第三方库的链接文件 (.so/.dll/.lib/.dylib/…)对比GCC的 -L 参数链接的第三方库的文件的名字对比GCC的 -l 参数

知道上面的信息后,就可以在CMakeLists.txt中方便的包含第三方库的头文件、访问的链接第三方库的库文件(.a、.so)了;

二、为什么使用find_package

          第三方库的安装路径,在不同的机器上可能不同,所以在CMakeLists.txt中指定包含路径、链接路径和库,不太现实,除非你指定工程所需要的依赖库安装在固定的目录(貌似不太现实),否则在你的机器上构建成功了,在别人的机器上可能构建失败。或修改了第三方库的安装路径或版本升级后,在自己的机器上可能也无法构建成功。

          使用cmake的find_package可解决上面的问题。

         举个栗子:

              比如说,我们需要一个第三方库 curl,那么我们的 CMakeLists.txt 需要指定头文件目录,和库文件,类似:

                 include_directiories(/usr/include/curl)                  target_link_libraries(myprogram path/curl.so)                如果借助于cmake提供的finder会怎么样呢?使用cmake的Modules目录下的FindCURL.cmake,相应的CMakeList.txt 文件:                  find_package(CURL REQUIRED)                  include_directories(${CURL_INCLUDE_DIR})                  target_link_libraries(curltest ${CURL_LIBRARY})

                  #使用find_package,不用关心curl具体安状在什么位置,find_package会替我们解决,那么是如何智能查找的呢?

三、find_package的原理

           find_package首先会在模块路径中寻找Findxxxx.cmake,这是查找库的典型方式。具体查找路径依次为CMake变量${CMAKE_MODULE_PATH}中的所有目录,如果没有,再查找它自己的模块目录/share/cmake-x.y/Modules/($CMAKE_ROOT的具体值可以通过CMake中message命令输出)。如果找到了xxxx模块,那么Findxxxx.cmake一般会设置以下变量供CMakeLists.txt使用:

          xxxx_FOUND    #为true

          xxxx_INCLUDE_DIRS   #include路径

          xxxx_LIBRARY_DIRS    #library路径

          xxxx_LIBRARIES           #library的名字

          xxxx_yyyy_VERSION    #具体详见Findxxxx.cmake

          为了能支持各种常见的库和包,CMake自带了很多Findxxxx模块。可以通过命令 cmake –help-module-list (输入cmake –help,然后双击Tab会有命令提示)得到你的CMake支持的模块的列表,也可以直接查看模块路径: ls /usr/share/cmake/Modules/

四、基本语法和模式

        根据cmake官方文档可以知道,find_package()有Module模式(基本用法,basic signature)和Config模式(full signature,完全用法),其中Module模式是基础,Config模式则更复杂高级些。

        Module模式基本语法:

find_package(<PackageName> [version] [EXACT] [QUIET] [MODULE] [REQUIRED] [[COMPONENTS] [components...]] [OPTIONAL_COMPONENTS components...] [NO_POLICY_SCOPE])

        Config模式基本语法:                                              

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])

    更详细的请参见:https://cmake.org/cmake/help/v3.18/command/find_package.html#find-package

    只有以下3种情况下才是Config模式:

                find_package()中指定CONFIG关键字                 find_package()中指定NO_MODULE关键字                  find_package()中使用了不在"basic signature"(也就是Module模式下所有支持的配置)关键字      只要不指定"CONFIG",不指定“NO_MODULE",也不使用"full signature"中的关键字,那我就是在Module模式。排查find_package()的第一步,应当判断它是Module模式还是Config模式。

五、基本用法

    1、Module模式下find_package()的用法         

find_package(<PackageName> [version] [EXACT] [QUIET] [MODULE] [REQUIRED] [[COMPONENTS] [components...]] [OPTIONAL_COMPONENTS components...] [NO_POLICY_SCOPE])

Module模式下,相比于Config模式,可选配置参数少一些,并且如果按用户指定的配置却找不到包,就会自动进入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停止执行。

Module模式查找顺序 Module模式下是要查找到名为Find<PackageName>.cmake的文件。

先在CMAKE_MODULE_PATH变量对应的路径中查找。如果路径为空,或者路径中查找失败,则在cmake module directory(cmake安装时的Modules目录,比如/usr/local/share/cmake/Modules)查找。

  2、Config模式下find_package()的用法

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模式下的查找顺序

比Module模式下要多得多。而且,新版本的CMake比老版本的有更多的查找顺序(新增的在最优先的查找顺序)。它要找的文件名字也不一样,Config模式要找<PackageName>Config.cmake或<lower-case-package-name>-config.cmake。查找顺序为:

名为<PackageName>_ROOT的cmake变量或环境变量。CMake3.12新增。设定CMP0074 Policy来关闭。 注意:如果定义了<PackageName>_DIR cmake变量,那么<PackageName>_ROOT 不起作用。

cmake特定的缓存变量:

CMAKE_PREFIX_PATH CMAKE_FRAMEWORK_PATH CMAKE_APPBUNDLE_PATH 可以通过设定NO_CMAKE_PATH来关闭这一查找顺序

cmake特定的环境变量

<PackageName>_DIR CMAKE_PREFIX_PATH CMAKE_FRAMEWORK_PATH CMAKE_APPBUNDLE_PATH 可以通过NO_CMAKE_ENVIRONMENT_PATH来跳过。

HINT字段指定的路径

搜索标准的系统环境变量PATH。 其中如果是以/bin或者/sbin结尾的,会自动转化为其父目录。 通过指定NO_SYSTEM_ENVIRONMENT_PATH来跳过。

存储在cmake的"User Package Registry"(用户包注册表)中的路径。 通过设定NO_CMAKE_PACKAGE_REGISTRY,或者: 设定CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY为true, 来避开。

设定为当前系统定义的cmake变量:

CMAKE_SYSTEM_PREFIX_PATH CMAKE_SYSTEM_FRAMEWORK_PATH CMAKE_SYSTEM_APPBUNDLE_PATH 通过设定NO_CMAKE_SYSTEM_PATH来跳过。

在cmake的"System Package Registry"(系统包注册表)中查找。 通过设定NO_CMAKE_SYSTEM_PACKAGE_REGISTRY跳过。 或者通过设定CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY为true。

从PATHS字段指定的路径中查找。

 

ztenv 认证博客专家 C++ Python Linux 吃遍深圳,踏遍鹏城;不仅喜欢C++、Python、golang、java、linux;喜欢美食的可以约起来,喜欢自驾游的可以约起来,喜欢游戏的也可以约起来, 。
最新回复(0)