Makefile 中的双冒号规则有何用途?

发布于 2024-12-11 17:02:05 字数 833 浏览 0 评论 0 原文

GNU Make 手册的 4.13 部分描述了所谓的 double - 冒号规则:

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

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

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

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

双冒号规则有些晦涩难懂,而且通常不是很有用;它们为用于更新目标的方法根据导致更新的必备文件而不同的情况提供了一种机制,但这种情况很少见。

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

我有点单独理解本节每个句子的含义,但我仍然不清楚双冒号规则的用途。至于罕见性,我还没有看到任何开源项目的 Makefile 不是以“

all::

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

Section 4.13 of the GNU Make manual describes the so-called double-colon rules:

Double-colon rules are rules written with ‘::’ instead of ‘:’ after the target names. They are handled differently from ordinary rules when the same target appears in more than one rule.

When a target appears in multiple rules, all the rules must be the same type: all ordinary, or all double-colon. If they are double-colon, each of them is independent of the others. Each double-colon rule's commands are executed if the target is older than any prerequisites of that rule. If there are no prerequisites for that rule, its commands are always executed (even if the target already exists). This can result in executing none, any, or all of the double-colon rules.

Double-colon rules with the same target are in fact completely separate from one another. Each double-colon rule is processed individually, just as rules with different targets are processed.

The double-colon rules for a target are executed in the order they appear in the makefile. However, the cases where double-colon rules really make sense are those where the order of executing the commands would not matter.

Double-colon rules are somewhat obscure and not often very useful; they provide a mechanism for cases in which the method used to update a target differs depending on which prerequisite files caused the update, and such cases are rare.

Each double-colon rule should specify commands; if it does not, an implicit rule will be used if one applies. See section Using Implicit Rules.

I kinda grok the meaning of each sentence of this section individually, but it's still not clear to me what double-colon rules are for. As for being rare, I have not yet seen any open-source project whose Makefile did not begin with

all::

Therefore: What's the intended purpose of double-colon rules in Makefiles?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

帥小哥 2024-12-18 17:02:05

每个::规则都是独立处理的,所以可以更简单。例如,单个规则:

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

感谢 RK Owen 写下它,感谢 Edward Minnix 再次找到它!

Each :: rule is processed independently, so it can be simpler. For example, the single rule:

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

can be replaced with two simpler rules:

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

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

Utilities like AutoMake have an easier time spitting out many simple rules than a few complex ones.

A great answer with more examples was posted, then taken down, then found here:

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

Thanks to R.K. Owen for writing it, and Edward Minnix for finding it again!

安静 2024-12-18 17:02:05

双冒号在 3 种情况下很有用:

  1. 根据先决条件比目标更新的编译规则之间进行交替。以下示例基于 http://books.gigatux.nl/mirror/cinanutshell/0596006977/cinanut-CHP-19-SECT-3.html

示例 .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 
lt;"
        @$(CP) 
lt; $@

%.cfg: %.cfg1
        @echo "$@ from 
lt;"
        @$(CP) 
lt; $@

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!

There are 3 situations where the double colon are useful:

  1. Alternate between the compile rules based on which prerequisite is newer than the target. The following example is based on "Example 19-3. Double-colon rules" from http://books.gigatux.nl/mirror/cinanutshell/0596006977/cinanut-CHP-19-SECT-3.html

Sample .c file:

c@desk:~/test/circle$ cat circle.c 
#include <stdio.h>

int main (void)
{
  printf("Example.\n");
  return 0;
}

Makefile used:

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

Outcome:

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. Make a pattern rule terminal.

The following example explains this situation: the a.config file is obtained from a.cfg, which in turn is obtained from a.cfg1 (a.cfg being the intermediate file).

c@desk:~/test/circle1$ ls
a.cfg1  log.txt  Makefile
c@desk:~/test/circle1$ cat Makefile 
CP=/bin/cp

%.config:: %.cfg
        @echo "$@ from 
lt;"
        @$(CP) 
lt; $@

%.cfg: %.cfg1
        @echo "$@ from 
lt;"
        @$(CP) 
lt; $@

clean:
        -$(RM) *.config

Outcome (as the %.config rule is terminal, make inhibits the creation of the intermediate a.cfg file from a.cfg1):

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

Without the double colon for the %.config, the outcome is:

c@desk:~/test/circle1$ make a.config
a.cfg from a.cfg1
a.config from a.cfg
rm a.cfg
  1. Make a rule that executes always (useful for clean rules). The rule must not have prerequisites!

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

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

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

clean:  
    -$(RM) *.config  

Outcome:

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!
我也只是我 2024-12-18 17:02:05

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

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

L :: L1
     cat 
lt; >> $@ && rm 
lt;

L :: L2
     cat 
lt; >> $@ && rm 
lt;

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

Just as the documentation says, double-colon rules are rarely very useful. They are a nice, little way of not naming the individual targets of a composite phony target (like all::), but not really necessary in this role. I can only form one contrived example where they are necessary:

Suppose you have a logfile L that is concatenated from several other logfiles L1, L2, .... You formulate a number of double-colon rules like:

L :: L1
     cat 
lt; >> $@ && rm 
lt;

L :: L2
     cat 
lt; >> $@ && rm 
lt;

Nowadays in GNU make, you would of course use $^ for this kind of magic, but it is listed as an inspired feature on GNU make's feature tab.

谈情不如逗狗 2024-12-18 17:02:05

它们对于非递归 makefile 和诸如 clean 之类的目标非常方便。也就是说,单个 .mk 文件可以将自己的命令添加到已在其他地方定义的 clean 目标中。

文档给出了答案:

双冒号规则有些晦涩难懂,而且通常不是很有用; 它们为用于更新目标的方法根据导致更新的必备文件而不同的情况提供了一种机制,但这种情况很少见。

They are handy for non-recursive makefiles and targets like clean. That is, an individual .mk file can add its own commands to the clean target already defined elsewhere.

Documentation gives an answer:

Double-colon rules are somewhat obscure and not often very useful; they provide a mechanism for cases in which the method used to update a target differs depending on which prerequisite files caused the update, and such cases are rare.

听风念你 2024-12-18 17:02:05

我将提供一个简单的示例,希望使用法更清楚:

使用以下 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会运行。

I'll contribute a simple example to hopefully make the usage clear:

Experiment with the following 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

Run make a.faux, it will causes dep1.fake and dep2.fake to run. Delete dep1.fake and run make a.faux again, only dep1.fake will run.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文