1.Makefile总述
在一个完整的 Makefile 中,包含了 5 个东西:显式规则、隐含规则、变量定义、指示符和注释。关于“规则”、“变量”和“ Makefile 指示符”将在后续的章节进行详细的讨论。本章讨论的是一些基本概念。
• 显式规则:它描述了在何种情况下如何更新一个或者多个被称为目标的文件( Makefile 的目标文件)。书写 Makefile 时需要明确地给出目标文件、目标的依赖文件列表以及更新目标文件所需要的命令(有些规则没有命令,这样的规则只是纯粹的描述了文件之间的依赖关系)。
• 隐含规则:它是 make 根据一类目标文件(典型的是根据文件名的后缀)而自动推导出来的规则。 make 根据目标文件的名,自动产生目标的依赖文件并使用默认的命令来对目标进行更新(建立一个规则)。
• 变量定义:使用一个字符或字符串代表一段文本串,当定义了一个变量以后, Makefile 后续在需要使用此文本串的地方,通过引用这个变量来实现对文本串的使用。
• Makefile 指示符:指示符指明在 make 程序读取 makefile 文件过程中所要执行的一个动作。其中包括:
– 读取一个文件,读取给定文件名的文件,将其内容作为 makefile 文件的一部分。
– 决定(通常是根据一个变量的得值)处理或者忽略 Makefile 中的某一特定部分。
– 定义一个多行变量。
• 注释: Makefile 中“ # ”字符后的内容被作为是注释内容(和 shell 脚本一样)处理。如果此行的第一个非空字符为“ # ”,那么此行为注释行。注释行的结尾如果存在反斜线(),那么下一行也被作为注释行。一般在书写 Makefile 时推荐将注释作为一个独立的行,而不要和 Makefile的有效行放在一行中书写。当在 Makefile 中需要使用字符“ # ”时,可以使用反斜线加“ # ”( # )来实现(对特殊字符“ # ”的转义),其表示将“ # ”作为一字符而不是注释的开始标志。
2.make与Makefile的关系
make是linux下的二进制程序,用来处理Makefile这种文本文件。在linux的Shell命令行键入make的时候,将自动寻找名称为“Makefile”的文件作为make的时候,如果没有名称为“Makefile”的文件,将继续查找名称为“makefile”的文件。找到编译文件后,make工具将根据Makefile中的第一个目标自动寻找依赖关系,找出这个目标所需要的其它目标。如果所需要的目标也需要依赖其它的目标,make工具将一层层寻找直到找到最后一个目标为止。
- make工具的使用格式为:
- make [options] [target] …
- options为make工具的参数选项,target为Makefile中指定目标。
表1给出了make工具的参数选项。
表1 make工具的参数选项
3.Makefile的书写规则
- 任何一种编程语言都有自己的语法规则,Makefile的语法格式如下:
targets : prerequisites
command
…
- 或者是:
targets : prerequisites ; command
command
- targets是目标文件名称,多个文件以空格分开,可以使用通配符。一般来说,Makefile的目标是一个文件,但也有可能是多个文件。
- prerequisites是目标所依赖的文件(或依赖目标)。
- command是命令行,如果它不与“targets : prerequisites”在一行,那么,必须以Tab键开头,如果和prerequisites在一行,那么可以使用分号作为分隔。如果命令太长,可以使用反斜杠“”作为换行符。make对一行上有多少字符没有限制。规格告诉make两件事,文件的依赖关系和如何生成目标文件。
- 一般来说,make会以UNIX的标准Shell,也就是/bin/sh来执行命令。
举例:
4.在makefile规则中使用通配符
- 如果想定义一系列比较类似的文件,我们很自然地就想起使用通配符。Make支持三种通配符:“*”(任意长度的字符串)、“?”(单个任意长度的字符)、“[…]”,这个UNIX和BShell是相同的。
- 通配符代替了一系列的文件,如“*.c”表示了所有以后缀名为.c的文件。如果文件名中含有通配符,如“*”,那么可以用转义字符斜杠“”,如“*”来表示真实的“*”字符,而不是任意长度的字符串。
- 下面是一个在命令中使用通配符的例子:
clean :
rm -f *.o
例子的含义是删除所有以.o为后缀名的文件,这是由操作系统的shell所支持的通配符。
- 通配符也可以使用在Makefile的规格中,比如:
print : *.c
lpr -p $?
touch print
目标print依赖于所有的.c文件。其中“$?”是一个自动化变量,表示所有比目标新的依赖文件的集合。
- 通配符还可以使用在变量中,比如:
objects = *.o
需要注意的是,这个上面的两种情况不同,这里的*.o不会展开!objects变量的值就是“*.o”。
Makefile中的变量其实就是C/C++中的宏。如果要让通配符在变量中展开,也就是让objects的值是所有.o的文件名的集合,那么,可以这样:
objects :=$(wildcard *.o)
这种用法是通过Makefile 的关键字“wildcard”在指示的。
5.伪目标
- “clean”目标,这是一个伪目标。伪目标并不是一个文件,而只是一个标签,Makefile并不生成“clean”这个文件。
- 由于伪目标不是文件,所以make无法生成它的依赖关系和决定它是否要执行,只有通过显示地指明这个“目标”才能让其生效。需要注意的是,伪目标的取名不能和文件名重名,不然其就失去其伪目标的意义了。
当然,为了避免和文件重名的这种情况,我们可以使用一个特殊的标记“.PHONY”来显示地指明一个目标是伪目标,向make说明,不管是否有这个文件,这个目标都是伪目标。
- .PHONY:clean
只要这个声明,不管是否有“clean”文件,要运行“clean”这个目标,只要在命令提示符下输入命令“make clean”即可。
6.多目标
- Makefile规则中的目标可以不止一个,其支持多目标,当Makefile中的多个目标同时依赖于同一个文件,并且其生成的命令大体类似,就能把它们合并起来。
- 当然,多个目标的生成规则的执行命令是同一个,这可能会给我们带来麻烦,不过好在我们可使用一个自动化变量“$@”,这个变量意味着目前规则中所有的目标的集合。
- 对于多个具有类似重建命令的目标。重建这些目标的命令并不需要是完全相同,因为可以在命
- 令行中使用自动环变量“$@”来引用具体的目标,完成对它的重建。例如规则:
bigoutput littleoutput : text.g
generate text.g -$(subst output,,$@) > $@
其等价于:
bigoutput : text.g
generate text.g -big > bigoutput
littleoutput : text.g
generate text.g -little > littleoutput
例子中的“ generate ”根据命令行参数来决定输出文件的类型。使用了 make 的字符串处理函数“ subst ”来根据目标产生对应的命令行选项。
7.Makefile的命令
Makefile中,make会按顺序一条一条地执行命令,每条命令必须以“Tab”键开头,除非命令是紧跟在依赖规则后面的分号后的。
8.Makefile的变量
- Makefile的变量就像是C/C++语言中的宏,代表了一个文本子串,在Makefile执行的时候会自动地展开在所使用的地方。与C/C++的宏所不同的是,Makefile变量的值是可以改变的。在Makefile中,变量可以使用在目标、依赖目标、命令或是Makefile的其它部分中。
- 变量在声明时需要给予初值,而在使用时,需要在变量名前加上“$”符号,但最好用小括号“( )”或是大括号“{ }”把变量给包括起来。如果要使用真实的“$”字符,那么需要用“$$”来表示。
9.函数
10.隐式规则
- 隐式规则即Makefile预先约定好了的,不用再写出来的规则。比如把.c文件编译成.o文件这一规则,不用写出来,make便会自动推导,并生成我们需要的.o文件。隐式规则会使用一些系统变量,可以改变这些系统变量的值来定制隐式规则运行时的参数。如系统变量“CFLAGS”可以控制编译时的编译器参数。还可以通过模式规则的方式自定义隐式规则。
- 如果要使用隐式规则来生成目标,就不需要写出这个目标的规则,make会试图去自动推导产生这个目标的规则和命令,如果make可以自动推导生成这个目标的规则和命令,那么这个行为就是隐式规则的自动推导。隐式规则是make事先约定好的一些东西。
使用 make 内嵌的隐含规则,在 Makefile 中就不需要明确给出重建某一个目标的命令,甚至可
以不需要规则。 make 会自动根据已存在(或者可以被创建)的源文件类型来启动相应的隐含规则。
例如:
foo : foo.o bar.o
cc -o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)
可以看到,这个Makefile中并没有写下如何生成foo.o和bar.o这两个目标的规则和命令。因为make的隐式规则功能会自动去推导这两个目标的依赖目标和生成命令。也就是说,完全没有必要写出下面的两条规则:
foo.o:foo.c
cc -c foo.c $(CFLAGS)
bar.o:bar.c
cc -c bar.c $(CFLAGS)
make使用约定好的C编译器“CC”生成.o文件,这就是隐式规则。



