2、第一个Makefile:编写最简单的单文件编译规则、使用make命令执行、理解执行流程

好,咱们直接动手。别怕,第一个Makefile简单到让你怀疑人生。

我记得我刚开始学Makefile的时候,看了半天文档,什么变量、函数、自动推导……头都大了。后来我发现,其实核心就一句话:Makefile就是告诉make,怎么从源文件生成目标文件。

说白了,它就是个“菜谱”。你告诉它原料(源文件)和步骤(编译命令),它帮你做菜(生成可执行文件)。

2.1 先准备一个C源文件

咱们先写个最简单的C程序,就叫 hello.c:

#include <stdio.h>

int main() {
    printf("Hello, Makefile!\n");
    return 0;
}

这个不用多解释了吧?就是打印一句话。保存好,放在一个空目录里。

2.2 编写第一个Makefile

在同一个目录下,新建一个文件,名字就叫 Makefile(注意大小写,M大写)。

打开它,写入以下内容:

hello: hello.c
    gcc -o hello hello.c

嗯,就两行。是不是比你想象中简单?

我来解释一下这两行在说什么:

  • 第一行:hello: hello.c —— 这叫“依赖关系”。冒号左边是目标(我要生成的文件),右边是依赖(生成它需要的原料)。意思是:hello 依赖于 hello.c。
  • 第二行:gcc -o hello hello.c —— 这叫“命令”。注意,前面必须有一个Tab缩进,不能是空格。这是Makefile最坑的地方,我当年就被这个坑过——明明写对了,就是报错,结果发现是空格不是Tab。
⚠️ 避坑指南
我曾经花了一个小时排查一个Makefile报错,最后发现是编辑器把Tab自动转换成了空格。建议你用vim或者VS Code,确保缩进是真正的Tab字符。

2.3 执行make命令

好,现在打开终端,进入这个目录,直接输入:

make

你会看到类似这样的输出:

gcc -o hello hello.c

然后目录下就多了一个 hello 可执行文件。运行它:

./hello

输出:

Hello, Makefile!

成了!第一个Makefile就这么跑起来了。

2.4 理解执行流程

为什么会这样?make到底做了什么?我画个图帮你理解:

1. 读取Makefile 2. 查找目标 hello 3. 检查 hello.c 是否存在 4. 执行 gcc -o hello hello.c

流程其实很简单:

  1. make读取Makefile,找到第一个目标(这里是 hello)。
  2. 检查依赖文件:看看 hello.c 存不存在。如果不存在,报错退出。
  3. 检查目标文件:如果 hello 已经存在,并且比 hello.c 更新,make会说“无事可做”,直接退出。否则,执行命令。
  4. 执行命令:运行 gcc -o hello hello.c,生成 hello。

💡 核心思想

make只做一件事:当依赖比目标新时,重新生成目标。这就是增量编译的基础——只编译修改过的文件,没改的就不动。

2.5 再试一次

现在你再执行一次 make:

make

你会看到:

make: 'hello' is up to date.

看到了吧?make发现 hello 已经是最新的了,就不做任何操作。这就是make的智能之处——避免不必要的重复编译。

你试试修改一下 hello.c,比如把打印的字符串改一改,再执行 make:

gcc -o hello hello.c

它又编译了。因为 hello.c 比 hello 新,make检测到了变化。

📌 个人小技巧

我习惯在写Makefile时,先手动执行一次编译命令,确认能编译通过,再写到Makefile里。这样能避免“Makefile写错了,但不知道是命令本身有问题还是Makefile语法有问题”的尴尬。

2.6 关于目标名称

你可能注意到了,我写的目标是 hello,而不是 hello.exe 或 hello.out。在Linux/macOS下,可执行文件没有后缀名,所以直接叫 hello 就行。

如果你在Windows下用MinGW或Cygwin,可以写成 hello.exe: hello.c,但原理完全一样。

另外,目标名可以和源文件同名(不带后缀),这是最常见的做法。你也可以起别的名字,比如:

myapp: hello.c
    gcc -o myapp hello.c

这样生成的可执行文件就叫 myapp。但说实话,我建议保持目标名和源文件同名,这样一眼就能看出对应关系。

2.7 总结一下

第一个Makefile,你只需要记住三件事:

  • 格式:目标: 依赖 + Tab + 命令
  • 执行:直接输入 make,它会找第一个目标
  • 流程:检查依赖 → 判断是否需要重新编译 → 执行命令

就这么简单。别被Makefile吓到,它本质上就是个自动化脚本,只不过加了一点“智能判断”功能。

下一节,我们会把这个单文件规则扩展一下,看看多文件项目怎么处理。但今天,你先把这个最简单的Makefile跑通,感受一下make的魔力。

✅ 本章核心要点

概念 说明
目标 (target) 要生成的文件,如 hello
依赖 (prerequisites) 生成目标需要的源文件,如 hello.c
命令 (recipe) 如何生成目标,必须以Tab开头
增量编译 只有依赖更新时才重新编译

好了,去试试吧。写一个你自己的Makefile,编译一个C程序。相信我,当你看到 make 一行命令就搞定编译时,那种感觉——嗯,挺爽的。