第一个CMake项目:从零开始构建你的第一个程序

说实话,我见过太多人一上来就扎进CMake的复杂语法里,结果被各种变量、函数搞得晕头转向。其实CMake没那么玄乎——它本质上就是个「项目描述文件」。今天我们就从最基础的东西开始,亲手搭一个能跑起来的C++项目。

CMakeLists.txt:项目的「身份证」

每个CMake项目都离不开一个叫 CMakeLists.txt 的文件。这名字是固定的,大小写都不能错。我刚开始用CMake时,就因为手滑写成了 CMakeList.txt(少了个s),结果折腾了半天才发现问题。

这个文件放在项目的根目录下,CMake就是靠它来理解你的项目结构。说白了,它就是一份「说明书」——告诉CMake:你的项目叫什么、用什么语言、需要编译哪些源文件。

cmake_minimum_required:版本兼容的「安全网」

每个 CMakeLists.txt 的第一行,我建议都写上 cmake_minimum_required。为什么?因为不同版本的CMake,行为可能有差异。我曾经接手过一个老项目,它用的是CMake 2.8的语法,而我本地装的是3.20,结果一堆警告和兼容性问题。

cmake_minimum_required(VERSION 3.10)

这行命令告诉CMake:低于3.10版本的,就别来碰我的项目了。它会在构建前检查版本,不满足就直接报错退出。我个人习惯把这个版本号设得稍微保守一点——比如你实际用3.20,但写3.15,这样别人用稍旧一点的CMake也能编译。

注意:cmake_minimum_required 必须写在 project() 之前。这是CMake的硬性要求,顺序错了会报错。

project():给你的项目起个名字

接下来就是 project() 命令。它做的事情比你想的要多:

  • 定义项目名称
  • 设置语言(C、CXX、Fortran等)
  • 自动创建一些有用的变量,比如 PROJECT_NAMEPROJECT_SOURCE_DIR
project(HelloWorld LANGUAGES CXX)

这里我指定了语言为 CXX(也就是C++)。如果你不写 LANGUAGES,CMake默认会检测C和C++。我个人建议显式写出来,这样更清晰,也避免不必要的检测开销。

你想想看,如果你的项目只用C++,却让CMake去检测C编译器,是不是有点浪费?

add_executable():把源文件变成可执行程序

核心命令来了——add_executable()。它的作用就是:把一堆源文件编译成一个可执行文件

add_executable(hello main.cpp)

第一个参数是目标名称(也就是生成的可执行文件名),后面跟的是源文件列表。你可以写多个源文件:

add_executable(hello main.cpp utils.cpp logger.cpp)

嗯,这里要注意:源文件路径是相对于 CMakeLists.txt 所在目录的。如果你把源文件放在 src/ 子目录下,就要写成 src/main.cpp

完整的CMakeLists.txt:把一切串起来

好了,我们把上面这些拼在一起,就是一个完整的、能工作的CMake项目了:

cmake_minimum_required(VERSION 3.10)

project(HelloWorld LANGUAGES CXX)

add_executable(hello main.cpp)

就这么三行。真的,一个最简单的CMake项目,三行就够了。我当年第一次看到这个时还不敢相信——就这么简单?

构建与运行:让代码跑起来

有了 CMakeLists.txt 和源文件,接下来就是构建了。CMake推荐使用「外部构建」——也就是在项目目录外建一个 build 文件夹,在里面执行构建。这样做的好处是:所有临时文件都集中在 build 里,项目根目录干干净净。

步骤很简单:

  1. 创建并进入 build 目录
  2. 运行 cmake .. 生成构建系统
  3. 运行 cmake --build . 编译项目
mkdir build
cd build
cmake ..
cmake --build .

执行完这些,你会在 build 目录下看到一个可执行文件(Linux/macOS下叫 hello,Windows下叫 hello.exe)。运行它:

./hello

如果一切顺利,你会看到程序输出。嗯,就这么简单。

小技巧: 我习惯在 CMakeLists.txt 里加上 set(CMAKE_EXPORT_COMPILE_COMMANDS ON),这样CMake会生成一个 compile_commands.json 文件,很多代码补全工具(比如clangd)都需要它。

知识体系一览

下面这张图把本章的核心逻辑串起来了——从 CMakeLists.txt 到最终的可执行文件,每一步都清晰可见:

CMake项目构建流程 CMakeLists.txt 项目描述文件 cmake .. 生成构建系统(Makefile / Ninja) cmake --build . 编译源代码 → 生成可执行文件 hello(可执行文件) cmake_minimum_required 版本检查 project() 项目名称/语言 add_executable() 定义目标与源文件 build/ Makefile / Ninja build/ hello.o 等目标文件

避坑指南:我踩过的几个坑

讲几个我实际遇到过的问题,你遇到了可以少走弯路:

  • 源文件路径写错了:CMake报错说找不到文件。检查一下路径是不是相对于 CMakeLists.txt 的。
  • 忘记创建 build 目录:直接在项目根目录运行 cmake 也可以,但会把临时文件散落一地。我建议养成外部构建的习惯。
  • cmake_minimum_required 版本太低:比如你写了 VERSION 2.8,但用了3.0才有的语法,CMake会默默忽略一些特性。我曾经因为这个排查了半天。

核心要点回顾:

  • 每个CMake项目必须有一个 CMakeLists.txt
  • cmake_minimum_required 确保版本兼容
  • project() 定义项目元信息
  • add_executable() 把源文件变成可执行程序
  • 使用外部构建(build目录)保持项目整洁

好了,到这里你已经掌握了CMake最核心的「三板斧」。别看简单,这三个命令能覆盖你80%的日常需求。下一节我们会深入目标属性的世界——那才是CMake真正强大的地方。


公众号:蓝海资料掘金营,微信deep3321