makefile - 终端匹配任何模式规则,多个先决条件
问题描述
我正在尝试使用 Paul Smith 的Advanced VPATH Method for Multi-Architecture Builds(稍作修改的版本)在不同的子目录中构建我们的应用程序,每个子目录使用不同的编译器标志,基于传递给make
命令行的环境变量.
以下Makefile
是我们正在做的简化示例:
- 如果用户调用
make debug=1
,output
目标将被构建到_debug
带有特定编译器标志的子目录中。 - 如果用户调用
make release=1
,output
目标将被构建到_release
带有特定编译器标志的子目录中。
RELEASEDIR := _release
DEBUGDIR := _debug
OBJDIRS :=
ifdef debug
OBJDIRS += $(DEBUGDIR)
endif
ifdef release
OBJDIRS += $(RELEASEDIR)
endif
ifneq ($(MAKECMDGOALS),clean)
ifndef OBJDIRS
$(error Must define "debug" and/or "release" in the environment, e.g. "make debug=1")
endif
endif
#----- Begin section to determine what subdir(s) to change into
ifeq (,$(filter _%,$(notdir $(CURDIR))))
.SUFFIXES:
MAKETARGET = $(MAKE) --no-print-directory -C $@ -f $(CURDIR)/Makefile \
SRCDIR=$(CURDIR) $(MAKECMDGOALS)
.PHONY: $(OBJDIRS)
$(OBJDIRS):
+@[ -d $@ ] || mkdir -p $@
+@$(MAKETARGET)
Makefile : ;
%.mk :: ;
% :: $(OBJDIRS) ;
.PHONY: clean
clean:
rm -rf _*
else
#----- End subdir(s) section
VPATH = $(SRCDIR)
# do special things (e.g. set compiler flags) based on the subdirectory
CURRENT_SUBDIR := $(notdir $(CURDIR))
ifneq (,$(findstring $(RELEASEDIR),$(CURRENT_SUBDIR)))
$(info ...set compiler flags specific to release build...)
endif
ifneq (,$(findstring $(DEBUGDIR),$(CURRENT_SUBDIR)))
$(info ...set compiler flags specific to debug build...)
endif
output : prereq
cp -f $< $@
# user can specify v=1 to turn verbosity up
$(v).SILENT:
#----- End the "subdirs" if-statement
endif
如果我只指定两个变量之一,这将按我的预期工作:
$ make debug=1 v=1
...set compiler flags specific to debug build...
cp -f /home/me/work/prereq output
和:
$ make release=1 v=1
...set compiler flags specific to release build...
cp -f /home/me/work/prereq output
因此该文件output
是在_debug
和_release
子目录中创建的。
问题是当我尝试指定两个变量时,它只在_debug
子目录中构建目标:
$ make clean
rm -rf _*
$ make debug=1 release=1 v=1
...set compiler flags specific to debug build...
cp -f /home/me/work/prereq output
我不明白这种行为。OBJDIRS
在这种情况下等于"_debug _release"
,因此我的终端匹配任何规则 ( % :: $(OBJDIRS) ;
) 同时具有和 _debug
作为_release
先决条件。因此,我希望 GNU Make 能够检查(并构建)这两个先决条件,但它只构建第一个(_debug
)。
有趣的是,如果我在命令行上明确指定要构建的目标,它会同时构建:
$ make clean
rm -rf _*
$ make debug=1 release=1 v=1 output
...set compiler flags specific to debug build...
cp -f /home/me/work/prereq output
...set compiler flags specific to release build...
cp -f /home/me/work/prereq output
我假设我对终端匹配任何规则的工作方式有一些误解。我不明白为什么明确指定目标时的行为与未明确指定时的行为不同。
我在 CentOS 7 中使用 GNU Make 3.82。
编辑:我也在 Arch Linux 上的 GNU Make 4.2.1 中看到了这种行为。
解决方案
我不得不承认我没有完全分析你的makefile
,但以下内容对我来说很突出。阅读9.2 指定目标的参数:
默认情况下,目标是 makefile 中的第一个目标(不包括以句点开头的目标)。因此,通常编写 makefile 以使第一个目标是编译它们描述的整个程序或程序。如果 makefile 中的第一个规则有多个目标,则只有规则中的第一个目标成为默认目标,而不是整个列表。
因此make debug=1 release=1 v=1
,在没有提及明确目标的情况下执行,只会导致您的$(OBJDIRS)
值中的第一个条目成为目标,而不是整个列表。这将是$(DEBUGDIR)
,而不是$(DEBUGDIR) $(RELEASEDIR)
。这似乎解释了你所观察到的。
使用此代码段模拟您的情况的实验演示了该行为:
RELEASEDIR := _release
DEBUGDIR := _debug
OBJDIRS :=
ifdef debug
OBJDIRS += $(DEBUGDIR)
endif
ifdef release
OBJDIRS += $(RELEASEDIR)
endif
.PHONY: $(OBJDIRS)
$(OBJDIRS):
@echo OBJDIRS = $(OBJDIRS), Target = $@
output: $(OBJDIRS)
@echo Creating the output target
结果如下:
$ make debug=1
OBJDIRS = _debug, Target = _debug
$ make release=1
OBJDIRS = _release, Target = _release
$ make release=1 debug=1
OBJDIRS = _debug _release, Target = _debug
$ make release=1 debug=1 _release
OBJDIRS = _debug _release, Target = _release
$ make release=1 debug=1 output
OBJDIRS = _debug _release, Target = _debug
OBJDIRS = _debug _release, Target = _release
Creating the output target
更新:上面确定了问题,这是一个解决方案(由OP找到)
推荐阅读
- java - 我可以在 .removeif() 中使用 .size() 作为 ArrayLists 的条件吗?
- ios - UITextfield中的iOS Swift十进制输入从右到左
- python-3.x - 条件参数的 Pythonic 解决方案
- assembly - 如何在 MARIE 中将数字 x 提高到 y 次方?
- javascript - 向有效负载添加超过 1 个数值
- saleor - 销售或可选应用程序
- excel - SSIS Excel 连接“无法加载任何表或视图”
- java - MIPS 组装调度
- javascript - 在鼠标悬停时显示/隐藏 DIV 容器
- javascript - 如何根据信息表绘制动态折线图?