【Linux】自动化构建-Make/Makefile
前言
上文我们讲到了Linux中的编译器gcc/g++ 【Linux】编译器gcc/g++及其库的详细介绍-CSDN博客
本来我们将一个对于编译来说很重要的工具:make/makfile
1.背景
在一个工程中源文件不计其数,其按类型、功能、模块分别放在若干个目录中,makefile可以来定义那些文件先编译,那些文件后编译,那些文件需要重新编译。甚至是一些更为复制的功能
makefile带来是好处就是"一键化编译",只需要一个make命令就可以实现。这极大的提高了开发的效率
会不会写makefile从侧面很大程度的反应了一个人是否具有完成大型工程项目的能力
2.简单认识什么是make/makefile
make是一个命令工具,用于解释makefile中指令的工具
make是一个指令,makefile是一个文件,需要我们自己建立。两个搭配使用就可以实现我们的自动化编译
3.make/makefile的基本使用
3.1简单一键化编译
创建makefile,并使用vim写入内容(m大小写都可以)
hyc@hcss-ecs-4ce7:~$ touch makefile
hyc@hcss-ecs-4ce7:~$ vim makefile
makefile的基本结构为:依赖关系+依赖方法
(注:写入依赖方法前,要先点击TAB键。一定要TAB键不要4个空格,这是写书格式!!)
退出并保存,执行make指令,自动化执行编译
hyc@hcss-ecs-4ce7:~$ make
gcc test.c -o testhyc@hcss-ecs-4ce7:~$ ./test
hi,Yuzurihahyc@hcss-ecs-4ce7:~$ ls
makefile test test.c
3.2简单一键化清理
再次打开makefile,写入清除命令
注:
先忽略.PHONY:clear不看,clear的格式其实和上面的格式都是:依赖关系+依赖方法。只不过clear并没有依赖什么文件,就不写依赖文件
退出并保存,执行make clear,完成一键清除
hyc@hcss-ecs-4ce7:~$ ls
makefile test test.chyc@hcss-ecs-4ce7:~$ make clear
rm testhyc@hcss-ecs-4ce7:~$ ls
makefile test.c
大家看到这里可能一头雾水,但是没关系我们来一个一个看
3.2.1为什么这个我们要使用make clear才能达到效果,而前面只需要执行make就可以了呢?
我们可以把这个两个指令交换一下位置,看看效果
我们可以看到此时我们执行make指令时,实现的效果就是清除效果
hyc@hcss-ecs-4ce7:~$ ls
makefile test test.c
hyc@hcss-ecs-4ce7:~$ make
rm test
hyc@hcss-ecs-4ce7:~$ ls
makefile test.c
总结:
执行make指令时,会从上到下扫描makefile文件,并且默认执行第一个扫描到的方法。如果想要执行其他方法,可以make + 目标名,显示的调用
3.2.2 .PHONY是什么东西?
.PHONY + 目标名用于声明伪目标
伪目标不是实际存在的目标,而是用于执行特定的命令(如清理)
.PHONT的主要功能是保证对应的依赖关系和依赖方法总是被执行
要理解什么叫总是被执行?我们可以先理解什么叫不总是被执行。我们可以看到当我们多次执行make指令时,第一次正常执行,而后面的都不再执行了。这就叫不总是被执行。
而换做clear,则可以正常执行多次
同样的,当我们使用 .PHNOY修饰test时,也可以反复的执行编译语句
总结:
.PHONY是定义伪目标,其功能是让其对应的依赖关系和依赖方法总是被执行。但一般情况下编译都是不要.PHNOY修饰的,因为代码没有经过修改是不需要反复编译的。反复编译会极大的浪费资源。
补充:make是如何判断源文件是否被修改?
1.文件的acm时间
make正是通过两个文件的Modify时间来判断出源文件是否被修改的。
如果可执行文件的时间晚于源文件时间,这代表没有被修改。反之,这代表源文件被修改了想要重新编译。
ACM时间小知识:
Access时间并不是我们想象的我们访问一次就更新一次时间,因为整个系统最频繁的操作就是访问文件,如果每访问一次文件就统计一次时间这会带来巨大的开销。因此,Access时间是会等用户访问到一定次数才会更新。
Modify时间修改,Change时间往往也会一起修改。因为Modify是记录文件内容修改的时间,但是文件内容有修改其体积大小往往会修改,而体积是属于文件属性这一块的,所以Change时间也会修改。
4.make/makefile深入理解
4.1make的推导规则
现在makefile中为一下内容:
执行make指令我们可以看见,执行的指令顺序和我们写入makefile中的顺序是不一样的。
因为makefile的推导规则是满足类似栈结构的:myproc由myproc.o文件推导,而myproc.o当先并不存在,要由myproc.s文件推导。那就先把推导myproc的指令入栈,先推导myproc.o文件。以此类推,直到前置条件完成后,通过出栈依次执行后面的指令
4.2makefile的升级之路
4.2.1makefile可以使用变量
makefile可以像我们的编程语言一样使用自定义变量,来指代文件、指令、选项等等。
$:表示这个变量所指代的内容,比如:$(BIN)就表示myproc这个文件
4.2.2makefile中的特殊变量
$@:表示目标文件,既$(BIN)
$^:表示依赖文件,既$(SRC)
特殊符号可以帮助我们简化makefile的书写
4.2.3makefile中一段依赖关系中可以存在多个依赖方法
一段依赖关系中可以存在多个依赖方法,比如这里我们想直观的看到我们做了什么操作,就可以利用echo来输出信息
效果如下:
我们这里看到了我们自己写是echo指令回显了,我们如果想要指令不回显只看效果,可以使用@符号
这样指令就不会回显了
4.2.4makefile处理多文件编译
对于多文件的编译,我们上一篇文章讲到过,我们一般都是先将所有文件编译为.o文件,最后在将所有的.o文件连接
%是makefile下的通配符。%.c:%.o表示将当前路径下的所有.o/.c文件全部展开,比如有100个.c文件,这里就会展开一百个依赖关系,并执行一百个依赖方法。
效果如下:
4.2.5makefile最终版本
上面我们说到,如果有多文件的编译makefile的处理方式。确实在依赖方法的书写上,这个便捷多了。但是我们没有考虑到这一切都是建立在使用变量上的,而我们的变量则需要将多个文件表示出来。
如果我们想要编译多个文件,就需要在变量里写多个文件
3个文件还能接受,可对于项目工程来说有上百个文件怎么办?我们不可能一个一个写上去
解决这一问题有两个方法
方法一:
OBJ=$(SRC:.c=.o) :语法格式,将.c文件替换为同名.o文件
SRC=$(shell ls *.c):使用命令行指令,一键获取.c文件
方法二:
SRC=$(wildcard *.c) :wildcard为makefile的函数,其作用就是获取所有.c文件
至此这就是makefile的最终形态