Linux下的Makefile基本操作
1.Makefile与 make介绍
在Linux中, Makefile 是⼀个⽂件, 令会在当前⽬录下找 make 是⼀个指令,当使⽤ Makefile ⽂件从⽽执⾏内部的内容
2.创建第一个 Makefile并使用make
⾸先,在当前⽬录下创建⼀个makefile文件
接下来在同级⽬录下创建⼀个 code.c ⽂件
使⽤vim编辑器输⼊下⾯的内容:
#include <stdio.h>int main(){printf("hello linux\n");return 0;}
保存c ode.c ⽂件后退出当前vim,使⽤vim打开 Makefile 文件 输⼊下⾯的内容:
code:code.cgcc -o code code.c.PHONY:cleanclean:rm -rf code
需要注意, gcc -o code code.c和 rm -rf code前⽅是⼀个Tab键的⼤ ⼩,⽽不是4个或者8个空格
保存M akefile ⽂件后退出当前vim,在当前⽬录下输⼊ make 指令即可在当前⽬录下 创建code.c 对应的可执⾏⽂件(具有可执⾏权限并且⽂件本身可执⾏) code ,例 如下图:
通过常规⽅式运⾏该可执⾏⽂件 ./code 即可看到打印输出的内容:
接着使⽤ make clean 指令清理刚才⽣成的可执⾏⽂件 code :
3.Makefile文件基本格式介绍
以前⾯例⼦中的 Makefile 为例:
code:code.cgcc -o code code.c.PHONY:cleanclean:rm -rf code
第⼀⾏中的 code:code.c 代表依赖关系, code 表示⽬标⽂件, 赖⽂件列表中的⽂件,第⼆⾏的 code.c 表示依 gcc -o code code.c代表依赖⽅法(指令)
第三⾏中的 .PHONY 表示⽣成⼀个伪⽬标, clean 表示伪⽬标的名字(可以类⽐ 变量名)
第四⾏及第五⾏与第⼀⾏及第⼆⾏含义⼀致,表示依赖关系和依赖⽅法,⽽因为 clean 没有需要依赖的⽂件,所以 clean: 后没有任何依赖⽂件列表⽂件
依赖关系:表示两个⽂件之间构成的⼀定关系,⽐如⽗⼦关系
依赖⽅法:通过依赖⽅法可以执⾏的对应的指令
依赖⽂件列表: code.c 所处的位置即为依赖⽂件列表,为了⽣成⽬标⽂件 code 而需要的⽂件称为依赖⽂件,依赖⽂件列表可以含有不⽌⼀个⽂件
注意:理论上来说,依赖⽂件列表中的 code.c 在当前情况下可以不写,但是如 果不写,在第⼀次执⾏ make 指令后,不论之后 code.c 是否修改,再执⾏ make 指令都⽆法执⾏对应的依赖⽅法,因为 code ⽂件已经存在,所以为了保 证可以修改,需要加上 code.c
从上⾯的运⾏结果可以看出,每⼀次执⾏make时都会在控制台回显出对应的依赖⽅ 法,如果将编译指令改为 echo "测试",则效果如下:
可以看到先回显了对应的依赖⽅法,再执⾏依赖⽅法,如果不希望出现这种情况,可 以在执⾏的指令前加上@使指令不再回显,所以上⾯的 Makefile 可以修改为:
code:code.c2 @echo "Start Compiling..."3 @gcc -o code code.c4 @echo "End Comliling"5 .PHONY:clean6 clean:7 @echo "Cleaning code..."8 @rm -rf code9 @echo "End Cleaning..."10
~
⼀个依赖集中可以有多个依赖⽅法
此时正常运⾏结果如下:
如果代码出现错误,则 gcc 会中断编译,所以此时运⾏结果如下:
使⽤. PHONY 可以⽣成⼀个指定名字的伪⽬标,伪⽬标的作⽤是:清除依赖⽅法执⾏ 时进⾏的⽂件时间对⽐,下⾯是具体介绍: ⾸先,在Linux中可以使⽤ stat+⽂件名查看⽂件当前的属性,对于 code.c 有:
执⾏结果中,主要关注三个部分: Access 、 Modify 和 Change ,这三个部分分别 表示⽂件最近⼀次的访问时间、⽂件内容被修改的时间和⽂件属性被修改的时间
Access 时间:⼀般不是特别精确,因为如果⼀个⽂件访问⼀次就需要更新⼀次 访问时间,那么对于多个⽂件来说,这种操作的消耗对于CPU来说是很⼤的
Modify 时间: Modify 时间只表示⽂件内容被修改的时间,如果⽂件属性时间 修改,则不影响 Modify 时间,但是需要注意, 着Ch Modify 时间⼀旦改变⼀般伴随 ange 时间改变,因为修改⽂件内容有时会影响到⽂件的相关属性(例如⽂ 件⼤⼩等)
Change 时间:Change 时间只表示⽂件属性被修改的时间,修改⽂件属性时间不会影响Modify时间
接着,观察对于没有添加伪⽬标的 Makefile 第⼀部分依赖集,如果code文件已经存在,再一次进行make的效果:
code:code.c@echo "Start Compiling..."@gcc -o code code.c@echo "End Compiling..."
那么指令是如何知道⽂件是否被修改呢?就是通过前⾯提到的 Modify 时间和 Change 时间,过程如下图所示:
因为code.c 创建的时间早于 code.c 编译的时间,所以开始时不存在 所以第⼀次执⾏ code ⽂件, make 指令时正常执⾏。
当code.c ⽂件未修改时,第⼆次执⾏ make 指令会发现 code.c 的 Change 时间依旧在 make 之前,因为第⼀次已经满⾜了 Modify 时间和 code.c 的两个时间在 code ⽂件的两个时间之前,所以 gcc 就不会再进⾏⼀次编译。
当修改 code.c ⽂件后, code.c 的 的两个时间在 Modify 时间和 Change 时间改变,导致 code ⽂件的两个时间之后,此时 code.c gcc 就可以正常执⾏,从⽽ make 指 令不受影响
⽽如果再 Makefile 中为这⼀部分添加⼀个伪⽬标,则可以清除指令中⽂件时间的对 ⽐过程:
.PHONY:codecode:code.c@echo "Start Compiling..."@gcc -o code code.c@echo "End Compiling..."
此时⽆论执⾏多少次 make 指令,都不会出现 : make 指令中 gcc 因为⽂件时间对⽐⽽导致执⾏结果不同
make 指令虽然结果完全相同,但是不代表依赖⽅法没有执⾏,即⽂件确实每⼀ 次都重新编译
执⾏完编译部分的 make 指令,想要执⾏删除 code ⽂件对应的 make 后加上 clean ,这个 make 指令需要在 clean 代表伪⽬标名,之所以前⾯直接使⽤ make 就可以 执⾏编译指令,是因为 make 指令在读取 Makefile ⽂件时是从上⾄下顺序查找,⽽ 直接使⽤ make ,就会执⾏第⼀个依赖集对应的依赖⽅法,执⾏完毕后就不会再继续 往下读;⽽对于删除 code ⽂件的指令来说,其所在位置时Makefile 中的第⼆个依赖集,所以需要告诉 make 指令找哪⼀部分
所以,此处可以看出.PHONY 的第⼆个作⽤就是声明⼀个伪⽬标,通过该伪⽬标帮助make 指令快速定位需要执⾏的依赖集
如果细⼼可以发现,对于 clean 依赖集来说,不论是否有 .PHONY 都可以⽆限制执⾏ rm -rf依赖⽅法,所以可以推断出 rm -rf指令本身不会考虑⽂件的时间属性,但 是为什么此处还需要加 .PHONY ?⼀⽅⾯是为了声明伪⽬标,另⼀⽅⾯是为了当前依 赖集中的其他指令会有时间对⽐
4.Makefile通用写法
在前⾯的Makefile中,每⼀个依赖⽅法都需要在前⾯的依赖关系部分的⽂件重新写⼀ 遍,为了简化过程,可以使⽤下⾯的写法:
TARGET=codeSRC=code.o$(TARGET):$(SRC)$(CC) -o $@ $<%.o:%.c$(CC) -c $< -o $@.PHONY:cleanclean:@rm -rf $(TARGET) $(SRC)
上⾯的代码中,⾸先创建了两个变量分别代表⽣成的⽬标⽂件 code 以及第⼀个依赖 集中的依赖⽂件列表中的⽂件,在依赖⽅法中使⽤了两个⾃动变量(⼀般建议⼤ 写),分别是$@和$<
在M akefile 中, $@ 表示⽣成的⽬标⽂件,$<表示从依赖⽂件列表中取出⼀个⽂ 件,对应的还有$^表示依赖⽂件列表中的所有⽂件
⽽对于gcc来说,在 Makefile 中可以使⽤内置变量 如果涉及到多个⽂件编译,则在 SRC 和 CC (表示C编译器的名字)代替 %.c 处使⽤空格分隔每⼀个⽂件
⾄此,⼀个基本的 Makefile ⽂件编写语法就这么多