
自动化构建-make/Makefile详解
友情专栏:【把Linux"聊"明白】
文章目录
- 自动化构建-make/Makefile详解
- 前言
- 一、make/Makefile是什么?
- 二、make/Makefile的基本概念
- [三、 推导过程](#三、 推导过程)
- [四、"高级版"的 Makefile](#四、“高级版”的 Makefile)
前言
在Linux开发环境中,我们经常需要编译和构建复杂的项目。手动一个个编译源文件不仅效率低下,而且容易出错。这时候,make和Makefile就成为了每个Linux开发者必须掌握的利器。本文将带你从零开始,深入理解make和Makefile的工作原理,并通过实战案例展示如何编写高效的自动化构建脚本。
一、make/Makefile是什么?
简单来说,make是一条命令,Makefile是一个文件 ,两个搭配使用,完成项目自动化构建。
详解:
一个工程中的源文件不计其数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作。
makefile带来的好处就是⸺"自动化编译",一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
make是一个命令工具,是一个解释makefile中指令的命令工具。
二、make/Makefile的基本概念
首先,建立一个Makefile文件
shell
touch Makefile # Makefile/makefile都可以,我们习惯于Makefile
然后,保证你有个mypro.c的c文件,然后对Makefile写入:
shell
mypro:mypro.c
gcc mypro.c -o mypro # 前面的空格是一个tab的结果
依赖关系和依赖方法说明
即mypro依赖于mypro.c,而gcc myproc.c -o myproc 正是这种依赖方法。
在对Makefile写入上述依赖关系与依赖方法之后,我们就可以直接使用make命令了。
上面我们只是完成了Makefile的冰山一角,有个基础的认识。
我们可以用Makefile快速生成可执行,那有没有快速清理项目的呢?
项目清理
我们可以在已经有的Makefile再进行增加:
shell
mypro:mypro.c
gcc mypro.c -o mypro
.PHONY:clean
clean: # 可以没有依赖关系
rm -f myproc
有了上面的Makefile,我们就可以进行项目清理了:
像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要make执行。即命令⸺make clean,以此来清除所有的目标文件,以便重编译。
.PHONY **
但是一般我们这种clean的目标问文件,我们将它设置为伪目标,用.PHONY修饰,伪目标的特性是 总是被执行的**。
我们也可以对第一组依赖关系用.PHONY修饰,来看看效果:
未修饰之前:
修饰之后:
有这些理解,我们来看看什么叫做总是被执行?
什么叫做总是被执行?
我们要知道,在编译的时候,默认老代码(以前编译过)是不会别重新编译的,很好理解,但是编译器是如何知道你是老代码还是新代码/更改过的代码。
我们知道:文件 = 内容 + 属性 ,我们可以用stat来查看文件的属性。
Modify: 内容变更,时间更新
Change:属性变更,时间更新
Access:常指的是文件最近一次被访问的时间。在Linux的早期版本中,每当文件被访问时,其atime都会更新。但这种机制会导致大量的IO操作。具体更新原则,不做过多解释
有了上面的了解,可以,对于每个文件的属性中都有其''时间''属性,我们改变了文件的内容,它的Modify时间一定会变,所以通过Modify时间来进行对比判断它是老代码还是新代码/更改过的代码。和谁对比呢,就和对应的二进制文件中的相对应的时间属性进行对比。
但是,有了.PHONY,它不管老代码还是新代码/更改过的代码,都会重新编译,可见:
.PHONY会让make忽略源文件和可执行目标文件的M时间对比。
三、 推导过程
我们知道,C语言程序从.c文件到可执行文件会经过预处理、编译、汇编、链接,现在我们可以依次在Makefile中实现它:
shell
1 mypro:mypro.o
2 gcc mypro.o -o mypro
3 mypro.o:mypro.s
4 gcc -c mypro.s -o mypro.o
5 mypro.s:mypro.i
6 gcc -S mypro.i -o mypro.s
7 mypro.i:mypro.c
8 gcc -E mypro.c -o mypro.i
9
10 .PHONY:clean
11 clean:
12 rm -f *.i *.s *.o mypro
执行make:
可见这样写也是可以的,那是怎么推导的呢?
当执行make命令时:
详细图解:
接下来,我们来总结分析一下make 是如何工作的 ,我们只 make 命令之后。那么:
-
make 会在当前目录下找名字叫 "Makefile" 或 "makefile" 的文件。
-
如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,它会找到 mypro 这个文件,并把这个文件作为最终的目标文件。
-
如果 mypro 这个文件不存在,或者 mypro 所依赖的 mypro.o 文件的修改时间要比 mypro 这个文件新,那么,它就会执行后面所定义的命令来生成 mypro 这个文件。
-
如果 mypro 所依赖的 mypro.o 文件不存在,那么 make 会在当前文件中找目标为 mypro.o 文件的依赖性,如果找到则再根据那个规则生成 mypro.o 文件。(这有点像一个堆栈的过程)
-
当然,你的 .c 文件和 .h 文件是存在的啦,于是 make 会生成 mypro.o 文件,然后再用 mypro.o 文件完成 make 的终极任务,也就是生成可执行文件 mypro 了。
-
这就是整个 make 的依赖性,make 会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
-
在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么 make 就会直接退出,并报错。而对于后定义的命令的错误,或是编译不成功,make 根本不理。
-
make 只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不存在,那么对不起,我就不工作啦。
四、"高级版"的 Makefile
有了上面一大串的铺垫,我们上面的Makefile文件只是一个较为简单的Makefile,只适合有一个源文件的时候使用,接下来,我要在此基础上在进行升级(适用于多个源文件的情况):
代码
makefile
BIN=proc.exe # 定义变量:最终生成的可执行文件名
CC=gcc # 定义变量:使用的编译器
#SRC=$(shell ls *.c) # 方式一:用 shell 命令获取所有 .c 文件名
SRC=$(wildcard *.c) # 方式二:用 make 自带函数 wildcard 获取所有 .c 文件
OBJ=$(SRC:.c=.o) # 把所有 .c 文件替换成对应的 .o 文件(目标文件列表)
LFLAGS=-o # 链接选项(用于生成可执行文件)
FLAGS=-c # 编译选项(用于生成目标文件)
RM=rm -f # 定义删除命令
# === 构建目标规则 ===
$(BIN):$(OBJ)
@$(CC) $(LFLAGS) $@ $^ # 第一个@:不回显命令
@echo "linking ... $^ to $@"
# === 模式规则:编译每个 .c 文件成 .o 文件 ===
%.o:%.c
@$(CC) $(FLAGS) $<
@echo "compiling ... $< to $@"
# === 清理规则 ===
.PHONY:clean
clean:
$(RM) $(OBJ) $(BIN)
# === 测试输出规则(非编译) ===
.PHONY:test
test:
@echo $(SRC)
@echo $(OBJ)
逐行讲解
定义变量
makefile
BIN=proc.exe
CC=gcc
SRC=$(wildcard *.c)
OBJ=$(SRC:.c=.o)
BIN:最终生成的可执行文件名CC:指定编译器为gccSRC:自动收集当前目录下所有.c文件OBJ:把.c扩展名替换为.o,即生成目标文件列表
例如,当前目录中有:
main.c util.c
则:
SRC = main.c util.c
OBJ = main.o util.o
编译并链接
makefile
$(BIN):$(OBJ)
@$(CC) $(LFLAGS) $@ $^
解释:
- 目标:
proc.exe - 依赖:
main.o util.o - 命令:使用 gcc 链接所有
.o文件生成proc.exe $@:代表目标(这里是proc.exe)$^:代表所有依赖文件(这里是main.o util.o)
模式规则
makefile
%.o:%.c
@$(CC) $(FLAGS) $<
这是 模式匹配规则:
%代表任意匹配部分(相同名字的.c和.o)$<:代表第一个依赖文件(即.c文件)- 命令会自动把每个
.c编译成.o
等价于:
makefile
main.o: main.c
gcc -c main.c
util.o: util.c
gcc -c util.c
清理规则
makefile
.PHONY: clean
clean:
$(RM) $(OBJ) $(BIN)
.PHONY:声明伪目标(表示clean不是文件名)$(RM):删除命令,这里是rm -f- 作用:清除生成的目标文件和可执行文件。
执行:
bash
make clean
即可清空中间文件。
测试规则
makefile
.PHONY: test
test:
@echo $(SRC)
@echo $(OBJ)
输出当前检测到的 .c 和 .o 文件,用于调试验证。
总结
这个 Makefile 利用了 变量 + 通配符 + 模式规则,实现了自动化、多文件可扩展的编译流程,可以认真学习学习。
如果本文对您有启发:
✅ 点赞 - 让更多人看到这篇硬核技术解析 !
✅ 收藏 - 实战代码随时复现
✅ 关注 - 获取Linux系列深度更新
您的每一个[三连]都是我们持续创作的动力!✨










