Makefile 忽略模式中包含子目录的模式规则
Makefile ignores pattern rule with subdirectory in pattern
考虑以下目录结构:
.
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
存在。
规则 _%.ext2: bar/%.ext1
,当在目标 _foo/test.ext2
上调用时,将匹配词干 (%) 等于 foo
的规则。要使规则生效,需要有一个文件(或规则)来创建 bar/%.ext1 = bar/foo.ext1
.
根据 OP,输入文件是 bar/foo/test1.ext1.
尝试调整依赖关系:
_%/test.ext2: bar/%/test.ext1
echo $*
我通过将以下内容添加到 makefile 中设法避免了这个问题:
TARGETS := $(patsubst bar/%,_%,$(patsubst %.ext1,%.ext2,$(wildcard bar/**/*.ext1)))
$(TARGETS): _%.ext2: bar/%.ext1 # % should match foo/test
echo $*
这基本上是预先构建目标(从 bar/
中可用的文件),然后将规则应用于它们。但是,我不确定为什么模式规则适用于这种情况而不适用于原始情况,所以我仍然希望得到解释。
这在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 从目标中生成前导 ./
的条带,使模式再次不匹配:
$ 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
函数。
您的最终方法有效,因为您的最终解决方案是静态模式规则。它不应用相同的 'strip directory' 方法,而只是匹配字符串。
考虑以下目录结构:
.
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
存在。
规则 _%.ext2: bar/%.ext1
,当在目标 _foo/test.ext2
上调用时,将匹配词干 (%) 等于 foo
的规则。要使规则生效,需要有一个文件(或规则)来创建 bar/%.ext1 = bar/foo.ext1
.
根据 OP,输入文件是 bar/foo/test1.ext1.
尝试调整依赖关系:
_%/test.ext2: bar/%/test.ext1
echo $*
我通过将以下内容添加到 makefile 中设法避免了这个问题:
TARGETS := $(patsubst bar/%,_%,$(patsubst %.ext1,%.ext2,$(wildcard bar/**/*.ext1)))
$(TARGETS): _%.ext2: bar/%.ext1 # % should match foo/test
echo $*
这基本上是预先构建目标(从 bar/
中可用的文件),然后将规则应用于它们。但是,我不确定为什么模式规则适用于这种情况而不适用于原始情况,所以我仍然希望得到解释。
这在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 从目标中生成前导 ./
的条带,使模式再次不匹配:
$ 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
函数。
您的最终方法有效,因为您的最终解决方案是静态模式规则。它不应用相同的 'strip directory' 方法,而只是匹配字符串。