各位读者大佬好,我是落羽!一个坚持不断学习进步的学生。
如果您觉得我的文章还不错,欢迎多多互三分享交流,一起学习进步!
也欢迎关注我的blog主页: 落羽的落羽
文章目录
- 一、make与Makefile是什么
- 二、使用方法
-
- [1. 基本使用方法](#1. 基本使用方法)
- [2. .PHONY](#2. .PHONY)
- [3. Makefile的扩展语法](#3. Makefile的扩展语法)
一、make与Makefile是什么
一个大型工程中的源文件数不胜数,想象你在开发一个C项目,有上百个源文件,你要把他们全部编译好链接,难道要写上百条gcc命令吗?如果之后修改了一个源文件,还要把所有的源文件重新编译吗?这未免也太麻烦了吧!
于是make与Makefile出手了:
- make:是一个命令,是一个在Linux系统中常用的自动化构建工具。
- Makefile:是一个名为Makefile的文本文件,规定make如何构建和管理项目。
二、使用方法
1. 基本使用方法
首先,肯定必须要有写好的源文件吧,为了编译成可执行程序,这里先以一个test.c为例:

然后,第一步,要创建出Makefile这个文件:touch Makefile
用vim打开Makefile,写如下内容:
test.exe:test.c
gcc test.c -o test.exe
其中test.exe test.c称为依赖关系,冒号前是目标文件,冒号后是依赖文件列表(可以为空,表示不需要依赖任何文件)
第二行gcc test.c -o test.exe称为依赖方法,依赖方法可以是任何shell命令,依赖方法可以有很多行,但是必须都以Tab键开头。
写好之后,保存退出Makefile文件,随后在系统中直接执行make命令:

可以看到,系统就会自动执行Makefile文件中的gcc test.c -o test.exe命令,生成test.exe可执行程序,而且能正常运行。
2. .PHONY
上述语句中,test.exe称为目标文件。
Makefile的语法中,还有一种.PHONY用法,它可以声明一个"伪目标",伪目标可以自己起名。
伪目标不是一个真实存在的文件,语句.PHONY:伪目标名定义一个伪目标,随后几行也要写出他的依赖关系和依赖方法。
举个栗子,一个项目需要被清理(删除),那我们就可以在Makefile中写下:
.PHONY:clean #定义clean伪目标
clean: #依赖关系,没有依赖文件
rm -rf test.exe #依赖方法
保存退出Makefile,随后执行make clean:

其实,在上面生成test.exe的栗子中,我们也可以显示写成make test.exe,效果也是相同的。
使用make命令,后面可以跟"目标名",然后就会解析他的依赖关系和依赖方法。不加目标名只执行make,make默认只会执行Makefile中的开头第一个依赖关系,只会形成一条完整的推导链。一般我们习惯将项目清理写在后面,make默认执行项目生成。
其实从本质来说,.PHONY定义的伪目标,特性是"总是被执行的 ",这个怎么理解呢?
假设现在Makefile中写成这样:

如果连续执行make,发现第二次make执行失败了,显示说test.exe已是最新

如果我将test.c的内容修改一下,再一次执行make,却发现又能执行成功了,并且test.exe也随着test.c的内容更新而变化了:

但是在.PHONY定义的另一种情况下,连续执行make test1.exe,却发现可以一直执行,不会显示程序已至最新:

我们先来谈论make是怎么判断test.exe是最新的?是通过文件的修改时间判断的:
stat命令能显示文件的详细属性

其中有三个时间:Access是文件最近一次被访问的时间、Modify是文件最新内容变更的时间、Change是文件最新属性变更的时间。make的规则是,如果目标文件的修改时间晚于或等于所有依赖文件的修改时间,则认为目标文件是最新的,不需要重新构建。反之,如果任何一个依赖文件的修改时间晚于目标文件,则认为目标是过期的,需要重新执行Makefile中的命令来生成它 。这个修改时间就是Modify时间。
哦!这下我们就明白一开始的问题了,.PHONY的特性是"总是被执行的",也就是说:PHONY让make忽略源文件和可执行文件的Modify时间对比,这样命令就可以反复被执行。
除了文件内容被修改会改变Modify时间,我们也可以用命令touch 文件手动更新一下这个时间。
小细节:一般Modify时间改变同时,Change时间也会改变。因为内容改变了,文件大小也会改变,文件大小也是文件属性。
3. Makefile的扩展语法
Makefile的内置语法功能十分丰富,我们也需要学习了解一些。
之前我们执行Makefile命令时,发现他还会将命令内容回显一次再执行:

想要阻止回显,在相应Makefile前加@即可


Makefile中也可以定义变量,变量名=内容名,这一个用法有点像C语言中的宏定义。变量就像一个命名的容器,里面存储了一个文本字符串。当你使用这个变量时,make会用容器里的文本字符串来替换它。使用变量要$(变量)
举个栗子:
BIN=test.exe #定义变量BIN
SRC=test.c #定义变量SRC
.PHONY:test
test:
@echo $(BIN) #使用变量
@echo $(SRC) #使用变量

有了变量的用法,大型项目中就可以方便修改变量内容了,不用一个个修改
Makefile中几乎所有内容都能用变量替代,甚至gcc、选项、一条命令等都可以。
在依赖关系中,可以用$@代表目标文件名,可以用$^代表依赖文件列表


最后回到一开始的问题,假如我有上百个源文件,想要把它们全部编译起来形成一个程序,在Makefile中难道也要一句句写gcc命令吗?肯定不是这样的。
为了举例,我创建了一百个源文件,假设要把它们编成一个可执行文件
wildcard函数,可以获取当前目录下的所有.c文件,我们可以写SRC=$(wildcard *.c)让SRC代表所有.c文件,以及OBJ=$(SRC:.c=.o)代表SRC的所有.c文件替换为.o同名文件。
然后在依赖关系中,%.o:%.o代表展开当前目录下所有.c和.o文件,且名字一一对应。
依赖方法中,$<代表对于展开的.c文件,一个个交给gcc进行编译。
整个语句为:
powershell
BIN=test.exe
SRC=$(wildcard *.c)
OBJ=$(SRC:.c=.o)
$(BIN):$(OBJ)
gcc $^ -o $@
%.o:%.c
gcc $< -c
写好以后执行make,就会发现系统会将所有源文件先转换为.o文件,再链接成test.exe文件

虽然说我们可以不命令生成.o中间文件,但有.o文件的好处是,如果之后修改了其中一个源文件,再执行make时,只会重新编译修改了的源文件,因为其他.o和.c文件的Modify时间没有变化,系统检查后直接回忽略。这样大大减少了整个项目的重写编译时间!
本篇完,感谢阅读!
