我有以下简化的makefile,我正在尝试根据不同的目标设置不同的路径。不幸的是,我没有得到我期望的结果。这与make版本3.81。
.SECONDEXPANSION:
all: Debug32
# Object directory set by target
Debug32: OBJDIR = objdir32
#OBJDIR = wrongdirectory
# ObjDir is empty here. :(
OBJS = $(addprefix $(OBJDIR)/,DirUtil.o)
$(OBJDIR)/%.o : %.cpp
echo Compile: $@
Debug32: $(OBJS)
$(OBJS): | $(OBJDIR)
$(OBJDIR):
echo mkdir $(OBJDIR) - $@
结果如下,没有设置OBJDIR:
echo Compile: /DirUtil.o
如果我取消注释“OBJDIR = wrongdirectory”行,我会得到以下结果,这是令人困惑的,因为我看到变量的两个值,我认为我应该只看到一个:
echo mkdir objdir32 - wrongdirectory -
echo Compile: wrongdirectory/DirUtil.o
我认为变量在我认为应该扩展时没有扩展,但我无法弄清楚如何改变这种行为。
答案 0 :(得分:13)
来自GNU信息手册
makefile的所有部分中的变量和函数在扩展时都会扩展 阅读,除了食谱,变量的右侧 使用'='的定义,以及使用的变量定义的主体 'define'指令。
特定于目标的变量仅适用于配方。内
$(OBJS): | $(OBJDIR)
和
$(OBJDIR):
它正在获取全局变量。
因此,当您运行make Debug32
时会发生什么,它会将OBJS
的内容视为先决条件,从而导致上面的第一条规则。 $(OBJDIR)
已被全局值替换,这与第二个规则中的target-name匹配,该规则也以相同的方式替换。
然而,当我们到达食谱时:
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
没有功能可以在每个目标的上下文中读取规则,替换目标特定变量并为每个目标获取新规则。使用特定于目标的变量不能以这种方式编写通用规则。
答案 1 :(得分:3)
处理
的好方法%/obj.o: | %
touch $@
OBJDIRS = objdir32 wrongdirectory anotherdirectory
$(OBJDIRS):
mkdir $@
是:
%/.:
mkdir -p $@
.SECONDEXPANSION:
然后您可以为任何可能需要目录的目标编写
target: prerequisites | $$(@D)/.
normal recipe for target