代码之家  ›  专栏  ›  技术社区  ›  Daniel Engel

为什么我不能在Makefile先决条件中使用$(@D)?

  •  0
  • Daniel Engel  · 技术社区  · 1 年前

    当我编译C文件并将生成的对象文件输出到它们自己的目录中时,我需要确保这个目录存在。 我看到了两种方法:

    1. 始终创建当前目标的目录:
      obj/%.o: src/%.c
          @mkdir -p $(@D)
          $(CC) ...
      
    2. 添加 obj/ 目录作为先决条件
      obj/%.o: src/%.c | obj/
          $(CC) ...
      
      obj/:
          mkdir -p $@
      

    从…起 1. 我喜欢它使用 $(@D) 为了避免重复并使其更加健壮 2. 我喜欢这样 mkdir 只被调用过一次,我觉得它在概念上更有意义。

    当我试着像这样把两者结合起来的时候

    obj/%.o: src/%.c | $(@D)
        $(CC) ...
    
    obj/:
        mkdir -p $@
    

    那么编译器会抱怨 obj/ 目录不存在。


    对于任何好奇这个特定用例的人: MadScientist提到了二次扩展,所以我使用了以下内容

    .SECONDEXPANSION
    obj/%.o: src/%.c: $$(@D)
        $(CC) ...
    
    obj:
        mkdir -p $@
    

    这似乎有效,但它有一些拖尾问题 / ,所以我暂时删除了它们。

    1 回复  |  直到 1 年前
        1
  •  1
  •   MadScientist    1 年前

    因为make就是这样工作的;从…起 the manual :

    非常重要的是,您要认识到自动变量值可用的范围有限:它们只在配方中有值。特别是,您不能在规则的目标列表中的任何位置使用它们;它们在那里没有值,将扩展到空字符串。此外,不能在规则的先决条件列表中直接访问它们。一个常见的错误是试图在先决条件列表中使用$@;这是行不通的。但是,GNU make有一个特殊功能,即二次扩展(请参阅 Secondary Expansion ),这将允许在先决条件列表中使用自动变量值。

    如果你在问, 为什么? 这样做是因为make在解析makefile时总是扩展目标和先决条件,而不是在匹配规则时。否则,这样的事情就无法工作:

    OBJ = foo
    
    $(OBJ)/%.o : %.c | $(OBJ) ;
    
    OBJ = bar
    
    $(OBJ)/%.o : %.cpp | $(OBJ) ;
    

    试图对某些变量或函数进行特殊处理,使其立即扩展,而某些则推迟到以后扩展,这将是一场混乱,甚至更难解释和理解。当然,在规则真正匹配之前,我们不知道是什么 $(@D) 应该是,因为 % 可以包含目录。

    上面的手册摘录中给出的链接解释了如何使用 二次膨胀 以提供某种形式的这种行为。