Makefile的介绍与使用(一)
Makefile是Linux系统中必不可缺的一部分,Makefile可以简单理解为一份规则,也就是一个系统的执行指南,其规定了你所要执行的程序如何去做,需要什么文件参与等等。这部分就简单了解一下子目录下的Makefile。
一、最简单的Makefile
最简单的Makefile所需要的只有三个:target(目标文件)、prerequisites(所依赖的文件)、command(此目标文件所需要执行的命令);
举一个例子:
hello : hello.o
gcc hello.o -o hello
上例中,左上角的hello便是目标文件(target),在其:后面的hello.o便是所依赖的.o文件(prerequisites),而下一行的gcc hello.o -o hello便是此目标文件所需执行的命令(command)。
gcc的格式并没有固定,例如也是可行的,但是hello.o必须在hello的前面,即是错误的。(可以看成‘ -o hello ’必须以前移动)
接下来举一个完整的例子:
hello : hello.o gcc -o hello hello.o hello.o : hello.c gcc -c hello.c clean : rm *.o hello
简单说明一下上面代码的意思: 1、生成一个hello程序,此程序依赖于hello.o文件,gcc...为其执行命令的方法。
2、hello.o文件依赖于hello.c文件。(一开始,hello.o文件是不存在的,因为gcc -c hello.c这个命令才会生成)
3、清除命令:清除(rm)所有的.o文件(*.o)以及hello程序。
需要注意的是: 1、gcc...前面的一大片空白是Tab键,Makefile的编写时非常严肃的,多一个空格都会出错,这里是Tab键就不要用空格隔开,否则就会出错。
2、依赖关系是每一组文件都会有的,而依赖关系的实质即说明了目标文件是由哪些文件生成的,亦或者是由哪些文件更新的。
3、clean不是一个文件而是一个动作,其冒号后什么都没有的时候,make就不会去找其依赖,也就不会执行其命令。
4、make会对target和prerequisite的修改日期进行比较,如果prerequisite文件的日期比target文件的日期要新,亦或者target文件不存在,那么make才会执行后续的命令。
接下来,我们执行一下此Makefile。
我们需要先编写一个.c文件,要求其在终端显示“hello!!”
1 #include <stdio.h> 2 3 int main(void) 4 { 5 printf("hello!!\n"); 6 return 0; 7 }
此时我们这个文件夹内便有了hello.c和Makefile。
打开终端进行make。
可以看出终端执行了两条command命令,同时在此文件夹中生成了一个hello程序及hello.o文件。
接下来我们跑一下所生成的hello程序。
可以看出hello程序其实就是hello.c的执行。
二、make的工作情况
上面演示时用了make执行,所以这里简单说明一下make的工作情况,看看make是如何工作的。
可以看出,我们在执行make时,出现了gcc -c hello.c以及gcc -o hello hello.o两行代码,而这两行是和我们所输入command是一模一样的。而我们所属入的command是target找到prerequisite的路径。由此也就可以看出make的工作情况为:通过command找到prerequisite,并以此生成target。 如果在寻找依赖的过程中出现了问题(例如依赖找不到),那么make就会退出并报错。例如的执行就会找不到文件,从而报错。
而在make过程中,它会对prerequisite和target二者的日期进行比较,并以二者时间判断是否进行重新链接。假如我们对.c文件进行了更改,那么.c文件的时间就会在.o文件之后,你再次make的时候它就会重新链接.c生成新的.o文件,以此类推所生成的程序也就会更新。
通过“一”中的例子可以看出,clean并没有被执行,因为其冒号后面是没有东西的。而我们使用make也可以强制执行它,只要在终端输入make clean就可以强制执行clean命令,使得生成的hello.o文件和hello程序全部清除。
三、在Makefile中使用变量
在我们编写小程序的时候我们可以直接这样将target、prerequisite以及command写上去,没什么问题,也容易排查错误。但是在我们编写一些比较大的程序的时候,我们这样直接编写,就会看起来很乱,并且容易敲错,到时候再回来找错就会非常困难。所以我们一般都会使用变量来将系统的一些定义,以后再次使用这个值的时候就会方便很多,而且也不易出错。
Makefile中的变量有四种:自定义变量,自动变量,预定义变量,环境变量。
而我们一般情况下使用的都是自定义变量和自动变量,我也就对这两种变量进行分析。
自定义变量:定义变量使用的变量名 := 变量值 使用变量 $(变量名)
仍然以hello为例:
HEL := hello.o hel := hello $(hel) : $(HEL) gcc -o $(hel) $(HEL) hello.o : hello.c gcc -c hello.c clean : rm *.o hello
可以看到这时我将HEL设为hello.o的变量名;将hel设为hello的变量名,然后将下面对应的hello和hello.o部分全部修改为$(变量名)的形态。 (可以看见,我有部分.o文件改动,有部分维持原样)
尝试make,会发现此时和原来没有改变的时候是一样的。
自动变量:特殊宏‘$@’,‘$^’,‘$<’等等
$@:规则的目标文件名 $^:规则的所有依赖文件列表 $<:规则的第一个依赖文件名 (如果想知道更多的特殊宏的作用可以自行百度)
所谓的自动变量便是系统已经定义好的一种使用规则,它并不会单独特指某一变量,而是根据情况使用。
HEL := hello.o hel := hello $(hel) : $(HEL) gcc -o $@ $^ hello.o : hello.c gcc -c $^ clean : rm *.o hello
这样写的情况下,make的情况和上面是一样的,是gcc -o hello.c和gcc -o hello hello.o