初衷
CMake能用来编写跨平台(cross-platform)的构建规则,通过这些规则来调用各个平台的编译器、链接器,生成各个目标(静态库,静态库,或者可执行)。
我第一个接触的大型C++项目是OpenCV,它是基于CMake构建的。后来接触的另一个大型C++项目是Caffe,既提供Makefile也提供CMakeLists.txt。在使用CMake的过程中对CMake的常用语法越发熟悉,甚至日常工作中的项目代码也被我用CMake进行构建。但是仍然觉得对CMake不够了解,CMake的不少用法在官方文档中的描述也感觉有些晦涩、不清晰。
好在CMake是开源项目,觉得CMake文档写的烂那就直接翻源码。而由于越来越多的C/C++开源项目使用CMake进行构建,学习和深入理解CMake对于一个C/C++(尤其是跨平台)程序员来说还是有相当的好处和必要的。(当然你也可以用gradle/bazel/buck/please/scons/xmake/emake等来构建)
此外,我也仔细看过CMake官方文档的部分章节,写过几篇蹩脚的CMake笔记文档,了解到通常越新版本的CMake特性越多。这系列blog分析的CMake源码是CMake-3.14.3版。(不得不吐槽一下:CMake官网打开速度太慢,国内各大开源镜像站点也都不收录CMake源码,CMake官方文档打开也超慢,而且写的也不够系统,中文博客讲CMake的也往往很初级)。如果你是第一次接触CMake,我觉得你最好先用用CMake,稍微熟悉一点基本用法和一些概念后再来看本篇不迟。
CMake入口:命令行参数
CMake是什么?或许我们应该从"cmake.exe是什么?"(windows),或file `which cmake`
(Linux或Mac)来分析。当我们安装好CMake,它提供了一个可执行文件cmake或cmake.exe,有时候还提供一个GUI版本,比如ccmake或cmake-gui,不过GUI版本可以认为是外科,里子还是cmake命令。
那么cmake命令是什么?其实就是一个C/C++项目编译出来的可执行文件。它或许提供了一大堆支持的函数、类,但是对外的接口可以说只有一个,那就是main()
函数。当我敲下cmake ..
或者cmake .. -DCMAKE_BUILD_TYPE=Debug
,再或者cmake --build .
,cmake到底会怎么执行,其实就是看它的commandline argument parser是怎么处理的了:它能接受的(合法的)命令行参数有哪些?每一种分别是什么含义?
从$CMAKE_ROOT/Source/cmakemain.cxx
可以看出,它肯定支持的三个参数是:
--build
--open
-
-E
继续看,发现do_cmake()
函数中处理了其他参数的情况,而且数量非常多:
而我们看看官方文档对cmake命令行支持的参数是怎么写的,先看cmake3.13版本的文档,可以说是稀巴烂:
再看cmake3.14版本的命令行参数文档:
看起来有所改善,把支持的命令行参数分成了几个类别,思路上清晰了不少;不过仍然需要改进,比如说
cmake -N
这一条没有被列出,但是其实后文又有提到。
根据3.14版的文档可以看出,执行cmake命令,支持7大类参数:
- 指定
CMakeLists.txt
所在路径,用来生成目标平台的构建文件如Makefile、.sln、.xcodeproject等(cmake的主要特色) - 执行构建,相当于用通用的写法,对生成的目标平台构建描述文件进行调用,替代具体的"make"、"nmake"等写法。(个人经常用,还可以指定
--target TargetName
和--config BUILD_TYPE
) - 用VS、XCode等打开工程(我没用过,我也觉得没必要)
- 执行cmake脚本
- 执行命令行工具
- 执行
find-package
工具(其实很废柴,和CMakeLists.txt中的find_package()
根本不是一会事儿,试了好几个包都找不到) - 在命令行里查看帮助(已经是9012年了,多少年前就有
git help xxx --web
在网页中看帮助文档了,cmake什么时候支持一下?)