在一个大的工程中,源文件不计其数,其可能会按类型、功能、模块分别放在若干个目录中,那我们每次去编译的时候就会输入一长串编译命令以及相应的路径,极其麻烦;另外一点就是每次若更改了一个源文件,对于其他没有更改的文件也得重新编译,效率低下。为了解决这些问题,makefile出现了。
makefile
1:makefile关系到整个工程的编译规则(根据你写的依赖关系执行),决定了哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译。
2:makefile带来的好处就是"自动化编译",一旦写好,只需要一个make命令,不需要每次输入一长串编译命令(gcc/g++等),整个工程完全自动编译,极大的提高了软件开发的效率。一次写好永久使用,makefile在产品开发过程中不断编写,不断完善。
3:makefile还可以进行更复杂的功能操作,因为 makefile就像一个shell脚本一样,其中也可以执行操作系统的命令。
make与makefile关系
make是一个命令工具,是一个解释makefile中指令的命令工具,大多数的IDE都有这个命令,比如:Visual C++的nmake,Linux下GNU的make等。即Make工具最主要也是最基本的功能就是通过makefile文件来描述源程序之间的相互关系并自动维护编译工作。
makefile编写规则
当执行make时,编译器会读取当前目录下的Makefile,然后根据Makefile的规则编译
目标文件:依赖1 依赖2
<tab> 命令
目标文件可以是.o文件(编译之后的文件),也可以是可执行文件;使用命令(生成目标文件的命令),将依赖生成目标,目标顶格写,命令前加tab键,不可以用空格代替!!!
命令执行的条件:
a. “依赖”文件 比 “目标”文件 新
b. 没有“目标”这个文件
满足上述条件之一,命名就会执行
举例子看下(一个工程有3个C文件,2个头文件)
edit:main.o command.o display.o
gcc -o edit main.o command.o display.o
main.o:main.c defs.h
gcc -c main.c
command.o:command.c defs.h cas.h
gcc -c command.c
display.o:display.c cas.h
gcc -c display.c
一般来说我们将最终的目标文件放至第一行(假设只生成一个可执行文件),其后跟随着生成目标文件(edit)所要依赖的中间文件(main.o),当判断到中间文件时,再去对应的中间文件(main.o)作为目标文件的分支执行对应命令,得到目标文件所要依赖的中间文件(main.o),有点像堆栈的过程,类比一下。
make会比较目标文件和依赖文件,当目标文件不存在或者比依赖文件旧时,make就会执行对应的命令更新。
ps:如果依赖不在一行可以用“\”来标记,如果命令和依赖在同一行,需要用;隔开
makefile编译规则:
1)假设这个工程沒有编译过,那么我们的全部C文件都要编译并被链接。
2)假设这个工程的某几个C文件被改动,那么我们仅仅编译被改动的C文件,并链接目标程序。
3)假设这个工程的头文件被改变了,那么我们须要编译引用了这几个头文件的C文件,并链接目标程序
对于没有改变或者改变对其不产生影响的文件就可以不需要重新编译,提高了效率。
makefile使用变量
当我们一个依赖文件很多时,我们如果还要增加依赖文件,我们需要给生成目标文件那一行以及命令一行都要加,如果手误就会发生错误,所以我们需要一个变量(相当于字符串)。
变量表示示例,声明一个变量就行,名字随意起
obj=main.o command.o display.o
edit:$(obj)
gcc -o edit $(obj)
makefile变量其实就是C/C++中的宏!!!
当obj=*.o他并不会展开,而会替换成*.o,需要展开的话要用wildcard关键字 obj:=$(wildcard *.o)即可
make自动推导
对于.o文件作为目标文件,其与他同名的.c/.cpp文件就是它的依赖文件,所以make会自动识别,并且可以自动推导出对应的命令(因为.c->.o就是gcc -c main.c)。所以就可以省略部分命令及依赖。
obj=main.o command.o display.o
edit:$(obj)
gcc -o edit $(obj)
main.o:defs.h
command.o:defs.h cas.h
display.o:cas.h
makefile简化
$^ 代表所有的依赖文件
$@ 代表所有的目标文件
$< 代表第一个依赖文件
edit:main.o command.o display.o
gcc -o $@ $^ // $@代表了当前的目标文件即edit,$^代表所有依赖文件
main.o:main.c defs.h
gcc -c $< // $<代表当前依赖第一项即main.c
另外make是会以UNIX标准shell(Bash)来执行命令,所以我们也可以使用shell中的通配符,make支持三个通配符 * ? []
对于%号,这个也表示了任意的,但是与*有些不同,%一般用于规则描述,比如
%.o:%.c
gcc $< -o $@
表示所有的目标文件及其依赖文件,但当你用*时,则不行
obj=main.o command.o display.o
edit: $(obj)
%.o:%.c
gcc -c $^ -o $@
这里的%.o : %.c 这代表的意思就是所有的.o文件依赖相应的.c文件,是一个模式规则。这样一来,make命令就会自动将所有的.c源文件编译成同名的.o文件。
清空目标文件规则
对于生成的中间依赖文件,我们往往是要清除它的,所以我们需要clean(执行make clean即可清除)
edit:main.o command.o display.o
gcc -o edit main.o command.o display.o
main.o:main.c defs.h
gcc -c main.c
command.o:command.c defs.h cas.h
gcc -c command.c
display.o:display.c cas.h
gcc -c display.c
.PHONY:clean
clean:
-rm -rf *.o
有时只是简单的清除所有.o文件只需要以下一句即可
clean:
rm *.o
.PHONY意思表示clean是一个“伪目标”,也即无论clean是否最新,一定执行它。rm命令前面加了一个小减号的意思就是,也许某些文件出现问题,但并不理睬。当然,clean的规则不要放在文件的开头,否则这就会变成make的默认目标,相信谁也不愿意这样。不成文的规矩是——“clean从来都是放在文件的最后”
关于clean:
它只不过是一个动作名字,有点像c语言中的lable一样,其冒号后什么也没有,那么,make就不会自动去找它的依赖性,也就不会自动执行其后所定义的命令。要执行其后的命令(不仅用于clean,其他lable同样适用),就要在make命令后明显得指出这个lable的名字。这样的方法非常有用,我们可以在一个Makefile中定义不用的编译或是和编译无关的命令,比如程序的打包,程序的备份,等等。
编译成多个可执行文件
makefile执行的时候只根据第一条目标文件来执行,那么需要多个可执行文件需要怎么做呢?
all:main1 main2
main1:main1.o
gcc -o main1 main1.o
main2:main1.o
gcc -o main2 main2.o
%.o:%.c
gcc -c $<
clean:
rm *.o main1 main2
将最终结果main1,main2写到另一个分支下,那么就会有依赖了,两个都会执行了,反之不会执行第二个(all 名字可随意更改)
其他
显式规则。显式规则说明了,如何生成一个或多个目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。
隐晦规则。由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较简略地书写Makefile,这是由make所支持的。
变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点像你C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。
文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;还有就是定义一个多行的命令。
注释。Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这个就像C/C++中的“//”一样。如果你要在你的Makefile中使用“#”字符,可以用反斜杠进行转义,如:“\#”