Makefile中=与:=的区别

在GNU make中,可以使用“=”和“:=”来定义变量,二者的区别主要在于展开时机。

1. 使用“:=”定义变量

  使用“:=”定义的变量称为“简单展开变量(simply expanded variables)”,顾名思义,“:=”右边的表达式如果包含对变量的引用,则这些对变量的引用会直接展开,得到一个确切的值,并赋给“:=”左边的变量。这种定义和赋值的形式类似于C语言中对变量的赋值,举例来说:

x := foo
y := $(x) bar
x := later

 等价于:

y := foo bar
x := later

 在y := $(x) bar 处,x 的值为foo ,故y 被赋值为foo bar ,随后x := later 更新了x 的值,最终x 的值为later ,y 的值为foo bar 。

2.使用“=”定义变量

  使用“=”定义的变量称为“递归展开变量(recursively expanded variables)”,在使用“=”定义变量时,如果“=”右边存在对其他变量或函数的引用,这些引用并不会立即展开。在实际使用到递归展开变量时,递归展开变量才会展开,同时定义递归展开变量时使用的其他变量或函数的引用也会被展开,从而得到当前递归展开变量的值。在引用递归展开变量的地方,执行的是严格的文本替换过程,递归展开变量中的字符串原模原样的出现在引用变量的地方,而递归展开变量中对其他变量的引用,只会在递归展开变量被展开的同时被展开。文字的描述比较复杂,举例来说,下面的例子将输出Huh? :

foo = $(bar)
bar = $(ugh)
ugh = Huh?

all:;echo $(foo)

foo = $(bar) 处,foo 的定义使用了“=”,是一个递归展开变量,在这里bar 还没有定义,实际上bar 并也没有在这里定义foo 时展开。在bar = $(ugh) 处同理。而在echo $(foo) 处,引用了foo ,这时foo 被展开,发现bar 的引用,进而展开bar ,又发现了ugh 的引用,再展开ugh ,得到Huh? ,于是以此作为foo 的值进行输出。

  实际上,递归展开变量的值是它在整个Makefile中最后被指定的值,如下面的例子将输出Ha!

foo = $(bar)
bar = $(ugh)
ugh := Huh?

all:;echo $(foo)

ugh := Ha!

注意ugh := Huh? 和ugh := Ha! 使用了“:=”。这里涉及到GUN make的执行过程,GUN make的执行过程分为两个阶段:第一阶段读取所有的makefile文件(包括“MAKIFILES”变量指定的、指示符“include”指定的、以及命令行选项“-f(–file)”指定的makefile文件),内建所有的变量、明确规则和隐含规则,并建立所有目标和依赖之间的依赖关系结构链表;第二阶段根据第一阶段已经建立的依赖关系结构链表决定哪些目标需要更新,并使用对应的规则来重建这些目标。如果变量和函数在make执行的第一阶段中被展开,则称此展开是“立即(Immediate)”的,此时所有的变量和函数被展开在需要构建的结构链表的对应规则中(此规则在建立链表是需要使用)。其他的展开称之为“延后(Deferred)”的。这些变量和函数不会被“立即”展开,而是直到后续某些规则须要使用时或者在make处理的第二阶段它们才会被展开。变量定义的解析过程为:

IMMEDIATE = DEFERRED
IMMEDIATE ?= DEFERRED
IMMEDIATE := IMMEDIATE
IMMEDIATE += DEFERRED or IMMEDIATE
define IMMEDIATE
       DEFERRED
Endef

  回到刚才的例子:

foo = $(bar)
bar = $(ugh)
ugh := Huh?

all:;echo $(foo)

ugh := Ha!

对于ugh 这个简单展开的变量,是立即(Immediate)展开的,会在make的第一阶段立即展开;而对于foo 这个递归展开的变量,是延后(Deferred)展开的,在make的第二阶段才会展开,此时ugh 已经展开完毕,得到了最终的值Ha! ,所以在echo $(foo) 处,输出foo 的值为Ha! 。

  使用递归展开变量时,需要注意不要出现循环引用,如变量a引用自身(如进行自加操作),或者变量a引用变量b、而b又引用a,这会导致make对变量进行无限展开而发生错误。

  现在问题来了,对于下面的例子:

foo = $(bar)
bar = $(ugh)
ugh = Huh?

all:;echo $(foo)

ugh = Ha!

echo $(foo) 将输出什么值?注意这里ugh 是递归展开变量。【答案:Ha!

 

  更多说明可以查看GNU的相关文档:

http://www.gnu.org/software/make/manual/make.html

  其中关于变量的部分在:

http://www.gnu.org/software/make/manual/make.html#Reading

http://www.gnu.org/software/make/manual/make.html#Setting

http://www.gnu.org/software/make/manual/make.html#Flavors

等处。