终端匹配任何模式规则,多个先决条件

时间:2019-01-15 23:31:16

标签: makefile gnu-make

我正在尝试使用Paul Smith的Advanced VPATH Method for Multi-Architecture Builds(略有修改的版本)在不同子目录中构建我们的应用,基于传递给{{1}的环境变量,每个子目录具有不同的编译器标志}。

以下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) ;和{{1 }}作为先决条件。因此,我希望GNU Make能够检查(并构建)这两个先决条件,但这只是构建第一个(_debug)。

有趣的是,如果我明确指定要在命令行上构建的目标,则它将同时构建两个目标:

_release

我假设我对终端match-anything rules的工作方式有误解。我不明白为什么在明确指定目标与未明确指定目标时行为会有所不同。

我在CentOS 7中使用GNU Make 3.82。

编辑:我也在Arch Linux的GNU Make 4.2.1中也看到了这种行为。

2 个答案:

答案 0 :(得分:1)

我必须承认,我还没有完全分析您的makefile,但以下内容对我很突出。读9.2 Arguments to Specify the Goals

  

默认情况下,目标是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

更新:以上内容可以确定问题所在,here是一种解决方案(由OP找到)

答案 1 :(得分:1)

Reinier's answer对我的问题是正确的。我的解决方案是检查$(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 _*