终端匹配任何模式规则,多个先决条件
Terminal match-anything pattern rule, multiple prerequisites
我正在尝试使用 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) ;
) 具有 both _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
我假设我对终端 match-anything rules 的工作方式有一些误解。我不明白为什么明确指定目标时和未明确指定目标时的行为不同。
我在 CentOS 7 中使用 GNU Make 3.82。
编辑:我在 Arch Linux.
上的 GNU Make 4.2.1 中也看到了这种行为
我不得不承认我没有完全分析您的 makefile
,但以下内容对我来说很突出。阅读 9.2 Arguments to Specify the Goals:
By default, the goal is the first target in the makefile (not counting
targets that start with a period). Therefore, makefiles are usually
written so that the first target is for compiling the entire program
or programs they describe. If the first rule in the makefile has
several targets, only the first target in the rule becomes the default
goal, not the whole list.
因此,在没有明确提及目标的情况下执行 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找到)
对我的问题是正确的。我的解决方案是检查 $(OBJDIRS)
是否包含多个单词,如果是,则使单词 2 到 N(我只是选择了 100 的任意高 N)单词 1 的先决条件:
ifneq (1,$(words $(OBJDIRS)))
$(firstword $(OBJDIRS)): $(wordlist 2,100,$(OBJDIRS))
endif
因此 Makefile
的那部分现在看起来像这样:
.SUFFIXES:
MAKETARGET = $(MAKE) --no-print-directory -C $@ -f $(CURDIR)/Makefile \
SRCDIR=$(CURDIR) $(MAKECMDGOALS)
.PHONY: $(OBJDIRS)
$(OBJDIRS):
+@[ -d $@ ] || mkdir -p $@
+@$(MAKETARGET)
ifneq (1,$(words $(OBJDIRS)))
$(firstword $(OBJDIRS)): $(wordlist 2,100,$(OBJDIRS))
endif
Makefile : ;
%.mk :: ;
% :: $(OBJDIRS) ;
.PHONY: clean
clean:
rm -rf _*
我正在尝试使用 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) ;
) 具有 both _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
我假设我对终端 match-anything rules 的工作方式有一些误解。我不明白为什么明确指定目标时和未明确指定目标时的行为不同。
我在 CentOS 7 中使用 GNU Make 3.82。
编辑:我在 Arch Linux.
上的 GNU Make 4.2.1 中也看到了这种行为我不得不承认我没有完全分析您的 makefile
,但以下内容对我来说很突出。阅读 9.2 Arguments to Specify the Goals:
By default, the goal is the first target in the makefile (not counting targets that start with a period). Therefore, makefiles are usually written so that the first target is for compiling the entire program or programs they describe. If the first rule in the makefile has several targets, only the first target in the rule becomes the default goal, not the whole list.
因此,在没有明确提及目标的情况下执行 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
更新:以上确定了问题,
$(OBJDIRS)
是否包含多个单词,如果是,则使单词 2 到 N(我只是选择了 100 的任意高 N)单词 1 的先决条件:
ifneq (1,$(words $(OBJDIRS)))
$(firstword $(OBJDIRS)): $(wordlist 2,100,$(OBJDIRS))
endif
因此 Makefile
的那部分现在看起来像这样:
.SUFFIXES:
MAKETARGET = $(MAKE) --no-print-directory -C $@ -f $(CURDIR)/Makefile \
SRCDIR=$(CURDIR) $(MAKECMDGOALS)
.PHONY: $(OBJDIRS)
$(OBJDIRS):
+@[ -d $@ ] || mkdir -p $@
+@$(MAKETARGET)
ifneq (1,$(words $(OBJDIRS)))
$(firstword $(OBJDIRS)): $(wordlist 2,100,$(OBJDIRS))
endif
Makefile : ;
%.mk :: ;
% :: $(OBJDIRS) ;
.PHONY: clean
clean:
rm -rf _*