CMake之外部依赖


我们承担ROS,FastDDS等通信中间件,C++,cmake等技术的项目开发和专业指导和培训,有10年+相关工作经验,质量有保证,如有需要请私信联系。

本文主要记录通过CMake中的find家族命令加载外部依赖,通过execute_process执行外部程序,以及相关CMake示例代码,了解CMake时怎样加载和运行外部对象。

find族命令

find_file:在相应路径下查找命名文件
find_library:查找一个库文件
find_package:从外部项目查找和加载设置
find_path:查找包含指定文件的目录
find_program:找到一个可执行程序

find_package

用于发现和设置包的CMake模块的命令。这些模块包含CMake命令,用于标识系统标准位置中的包。CMake模块文件称为Find<name>.cmake,当调用find_package(<name>)时,模块中的命令将会运行。

基本签名

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

<PackageName>_FOUND会被设置用来表示该package有没有找到。

  • 可选参数说明:
    • QUIET:不会有信息输出,如果没有加上关键字参数REQUIRED,包没有被发现,也不会有信息输出
    • REQUIRED:包没有查找到,会输出一条错误信息并停止编译
    • version:版本参数要求查找到的包版本要符合
    • EXACT :要求版本必须匹配。
    • COMPONENTS:要求的组件列表可以在该参数之后指定。
    • OPTIONAL_COMPONENTS :额外可选的组件可以在该参数之后指定TODO
  • 这个命令分为Module模式和Config模式。如果没有给关键字参数MODULE,module没有被发现将自动回退到Config模式。
    • MODULE模式下,cmake会查找 Find<PackageName>.cmake,优先在 CMAKE_MODULE_PATH 路径中查找。查找模块还会设置一些有用的变量,反映实际找到了什么,可以在CMakeLists.txt中使用这些变量,这些变量为:
      • <PackageName>_FOUND 是否发现该库
      • <PackageName>_INCLUDE_DIR or <PackageName>_INCLUDES 头文件目录
      • <PackageName>_LIBRARY<PackageName>_LIBRARIES 库文件
    • 如果没有指定可选的关键字参数MODULE,cmake将首先使用Module模式,如果没找到才会使用Config模式去查找。可以通过将变量 CMAKE_FIND_PACKAGE_PREFER_CONFIG 设置为TRUE来直接使用Config模式。在Config模式下,CMake会搜索名为<package>Config.cmake<package>-config.cmake文件

查找路径

cmake构造了以下查找路径:

<prefix>/                                                       (W)
<prefix>/(cmake|CMake)/                                         (W)
<prefix>/<name>*/                                               (W)
<prefix>/<name>*/(cmake|CMake)/                                 (W)
<prefix>/(lib/<arch>|lib*|share)/cmake/<name>*/                 (U)
<prefix>/(lib/<arch>|lib*|share)/<name>*/                       (U)
<prefix>/(lib/<arch>|lib*|share)/<name>*/(cmake|CMake)/         (U)
<prefix>/<name>*/(lib/<arch>|lib*|share)/cmake/<name>*/         (W/U)
<prefix>/<name>*/(lib/<arch>|lib*|share)/<name>*/               (W/U)
<prefix>/<name>*/(lib/<arch>|lib*|share)/<name>*/(cmake|CMake)/ (W/U)

find_program

find_program (
          <VAR>
          name | NAMES name1 [name2 ...] [NAMES_PER_DIR] # 指定一个或多个可能的名字
          [HINTS path1 [path2 ... ENV var]]
          [PATHS path1 [path2 ... ENV var]]
          [PATH_SUFFIXES suffix1 [suffix2 ...]]
          [DOC "cache documentation string"]
          [REQUIRED]
          [NO_DEFAULT_PATH]
          [NO_PACKAGE_ROOT_PATH]
          [NO_CMAKE_PATH]
          [NO_CMAKE_ENVIRONMENT_PATH]
          [NO_SYSTEM_ENVIRONMENT_PATH]
          [NO_CMAKE_SYSTEM_PATH]
          [CMAKE_FIND_ROOT_PATH_BOTH |
           ONLY_CMAKE_FIND_ROOT_PATH |
           NO_CMAKE_FIND_ROOT_PATH]
         )

发现程序的命令。VAR是一个缓存变量,用于保存查找到的结果

execute_process

execute_process(COMMAND <cmd1> [<arguments>]       # 要启用的子进程
                [COMMAND <cmd2> [<arguments>]]...
                [WORKING_DIRECTORY <directory>]    # 子进程的目录
                [TIMEOUT <seconds>]  # 子进程超时时长,超时后没结束的子进程将被终止,RESULT_VARIABLE会被设置为"timeout"
                [RESULT_VARIABLE <variable>]  # 子进程的执行结果,是一个整数的错误码或者描述错误信息的字符串。子进程执行成功返回0?
                [RESULTS_VARIABLE <variable>]
                [OUTPUT_VARIABLE <variable>]  # 存储标准输出的内容
                [ERROR_VARIABLE <variable>]
                [INPUT_FILE <file>]
                [OUTPUT_FILE <file>]
                [ERROR_FILE <file>]
                [OUTPUT_QUIET]
                [ERROR_QUIET]
                [COMMAND_ECHO <where>]
                [OUTPUT_STRIP_TRAILING_WHITESPACE]
                [ERROR_STRIP_TRAILING_WHITESPACE]
                [ENCODING <name>]
                [ECHO_OUTPUT_VARIABLE]
                [ECHO_ERROR_VARIABLE]
                [COMMAND_ERROR_IS_FATAL <ANY|LAST>])

将从当前正在执行的CMake进程中派生一个或多个子进程,从而提供了在配置项目时运行任意命令的方法。可以在一次调用execute_process时执行多个命令,但注意,每个命令的输出将通过管道传输到下一个命令中

检测外部程序

CMake官方为用户定义了很多外部依赖包

检测并执行外部程序

  • 查找python并使用python解释器执行
find_package(Python3 REQUIRED)
execute_process(
    COMMAND ${Python3_EXECUTABLE} "-c" "print('hello, world')"
    RESULT_VARIABLE _status
    OUTPUT_VARIABLE _hello_world
    ERROR_QUIET
    OUTPUT_STRIP_TRAILING_WHITESPACE
)
include(CMakePrintHelpers)
cmake_print_variables(_status _hello_world)

输出如下:
在这里插入图片描述

  • 查找python库并在C代码中调用python
    在我的Ubuntu中路径为/usr/share/cmake-3.22/Modules/FindPythonLibs.cmake。以下示例在C++程序中调用python库函数链接编译,用python解释器执行一条打印:
find_package(Python3 COMPONENTS Interpreter Development)
add_executable(hello-embedded-python hello-embedded-python.cpp)
target_include_directories(
    hello-embedded-python
    PRIVATE
    ${Python3_INCLUDE_DIRS}
)
target_link_libraries(
    hello-embedded-python
    PRIVATE
    ${Python3_LIBRARIES}
)

C代码为

#include <Python.h>
int main(int argc, char_t *argv[]) {
  Py_SetProgramName(argv[0]); /* optional but recommended */
  Py_Initialize();
  PyRun_SimpleString("from time import time,ctime\n"
                     "print('Today is',ctime(time()))\n");
  Py_Finalize();
  return 0;
}

通过 find_package(<name>) 命令查找包,除了查了包之外还会设置一些有用的变量,直接在CMakeLists.txt中使用这些变量。
软件包没有安装在标准位置时,CMake无法正确定位,用户可以使用-D参数传递相应的选项,告诉CMake查看特定的位置。如:cmake -D Python3_EXECUTABLE=/custom/location/python

FetchContent

FetchContent是CMake中的一个模块,它提供了一种在构建过程中下载和填充项目源代码的方法。FetchContent模块在CMake 3.11版本中首次引入,它的主要应用场景是下载和使用项目依赖项。
FetchContent的工作原理是,你可以在CMake脚本中定义需要下载的内容的详细信息(包括URL,GIT仓库,文件哈希等),然后FetchContent会在构建过程中下载这些文件,并将它们加入到你的项目源码中。

函数

FetchContent主要有四个函数:
FetchContent_Declare:用于声明要下载内容的详细信息。
FetchContent_GetProperties:用于查询已下载内容的详细信息。
FetchContent_Populate:用于实际下载和填充内容。这通常在配置期间完成,这样生成的构建文件可以包含下载的内容。
FetchContent_MakeAvailable:这是CMake 3.14版本中添加的新功能,它同时执行FetchContent_GetPropertiesFetchContent_Populate

构建时下载googletest为例

include(FetchContent)
FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG origin/main
)

FetchContent_MakeAvailable(googletest)

在链接时可以直接使用gtest_main等库了

add_executable(${TARGET_NAME})
target_link_libraries(
    ${TARGET_NAME}
    PUBLIC
    gtest_main
)

具体使用googletest构建UT的示例参考CMake之创建和运行测试

ExternalProject

官方文档
这个模块可以在当前系统中下载,配置,构建并安装其他的CMake项目或者其他构建系统的项目。
示例:

include(ExternalProject)
ExternalProject_Add(foobar
  GIT_REPOSITORY    git@github.com:FooCo/FooBar.git
  GIT_TAG           origin/release/1.2.3
)

ExternalProject_Add 和 FetchContent比较

ExternalProject_AddFetchContent 都是 CMake 中用于处理外部依赖的模块,但是它们的工作方式有一些不同。

ExternalProject_Add:该命令允许在构建过程中下载,配置,构建并安装外部项目。它在主构建过程的一个固定点(经常是构建过程的一开始)下载和安装外部项目,然后在主项目中使用find_package或者其他方式来查找和使用这些已经安装的外部项目。

FetchContent:这是一个相对较新的命令,首次出现在 CMake 3.11 中,它允许在主构建系统的配置阶段下载外部内容。然后可以使用add_subdirectory命令将这些下载的源代码作为主项目的一部分进行编译和链接。这意味着不需要预先安装外部依赖,也不需要使用find_package或其他方式来查找这些依赖。

总的来说,FetchContent 提供了一种更简洁、直接的方式来处理外部依赖,它可以使项目结构更简单,构建过程更快。而 ExternalProject_Add 提供了更多的灵活性,例如可以处理更复杂的构建和安装过程。可以根据项目的具体需求来选择使用哪一个。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值