make包含两种语言。第一种语言用来描述工作与必要条件所组成的依存图。第二种语言是宏语言,用来进行文字替换。像
C预处理器,m4以及宏汇编器。
一个变量名称几乎可以由任何字符自称。包括大部分的标点符号,即使空格也可以使用,但应该避免这么做。事实上只有:
、#和=等字符不允许使用在变量名称中。
变量名称是区分大小写的。要取得某个变量的值,请使用$()括住该变量的名称,有一个特例:变量名称若为单一字母(letter)
则可以省略圆括号,所以请直接使用$letter。
你还可以使用花括号来扩展变量,例如${CC}.现代化的makefile多半使用园括号。
当 变量用来表示用户在命令行上或环境中所定义的常数时,习惯上会全部以大写来编写其名称,单词之间以下划线符号_隔开。
至于只在makefile文件中出现的变量,则会全部以小写来编写其名称,单词之间以下划线符号隔开。最后,内含用户自定义
函数的变量以及宏都会以小写来编写其名称,单词之间以破折号-隔开。
#常数
CC := gcc
MKDIR := mkdir -p
#内部变量
sources = *.c
objects = $(subst .c,.o,$(sources))
#函数
maybe-make-dir = $(if $(wildcard $1),,$(MKDIR) $1)
1.变量的类型
make的变量有两种类型:经简单扩展的变量以及经递归扩展的变量
你可以使用:=赋值运算来定义一个经简单扩展的变量:
MAKE_DEPEND := $(CC) -M
之所以称此变量为经简单扩展时因为,一旦make从makefile读进该变量的定义语句,赋值运算符的右边部分会立即被扩展。
赋值运算符的右边部分只要出现make变量的引用就会被扩展,而扩展后所产生的文本则会被存储称该变量的值。此行为跟
大多数的程序和脚本语言相同。举例,此变量被扩展之后一般会成为下面这样:
gcc -M
然而,如果上面的CC变量尚未定义,则此变量被扩展后一般会变成这样:
<space> -M
第二种变量类型称为经递归扩展的变量。你可以用=赋值运算符来定义一个经递归扩展的变量:
MAKE_DEPEND = $(CC) -M
之所以称此变量为经递归扩展是因为,make只会读进赋值运算符右边的部分,并将之存储成该变量的值,但不会进行任何
扩展的动作,扩展动作会被延迟到该变量被使用的时候才进行,将此变量称为延后扩展的变量或许比较恰当。因为扩展动作
会延迟到该变量实际被使用的时候才进行。
MAKE_DEPEND = $(CC) -M
...
#稍后
CC = gcc
这样,当MAKE_DEPEND被使用的使用,即使CC并未定义,MAKE_DEPEND在脚本中的值也会被扩展成gcc -M。
2.其他的赋值类型
make还另外提供了两种赋值运算符:?= 和 +=。
?= 运算符称为附带条件的变量赋值运算符。此运算符只会在变量的值尚不存在的状况下进行变量要求赋值的动作。
#将所产生的每个文件放到$(PROJECT_DIR)/out目录中。
OUTPUT_DIR ?= $(PROJECT_DIR)/out
我们只会在输出目录变量OUTPUT_DIR的值尚不存在的状况下对它进行赋值的动作。
+=运算符通常被称为附件运算符,此运算符会将文本附加到变量中,对于简单变量进行附加的动作,+=运算符可以被实现
成这样:
simple := $(simple) new stuff
但是递归变量会导致一个问题,如果将+=运算符实现成下面这样,是不允许的。
recursive = $(recursive) new stuff
这时一个错误,因为make没有办法妥善地加以处理。如果make存储recursive当前的定义加上new stuff,则make就不能在
运行时再次扩展它。此时试图扩展一个自我引用的递归变量将会产生一个无限循环。
所以,+=被特别实现成可将文本附加到递归变量中并做正确的事。
3.宏
变量适合用来存储单行形式的值,对于多行形式的值,例如命令脚本,如果我们想在不同的地方执行它,该怎么办?例如
下面的命令序列:
ehco create [email protected]
$(RM) $(TMP_DIR)
$(MKDIR) $(TMP_DIR)
$(CC) main.c
$(RM) $(TMP_DIR)
在GNU make中,我们可以通过define指令以创建宏,在make中,宏只是用来定义变量的另一种方式,此变量还可以包含
内置换行符号。
define create-obj
@echo create [email protected]
$(RM) $(TMP_DIR)
$(MKDIR) $(TMP_DIR)
$(CC) main.c
$(RM) $(TMP_DIR)
endef
define指令后面跟着变量名称以及一个换行符号。变量的主体包含了所有的命令序列直到endef关键字出现为止,endef关键字
必须自成一行。一个由define创建的变量,就像任何其他的边来个你一样,会被扩展许多次,除非它被使用在命令脚本的语境
中,下面是宏的使用范例:
$(UI-OBJ):$(UI-CLASSES)
$(create-obj)
请注意,我们为echo命令前置了一个@字符,当执行命令脚本时,前置@字符的命令行不会被make输出,因此,当我们运行
make时,它不会输出echo命令本身,只会输出该命令的输出,如果在宏内部使用@前缀,这个前缀字符只会影响使用到它的
命令行。然而,如果将这个前缀用在宏引用上,则整个宏主体都会被隐藏起来:
$(UI-OBJ):$(UI-CLASSES)
@$(create-obj)
当make运行时只会显示:
$make
create ui-obj...