makefile rule

Makefile Rule

在 makefile 中,规则描述了在何种情况下使用什么命令来重建一个特定的文件。特定的文件称为规则的目标;规则的依赖决定了在何种情况下使用命令重建目标;规则的命令用来更新或者创建规则的目标。

语法格式如下:

targets : prerequisites
commands

或者

targets : prerequisites ; commands
commands

1 targets 可以是使用空格分隔的多个文件名,也可以是标签。
2 规则的 targets、prerequisites、commands 中都可以使用通配符。targets、prerequisites 中的通配符在 make 读取 makefile 的时候被展开;commands 中的通配符在 shell 执行此命令的时候被展开。
3 规则的命令有两种书写方式:命令可以和目标、依赖放在同一行,命令在依赖后使用分号(;)和依赖分开;命令在目标、依赖的下一行,作为独立的命令行。当作为独立的命令行时必须以 Tab 字符开始。
4 同一行中多条命令通过分号或者 && 分隔。&& 不能作为依赖和命令的分隔符。只能分隔命令。
5 较长的行通过反斜线(\)分解成多行。
6 make 把 makefile 中第一个规则之后出现的所有以 Tab 字符开始的行都作为命令来处理。

示例 1
Makefile 文件的内容如下
print : printa printb ; cd ~/.. && pwd
echo $@

printa : ; echo $@
printb : ; echo $@

在命令提示符下输入 “make -s”,执行结果如下:
printa
printb
/home
print

终极目标(default goal)
终极目标就是当没有使用 make 的命令行指定具体目标时,make 默认执行的目标。make 把在 makefile 中出现的第一个规则的第一个目标称为终极目标。
除了终极目标所在的规则以外,其它规则的顺序在 makefile 文件中没有区别。
这两种目标不会作为终极目标:目标的名称是以点号开头的,其后不存在斜线(这是 makefile 的特殊目标);模式规则的目标(包含模式字符 %)。

依赖的类型(types of prerequisites)
make 的规则中可以使用两种不同类型的依赖:normal prerequisites 和 order-only prerequisites。

normal prerequisites
规则的 normal prerequisites 有两个方面的含义:决定了重建这个规则的目标所要执行的命令的顺序。更新目标之前需要按照什么样的顺序、执行那些命令来重建这些依赖文件;确定了一个依赖关系。规则中如果依赖文件的任何一个比目标文件更新,则被认为规则的目标已经过期需要重建目标文件。

order-only prerequisites
规则的 order-only prerequisites 指明在依赖文件更新之后不需要重建规则的目标。也就是说,只有在目标不存在时,其才会参与规则的执行。
有时我们需要定义一个这样的规则,在更新目标(目标文件已经存在)时只需要根据依赖文件中的部分文件来决定目标是否需要被重建,而不是在依赖文件的任何一 个被修改后都重建目标。为了实现这个目的,我们需要对依赖进行分类,一类是这些依赖文件的更新需要更新目标文件;另一类是这些依赖文件的更新不会导致目标被重建。前者即 normal prerequisites,后者即 order-only prerequisites。

语法格式如下:

targets : normal prerequisites | order-only prerequisites
commands

1 order-only prerequisites 使用管道符号(|)开始。管道符号的左边是 normal prerequisites,管道符号的右边是 order-only prerequisites。
2 规则中 normal prerequisites 和 order-only prerequisites 都可以为空。
3 order-only prerequisites 所实现的动作是 normal prerequisites 所实现动作的子集。如果一个文件被同时声明为 normal prerequisites 和 order-only prerequisites,那么此文件会作为 normal prerequisites 处理。

示例 2
Makefile 文件的内容如下
test : 1.o | test.o
gcc -o test test.o 1.o

1.o : 1.c
gcc -c 1.c

test.o : test.c 1.h
gcc -c test.c

clean :
-rm test 1.o test.o

在命令提示符下输入 “make -n”,执行结果如下:
gcc -c 1.c
gcc -c test.c
gcc -o test test.o 1.o

在示例 2 中,目标 test 不存在时,目标 test 所有的依赖所在的规则都将被执行,目标 test 被创建。

在命令提示符下输入 “make -n”,执行结果如下:
gcc -c test.c

在示例 2 中,目标 test 存在时,修改了 test.c 或者 1.h 之后,目标 test.o 所在的规则会被执行,但是目标 test 不会被重建。因为 test.o 已被指定为 order-only prerequisites,其只有在目标 test 不存在的情况下,才会参与目标 test 的创建。

没有依赖的规则(rules without prerequisites)
如果一个规则没有依赖,而且它的目标也不是一个存在的文件。make 在执行此规则时,会认为目标已经被更新。这样的目标作为另一个规则的依赖时,因为依赖总被认为更新过,因此作为依赖所在的规则定义的命令总会被执行(即此规则会被无条件执行)。

示例 3
Makefile 文件的内容如下
clean : FORCE
-@rm *.o

FORCE :

在示例 3 中,规则 FORCE :没有依赖和命令,且目标 FORCE 也不存在。make 在执行 FORCE :规则时,会认为目标 FORCE 被更新过,故目标 clean 所在的命令会被无条件执行。

事实上,使用无依赖的规则(这里是 FORCE :)作为当前规则(这里是 clean : FORCE)的依赖和将当前规则的目标(这里是 clean)指定为伪目标(.PHONY : clean)的效果相同。两种方式相比较,使用 .PHONY 的方式声明更加直观和高效。
无依赖的规则主要用在不支持 .PHONY 的 make 版本中。在 GNU make 中,我们建议使用伪目标方式。

空目标文件(empty target files to record events)
空目标是伪目标的一个变种。和伪目标不同的是,这个目标可以是一个存在的文件,但是我们不关心文件的具体内容,此文件的内容一般为空。
空目标文件的作用是用来记录上一次执行此规则定义的命令的时间。在这样的规则中,命令部分的最后会使用 touch 来更新目标文件的时间戳,以记录规则命令的执行完成时间。touch 命令在文件不存在时,将创建一个空的文件。
一个空目标文件应该存在一个或者多个依赖文件,否则没有意义。当依赖文件比空目标文件更新时,空目标文件所在规则中定义的命令会被执行,空目标文件的时间戳会被更新。

示例 4
Makefile 文件的内容如下
print : 1.c
touch $@

在示例 4 中,当目标文件 print 不存在、依赖 1.c 修改后,会创建和更新空目标文件 print。空目标文件 print 的时间戳记录了命令最后执行的时间。

多目标规则(multiple targets in a rule)
规则中可以有多个目标,所有的目标具有相同的依赖文件,规则所定义的命令对所有的目标都有效。一个具有多目标的规则相当于多个规则。
在多目标的规则中通常会使用自动化变量,动态实现对不同目标的操作。

多目标规则常用于下面两种情况:
1 仅需要一个描述依赖关系的规则,而不需要在规则中定义命令。
2 具有类似重建目标的命令的规则。

示例 5
Makefile 文件的内容如下
# 当前工作目标不存在文件 printa、printb、printc
# 当前工作目标存在 printd
print : printa printb printc

printa printb printc : printd
@echo $@ : $<
@echo $(patsubst print%,print,$@) > $@

在命令提示符下输入 “make -n”,执行结果如下:
echo printa : printd
echo print > printa
echo printb : printd
echo print > printb
echo printc : printd
echo print > printc

虽然在多目标的规则中,可以根据不同的目标使用不同的命令,但是多目标的规则并不能做到根据目标文件自动改变依赖文件(多目标规则的依赖文件都相同)。

多规则目标(multiple rules for one target)
一个文件可以作为多个规则的目标。此目标文件的所有依赖文件将会被合并成此目标的一个依赖文件列表。其中任何一个依赖文件比目标更新时,目标文件所在的命令将会被执行。
重建多规则目标的命令只能出现在一个规则中。如果多个规则给出命令,make 将使用最后一个规则所用的命令,并提示警告信息。
warning: overriding commands for target。
warning: ignoring old commands for target。

多规则目标常和多目标规则结合使用,多规则目标用于描述依赖关系;多目标规则用于定义类似的重建规则的命令。

示例 6
Makefile 文件的内容如下
# 当前工作目标不存在文件 printa、printb、printc
# 当前工作目标存在 printd
print : printa printb printc

printa printb printc : printd
@echo $@ : $<
@echo $(patsubst print%,print,$@) > $@

printa : printc

在命令提示符下输入 “make -n”,执行结果如下:
echo printc : printd
echo print > printc
echo printa : printd
echo print > printa
echo printb : printd
echo print > printb

在示例 6 中,目标 printa 的依赖文件有:printd、printc。故依赖 printd 和 printc 的改变都将触发 printa 所在规则的命令被执行。规则 printa : printc 的作用是控制目标的处理顺序(目标 printa 依赖 printc,故目标 printc 所在的规则需要先处理)。

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

语法格式如下:

targets : target-pattern : prerequisites-pattern
commands

1 规则中的目标、依赖、命令都可以使用通配符,和普通规则的用法一样。
2 目标模式(target-pattern)和依赖模式(prerequisites-pattern)说明了如何为每一个目标文件生成依赖文件。从 target-pattern 中获取符合模式的字符串(称为茎),使用茎替代 prerequisites-pattern 中的模式字符来产生目标文件的依赖文件。
3 target-pattern 和 prerequisites-pattern 一般都包含模式字符(%)。
4 每一个目标的依赖文件是使用此目标的茎替代依赖模式中的模式字符(%)而得到的。
5 在模式规则中,如果需要使用模式字符,需要使用反斜线转义(\%)。

示例 7
Makefile 文件的内容如下
objects = 1.o test.o

test : $(objects)
gcc -o test test.o 1.o

$(objects) : %.o : %.c
echo $@ : $<
echo stem : $*
gcc -c $<

在命令提示符下输入 “make -n”,执行结果如下:
echo 1.o : 1.c
echo stem : 1
gcc -c 1.c
echo test.o : test.c
echo stem : test
gcc -c test.c
gcc -o test test.o 1.o

在示例 7 中,目标 1.o 符合目标模式 %.o,故其茎为 1;然后用茎替代依赖模式中的模式字符,得到依赖文件为 1.c。

静态模式规则和多目标规则的区别在于前者可以为不同的目标文件指定不同的依赖文件,后者对于所有的目标文件都具有相同的依赖文件。可以说,多目标规则实现的功能是静态模式规则的功能子集。
静态模式规则在一个大型的工程中非常有用,它通过对工程中的同类文件的重建规则进行一次定义,就可以实现整个工程中同类文件使用相同的重建规则。

自动产生依赖(generating prerequisites automatically)
在 makefile 中,通常会需要一些规则来描述一个 .o 目标文件和头文件直接的依赖关系。如在 test.c 中使用语句 #include 1.h,那么我们会需要规则 test.o : test.c 1.h 来描述当头文件 1.h 被修改后,目标 test.o 被重建。
在一个源文件中加入或者删除头文件后,也需要修改 makefile。为了避免这个问题,现代的 linux c 编译器提供了通过查找源文件中的 #include 来自动产生这种依赖关系的功能。

gcc -M c_file.c
gcc -MM c_file.c

使用 gcc 自动产生依赖关系时,-M 选项表示输出的依赖关系中包含所有的头文件(包括标准库头文件);-MM 选项表示输出的依赖关系中不考虑标准头文件。

如 test.c 包含了头文件 1.h、stdio.h,则执行 gcc -MM test.c,其输出为:test.o: test.c 1.h。

示例 8
Makefile 文件的内容如下
files = 1.c test.c

test : $(files:%.c=%.o)

define rules
$(shell gcc -MM $1)
@echo $$@ : $$?
gcc -c $$<
endef

$(foreach file,$(files),$(eval $(call rules,$(file))))

clean :
-rm test *.o

在命令提示符下输入 “make”,执行结果如下:
test.o : test.c 1.h
gcc -c test.c
1.o : 1.c
gcc -c 1.c
cc test.o 1.o -o test

在示例 8 中,通过 eval 函数动态的构建规则,$(shell gcc -MM $1) 将输出一个依赖关系。通过自动产生依赖关系,我们就实现了一个动态创建规则的模版。
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值