来自 GNU 信息手册
makefile 的所有部分中的变量和函数在读取时都会被扩展,除了在配方中,使用'='的变量定义的右侧,以及使用'define'指令的变量定义的主体。
特定于目标的变量仅适用于配方。内
$(OBJS): | $(OBJDIR)
和
$(OBJDIR):
它正在获取全局变量。
因此,通过运行时发生的事情make Debug32
,它将 的内容OBJS
视为先决条件,这导致了上面的第一条规则。$(OBJDIR)
已经被全局值替换,并且这与第二条规则中的目标名称匹配,该规则也以相同的方式替换。
然而,当我们得到食谱时:
echo mkdir $(OBJDIR) - $@
$(OBJDIR)
尚未被替换,因此它获取特定于目标的变量值。
一个工作版本
.SECONDEXPANSION:
all: Debug32
# Object directory set by target
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 $(OBJDIR)
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 $@ # Replace with the proper recipe
包含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 $@ # Replace with proper recipe
没有在每个目标的上下文中读取规则、替换目标特定变量并为每个目标获取新规则的功能。不能使用特定于目标的变量以这种方式编写通用规则。