Cmkae external dependency management

1. Introduction to cmake dependency management

Insert image description here

CMake is a cross-platform build system that supports C/C++, Objective-C, Fortran and other languages. CMake provides rich dependency management functions that can help developers easily manage dependencies of third-party libraries.

CMake’s dependency management functions mainly include the following points:

  • Supports multiple dependency acquisition methods: CMake supports Git Clone, downloading source code compression packages and other methods to obtain third-party libraries.
  • Support duplicate dependency processing: CMake can automatically handle duplicate dependencies that exist in the dependency tree.
  • Support Find scripts for third-party libraries: CMake officially provides Find scripts for many commonly used third-party libraries, which can help developers quickly obtain and configure these libraries.

CMake's dependency management functions can be divided into two types:

  • Source code dependency: This dependency refers to the source code of the third-party library. CMake can use Git Clone or download the source code compressed package to obtain the source code of the third-party library.
  • Binary dependency: This dependency refers to the binary file of the third-party library. CMake can use the third-party library's package manager (such as Conda or Homebrew) to obtain the binaries of the third-party library.

Today we will mainly introduce: Source code management

2. Source code management

  1. The commands that come with cmake:FetchContent
  2. Commands supported by the cmake third-party community:CPM
  3. gitsubmodule

How to choose which method for dependency management:
If your dependency is not a cmake project, it is recommended to use git submoduleManage
If your dependent project is the cmake project, then FetchContent and CPMBoth can meet the needs, butCPM is more elegant, so it is more recommended

1. FetchContent

UsingFetchContent is very simple and requires only three steps:

  1. Introduce header files
  2. Define the address where the code needs to be downloaded and some other attributes for dependencies
  3. enable dependency

for example:

# 从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)

The FetchContent_Declare command generates the following variables:

  1. <lowercaseName>_POPULATED: Set asTRUE
  2. <lowercaseName>_SOURCE_DIR: Set the location of dependent downloads
  3. <lowercaseName>_BINARY_DIR: Set the location of dependent builds

When setting some information using keywords to define dependent sources, there are some settings available:

# 从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)

In some cases, if we want to have more granular control over dependency management, we can:

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

Use this method to download and install dependencies. This method depends onFetchContent_Declare() and declare dependencies in advance.

However, downloading dependencies will only be downloaded once. If called twice, it will stop due to an error.

So before calling FetchContent_Populate, the project should first call the FetchContent_GetProperties() method to obtain the current status

When using saved content details, calls to FetchContent_MakeAvailable() or FetchContent_Populate() record information in global properties that can be queried at any time. This information can include the source and binary directories associated with the content, and whether content population has been processed during the current configuration run.

Integrate with 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)

ForCatch2 there is no need to provide additional parameters for find_package() so no need to provide after FIND_PACKAGE_ARGS keyword additional parameters. For googletest , its package is usually called GTest , so a parameter is added to support finding it by that name.

OVERRIDE_FIND_PACKAGE: find_package should only use the information in FetchContent_Declare
FIND_PACKAGE_ARGS: If find_package successfully finds the binary file locally, the information in FetchContent_Declare is not needed.
SOURCE_SUBDIR cmake: Specify the address of the dependent project cmakelist file. This instance is configured in the cmake folder under the project and path.
SOURCE_DIR firmware: Specify the path where dependencies are stored after downloading them. This setting is configured in the firmware folder under the project and path.

2. CPM

Unofficial cmake dependency management tool: CPM GitHub

Usage: Go directly to the release page, download the cmake script file, and import it into your own code.

image-20231203103400676

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

3. git submodule

cmake demo

If the dependency is a cmake project, it is not recommended to use this method and encapsulate the method into a function. You can refer to:


# 函数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)
  • Instructions:
# 设置cmake模块的路径
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/")
# 引入cmake的模块(xx.txt, xx就是模块名字)
include(AddGitSubmodule)

add_git_submodule(external/json)

Additional: address_sanitizer and 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)

Guess you like

Origin blog.csdn.net/sexyluna/article/details/134769920