仅供自己学习使用
一、Makefile介绍
Makefile 或 makefile: 告诉make维护一个大型程序, 该做什么。Makefile说明了组成程序的各模块间的相互 关系及更新模块时必须进行的动作, make按照这些说明自动地维护这些模块。
执行make命令时,需要一个 Makefile 文件,以告诉make命令需要怎么样的去编译和链接程序。 Makefile是一个文本形式的数据库文件,其中包含一些规则来告诉make处理哪些文件以及如何处理这些文件。这些规则主要是描述哪些文件(称为target目标文件,不要和编译时产生的目标文件相混淆)是从哪些别的文件(称为dependency依赖文件)中产生的,以及用什么命令(command)来执行这个过程。
二、Makefile里包含什么?
Makefile里主要包含了五个部分内容:显式规则、隐式规则、变量定义、文件指示和注释。
1、显式规则。显式规则说明了如何生成一个或多个目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。
2、隐晦规则。由于make有自动推导的功能,所以隐式规则可以让我们比较粗糙地简略地书写Makefile,这是由make支持的。
3、变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点你C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上
4、文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样。
5、注释。和UNIX的Shell脚本一样Makefile中只有行注释, 其注释是用“#”字符,这个就像C/C++中的“//”或者“ /*”。
在Makefile中的命令,必须要以[Tab]键开始!!!
Makefile的默认文件名为GNUmakefile、makefile或Makefile,多数Linux程序员使用第三种。 执行make命令时,会对磁盘上的文件进行检查,如果目标文件的生成或被改动时的时间至少比它的一个依赖文件还旧的话,make就执行相应的命令,以更新目标文件。目标文件不一定是最后的可执行文件,可以是任何一个中间文件并可以作为其他目标文件的依赖文件。
引用其它的Makefile
Makefile使用include关键字可以把别的Makefile包含进来,这很像C语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。
include的语法是:
include <filename>
filename可以是当前操作系统Shell的文件模式(可以保含路径和通配符)
Make支持三种通配符: “*”、“?”、“[…]”
include foo.make *.mk $(bar)等价于: include foo.make a.mk b.mk c.mk e.mk f.mk
三、Makefile的规则
Makefile文件主要含有一系列的规则,每条规则包含以下内容:
一个目标(target),即make最终需要创建的文件,如可执行文件和目标文件;目标也可以是要执行的动作,如“clean”。
一个或多个依赖文件(dependency)列表,通常是编译目标文件所需要的其他文件。
一系列命今(command),是make执行的动作,通常是把指定的相关文件编译成目标文件的编译命令,每个命令占一行,且每个命令行的起始字符必须为TAB字符。
例如,有以下的Makefile文件:
# 一个简单的Makefile的例子
# 以#开头的为注释行
test:prog.o code.o
gcc –o test prog.o code.o
prog.o:prog.c prog.h code.h
gcc –c prog.c –o prog.o
code.o:code.c code.h
gcc –c code.c –o code.o
clean:
rm –f *.o
上面的Makefile文件中共定义了四个目标:test、prog.o、code.o和clean。目标从每行的最左边开始写,后面跟一个冒号(:),如果有与这个目标有依赖性的其他目标或文件,把它们列在冒号后面,并以空格隔开。然后另起一行开始写实现这个目标的一组命令。在Makefile中,可使用续行号(\)将一个单独的命令行延续成几行。但要注意在续行号(\)后面不能跟任何字符(包括空格和键)。
一般情况下,调用make命令可输入:
# make target
target是Makefile文件中定义的目标之一,如果省略target,make就将生成Makefile文件中定义的第一个目标。对于上面Makefile的例子,单独的一个“make”命令等价于:
# make test
因为test是Makefile文件中定义的第一个目标,,所有make首先将其读入,然后从第一行开始执行,把第一个目标test作为它的最终目标后面的目标的更新都会影响到test的更新。第一条规则说明只要文件test的时间戳比文件prog.o或code.o中的任何一个旧,下一行的编译命令将会被执行。
但是,在检查文件prog.o和code.o的时间戳之前,make会在下面的行中寻找以prog.o和code.o为目标的规则,在第三行中找到了关于prog.o的规则,该文件的依赖文件是prog.c、prog.h和code.h。同样,make会在后面的规则行中继续查找这些依赖文件的规则,如果找不到,则开始检查这些依赖文件的时间戳,如果这些文件中任何一个的时间戳比prog.o的新,make将执行“gcc –c prog.c –o prog.o”命令,更新prog.o文件。
以同样的方法,接下来对文件code.o做类似 的检查,依赖文件是code.c和code.h。当make执行完所有这些套嵌的规则后,make将处理最顶层的test规则。如果关于prog.o和code.o的两个规则中的任何一个被执行,至少其中一个.o目标文件就会比test新,那么就要执行test规则中的命令,因此make去执行gcc命令将prog.o和code.o连接成目标文件test。 在上面Makefile的例子中,还定义了一个目标clean,它是Makefile中常用的一种专用目标,即删除所有的目标模块。
现在来看一下make做的工作:
首先make按顺序读取makefile中的规则,然后检查该规则中的依赖文件与目标文件的时间戳哪个更新,如果目标文件的时问戳比依赖文件还早,就按规则中定义的命令更新目标文件。如果该规则中的依赖文件又是其他规则中的目标文件,那么依照规则链不断执行这个过程,直到Makefile文件的结束,至少可以找到一个不是规则生成的最终依赖文件,获得此文件的时间戳,然后从下到上依照规则链执行目标文件的时间戳比此文件时间戳旧的规则,直到最顶层的规则。
四、Makefile中的变量
Makefile里的变量就像一个环境变量。事实上,环境变量在make中也被解释成make的变量。这些变量对大小写敏感,一般使用大写宇母。几乎可以从任何地方引用定义的变量,变量的主要作用如下:
保存文件名列表。在前面的例子里,作为依赖文件的一些目标文件名出现在可执行文件的规则中,而在这个规则的命令行里同样包含这些文件并传递给gcc做为命令参数。如果使用一个变量来保存所有的目标文件名,则可以方便地加入新的目标文件而且不易出错。
保存可执行命令名,如编译器。在不同的Linux系统中存在着很多相似的编译器系统,这些系统在某些地方会有细微的差别,如果项目被用在一个非gcc的系统里,则必须将所有出现编译器名的地方改成用新的编译器名,比如编译器的版本不同,arm-linux 3.4.1、4.3.2等。如果使用一个变量来代替编译器名,那么只需要改变该变量的值。其他所有地方的命令名就都改变了。
保存编译器的参数。在很多源代码编译时,gcc需要很长的参数选项,在很多情况下,所有的编译命令使用一组相同的选项,如果把这组选项使用一个变量代表,那么可以把这个变量放在所有引用编译器的地方。当要改变选项的时候,只需改变一次这个变量的内容即可。
五、定义变量
Makefile中的变量是用一个文本串在Makefile中定义的,这个文本串就是变量的值。只要在一行的开始写下这个变量的名字,后面跟一个“=”号,以及要设定这个变量的值即可定义变量,下面是定义变量的语法:
VARNAME=string
使用时,把变量用括号括起来,并在前面加上$符号,就可以引用变量的值: ${VARNAME}
make解释规则时,VARNAME在等式右端展开为定义它的字符串。变量一般都在Makefile的头部定义。按照惯例,所有的Makefile变量都应该是大写。如果变量的值发生变化,就只需要在一个地方修改,从而简化了Makefile的维护。
现在利用变量把前面的Makefile重写一遍:
OBJS=prog.o code.o
CC=gcc
test:${ OBJS }
${ CC } –o test ${ OBJS }
prog.o:prog.c prog.h code.h
${ CC } –c prog.c –o prog.o
code.o:code.c code.h
${ CC } –c code.c –o code.o
clean: rm –f *.o
如:源程序为
经过变量替换过后的makefile文件:
六、Makefile的隐含规则
在上面的例子中,几个产生目标文件的命令都是从“.c”的C语言源文件和相关文件通过编译产生“.o”目标文件,这也是一般的步骤。实际上,make可以使工作更加自动化,也就是说,make知道一些默认的动作,它有一些称作隐含规则的内置的规则,这些规则告诉make当用户没有完整地给出某些命令的时候,应该怎样执行。
例如,把生成prog.o和code.o的命令从规则中删除,make将会查找隐含规则,然后会找到并执行一个适当的命令。由于这些命令会使用一些变量,因此可以通过改变这些变量来定制make。象在前面的例子中所定义的那样,make使用变量CC来定义编译器,并且传递变量CFLAGS(编译器参数)、CPPFLAGS(C语言预处理器参数)、TARGET_ARCH(目标机器的结构定义)给编译器,然后加上参数-c,后面跟变量$<(第一个依赖文件名),然后是参数-o加变量$@(目标文件名)。综上所述,一个C编译的具体命令将会是:
$ {CC} $ {CFLAGS} $ {CPPFLAGS} $ {TARGET_ARCH} –c $< -o $@
在上面的例子中,利用隐含规则,可以简化为:
OBJS=prog.o code.o
CC=gcc
test:${ OBJS }
${ CC } –o $@ $^
prog.o:prog.c prog.h code.h
code.o:code.c code.h
clean: rm –f *.o