Makefile 终端匹配任何模式规则,多个先决条件
我正试图使用Paul Smith的(稍加修改的版本)在不同的子目录中构建我们的应用程序,根据在命令行上传递给Makefile 终端匹配任何模式规则,多个先决条件,makefile,gnu-make,Makefile,Gnu Make,我正试图使用Paul Smith的(稍加修改的版本)在不同的子目录中构建我们的应用程序,根据在命令行上传递给make的环境变量,每个子目录都有不同的编译器标志 下面的Makefile是我们所做工作的简化示例: 如果用户调用make debug=1,则输出目标将内置到带有特定编译器标志的\u debug子目录中 如果用户调用makerelease=1,则输出目标将内置到带有特定编译器标志的\u release子目录中 以及: 因此,在\u debug和\u release子目录中都会创建文件
make
的环境变量,每个子目录都有不同的编译器标志
下面的Makefile
是我们所做工作的简化示例:
- 如果用户调用
,则make debug=1
目标将内置到带有特定编译器标志的输出
子目录中\u debug
- 如果用户调用
,则makerelease=1
目标将内置到带有特定编译器标志的输出
子目录中\u release
以及: 因此,在
\u debug
和\u release
子目录中都会创建文件输出
问题是,当我尝试指定这两个变量时,它只在\u 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
我不理解这种行为<在这种情况下,code>OBJDIRS
等于“\u debug\u release”
,因此我的终端匹配任何规则(%::$(OBJDIRS);
)都以\u debug
和\u release
为先决条件。因此,我希望GNU Make检查(并构建)这两个先决条件,但它只是构建第一个先决条件(\u 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中的第一个目标(不计算在内)
以句点开头的目标)。因此,makefile通常是
编写时,第一个目标是编译整个程序
或者他们描述的程序。如果makefile中的第一条规则
多个目标,只有规则中的第一个目标成为默认目标
目标,而不是全部
因此,在没有明确目标的情况下执行makedebug=1release=1v=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设置为单词1的先决条件2到N(我只是选择了任意高的N值100):
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的“高级VPATH方法”的第一个链接。我了解了一些我从未遇到过的Make特性,所以你说的很有道理,我想这绝对是我的问题。有两件事:1)在这种情况下,我不能使用仅限订单的先决条件,因为每个单独的
OBJDIRS
目标都会调用sub-make
,以便在该目录中构建我的所有源代码……如果它是仅限订单的先决条件,那么一旦目录存在,就不会再次构建它(对吗?);为了缓解这种情况,我尝试移动%::$(OBJDIRS)代码>目标在$(OBJDIRS)
目标之上,使其成为所选目标(从而构建两个OBJDIRS
目标,这是它的先决条件),但它仍然只构建\u debug
@villapx是的,我将阅读它,感谢指针。我喜欢他的帖子,一位伟大的老师。你关于“仅目标”先决条件的评论是正确的。这是一个相当有创意的解决方案。我想为任何需要保持这一点的人补充几句解释,除了你自己:——)确实如此。确实在我们的实际项目中写了一条评论不幸的是,我刚刚意识到这个解决方案有一个缺点:由于第一个解决方案之外的任何OBJDIR
都是第一个解决方案的先决条件,因此在使用-j
进行并行构建时,会丢失一些并行性,由于make
无法构建第一个OBJDIR
,直到后者完成。找到了更好的解决方案:在$(OBJDIRS)
目标上方创建一个目标all:$(OBJDIRS)
。这样,当命令行上没有显式设置目标时,all
目标被选中,使得所有的OBJDIRS
都被构建,现在它们都可以完全并行地构建。
$ 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
ifneq (1,$(words $(OBJDIRS)))
$(firstword $(OBJDIRS)): $(wordlist 2,100,$(OBJDIRS))
endif
.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 _*