GNU make---When Variables Are Expanded

GNU make---When Variables Are Expanded 

变量引用中的内容依赖于变量定义的内容,及其在Makefile中位置。 在定义和使用变量时,经常会出现意想不到的错误,其中很大原因就是由于变量定义或者变量使用错误。

本节将介绍变量展开规则及其工作原理。

Make运行时,分为两个阶段。

在第一阶段中,Make读入makefile文件,包括所有被包含的子makefile。此时,变量和规则都被导入到Make的内部数据库,并且建立好依赖关系图。

第二阶段,Make分析依赖关系图,决定哪些文件需要更新,然后执行命令脚本进行更新。

当Make处理递归变量或define指令时,变量所在的行或宏体将被存储起来,包括换行符(不需要展开)。宏定义的最后一个换行符不需要被存储起来。否则当宏展开时,将会有一个换行符。

当宏展开时,Make会立即扫描宏内容,进一步展开宏内容中的变量引用。如果宏在命令语句中展开,宏体内的每一行必须以TAB符开始。

总结:

  • 对于变量赋值,在Make读入makefile的第一阶段中,赋值符左侧部分总是立即展开。
  • “=”和“?=”的右侧部分的展开则是延迟到第二阶段。
  • “:=”的右侧部分为立即展开。
  • “+=”的右侧部分,当左侧部分为简单变量时,立即展开;否则延迟到第二阶段展开。
  • 对于宏定义(使用define指令),宏变量立即展开,而宏体则延迟到使用时展开。
  • 对于rules, 目标和依赖对象总是立即展开,命令行则是延迟展开。
Table 3-1. Rules for immediate and deferred expansion

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 }' endef

define指令后面的变量名将会立即展开。宏体将被读取,并直接存储不需要展开。

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的右侧部分都是记录展开。

你可能感兴趣的:(File,存储,makefile,disk,output,variables)