make是一个命令工具,用于读取并执行名为Makefile(makefile)的文件中定义的规则。
Makefile是一个文本文件,它告诉make哪些文件依赖于其他文件,以及如何从这些依赖项生成最终的目标文件。
我们先简单看一下使用make的示例:
首先我们先创建一个源文件,然后在里面简单写一下代码。
然后,我们创建Makefile文件,并在文件中写入:
当我们输入make时,就会执行Makefile中的相应命令:
接下来详细讲解Makefile中写入的各个部分。
上面示例中的test是目标文件,冒号后边是依赖文件列表,依赖文件可以有多个,在上面示例中只有test.c一个,也就是说test文件是依赖test.c文件生成的。
第二行gcc -o test test.c命令是使用gcc编译test.c文件,第二行其实就是依赖方法,我们要生成test目标文件,既需要依赖关系,也需要依赖方法,第二行便是依赖方法,需要注意的是,前边要按table键。同样的,也可以有多个依赖方法,在上面示例中只有一个。
接下来的三行又是一组,首先.PHONY是用来声明一个伪目标的,后边的clean便是伪目标的名称。下一行的clean:同样也是依赖关系,只是比较特殊,依赖文件列表为空,rm -f test则是依赖方法。
在上面的示例中我们可以看到,当我们输入make后,就自己显示相应的依赖方法了,make后边是可以跟相应的目标文件的,最后就会执行依赖方法中的指令,当我们要重复多次输入某一指令时,就可以用make来方便自己。当make后边不加目标文件时,默认就是第一个目标文件,如上面示例中make后执行的是gcc指令。
关于.PHONY的具体作用,我们先演示效果,再讲解:
上面示例时:
当加上.PHONY:test并删除.PHONY:clean时:
可以看到,对于test来说,没加上.PHONY时,make是不能被多次执行的,而加上后是可以被多次执行的,.PHONY的作用就是让目标文件对应的方法总是被执行,但是,clean为什么在两次执行过程中都可以多次被执行呢?这个跟时间有关系,没加.PHONY时,多次使用make会提示已经是最新的了,所以它不会再次编译,而rm指令由于与时间无关,所以每次都可以被执行。
我们再来看一个演示:
当前还是不能执行多次make,然后我们再进入到test.c中,并对其内容进行修改,再次执行make时发现可以了。
为什么有时候需要重新编译,但是有时候又不需要呢?
答案是:根据对比文件的Mtime来确定的。
通过stat指令可以查看Mtime:
可以看到有三个时间,即Access、Modify和Change,Access 是文件被访问的时间,Modify是文件内容最近被修改的时间,Change是文件属性最近被修改的时间。在上面例子中,我们在test.c文件中加了一条语句,所以修改了其Mtime,每一次编译生成可执行文件后,也会有一个Mtime,这个时间是比源文件的时间晚的,但是当我们修改文件后,其时间比原来生成的可执行文件的时间晚,所以就又可以重新编译了,即通过比较Mtime来确定是否要重新编译。
我们再来看下一个示例:
在这个示例中,test依赖于test.o,而test.o又依赖于test.s,而test.s依赖于test.i,test.i依赖于test.c,test.c是存在的,所以找到后执行依赖方法,然后逐步递归构建test,也就是说,make解释Makefile时是会自动推导的,一直推导,推导过程不执行依赖方法,直到推导到有依赖文件存在,然后再逆向执行所有的依赖方法。
其他:
在Makefile中,%是通配符,%.o:%.c则是表示任何.c文件都可以生成对应的.o文件,即将当前目录下的所有.c文件都展开在依赖列表中。$<则是表示将依赖文件一个一个交给gcc -c选项,形成同名的.o文件。
下一个示例:
Makefile中也可以定义变量,上面的bin和src便是变量,$(bin)就是test,$(src)就是test.o,$^表示所有的依赖文件列表,这里指的就是test.o,$@代表目标文件,也就是test。
其实Makefile就是指令,执行make后会执行Makefile中写入的相应指令,如果我们不想让这些指令回显,可以在前面加上@:
另外,注释是用#。