PS:直接从WORD拷贝,未对格式进行仔细整理
3 makefilemakefile中的变量
在makefile中,我们可以使用变量来简化我们的makefile脚本的编写
例1 Makefile中变量的使用(1)
CC = gcc
CFLAGS= -Wall –g –std=c99
LDFLAGS =-lm
circle : circle.o circulararea.o
$(CC) $(LDFLGAS) –o circle circle.o circulararea.o
circle.o : circle.c
$(CC) $(CFLAGS) –o circle.o–c circle.c
circulararea.o : circulararea.c
$(CC) $(CFLAGS) –o circulararea.o –c circulararea.c
在例7中,CC、CFLAGS、LDFLAGS都是变量,当make构建一个规则时,它会替换(或称为展开)“包含在该规则中的目标、前提和命令中的”所有变量。对于例7make程序会最终这样展开它:
circle : circle.o circulararea.o
gcc -lm –o circle circle.o circulararea.o
circle.o : circle.c
gcc -Wall –g –std=c99 –o circle.o–c circle.c
circulararea.o : circulararea.c
gcc -Wall –g –std=c99 –o circulararea.o –c circulararea.c
当然我们还可以进一步简化例8,简化后的展开与上面相同,那就是使用模式规则和自动化变量:
%.o : %.c
$(CC) $(CFLAGS) –o $@ –c $<
关于模式规则和自动化变量,将在后面讲述。
在make中,所有的变量都属于相同的类型:字符序列。
还有一点需要注意的是变量名不能使用:、=、#等特殊字符。
3.1 变量的递归展开和简单展开
在makefile中,对变量赋值有两种赋值符号,一个是:=,一个是=,如下例子所示:
CFLAGS = -Wall –g –std=c99
CFLAGS := -Wall –g –std=c99
需要强调的是,这两种方式是不等同的。到底有什么区别呢?我们还是举例说明吧!
例2 Makefile中变量的使用(2)
VAR1 := $(VAR0)
VAR2 = $(VAR0)
VAR0 = varValue
上面的变量VAR1, 和VAR2展开后的值是什么呢?
答案是:
VAR1=
VAR2= varValue
VAR1的值是空的!!! VAR2的值则等于VAR0的值。为什么会这样呢?
因为在makefile中对变量的展开存在两种方式:递归展开和简单展开。使用=对变量赋值时会递归式展开,使用:=对变量赋值时会简单展开。下面是对这两种展开方式的定义:
递归展开的变量中,嵌套的变量引用(nested variable references)会被逐字存储,直到此变量的值被计算出来。[LU1]
简单展开得我变量中,变量引用会在赋值时立刻展开,并且展开的值会被存储起来,而不是存储变量的名称。[LU2]
在例8中,VAR1是简单变量,因此会被立刻展开;它被赋予了VAR0的值,但是此时并没有定义VAR0,因此该值被展开为空值,因此VAR1的值也就是空。
VAR2则是递归变量,因此由于VAR0未定义,因此在语句VAR2 := $(VAR0)中,VAR2并不会立即展开,直到VAR0被定义可以被计算出来的时候,才会被展开。
那么在实际应用中应该使用递归变量还是简单变量呢?
笔者的建议是:最好使用简单变量,而不要使用递归变量。
为什么呢?其主要缺点在于无法在变量的末尾附加其它东西,如:
CFLAGS = $(CFLAGS) –O
因为这种会导致变量扩展的无限循环。
另一个缺点是变量定义中引用的任何函数都会在每次扩展时被执行一遍。这会使make运行变慢;更糟的是,它会使wildcard和shell函数给出不确定的结果,因为无法轻易地控制函数被调用的时间与次数。
使用简单变量的时候一定要注意:一个变量要引用的变量一定要在该变量之前定义。
3.2 变量和空格
在一个变量的赋值操作中,make会忽略”赋值运算符”和”值的第一个非空格符”之间的任何空白。
例如:
CFLAGS := -Wall –g –std=c99
展开后的值为“ -Wall –g –std=c99“,而不是“ -Wall –g –std=c99“
另外需要注意的是,行结束或遇到该行注释之前,任何部分包括空格都属于变量的值,例如:
CFLAGS := -Wall –g –std=c99 # here is comment[LU3]
展开后的值为“ -Wall –g –std=c99 “
因此如果你不想要后面的空格的画,在键入变量值的时候一定要注意。
还有一个问题,如果我们希望一个变量的值就是一个空格呢?
或许你会认为应该这样:
ONESPACES := ‘ ‘
但是实际的效果是,ONESPACES的值是一个单引号,加上一个空格,再加上一个单引号
实际可行的方法应该是:
NOTHING :=
ONESPACES := $(NOTHING) #this comment termintes the variable’s value.
3.3 自动变量
CC = gcc
CFLAGS = –Wall –g –std=c99
obj.o : obj.c
$(CC) $(CFLAGS) –o obj.o –c obj.c
我们会obj.o既在目标中用到了,也在命令中用到了;obj.c既在前提中用到了,也在命令中用到了。
对于上面的这个makefile脚本,可以使用下面的makefile脚本替换,两者之间是等同的:
CC = gcc
CFLAGS = –Wall –g –std=c99
obj.o : obj.c
$(CC) $(CFLAGS) –o $@ –c $<
其中的$@和$<称为自动变量,由make在实现规则时定义和展开。自动变量都有自己固定的特殊含义。
$@展开的含义是目标名称,在这里就是obj.o
$<展开的含义第一个前提,这里只有一个前提,因此就是obj.c
接下来用例子的形式,给大家介绍其他常用的自动变量
CC = gcc
LDFLAGS =-lm
app : obj1.o obj2.o obj2.o obj3.o
$(CC) $(LDFLAGS) –o $@ $^
自动变量$^展开的含义是前提的列表,但是排除重复的元素,因此上面的规则展开后就是
app : obj1.o obj2.o obj2.o obj3.o
gcc -lm –o app obj1.o obj2.o obj3.o
与$^类似的自动变量是$+,但是它展开的含义是前提完整列表,也就是说包含重复的元素。
$?则是比目标新的前提列表
$*则是目标文件名的stem,也就是模式规则中%所代表的部分。(关于什么是stem,什么是模式规则,请参考后面章节)