为什么这个makefile目标特定变量没有按预期展开?
我有以下简化的makefile,我正试图根据不同的目标设置不同的路径。不幸的是,我没有得到我期望的结果。这是make版本3.81为什么这个makefile目标特定变量没有按预期展开?,makefile,gnu-make,Makefile,Gnu Make,我有以下简化的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.
.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=errowdirectory”行,我将得到以下结果,这是令人困惑的,因为我看到了变量的两个值,而我认为我应该只看到一个:
echo mkdir objdir32 - wrongdirectory -
echo Compile: wrongdirectory/DirUtil.o
我假设变量没有在我认为应该扩展的时候被扩展,但是我不知道如何改变这种行为。来自GNU信息手册 生成文件所有部分中的变量和函数在 阅读,除了在食谱中,变量的右侧 使用“=”的定义,以及使用 “定义”指令 目标特定变量仅适用于配方。内
$(OBJS): | $(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: | %
然后运行makeanotherdirectory/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
没有在每个目标的上下文中读取规则、替换特定于目标的变量并为每个目标获取新规则的功能。通用规则不能使用特定于目标的变量以这种方式编写。这是一种很好的处理方法
%/obj.o: | %
touch $@
OBJDIRS = objdir32 wrongdirectory anotherdirectory
$(OBJDIRS):
mkdir $@
是:
然后,您可以为可能需要目录的任何目标编写
target: prerequisites | $$(@D)/.
normal recipe for target
这是可行的,但似乎我必须两次指定对象目录,两次指定每个.o文件,包括每个源文件的模式规则。我这样做完全错了吗?为两个或多个目标创建一个简单的通用makefile似乎太难了。我添加了一种覆盖所有对象文件的方法。我希望不列出它们(比如
%.o:$$(basename%).cpp |$$(dir$$@)
)是可能的,但我还没有让它工作。我已经添加了一个更易于使用的版本和关于必须列出目录的注释。不过,马克·加莱克(MarkGaleck)构建目录的方法看起来可能更好。
%/.:
mkdir -p $@
.SECONDEXPANSION:
target: prerequisites | $$(@D)/.
normal recipe for target