Makefile中的双冒号规则是什么?

lin*_*lof 31 makefile gnu-make

GNU Make手册的4.13节描述了所谓的双冒号规则:

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

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

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

目标的双冒号规则按它们在makefile中出现的顺序执行.但是,双冒号规则确实有意义的情况是执行命令的顺序无关紧要的情况.

双冒号规则有点模糊,通常不太有用; 它们为用于更新目标的方法根据导致更新的先决条件文件而不同的情况提供了一种机制,这种情况很少见.

每个双冒号规则应指定命令; 如果没有,则使用隐式规则(如果适用).请参阅使用隐式规则部分.

我有点单独理解本节每个句子的含义,但我仍然不清楚双冒号规则什么.至于是罕见的,我还没有看到任何的开源项目,其Makefile文件并没有开头

all::
Run Code Online (Sandbox Code Playgroud)

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

Wil*_*per 16

每个::规则都是独立处理的,因此它可以更简单.例如,单个规则:

libxxx.a : sub1.o sub2.o
    ar rv libxxx.a sub1.o
    ar rv libxxx.a sub2.o
Run Code Online (Sandbox Code Playgroud)

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

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

libxxx.a :: sub2.o
    ar rv libxxx.a sub2.o
Run Code Online (Sandbox Code Playgroud)

像AutoMake这样的工具可以更容易地分出许多简单的规则,而不是几个复杂的规则.

发布了更多示例的一个很好的答案,然后删除,然后在这里找到:

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

感谢RK Owen的写作,以及Edward Minnix再次找到它!

  • 如果 makefile 实际上是由人编写的,同时查看所有任务并综合一个模式,那么您的规则就没有问题。要点是 Automake 一次将一个依赖项和 clean:: 规则附加到 makefile 中。事先可能无法知道 Automake 运行生成的所有 mainx 依赖项是否符合您所描述的简洁模式。在运行时,Automake 可以使用常规 : 语法将附加依赖项添加到 mainx 上,但如果没有 ::,它就无法将操作附加到 clean 中。 (3认同)

zco*_*tin 7

有三种情况,双冒号是有用的:

  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;
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

结果:

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
Run Code Online (Sandbox Code Playgroud)
  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
Run Code Online (Sandbox Code Playgroud)

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

c@desk:~/test/circle1$ make a.conf
make: *** No rule to make target 'a.conf'.  Stop.
Run Code Online (Sandbox Code Playgroud)

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

c@desk:~/test/circle1$ make a.config
a.cfg from a.cfg1
a.config from a.cfg
rm a.cfg
Run Code Online (Sandbox Code Playgroud)
  1. 制定一个始终执行的规则(对于干净的规则很有用).规则必须没有先决条件!

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

CP=/bin/cp  
a.config::  
    @echo "Always" >> $@  

a.config::  
    @echo "Always!" >> $@  

clean:  
    -$(RM) *.config  
Run Code Online (Sandbox Code Playgroud)

结果:

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!
Run Code Online (Sandbox Code Playgroud)


Max*_*kin 5

它们非常适合非递归的makefile和目标clean.也就是说,单个.mk文件可以将自己的命令添加到clean已在别处定义的目标.

文档给出了答案:

双冒号规则有点模糊,通常不太有用; 它们为用于更新目标的方法根据导致更新的先决条件文件而不同的情况提供了一种机制,这种情况很少见.


thi*_*ton 5

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

假设您有一个日志文件L,它与其他几个日志文件L1,L2,....连接起来.您可以制定一些双冒号规则,例如:

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

L :: L2
     cat $< >> $@ && rm $<
Run Code Online (Sandbox Code Playgroud)

现在在GNU make中,你当然会使用$^这种魔法,但它被列为GNU make的功能选项卡上的灵感功能.