这是一个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
我现在可以使用make build-all
或单个包构建所有包,例如make build PACKAGE=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).%: $*
这样,模式匹配逻辑与" main"完全分离。应包含实际构建命令的目标build
和test
;使Makefile的重要部分更具可读性。但是,最后一行不能按预期工作,即运行make a.build
,因此make build-all
应使用build
触发目标PACKAGE=a
。在倒数第二行中的变量赋值起作用,最后一行中的目标匹配不起作用。
问题:有没有办法表达匹配的目标,例如$(PACKAGE).%: $*
或匹配目标的不同部分,例如%.%: $2
?
答案 0 :(得分:0)
首先你可能想要:
$(PACKAGE).% : %
不使用$*
,这是一个自动变量,所以除了食谱之外它没有任何价值;你不能像在先决条件清单中那样使用它。
其次,你不能在GNU make中这样做。没有配方的模式规则不仅仅是创建先决条件关系,就像显式规则一样;而是删除模式规则。由于您还没有$(PACKAGE).%
的模式规则,这基本上是一个无操作。此外,特定于目标的变量仅在配方中可用,因此尝试在目标定义中使用$(PACKAGE)
并期望它从某些先前设置的特定于目标的变量中获取值不起作用。
你可以这样做,但它不是完全动态的(你仍然需要包和类型列表):
PACKAGES = a b
TYPES = build test
$(foreach T,$(TYPES),$(eval $(addsuffix .$T,$(PACKAGES)): $T))
答案 1 :(得分:0)
正如MadScientist所解释的那样,这个问题在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
变量以便于添加新软件包,默认PACKAGE
以防止执行错误配置的构建命令,并添加公共目标all
和clean
作为补充。
在命令行中,您只需拨打make
all
,clean
,build PACKAGE=x
,build-all
等,即只有静态目标,然后,它将触发动态目标中的构建命令。静态目标和两个变量在Bash / Zsh自动完成中也可见。
我认为这是构建多个动态目标的最灵活且可读的方式。