💯枫亭湖区:个人主页
🥰个人专栏:《C++知识分享》 《Linux 入门到实践:零基础也能懂》
🌠 有善始者实繁,能克终者盖寡
索引与导读
- 前言
- 一、make是一条命令,makefile是一个文件
- 二、最初始的Makefile
- 1)Makefile的基本机构
- 1.1 编译规则:目标的生成逻辑
- 1.2 伪目标:管理清理逻辑
- 2)实际操作演示
- 3)核心用法
- 三、深入理解Makefile
- 1)基本语法格式
- 2)深入理解依赖关系与推导(入栈与出栈)
- 最佳实践
- 3)伪目标
- 解释:伪目标总是被执行
- 思考:make如何知道源文件是否被重新编译?
- 四、进阶用法:变量与函数
- 1)变量定义与使用
- 2)常用函数(简化文件列表)
- 2.1 wildcard:获取指定模式的文件
- 2.2 替换文件后缀
- 3)自动变量(简化命令)
- 详细讲解
- 工程化示例(多源文件适配,简化 Makefile/makefile)
- 1. 核心作用:自动化编译与管理
- 2. 代码逻辑大揭秘
- 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 编译规则:目标的生成逻辑
code: code.c gcc -o code code.c关键语法规则:
目标: 你想要生成的文件名(通常是可执行文件或目标文件
.o)依赖: 生成目标所需要的源文件(
.c、.h等)命令: 生成目标所需要执行的具体操作(注意:命令前必须有一个
TAB键,不能使用空格)
1.2 伪目标:管理清理逻辑
.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)实际操作演示
fthq@fthq-virtual-machine:~/Linux_test$vimMakefile fthq@fthq-virtual-machine:~/Linux_test$makegcc-ohello hello.c fthq@fthq-virtual-machine:~/Linux_test$ ll 总计880drwxrwxr-x2fthq fthq40965月1921:29 ./ drwxr-x---18fthq fthq40965月1921:29../ -rwxrwxr-x1fthq fthq159605月1921:29 hello* -rw-rw-r--1fthq fthq695月1723:43 hello.c -rwxrwxr-x1fthq fthq159605月1822:20 hello_dynamic* -rwxrwxr-x1fthq fthq159605月1819:03 hello.exe* -rwxrwxr-x1fthq fthq159605月1819:05 hello.exe1* -rw-rw-r--1fthq fthq213015月1723:53 hello.i -rw-rw-r--1fthq fthq14975月1818:59 hello.o -rw-rw-r--1fthq fthq6665月1818:56 hello.s -rwxrwxr-x1fthq fthq7853605月1822:08 hello_static* -rw-rw-r--1fthq fthq695月1921:29 Makefile fthq@fthq-virtual-machine:~/Linux_test$ ./hello Hello world fthq@fthq-virtual-machine:~/Linux_test$makecleanrm-fhello fthq@fthq-virtual-machine:~/Linux_test$ ll 总计864drwxrwxr-x2fthq fthq40965月1921:33 ./ drwxr-x---18fthq fthq40965月1921:29../ -rw-rw-r--1fthq fthq695月1723:43 hello.c -rwxrwxr-x1fthq fthq159605月1822:20 hello_dynamic* -rwxrwxr-x1fthq fthq159605月1819:03 hello.exe* -rwxrwxr-x1fthq fthq159605月1819:05 hello.exe1* -rw-rw-r--1fthq fthq213015月1723:53 hello.i -rw-rw-r--1fthq fthq14975月1818:59 hello.o -rw-rw-r--1fthq fthq6665月1818:56 hello.s -rwxrwxr-x1fthq fthq7853605月1822:08 hello_static* -rw-rw-r--1fthq fthq695月1921:29 Makefile3)核心用法
编译项目:在
Makefile所在目录执行make,自动查找第一个目标(code),检查依赖是否更新,执行编译命令清理项目:执行
make clean,执行clean目标下的命令,删除可执行文件增量编译:修改
code.c后再次执行make,只会重新编译修改后的文件,而非全部重编
三、深入理解Makefile
1)基本语法格式
目标:依赖 命令1 命令2...- 目标可以是可执行文件、目标文件(.o)、伪目标(如clean);
- 依赖可以是源文件、目标文件、其他目标;
- 命令必须以
Tab 键开头(不能用空格,否则make会报错); - 注释以
#开头,单行有效
2)深入理解依赖关系与推导(入栈与出栈)
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- 入栈顺序
- 出栈顺序
最佳实践
# 最终目标:可执行文件code.exe,依赖code.ocode.exe: code.o gcc code.o-ocode.exe# 目标文件code.o,依赖code.ccode.o: code.c gcc-ccode.c-ocode.o# 伪目标clean.PHONY: clean clean:rm-fcode.exe code.o3)伪目标
伪目标不是实际文件,而是一个 “命令标签”(如clean)
作用: 避免项目中存在与伪目标同名的文件,导致
make误判为“目标已存在,无需执行”语法:
.PHONY: 伪目标名,例如.PHONY: clean,确保make clean总是执行命令
解释:伪目标总是被执行
思考:make如何知道源文件是否被重新编译?
这里给一个结论:只要源文件的Modify时间比可执行文件时间新,就可以重新编译;接下来会解释原因
- 文件 = 内容 + 属性
- 属性改变了,内容没变——
创建时间就会改变 - 内容改变了——
变更时间改变,创建时间也会改变(因为文件内容的改变会影响文件的属性,比如文件大小等)
- 属性改变了,内容没变——
注意:当我们touch 文件名(可以更新文件的所有时间)
四、进阶用法:变量与函数
随着项目源文件数量的增长,手动配置依赖关系和编译指令的效率较低。利用Makefile的变量与函数功能,可大幅简化配置并提升代码复用性
1)变量定义与使用
变量本质上是文本替换。在 Makefile 中:
- 变量名通常使用大写字母;语法:
变量名=值 - 使用时需要使用
$(变量名) 或 ${变量名}
# 定义变量:编译器、可执行文件名、目标文件列表、清理命令CC=gcc# 给编译器 gcc 起个名字叫 CCBIN=myproc# 给生成的程序名字叫 myprocRM=rm-f# 给删除文件的命令起个名字叫 RMCFLAGS=-Wall-g# 编译参数(-Wall显示所有警告,-g生成调试信息)# 最终目标:依赖OBJ变量$(BIN):$(OBJ)$(CC)$(CFLAGS)-o$(BIN)$(OBJ)当make执行时:
$(CC)变成了gcc(编译器名称)$(CFLAGS)变成了-Wall -g(编译选项)-o是一个参数,固定写法,表示“输出到…”$(BIN)变成了myproc(你最终想要的可执行文件名)$(OBJ)变成了myproc.o(你要参与链接的零件文件)
# 目标文件依赖源文件(可省略,make自动推导)myproc.o: myproc.c# 清理伪目标.PHONY: clean clean:$(RM)$(BIN)$(OBJ)2)常用函数(简化文件列表)
Makefile提供内置函数,可自动获取文件列表,无需手动罗列
2.1 wildcard:获取指定模式的文件
# 获取当前目录下所有.c文件,存入SRC变量SRC=$(wildcard *.c)2.2 替换文件后缀
# 将SRC中的.c文件替换为.o文件,存入OBJ变量Obj=$(Src:.c=.o)3)自动变量(简化命令)
Makefile提供自动变量,替代命令中重复出现的目标和依赖,简化书写:
| 自动变量 | 含义 | 示例 |
|---|---|---|
$@ | 当前目标文件名 | $(CC) -o $@ |
$^ | 所有不重复的依赖文件 | $(CC) -o $@ |
$< | 第一个依赖文件名 | $(CC) -c $< -o $@ |
详细讲解
# 定义编译器和基础参数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如下所示:
Bin=code.exe# 定义变量#Src=$(shell ls *.c) # 做法1 -- 采用shell命令行方式,获取当前所有.c文件名Src=$(wildcard *.c)# 做法2 -- 使用 wildcard 函数,获取当前所有.c文件名Obj=$(Src:.c=.o)# 将Src的所有同名 .c 替换成 .o 形成目标文件列表Echo=echocc=gccRm=rm-fFlags=-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.exemake clean:彻底清理目录,删除所有生成的.o和code.exe,让目录变回最初的状态make debug:查看当前Makefile到底获取到了哪些文件名,确认配置是否正确
💻结尾— 核心连接协议
警告:🌠🌠正在接入底层技术矩阵。如果你已成功破解学习中的逻辑断层,请执行以下指令序列以同步数据:🌠🌠
【📡】 建立深度链接:关注本终端。在赛博丛林中深耕底层架构,从原始代码到进阶协议,同步见证每一次系统升级。
【⚡】 能量过载分发:执行点赞操作。通过高带宽分发,让优质模组在信息流中高亮显示,赋予知识跨维度的传播力。
【💾】 离线缓存核心:将本页加入收藏。把这些高频实战逻辑存入你的离线存储器,在遭遇系统崩溃或需要离线检索时,实现瞬时读取。
【💬】 协议加密解密:在评论区留下你的散列码。分享你曾遭遇的代码冲突或系统漏洞(那些年踩过的坑),通过交互式编译共同绕过技术陷阱。
【🛰️】 信号频率投票:通过投票发射你的选择。你的每一次点击都在重新定义矩阵的进化方向,决定下一个被全量拆解的技术节点。