我尝试将foo/bar/baz.proto
等文件名转换为Makefile中的foo/bar/Baz.java
。为此,我想我可以使用sed
。但是,该命令似乎无法按预期工作:
uppercase_file = $(shell echo "$(1)" | sed 's/\(.*\/\)\(.*\)/\1\u\2/')
# generated Java sources
PROTO_JAVA_TARGETS := ${PROTO_SPECS:$(SRCDIR)/%.proto=$(JAVAGEN)/$(call uppercase_file,%).java}
当我尝试在命令行上运行sed命令时,似乎可以工作:
~$ echo "foo/bar/baz" | sed 's/\(.*\/\)\(.*\)/\1\u\2/'
foo/bar/Baz
为什么这在Makefile中不起作用?
更新:
使用以下目标生成java文件:
$(JAVAGEN)/%.java: $(SRCDIR)/%.proto
如何将替换应用于目标?
答案 0 :(得分:1)
GNU Make不会替换substitution reference的替换部分中的%
字符(这基本上是patsubst
的语法糖),如果它是变量引用的一部分。我没有发现文档中描述的这种行为,但你可以看一下它在源代码中实现(相信函数我认为是find_char_unquote)。
我建议将调用移出替换引用,因为uppercase_file
显然可以在任何文件路径上正常运行:
PROTO_JAVA_TARGETS := $(call uppercase_file,${PROTO_SPECS:$(SRCDIR)/%.proto=$(JAVAGEN)/%.java})
如果$(PROTO_SPECS)
不解析为单个元素,而是解析为元素列表,则可以使用foreach在已处理列表的每个元素上调用该函数:
PROTO_JAVA_TARGETS := $(foreach JAVA,${PROTO_SPECS:$(SRCDIR)/%.proto=$(JAVAGEN)/%.java},$(call uppercase_file,$(JAVA)))
使用以下目标生成java文件:
$(JAVAGEN)/%.java: $(SRCDIR)/%.proto
如何将替换应用于目标?
由于Make首先匹配目标,并且无法向后运行sed
,因此您需要定义反函数或生成多个显式规则。我将展示后一种方法。
define java_from_proto
$(call uppercase_file,$(1:$(SRCDIR)/%.proto=$(JAVAGEN)/%.java)): $1
# Whatever recipe you use.
# Use `$$@`, `$$<` and so on instead of `$@` or `$<`.
endef
$(foreach PROTO,$(PROTO_SPECS),$(eval $(call java_from_proto,$(PROTO))))
我们基本上使用multiline variable语法在$(PROTO_SPEC)
中为每个文件生成一个规则,然后使用eval
来安装该规则。 <{3}}上还有一个非常相似的例子可以提供帮助。