匹配Makefile目标的不同/多个部分

匹配Makefile目标的不同/多个部分,makefile,pattern-matching,target,Makefile,Pattern Matching,Target,这是一个Makefile,我目前使用它来创建具有不同配置的目标,也就是说,我正在使用同一个目标构建不同的软件包,可以一次生成,也可以单独生成 .PHONY: build test %.build %.test build-all test-all %.build %.test: PACKAGE = $* %.build: @echo build $(PACKAGE) %.test: @echo test $(PACKAGE) build-all: a.build b.bui

这是一个Makefile,我目前使用它来创建具有不同配置的目标,也就是说,我正在使用同一个目标构建不同的软件包,可以一次生成,也可以单独生成

.PHONY: build test %.build %.test build-all test-all
%.build %.test: PACKAGE = $*

%.build:
    @echo build $(PACKAGE)

%.test:
    @echo test $(PACKAGE)

build-all: a.build b.build
test-all:  a.test  b.test

build: $(PACKAGE).build
test:  $(PACKAGE).test
我现在可以使用
makebuildall
构建所有包,也可以使用
makebuildpackage=a
构建单个包。但是,我想切换
%.build
build
等目标的主体,如下所示:

.PHONY: build test %.build %.test build-all test-all
build:
    @echo build $(PACKAGE)

test:
    @echo test $(PACKAGE)

build-all: a.build b.build
test-all:  a.test  b.test

%.build %.test: PACKAGE = $*
$(PACKAGE).%: $*
这样,模式匹配逻辑就与应该包含实际构建命令的“主要”目标
build
test
完全分离;使Makefile的重要部分更具可读性。但是,最后一行没有按预期工作,即运行
make a.build
,因此
make build all
应该触发目标
build
,使用
PACKAGE=a
。最后一行中的变量赋值有效,而最后一行中的目标匹配无效


问题:有没有一种方法可以表示匹配的目标,如
$(包)。%:$*
或匹配目标的单独部分,如
%。%:$2

首先您可能需要:

$(PACKAGE).% : %
不使用
$*
,这是一个自动变量,因此除了配方内部之外,它没有任何值;在必备条件列表中不能这样使用它

第二,在GNU make中不能这样做。没有配方的模式规则不仅仅像显式规则那样创建先决条件关系;相反,它会删除模式规则。由于您没有
$(包).%
的模式规则,这基本上是不可行的。而且,特定于目标的变量仅在配方中可用,因此尝试在目标定义中使用
$(包)
,并期望它从以前设置的特定于目标的变量中获取值是行不通的

您可以这样做,但它不是完全动态的(您仍然需要包和类型的列表):


正如疯狂科学家所解释的,这个问题在GNU make中不容易解决。为了完整起见,我想补充并解释我的最终和更全面的解决方案:

.PHONY: all build test clean %.build %.test build-all test-all

PACKAGES  = a b c e f g
PACKAGE   = a

all: clean build-all test-all

%.build %.test: PACKAGE = $*

%.build:
    @echo build $(PACKAGE)

%.test:
    @echo test $(PACKAGE)

clean:
    @echo remove build dir

build-all: $(addsuffix .build, $(PACKAGES))
test-all:  $(addsuffix .test,  $(PACKAGES))

build: $(PACKAGE).build
test:  $(PACKAGE).test
此解决方案避免了
eval
foreach
,并基于我的初始工作解决方案,其中动态
%.build
%.test
目标包含实际的构建命令。我添加了
PACKAGES
变量以方便添加新包,添加了默认的
PACKAGES
以防止执行错误配置的构建命令,并添加了公共目标
all
clean
作为补充

从命令行,您只需调用
make
all
clean
build PACKAGE=x
build all
,等等,即仅调用静态目标,然后将触发动态目标中的构建命令。静态目标和两个变量在Bash/Zsh自动完成中也可见


我认为这是构建多个动态目标的最灵活且可读的方法。

我意识到这只是一个示例,但这里没有需要make的东西。如果这真的是全部,那么您使用了错误的工具,只需编写一个shell脚本。正如上的第一句所指出的,“GNU Make是一个工具,它控制从程序的源文件生成程序的可执行文件和其他非源文件。”我使用此模式从go代码生成和测试可执行文件,例如,调用
go build
go install
,和
go test
,用于不同分区中的包。我还使用此模式在docker映像内构建可执行文件,并构建docker映像本身。好的,因此我需要
foreach
eval
。我想避免那样。然而,这回答了我的问题。谢谢如果您只有两种目标类型(
build
test
),那么编写它们肯定更简单、更可读。
.PHONY: all build test clean %.build %.test build-all test-all

PACKAGES  = a b c e f g
PACKAGE   = a

all: clean build-all test-all

%.build %.test: PACKAGE = $*

%.build:
    @echo build $(PACKAGE)

%.test:
    @echo test $(PACKAGE)

clean:
    @echo remove build dir

build-all: $(addsuffix .build, $(PACKAGES))
test-all:  $(addsuffix .test,  $(PACKAGES))

build: $(PACKAGE).build
test:  $(PACKAGE).test