为什么 gnu make 不能弄清楚这个规则序列?

Why can't gnu make figure out this sequence of rules?

我有这个Makefile:

%: %.x
    cp $< $@

build/%: src/%
    cp $< $@

目录结构如下所示:

Makefile
build/
src/
    hello.x

为什么 make 表现如下:

$ make build/hello
make: *** No rule to make target 'build/hello'.  Stop.

为什么它看不到

  1. 它可以使用第一条规则将src/hello.x翻译成src/hello,并且
  2. 使用第二条规则将 src/hello 复制到 build/hello 中?

根据GNU make manual,即:

If you do not mark the match-anything rule as terminal, then it is non-terminal. A non-terminal match-anything rule cannot apply to a file name that indicates a specific type of data. A file name indicates a specific type of data if some non-match-anything implicit rule target matches it.

您的第一条规则是非终结符匹配任何规则,因此它不能应用于指示特定数据类型的目标 src/hello。带有 make build/hello -d 的调试日志也显示了过程:

......
Considering target file `build/hello'.
 Looking for an implicit rule for `build/hello'.
 Trying pattern rule with stem `hello'.
 Trying implicit prerequisite `src/hello'.
 Trying pattern rule with stem `hello'.
 Trying implicit prerequisite `build/hello,v'.
 Trying pattern rule with stem `hello'.
 Trying implicit prerequisite `build/RCS/hello,v'.
 Trying pattern rule with stem `hello'.
 Trying implicit prerequisite `build/RCS/hello'.
 Trying pattern rule with stem `hello'.
 Trying implicit prerequisite `build/s.hello'.
 Trying pattern rule with stem `hello'.
 Trying implicit prerequisite `build/SCCS/s.hello'.
 Trying pattern rule with stem `hello'.
 Trying implicit prerequisite `src/hello'.
 Looking for a rule with intermediate file `src/hello'.
  Avoiding implicit rule recursion.
  Trying pattern rule with stem `hello'.
  Trying implicit prerequisite `src/hello,v'.
  Trying pattern rule with stem `hello'.
  Trying implicit prerequisite `src/RCS/hello,v'.
  Trying pattern rule with stem `hello'.
  Trying implicit prerequisite `src/RCS/hello'.
  Trying pattern rule with stem `hello'.
  Trying implicit prerequisite `src/s.hello'.
  Trying pattern rule with stem `hello'.
  Trying implicit prerequisite `src/SCCS/s.hello'.
 No implicit rule found for `build/hello'.
 Finished prerequisites of target file `build/hello'.
No need to remake target `build/hello'.
make: Nothing to be done for `build/hello'.

您应该将您的第一个规则标记为 terminal,方法是用双冒号定义它。

When a rule is terminal, it does not apply unless its prerequisites actually exist. Prerequisites that could be made with other implicit rules are not good enough. In other words, no further chaining is allowed beyond a terminal rule.

将您的 makefile 更改为:

%:: %.x
        cp $< $@

build/%: src/%
        cp $< $@

测试make build/hello

cp src/hello.x src/hello
cp src/hello build/hello
rm src/hello

下面的调试日志显示了它是如何工作的:

 ......
 Looking for a rule with intermediate file `src/hello'.
  Avoiding implicit rule recursion.
  Trying pattern rule with stem `hello'.
  Trying implicit prerequisite `src/hello.x'.
 Found an implicit rule for `build/hello'.
   Considering target file `src/hello.x'.
    Finished prerequisites of target file `src/hello.x'.
   No need to remake target `src/hello.x'.
 Considering target file `src/hello'.
  File `src/hello' does not exist.
   Pruning file `src/hello.x'.
  Finished prerequisites of target file `src/hello'.
 Must remake target `src/hello'.
cp src/hello.x src/hello
Putting child 0x08a51438 (src/hello) PID 30908 on the chain.
Live child 0x08a51438 (src/hello) PID 30908
Reaping winning child 0x08a51438 PID 30908
Removing child 0x08a51438 PID 30908 from chain.
 Successfully remade target file `src/hello'.
 Finished prerequisites of target file `build/hello'.
Must remake target `build/hello'.
cp src/hello build/hello
Putting child 0x08a51438 (build/hello) PID 30909 on the chain.
Live child 0x08a51438 (build/hello) PID 30909
Reaping winning child 0x08a51438 PID 30909
Removing child 0x08a51438 PID 30909 from chain.
Successfully remade target file `build/hello'.
Removing intermediate files...
rm src/hello