【Linux系统】从零掌握make与Makefile:高效自动化构建项目的工具


各位读者大佬好,我是落羽!一个坚持不断学习进步的学生。
如果您觉得我的文章还不错,欢迎多多互三分享交流,一起学习进步!

也欢迎关注我的blog主页: 落羽的落羽

文章目录

一、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时间没有变化,系统检查后直接回忽略。这样大大减少了整个项目的重写编译时间!

本篇完,感谢阅读!

相关推荐
TG:@yunlaoda360 云老大3 小时前
腾讯云国际站WAF:如何在腾讯云WAF上设置CC攻击防护,以保障业务数据免被恶意爬取?
服务器·云计算·腾讯云
电子云与长程纠缠3 小时前
Blender入门学习06 - 粒子
学习·blender·1024程序员节
_extraordinary_3 小时前
Java SpringAOP --- AOP的使用,AOP的源码
java·spring·1024程序员节
爱看科技3 小时前
亚马逊“Amelia”智能眼镜登场,三星/微美全息加速AI+AR技术融合引领穿戴赛道!
1024程序员节
Aevget3 小时前
界面控件Kendo UI for Angular 2025 Q3亮点 - 全新的AI编码助手
ui·界面控件·kendo ui·ui开发·1024程序员节
KevinPedri3 小时前
测试:uk8s创建监控和告警同步飞书等渠道
docker·kubernetes·云计算·1024程序员节
应用市场3 小时前
VSCode + AI Agent实现直接编译调试:告别Visual Studio的原理与实践
人工智能·vscode·visual studio
葱头的故事3 小时前
vant van-uploader上传file文件;回显时使用imageId拼接路径
前端·1024程序员节
-森屿安年-3 小时前
STL 容器:List
开发语言·c++·list·1024程序员节