考虑以下目录结构:
.
bar/
foo/
test.ext1
_foo/
我用以下规则编写了一个makefile:
_%.ext2: bar/%.ext1 # % should match foo/test
echo $*
但是正在运行
make _foo/test.ext2
给出:make: *** No rule to make target '_foo/test.ext2'. Stop.
当我看着
make -d _foo/test.ext2
,它甚至不考虑上述规则,即它直接跳转到尝试内置规则。我不明白这里发生了什么,所以有人可以向我解释一下吗?编辑:
我希望能够从
bar
中的任意文件生成,例如:bar/foo/test-2.ext1
-> _foo/test-2.ext2
bar/baz/another.ext1
-> _baz/another.ext2
_foo
的存在和 _baz
是有保证的。
最佳答案
这在 manual 中有解释:
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.
和
When prerequisites are turned into file names, the directories from the stem are added at the front, while the rest of the stem is substituted for the
%
因此,使用类似
_%.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.
修复它的最简单方法是使目标包含斜杠。如果您可以将输出移动到其他地方,则可以执行以下操作:
$ 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
甚至
$ 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
如果您不能这样做,您可以尝试通过完整路径进行引用,即:
$ 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
请注意,使用
./
不起作用,因为 GNU make strips 领先 ./
来自使模式再次不匹配的目标:$ 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.
如果你想让你的同行评审同事紧张,你也可以使用一些二次扩展魔法来生成正确的先决条件名称,例如:
$ 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
注:
%
必须通过 $(percent)
引用变量,否则 GNU make 解析器可能会提示或误解 patsubst
功能。您的 final方法有效,因为您的最终解决方案是静态模式规则。它不应用相同的“ strip 目录”方法,而只是匹配字符串。
关于Makefile 忽略模式中子目录的模式规则,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59048388/