1、CMake入门:CMake是什么、为什么企业项目需要CMake、CMake与Makefile的关系、安装CMake

1.1 CMake到底是什么?

先说说CMake是什么。说白了,CMake是一个跨平台的构建系统生成器。

你写C++代码,总得把它变成可执行文件吧?这个过程叫构建。不同平台(Windows、Linux、macOS)用的构建工具不一样——Linux用Make,Windows用Visual Studio的MSBuild,macOS用Xcode。你总不能每换一个平台,就重写一套构建脚本吧?

CMake就是来解决这个问题的。你写一份CMakeLists.txt,CMake就能自动生成对应平台的构建文件。我个人习惯把CMake比作一个翻译官——你说一遍需求,它帮你翻译成各个平台能听懂的语言。

核心要点:CMake本身不编译代码,它只负责生成Makefile或项目文件,真正的编译工作还是交给Make、Ninja或Visual Studio去做。

1.2 为什么企业项目需要CMake?

我在刚入行那会儿,也用过纯Makefile。小项目还行,三五个文件,写个Makefile挺顺手。但项目一大了,问题就来了。

举个例子。我曾经接手过一个遗留项目,光源文件就2000多个,Makefile写了将近800行。每次加一个新模块,都得手动改Makefile,改完还得祈祷别把依赖搞乱。更别提跨平台了——Windows上跑不起来,因为Makefile里全是Linux的路径和命令。

企业项目为什么需要CMake?我总结了几个原因:

  • 跨平台能力:一套CMakeLists.txt,Windows、Linux、macOS通吃。我在一个嵌入式项目里,用CMake同时管理了ARM Linux和x86 Windows两套编译,省了不少事。
  • 依赖管理:CMake的find_package能自动查找第三方库。你想想看,如果手动写Makefile,你得自己去配置头文件路径、库路径、链接参数,一个库还好,十个库就够你喝一壶的。
  • 模块化组织:CMake支持add_subdirectory,可以把项目拆成多个子模块。每个模块有自己的CMakeLists.txt,互不干扰。我习惯把公共库、业务逻辑、测试用例分成三个子目录,清晰得很。
  • 生成器支持:CMake能生成Ninja、Visual Studio、Xcode等多种构建系统的文件。你团队里有人用VS,有人用CLion,有人用VSCode?没问题,CMake都能照顾到。
  • 测试与安装:CMake内置了CTest和CPack,测试和打包一条龙。我之前的项目,每次发版都要手动打包,后来用CPack配置好,一条命令搞定。

我的建议:如果你还在用纯Makefile管理超过50个源文件的项目,赶紧换成CMake。别等到文件数破百再动手,那时候改起来就痛苦了。

1.3 CMake与Makefile的关系

这个问题经常有人问。我直接说结论:CMake不是Makefile的替代品,而是Makefile的生成器。

它们的关系可以用一张图来表示:

CMakeLists.txt CMake 处理 Makefile / .sln / .xcodeproj Make / Ninja / MSBuild 可执行文件

流程很清楚:你写CMakeLists.txt → CMake读取并生成Makefile(或其他构建文件)→ 构建工具(Make/Ninja等)编译链接 → 得到可执行文件。

那直接用Makefile不行吗?行,但有几个痛点:

对比项 CMake 纯Makefile
跨平台 原生支持,一套脚本到处用 需要手动适配不同平台
依赖查找 find_package自动搞定 手动配置路径和链接参数
模块化 add_subdirectory天然支持 需要include其他Makefile
调试体验 生成带调试信息的项目文件 需要手动设置调试符号
学习曲线 中等,语法简单但概念多 低门槛,但复杂项目维护成本高

注意:CMake生成的Makefile不要手动修改!我曾经见过有人直接改生成的Makefile来加编译选项,结果下次重新运行CMake时,所有修改都被覆盖了。正确的做法是修改CMakeLists.txt,然后重新生成。

1.4 安装CMake

安装CMake其实很简单。我根据不同平台说一下:

Linux(Ubuntu/Debian)

# 直接用apt安装,简单粗暴
sudo apt update
sudo apt install cmake

# 验证安装
cmake --version

不过要注意,apt源里的CMake版本可能比较旧。我建议去cmake.org下载最新版,或者用Kitware的官方APT仓库。为什么?因为新版本支持更多特性,比如我在用CMake 3.20+时,CMAKE_CXX_STANDARD的默认值变了,旧版本可能不兼容。

macOS

# 用Homebrew安装
brew install cmake

# 或者去官网下载.dmg安装包

Windows

去CMake官网下载安装包,运行安装程序。安装时记得勾选"Add CMake to the system PATH",这样你就能在命令行里直接使用cmake命令了。

小技巧:Windows用户如果装了Visual Studio,CMake安装程序会自动检测VS版本,并帮你配置好生成器。我习惯在VS里用"打开本地文件夹"功能,CMake项目会自动识别,省去手动配置的麻烦。

验证安装

不管哪个平台,装完后在终端里输入:

cmake --version

如果看到类似 cmake version 3.27.0 的输出,就说明装好了。

1.5 第一个CMake项目

光说不练假把式。我们来写一个最简单的CMake项目:

# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(HelloCMake)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(hello main.cpp)
// main.cpp
#include <iostream>

int main() {
    std::cout << "Hello, CMake!" << std::endl;
    return 0;
}

然后在项目目录下执行:

mkdir build
cd build
cmake ..
make
./hello

看到"Hello, CMake!"输出,恭喜你,第一个CMake项目跑起来了。

为什么用build目录?这叫"外部构建",生成的中间文件都在build里,不会污染源码目录。我习惯把所有构建产物都放在build下,想清理时直接删掉build目录就行,干净利落。

1.6 避坑指南

最后分享几个我踩过的坑:

  • 路径别用中文:CMake对中文路径支持不太好,尤其是Windows上。我吃过一次亏,项目路径里有中文,结果find_package死活找不到库。
  • 版本号别写错:cmake_minimum_required写的版本号,决定了你能用哪些特性。写低了,新特性用不了;写高了,旧版本CMake跑不了。我一般写3.10,兼容性比较好。
  • 别在源码目录里构建:内部构建(直接在源码目录运行cmake)会生成一堆临时文件,清理起来很麻烦。养成用build目录的习惯。

嗯,第一章就到这里。CMake入门其实不难,关键是理解它的定位——它是个生成器,不是编译器。下一章我们会深入CMakeLists.txt的语法,到时候见。