【问题标题】:depending on directories in make [duplicate]取决于make中的目录[重复]
【发布时间】:2011-05-25 08:09:18
【问题描述】:

这是我之前的问题的后续问题: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 不应该发生?为什么这个简单的改变会破坏依赖关系?

【问题讨论】:

  • 正如我在回复您的问题/对我在 4403861 上的回答的评论所评论的那样:“您的规则说对象文件取决于对象目录 - 这意味着如果对象目录已更改由于目标文件是最后构建的(例如,因为您编译了另一个文件),那么您的文件需要重新构建。您的整体构建目标取决于存在的目标目录;单个目标文件不需要仅仅因为目录更改而重新构建。”

标签: makefile rules gnu-make


【解决方案1】:

正如已经回答的那样,您不能将带有输出文件的目录作为依赖项,因为它每次都会更新。

一种解决方案(如上所述)是在每次构建之前放置一个 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) ...

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

【讨论】:

  • 这是一个鲜为人知的烦恼,但您不能在并行制作中使用mkdir -p。原因是如果文件夹 /a/b/c 和 /a/b/cc 被并行创建,则在 mkdir -p 检查 /a 目录是否存在然后创建它之间存在竞争条件。另一个进程(make 作业)可以在检查和操作之间创建目录,因此第一个作业在创建目录时失败并显示 EEXIST。
  • 并且对目录的依赖必须是 order-only,正如他们已经提到的那样。
  • 如前所述,此解决方案在-j 下被破坏。你可以使用mkdir -p,但你必须得到正确的依赖。基本上,您必须使所有 objects 依赖于目录。此外,要停止由于 dir 更改时间而重新编译,请使用哨兵文件(和模式规则)。见下文(我无法在此框中输入解决方案,grrrr。)。
  • 根据您发布的内容,无法保证 dirs 目标将在 $(OBJ) 之前处理或者它们不会同时发生(所以 dirs将被创建但 $(OBJ) 失败,因为它不存在)。
【解决方案2】:

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

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

【讨论】:

  • 我不认为这是真的。当添加、删除或重命名目录中的任何成员时,目录就会发生变化。 (目录是包含文件名到文件的映射的文件。)
  • 我在这里解释一下:stackoverflow.com/questions/3477418/…
  • reinierpost:谢谢,更正了答案。
【解决方案3】:

你也可以试试Order-only prerequisites

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

 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)

【讨论】:

    【解决方案4】:

    某种哨兵文件会很好地解决这个问题。允许您编写单个模式规则来创建所有文件夹,没有任何恶意重新编译问题,并且是-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 $@
    

    【讨论】:

      猜你喜欢
      • 2012-12-13
      • 1970-01-01
      • 2012-10-03
      • 2013-07-03
      • 1970-01-01
      • 2016-08-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多