在上一讲中,我们介绍了Linux下的编译器 - gcc/g++的使用,本节我们来介绍一下如何使用make/Makefile实现项目的自动化构建
a.out
的由来,是从
test.c
经过预编译到test.i
test.i
经过编译到test.s
test.s
经过汇编到test.o
test.o
经过链接到a.out
首先我们来介绍一下什么是make/Makefile,以及它们之间的关系
文件
。它是一个工程文件的编译规则,它记录了原始码如何编译的详细信息、描述了整个工程的编译链接等规则。先来看一下Makefile的【语法】:
target(目标文件):文件1 文件2(依赖文件列表) //依赖关系
<Tab>gcc -o 欲建立的执行文件 目标文件1 目标文件2 ///依赖方法
command
...
...
目标文件
。而后面的依赖文件列表
就是具有相关性的 object files,也就是目标文件所依赖的文件(可以是一个或多个,也可以没有)然后看一下Makefile的【规则】:
目标文件:依赖文件列表
会不会写Makefile ,从一个侧面说明了一个人是否具备完成大型工程的能力
命令工具
,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,Makefile都成为了一种在工程方面的编译方法【总结一下】:make是一条命令,makefile是一个文件,两个搭配使用,完成项目自动化构建
了解可什么是make/Makefile之后,我们就来用一用它们
vim Makefile
即可,进入编辑界面,如果有不懂vim的,可以看看我的文章 —— 人生苦短,我用Vimtest.c
,作为源文件,然后开始往里面写内容mytest:test.c
gcc -o mytest test.c
make
指令,它就是自动在当前源文件的所在路径下搜寻Makefile,并解释里面命令。之后若是我们需要去编译任何文件,只需要在Makefile里面做一个添加即可,怎么样,是不是很方便通过小小的demo,想必你已经感受到了【自动化构建工具】的强大,我们来仔细看看Makefile中的指令为何要如此书写✍
mytest:test.c
,冒号左侧是目标文件,右侧是它的依赖文件,所以就可以说它们之间存在一种【依赖关系】,只有test.c存在才可以有mytestgcc -o mytest test.c
它叫做【依赖方法】就这么说还是不太好理解,我们举个父与子的生活小案例来帮助理解
今天呢,是这个月的28号的了,家里面在月初给你的2000块钱差不多也花完了,于是这两天只能吃土,此时你打开微信后看到和老爸的聊天框,于是就想着和老爸要点钱【毕竟儿子向父亲要钱天经地义】
[依赖关系]
[依赖方法]
下面辨析几种依赖关系与依赖方法
❌错误的依赖方法
所以只有依赖关系不行,还得有正确的依赖方法
❌错误的依赖关系
依赖方法对了,但是依赖关系不对也不行
✔ 正确的依赖关系与依赖方法
完成一件事,必须得有正确的依赖关系 + 正确的依赖方法
看完了【依赖关系】与【依赖方法】的感性理解,相信你对它们有了一定程度的认识,接下去深入地来了解一下它们之间的关系
1 mytest:test.o
2 gcc test.o -o mytest
mytest
是依赖于汇编后的目标文件test.o
的,但是现在我们没有这个文件,因此就要去倒推一下如何获取这个test.o
test.o
来说,它依赖于test.s
这个经过编译之后文件,可是【test.s】不存在,所以跳转到下一条依赖关系 3 test.o:test.s
4 gcc -c test.s -o test.o
test.s
来说,它依赖于test.i
这个经过预编译之后的文件,可是【test.i】不存在,所以跳转到下一条依赖关系 5 test.s:test.i
6 gcc -S test.i -o test.s
test.i
来说,它依赖于test.c
这个源文件,查找后发现源文件存在,于是开始执行gcc命令 7 test.i:test.c
8 gcc -E test.c -o test.i
以下就是我们需要在Makefile中修改的【依赖关系】与【依赖方法】
【总结一下】:在[依赖关系]
中,若是目标文件所依赖的文件不存在,就将这个依赖方法
入栈,转到下一组[依赖关系]
,依次循环往复,直到当前目标文件所依赖的文件存在时,就进行出栈,开始执行依赖方法
。最后获取的便是那个我们最初想要的目标文件
平时我们在进行各种操作之后目录中都会出现很多文件,此时当我们不想要这些文件的时候,就得去一一删除,显得尤为麻烦,如果编译可以使用Makefile来自动化构建,那清理项目中的文件可不可以呢,我们来看看
Makefile
中增加一个【清理】功能make
,而是在make后面加上了一个clean
,这是为什么呢?.PHONY
是什么?它对clean而言意味着什么?我们带着这些问题一起进入【伪目标】的学习
PHONY是一个伪目标,Makefile中将.PHONY放在一个目标前就是指明这个目标是伪文件目标。其作用就是防止在Makefile中定义的执行命令的目标和工作目录下的实际文件出现名字冲突
也就是下面这句,此时的clean被.PHONY
修饰了,那么它就可以反复执行它的依赖方法
.PHONY:clean
clean
来说,它的依赖文件列表为空,上面我们也有提到过它可以为空 11 clean:
12 rm -f test.o test.s test.i
rm -f test.o test.s test.i
make clean
会自动忽略名为"clean"文件的存在,因此声明.PHONY配置项会改善性能,并且不需要担心实际同名文件存在与否我们到命令行中来验证一下⌨️
make clean
,不过其实在第一次执行的时候,就已经达成了我们清理的目的,可是后面还可以继续执行,这其实就是.PHONY
修饰起的作用.PHONY
做修饰之后一样是可以反复执行那就有同学问:这是为什么呢?为何clean不加
.PHONY
修饰也可以多次执行
[clean]
.PHONY
的修饰试试.PHONY
修饰的原理】.PHONY
修饰之后便可以多次make,但是可以看到在编译的过程中进行make的时候所执行的指令不太相同,只有gcc test.o -o mytest
这一句,却少了如何产生【test.o】的过程,这是为何呢❓生成时间
,若是依赖对象的生成时间要早于目标对象,说明它还没有被重新修改过,所以无需再度去重新编译生成(这一块在后面make的工作原理中细讲).PHONY
的修饰,就防止其被多次重复编译【总结一下】:
.PHONY
修饰的是伪目标,对于伪目标来说,它可以被反复执行
.PHONY
修饰的一定能被反复执行,但是能被反复执行的不一定被.PHONY
修饰
以上就是有关伪目标的叙述,如果还不太清楚可以看看这篇文章 ——> 链接
make
指令的动态依赖关系,我们可以发现make指令也是依赖于标准的C库,而我们在Makefile中写得也都是一些指令,因此使用make指令才可以对Makefile中的内容做一个识别make
是专门给【Makefile】写的一个命令,在执行make的时候,就会自动在你当前目录下去搜索Makefile
这个文件,搜索之后打开,然后对它里面的内容做分析
那make是如何进行分析Makefile的呢,有什么规则吗❓
mytest
这个目标对象的时候都是直接使用的【make】吗;clean
这个目标对象时却用的是【make clean】好,我们来深入探讨一下刚才遗留下的问题:make究竟是如何知道我们的可执行文件是否需要重新编译呢❓
mytest
这个目标文件后,第二次再去执行【make】指令就不会其效果了,这是为何呢?可执行文件
来收,它生成的时间一定是晚于源文件
的【因为中间要经过一系列编译 + 链接的过程】20:17:56
是要晚于17:45:48
的。所以【make】指令才会不起作用。所以它就是通过这个Modify时间来进行对比才能判断出是否需要重新编译那我们能否钻个空子,来欺骗一下make呢
我们来试试看。可以很清楚地看到源文件的Modify时间从17:45:48
变到了20:17:56
,那就比可执行文件要来得晚了,此时再去【make】的话就会重新编译了
我们在VS下有时候经常会出现修改了源代码但是生成不了.exe可执行文件的问题,既然说到了【源文件】和【可执行文件】的关系,就顺带拓展一下
最后我们来总结一下本文所学习的内容
本文我们学习了Linux下的项目自动化构建工具 - make/Makefile
.PHONY
修饰的文件叫做【伪目标文件】源文件
与目标文件
的【Modify时间】以上就是本文要讲解的所有内容,感谢您的观看