一、基本概念
系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至 于进行更复杂的功能操作。
二、makefile写法与理解
2.1 makefile基础写法
例如我们有一个code.c文件,要让它形成一个名为code的可执行程序
第二行的开头必须是tab键,同理第六行的开头也是tab键。根据上述写法,我们来反向推导理论,code依赖于code.c,所以 code:code.c 这一行叫做依赖关系, gcc -o code code.c 这一行叫做依赖方法,依赖关系+依赖方法,才能达到我们要编译程序的目的。而在依赖关系中,出现在:之前的,叫目标文件,出现在:之后的,叫依赖文件列表。clean: 是依赖关系,rm -f code是依赖方法,目标文件是clean,依赖文件列表为空。
现在我们要编译程序,就直接输入make命令即可,而要清除文件,输入make clean即可清理。
通过上面大家会发现,为什么编译就只make就行,而清理需要在make带上clean才可以?这是因为,当我们输入make命令以后,系统会在当前路径下查找名为makefile/Makefile文件,make默认执行的是从上往下遇到的第一个目标,如果要执行其他的目标,就需要在make后面带上目标文件,才可以执行。
2.2 伪目标
.PHONY修饰的目标是一个伪目标,伪目标总是被执行的,那总是被执行和总是不被执行又是什么意思呢?
我们连续make,发现并不是每次都执行,提示了一个code已经是最新的,这叫总是不被执行,而连续make clean发现每一次都执行了清理,这叫总是被执行。在大型项目中代码量非常多,这个时候如果就改了两个文件,所有文件都要重新编译,那太浪费时间了,就只编译改的那两个文件就可以,所以在实际中,编译成可执行程序,不需要修饰成伪目标,但是清理要修饰成伪目标,要把工程清理干净,那么makefile就可以提高我们编译工作的效率。
那系统怎么知道要不要重新编译呢?在这里介绍一个命令,stat,stat后面跟文件可以查看文件属性以及ACM时间,其中modify时间代表对文件内容的最近修改时间,change时间代表对文件属性的最近修改时间。
系统就通过对比源文件和可执行目标文件的modify时间,来决定要不要重新编译,最开始没有可执行程序,make之后,形成了可执行程序code,这个时候code的modify时间肯定是比code.c的modify时间晚的,就不重新编译,如果对code.c的代码进行了修改,那现在code.c的modify时间就比code的modify时间要晚,就需要编译。
第一次编译后的modify时间,不会再编译
对源文件修改之后的modify时间,会再编译
三、推导过程
我们把编译的过程写全,预处理编译汇编链接每一步都生成对应的文件
code依赖于code.o,当前没有code.o,但是有一组code.o的依赖关系,code.o依赖于code.s,当前没有code.s,但是有一组code.s的依赖关系,code.s依赖于code.i,当前没有code.i,但是有一组code.i的依赖关系,code.i依赖于code.c,当前有code.c,用code.c生成code.i,code.i生成code.s,code.s生成code.o,code.o生成code
这个过程中用了栈结构,在向下找存在的依赖关系时把依赖方法依次入栈,当找到code.c存在时,再依次取栈顶元素完成编译
四、定义变量
但是通常为了让我们的makefile更通用,以及在多文件时提高效率,我们会采用定义变量的方式
可以做到更加灵活的控制,生成的可执行程序,编译器,改这里就全部改了,类似于C/C++中的#define
如果想要打印变量对应的内容,可以采用$(变量)来获取变量对应的内容
可以看到变量对应的内容全部打印出来了,但makefile依赖方法语句会回显,怎么才能关闭这个回显,只打印语句输出的内容呢,在依赖方法前加上@就可以
既然这种方式可以打印变量的内容,那我们的编译和清理语句也可以换掉,而在编译时一般习惯生成.o文件,所以我们要先用code.c生成code.o,再用code.o生成code
在第一组依赖关系中$(BIN),$(OBJ)以及第二组依赖关系的$(SRC)都写了两次,其实可以不用这么麻烦,$@表示目标文件,$^表示依赖文件列表
五、多文件下的makefile
那如果现在有100个文件怎么办呢?难道把BIN,SRC,OBJ都要定义100个吗?显然不是的,这样的话就跟直接写没有区别了,我们可以采用这种写法
SRC=$(wildcard *.c)就是获取当前路径下所有后缀为.c的文件,OBJ=$(SRC:.c=.o)就是把所有同名.c 替换 成为.o 形成目标文件列表,然后创建100个文件,code1~code100,后缀为.c,我们验证SRC和OBJ是否可以获取当前路径下的.c文件以及把.c替换成.o
那怎么编译呢?怎么让每一个.o和.c对应起来呢?
%是通配符,展开所有的.c文件和.o文件,而且是一一对应的,$<是将展开的每一个.c文件都交给gcc。
六、makefile整体代码
当多文件的时候可以这么写,自动化构建非常方便,如果只有一个文件的话,就按照最初的那种最基础的写法写就可以了。