首页 > 解决方案 > 终端匹配任何模式规则,多个先决条件

问题描述

我正在尝试使用 Paul Smith 的Advanced VPATH Method for Multi-Architecture Builds(稍作修改的版本)在不同的子目录中构建我们的应用程序,每个子目录使用不同的编译器标志,基于传递给make命令行的环境变量.

以下Makefile是我们正在做的简化示例:


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 中看到了这种行为。

标签: makefilegnu-make

解决方案


我不得不承认我没有完全分析你的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找到)


推荐阅读