makefile规则-目标

一、makefile的特殊目标

在 Makefile 中,有一些名字,当它们作为规则的目标时,具有特殊含义。它们是一些特殊的目标,GNU make 所支持的特殊的目标有:

.PHONY:
目标“.PHONY”的所有的依赖被作为伪目标。伪目标时这样一个目标:当使用make命令行指定此目标时,这个目标所在规则定义的命令、无论目标文件是否存在都会被无条件执行。

.SUFFIXES:
特殊目标“SUFFIXES”的所有依赖指出了一系列在后缀规则中需要检查的后缀名(就是当前make需要处理的后缀)。

.DEFAULT
Makefile 中,目标“.DEFAULT”所在规则定义的命令,被用在重建那些没有具体规则的目标(明确规则和隐含规则)。就是说一个文件作为某个规则的依赖,但却不是另外一个规则的目标时。Make 程序无法找到重建此文件的规则,此种情况时就执行“.DEFAULT”所指定的命令。

.PRECIOUS
目标“.PRECIOUS”的所有依赖文件在make过程中会被特殊处理:当命令在执行过程中被中断时,make不会删除它们。而且如果目标的依赖文件是中间过程文件,同样这些文件不会被删除。这一点目标“.PRECIOUS”和目标“.SECONDAY”实现的功能相同。

.INTERMEDIATE
目标“.INTERMEDIATE”的依赖文件在make时被作为中间过程文件对待。没有任何依赖文件的目标“.INTERMEDIATE”没有意义。

.SECONDARY
目标“.SECONDARY”的依赖文件被作为中间过程文件对待。但这些文件不会被自动删除。

.DELETE_ON_ERROR
如果在Makefile中存在特殊目标“.DELETE_ON_ERROR”,make在执行过程中,如果规则的命令执行错误,将删除已经被修改的目标文件。

.IGNORE
如果给目标“.IGNORE”指定依赖文件,则忽略创建这个文件所执行命令的错误。给此目标指定命令是没有意义的。当此目标没有依赖文件时,将忽略所有命令执行的错误。



二、多目标

一个规则中可以有多个目标,规则所定义的命令对所有的目标有效。一个具有多目标的规则相当于多个规则。规则的命令对不同的目标的执行效果不同,因为在规则的命令中可能使用了自动环变量“$@”。多目标规则意味着所有的目标具有相同的依赖文件。多目标通常用在以下两种情况:

a.仅需要一个描述依赖关系的规则,不需要在规则中定义命令。例如
kbd.o command.o files.o: command.h

这个规则实现了同时给三个目标文件指定一个依赖文件。

b.对于多个具有类似重建命令的目标。重建这些目标的命令并不需要是完全相同,
因为可以在命令行中使用自动环变量“$@”来引用具体的目标,完成对它的重
建。

例如规则:
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”来根据目标产生对应的命令行选项。

虽然在多目标的规则中,可以根据不同的目标使用不同的命令(在命令行中使用自动化变量“$@”)。但是,多目标的规则并不能做到根据目标文件自动改变依赖文件(像上边例子中使用自动化变量“$@”改变规则的命令一样)。需要实现这个目的是,要用到make的静态模式。



三、多规则目标

Makefile 中,一个文件可以作为多个规则的目标。这种情况时,以这个文件为目标的规则的所有依赖文件将会被合并成此目标一个依赖文件列表,当其中任何一个依赖文件比目标更新(比较目标文件和依赖文件的时间戳)时,make 将会执行特定的命令来重建这个目标。

对于一个多规则的目标,重建此目标的命令只能出现在一个规则中(可以是多条命令)。如果多个规则同时给出重建此目标的命令,make将使用最后一个规则中所定义的命令,同时提示错误信息。某些情况,需要对相同的目标使用不同的规则中所定义的命令,我们需要使用另外一种方式——“双冒号”规则来实现。



四、静态模式

静态模式规则是这样一个规则:规则存在多个目标,并且不同的目标可以根据目标文件的名字来自动构造出依赖文件。静态模式规则比多目标规则更通用,它不需要多个目标具有相同的依赖。但是静态模式规则中的依赖文件必须是相类似的而不是完全相同的。

静态模式规则的语法

首先,我们来看一下静态模式规则的基本语法:
TARGETS ...: TARGET-PATTERN: PREREQ-PATTERNS ...
COMMANDS
...

“TAGET-PATTERN”和“PREREQ-PATTERNS”说明了如何为每一个目标文件生成依赖文件。从目标模式(TAGET-PATTERN)的目标名字中抽取一部分字符串(称为“茎”)。使用“茎”替代依赖模式(PREREQ-PATTERNS)中的相应部分来产生对应目标的依赖文件。下边详细介绍这一替代的过程。

首先在目标模式和依赖模式中 ,一般需要包含模式字符“% ”。在目标模式(TAGET-PATTERN)中“%”可以匹配目标文件的任何部分,模式字符“%”匹配的部分就是“茎”。目标文件和目标模式的其余部分必须精确的匹配。看一个例子:目标foo.o”符合模式“%.o”,其“茎”为“foo”。而目标“foo.c”和“foo.out”就不符合此目标模式。

每一个目标的依赖文件是使用此目标的“茎”代替依赖模式(PREREQ-PATTERNS)中的模式字符“%”而得到。例如:上边的例子中依赖模式
(PREREQ-PATTERNS)为“%.c”,那么使用“茎”“foo”替代依赖模式中的“%”得到的依赖文件就是“foo.c”。需要明确的一点是:在模式规则的依赖列表中使用不包含模式字符“%”也是合法的。代表这个文件是所有目标的依赖文件。

我们来看一个例子,它根据相应的.c 文件来编译生成“foo.o”和“bar.o”文件:
objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@

例子中,规则描述了所有的.o文件的依赖文件为对应的.c文件,对于目标“foo.o”,取
其茎“foo”替代对应的依赖模式“%.c”中的模式字符“%”之后可得到目标的依赖文
件“foo.c”。这就是目标“foo.o”的依赖关系“foo.o: foo.c”,规则的命令行描述了如
何完成由“foo.c”编译生成目标“foo.o”。命令行中“$<”和“$@”是自动化变量,
“$<”表示规则中的第一个依赖文件,“$@”表示规则中的目标文件。上边的这个规则描述了以下两个具体的规则:
foo.o : foo.c
$(CC) -c $(CFLAGS) foo.c -o foo.o
bar.o : bar.c
$(CC) -c $(CFLAGS) bar.c -o bar.o



五、双冒号规则

双冒号规则就是使用“::”代替普通规则的“:”得到的规则。当同一个文件作为多个规则的目标时,双冒号规则的处理和普通规则的处理过程完全不同(双冒号规则允许在多个规则中为同一个目标指定不同的重建目标的命令)。

首先需要明确的是:Makefile 中,一个目标可以出现在多个规则中。但是这些规则必须是同一类型的规则,要么都是普通规则,要么都是双冒号规则。而不允许一个目标同时出现在两种不同类型的规则中。双冒号规则和普通规则的处理的不同点表现在以下几个方面:

1. 双冒号规则中,当依赖文件比目标更新时。规则将会被执行。对于一个没有依赖而只有命令行的双冒号规则,当引用此目标时,规则的命令将会被无条件执行。而普通规则,当规则的目标文件存在时,此规则的命令永远不会被执行(目标文件永远是最新的)。

2. 当同一个文件作为多个双冒号规则的目标时。这些不同的规则会被独立的处理,而不是像普通规则那样合并所有的依赖到一个目标文件。这就意味着对这些规则的处理就像多个不同的普通规则一样。就是说多个双冒号规则中的每一个的依赖文件被改变之后,make 只执行此规则定义的命令,而其它的以这个文件作 为目标的双冒号规则将不会被执行。



六、自动产生依赖

Makefile 中,有时需要书写一些规则来描述一个.o 文件和头文件的依赖关系。例如,如果在 main.c 中使用“#include defs.h”,那么我们可能就需要一个像下边那样的规则来描述当头文件“defs.h”被修改以后再次执行 make,目标“main.o”应该被重建。

main.o: defs.h

这样,对于一个大型工程。就需要在 Makefile 中书写很多条类似于这样的规则。并且,当在源文件中加入或删除头文件后,也需要小心地去修改 Makefile。这是一件非常费力、费时并且危险(容易出错误)的工作。为了避免这个讨厌的问题,现代的 c 编译器提供了通过查找源文件中的“#include”来自动产生这种依赖关系的功能。

Gcc 通过“-M”选项来实现此功能,使用“-M”选项 gcc 将自动找寻源文件中包含的头文件,并生成文件的依赖关系。例如,如果“main.c”只包含了头文件“defs.h”,那么在 Linxu 下执行下面的命令:

gcc -M main.c

其输出是:
main.o : main.c defs.h

既然编译器已经提供了自动产生依赖关系的功能,那么我们就不需要去动手写这些规则的依赖关系了。但是需要明确的是:如果在“main.c”中包含了标准库的头文件,使用 gcc 的“-M”选项时,其输出结果中也包含对标准库的头文件的依赖关系描述。当不需要在依赖关系中考虑标准库头文件时,对于 gcc 需要使用“-MM”参数。

在使用 gcc 自动产生依赖关系时,所产生的规则中明确的指明了目标是“main.o”。一次在通过.c 文件直接产生可执行文件时,作为中间过程文件的“main.o”在使用完之后将不会被删除。

在使用 gcc 自动产生依赖关系时,所产生的规则中明确的指明了目标是“main.o”。一次在通过.c 文件直接产生可执行文件时,作为中间过程文件的“main.o”在使用完之后将不会被删除。

posted on 2017-01-03 15:34  sichenzhao  阅读(492)  评论(0编辑  收藏  举报

导航