Cmake学习

CMake是一个开源的、跨平台的自动化构建工具,通过cmake我们可以轻松地管理我们的项目。

注意:
CMake并不是包管理工具!
CMake并不只支持C/C++!
掌握CMake是学习C++必经之路!

CMake与Makefile、Make的关系

  • Makefile并不跨平台,CMake会根据编译器的类型来决定是否生成Makefile,大多数情况下CMake会生成Makefile

  • 大型项目不推荐大家手动编写Makefile

  • Make工具(类似批处理工具)是通过调用makefile文件中的命令实现编译和链接的

CMake流程图

CMake 命令行执行流程

  • 1.编写CMakeLists.txt文件,下面是最基本的配置

    • cmake_minimum required(VERSION 3.20)#最小版本。
    • project(Hello)#项目名
    • add_executable(Hello hello.cpp)#由源文件生成一个可执行的程序。
  • 2.cmake -B build

    • 创建一个build并在此目录下生成makefile或其他文件。
  • 3.cmake –build build

    • 生成项目。

CMake Language概述

  • CMake项目是基于CMakeLists.txt构建的,在CMakeLists.txt中(或是*.cmake)我们用到的就是CMake Language

  • CMake Language的语法非常像一些命令式编程语言

  • 执行从源树(CMakeLists.txt)的根文件文件开始

cmake命令行工具是由五个可执行文件构成

  • cmake
  • ctest
  • cpack
  • cmake-gui
  • ccmake

如何不通过CMakeLists.txt,运行CMake

  • cmake -P *.cmake

  • 以上用法很少在项目中用到,但适合学习CMake语法

变量操作 set、list

  • CMake中的变量分为两种

    • CMake提供
    • 自定义
  • CMake变量的命名区分大小写

  • CMake中的变量在存储时都是字符串

  • CMake获取变量:${变量名}

  • 变量的基础操作是set()与unset(),但你也可以用list或是string操作变量

Set
  • set(… [PARENT SCOPE])

  • set可以给一个变量设置多个值

  • 变量内部存储时使用”:”分割,但显示时只进行连接处理

List
  • list(APPEND [..])列表添加元素

  • list(REMOVEITEM [value..]) 列表删除元素

  • list(LENGTH )获取列表元素个数

  • list(FIND )在列表中查找元素返回索引

  • list(INSERT […])在index位置插入

  • list(REVERSE )反转list

  • list(SORT […])排序list

流程控制

  • if条件流程控制

  • loop 循环流程控制

    • break
    • continue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
cmake_minimum_required(VERSION 3.20)

set(VARBOOL TRUE)

if(VARBOOL)
message(TRUE)
else()
message(FALSE)
endif()

if(NOT VARBOOL)
message(TRUE)
else()
message(FALSE)
endif()

if(1 LESS 2)
message("1 LESS 2")
endif()

if("ok" LESS 233)
message("OK is less")
endif()

foreach(var RANGE 3)
message(${var})
endforeach()

函数

  • 定义函数的语法
    function( […])
    endfunction()
1
2
3
4
5
6
7
8
9
10
11
12
13
function(MyFunc FirstArg)
message("MyFunc Name: ${CMAKE_CURRENT_FUNCTION}")
message("FirstArg ${FirstArg}")
set(FirstArg "New value" )
message("FirstArg again: ${FirstArg}")
message("ARGVO ${ARGVO}")
message("ARGV1 ${ARGV1}")
message("ARGV2 ${ARGV2}")
endfunction()

set(FirstArg "first value")
MyFunc(FirstArg "value")
message("FirstArg ${FirstArg}")

Scope作用域

CMake 有两种作用域

  • 1.Function scope 函数作用域

  • 2.Directory scope 当从add subdirectory()命令执行嵌套目录中的CMakeLists.txt列表文件
    时,注意父CMakeLists.txt其中的变量可以被子CMakeLists.txt使用

CMake 构建项目的四种方式

  • 直接写入源码路径的方式

    • add executable中直接写入相对路径
    • 在源码中引入头文件时需要写相对路径
  • 调用子目录cmake脚本的方法

    • include方法可以引入子目录中的cmake后缀的配置文件
    • 将配置加入add executable中
  • CMakeLists嵌套(最常见)

    • target include_directories 头文件目录的声明
    • target link libraries 连接库文件
    • add subdirectory 添加子目录
    • add library 生成库文件
      • 默认 STATIC library
  • Object Libraries

    • add library OBJECT
      • Object Library 是一个特殊的库类型,它将目标文件编译成一个库,但不会生成最终的链接文件。这意味着你可以在后续的 add library()或 add executable()命令中,将 Object Library 作为源文件进行链接,从而生成最终的可执行文件或库文件。
    • 将target include directories移入到子CMakeLists中

CMake 如何生成动态库/静态库

  • 静态库
    • 在连接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态连接。对函数库的连接是在编译时完成的!
  • 动态库
    • 动态库不是在编译时被连接到目标代码中,而是运行时是才被载入.
    • 静态库对空间的浪费是巨大的!
动态库的命名

lib<name>.so/dll

静态库的命名

lib<name>.a/lib

命令
  • file()常用于搜索源文件
  • add_library(animal STATIC ${SRC})生成静态库
  • add_library(animal SHARED ${SRC})生成动态库
  • ${LIBRARY_OUTPUT_PATH}导出目录