一.背景
会不会写makefile,从侧面说明了一个人是否具备完成大型工程的能力。
一个工程中的源文件不计其数,按照类型,功能,模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作。
makefile带来的好处就是–“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。
make是一条命令,makefile是一个文件,两个搭配使用,完成项目自动化构建。
二.实例
首先我们要使用makefile,我们就得创建一个makefile文件,makefile或者Makefile都可以,允许首字母大写或者小写。
使用touch makefile命令,创建出makefile文件,或者直接使用vim makefile也可以创建出makefile文件。
向下面这样的一个代码,我们不想使用gcc得到可执行程序,而直接使用make命令来得到可执行程序。
源文件:
#include<stdio.h>
2 int main()
3 {
4 printf("makefile自动化构建\n");
5 return 0;
6 }
makefile文件:
mybin:test.c
gcc test.c -o mybin
可以看出,使用了makefile文件,就不需要我们使用gcc生成可执行程序了,使用make命令就可以得到可执行程序。
1.依赖关系
makefile文件的语法:首先写的是可执行程序的名字,然后接着:源文件。
2.依赖方法
依赖方法就是我们要执行的操作,很明显,这里的依赖方法就是gcc编译可执行程序的命令。
注意:依赖方法是按键盘上的TAB键,然后再写后面的语句。
关于依赖关系和依赖方法的理解:
人在这世界上不是一座孤岛,你一定和某些人,某些事等存在着紧密的联系。这样才能很好的完成一件事。
比如说,你的老妈和你就是依赖关系,一定是你老妈叫你去打扫卫生(正确的依赖方法),而不是你去叫你老妈去打扫卫生(错误的依赖方法)。也不可能是别人的老妈叫你打扫卫生,你们都不认识,你当然不会做了(错误的依赖关系)。
所以说,做好一件事情,要有正确的【依赖关系】和【依赖方法】。
当然依赖方法也可以不这么写,我们可以把源文件生成可执行程序的过程写成预处理,编译,汇编,链接。
mybin:test.o
gcc test.o -o mybin #链接
test.o:test.s
gcc -c test.s -o test.o #汇编 -c
test.s:test.i
gcc -S test.i -o test.s #编译 -S
test.i:test.c
gcc -E test.c -o test.i #预处理 -E
三.实现的原理
make是如何工作的呢?(统一用上面的例子)
①make会在当前目录中查找makefile或者Makefile的文件。
②如果找到,它会找文件中第一个文件,也就是mybin文件,将这个文件作为最终的目标文件。
③mybin依赖的文件test.o是不存在的,那么make会去找test.o的文件,依次类推去找,最后再倒回来执行(像是调用堆栈的过程)
④ 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
⑤make只管文件的依赖性,如果冒号后面没有文件的话,make就不工作了。
四.项目清理
当一个目录中我们进行各种操作,出来很多文件,我们想要删除这些文件,每次需要我们手动删除这些文件吗?显得很麻烦,于是我们一样可以使用makefile文件自动化删除这些文件。
命令:make clean。
语法:
.PHONY:clean #这个待会说
clean:
rm -f test.o test.s test.i mybin
clean:是依赖关系,换行加上TAB键,这一行是依赖方法。
这里make和makefile配合使用,就可以实现创建文件,然后删除文件。
五. .PHONY是什么?
.PHONY是一个伪目标,makefile中将.PHONY放在一个目标前,就表示这个目标是一个伪目标。
那.PHONY有什么作用呢?
1.PHONY的作用
防止在makefile中定义的执行命令的目标和工作目录下的实际文件出现名字冲突。
也就是clean前有.PHONY,那么clean操作就可以一直反复执行,一直可以执行它的依赖方法。
这里我们可以一直执行make clean操作,其实第一次make clean操作我们就已经完成文件的删除了,但是还是可以继续执行该命令。
2.PHONY的原理
.PHONY修饰的clean,clean后面的依赖关系为空,.PHONY修饰的目标clean并不是某个依赖项生成的实际文件,因此make不会在当前目录去搜索是否有clean文件。
其实对于clean来说,不加.PHONY修饰也可以实现一直make clean操作,就是因为它依赖的对象为空,生成clean文件时,不需要某些文件必须存在。
没有伪目标:
得到的结果是一样的。
我们知道make操作一次了之后,就不能在继续操作了,这是因为该目录下已经存在该文件了,是最新的文件,除非文件的acm时间变了,我们是不是可以在make前加上.PHONY,也可以一直执行make命令。
当然,结果达到了我们的预期。
为什么make不加.PHONY伪目标,就无法连续多次的使用make呢?这里我们就要探讨一下make的判断机制了。
六.make的判断机制:文件的ACM时间
当我们使用ll命令,查看文件的信息时,会出现一个时间,该时间是最近文件修改的时间,这个时间也叫Modify time。
文件其实还有两个时间Access time和Change time,使用命令【stat+文件名】查看。
1.Access time
Access time简称atime,这是目录或文件最后一次访问的时间。
使用命令cat可以查看文件的内容,也就是访问了文件,那么atime时间就会改变。
可以看出,使用了cat命令查看文件的内容后,文件的atime并没有修改,这是为什么呢?
一般而言,一个文件被访问的频率是很高的,我们看到的文件都是在磁盘存放着的,如果我们每次访问文件,都要修改atime,其实就是访问磁盘,如果linux中充斥着大量的访问磁盘的IO操作,变相的减慢系统的效率。
故而修改文件的atime,添加了【次数限制】。
2.Modify time
Modify time简称mtime,这是目录或文件最后一次被修改的时间。
修改文件的内容,mtime的时间自然会改变,但是这里的修改不是指只有文件的内容修改了才是修改,不是说文件的内容不改变它就不会变,而是文件是否进行了写操作。
比如,我们使用vim打开一个文件,然后我们不修改文件的内容,而使用wq来退出文件。
可以看出确实如此。mtime和文件是否进行了写操作相关。
回到之前,make是如何知道这是一个最新的文件,从而不能多次使用make命令,其实就是比较文件的mtime来看的。
解释:刚开始一定是先有的源文件,使用make命令创建除了可执行程序,源文件时间<可执行程序时间,再次使用make命令就无法实现,因为可执行程序此时就是最新的文件。
但是,当我们对文件执行了写操作的话,源文件的时间就变成了:源文件时间>可执行程序时间,于是此时的源文件就变成了最新的文件,于是就可以继续执行make命令来生成可执行程序。
3.Change time
Change time简称ctime,这是对文件Inode信息最后修改时间。
I 什么是Inode?
理解inode,要从文件储存说起。
文件储存在硬盘上,硬盘的最小存储单位叫做“扇区”(Sector)。每个扇区储存512字节(相当于0.5KB)。
操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个“块”(block)。这种由多个扇区组成的“块”,是文件存取的最小单位。“块”的大小,最常见的是4KB,即连续八个 sector组成一个 block。
文件数据都储存在“块”中,那么很显然,我们还必须找到一个地方储存文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode,中文译名为“索引节点”。
II Inode中的内容?
- 文件的字节数
- 文件拥有者的User ID
- 文件的Group ID
- 文件的读、写、执行权限
- 文件的时间戳,共有三个:ctime指inode上一次变动的时间,mtime指文件内容上一次变动的时间,atime指文件上一次打开的时间。
- 链接数,即有多少文件名指向这个inode
- 文件数据block的位置
总之,除了文件名以外的所有文件信息,都在存在Inode中。
七.makefile的小招
1.简化makefile写法
正常的写法:
mybin:test.c
gcc test.c -o mybin
.PHONY:clean
clean:
rm -f mybin
简化的写法①:
mybin:test
gcc $^ -o $@
.PHONY:clean
clean:
rm -f mybin
这里使用$^来代替源文件,也就是依赖文件;$@来代替可执行程序,也就是目标文件。
简化的写法②:
mybin:test
gcc -o $@ $^
.PHONY:clean
clean:
rm -f mybin
只需要保证-o的后面保证是$@即可。
简化的写法③:
cc=gcc
src=test.c
target=mybin
$(target):$(src)
cc -o $@ $^
.PHONY:clean
clean:
rm -f $(target)
2.不显示makefile代码
每次我们使用make或者make clean命令时,它都会把该命令打印出来,可以在makefile中的代码前加上一个@即可,这样每次就不回显了。
mybin:test.c
@gcc -o $@ $^
.PHONY:clean
clean:
@rm -f mybin