Cmkae外部依赖管理

一、cmake依赖管理介绍

在这里插入图片描述

CMake 是跨平台的构建系统,支持 C/C++、Objective-C、Fortran 等多种语言。CMake 提供了丰富的依赖管理功能,可以帮助开发人员轻松地管理第三方库的依赖关系。

CMake 的依赖管理功能主要包括以下几点:

  • 支持多种依赖获取方式:CMake 支持 Git Clone、下载源码压缩包等多种方式获取第三方库。
  • 支持重复依赖处理:CMake 可以自动处理依赖树中存在的重复依赖。
  • 支持第三方库的 Find 脚本:CMake 官方为许多常用的第三方库提供了 Find 脚本,可以帮助开发人员快速地获取和配置这些库。

CMake 的依赖管理功能可以分为两种类型:

  • 源码依赖:这种依赖是指第三方库的源代码。CMake 可以使用 Git Clone 或下载源码压缩包的方式获取第三方库的源代码。
  • 二进制依赖:这种依赖是指第三方库的二进制文件。CMake 可以使用第三方库的包管理器(例如 Conda 或 Homebrew)来获取第三方库的二进制文件。

今天我们就主要介绍: 源码管理

二、源码依管理

  1. cmake自带的命令:FetchContent
  2. cmake三方社区支持的命令: CPM
  3. git的submodule

该如何选择何种方式作为依赖管理:
如果你的依赖不是cmake项目,那就推荐使用git submodule进行管理
如果你的依赖项目是cmake项目,那FetchContentCPM都能满足需要,但是CPM更为优雅,所以更加推荐

1. FetchContent

使用FetchContent是非常简单的,只需要三步:

  1. 引入头文件
  2. 定义依赖需要下载代码的地址和一些其它属性
  3. enable依赖

比如:

# 从GitHub上下载代码
FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG        703bd9caab50b139428cea1aaff9974ebee5742e # release-1.10.0
)

# 从网址下载代码
FetchContent_Declare(
  myCompanyIcons
  URL      https://intranet.mycompany.com/assets/iconset_1.12.tar.gz
  URL_HASH MD5=5588a7b18261c20068beabfb4f530b87
)
# 确保依赖项在这里已经完成下载和构建
FetchContent_MakeAvailable(googletest myCompanyIcons)

FetchContent_Declare命令会生成以下变量:

  1. <lowercaseName>_POPULATED: 设置为TRUE
  2. <lowercaseName>_SOURCE_DIR: 设置依赖下载的位置
  3. <lowercaseName>_BINARY_DIR: 设置依赖构建的位置

在设置使用关键字定义依赖源的一些信息时候,有一些可供选择的设置:

# 从GitHub上下载代码
FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG        703bd9caab50b139428cea1aaff9974ebee5742e # release-1.10.0
  
  # 先通过find_package命令,通过name Gtest来找库,找不到就调用Fetch下载依赖
  # FIND_PACKAGE_ARGS NAMES GTest
  
  # find_package不能从本地找库,要用Fetch下载下来的库
  # OVERRIDE_FIND_PACKAGE
  
  # 该依赖的cmakelist配置文件是在该项目cmake文件夹内。
  # SOURCE_SUBDIR  cmake
  
  # 设置下载的依赖存放路径
  # SOURCE_DIR firmware
  
  # 是否启用浅克隆,(只会克隆当前分支或标签的提交,以及这些提交所引用的所有父提交)
  # GIT_SHALLOW
)
# 确保依赖项在这里已经完成下载和构建
FetchContent_MakeAvailable(googletest myCompanyIcons)

有些情况下,想要对依赖的管理控制的颗粒度更加精细,我们可以:

# 检查是否依赖已经下载
FetchContent_GetProperties(depname)
# 如果还没有下载
if(NOT depname_POPULATED)
		# 执行下载
		FetchContent_Populate(depname	)
endif()

使用该方法进行下载安装依赖,该方法依赖FetchContent_Declare() 提前声明依赖。

但是下载依赖只会下载一次,如果二次调用则会因为错误而停止,

所以项目在调用FetchContent_Populate之前,应该先调用FetchContent_GetProperties()方法,获得当前的状态

使用保存的内容详细信息时,对 FetchContent_MakeAvailable()FetchContent_Populate() 的调用会在全局属性中记录可以随时查询的信息。该信息可以包括与内容相关联的源目录和二进制目录,以及内容填充是否已在当前配置运行期间被处理。

与find_package进行集成

include(FetchContent)
FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG        703bd9caab50b139428cea1aaff9974ebee5742e # release-1.10.0
  FIND_PACKAGE_ARGS NAMES GTest
)
FetchContent_Declare(
  Catch2
  GIT_REPOSITORY https://github.com/catchorg/Catch2.git
  GIT_TAG        605a34765aa5d5ecbf476b4598a862ada971b0cc # v3.0.1
  FIND_PACKAGE_ARGS
)

# This will try calling find_package() first for both dependencies
FetchContent_MakeAvailable(googletest Catch2)

对于 Catch2 ,不需要为 find_package() 提供额外的参数,因此在 FIND_PACKAGE_ARGS 关键字之后不需要提供额外的参数。对于 googletest ,其包通常称为 GTest ,因此添加参数以支持通过该名称找到它。

OVERRIDE_FIND_PACKAGE : find_package应该只使用FetchContent_Declare里的信息
FIND_PACKAGE_ARGS: 如果find_package成功在本地找到二进制文件,则不需要FetchContent_Declare里的信息。
SOURCE_SUBDIR cmake: 指定依赖的项目cmakelist文件的地址, 该实例配置在项目跟路径下的cmake文件夹。
SOURCE_DIR firmware:指定下载依赖后存放的路径,该设置配置在项目跟路径下的firmware 文件夹

2. CPM

非官方的cmake依赖管理工具: CPM GitHub

使用方法: 直接到release页面,将cmake脚本文件下载,导入到自己的代码中。

image-20231203103400676

include(CPM)
# 使用GitHub用户nlohmann的仓库的json仓的3.11.2版本代码
cpmaddpackage("gh:nlohmann/json#v3.11.2")
  1. gh: : github
  2. nlohmann: 用户名
  3. json: 仓库名
  4. #v3.11.2: tag版本

3. git submodule

cmake demo

如果依赖是cmake项目,不太推荐使用该方法,将该方法封装为一个函数。可以参考:


# 函数add_git_submodule, 接受一个参数dir
function(add_git_submodule dir)
		# 依赖Git
    find_package(Git REQUIRED)

		# 如果指定的文件夹不存在,则调用git submodule的方法拉取最新代码
    if (NOT EXISTS ${CMAKE_SOURCE_DIR}/${dir}/CMakeLists.txt)
        execute_process(COMMAND ${GIT_EXECUTABLE}
            submodule update --init --recursive -- ${CMAKE_SOURCE_DIR}/${dir}
            WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
    endif()

		# 拉取下来后将项目进行编译
    if (EXISTS ${CMAKE_SOURCE_DIR}/${dir}/CMakeLists.txt)
        message("Adding: ${dir}/CMakeLists.txt")
        add_subdirectory(${CMAKE_SOURCE_DIR}/${dir})
    else()
        message("Could not add: ${dir}/CMakeLists.txt")
    endif()
endfunction(add_git_submodule)
  • 使用方法:
# 设置cmake模块的路径
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/")
# 引入cmake的模块(xx.txt, xx就是模块名字)
include(AddGitSubmodule)

add_git_submodule(external/json)

附加: address_sanitizer 和 undefined sanitizer

option(ENABLE_SANITIZE_ADDR "Enable address sanitizer" ON)
option(ENABLE_SANITIZE_UNDEF "Enable undefined sanitizer" ON)

function(add_sanitizer_flags)
    if (NOT ENABLE_SANITIZE_ADDR AND NOT ENABLE_SANITIZE_UNDEF)
        message(STATUS "Sanitizers deactivated.")
        return()
    endif()

    if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU")
        add_compile_options("-fno-omit-frame-pointer")
        add_link_options("-fno-omit-frame-pointer")

        if(ENABLE_SANITIZE_ADDR)
            add_compile_options("-fsanitize=address")
            add_link_options("-fsanitize=address")
        endif()

        if(ENABLE_SANITIZE_UNDEF)
            add_compile_options("-fsanitize=undefined")
            add_link_options("-fsanitize=undefined")
        endif()
    elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
        if(ENABLE_SANITIZE_ADDR)
            add_compile_options("/fsanitize=address")
        endif()

        if(ENABLE_SANITIZE_UNDEF)
            message(STATUS "Undefined sanitizer not impl. for MSVC!")
        endif()
    else()
        message(STATUS "Sanitizer not supported in this environment!")
    endif()
endfunction(add_sanitizer_flags)

猜你喜欢

转载自blog.csdn.net/sexyluna/article/details/134769920