GNU使用不匹配的模式制作模式规则

时间:2018-03-16 13:58:32

标签: makefile gnu-make

我尝试使用Go的codegen工具根据其他go文件的内容自动生成一些代码。 codegen工具将获得标准参数,这些参数可以从其生成的文件名称和它解析的文件名称中推断出来。如果我手动完成所有操作,它将如下所示:

foo-tool -name FooInterface -file foo/api.go
foo-tool -name BarInterface -file foo/api.go
foo-tool -name BingInterface -file foo/bing.go
foo-tool -name BazInterface -file foo/baz.go

但我不想手动操作,我想使用Make!所以我尝试使用Makefile和模式规则完成同样的事情。

foo_FooInterface.go : foo/api.go    
foo_BarInterface.go : foo/api.go
foo_BingInterface.go : foo/bing.go
foo_BazInterface.go : foo/baz.go

foo_%.go : %.go
    $(eval foo_name=$(subst mock_,,$(subst .go,,$(@F))))
    build-foo -name $(foo_name) -file $<

在我看来,前3条规则会设置依赖图,模式规则会告诉Make如何处理依赖项。但是当我尝试运行make foo_BarInterface.go时,我得到make: Nothing to be done for foo_BarInterface.go。我理解为什么会发生这种情况,因为Make期望将foo_FooInterface.go与FooInterface.go匹配,但我不想重构我的项目文件。

这是可能的,还是我需要做类似的事情:

foo_FooInterface.go : foo/api.go
    build-foo -name FooInterface -file foo/api.go

foo_BarInterface.go : foo/api.go
    build-foo -name BarInterface -file foo/api.go

foo_BingInterface.go : foo/bing.go
    build-foo -name BingInterface -file foo/bing.go

foo_BazInterface.go : foo/baz.go
    build-foo -name BingInterface -file foo/baz.go

我真的不想这样做,因为随着代码库的增长会添加新的Interface,而且我不想要求人们每次都输入所有内容。

编辑:我不介意每次都手动指定规则,但我需要一个规则来收集所有生成的文件,而且我不想指定每个foo _ *。 。有没有办法说&#34;这个规则取决于与模式匹配的所有规则(而不是文件)?&#34;我能够做到

foo_files := $(shell grep 'foo_\w\+.go' Makefile | cut -d : -f1)

但这对我来说似乎很糟糕。

3 个答案:

答案 0 :(得分:1)

您可以为这三个规则编写一个通用配方:

foo_FooInterface.go foo_BarInterface.go foo_BingInterface.go: foo/api.go
    foo-tool -name $(@:foo_%.go=%) -file $<

对于每个单独的目标单独执行单独 ,即:foo_FooInterface.go一次,foo_BarInterface.go一次和一次对于foo_BingInterface.go仅适用于所有人。

答案 1 :(得分:1)

没有比这更容易了:

foo_%.go : foo/api.go
    foo-tool -name $* -file $<

如果你想要一个将它们聚集在一起的规则:

INTERFACES := Foo Bar Bing
FILES := $(patsubst %, foo_%Interface.go, $(INTERFACES))

all: $(FILES)
    @echo do something with $^

答案 2 :(得分:1)

如果每个目标文件都有不同的先决条件,则无法避免单独指定它们。您可以使用空规则执行此操作,如下所示:

foo_FooInterface.go : foo/api.go    
foo_BarInterface.go : foo/api.go
foo_BingInterface.go : foo/bing.go
foo_BazInterface.go : foo/baz.go

但是,使用这种样式的makefile,构建目标的规则不能具有先决条件:如果这样做,则忽略空规则中指定的先决条件。所以你需要的是简单的规则

foo_%.go:
    foo-tool -name $* -file $<

(请注意$^已扩展为所有先决条件的列表,$<仅为第一个。在您的示例中没有区别,但可能有。)

我不知道有任何方法指定“匹配模式的makefile中的所有规则”。但正如@Beta建议的那样,在变量中“注册”所有所需名称的成本很低。把它们放在一起:

# Register all your components here
INTERFACES := Foo Bar Bing Baz

# Make ’em all
.PHONY : all
all: $(patsubst %, foo_%Interface.go, $(INTERFACES))

# Specify individual dependencies with empty rules
foo_FooInterface.go : foo/api.go    
foo_BarInterface.go : foo/api.go
foo_BingInterface.go : foo/bing.go
foo_BazInterface.go : foo/baz.go

# This is your catch-(almost-)all rule
foo_%.go:
    foo-tool -name $* -file $<

因此,每个新目标只需要在INTERFACES中注册其名称并添加具有依赖性的行。在我看来,尝试仅添加依赖行并从中推断出新目标的存在(正如您通过grepping foo_\w\+.go的makefile所做的那样),降低了整体可读性而没有任何实际好处。