1.1 为什么需要CMake
你或许听过好几种 Make 工具,例如 GNU Make ,QT 的 QMake ,微软的 MS NMake,BSD PMake,Makepp等等。这些 Make 工具遵循着不同的规范和标准,所执行的 Makefile 格式也千差万别。这样就带来了一个严峻的问题:如果软件想跨平台,必须要保证能够在不同平台编译。而如果使用上面的 Make 工具,就得为每一种标准写一次 Makefile ,这将是一件让人抓狂的工作。
CMake就是为解决此问题而诞生的,CMake的全称是Cross platform Make,正如其名,是一种跨平台的Make工具。
CMake首先需要开发者编写一种平台无关的 CMakeList.txt 文件来定制整个编译流程,然后再根据目标用户的平台进一步生成所需的本地化 Makefile 和工程文件,如 Unix 的 Makefile 或 Windows 的 Visual Studio 工程。从而做到“Write once, run everywhere”。显然,CMake 是一个比上述几种 Make更高级的编译配置工具。
参考 CMake 入门实战 | HaHack
1.2 CMake诞生历史
CMake于1999年开始开发,是为了解决美国国家医学图书馆出资的Visible Human Project项目下的Insight Segmentation and Registration Toolkit(ITK)软件的跨平台建构的需求而创造出来的。
其设计受到了Ken Martin开发的pcmaker所影响,pcmaker当初则是为了支持Visualization Toolkit(VTK)这个开放源代码的三维图形和视觉系统才出现的,今日VTK也采用了CMake。
CMake于2000年首次发布,并于2001年进一步发展。
参考:https://en.wikipedia.org/wiki/CMake#History
2 安装必要软件
2.1 安装MinGW
下载64位的MinGW离线安装包,并使用7z进行解压(注意:WinRAR不支持7z格式,),然后MinGW安装目录下的bin目录加入PATH环境变量。(使用在线安装工具可能提示“Cannot download repository.txt”)
https://sourceforge.net/projects/mingw-w64/files/
然后MinGW安装目录下的bin目录加入PATH环境变量
输入g++ --version验证是否安装配置成功
MinGW的bin目录中,make程序名称默认为mingw32-make.exe,为了方便调用后面章节由CMake工具生成的Makefile文件,请复制一份更名make.exe
输入make,验证make是否可用
如果不能出现上述提示,请检查是否将MinGW安装目录下的bin目录加入PATH环境变量。
2.2 安装CMake
Download | CMake,双击安装。然后将CMake安装目录下的bin目录加入PATH环境变量。输入cmake --version,验证cmake是否配置OK
2.3 CMake可执行程序介绍
在CMake安装目录下的bin目录,是CMake的可执行文件。
可执行程序 | 描述 |
cmake.exe | 用于生成本地构建文件,如Makefile |
cmake-gui.exe | 用于显示用户的配置项以及生成本地构建文件。 |
ctest.exe | 用于对生成的可执行文件进行测试,参见3.4.2 |
pack.exe | 用于制作安装包文件,参见 |
3 CMake 入门
3.1 参考官方入门资料
CMake Tutorial | CMake
官方指南,是一个非常的入门材料,它通过Step by step的讲解方式,给出各种CMake的场景样例,对了解CMake的功能全貌很有帮助。
如果看不懂没有关系,也可以以下链接,是对上述官方指南的翻译和解读
GitHub - chaneyzorn/CMake-tutorial: CMake 官方教程----的翻译
cmake使用教程(二)-添加库 - 掘金
3.2 一个简单的例子
实现开根方的功能:tutorial.cpp
// A simple program that computes the square root of a number
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main (int argc, char *argv[])
{
if (argc < 2)
{
fprintf(stdout,"Usage: %s number\n",argv[0]);
return 1;
}
double inputValue = atof(argv[1]);
double outputValue = sqrt(inputValue);
fprintf(stdout,"The square root of %g is %g\n",
inputValue, outputValue);
return 0;
}
参看下面的CMakeLists.txt文件,最简单的一个工程需要有一个这样的cmake文件,一共三行
# CMake 最低版本号要求
cmake_minimum_required(VERSION 3.0)
# CMake 设置项目名称
project(Tutorial)
# 指定生成目标
add_executable(Tutorial tutorial.cpp)
CMakeLists.txt 的语法比较简单,由命令、注释和空格组成,其中命令是不区分大小写的。符号 #
后面的内容被认为是注释。命令由命令名称、小括号和参数组成,参数之间使用空格进行间隔。对于上面的 CMakeLists.txt 文件,依次出现了几个命令:
cmake_minimum_required
:指定运行此配置文件所需的 CMake 的最低版本;project
:参数值是 Tutorial,该命令表示项目的名称是 Tutorial 。add_executable
:将名为 tutorial.cpp 的源文件编译成一个名称为 Tutorial 的可执行文件。
为了指定makefile生成器类型,以及后续代码编译工具,所以写了一个构建makefile及编译的脚本
:: 删除build文件夹,“/S”表示递归删除,“/Q” 表示静默,可通过在cmd下执行“help rmdir”查看参数具体功能
rmdir /S /Q build
mkdir build
cd build
:: -G后面指定具体的生成器类型,MinGW表示windows,Unix表示unix或linux环境下的生成器。
:: 有关生成器详细参见手册: https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html#manual:cmake-generators(7)
:: DCMAKE_C_COMPILER表示C代码的编译工具,DCMAKE_CXX_COMPILER表示C++代码的编译工具;
:: “..”表示到上一级目录寻找"CMakeLists.txt",等同于“../”。
cmake -G "MinGW Makefiles" -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ ..
:: 在cmake生成了makefile文件,并指定了C/C++源码编译工具后,使用make命令编译makefile定义的工程。
make
pause
整个工程三个文件,结构如下:
编译过程:
编译结果:
运行试验:
4 CMake基本语法
在经过前面的CMake入门后,对CMake的功能有一个初步的了解。而要掌握CMake,首先要学编写CMake构建文件(CMakeLists.txt和*.cmake),它是由一些列命令构成,需要掌握一些常用的命令。
4.1 CMake构建文件编辑器
VSCode安装CMake插件,支持对CMake构建文件中命令高亮显示,以及输入联想功能,如下图
4 CMake基本语法
在经过前面的CMake入门后,对CMake的功能有一个初步的了解。而要掌握CMake,首先要学编写CMake构建文件(CMakeLists.txt和*.cmake),它是由一些列命令构成,需要掌握一些常用的命令。
4.1 CMake构建文件编辑器
VSCode安装CMake插件,支持对CMake构建文件中命令高亮显示,以及输入联想功能,如下图
4.2 CMake构建文件执行
CMake构建文件有三种执行方式
4.2.1 cmake
在命令行cmd/shell下,提供cmake命令执行文件
4.2.2 cmake-gui
提供一个GUI执行CMake构建文件
4.2.3 VS Code的CMake Tools插件
VS Code提供了插件,可以调用CMake命令执行CMake构建文件。
点击VS Code视窗底部的按钮
4.3 CMake构建文件语法
CMake构建文件就是由多条命令组成。包括变量定义、条件块、循环块、宏定义和函数定义,这些定义本身也是命令。
一个源码工程的CMake文件按三种文件方式组织:
l Directories (CMakeLists.txt):源码工程根目录下的CMakeLists.txt代表整个工程构建,通过add_subdirectory添加子目录构建,最终形成整个源码工程对应的构建树。
l Scripts (<script>.cmake):独立的CMake脚本文件,可以通过cmake –p <script>.cmake单独执行一次构建。
l Modules (<module>.cmake):CMake模块文件,,前面两种文件都可以通过include,在其上下文环境中加载并执行<module>.cmake文件。CMake自带很多模块,源码工程构建可以直接使用,参见5.1.6。
参考下面链接。
cmake使用教程(六)-蛋疼的语法 - 掘金
cmake使用教程(八)-macro和function - 掘金
cmake使用教程(六)-蛋疼的语法 - 掘金
对应官方资料参考
https://cmake.org/cmake/help/latest/manual/cmake-language.7.html
4.4 需要掌握的常用命令
CMake支持的命令很多,以下常用的命令需要掌握:
分类 |
| |
基本语法 | 工程名称设置 | project |
变量定义 | set | |
属性设置 | set_target_properties、set_directory_properties、 set_tests_properties、set_source_files_properties、 | |
控制语句 | if、foreach、while | |
控制台输出 | message | |
构建相关 | 构建目标 | add_executable、add_library |
构建目标之间链接 | target_link_libraries、link_libraries | |
头文件搜索目录 | include_directories、target_include_directories、 | |
库文件搜索目录 | link_directories、target_link_directories、 | |
编译宏定义 | add_compile_definitions target_compile_definitions、 | |
编译和链接选项 | target_link_options、target_compile_options、 add_compile_options、add_link_options | |
子目录构建 | add_subdirectory、 | |
辅助类命令 | 查找 | find_file、find_path、find_library find_package |
文件读写 | file |
CMake所有命令的详细信息可查阅以下链接:
cmake-commands(7) — CMake 3.26.3 Documentation
4.5 常见的变量、属性、环境变量
4.5.1 变量
CMake内置很多变量,常见的变量有
类别 | 变量名 | 描述 |
读写 | 指定编译器类型 | CMAKE_C_COMPILER、 CMAKE_CXX_COMPILTER |
指定构建类型(Debug/Release等) | CMAKE_BUILD_TYPE | |
指定构建库的默认类型(Static/Share) | BUILD_SHARED_LIBS | |
指定构建输出路径 | EXECUTABLE_OUTPUT_PATH、 LIBRARY_OUTPUT_PATH、 | |
只读 | 执行CMake命令的源码目录和二进制目录 | CMAKE_CURRENT_SOURCE_DIR、 CMAKE_CURRENT_SOURCE_DIR |
代码工程的源码目录和二进制目录 | PROJECT_SOURCE_DIR、 PROJECT_BINARY_DIR、 <PROJECT-NAME>_SOURCE_DIR、 <PROJECT-NAME>_BINARY_DIR | |
工程版本号 | PROJECT_VERSION |
CMake所有预定义变量的详细信息可查阅以下链接:
cmake-variables(7) — CMake 3.26.3 Documentation
4.5.2 属性
除了变量外,CMake还支持为一些对象设置属性。如目录(Directories)、目标(Target) 等。通过这4条命名进行设置set_target_properties、set_directory_properties、set_tests_properties、set_source_files_properties。
属性和变量区别可以参考一下链接
https://codeday.me/bug/20190627/1304456.html
CMake支持的所有属性参考以下链接
cmake-properties(7) — CMake 3.26.3 Documentation
4.5.3 环境变量
也可以通过设置操作系统环境变量,影响CMake构建的行为。使用以下形式的set命令设置环境变量:
set(ENV{<variable>} [<value>])
CMake支持的环境变量参考如下链接:
cmake-variables(7) — CMake 3.26.3 Documentation
5 CMake 一些重要概念
CMake概念参考
cmake-buildsystem(7) — CMake 3.26.3 Documentation
5.1.1 源码树(Source Tree)
即源码按目录组织形成的目录树
5.1.2 目标(Target)
使用CMake来构建的源码工程是根据一组构建目标进行组织的,这些目标之间通过构建依赖形成整个工程的构建树。
构建目标有三类:
l 二进制可执行文件:使用add_executable命令定义
l 二进制库:使用add_library命令定义
l 可执行命令:使用add_executable命令定义
其中,二进制构建目标通过target_link_libraries命令构建依赖关系,从而形成一颗构建树(Build Tree)
详细介绍参考以下链接:
cmake-buildsystem(7) — CMake 3.26.3 Documentation
5.1.3 构建树(Build Tree)
5.1.4 生成器(Generator)
CMake构建文件通过cmake或cmake-gui执行后,转换生成本地构建文件(如GUN make、MinGW make等),通过执行本地构建文件完成对源码工程的构建。
CMake针对每一种本地构建文件,都有一个对应的LocalGenerator类,负责将CMake构建文件转换为该特定的本地构建文件。
详细参考
cmake-generators(7) — CMake 3.26.3 Documentation
5.1.5 表达式expressions
CMake构建文件中支持表达式,类似其他C/C++语言的运算表达式,这些表达式进一步组合成更大的表达式。表达式主要应用于变量定义、属性设置、控制语句的相关命令中(参见4.4)
表达式详细参考以下链接。
cmake-generator-expressions(7) — CMake 3.26.3 Documentation
5.1.6 引入第三方库
使用第三方库有两种方式:
l 自行拷贝第三方库的头文件和库文件到本源码工程
l 通过查找构建环境中已经安装的第三方库
CMake支持查找这些库的路径,查找第三方库可以用find_package命令,如何使用第三方库参考以下链接
find_package与CMake如何查找链接库详解_find_package_handle_standard_args_bytxl的博客-CSDN博客
当然CMake还自带很多查找外部库的模块。
CMake自带的工具模块、查找第三方库的模块,这些模块的详细描述参见以下链接:
cmake-modules(7) — CMake 3.26.3 Documentation
当然,用户也可以自行创建模块,供find_package命令查找,如何自行创建可供查找的模块详细参见以下链接:
cmake-packages(7) — CMake 3.26.3 Documentation
5.1.7 交叉编译工具链文件
如果一个源码工程需要在交叉编译成不同平台上运行的文件,那么可以为每一种运行平台指定一种交叉编译工具链文件, 在执行源码工程CMake构建时(参见4.2.1),通过以下参数
-DCMAKE_TOOLCHAIN_FILE=<path/to/tool_chain_file>
指定编译的目标运行环境。
详细参考
cmake-toolchains(7) — CMake 3.26.3 Documentation
5.1.8 获取构建过程信息的文件API接口
要查询CMake构建过程中的信息,可以通过文件API接口。
例如,在build的目录下创建一个.cmake/api/v1/query/client-<clientId>/query.json文件,其中路径中<clientId>替换成标识任意字符。query.json内容如下:
然后点击cmake-gui的Configure和Generate按钮
CMake构建时会在query目录的同级目录生成一个reply目录,存放查询结果
详细参考以下链接
cmake-file-api(7) — CMake 3.26.3 Documentation
6 CMake调试
CMake没有交互式调试器。
如果CMake文件出错,或执行不符合预期,有以下手段可以进行问题定位:
1. 使用message命令在控制台输出变量、属性和环境变量
2. 执行cmake命令时,提供以下调试信息输出选项
--debug-output
--trace
--trace-expand
--trace-source=<file>
3. 查看CMake生的日志文件
4. 检查生成的本地构建文件内容是否正确
生成的本地构建文件Makefile的调用关系为:
build\Makefile调用build\CMakeFiles\Makefile2,而build\CMakeFiles\Makefile2再调用build\CMakeFiles\Makefile2\<project_name>\build.make
build\CMakeFiles\Makefile2\<project_name>目录下分别生成4个文件
文件名 | |
flags.make | 编译选项、编译宏、头文件路径 CXX_FLAGS、CXX_DEFINES、CXX_INCLUDES |
depend.make | 编译中间文件依赖的源文件定义 |
build.make | 本地构建文件,调用编译器编译源文件。 |
7 CMake相关资料
7.1 官方参考手册
CMake各种功能特性的详细查阅的地址。
CMake Reference Documentation — CMake 3.26.3 Documentation
7.2 书籍
7.2.1 官方推荐书籍CMake Mastering
该书各种概念和原理讲得比较透彻,建议阅读,书籍下载链接
learning-cmake/mastering-cmake.pdf at master · Akagi201/learning-cmake · GitHub
未找到中文版本,仅找到前三章节的中文翻译:
【译文】Mastering CMake(一)之为什么选择CMake_王正南的博客-CSDN博客
【译文】Mastering CMake 第二章 开始_kambits的博客-CSDN博客
【译文】Mastering CMake 第三章 关键概念_kambits的博客-CSDN博客
7.2.2 CMake Cookbook
GitHub - dev-cafe/cmake-cookbook: CMake Cookbook recipes.
7.2.3 CMake 实践
CMake实践这本书适合新手入门。
http://file.ncnynl.com/ros/CMake%20Practice.pdf
7.3 CMake工程样例
GitHub - ttroy50/cmake-examples: Useful CMake Examples
7.4 公司内关于CMake的资料汇总贴
http://3ms.huawei.com/hi/group/3145469/wiki_5433765.html
参考资料:
1、huangyanfei CMake学习指南
2、huangyanfei MinGW+CMake+GTest+GMock+VSCode实现Windows的UT单步调试