PS:直接从WORD文档拷贝,如存在部分格式问题请谅解
1 什么是makefile
什么是makefile,说简单点makefile就是一种脚本语言。就像其它脚本语言一样(例如HTML,JAVASCIRPT)往往是用来完成某种特定功能,并且拥有自己的语法。因此学习makefile就是要了解makefile是用来干什么的,以及它的语法规则。
那么makefile是用来干什么的呢?
打个比方,假如人只有在肚子饿了的时候才吃饭,而要有饭吃就必须自己做饭。那么我们可以说,吃饭的前提是肚子饿了,而要达到吃饭这个目标,必须有做饭这个行为。因此我们就有了下面的这样一个表达式:
吃饭(目标):肚子饿(前提)
做饭(行为)
现在可以开始解释什么是makefile了:所谓makefile,就是当某个前提成立的时候,为某个目标执行某种行为的脚本(这个是俺自己的定义,可能与官方定义不太一样,仁者见仁了,呵呵)
不过在makefile的官方术语中,一般将“行为”称为“命令”,因此makefile[LU1] 就是当某个前提成立的时候,为某个目标执行某种命令的脚本。上面的表达式可以更新为:
目标(Target):前提(DEPENDENCIES)
命令(Command)
一般在makefile中,将上面的一个表达式称为“规则(Ruler)”
这里需要像大家说明的是,上面的例子其实不太精确。因为在makefile中前提和目标往往是文件(当然目标也可能不是文件)。而在makefile中前提成立的条件一般是目标文件不存在或者目标文件的时间戳比前提文件的时间老。这个前提成立时,就执行规则中的命令。
看下面的例子:
例1 文档拷贝更新
file1.doc:file2.doc
copy file2.doc file1.doc
假设在系统上file1.doc最近的更新日期为2009-11-11 3:20, file2.doc最近的更新日期为2009-11-11 4:20,很明显file1.doc的时间的时间比file2.doc的时间老。因此当在make程序中指定目标file1.doc的时候,make就会执行Shell命令copy file1.doc file2.doc,用file2.doc覆盖file1.doc
相反,如果file.doc的时间戳不比file2.doc的时间戳老,当在make程序中指定目标file1.doc的时候,make就不会执行命令copy file1.doc file2.doc
Makefile为什么要用时间戳作为前提的判定条件呢?我们知道大家一般都是用makefile来搭建编译环境的。假设我们有一个源文件叫做test.c,编译生成的目标文件为test.o,因此我们可以用makefile编写下面的规则:
例2 Makefile时间戳前提判定条件
test.o : test.c
gcc –o test.o –c test.c
还有几点需要说明的是
(1)前提本身可能也是一个目标。
什么意思呢?我们来看下面的例子:
假设我们要编译生成一个可执行程序 ,这个可执行程序叫做app,它由一个叫做app.o的对象文件联编生成,而app.o是由app.c编译生成。因此就有了下面的makefile脚本。
app : app.o
gcc –lm –o app app.o
app.o : app.c
gcc –Wall –g –std=c99 –o app.o –c app.c
假设时间戳关系为:app比app.o 老,app.o比app.c老
当在make中执行目标app所在规则的时候, app依赖于app.o,由于make发现app.o也是一个目标,因此会先执行目标app.o所在的规则;该规则会在满足前提条件的情况下,生成app.o;紧接着会再执行app所在规则,该规则会在满足前提条件的情况下,生成可执行程序app。
(2)一个目标可以有多个前提。
在实际中,一个目标往往会依赖多个规则。例如要生成一个可执行程序往往要一来多个对象文件,而生成一个对象文件往往要依赖一个源文件和多个头文件。
app:obj1.o obj2.o obj3.o
gcc –lm –o app obj1.o obj2.o obj3.o
obj1.o : obj1.c hearder1.h hearder2.h
gcc –Wall –g –std=c99 –o obj1.o –c obj1.c
obj2.o : obj2.c hearder1.h hearder2.h
gcc –Wall –g –std=c99 –o obj2.o –c obj2.c
obj3.o : obj3.c hearder1.h hearder2.h
gcc –Wall –g –std=c99 –o obj3.o –c obj3.c
(3) 一个makefile中可以有多个规则。
在例3的makefile中,存在两个规则:目标app所在的规则,目标app.o所在的规则,这两个规则的关系是规则app依赖于规则app.o。
一个makefile中可以存在多个规则,可以在make程序中指定执行哪个规则,例如如果在例3中我们只想生成app.o,并不想生成可执行程序app, 那么我们可以像下面这样执行例3中的makefile脚本
make –f example3.mk app.o
这里我们假设例3中的makefile脚本保存在文件example3.mk中,-f选项告知make程序要运行的makefile文件,这里就是example3.mk, 紧跟在后面的app.o则是告知make程序要执行的规则。
当然在make中也可以存在多个相关独立,无依赖关系的规则,如下例所示:
例5 一个makefile多个规则
app1: app1.o
gcc –lm –o app1 app1.o
app1.o: app1.c
gcc –Wall –g –std=c99 –o app1.o –c app1.c
app2: app2.o
gcc –lm –o app1 app1.o
app2.o: app2.c
gcc –Wall –g –std=c99 –o app2.o –c app2.c
利用例5中的makefile脚本我们可以生成两个可执行程序:app1和app2。依次执行下面的命令就可以用make程序每次指定一个规则,分别生成app1和app2(假设例5中的makefile脚本保存在文件example5.mk中):
make –f example5.mk app1
make –f example5.mk app2
2 什么是make
在上一节,像大家简单介绍了一下什么是makefile,以及makefile的最基本的语法。我们知道makefile说简单点就是一种脚本,就像其它脚本一样,需要一个工具(或程序)去解析和执行它。举个例子,大家都知道HTML,HTML就是一种脚本,是用来描述静态网页的脚本,而要显示出网页来,必须有一个工具(或程序)来解析和执行HTML脚本,这个工具(或程序)我们通常称为浏览器,例如IE,FireFox等。
而用来解析和执行makefile脚本的工具(或程序)就是make程序。因此make本质是一个可执行程序,用来解析和执行makefile脚本。[LU2]
通常运行make程序解析和执行makefile脚本的时候,需要告诉make程序makefile脚本的文件名和要执行的规则
例6 指定makefile文件和规则
make -f mk\mymakefile myTarget
make --filename=mk\mymakefile myTarget
make --makefile=mk\mymakefile myTarget
例6中-f、--filename、--makefile都是make程序的命令行选项,这三个选项是等同的,都用来指定要解析和执行的makefile文件,在这里就是当前目录下的mk目录下的mymakefile文件。myTarget则是make要执行的规则的目标。
我们还可以下面这样运行make程序,可以不指定makefile文件名:
make myTarget
大家可能会问了,例7没有告诉make程序要执行的makefile文件,它怎么运行?答案是:make程序会在当前目录下寻找名称叫做makefile或Makefile文件,并解析和执行它,如果找不到,则make程序报错。
甚至可以不用指定目标,如下所示:
make
会发生什么呢?make程序会在当前目录下寻找名称叫做makefile或Makefile文件,并解析和执行它, 并运行(或称为构建)它的第一个目标
例如例5的makefile脚本如果保存在当前目录的名称为makefile的文件中,上面的命令就会构建它的第一个目标app1。
相信现在大家对makefile有了一个基本的认识了吧,有兴趣的同学可继续参考《makefile详解(二)》。