为什么这个makefile目标特定变量没有按预期扩展?

时间:2013-10-10 00:08:34

标签: 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.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

我认为变量在我认为应该扩展时没有扩展,但我无法弄清楚如何改变这种行为。

2 个答案:

答案 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