取决于make中的目录

时间:2010-12-14 15:02:53

标签: makefile rules gnu-make

这是我之前问题的后续内容:SO 4403861因为建议的解决方案打破了依赖关系,使makefile无用。我无法弄清楚原因。

我正在使用gnu make 3.82 我有一个规则,如果已创建obj目录,该规则有效:

objdir:=../obj
$(objdir)/%.o: %.C
    $(COMPILE) -MM -MT$(objdir)/$(notdir $@) $< -o $(DEPDIR)/$(notdir $(basename $<).d )
    $(COMPILE) -o $(objdir)/$(notdir $@ ) -c $<

但是,如果没有obj目录,则make失败。我希望make能够根据需要自动创建../obj,所以我添加了我认为非常简单的内容:

$(objdir)/%.o: %.C $(objdir)
    $(COMPILE) -MM -MT$(objdir)/$(notdir $@) $< -o $(DEPDIR)/$(notdir $(basename $<).d )
    $(COMPILE) -o $(objdir)/$(notdir $@ ) -c $<

$(objdir):
   if [ ! -d $(objdir) ] ; then mkdir $(objdir) ; fi

当我这样做时,每次都要强制编译。为什么?除非没有目录,否则不应该发生mkdir?为什么这种简单的改变会破坏依赖性呢?

4 个答案:

答案 0 :(得分:23)

您也可以尝试使用仅订购先决条件link

您的问题有一个类似的例子。

 OBJDIR := objdir
 OBJS := $(addprefix $(OBJDIR)/,foo.o bar.o baz.o)

 $(OBJDIR)/%.o : %.c
         $(COMPILE.c) $(OUTPUT_OPTION) $<

 all: $(OBJS)

 $(OBJS): | $(OBJDIR)

 $(OBJDIR):
         mkdir $(OBJDIR)

答案 1 :(得分:11)

正如其他人所说的那样,问题是每当添加或删除目录成员时,目录都会被视为“已更改”,因此make会看到您的输出目录一直在变化,并重新运行您告诉它的所有编译依赖于输出目录。

作为其他人描述的变通方法的替代方案,最近的GNU make版本支持“仅限订单的先决条件”。 The description of order-only prerequisites in the manual包含一个如何使用它们创建目录的示例。

答案 2 :(得分:4)

某种类型的sentinel 文件可以很好地解决这个问题。允许您编写单个模式规则来创建所有文件夹,没有任何恶意重新编译问题,并且-j是安全的。

OBJDIR := objdir/
OBJS := $(addprefix ${OBJDIR},foo.o bar.o baz.o)

all: ${OBJS}

${OBJDIR}%.o : %.c ${OBJDIR}.sentinel
     ${COMPILE.c} ${OUTPUT_OPTION} $<

# A single pattern rule will create all appropriate folders as required
.PRECIOUS: %/.sentinel # otherwise make (annoyingly) deletes it
%/.sentinel:
     mkdir -p ${@D}
     touch $@

答案 3 :(得分:3)

如前所述,您不能将包含输出文件的目录作为依赖项,因为它每次都会更新。

一个解决方案(如上所述)是在每次构建之前放置一个mkdir -p,如下所示:

objdir:=../obj
$(objdir)/%.o: %.C
    @mkdir -p $(objdir) $(DEPDIR) ...
    $(COMPILE) -MM -MT$(objdir)/$(notdir $@) $< -o $(DEPDIR)/$(notdir $(basename $<).d )
    $(COMPILE) -o $(objdir)/$(notdir $@ ) -c $<

但是我不喜欢这个解决方案,因为它会强制运行额外的过程很多次。但是,知道错误的原因,还有另一种方法:

exe: dirs $(OBJ)

在主要目标中,构建可执行文件的目标,只需在对象之前插入目标目录。因为它按顺序命中:

dirs:
    @mkdir -p $(objdir) $(DESTDIR) ...

这只对整个构建执行一次,而不是每个文件执行一次。