来自GNU info手册
在读取时,除了配方、使用'='定义的变量右侧和使用'define'命令定义的变量主体之外,makefile所有部分中的变量和函数都会被展开。
目标特定变量仅适用于配方内。在配方之外,它不起作用。
$(OBJS): | $(OBJDIR)
和
$(OBJDIR):
它正在获取全局变量。
因此,当您运行 make Debug32
时,它将OBJS
内容视为先决条件,这将导致第一条规则。 $(OBJDIR)
已经被替换为全局值,并且这与第二条规则中的目标名称匹配,该目标名称也已以同样的方式被替换。
但是,当我们到达配方部分时:
echo mkdir $(OBJDIR) - $@
$(OBJDIR)
尚未被替换,因此它获取了特定于目标的变量值。
一个可用的版本
.SECONDEXPANSION:
all: Debug32
Debug32: OBJDIR = objdir32
OBJDIR = wrongdirectory
Debug32: OBJS = $(addprefix $(OBJDIR)/,obj.o)
OBJS = wrongobjs
Debug32: $$(OBJS)
echo OBJS are $(OBJS)
echo OBJDIR is $(OBJDIR)
%/obj.o: | %
touch $@
OBJDIRS = objdir32 wrongdirectory anotherdirectory
$(OBJDIRS):
mkdir $@
主要的变化是在这行中使用了$$
:
Debug32: $$(OBJS)
只有一个 $
,我就会收到错误信息
make: *** No rule to make target `wrongobjs', needed by `Debug32'. Stop.
但是,使用$$
,我得到了
echo OBJS are objdir32/obj.o
OBJS are objdir32/obj.o
echo OBJDIR is objdir32
OBJDIR is objdir32
使用二次展开技术可以在前提条件中访问目标特定变量。
另一个更改是将
OBJS
变量指定为特定目标变量(因为它就是)。为了建立规则来构建无论
OBJS
的值如何,我必须使用模式规则。
%/obj.o: | %
为了避免每个目标文件都有单独的行,您可以改为执行以下操作:
OBJ_BASENAMES=obj.o obj2.o obj3.o
$(addprefix %/,$(OBJ_BASENAMES)): | %
touch $@
addprefix
宏所在的那一行扩展为:
%/obj.o %/obj2.o %/obj3.o: | %
运行make anotherdirectory/obj2.o
会首先创建一个名为"anotherdirectory"的目录,并在其中创建一个名为"obj2.o"的文件。
注意,所有可能的目录都必须列在 OBJDIRS
中。没有办法收集所有规则特定的 OBJDIR 值,因此列出它们是最好的选择。另一种选择是使用 %:
规则来构建任何目录,这可能存在风险。 (如果放弃使用目标特定变量,则还有另一种方法可以获取可构建目录的列表:使用具有可预测名称的变量,例如 Debug32_OBJDIR
,并使用 make 函数生成其值的列表。)
或者,可以使用不需要列出对象文件的通用规则:
SOURCE=$(basename $(notdir $@)).cpp
DIR=$(patsubst %/,%,$(dir $@))
%.o: $$(SOURCE) | $$(DIR)
touch $@
没有功能可以在每个目标的上下文中读取规则,用目标特定变量替换并为每个目标获取新规则。使用目标特定变量无法以这种方式编写通用规则。
%.o: $$(basename %).cpp | $$(dir $$@)
),但我还没有让它正常工作。 - Gavin Smith