变量和宏其实说的是同一东西。一个变量的内容是一个字符串,从一个变量名得到变量内容的过程叫作变量的扩展,用$()或者${}扩住变量名便可。而不想编程语言那样,使用变量名就能引用变量的值。shell
make的变量有两种:简易变量和递归扩展的变量。变量的定义是一个赋值动做,把等号右边的内容赋给左边。这里等号能够有多种::=、=、?=,他们决定了怎样赋值。等号两边的东西能够只是字面值,或者含有变量。赋值时,变量名须要是明确的,等号左边的内容当即扩展(没有变量就保持不变),右边的内容根据赋值符号决定什么时候扩展。数据库
用:=
或者::=
赋值运算符定义的是一个简易扩展的变量。一旦make读入该变量的定义语句,赋值运算符右边部分会马上扩展,而扩展后的文本会被存储成该变量的值。变量名和变量内容加入到数据库。MAKE_DEPEND := $(CC) -M
此变量通常被扩展为gcc -M
然而,若是CC变量还没有定义,则扩展为:<space>-M
变量没有定义不算错误。编程
用=
定义的变量。或者define
定义的变量。make
只会读进赋值运算符后边的部分,并将之存储成该变量的值,但不会进行任何扩展的动做,扩展的动做会被延迟到该变量被使用的时候进行。编程语言
make还提供了另外两种赋值运算符:?=
和+=
。ide
?=
运算符称为附带条件的变量赋值运算符。此运算符只会在变量的值尚不存在是进行复制动做。+=
运算符称为附加运算符。此运算符会将文本附加到变量里。对递归变量仍有用。
对于简单变量,等效于simple := $(simple) new stuff
然而,对于递归变量recursive = $(recursive) new stuff
这种表达式是非法的,会被无限扩展。要想将某段文本附加到递归变量上,就须要附加运算符了。ui
make提供了工做目标的专属变量。这些变量的定义会附加在工做目标上,且只有在该工做目标以及相应的必要条件被处理的时候,他们才会发生做用。spa
工做目标的专属变量语法以下:命令行
target...: variable = value target...: variable := value target...: variable ?= value target...: variable += value
若是在编译某个源文件的时候须要单独指定一个宏定义,然而该文件又在某个模式规则中:code
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会在执行一个规则的命令脚本以前当即建立自动变量。注意自动变量建立的时机。
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中的元素什么时候被扩展的原则:
=
和?=
的右边会被延后到他们被使用时扩展,而且在第二阶段运行。:=
右边的部分会被当即扩展+=
的左边部分本来被定义成一个简单变量,+=
的右边就会被当即扩展,不然,求值动做就会延后。延后扩展发生在它所在的表达式须要被扩展时,好比在规则的目标和必要条件中、在一个将要执行的命令中或者出如今简易变量赋值的右边等等。
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
。当工做目标不是一个档案文件时,$%
是空的。