1 $$的用处
在uboot/kernel的顶层Makefile中我们可以看到,当执行make时同时传入多个目标是这样处理的:
__build_one_by_one:
$(Q)set -e; \
for i in $(MAKECMDGOALS); do \
$(MAKE) -f $(srctree)/Makefile $$i; \
done
其中的$$i
比较奇怪,Makefile引用变量只需要一个$符号就够了,为什么这里要两个呢?因为变量i是在shell的for循环中定义的,是属于shell中的变量。make在读到$$i
的时候会把它扩展成$i
(有点转义字符的意味),然后交给shell解释程序执行。而如果读到$i
,由于变量i
没有在Makefile中定义,因此就会被扩展成空。要知道make会把Makefile规则中的命令部分交给shell执行,而它自己只对其做简单的处理,比如展开被引用的变量。换句话说,make根本不认识for i in
这样的语句,只知道要它们交给shell,因此在make看来,$i
中的i
是没有定义的。
通过上面这个例子,不难看出,$$
的用处可以概括为阻止一次Makefile的变量扩展,或者说施加了$$
的变量,需要两次展开,才能得到变量的值。第二次展开,可能是在shell中进行的,正如上例;也可能是在Makefile中,并且被阻止的动作除了变量扩展还可以是函数执行,下面就举个例子:
OBJS_LIST := aa bb cc dd ee
define test
DEP_OBJS_$(1) = $$(foreach l,$$(OBJS_LIST),$(1)_$$(l).o)
endef
all:
@echo $(DEP_OBJS_TTT)
CREATE_OBJS := TTT
$(foreach objs,$(CREATE_OBJS),$(eval $(call test,$(objs))))
本例在多行变量test
中使用了$$
,在make读取Makefile时,当读取到最下面的foreach
时,会去call
多行变量,也就是使用参数TTT
替换$(1)
,同时将$$
替换为$
,进而得到变量DEP_OBJS_TTT
:
DEP_OBJS_TTT = $(foreach l,$(OBJS_LIST),TTT_$(l).o)
执行make all,make处理@echo $(DEP_OBJS_TTT)
,对DEP_OBJS_TTT
进行展开,执行其中的foreach
,得到结果如下:
TTT_aa.o TTT_bb.o TTT_cc.o TTT_dd.o TTT_ee.o
不难看出此处$$
的作用是阻止了call
多行变量的时候就执行其中的foreach
。但在本例中不用$$
也行,也能得到同样的结果。将本例略作改变,将OBJS_LIST
的定义调整到call
多行变量的后面,此时如果不使用$$
就得不到预期的结果了:
define test
DEP_OBJS_$(1) = $(foreach l,$(OBJS_LIST),$(1)_$(l).o)
endef
all:
@echo $(DEP_OBJS_TTT)
CREATE_OBJS := TTT
$(foreach objs,$(CREATE_OBJS),$(eval $(call test,$(objs))))
OBJS_LIST := aa bb cc dd ee
这是因为当make在call
多行变量时,OBJS_LIST
还未定义,若立即执行多行变量中的foreach
,那么$(OBJS_LIST)
为空。
不妨再对这个例子做个修改:
define test
# 将'='修改为':='
DEP_OBJS_$(1) := $$(foreach l,$$(OBJS_LIST),$(1)_$$(l).o)
endef
all:
@echo $(DEP_OBJS_TTT)
CREATE_OBJS := TTT
$(foreach objs,$(CREATE_OBJS),$(eval $(call test,$(objs))))
OBJS_LIST := aa bb cc dd ee
这时,即便使用$$
也不行了,因为:=
会导致直接展开引用的变量。
2 二次展开(SECONDEXPANSION)
通过上文,我们已经知道了$$
需要两次展开才能够解引用变量或者执行函数。除此之外,make还提供了针对依赖的二次展开特性,需要使用特殊目标.SECONDEXPANSION
,在这个特殊目标之后定义的依赖,都可以进行二次展开,当然,这仍需要借助$$
的转义特性。举个例子:
.SECONDEXPANSION:
VAR = first
one: $(VAR)
two: $$(VAR)
VAR = second
first:
@echo "this is first"
second:
@echo "this is second"
one:
@echo $^
two:
@echo $^
执行make one将得到:
this is first
first
执行make two将得到:
this is second
second
需要注意的是,如果不加特殊目标,那么执行make two时会报错(依赖的二次展开特性没有使能):
make: *** No rule to make target ‘$(VAR)’, needed by ‘two’. Stop.
变量、函数以及依赖的二次展开在Makefile中可以实现很多有用的功能,无法尽说,本文着重介绍机制,用法的举例或许不是非常合适,更多用法可以搜索相关资料或实际的Makefile工程获取。