分类: LINUX
2012-05-21 14:55:30
变量引用中的内容依赖于变量定义的内容,及其在Makefile中位置。 在定义和使用变量时,经常会出现意想不到的错误,其中很大原因就是由于变量定义或者变量使用错误。
本节将介绍变量展开规则及其工作原理。
Make运行时,分为两个阶段。
在第一阶段中,Make读入makefile文件,包括所有被包含的子makefile。此时,变量和规则都被导入到Make的内部数据库,并且建立好依赖关系图。
第二阶段,Make分析依赖关系图,决定哪些文件需要更新,然后执行命令脚本进行更新。
当Make处理递归变量或define指令时,变量所在的行或宏体将被存储起来,包括换行符(不需要展开)。宏定义的最后一个换行符不需要被存储起来。否则当宏展开时,将会有一个换行符。
当宏展开时,Make会立即扫描宏内容,进一步展开宏内容中的变量引用。如果宏在命令语句中展开,宏体内的每一行必须以TAB符开始。
总结:
Definition |
Expansion of a | Expansion of b |
a = b |
Immediate |
Deferred |
a ?= b |
Immediate |
Deferred |
a := b |
Immediate |
Immediate |
a += b |
Immediate |
Deferred or Immediate |
define ab . . . b . . . b . . . endef |
Immediate |
Deferred |
作用通用规则,一般都是在使用前定义变量和宏,特别是在目标和依赖对象中使用的变量。
分析一个例子将有助于理解。下面我们来分析宏free-space。
BIN := /usr/bin PRINTF := $(BIN)/printf DF := $(BIN)/df AWK := $(BIN)/awk我们定义了三个变量,他们将回来宏被引用。为了避免代码重复,我们定义了变量BIN. 这四个变量将会在读取时立即展开,因为他们是简单变量。
因为BIN在其他变量前定义,所以它会被插入到其他三个变量中。
define free-space $(PRINTF) "Free disk space " $(DF) . | $(AWK) 'NR = = 2 { print $$4 }' endefdefine指令后面的变量名将会立即展开。宏体将被读取,并直接存储不需要展开。
OUTPUT_DIR := /tmp $(OUTPUT_DIR)/very_big_file: $(free-space)当读取$(OUTPUT_DIR)/very_big_file,目标和依赖对象中的任意变量都需要立即展开。$(OUTPUT_DIR)展开为/tmp, 目标变为/tmp/very_big_file。接着读取命令行,以tab开始的命令行,将被读取,并直接存储不需要展开。
下面是整个makefile, 他们的内容被故意打乱,来进一步分析Make的展开算法。
OUTPUT_DIR := /tmp $(OUTPUT_DIR)/very_big_file: $(free-space) define free-space $(PRINTF) "Free disk space " $(DF) . | $(AWK) 'NR = = 2 { print $$4 }' endef BIN := /usr/bin PRINTF := $(BIN)/printf DF := $(BIN)/df AWK := $(BIN)/awk可以看到尽管makefile的内容被弄反了,但是它依然能执行正确。这就是递归变量的作用之一,它非常有用,但是也极具混淆,很容易出错。这个makefile可以正确执行的原因是,命令行和宏体展开都被延迟到使用时,因此他们的相对位置对于makefile的执行并不重要。在读取makefile后,处理makefile时的第二阶段,Make确定目标,并进行依赖分析,执行规则命令。此时$(OUTPUT_DIR)/very_big_file就是唯一的目标,没有依赖对象,所以Make执行命令(假定目标文件不存在)。命令为$(free-space)。Make展开后为:/tmp/very_big_file: /usr/bin/printf "Free disk space " /usr/bin/df . | /usr/bin/awk 'NR = = 2 { print $$4 }'一旦展开全部变量,Make将执行命令行。下面来看看makefile顺序的重要性。正如上边解释的,目标$(OUTPUT_DIR)/very_big_file是立即展开。如果变量OUTPUT_DIR定义在$(OUTPUT_DIR)/very_big_file的后边,目标将会展开为/very_big_file,并非我们想要的。如果变量BIN定义在变量AWK后面,PRINTF,DF,AWK将被展开为/printf,/df,/awk,因为“:=”将会使其右侧部分立即展开。在这种情况下,我们可以把“:=”改为“=”来避免这种问题,标记他们为递归变量。 最后注意一点:如果把OUTPUT_DIR和BIN变为递归变量,也是不能解决顺序问题的。因为$(OUTPUT_DIR)/very_big_file和PRINTF,DF,AWK的右侧部分都是记录展开。