如果只有一个子字符串匹配,为什么 make select 模式规则?

Why does make select pattern rule if only a substring matches?

我正在编写一个 makefile,它应该从不同体系结构的代码中创建多个可执行文件。这个想法是每个可执行文件的后缀表示它是为构建的体系结构而构建的。这是一个简化的摘录:

TARGET_NAME = foo
TARGET_AMD64 = $(TARGET_NAME)_amd64
TARGET_ARMHF = $(TARGET_NAME)_armhf
  
.PHONY:
all: $(TARGET_AMD64) $(TARGET_ARMHF) ;
    
$(TARGET_NAME)_%: lib/mylib.a
    @echo "$@ built."
   
lib/mylib.a: bin/mylib/src/mac/foo_ct.o
    @echo "$@ built."

键入 make 时,我收到循环依赖:

make: Circular bin/mylib/src/mac/foo_ct.o <- lib/mylib.a dependency dropped.
bin/mylib/src/mac/foo_ct.o built.
lib/mylib.a built.
foo_amd64 built.
foo_armhf built.

显然,make 正在寻找匹配 bin/mylib/src/mac/foo_ct.o 的规则。由于我无法理解的原因,make 采用 $(TARGET_NAME)_%(第二条规则)。

为什么会这样?只有文件名 foo_ct.o 会匹配模式规则 $(TARGET_NAME)_%,但为什么前面的路径 bin/mylib/src/mac/ 会被忽略?

这实际上是 defined behaviour:

When the target pattern does not contain a slash (and it usually does not), directory names in the file names are removed from the file name before it is compared with the target prefix and suffix. After the comparison of the file name to the target pattern, the directory names, along with the slash that ends them, are added on to the prerequisite file names generated from the pattern rule’s prerequisite patterns and the file name. The directories are ignored [...]

因此,您必须使用绝对路径指定目标:

TARGET_NAME = $(CURDIR)/foo

但是请注意,$(CURDIR) 是一个 GNU make 扩展。


或者,您也可以为 `bin/mylib/src/mac/foo_ct.o` 指定更具体的规则来打破循环:
TARGET_NAME = foo
TARGET_AMD64 = $(TARGET_NAME)_amd64
TARGET_ARMHF = $(TARGET_NAME)_armhf
  
.PHONY:
all: $(TARGET_AMD64) $(TARGET_ARMHF;
    
$(TARGET_NAME)_%: lib/mylib.a
    @echo "$@ built."
   
lib/mylib.a: bin/mylib/src/mac/foo_ct.o
    @echo "$@ built."

bin/mylib/src/mac/foo_ct.o:
    true