介绍
make和makefile提供了自动化构建的能力,可以根据源文件的依赖关系和依赖方法自动决定哪些文件需要重新编译。而直接使用gcc需要手动指定每个源文件的编译命令,不具备自动化的构建功能。
会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力
一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作
makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。
make是一条命令,makefile是一个文件,两个搭配使用,完成项目自动化构建。
使用演示
自动编译
首先我们自己创建一个文件——Makefile(大小写都可)
用vim打开Makefile文件,开始编辑:
切记依赖方法前要Tab一下,这是语法
然后我们现在想把code.c生成可执行程序,直接make就可以(它就会自动去执行Makefile里面的gcc命令)
清理
如果我想把生成的mycode清理掉呢?正常我们可以使用rm命令删除文件。
我们也可以把它放到Mkfile里面:
输入make clean,mycode就被清理掉了:
语法及概念介绍
makefile 的语法
target:dependencies
command
target是目标文件名,dependencies是目标文件依赖的文件列表(如果有多个用空格隔开),command是构建目标文件的命令(记得前面加Tab)。
mycode是由code.c得到的;mycode是目标文件,code.c是目标文件形成需要依赖的文件,第二行gcc code.c -o mycode就是构建目标文件的命令。
我们把目标文件和他所依赖的文件列表之间的关系称为依赖关系,对应的命令称为依赖方法
所以,makefile是一个围绕依赖关系和依赖方法构建的一个自动化编译的工具
依赖关系与依赖方法的理解
想要得到可执行程序,得有源代码(依赖关系),有了源代码,你得用编译器去编译链接(依赖方法)。然后make命令,它会自动分析文件的依赖关系,决定哪些文件需要重新编译,然后执行相应的构建规则。
总结:
make是一个基于文件依赖关系的构建工具,它可以根据指定的规则和条件来自动更新程序的部分或全部,从而减少手动编译的工作量。通过使用make,你可以只重新编译已修改的源文件,而不是整个项目,提高了编译的效率。
makefile是一个文本文件,它包含了构建目标(target)和构建规则(rule)。在makefile中,你可以定义编译器的选项、源文件的依赖关系以及如何生成可执行程序等内容。make命令会读取makefile文件并根据其中的规则来进行构建。
使用make和makefile的主要步骤如下:
1. 创建一个makefile文件,并为各个目标指定构建规则。
2. 在makefile中定义源文件之间的依赖关系以及对应的编译命令(依赖方法)。
3. 运行make命令,它会自动分析文件的依赖关系,决定哪些文件需要重新编译,然后执行相应的构建规则。
依赖文件列表可以为空
我们上面说了,正常情况下,需要同时具备正确的依赖关系和正确的依赖方法。
但在makefile中,依赖文件列表可以为空,表示目标文件没有任何依赖。清理操作其实就是这样
通常情况下,clean规则的依赖文件列表为空,因为清理操作不依赖于其他文件。执行make clean
的时候,他就会自动执行clean对应的依赖方法:
make 的工作原理
如果我们想生成可执行文件,除了可以依赖源文件code.c之外,还可以依赖什么?
是不是还可以是code.o啊,因为.o链接之后就成可执行exe了嘛。但是这样写的话,我们没有code.o, 那就要先生成code.o, 那这样又要先生成code.s ,那还要先生成code.i
从上到下就像是一个压栈的过程,而最后执行的顺序其实就是出栈的顺序。
make之后显示的其实就是执行的顺序 ,刚好和文本中显示的顺序是相反的
注意:这样写法是不推荐的,直接用gcc编译.c文件形成可执行文件就好!
make是如何工作的,在默认的方式下,也就是我们只输入make命令。那么:
1. make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
2. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“mycode”这个文件,并把这个文件作为最终的目标文件。
3. 如果mycode文件不存在,或是mycode文件修改时间要比mycode所依赖的code.o文件的修改时间新(可以用 touch 测试,这个后面会提到),那么,他就会执行后面所定义的命令来生成mycode这个文件。
4. 如果mycode所依赖的myfile.o文件不存在,那么make会在当前文件中找目标为myfile.o文件的依赖项,如果找到则再根据那一个规则生成mycode.o文件,以此类推,直至依赖的那个文件存在,则开始依次向上生成。(这有点像一个堆栈的过程)
5.这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
6.在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。7.make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那make就不工作啦。
make默认只执行makefile文件中的第一个目标规则
为什么我们刚才执行生成操作的时候直接make就行了,而清理的时候是make clean,要把clean加上?这是因为:
在终端中运行make命令时,我们可以指定目标。如果没指定,默认情况下,它只会执行makefile文件中的第一个目标规则。
而我们上面写的,生成mycode的目标规则是在第一个,而clean在后面,所以,我们不指定的话,默认执行的就是第一个生成mycode的操作。
如果把清理设为第一个目标规则,make执行的就是清理。你要想生成mycode,就需要显示指定了:make mycode,
但是,我们一般把清理的规则放在最后。
伪目标
一般clean的目标文件,我们将它设置为伪目标,用 .PHONY
修饰,伪目标的特性是:总是被执行的。
什么意思呢?
我执行了两次make,第一次make执行了makefile文件中的第一个目标规则,并生成了对应的文件。但是第二次make,并没有执行对应的操作,而是告诉我们:make: `mycode' is up to date. 意思就是生成的mycode是最新的了,没必要在重新生成了。
这里面的clean我们多加了一个修饰 .PHONY
而我们刚才也提到:一般clean的目标文件,我们将它设置为伪目标,用 .PHONY
修饰,伪目标的特性是,总是被执行的。也就是说被 .PHONY
修饰后它的特性是总是被执行的。
如果也把myfile加上.PHONY
修饰的话:
它变成总是被执行了。
文件时间
使用stat指令,可以查看文件的属性:
指令:
stat 文件名
Access:最近访问文件的时间
Modify:最近修改文件内容的时间
Change:最近修改文件属性的时间
我们经常说,文件等于内容+属性, 修改文件的内容可能会影响文件属性, 因为增加或减少代码会影响文件的大小, 然而修改文件的属性, 不会影响文件内容比如修改文件的拥有者,修改文件权限等
修改了文件的权限,也就是文件的一种属性, Change的时间发生了更新:
如果我对文件的内容做修改:
1.Modify时间被更新了,
2. 而且可以发现,Change的时间也发生了变化,因为Modify也是一种文件属性,文件属性发生变化Change时间就会发生变化且和Modify时间是一样的.
3. Access时间也发生了变化,因为vim进入文件修改也是一种查看,所以时间也会发生变化,但是查看是在修改之前,所以Access时间会比其他两个时间早.
如果我们不修改源代码,能不能修改文件的时间呢?
使用touch指令,touch除了可以创建文件还可以修改文件或目录的时间
使用touch指令也可以让三个时间全部更新:
一般而言,一个文件被查看的频率是最高的,我们所看到文件都在磁盘中存放着, 更改时间的本质就是访问磁盘,Linux系统存在着大量的访问磁盘的IO操作,变相的减慢系统的效率,所以修改Acees时间有限制,具体参考:
cat后atime的问题(已解决) - osker - 博客园 (cnblogs.com)
简单总结下,能正常触发修改上述3种时间属性的命令和效果:
- atime:access time,访问时间, cat、less、more等只读文件,不修改文件的操作,只会修改atime的值(有限制)
- mtime:modify time,修改时间,文件内容有修改, vim会修改atime、ctime、mtime的值, echo会修改ctime和mtime的值。
- ctime:change time,create time,改变时间,文件的索引节点发生变化,具体的情况有:1、文件内容有修改;2、文件权限有修改; 3、inode变了;4、重命名(重命名不会导致inode改变)
那么系统是怎么知道当前产生的文件已经是最新的了?(不能多次make)
首先要说明,它其实是通过对比源代码的Modify时间和可执行程序的Modify时间来确定生成的可执行程序是不是最新的。修改源代码之后能重新编译是因为源代码修改了,源代码的修改时间一定就比之前生成可执行文件的时间要新,那之前产生的可执行文件就不是最新的了.
而第一次生成可执行程序,文件生成时间肯定比源代码修改保存的时间新,所以再make就不让你make了,因为此时的可执行文件就是最新的了。但是如果后面我们修改了源代码,那此时源代码的修改时间就比可执行程序生成的时间更新了,所以这种情况我们是可以重新make的。为什么要以Modify时间为标准,因为Access时间和Change时间在文件内容被查看和文件属性被修改才会更新, 没有重新编译的必要, 程序需要重新编译往往因为文件的内容作了修改.
使用make指令编译code.c,此时mycode修改时间一定比code.c修改时间新:
如果对code.c文件内容作修改, 此时code.c修改时间比mycode修改时间新,就可以重新make了:
如果我们直接用touch修改时间,虽然文件内容没有变化,但也可以达到code.c文件的m时间比mycode文件的m时间要新,也就可以再次编译了: