8、CMake入门:CMakeLists.txt基本语法、project()与add_executable()
说实话,我第一次接触CMake的时候,心里是有点抵触的。那时候我还在用Makefile,觉得手写依赖规则才叫「硬核」。直到有一次接手一个跨平台项目,要在Linux、Windows和嵌入式板子上分别编译,Makefile改得我头皮发麻……嗯,从那以后,我就老老实实学CMake了。
今天咱们聊的,是CMake最核心的三个东西:CMakeLists.txt怎么写、project()怎么用、add_executable()怎么配。说白了,你只要搞懂这三样,一个多文件C项目就能跑起来了。
8.1 CMakeLists.txt:项目的「总指挥」
每个用CMake管理的项目,根目录下必须有一个 CMakeLists.txt。它就像项目的配置文件,告诉CMake:你的源码在哪、要生成什么、依赖什么库。
我个人习惯把 CMakeLists.txt 放在项目根目录。比如这样一个结构:
my_project/
├── CMakeLists.txt
├── src/
│ ├── main.c
│ ├── utils.c
│ └── utils.h
└── build/ # 编译产物放这里
注意,build/ 目录是我手动建的。为什么?因为CMake推荐「外部构建」,也就是源码和编译产物分开。你想想看,如果编译出来的 .o 文件和源码混在一起,哪天想清理一下,一不小心就把源码删了……我曾经就干过这种蠢事。
mkdir build && cd build && cmake ..,所有临时文件都在 build 里,干净又卫生。
8.2 project():给项目起个名,顺便定个语言
project() 是CMakeLists.txt里第一个要调用的命令。它的作用很简单:声明项目名称,并指定使用的编程语言。
基本语法长这样:
project(项目名称 [语言列表])
举个例子:
project(MyProject C)
这行代码干了三件事:
- 项目名字叫
MyProject - 告诉CMake我们只用C语言(不加的话默认C、C++、Fortran都检测,没必要)
- 自动生成几个变量,比如
PROJECT_NAME、PROJECT_SOURCE_DIR、PROJECT_BINARY_DIR
我在项目中遇到过一个问题:如果项目里既有C又有C++,但忘了写 LANGUAGES,CMake会默认启用C++编译器。结果我明明只写了 .c 文件,它却去链接C++标准库,编译倒是过了,但链接时报一堆莫名其妙的错。所以我的建议是——明确写清楚语言,别偷懒。
project() 必须在 cmake_minimum_required() 之后调用。顺序错了,CMake会报错。
8.3 add_executable():把源码变成可执行文件
有了项目名,接下来就是告诉CMake:你要生成一个可执行程序,源码是哪些文件。
语法:
add_executable(目标名称 源文件列表)
比如:
add_executable(my_app src/main.c src/utils.c)
这行代码的意思是:用 main.c 和 utils.c 编译出一个叫 my_app 的可执行文件。注意,目标名称不能有空格,也不能和已有目标重名。
你可能会问:头文件呢?不用写进去吗?
嗯,这里要注意:头文件不需要写在 add_executable() 里。CMake会自动分析源码中的 #include 依赖。但如果你想让头文件出现在IDE的项目视图中,可以这样写:
add_executable(my_app src/main.c src/utils.c src/utils.h)
这样写不影响编译,只是让IDE更好看。我个人习惯把头文件也列上,因为团队里有人用VS Code,有人用CLion,列出来大家都能看到项目结构。
add_executable(my_app main.c) 但 main.c 在 src/ 目录下。CMake不会报错,但链接时会提示「找不到main函数」。检查路径,这是最基础的坑。
8.4 一个完整的入门示例
咱们把上面讲的串起来,写一个完整的 CMakeLists.txt:
cmake_minimum_required(VERSION 3.10)
project(DemoProject C)
add_executable(demo src/main.c src/utils.c)
就这么三行。然后执行:
mkdir build
cd build
cmake ..
make
你就会在 build/ 目录下看到一个叫 demo 的可执行文件。是不是很简单?
你想想看,如果不用CMake,你得手写Makefile,还得处理跨平台的编译器差异。用CMake,三行搞定。这就是工具的价值。
8.5 知识体系:CMake入门核心逻辑
下面这张图,是我梳理的CMake入门阶段的知识脉络。你看一眼,心里就有谱了。
8.6 常见问题与避坑
| 问题 | 原因 | 解决办法 |
|---|---|---|
cmake 提示 CMake Error: The source directory does not appear to contain CMakeLists.txt |
当前目录下没有 CMakeLists.txt | 检查是否在项目根目录执行 cmake |
| make 后找不到 main 函数 | 源文件路径写错了,或者漏写了源文件 | 检查 add_executable() 中的文件路径 |
编译时提示 undefined reference to |
某个函数定义在另一个 .c 文件中,但没被包含 | 把缺失的 .c 文件加到 add_executable() 里 |
| cmake 版本太低 | CMakeLists.txt 里写了高版本才支持的命令 | 降低 cmake_minimum_required 版本,或升级CMake |
CMakeLists.txt,能编译通过再说。后面再慢慢加库、加子目录、加编译选项。一口吃不成胖子,编译也是。
好了,CMake入门就聊到这儿。你只要记住:project() 定项目名和语言,add_executable() 定目标和源文件,再加上 cmake_minimum_required() 保底,一个多文件C项目就能用CMake管理起来了。剩下的,都是锦上添花。