【Linux】Make/Makefile (自动化构建):从“是什么”到“会用它”

发布于:2025-08-30 ⋅ 阅读:(18) ⋅ 点赞:(0)

前言:欢迎各位光临本博客,这里小编带你直接手撕Make/Makefile (自动化构建),文章并不复杂,愿诸君耐其心性,忘却杂尘,道有所长!!!!

解释_chmod_命令_(3).gif
🔥个人主页IF’Maxue-CSDN博客

🎬作者简介:C++研发方向学习者

📖**个人专栏:
《C语言》
《C++深度学习》
《Linux》
《数据结构》
《数学建模》
**

⭐️人生格言:生活是默默的坚持,毅力是永久的享受。不破不立,远方请直行!

一、先搞懂:Make 和 Makefile 是啥?

简单说,这俩是“搭档”——一个负责“干活”,一个负责“指挥”:

  • make:是 Linux 里的一个命令,就像个“工人”,听指挥做事;
  • makefile:是一个文本文件,就像“施工图纸”,写清楚要做什么、怎么做、需要什么原材料。

它们的最终目的很简单:帮你把写好的代码(比如 .c 文件),自动编译成能运行的程序(比如 mytest)。

看下面两张图就懂了:
第一张图是你的工作目录,里面有 makefile 文件和代码文件(比如 main.c);
image.png

第二张图是执行 make 命令后,“工人”按照 makefile 的指挥,生成了可执行文件 mytest
image.png

二、快速上手:Makefile 的核心逻辑

Makefile 之所以好用,全靠“依赖关系”和“依赖方法”这俩东西,说白了就是:

  • 依赖关系:要做一个“目标”(比如 mytest),得有哪些“原材料”(比如 main.o);
  • 依赖方法:用什么命令把“原材料”做成“目标”(比如 gcc main.o -o mytest)。

看这张图,里面把逻辑写得很清楚:
image.png

  • 目标 mytest 依赖 main.o
  • 要做 mytest,就执行 gcc main.o -o mytest
  • main.o 又依赖 main.c,要做 main.o 就执行 gcc -c main.c

执行 make 命令后,它会按这个逻辑一步步走,最后生成 mytest,就像下面这张图的效果:
image.png

三、核心思想:不做无用功

Make 的精髓就一个:只编译“改了的文件”
比如你改了 main.c,Make 会对比 main.cmain.o 的“修改时间”(后面会讲),发现 main.c 更新,就重新编译 main.o;如果没改,就跳过这一步,直接用之前的 main.o 生成 mytest——省时间,效率高。

四、Makefile 具体语法:这些细节要记住

1. 清除编译垃圾:clean 目标

编译后会生成 .o 文件(中间文件)和可执行文件,想一键删除这些“垃圾”,就加个 clean 目标,写法看下面的图:
image.png

  • rm -rf *.o mytest:就是删除所有 .o 文件和 mytest
  • 用的时候输命令:make clean

2. Make 的“默认行为”

Make 会从上到下扫描 Makefile,默认只生成第一个目标(比如前面的 mytest)。
如果想生成其他目标(比如 clean),就用 make 目标名(比如 make clean)。

3. 伪目标:.PHONY 是干啥的?

先想个问题:如果你的目录里不小心有个叫 clean 的文件,再执行 make clean,Make 会觉得“clean 文件已经存在,不用再执行了”——这就错了!

这时候就要用 伪目标:用 .PHONY 修饰的目标,不管有没有同名文件,都会执行对应的命令(忽略时间对比)。

写法看下面的图,给 clean 加个 .PHONY: clean 就行:
image.png

为什么需要伪目标?本质是“时间对比”

Make 判断要不要重新编译,靠的是“文件修改时间”(Modify 时间):

  • 如果“原材料”(比如 main.c)的 Modify 时间,比“目标”(比如 main.o)新,就重新编译;
  • 如果“目标”时间更新,就跳过。

下面这张图能看到时间对比的逻辑:
image.png

补充:文件的三组时间

Linux 里文件有三个关键时间,Make 主要看前两个:

  • Modify:文件内容修改的时间(最关键,改代码会变);
  • Access:文件被访问的时间(比如打开看一眼),但因为历史设计,不是实时更新;
  • (还有个 Change 是文件属性修改时间,比如改文件名,Make 用得少)

看这张图,能直观看到这些时间:
image.png

4. 注释:# 后面的内容是“备注”

Makefile 里用 # 写注释,# 后面的内容不会被执行,只是给人看的,比如下面这张图:
image.png
# 这是注释,不会执行 这句话就是备注,告诉别人这行代码是干嘛的。

5. 编译文件:从 .c 到 .o 再到可执行文件

前面其实提过编译步骤,这里看具体代码:
第一张图是“把 .c 编译成 .o”(gcc -c 是生成中间文件):
image.png
第二张图是“把 .o 编译成可执行文件 mytest”:
image.png

6. 推导原则:像“堆积木”一样处理依赖

Make 处理依赖时,会像“入栈出栈”一样,先处理最底层的依赖,再往上走。
比如要生成 mytest,得先有 main.o;要生成 main.o,得先有 main.c——Make 会先找 main.c 编译成 main.o(入栈处理依赖),再用 main.o 生成 mytest(出栈处理目标)。

看这张图,能理解这个“层层推导”的过程:
image.png

五、通用 Makefile:一次写好,多次复用

如果代码文件多了(比如有 main.cadd.csub.c),一个个写依赖太麻烦,这时候就需要“通用 Makefile”——自动找源文件、自动生成中间文件,不用手动改。

1. 基础通用版

先看下面这张基础通用 Makefile 的图:
image.png
里面用了变量(比如 CC=gcc 表示编译器,TARGET=mytest 表示目标程序),但还不够灵活,比如源文件多了要手动加。

2. 优化通用版:自动找文件、自动替换

下面这张是优化后的版本,解决了“手动加文件”的问题:
image.png
这里有几个关键技巧,咱们一个个说:

技巧1:动态获取所有 .c 文件(SRC 变量)

SRC=$(wildcard *.c)wildcard 是 Makefile 的“通配符函数”,意思是“找当前目录下所有 .c 文件”,不用手动写 main.c add.c 了,看这张图:
image.png

技巧2:@ 号:隐藏命令本身,只显示结果

如果写 echo "编译中...",执行 make 时会先打印命令 echo "编译中...",再打印 编译中...(两份内容);
加个 @ 变成 @echo "编译中...",就只显示 编译中...,干净多了,看这张图:
image.png

这是 Makefile 自己的语法,和 Shell 里的“隐藏命令输出”逻辑类似,看这张图的对比:
image.png

技巧3:自动把 .c 换成 .o(OBJ 变量)

OBJ=$(patsubst %.c,%.o,$(SRC))patsubst 是“替换函数”,意思是“把 SRC 里所有 .c 后缀,换成 .o 后缀”。
比如 SRCmain.c add.cOBJ 就自动变成 main.o add.o,不用手动写了,看这张图:
image.png

技巧4:$@ 和 $^:简化目标和依赖
  • $@:代表“当前目标”(比如下面图里的 $(TARGET),也就是 mytest);
  • $^:代表“当前目标的所有依赖”(比如下面图里的 $(OBJ),也就是 main.o add.o)。

用这两个符号,就能把 gcc main.o add.o -o mytest 简化成 $(CC) $^ -o $@,看这张图:
image.png

技巧5:% 和 $<:批量处理 .o 依赖

%.o: %.c 写“模式规则”,意思是“所有 .o 文件,都依赖对应的 .c 文件”(比如 main.o 依赖 main.cadd.o 依赖 add.c)。
再用 $< 代表“当前依赖的第一个文件”(这里就是对应的 .c 文件),所以 gcc -c $< -o $@ 就等于“把当前 .c 编译成对应的 .o”,批量处理所有文件,看这张图:
image.png

3. 通用 Makefile 效果

最后看这两张图,左边是执行 make 自动编译,右边是执行 make clean 自动清除垃圾,全程不用手动改命令:
image.png
image.png

最后总结

Make/Makefile 本质是“自动化构建工具”——它帮你省去重复输编译命令的麻烦,还能智能判断哪些文件需要重新编译,提高效率。
刚开始可能觉得语法有点绕,但记住“依赖关系+变量简化”这两个核心,再结合上面的通用模板,很快就能上手啦!