【问题标题】:What are double-colon rules in a Makefile for?Makefile 中的双冒号规则有什么用?
【发布时间】:2011-12-15 00:30:03
【问题描述】:

4.13 of the GNU Make manual 部分描述了所谓的双冒号规则:

双冒号规则是在目标名称后用“::”而不是“:”编写的规则。当同一目标出现在多个规则中时,它们的处理方式与普通规则不同。

当一个目标出现在多个规则中时,所有规则必须是同一类型:都是普通的,或者都是双冒号。如果它们是双冒号,则它们中的每一个都是独立于其他的。如果目标早于该规则的任何先决条件,则执行每个双冒号规则的命令。如果该规则没有先决条件,则始终执行其命令(即使目标已存在)。这可能导致不执行、不执行任何或所有双冒号规则。

具有相同目标的双冒号规则实际上是完全分开的。每个双冒号规则都是单独处理的,就像处理具有不同目标的规则一样。

目标的双冒号规则按照它们在 makefile 中出现的顺序执行。然而,双冒号规则真正有意义的情况是那些执行命令的顺序无关紧要的情况。

双冒号规则有些晦涩,通常不是很有用;它们为用于更新目标的方法不同的情况提供了一种机制,具体取决于导致更新的先决条件文件,这种情况很少见。

每个双冒号规则都应该指定命令;如果不适用,则将使用隐含规则(如果适用)。请参阅使用隐式规则部分。

我有点理解本节中每个句子的含义,但我仍然不清楚双冒号规则 的用途。至于稀有,我还没有看到任何 Makefile 不是以

开头的开源项目
all::

因此:Makefile 中双冒号规则的预期目的是什么?

【问题讨论】:

  • 预先设置all:: 的一个原因:它使all 成为默认目标。另一种方法是预先使用.DEFAULT_GOAL = all,但这需要使用make version 3.81 的系统。有很多 Unix 计算机的 make 要么是 GNU make 早于 3.81,要么是其他支持双冒号规则的 make。

标签: makefile gnu-make


【解决方案1】:

它们对于像clean 这样的非递归makefile 和目标很方便。也就是说,单个 .mk 文件可以将自己的命令添加到已在别处定义的 clean 目标。

Documentation给出答案:

双冒号规则有些晦涩难懂,通常不是很有用; 它们为用于更新目标的方法不同的情况提供了一种机制,具体取决于导致更新的先决条件文件,这种情况很少见。

【讨论】:

  • 一方面,my-clean 会取一个名字,并且必须设为 .PHONY,除非使用双冒号规则。更新了答案。
  • “将自己的命令添加到其他地方定义的[目标]”的参考(和/或示例)会很好。此外,文档链接只是其中一种情况。
【解决方案2】:

正如文档所说,双冒号规则很少有用。它们是一种很好的、​​不命名复合虚假目标的单个目标的小方法(就像 all::),但在这个角色中并不是真正必要的。我只能在必要时形成一个人为的例子:

假设您有一个日志文件 L,它与其他几个日志文件 L1、L2、...连接在一起。您制定了许多双冒号规则,例如:

L :: L1
     cat $< >> $@ && rm $<

L :: L2
     cat $< >> $@ && rm $<

现在在 GNU make 中,您当然会使用 $^ 来实现这种魔法,但它在 GNU make 的功能选项卡上被列为受启发的功能。

【讨论】:

    【解决方案3】:

    每个 :: 规则都是独立处理的,因此可以更简单。比如单条规则:

    libxxx.a : sub1.o sub2.o
        ar rv libxxx.a sub1.o
        ar rv libxxx.a sub2.o
    

    可以用两个更简单的规则代替:

    libxxx.a :: sub1.o
        ar rv libxxx.a sub1.o
    
    libxxx.a :: sub2.o
        ar rv libxxx.a sub2.o
    

    像 AutoMake 这样的实用程序比一些复杂的规则更容易生成许多简单的规则。

    发布了一个带有更多示例的好答案,然后将其删除,然后在此处找到:

    https://web.archive.org/web/20180122002430/http://owen.sj.ca.us/~rk/howto/slides/make/slides/makecolon.html

    感谢 R.K.欧文写下它,爱德华·明尼克斯再次找到它!

    【讨论】:

    • 谢谢!我正在将您的链接添加到我的评论中。
    • 我不确定我是否理解来自 Wayback 机器的那个例子......为什么不简单地使用prereqs := $(shell for file in *.c; do printf "%s " ${file/.c/.o}; done 并调用mainx : $(prereqs) 并为干净做-$(RM) $(prereqs)?
    • 如果 makefile 实际上是由人类编写的,同时查看所有任务并合成一个模式,那么您的规则就可以了。关键是 Automake 一次将依赖项和 clean:: 规则附加到 makefile 中。可能无法提前知道运行 Automake 生成的所有 mainx 依赖项是否符合您描述的简洁模式。在运行时,Automake 可以使用常规的 : 语法将其他依赖项附加到 mainx 上,但如果没有 :: 它就无法将操作附加到清理。
    【解决方案4】:

    双冒号有3种用处:

    1. 根据哪个先决条件比目标更新,在编译规则之间交替。以下示例基于http://books.gigatux.nl/mirror/cinanutshell/0596006977/cinanut-CHP-19-SECT-3.html 中的“示例 19-3. 双冒号规则”

    示例 .c 文件:

    c@desk:~/test/circle$ cat circle.c 
    #include <stdio.h>
    
    int main (void)
    {
      printf("Example.\n");
      return 0;
    }
    

    使用的Makefile:

    c@desk:~/test/circle$ cat Makefile 
    # A makefile for "circle" to demonstrate double-colon rules.
    
    CC = gcc
    RM = rm -f
    CFLAGS = -Wall -std=c99
    DBGFLAGS = -ggdb -pg
    DEBUGFILE = ./debug
    SRC = circle.c
    
    circle :: $(SRC)
            $(CC) $(CFLAGS) -o $@ -lm $^
    
    circle :: $(DEBUGFILE)
            $(CC) $(CFLAGS) $(DBGFLAGS) -o $@ -lm $(SRC)
    
    .PHONY : clean
    
    clean  :
            $(RM) circle
    

    结果:

    c@desk:~/test/circle$ make circle
    gcc -Wall -std=c99 -o circle -lm circle.c
    make: *** No rule to make target 'debug', needed by 'circle'.  Stop.
    c@desk:~/test/circle$ make circle
    gcc -Wall -std=c99 -o circle -lm circle.c
    gcc -Wall -std=c99 -ggdb -pg -o circle -lm circle.c
    c@desk:~/test/circle$ vim circle.c 
    c@desk:~/test/circle$ make circle
    gcc -Wall -std=c99 -o circle -lm circle.c
    c@desk:~/test/circle$ vim debug 
    c@desk:~/test/circle$ make circle
    gcc -Wall -std=c99 -ggdb -pg -o circle -lm circle.c
    
    1. 制作模式规则终端。

    下面的例子说明了这种情况:a.config文件是从a.cfg获取的,而a.cfg又是从a.cfg1获取的(a.cfg是中间文件)。

    c@desk:~/test/circle1$ ls
    a.cfg1  log.txt  Makefile
    c@desk:~/test/circle1$ cat Makefile 
    CP=/bin/cp
    
    %.config:: %.cfg
            @echo "$@ from $<"
            @$(CP) $< $@
    
    %.cfg: %.cfg1
            @echo "$@ from $<"
            @$(CP) $< $@
    
    clean:
            -$(RM) *.config
    

    结果(由于 %.config 规则是终端,make 禁止从 a.cfg1 创建中间 a.cfg 文件):

    c@desk:~/test/circle1$ make a.conf
    make: *** No rule to make target 'a.conf'.  Stop.
    

    %.config 没有双冒号,结果是:

    c@desk:~/test/circle1$ make a.config
    a.cfg from a.cfg1
    a.config from a.cfg
    rm a.cfg
    
    1. 制定始终执行的规则(对干净的规则很有用)。规则不能有先决条件!

    c@desk:~/test/circle3$ cat Makefile

    CP=/bin/cp  
    a.config::  
        @echo "Always" >> $@  
    
    a.config::  
        @echo "Always!" >> $@  
    
    clean:  
        -$(RM) *.config  
    

    结果:

    c@desk:~/test/circle3$ make a.config
    c@desk:~/test/circle3$ cat a.config 
    Always
    Always!
    c@desk:~/test/circle3$ make a.config
    c@desk:~/test/circle3$ cat a.config
    Always
    Always!
    Always
    Always!
    

    【讨论】:

    • 唯一一个进入令人讨厌的黑暗角落的人。但是官方参考会很好!
    • 缺乏官方文档是这个主题被认为是“令人讨厌的黑暗角落”的原因。
    • 不,不是。无处解释的功能也可能是错误(可能会产生意想不到的副作用或在下一个版本中消失)。例如,您的案例 2 是 在手册中。案例 3 的解释非常混乱,我不确定你是否将它与 .PHONY 目标混合,这就是我问的原因。
    • ... 并且案例 3 在手册中也是也是。所以,再一次,在这些黑暗的角落提供参考资料会特别好。
    • @hmijail,我喜欢您的 cmets,但您也可以提供这些链接,如果您仍然查找它们,对吧?... ;)
    【解决方案5】:

    我将提供一个简单的示例,希望能清楚地说明用法:

    使用以下 makefile 进行实验:

      a.faux:: dep1.fake
          $(info run a dep1.fake)
          touch a.faux
    
      a.faux:: dep2.fake
          $(info run a dep2.fake)
          touch a.faux
    
      dep1.fake:
          touch dep1.fake
    
      dep2.fake:
          touch dep2.fake
    

    运行make a.faux,它将导致dep1.fake 和dep2.fake 运行。删除dep1.fake并再次运行make a.faux,只有dep1.fake会运行。

    【讨论】:

    • 你忘了提到在这两种情况下你都运行make a.faux,两个双冒号配方都运行。
    猜你喜欢
    • 1970-01-01
    • 2010-12-03
    • 1970-01-01
    • 2019-09-14
    • 2016-05-16
    • 2011-08-21
    • 2011-03-01
    • 1970-01-01
    相关资源
    最近更新 更多