

🥰个人专栏: 《C++知识分享》 《Linux 入门到实践:零基础也能懂》
🌠 有善始者实繁,能克终者盖寡
索引与导读
- 前言
-
- 一、make是一条命令,makefile是一个文件
- 二、最初始的Makefile
-
- 1)Makefile的基本机构
-
- [1.1 编译规则:目标的生成逻辑](#1.1 编译规则:目标的生成逻辑)
- [1.2 伪目标:管理清理逻辑](#1.2 伪目标:管理清理逻辑)
- 2)实际操作演示
- 3)核心用法
- 三、深入理解Makefile
- 四、进阶用法:变量与函数
-
- 1)变量定义与使用
- 2)常用函数(简化文件列表)
-
- [2.1 wildcard:获取指定模式的文件](#2.1 wildcard:获取指定模式的文件)
- [2.2 替换文件后缀](#2.2 替换文件后缀)
- 3)自动变量(简化命令)
- [工程化示例(多源文件适配,简化 Makefile/makefile)](#工程化示例(多源文件适配,简化 Makefile/makefile))
-
- [1. 核心作用:自动化编译与管理](#1. 核心作用:自动化编译与管理)
- [2. 代码逻辑大揭秘](#2. 代码逻辑大揭秘)
- [3. 你可以怎么使用它?](#3. 你可以怎么使用它?)
- [💻结尾--- 核心连接协议](#💻结尾— 核心连接协议)
前言
在实际的 Linux 项目开发中,随着源文件数量的增加,手动使用 gcc/g++ 逐个编译不仅效率低下,且极易出错 为了实现工程化的管理,make 工具与 Makefile 文件应运而生 它定义了一套完整的编译规则和依赖体系,能够自动识别哪些文件已被修改,从而实现"按需编译",极大提升了构建效率
本文将深入浅出地拆解 Makefile 的核心语法、变量使用以及隐式规则,带你从零搭建一套工业级的自动化构建系统,彻底告别繁琐的重复劳动
一、make是一条命令,makefile是一个文件
-
一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,
makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作 -
makefile带来的好处就是------"自动化编译",一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率 。所以,会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力 -
makefile带来的好处就是------"自动化编译",一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率 -
make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make可见,makefile都成为了一种在工程方面的编译方法
二、最初始的Makefile
假设项目只有一个源文件hello.c,要生成可执行文件,并支持清理编译产物
1)Makefile的基本机构
一个典型的 Makefile 由一系列规则组成,每一条规则的基本格式如下:
1.1 编译规则:目标的生成逻辑
Makefile
code: code.c
gcc -o code code.c
关键语法规则:
-
目标 : 你想要生成的文件名(通常是可执行文件或目标文件
.o) -
依赖 : 生成目标所需要的源文件(
.c、.h等) -
命令 : 生成目标所需要执行的具体操作(注意:命令前必须有一个
TAB键,不能使用空格)
1.2 伪目标:管理清理逻辑
Makefile
.PHONY: clean
clean:
rm -f code
-
.PHONY: clean:这是一个非常重要的指令,它告诉make:clean只是一个任务名称(标签),而不是一个真实存在的文件名 如果不加这一行,如果你的目录下恰好有一个名为clean的文件,make clean就可能不会执行 -
clean:这是目标名称 当你运行make clean时,就会触发这里的逻辑 -
rm -f code:-
rm是删除命令 -
-f的作用是强制删除 即使code文件已经被删除了,make clean也不会报错,而是直接忽略
-
2)实际操作演示
bash
fthq@fthq-virtual-machine:~/Linux_test$ vim Makefile
fthq@fthq-virtual-machine:~/Linux_test$ make
gcc -o hello hello.c
fthq@fthq-virtual-machine:~/Linux_test$ ll
总计 880
drwxrwxr-x 2 fthq fthq 4096 5月 19 21:29 ./
drwxr-x--- 18 fthq fthq 4096 5月 19 21:29 ../
-rwxrwxr-x 1 fthq fthq 15960 5月 19 21:29 hello*
-rw-rw-r-- 1 fthq fthq 69 5月 17 23:43 hello.c
-rwxrwxr-x 1 fthq fthq 15960 5月 18 22:20 hello_dynamic*
-rwxrwxr-x 1 fthq fthq 15960 5月 18 19:03 hello.exe*
-rwxrwxr-x 1 fthq fthq 15960 5月 18 19:05 hello.exe1*
-rw-rw-r-- 1 fthq fthq 21301 5月 17 23:53 hello.i
-rw-rw-r-- 1 fthq fthq 1497 5月 18 18:59 hello.o
-rw-rw-r-- 1 fthq fthq 666 5月 18 18:56 hello.s
-rwxrwxr-x 1 fthq fthq 785360 5月 18 22:08 hello_static*
-rw-rw-r-- 1 fthq fthq 69 5月 19 21:29 Makefile
fthq@fthq-virtual-machine:~/Linux_test$ ./hello
Hello world
fthq@fthq-virtual-machine:~/Linux_test$ make clean
rm -f hello
fthq@fthq-virtual-machine:~/Linux_test$ ll
总计 864
drwxrwxr-x 2 fthq fthq 4096 5月 19 21:33 ./
drwxr-x--- 18 fthq fthq 4096 5月 19 21:29 ../
-rw-rw-r-- 1 fthq fthq 69 5月 17 23:43 hello.c
-rwxrwxr-x 1 fthq fthq 15960 5月 18 22:20 hello_dynamic*
-rwxrwxr-x 1 fthq fthq 15960 5月 18 19:03 hello.exe*
-rwxrwxr-x 1 fthq fthq 15960 5月 18 19:05 hello.exe1*
-rw-rw-r-- 1 fthq fthq 21301 5月 17 23:53 hello.i
-rw-rw-r-- 1 fthq fthq 1497 5月 18 18:59 hello.o
-rw-rw-r-- 1 fthq fthq 666 5月 18 18:56 hello.s
-rwxrwxr-x 1 fthq fthq 785360 5月 18 22:08 hello_static*
-rw-rw-r-- 1 fthq fthq 69 5月 19 21:29 Makefile
3)核心用法
-
编译项目 :在
Makefile所在目录执行make,自动查找第一个目标(code),检查依赖是否更新,执行编译命令 -
清理项目 :执行
make clean,执行clean目标下的命令,删除可执行文件 -
增量编译 :修改
code.c后再次执行make,只会重新编译修改后的文件,而非全部重编
三、深入理解Makefile
1)基本语法格式
bash
目标:依赖
命令1
命令2
...
- 目标可以是可执行文件、目标文件(.o)、伪目标(如clean);
- 依赖可以是源文件、目标文件、其他目标;
- 命令必须以
Tab 键开头(不能用空格,否则make会报错); - 注释以
#开头,单行有效
2)深入理解依赖关系与推导(入栈与出栈)
makefile会默认形成第一个自己遇到的目标文件
- Makefile:
makefile
code: code.o
gcc code.o -o code
code.o: code.s
gcc -c code.s -o code.o
code.s: code.i
gcc -S code.i -o code.s
code.i: code.c
gcc -E code.c -o code.i
- 入栈顺序

- 出栈顺序


最佳实践
bash
# 最终目标:可执行文件code.exe,依赖code.o
code.exe: code.o
gcc code.o -o code.exe
# 目标文件code.o,依赖code.c
code.o: code.c
gcc -c code.c -o code.o
# 伪目标clean
.PHONY: clean
clean:
rm -f code.exe code.o

3)伪目标
伪目标不是实际文件 ,而是一个 "命令标签"(如clean)
-
作用 : 避免项目中存在与伪目标同名的文件,导致
make误判为"目标已存在,无需执行" -
语法 :
.PHONY: 伪目标名,例如.PHONY: clean,确保make clean总是执行命令
解释:伪目标总是被执行



思考:make如何知道源文件是否被重新编译?

这里给一个结论:只要源文件的Modify时间比可执行文件时间新,就可以重新编译;接下来会解释原因
- 文件 = 内容 + 属性
- 属性改变了,内容没变------
创建时间就会改变 - 内容改变了------
变更时间改变,创建时间也会改变 (因为文件内容的改变会影响文件的属性,比如文件大小等)
- 属性改变了,内容没变------
注意:当我们touch 文件名(可以更新文件的所有时间)
四、进阶用法:变量与函数
随着项目源文件数量的增长,手动配置依赖关系和编译指令的效率较低。利用 Makefile 的变量与函数功能,可大幅简化配置并提升代码复用性
1)变量定义与使用
变量本质上是文本替换。在 Makefile 中:
- 变量名通常使用大写字母;语法:
变量名=值 - 使用时需要使用
$(变量名) 或 ${变量名}
bash
# 定义变量:编译器、可执行文件名、目标文件列表、清理命令
CC = gcc # 给编译器 gcc 起个名字叫 CC
BIN = myproc # 给生成的程序名字叫 myproc
RM = rm -f # 给删除文件的命令起个名字叫 RM
CFLAGS = -Wall -g # 编译参数(-Wall显示所有警告,-g生成调试信息)
bash
# 最终目标:依赖OBJ变量
$(BIN): $(OBJ)
$(CC) $(CFLAGS) -o $(BIN) $(OBJ)
当 make 执行时:
-
$(CC)变成了gcc(编译器名称) -
$(CFLAGS)变成了-Wall -g(编译选项) -
-o是一个参数,固定写法,表示"输出到..." -
$(BIN)变成了myproc(你最终想要的可执行文件名) -
$(OBJ)变成了myproc.o(你要参与链接的零件文件)
bash
# 目标文件依赖源文件(可省略,make自动推导)
myproc.o: myproc.c
bash
# 清理伪目标
.PHONY: clean
clean:
$(RM) $(BIN) $(OBJ)
2)常用函数(简化文件列表)
Makefile 提供内置函数,可自动获取文件列表,无需手动罗列
2.1 wildcard:获取指定模式的文件
bash
# 获取当前目录下所有.c文件,存入SRC变量
SRC = $(wildcard *.c)
2.2 替换文件后缀
bash
# 将SRC中的.c文件替换为.o文件,存入OBJ变量
Obj=$(Src:.c=.o)
3)自动变量(简化命令)
Makefile 提供自动变量,替代命令中重复出现的目标和依赖,简化书写:
| 自动变量 | 含义 | 示例 |
|---|---|---|
$@ |
当前目标文件名 | $(CC) -o $@ |
$^ |
所有不重复的依赖文件 | $(CC) -o $@ |
$< |
第一个依赖文件名 | $(CC) -c $< -o $@ |
详细讲解
bash
# 定义编译器和基础参数
CC = gcc
CFLAGS = -Wall
# 定义目标文件名
TARGET = my_program
# 定义依赖文件列表
OBJS = main.o math_tools.o
# 编译总规则
$(TARGET): $(OBJS)
$(CC) $^ -o $@
# 编译单个文件的规则
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
A. $@(目标文件)
-
规则行 :
$(TARGET): $(OBJS) -
具体动作 :
gcc main.o math_tools.o -o my_program -
这里的
$@:自动变成了my_program -
作用 :它确保了输出的文件名永远和你规则里定义的左侧目标名一致 如果你想把程序名改掉,只需改
TARGET一行,下面的命令完全不用动
B. $^(所有依赖)
-
规则行 :
$(CC) $^ -o $@ -
具体动作 :
gcc main.o math_tools.o -o my_program -
这里的
$^:自动变成了main.o math_tools.o -
作用 :它把冒号右边的所有文件(
OBJS)全部罗列出来传给gcc如果有10个依赖文件,写$^比手动一个一个写要省事得多,也绝不会漏掉文件
C. $<(第一个依赖)
-
规则行 :
$(CC) $(CFLAGS) -c $< -o $@ -
具体动作 :当
make处理main.o时,它会自动展开为gcc -Wall -c main.c -o main.o -
这里的
$<:自动变成了main.c -
作用 :这是编译过程最精妙的地方 对于每个
.o文件,它只需要"第一个"对应的.c原材料$<自动抓取当前任务对应的那个源文件,让这一行通用的规则可以处理项目中成百上千个不同的.c文件
工程化示例(多源文件适配,简化 Makefile/makefile)
假设项目有多个源文件,Makefile 可自动适配,简化后的 Makefile 如下所示:
bash
Bin=code.exe # 定义变量
#Src=$(shell ls *.c) # 做法1 -- 采用shell命令行方式,获取当前所有.c文件名
Src=$(wildcard *.c) # 做法2 -- 使用 wildcard 函数,获取当前所有.c文件名
Obj=$(Src:.c=.o) # 将Src的所有同名 .c 替换成 .o 形成目标文件列表
Echo=echo
cc=gcc
Rm=rm -f
Flags=-c -Wall # 编译选项
LD_Flags=-o # 链接选项
$(Bin):$(Obj)
@$(Echo) "我要开始链接了...$(Obj) -> $(Bin)" # $@: 代表目标文件名 $^: 代表依赖文件列表
@$(cc) $(LD_Flags) $@ $^
%.o:%.c # %.c: 展开当前目录下的所有.c %.o: 同时展开同名.o
@$(Echo) "我要开始编译了...$< -> $@" # @: 不回显命令
@$(cc) $(Flags) $< # %<: 对展开的依赖.c文件,一个个的交给gcc
.PHONY:clean
clean:
$(Rm) $(Obj) $(Bin) # $(RM): 替换,用变量内容替换它
.PHONY:debug
debug:
@$(Echo) "Bin: $(Bin)"
@$(Echo) "Obj: $(Obj)"
@$(Echo) "Src: $(Src)"
~
这是一个非常标准且高质量的 Makefile 文件
1. 核心作用:自动化编译与管理
这个 Makefile 的主要任务是:
-
自动发现文件 :它通过
wildcard函数,自动扫描目录下所有的.c文件,无论你增加了多少个源文件,它都能自动识别 -
分步构建:
- 先编译:将每个
.c变成.o(目标文件) - 再链接:将所有的
.o文件组装成最终的可执行程序code.exe
- 先编译:将每个
-
方便清理 :通过
make clean指令,一键删除所有产生的临时文件,保持目录整洁
2. 代码逻辑大揭秘
| 模块 | 代码片段 | 逻辑解释 |
|---|---|---|
| 定义变量 | Bin, Src, Obj |
给项目配置"起名字",方便修改 比如把 gcc 改成 clang 只要改 cc 变量 |
| 链接动作 | $(Bin): $(Obj) |
这是最终目标 告诉计算机:要生成 code.exe,必须先有所有的 .o 文件 |
| 编译动作 | %.o: %.c |
这是自动化编译规则 % 是通配符,表示无论你有多少个文件,它都会一个个地处理 |
| 清理伪目标 | .PHONY: clean |
这是一个安全机制 防止目录下正好有个真实文件叫 clean 而导致指令失效 |
| 调试信息 | debug: |
这是一个自检工具 你执行 make debug 就能在屏幕上看到 Makefile 当前识别到的所有文件名,非常适合排错 |
3. 你可以怎么使用它?
假设这个文件名为 Makefile,把它放在你的代码目录下,在终端里执行以下命令:
-
make:直接开始编译,它会自动识别所有的.c文件并生成code.exe -
make clean:彻底清理目录,删除所有生成的.o和code.exe,让目录变回最初的状态 -
make debug:查看当前Makefile到底获取到了哪些文件名,确认配置是否正确
💻结尾--- 核心连接协议
警告: 🌠🌠正在接入底层技术矩阵。如果你已成功破解学习中的逻辑断层,请执行以下指令序列以同步数据:🌠🌠
【📡】 建立深度链接: 关注本终端。在赛博丛林中深耕底层架构,从原始代码到进阶协议,同步见证每一次系统升级。
【⚡】 能量过载分发: 执行点赞操作。通过高带宽分发,让优质模组在信息流中高亮显示,赋予知识跨维度的传播力。
【💾】 离线缓存核心: 将本页加入收藏。把这些高频实战逻辑存入你的离线存储器,在遭遇系统崩溃或需要离线检索时,实现瞬时读取。
【💬】 协议加密解密: 在评论区留下你的散列码。分享你曾遭遇的代码冲突或系统漏洞(那些年踩过的坑),通过交互式编译共同绕过技术陷阱。
【🛰️】 信号频率投票: 通过投票发射你的选择。你的每一次点击都在重新定义矩阵的进化方向,决定下一个被全量拆解的技术节点。

