首页 文章

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

提问于
浏览
1

我正在尝试使用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 _ * . 进入那个 . 有没有办法说“这条规则取决于匹配模式的所有规则(而不是文件)?”我能够做到

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

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

3 回答

  • 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 中注册其名称并添加具有依赖性的行 . 在我看来,尝试仅添加依赖行并从中推断出新目标的存在(正如您通过为 foo_\w\+.go 点击makefile所做的那样),降低了整体可读性而没有任何实际好处 .

  • 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 $^
    
  • 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.gonot 仅一次用于所有这些目标 .

相关问题