前言
如果你在 Linux 下编译过 C/C++ 项目,一定遇到过这样的场景:
修改一个文件后,需要手动输入 gcc main.c func.c -o app 重新编译,文件多了还容易漏写;如果只改了一个小模块,却要重新编译所有文件 —— 既浪费时间又麻烦。
而 和 ,就是为解决这个问题而生的自动化构建方案。今天我们就从基础到实战,彻底掌握它们。
make 是 Linux 下用于自动化构建项目的命令行工具,通过 Makefile 定义编译规则。它根据文件修改时间智能判断是否需要重新编译,避免重复劳动。本文介绍了 Makefile 的基本语法、变量使用(如$@、$^)、伪目标(.PHONY)以及文件时间属性(atime、mtime、ctime)对编译决策的影响,帮助开发者高效管理多文件项目。

如果你在 Linux 下编译过 C/C++ 项目,一定遇到过这样的场景:
修改一个文件后,需要手动输入 gcc main.c func.c -o app 重新编译,文件多了还容易漏写;如果只改了一个小模块,却要重新编译所有文件 —— 既浪费时间又麻烦。
而 和 ,就是为解决这个问题而生的自动化构建方案。今天我们就从基础到实战,彻底掌握它们。
make 是一个命令行工具,核心能力是:
简单说:它能帮你 '智能编译',避免重复劳动。
Makefile 是一个文本文件,没有后缀名,核心作用是定义构建规则—— 告诉 make:
app**)main.o、func.o**)gcc -c main.c**)没有 Makefile,make 命令就会报错(不知道该做什么)。
1. 创建 Makefile 文件
touch Makefile
2. vim 编辑 Makefile
[图片:Makefile 编辑界面]
3. make
[图片:make 执行结果]
如果要清理项目时,可能会不小心将程序搞崩溃,所以这时候就需要 Makefile 自动化清理
[图片:清理命令示例]
[图片:清理效果]
[图片:依赖关系图]
我们现在将 clean 换到前面,我们再 make 试一试:
[图片:clean 前置执行]
[图片:执行结果]
我们发现 make 变成了进行 clean 的操作,那么我如果想编译 code.c 呢?—接着往下看:
[图片:指定目标编译]
我们只需在 make 后面加上目标文件即可!
那么 make 的逻辑是什么呢?
Makefile、make 默认形成的是从上向下遇到的第一个目标文件!! 且执行一组依赖关系和依赖方法
如图:
[图片:make 逻辑图]
思考:make / Makefile:具体是如何形成可执行程序的呢???推导过程又是如何呢?
我们打开 vim 编辑 Makefile
[图片:Makefile 内容]
我们 make 一下:
[图片:编译过程 1]
[图片:编译过程 2]
[图片:编译过程 3]
make 是如何工作的,在默认的方式下,也就是我们只输入 make 命令。那么:
清理工程:
[图片:清理工程 1]
[图片:清理工程 2]
[图片:PHONY 示例]
这里我们重复 make 发现不让我们 make 了,这里显示的意思是 code 文件没有进行更新
为什么我没有更新就不让我 make 呢?
答:gcc 编译代码的时候,代码如果没有被编译过,或者曾经被修改过,make+gcc 才会进行重新编译!
[图片:重复 make 提示]
那么 make 如何知道 code.c 是否需要被重新编译呢?
这里我们补充一个指令:
查看文件时间:stat code.c
[图片:stat 命令输出]
cat/less/more 打开文件查看内容、用 grep 匹配文件内容等。noatime 挂载选项关闭 atime 的自动更新(避免频繁写磁盘)。vim)修改文件内容后保存、用 echo "内容" >> 文件 追加内容等。chmod 修改权限、chown 变更所有者、mv 重命名文件、rm 删除文件(删除前会改元数据)等。我们再来回顾一下:
文件 = 内容 + 属性
先说结论:文件内容改变影响的是 Modify 时间,文件属性改变影响的是 Change 时间
这里我们改变文件属性,来验证哪个时间会变化:
[图片:修改属性时间]
说明:改变文件属性影响的是文件的 Change 时间,而其他时间不变
我们再来改变文件内容,来验证哪个时间会改变:
[图片:修改内容时间]
这里我们改变 code.c 的内容来验证一下
[图片:内容修改验证]
从图中发现我们改变内容,ACM 时间都改了!!!
在 Linux 系统中,修改文件内容,文件属性就会因此而变化,这是正常的!!!
我们再来读取一下文件信息,来观察时间变化情况:
[图片:读取文件信息]
先说结论这里读取文件信息会影响的是 Access 时间
但是你图片上展示的 Access、Modify、Change 时间都是一样的啊?我怎么知道你改变的是哪个?
说明:
我们读取文件会导致大量 IO 操作,Linux 系统不会傻傻的你用一次就给你更新一下 Access 时间,这样会导致太大的 IO 操作,Linux 对其进行特殊处理,它有一个策略,就是在你访问 7、8 次识别出来,他才会更新一下,最后总结一下:Access 时间更新最频繁,但是他不是常更新,他有自己的策略,和系统有关
那你还没有给我解决前一个问题啊:make 怎么知道我的文件是否需要被重新编译呢?
我们再来 make 一下:
[图片:make 再次执行]
**永远都是先有源文件才有目标可执行文件!**所以我们可以来画个图:
[图片:源文件与目标文件关系]
code 文件时间比源文件时间新,此时我们再 make 一下看看结果
[图片:时间对比结果]
果然如此,所以我们得到结论:make 是如何知道 code.c 需要被重新编译?
解析:
那么我可执行程序的时间比源文件新,说明源文件没有更新,也就说明 Modify 时间没有变!,如果源文件比可执行程序文件新,说明我们源文件被改了,Modify 时间变了!此时 make 就知道 code.c 需要被重新编译了!
答:只要 code.c 的 Modify 时间比 code 可执行程序文件新,就需要重新编译!
那么我们将.PHONY 修饰一下 gcc 编译试一下:
[图片:PHONY 修饰]
我们再来 make 一下:
[图片:PHONY 执行结果]
此时我们发现 make 竟然可以一直执行,那么我想问一下大家知道.PHONY 的本质了嘛?
.PHONY 的本质:忽略实践对比!
但是我们不建议用.PHONY 进行修饰!
在编译过程中,我们都倾向于将源文件 .c 先编译为 .o 文件
[图片:分步编译]
分两步是比较推荐的做法!
[图片:推荐做法]
code:code.o gcc -o $@ $^ // $@代表 code $^代表 code.o
code.o:code.c gcc -c code.c
.PHONY:clean
clean: rm -rf code.o code.exe
[图片:变量定义]
我们再来 make 一下看是否可以执行
[图片:变量执行结果]
我们可以观察到都是可以执行的!
[图片:变量打印]
我们再来打印一下要进行的操作!
[图片:打印操作]
有没有发现这样很挫,能不能不让它回显:有的!
[图片:屏蔽回显]
我们只需要在前面加上@符号即可,我们再来 make 一下:
[图片:屏蔽成功]
这样他就不会回显出来了!
那么如果不想让 gcc -c code.c 和 gcc -o code.exe code.o 回显,同样可以在他们前面加上@
[图片:全部屏蔽]
[图片:全部屏蔽结果]
查看文件:
[图片:文件查看 1]
[图片:文件查看 2]
[图片:$<和$^]
$^ 表示所有的依赖文件
$< 代表第一个依赖文件
[图片:$<和$^示例]
如果我们创建了 10 个文件都进行编译,我们来看一下 make 结果:
[图片:10 个文件编译]
我们创建了 10 个.c 文件,但为什么就给我一个.o 编译为.exe?
这是因为我们在声明.o 文件时只声明一个,那么向编译 10 个就要全写上嘛?那如果 100 个那岂不是很麻烦?
[图片:问题展示]
那么接下来有两种方法来进行展开:
做法一:
[图片:做法一]
[图片:做法一结果]
这时候就可以观察到全部展开了!
我们发现没有.o 文件啊,这里有个语法 make 自动识别:
[图片:自动识别]
我们再来 make 一下
[图片:make 执行]
这样.o 文件也展开了
我们再对 Makefile 进一步优化:
[图片:优化 1]
[图片:优化 2]
这样就可以全部删除了
再进行优化:
[图片:优化 3]
如果我们创建上 1000 个.c 文件进行编译,我们来看一下它的编译时间:
[图片:1000 文件编译]
这里大家可能看不到过程,其实是编译了很长时间的,我们在 make 一下发现他不让我百衲衣,因为会花费大量时间,我们再来 code.c 把源文件修改一下,如何发现再 make 它编译一下就出来了,这是为什么呢?
因为他只编译了被修改的内容!!!
我们之前说到的展开文件还有一个做法也是比较推荐的:
[图片:推荐做法 2]
如果你是 Linux 开发新手,建议从 '多文件案例' 开始动手实践,重点注意 Tab 键和变量简化这两个细节 —— 上手后会发现,Makefile 其实比想象中简单!

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online