在我们日常写代码中,一个工程的源文件不计其数, 按照类型、功能、模块等分别放在若干个目录中,这时候我们就可以利用makefile来指定哪些文件先编译,哪些后编译,以及更复杂的操作。我们只需要在makefile里指定所有的操作,在用make这个操作,即可让整个工程自动编译。
makefile的内容就是我们对于这个文件之间相互关系的描述,而make就是解释这些描述的工具,两者搭配使用就可以完成项目的自动化构建
例如
对于一个test.c
我们让他分别生成预处理,编译,汇编,连接四个步骤的所有文件
这样就生成了.i, .o .s .ext文件
依赖关系
- 对于上面那个文件,:前的为目标对象,而:号后面的为依赖对象,例如
这个的test是目标对象,test.o是依赖对象,意思就是如果我们要生成一个test,就要利用这个test.o来生成,如果没有test.o,就要先生成一个test.o
在上面那段程序中,因为test.o是test的依赖对象,test.s是test.o的依赖对象,test.i是test.s的依赖对象,test.c是test.i的依赖对象,所以如果要生成一个test,就要先生成一个test.o,要生产test.o就要先生成一个test.s,这样层层往下,就完成了一步步的构建。
依赖方法
例如这一句,就是与之相对应的依赖关系
同时我们还可以使用通配符来简化这个makefile
- $@ 目标对象
- $^ 所有依赖对象
- $< 所有依赖对象的第一个
我们还能进一步再简化,可以利用通配符来表示,在多个文件的依赖方法和对象都相似时,利用通配符%来减少工作量,这样就可以不用一个个写出每个文件的生成规则了
伪对象.PHONE:【命令】
.PHONE: [命令]
声明伪对象,无论对象是否最新,每次都重新生成。
例如
因为这个clean没有被第一个目标文件直接或者间接关联,所有他的语句不会自动执行,需要在make后面加上这个伪目标才会执行,如make clean
通常需要生成的程序不会设置伪对象,因为每个项目的构建需要很长的时间,尽可能判断不需要生成就不用重新生成
但是因为工程是需要被清理的,我们就可以将这种clean的目标文件设立为伪目标,因为总是被执行的。
make的原理
- make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
- 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“hello”这个文件,并把这个文件作为最终的目标文件。
- 如果hello文件不存在,或是hello所依赖的后面的hello.o文件的文件修改时间要比hello这个文件新(可以用 touch 测试),那么,他就会执行后面所定义的命令来生成hello这个文件。
- 如果hello所依赖的hello.o文件不存在,那么make会在当前文件中找目标为hello.o文件的依赖性,如果
找到则再根据那一个规则生成hello.o文件。(这有点像一个堆栈的过程)- 当然,你的C文件和H文件是存在的啦,于是make会生成 hello.o 文件,然后再用 hello.o 文件声明
make的终极任务,也就是执行文件hello了。- 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文
件。- 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,
而对于所定义的命令的错误,或是编译不成功,make根本不理。- make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦