Makefile 忽略模式中子目录的模式规则

Ste*_*ate 5 makefile gnu-make

考虑以下目录结构:

.
  bar/
    foo/
      test.ext1
  _foo/
Run Code Online (Sandbox Code Playgroud)

我用以下规则编写了一个 makefile:

_%.ext2: bar/%.ext1 # % should match foo/test
    echo $*
Run Code Online (Sandbox Code Playgroud)

然而运行make _foo/test.ext2给出:

make: *** No rule to make target '_foo/test.ext2'.  Stop.
Run Code Online (Sandbox Code Playgroud)

当我查看时make -d _foo/test.ext2,它甚至没有考虑上述规则,即它直接跳到尝试内置规则。我不明白这里发生了什么,有人可以向我解释一下吗?

编辑:我希望能够从 中的任意文件生成bar,例如:

  • bar/foo/test-2.ext1->_foo/test-2.ext2
  • bar/baz/another.ext1->_baz/another.ext2
  • ETC。

_foo和的存在_baz是有保证的。

ras*_*spy 3

手册中对此进行了解释:

当目标模式不包含斜杠(通常不包含)时,在与目标前缀和后缀进行比较之前,会从文件名中删除文件名中的目录名。

当先决条件转换为文件名时,主干中的目录将添加到前面,而主干的其余部分将替换%

因此,像下划线这样的模式_%.ext2与文件名匹配,而不是目录名。所以它不匹配,_foo/test.ext2但它会匹配foo/_test.ext2(并查找foo/bar/test.ext1)。你自己看:

$ cat Makefile
_%.ext2: bar/%.ext1
        echo Making $@ from $<

$ make -dr _foo/test.ext2
...
Considering target file '_foo/test.ext2'.
 File '_foo/test.ext2' does not exist.
 Looking for an implicit rule for '_foo/test.ext2'.
 No implicit rule found for '_foo/test.ext2'.
 Finished prerequisites of target file '_foo/test.ext2'.
Must remake target '_foo/test.ext2'.
make: *** No rule to make target '_foo/test.ext2'.  Stop.

$ make -dr foo/_test.ext2
...
Considering target file 'foo/_test.ext2'.
 File 'foo/_test.ext2' does not exist.
 Looking for an implicit rule for 'foo/_test.ext2'.
 Trying pattern rule with stem 'test'.
 Trying implicit prerequisite 'foo/bar/test.ext1'.
 Trying pattern rule with stem 'test'.
 Trying implicit prerequisite 'foo/bar/test.ext1'.
 Looking for a rule with intermediate file 'foo/bar/test.ext1'.
  Avoiding implicit rule recursion.
 No implicit rule found for 'foo/_test.ext2'.
 Finished prerequisites of target file 'foo/_test.ext2'.
Must remake target 'foo/_test.ext2'.
make: *** No rule to make target 'foo/_test.ext2'.  Stop.
Run Code Online (Sandbox Code Playgroud)

解决这个问题最简单的方法是让目标包含斜杠。如果您可以将输出移动到其他地方,您可以这样做,即:

$ cat Makefile
OBJDIR := obj
$(OBJDIR)/_%.ext2: bar/%.ext1
        echo Making $@ from $<

$ make obj/_foo/test.ext2
echo Making obj/_foo/test.ext2 from bar/foo/test.ext1
Making obj/_foo/test.ext2 from bar/foo/test.ext1
Run Code Online (Sandbox Code Playgroud)

甚至

$ cat Makefile
_/%.ext2: bar/%.ext1
        echo Making $@ from $<

$ make _/foo/test.ext2
echo Making _/foo/test.ext2 from bar/foo/test.ext1
Making _/foo/test.ext2 from bar/foo/test.ext1
Run Code Online (Sandbox Code Playgroud)

如果你不能这样做,你可以尝试通过完整路径引用,即:

$ cat Makefile
$(CURDIR)/_%.ext2: bar/%.ext1
        echo Making $@ from $<

$ make $(pwd)/_foo/test.ext2
echo Making /path/to/curdir/_foo/test.ext2 from bar/foo/test.ext1
Making /path/to/curdir/_foo/test.ext2 from bar/foo/test.ext1
Run Code Online (Sandbox Code Playgroud)

请注意,使用./不起作用,因为 GNU make./从目标开始的条带使得模式不再匹配:

$ cat Makefile
./_%.ext2: bar/%.ext1
        echo Making $@ from $<

$ make -dr ./_foo/test.ext2
...
Considering target file '_foo/test.ext2'.
 File '_foo/test.ext2' does not exist.
 Looking for an implicit rule for '_foo/test.ext2'.
 No implicit rule found for '_foo/test.ext2'.
 Finished prerequisites of target file '_foo/test.ext2'.
Must remake target '_foo/test.ext2'.
make: *** No rule to make target '_foo/test.ext2'.  Stop.
Run Code Online (Sandbox Code Playgroud)

如果您想让同行评审同事感到紧张,您还可以使用一些第二扩展魔法来生成正确的先决条件名称,例如:

$ cat Makefile
percent := %
.SECONDEXPANSION:
%.ext2: bar/$$(patsubst _$$(percent),$$(percent),$$(@D))/$$*.ext1
        echo Making $@ from $<

$ make _foo/test.ext2
echo Making _foo/test.ext2 from bar/foo/test.ext1
Making _foo/test.ext2 from bar/foo/test.ext1
Run Code Online (Sandbox Code Playgroud)

注意:%必须通过变量引用$(percent),否则 GNU make 解析器可能会抱怨或误解该patsubst函数。

您的最终方法有效,因为您的最终解决方案是静态模式规则。它不应用相同的“条带目录”方法,而是仅匹配字符串。