makefile变量

Invoker

makefile 变量与宏

变量和宏其实说的是同一东西。一个变量的内容是一个字符串,从一个变量名获得变量内容的过程叫做变量的扩展,用$()或者${}扩住变量名即可。而不想编程语言那样,使用变量名就能引用变量的值。

变量的类型

make的变量有两种:简易变量和递归扩展的变量。变量的定义是一个赋值动作,把等号右边的内容赋给左边。这里等号可以有多种::=、=、?=,他们决定了怎样赋值。等号两边的东西可以只是字面值,或者含有变量。赋值时,变量名需要是明确的,等号左边的内容立即扩展(没有变量就保持不变),右边的内容根据赋值符号决定何时扩展。

简易变量

:=或者::=赋值运算符定义的是一个简易扩展的变量。一旦make读入该变量的定义语句,赋值运算符右边部分会立刻扩展,而扩展后的文本会被存储成该变量的值。变量名和变量内容加入到数据库。
MAKE_DEPEND := $(CC) -M
此变量一般被扩展为
gcc -M
然而,如果CC变量尚未定义,则扩展为:
<space>-M
变量没有定义不算错误。

递归变量

=定义的变量。或者define定义的变量。
make只会读进赋值运算符后边的部分,并将之存储成该变量的值,但不会进行任何扩展的动作,扩展的动作会被延迟到该变量被使用的时候进行。

其他赋值类型

make还提供了另外两种赋值运算符:?=+=

?= 运算符称为附带条件的变量赋值运算符。此运算符只会在变量的值尚不存在是进行复制动作。
+= 运算符称为附加运算符。此运算符会将文本附加到变量里。对递归变量仍有用。
对于简单变量,等效于
simple := $(simple) new stuff
然而,对于递归变量
recursive = $(recursive) new stuff
这种表达式是非法的,会被无限扩展。要想将某段文本附加到递归变量上,就需要附加运算符了。

工作目标与模式的专属变量

make提供了工作目标的专属变量。这些变量的定义会附加在工作目标上,且只有在该工作目标以及相应的必要条件被处理的时候,他们才会发生作用

工作目标的专属变量语法如下:

target...: variable = value
target...: variable := value
target...: variable ?= value
target...: variable += value

如果在编译某个源文件的时候需要单独指定一个宏定义,然而该文件又在某个模式规则中:

gui.o: CPPFLAGS += -DUSE_NEW_MALLOC=1
gui.o: gui.h

当make处理gui.o这个工作目标时,CPPFLAGS这个变量会附加-DUSE_NEW_MALLOC=1,当处理完gui.o这个目标之后,CPPFLAGS会恢复它原来的值。

变量的来源

文件

变量可以被定义在makefile中,或是被makefile引入(include指令)

命令行

可以直接在make命令行上定义或者重新定义变量: $ make CFLAG=-g CPPFLAGS='DBSD -DDEBUG'
每个命令行参数中所包含的等号,都是一个变量赋值运算符。在命令行上,每个变量赋值运算符的右边部分必须是一个单独的shell参数。如果变量的值含有空格,则必须为参数加上括号或是引号。
命令行上变量的赋值将会覆盖掉环境变量以及makefile中的赋值结果。如果要使makefile中的变量覆盖命令行中的变量,可以在makefile中的变量前加override指令。

环境

当make启动时,所有来自环境的变量都会被定义为make的变量。这些变量具有非常低的优先级,makefile文件或命令行参数的赋值结果都会覆盖环境变量的值。但是,可以使用--environment-overrides(或-e)命令行选项,让环境变量覆盖相应的makefile变量。
当make被递归调用时,有若干来自上层make的变量会通过环境传递给下层的make。默认只有原来就来自环境的变量会被导出到下层的环境中。可以使用export指令导出任何变量。

自动创建

make会在执行一个规则的命令脚本之前立即创建自动变量。注意自动变量创建的时机。

在makefile中定义变量

variable := value

variable = value

variable ?= value

define variable =
...
...
endef
define variable :=
...
...
endef

define的特长是可以定义多行内容的变量。

一个变量的值由赋值符号右边除去前导空格的所有字符组成。跟在所有字之后的空格不会被删除。这有时会导致问题。

宏是以前对变量的另一种称呼。
可以通过define指令创建“封装命令序列”,称为宏。在make中,宏只是用来定义变量的另一种形式,此变量还可以包含换行符。一般,将由define定义的变量称为宏,由赋值运算符定义的变量称为变量。

define后跟着变量名和换行,变量的主体是由跳格符开头的命令行,最后以endef结尾。

define create-jar
    @echo Creating $@...
    $(RM) $(TMP_JAR_DIR)
    $(MKDIR) $(TMP_JAR_DIR)
    $(CP) -r $^ $(TMP_JAR_DIR)
    cd $(TMP_JAR_DIR) && $(JAR) $(JARFLAGS) $@ .
    $(JAR) -ufm $@ $(MANIFEST)
    $(RM) $(TMP_JAR_DIR)
endef

@的作用:make将每条命令交给shell执行之前都会打印出此条命令,在该命令之前加@可以使make不这样做。如果在命令中应用了一个宏,使用@将使整个宏扩展后的命令之前都加上@。

当make运行时,它会以两个阶段来完成他的工作。第一阶段,make读进makefile以及被引入的任何其他makefile。这时,其中所定义的变量会被加载到make的内部数据库,并建立依存图。第二阶段,make分析依存图并判断需要更新的目标,然后执行脚本。

当make在处理递归变量或者define指令的时候,会将变量里的每一行或宏的主体存储起来,包括换行符号,但不会予以扩展。宏定义的最后一个换行符不会作为宏的一部分,否则,宏被扩展时会多一个换行。
当宏被扩展时,make会立即扫描被扩展的文本中是否存在宏或变量的引用,如果存在就予以扩展,如此递归下去。如果宏是在命令脚本里被扩展的,则宏的主体的每一行都会被插入一个跳格符。

下面是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

说明:

第一阶段:make逐行读取makefile并将变量加入内部数据库,建立依存图。
OUTPUT_DIR是简单变量,它的值就是普通的字面值(如果这里有$引用的变量,将会进行扩展动作),放到数据库中。
接下来是一条规则,规则的目标和条件都是立即扩展的,而命令是延后扩展的,保持不变。所以这条规则变为:

/tmp/very_big_file:
    $(free-space)

之后是一个宏定义,宏名是立即扩展的,这里只是字面值,不用扩展。宏体是延后扩展的,在使用该宏的时候才扩展。
最后4个简易变量都是直接扩展的,将变量值加入到数据库中。

第二阶段:
按照后序遍历规则树进行规则的执行动作。这时要使用规则中的命令部分,对命令中的变量和宏进行扩展,并执行命令。

自动变量

自动变量是一种make在处理规则时自动赋值的变量。

变量名 描述
$@ 工作目标的文件名
$% 档案文件成员结构中的文件名元素
$< 第一个必要条件的文件名
$? 时间戳在工作目标之后的所有必要条件,并以空格隔开。
$^ 所有必要条件的文件名,并以空格隔开。
$+ 如同$^,代表所有必要条件的文件名,并以空格隔开。不过$+包含重复的文件名。
$* 工作目标的主文件名。文件名由主文件名和扩展名构成。

说明: 档案文件中个别的成员可作为工作目标或必要条件。可以通过archive(member)这样的语法在档案文件archive中指定名为member的成员。若工作目标是foo.a(bar.o),则$%bar.o$@foo.a。当工作目标不是一个档案文件时,$%是空的。

阅读 1.8k

Invoker
Invoker的个人博客
34 声望
0 粉丝
0 条评论
34 声望
0 粉丝
宣传栏