ardupilot开发 --- cmake 篇

本文介绍了CMake的基本概念,包括CMakeLists.txt的作用,如何创建和配置CMake项目,指定C++标准,使用库和依赖,自定义选项与编译定义,以及安装和选择静态/动态链接库的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一步一莲花祈祷

0. 官方文档

首页:CMake: A Powerful Software Build System
开发文档CMake Reference Documentation

1. 一些概念

  • CMake是构建C++代码的标准;

  • 支持构建多平台的C++代码,如unix,windows,ios…
    在这里插入图片描述

  • 以linux系统为例,make使用Makefile来构建代码,cmake使用CMakeLists.txt来构建Makefile文件,因此,学习cmake就是:(1)学习如何编写 CMakeLists.txt !(2)学习如何使用cmake命令!(有哪些参数?如何使用?)注意:在cmake问世的初期,cmake的功能仅支持为项目生成Makefile文件,编译和安装还需使用make命令,但是cmake自2000年问世发展至今,已经具备编译和安装的功能(其实底层调用了make、msbuild、ninja等构建工具实现构建功能)!

  • cmake允许每个源代码树具有多个构建树:
    在这里插入图片描述

  • CMake支持许多流行的C++IDE系统以及命令行构建工具如:Visual Studio、Xcode、ninja、make和VSCode等;

  • cmake除了可以构建库文件和可执行文件外,CMake还允许在构建时运行任意cmd命令。

  • 尽管CMake支持大写、小写和混合大小写命令,但小写命令是首选

  • CMake的发展历史:https://cmake.org/history/

  • CMake News and Updates

  • Forum、FAQs、Support、Issue、Documentation
    CMake Discourse Forum
    CMake FAQs
    Advanced Support
    Issue Tracker
    CMake Reference Documentation

  • 如何阅读文档
    (1)将不懂的指令或函数放入搜索框中搜索阅读。

       在这里插入图片描述

    (2)有哪些工具行命令?用法?:Command-Line Tools;如cmake、cpack。
    (3)有哪些函数?用法?:cmake-commands(7);如set。
    (4)有哪些变量?用法?:cmake-variables(7);如CMAKE_CXX_STANDARD。

  • 只有包含main函数的xx.cpp才可以被cmake构建成可执行程序!其余的会被构建成库文件 xx.so 或 xx.o 或 xx.a(因操作系统而异)。

  • 构建、编译、链接
    构建 = 编译 + 链接
    构建:Build,指生成可执行文件的全过程,包括预编译、编译、和链接等。
    编译:Compile,将main.cpp, main.h编译成目标文件main.o;将xx.cpp, xx.h编译成库文件xx.so
    链接:Link,将库文件xx.so链接到目标文件main.o得到最终的可执行文件main.exe

  • 构建可执行程序的过程
    在这里插入图片描述

  • 顶层CMakeLists.txt和子层CMakeLists.txt
    cmake的对象是顶层CMakeLists.txt文件。因此要么在顶层CMakeLists.txt所在目录下执行cmake命令,要么执行cmake命令时使用顶层CMakeLists.txt文件所在的绝对或相对路径。

    • 顶层CMakeLists.txt的作用
      • 解决依赖问题:findpakage()
      • 告知cmake要编译哪些cpp源文件
        1)编译默认路径下的源文件,即与顶层CMakeLists.txt同级路径下的源文件。
        2)编译通过add_subdirectory()指定的目录下的源文件。如,add_subdirectory(subdirectoryName)
      • 告知cmake如何编译cpp源文件
        1)将哪些cpp文件构建成可执行文件xx.exe:add_executable()
        2)将哪些cpp文件编译成库文件xx.so:add_library(xxso_name xx1.cpp xx2.cpp),其中xx1.cpp和xx2.cpp可以是相互调用的关系也可以毫无关系。
      • 告知cmake如何将库文件xx.so链接到目标文件main.o以得到最后的可执行文件main.exe
        1)告知cmake该库的.h文件所在:target_include_directories()
        2)告知cmake该库的.so文件所在:target_link_libraries()
      • 其他
        声明项目名称:project()
        声明cmake版本要求:cmake_minimum_required()
        C++版本要求:
        CMAKE_CXX_STANDARD
        CMAKE_CXX_STANDARD_REQUIRED
        set()
    • 子层CMakeLists.txt的作用
      将同级目录中的xx.h, xx.cpp编译成库文件libxx.so。
      add_library(libxx )
  • 在cmake中,[[...]][*[...]*]表示换行符,其中*表示某个任意字符。

  • 在CMake中的路径分隔符总是应当使用/,因为CMake会对字符串中的\转义,CMake在对接VS时会自动处理路径分隔符的替换问题。

  • 在较复杂的项目中,我们可以在不同的子目录下使用多个CMakeLists.txt,在根目录下的CMakeLists.txt是最顶级的,例如可以使用add_subdirectory(source)命令,进入source文件夹,然后自动执行source目录下的CMakeLists.txt,执行完毕后返回上一级,还可以继续前往其它子目录执行相应的CMakeLists.txt。

2. CMakeLists 如何编写?

2.1 创建一个最简单的CMake项目:1 xx.cpp + 1 CMakeLists.txt

  • 使用场景:项目结构 = 1 xx.cpp + 1 CMakeLists.txt

  • 目录结构(文件路径):CMakeLists.txt是否必要与写有main函数的cpp文件同一目录?不一定,但一般一个CMakeLists.txt与它负责的xx.cpp、xx.h文件会放在同一个目录下。如果没放在同一个目录,则在CMakeLists.txt中要写相对路径!

  • 学习目标:语法、变量、命令

  • 最小 CMakeLists.txt:至少包含3行代码(命令)

    • 1)使用CMake_minimum_required() 指定最低CMake版本。
      项目的最高层CMakeLists.txt都必须通过使用CMake_minimum_required() 来指定最低CMake版本。
    • 2)使用 project() 设置项目名称、语言、版本号等。
      每个项目都需要此调用,并且应在cmake_minimum_required()之后立即调用。
    • 3)使用 add_executable() 命令告诉CMake将哪些.cpp构建成可执行程序。
      注意:只有包含main函数的xx.cpp才可以被cmake编译成可执行程序!
  • 如何编写项目的顶层CMakeLists.txt ?
    例子:项目结构 = 1 xx.cpp + 1 CMakeLists.txt
    目录结构(文件路径):CMakeLists.txt 与 xx.cpp 放在同一目录下。
    CMakeLists.txt的写法如下:

    cmake_minimum_required(VERSION 3.10)#cmake的最低版本
    project(Tutorial)#cmake项目名称
    add_executable(Tutorial tutorial.cpp)#将源文件tutorial.cpp编译成可执行程序Tutorial 
    

    注意:只有包含main函数的xx.cpp才可以被cmake编译成可执行程序!add_executable的更多用法请参考:cmake-commands

  • 编译(构建)项目
    写完CMakeLists.txt后如何用cmake命令构建项目?

    • 1)创建一个build目录:
      mkdir Step1_build
      cd Step1_build
    • 2)根据CMakeLists.txt构建Makefile:
      格式:cmake <顶层CMakeLists.txt的路径>
      如:cmake ../Step1
      其中../Step1是项目最顶层CMakeLists.txt的所在路径。
    • 3)编译:
      格式: cmake --build <Makefile所在路径>
      如:cmake --build .
      或用 make命令进行编译。
      其中 .是Makefile的所在目录

2.2 指定 C++ 标准

  • 学习目标:1. cmake变量;2. 使用set函数设置cmake变量;
  • cmake中的变量:CMake中的系统变量如CMAKE_CXX_STANDARDCMAKE_CXX _STANDARD_REQUIRED可以用于指定构建项目所需的C++标准。
    CMake机制中许多变量以CMAKE_开头。
  • 使用场景:某.cpp文件中使用了C++11的语法,CMakeLists.txt 中必须要指定构建程序时使用C++11标准。C++其他版本则同理。
  • 涉及命令:
    CMAKE_CXX_STANDARD
    CMAKE_CXX_STANDARD_REQUIRED
    set()
  • CMakeLists.txt的写法:
    在add_executable()之前添加代码:
    set(CMAKE_CXX_STANDARD 11)
    set(CMAKE_CXX_STANDARD_REQUIRED True)
  • 编译方法同理。

2.3 显示编译版本号

  • 使用场景:有时,在源代码中希望可以使用CMakelists.txt文件中定义的变量。例如,我们希望根据每次编译来作为项目版本的依据。
  • 涉及命令
    <PROJECT-NAME>_VERSION_MAJOR
    <PROJECT-NAME>_VERSION_MINOR
    configure_file()
    target_include_directories()
  • 其本质是.cpp文件与cmake变量之间的交互!
  • 非重点,待续…
  • 详细请参考:Exercise 3 - Adding a Version Number and Configured Header File

2.4 使用库来构建可执行程序

  • 参考:Adding a Library
  • 使用场景:一个项目,包含多个目录、子目录,源文件被放在不同的目录中,顶层的cpp代码引用了子层的cpp,或互引用。
  • 说明:这种场景下,包含main函数的cpp所在目录即项目的顶层目录,顶层目录拥有一个CMakeLists.txt文件,称为顶层CMakeLists,其他层级的目录也拥有属于自己的CMakeLists.txt文件,称为子层CMakeLists,它们文件名相同但路径不同!功能上也略有不同。
  • 子层CMakeLists在构建程序时会将该层级的cpp编译成库文件xx.o(linux平台),顶层CMakeLists负责将整个项目代码编译成最后的可执行程序,在顶层CMakeLists文件中,使用add_subdirectory命令将子目录添加到构建中,最后通过target_include_directoriestarget_link_libraries将库文件xx.o 链接(link) 到最后的可执行程序。
  • 涉及命令:
    add_library()
    add_subdirectory()
    target_include_directories()
    target_link_libraries()
    PROJECT_SOURCE_DIR
  • 例子:
    顶层目录: 1 CMakeLists.txt + 1 tutorial.cpp(入口函数main所在的cpp)
    库目录(顶层目录的子目录):CMakeLists.txt + 2 xx.cpp + 2 xx.h
    调用关系:顶层目录中的代码调用了库目录中的代码(函数)。
    子层CMakeLists写法:
    1)创建一个名为MathFunctions的库文件(编译结果应该是MathFunctions.o),库文件包含这些MathFunctions.cxx mysqrt.cxx源文件的代码。
    add_library(MathFunctions MathFunctions.cxx mysqrt.cxx)
    顶层CMakeLists写法:
    1)添加要被编译成库的子目录(名称):
    add_subdirectory(MathFunctions)
    2)将库链接到可执行程序:
    target_link_libraries(Tutorial PUBLIC MathFunctions)
    3)使用这些库的必要依赖信息(指定库的头文件位置):
target_include_directories(Tutorial PUBLIC
                          "${PROJECT_SOURCE_DIR}/MathFunctions"
                          )

如果链接的库比较多时,还可以这样写:

# 添加要被编译成库都的子目录
add_subdirectory(MathFunctions1)
add_subdirectory(MathFunctions2)
list(APPEND EXTRA_INCLUDES1 "${PROJECT_SOURCE_DIR}/MathFunctions1")
list(APPEND EXTRA_INCLUDES2 "${PROJECT_SOURCE_DIR}/MathFunctions2")
target_link_libraries(Tutorial PUBLIC MathFunctions1)
target_link_libraries(Tutorial PUBLIC MathFunctions2)
target_include_directories(Tutorial PUBLIC
                           "${PROJECT_BINARY_DIR}"
                           ${EXTRA_INCLUDES1}
                           ${EXTRA_INCLUDES2}
                           )
  • 完整的CMakeLists

顶层CMakeLists:

cmake_minimum_required(VERSION 3.10)
# set the project name and version
project(Tutorial VERSION 1.0)
# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# configure a header file to pass some of the CMake settings
# to the source code
configure_file(TutorialConfig.h.in TutorialConfig.h)
# TODO 2: Use add_subdirectory() to add MathFunctions to this project
add_subdirectory(MathFunctions)
# add the executable
add_executable(Tutorial tutorial.cxx)
# TODO 3: Use target_link_libraries to link the library to our executable
target_link_libraries(Tutorial PUBLIC MathFunctions)
# TODO 4: Add MathFunctions to Tutorial's target_include_directories()
# Hint: ${PROJECT_SOURCE_DIR} is a path to the project source. AKA This folder!
# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
target_include_directories(Tutorial PUBLIC
                           "${PROJECT_BINARY_DIR}"
                           "${PROJECT_SOURCE_DIR}/MathFunctions"
                           )

子层CMakeLists:

# TODO 1: Add a library called MathFunctions with sources MathFunctions.cxx
# and mysqrt.cxx
# Hint: You will need the add_library command
add_library(MathFunctions MathFunctions.cxx mysqrt.cxx)

2.5 cmake变量、编译定义(宏定义)

  • 使用场景:在使用cmake编译代码时需要输入自定义选项来控制代码块的选择性编译,如cmake -DTHIS_MYOPTION=ON
  • 涉及命令
    if()
    option()
    target_compile_definitions()
  • 学习目标:
    • 添加cmake自定义选项
      option(USE_MYOPTION "my diy option" ON)
      什么是自定义选项?cmake -DUSE_MYOPTION =ON中的 -DUSE_MYOPTION就是自定义选项!其中选项的默认前缀为-D
    • 添加编译定义
      target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
      什么是编译定义?原名词为 compile definition,可以理解为编译某块代码的使能,如 xx.cpp、xx.h 中:
      #ifdef MY_ COMPILE_DEFINITION
      #else
      #endif

      其中的 MY_ COMPILE_DEFINITION 就是编译定义(compile definition)!!
    • 条件编译语句
      如 xx.cpp、xx.h 中:
      #ifdef USE_MYMATH
      #else
      #endif
  • 用法、语法
    • 添加自定义选项
      对于cmake命令行工具来说是选项;对于CMakeLists.txt来说是变量
      1)在CMakeLists.txt中添加一个变量:option(USE_MYOPTION "my diy option" ON)
      2)该变量USE_MYOPTION的赋值可以通过命令行选项的形式输入,如cmake -DUSE_MYOPTION =OFF
      3)然后就可以在同一个CMakeLists.txt中使用该变量(选项),如
      if (USE_MYMATH)
      target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
      endif()
    • 为库添加编译定义
      在xx.cpp、xx.h中,除了可以用#define来添加(声明)编译定义之外,还可以在CMakeLists.txt中用target_compile_definitions添加,如:
      target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
      其中第一个参数的含义是要为哪个target添加,target可以是库(Library),也可以是可执行程序;第二个参数是作用域;第三个参数是编译定义的名称,并且"USE_MYMATH"与USE_MYMATH、-DUSE_MYMATH都会被认定为名称为USE_MYMATH !

2.6 库的使用依赖(usage requirement)

  • usage requirement 表示使用某个库时必须要包含的依赖(如:库的源码路径等信息)。
  • 解决的痛点:
    在之前,任何想要链接到某个库如MathFunctions的target都需要被告知该库的源码路径信息,例如可执行程序Tutorial要链接库MathFunctions,在顶层CMakeLists.txt中必须包含这些代码:
target_include_directories(Tutorial PUBLIC
                           "${PROJECT_BINARY_DIR}"
                           "${PROJECT_SOURCE_DIR}/MathFunctions"
                           )

其中${PROJECT_BINARY_DIR}是可执行程序Tutorial的源码目录,${PROJECT_SOURCE_DIR}/MathFunctions是库MathFunctions的源码目录;
为了节省这些不必要的源码路径声明,可以使用INTERFACE依赖项来实现!

  • 用法
    • 为库添加依赖项(usage requirement)
      1)为库添加 INTERFACE usage requirement
      在该库的CMakeLists.txt中添加:
      target_include_directories(MathFunctions
        INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
      )
      其中,CMAKE_CURRENT_SOURCE_DIR是该库所在的绝对路径。
      这样就为该库指定了一个INTERFACE usage requirement。
      INTERFACE usage requirement 表示调用者需要而库本身不必须得东西。
      usage requirement 被称为“使用依赖”,而 INTERFACE 是其中一项。
      2)在调用层的 CMakeLists.txt 删除该库的依赖信息
      list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
      target_include_directories(Tutorial PUBLIC
        ${EXTRA_INCLUDES}
      )
      或:
      target_include_directories(Tutorial PUBLIC
        "${PROJECT_SOURCE_DIR}/MathFunctions"
      )
      3)这样,在调用层的CMakeLists.txt仅这几行代码,需要就可以实现对某库的调用(链接):
      add_subdirectory(MathFunctions)
      target_link_libraries(Tutorial PUBLIC MathFunctions)

2.7 为库指定C++标准

  • 使用场景:为不同的库指定不同的C++版本。
  • 技术路线:使用接口库(interface library)。
  • 用法
    • (1)在顶层CMakeLists.txt中,删除全局配置:
      set(CMAKE_CXX_STANDARD 11)
      set(CMAKE_CXX_STANDARD_REQUIRED True)
    • (2)创建一个接口库,并为接口库添加特性(指定C++版本)
      add_library(tutorial_compiler_flags INTERFACE)
      target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)
    • (3)将可执行程序、库、接口库链接到一起
      target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags)
      target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)
      target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)

2.8 添加生成器表达式

2.9 Installing and Testing

  • 使用场景:通常,仅仅构建一个可执行文件是不够的,它还应该是可安装的。使用CMake,我们可以使用 install 命令指定安装规则。在CMake中支持构建的本地安装通常很简单,只需指定安装位置以及要安装的目标和文件即可。
  • 学习目标:安装可执行程序Tutorial 以及库 MathFunctions
  • 涉及语法:cmake --install <dir-build>make install
  • 条件:cmake3.15或以上版本;老版本的安装只支持make install
  • 关于安装路径
    可以通过设置变量 CMAKE_INSTALL_PREFIX 来指定安装路径,也可用--prefix选项来指定,如:cmake --install ./ --prefix "/home/myuser/installdir", --prefix指定的目录将会覆盖CMAKE_INSTALL_PREFIX的值成为最终的安装路径!
  • 待续…
  • 参考:Installing and Testing

2.10 Selecting Static or Shared Libraries

2.11 Selecting Static or Shared Libraries

待续…

3. cmake命令如何使用?

参考

cmake
DeepSeek

功能

  • Generate a Project Buildsystem
    cmake [<options>] -B <path-to-build> [-S <path-to-source>]
    cmake [<options>] <path-to-source | path-to-existing-build>
  • Build a Project
    cmake --build <dir> [<options>] [-- <build-tool-options>]
  • Install a Project
    cmake --install <dir> [<options>]
  • Open a Project
    cmake --open <dir>
  • Run a Script
    cmake [-D <var>=<value>]... -P <cmake-script-file>
  • Run a Command-Line Tool
    cmake -E <command> [<options>]
  • Run the Find-Package Tool
    cmake --find-package [<options>]
  • Run a Workflow Preset
    cmake --workflow [<options>]
  • View Help
    cmake --help[-<topic>]
  • 什么是Project Buildsystem
    例如,Project Buildsystem可以是与make一起使用的Makefile文件,也可以是某IDE的项目文件,总之,它描述了如何使用构建工具(如make)从源代码编译成最终的可执行文件或可被调用的库文件。

标准语法

cmake命令的所有可用选项可在这 cmake-options 查阅,因cmake版本而异。注意:cmake options 分为:(1)Generate 阶段的option;(2)build阶段的option;(3)install阶段的option。使用顺序不同会影响构建结果!

cmake命令的执行可以分为3个阶段(步骤):


1)Generate 阶段(配置阶段)
参考:Generate a Project Buildsystem
实现:项目配置、依赖检测与安装、生成makefile文件以供后续阶段调用!

阶段1常用语法:cmake [其他选项]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值