第一个CMake项目:从零开始搭建你的构建系统
说实话,很多嵌入式工程师第一次接触CMake时,都觉得它很神秘。我当年也是这样——看着一堆CMakeLists.txt文件,完全不知道从哪下手。但等你真正动手创建一个最简单的项目后,你会发现,嗯,其实核心就那么几个命令。
今天我们就来亲手搭建第一个CMake项目。我会带着你一步步写CMakeLists.txt,然后编译、运行。整个过程不会超过10分钟。
1. 项目结构:先想清楚怎么放文件
我个人习惯,嵌入式项目一般这样组织:
my_first_project/
├── CMakeLists.txt
└── main.c
对,就两个文件。一个CMakeLists.txt,一个main.c。简单到不能再简单。
main.c的内容也很朴素:
#include <stdio.h>
int main(void) {
printf("Hello, CMake!\n");
return 0;
}
这里我用的是printf,而不是嵌入式里常见的串口打印。为什么?因为咱们先跑通桌面环境,把CMake的流程搞明白。等后面讲到交叉编译,再切到真正的嵌入式目标板。
2. 编写CMakeLists.txt:三个核心命令
打开CMakeLists.txt,写入以下内容:
cmake_minimum_required(VERSION 3.10)
project(MyFirstProject)
add_executable(my_app main.c)
就三行。我刚开始学的时候也觉得太简单了,但实际项目里,90%的CMakeLists.txt都是这三行的变体。
咱们逐行拆解一下:
2.1 cmake_minimum_required()
这行指定了CMake的最低版本。比如我写VERSION 3.10,意思是如果用户电脑上的CMake版本低于3.10,就会报错退出。
2.2 project()
这行定义了项目名称。比如project(MyFirstProject),项目名就叫MyFirstProject。
这个命令还会隐式地设置一些变量,比如:
PROJECT_NAME— 项目名PROJECT_SOURCE_DIR— 源码根目录PROJECT_BINARY_DIR— 构建目录
这些变量后面会经常用到。你想想看,如果没有project(),CMake连项目叫啥都不知道,后面很多功能就没法用了。
2.3 add_executable()
这行告诉CMake:我要生成一个可执行文件。第一个参数是目标名(比如my_app),后面跟的是源文件列表。
说白了,add_executable(my_app main.c) 就是告诉CMake:把main.c编译链接成my_app这个可执行文件。
set(SRC_FILES main.c uart.c gpio.c)add_executable(my_app ${SRC_FILES})这样后面加文件时,只改set那一行就行。
3. 构建与运行:三步走
写好了CMakeLists.txt,接下来就是构建。我习惯在项目根目录下创建一个build文件夹,所有编译产物都放里面,保持源码目录干净。
mkdir build
cd build
cmake ..
make
这三条命令干了什么?
- mkdir build && cd build — 创建并进入构建目录
- cmake .. — 解析上一级目录的CMakeLists.txt,生成Makefile
- make — 根据Makefile编译链接,生成可执行文件
执行完cmake后,你会看到类似这样的输出:
-- The C compiler identification is GNU 9.3.0
-- The CXX compiler identification is GNU 9.3.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/user/my_first_project/build
看到"Configuring done"和"Generating done"就说明CMake配置成功了。然后make一下:
Scanning dependencies of target my_app
[ 50%] Building C object CMakeFiles/my_app.dir/main.c.o
[100%] Linking C executable my_app
最后运行:
./my_app
Hello, CMake!
看到"Hello, CMake!"了吗?恭喜你,第一个CMake项目跑通了!
4. 避坑指南:我踩过的三个坑
做嵌入式这么多年,CMake的坑我踩过不少。这里分享三个最常见的:
坑一:忘记创建build目录
直接在源码目录下运行cmake,会把生成的Makefile和中间文件跟源码混在一起。清理起来非常麻烦。我曾经有一次不小心把build目录删错了,结果源码也被误删了……从那以后,我坚持用out-of-source build。
坑二:cmake_minimum_required版本写错
如果你用了某个CMake特性,但版本写低了,CMake不会报错,但行为可能不符合预期。反过来,版本写高了,老环境的用户就没法用。我建议:先用你本地CMake版本,等确认项目稳定后,再适当降低版本号。
坑三:add_executable里漏了源文件
这个错误很隐蔽。比如你新增了一个uart.c,但忘了加到add_executable里。编译不会报错,但链接时会提示"undefined reference"。排查起来很费时间。我的习惯是:每加一个源文件,立刻更新CMakeLists.txt。
5. 知识体系总览
下面这张图,把本章的核心逻辑串起来了:
从图上可以清楚看到:三个核心命令定义了项目的基本信息,CMake根据这些信息生成Makefile,最后make编译出可执行文件。整个过程环环相扣,缺一不可。
6. 总结
今天咱们做了三件事:
- 写了一个最简单的CMakeLists.txt,用了三个命令
- 用cmake + make完成了构建
- 运行了生成的可执行文件
别看现在项目小,这套流程跟大型嵌入式项目完全一样。后面章节我们会逐步加入库管理、交叉编译、单元测试等高级功能,但核心骨架就是今天这三行命令。
嗯,动手试试吧。把代码敲一遍,感受一下CMake的工作流。遇到问题别慌,对照着避坑指南检查一下,多半能解决。