14

我有以下简化的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

我假设变量在我认为应该扩展时没有被扩展,但我不知道如何改变这种行为。

4

2 回答 2

17

来自 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

没有在每个目标的上下文中读取规则、替换目标特定变量并为每个目标获取新规则的功能。不能使用特定于目标的变量以这种方式编写通用规则。

于 2013-10-10T00:31:37.310 回答
4

处理的好方法

%/obj.o: | %
    touch $@

OBJDIRS = objdir32 wrongdirectory anotherdirectory
$(OBJDIRS):
    mkdir $@

是:

%/.:
    mkdir -p $@

.SECONDEXPANSION:

然后稍后您可以为任何可能需要目录的目标编写

target: prerequisites | $$(@D)/.
    normal recipe for target
于 2013-10-11T18:25:10.827 回答