我正在处理一个相当复杂且有些旧的makefile(因此随着时间的推移,它可能会消失(,并注意具有此配方模式的规则:
SUBDIRS = lib1 lib2 lib3
all: prerequisites
$(MAKE) subdirs
subdirs: $(SUBDIRS)
# (empty recipe body)
是否有(或曾经(有任何理由以这种方式编写all
规则,而不是简单地
all: prerequisites subdirs
# (empty recipe body)
两者之间有微妙的区别吗?我同时运行GNU make 4.1,并且没有看到任何差异。
makefile是针对Linux和OS/X的,应该支持一些过时的设置,但不是很(首先,我们需要gcc 4.7,这是3年的历史(。
有一个巨大而重要的区别: 将其编写为三个单独的目标可使-j
标志正常工作。make -j9
告诉 make 保持 9 个作业同时运行,如果您有 8 个 CPU,那就太好了。
这是对任何制作文件的一个很好的测试,也是恕我直言的全部意义。
您提供的模式是尝试确保在子目录开始构建之前完成先决条件。当然,还有一种更直接的写作方式:
all: ${SUBDIRS}
echo $@ Success
${SUBDIRS}: prerequisites
echo Building $@...
subdirs: ${SUBDIRS} ; # Empty symbolic target
subdirs目标只是为了方便任何希望键入make subdirs
的人。
当然,应酌情添加PHONY:
有微妙的差异,但这肯定不重要,也不是故意的,与你的具体情况。
包括但不限于,这里有一些细微的区别。
第一个区别:gmake有一个特殊功能,可以在开始时定义(如果尚未完成(并增加变量MAKELEVEL。因此,以下两个生成文件具有不同的结果:
SUBDIRS = lib1 lib2 lib3
all: prerequisites
$(MAKE) subdirs
subdirs: $(SUBDIRS)
lib3:
echo $(MAKELEVEL)
运行此生成文件将显示:
1
但有以下几点:
SUBDIRS = lib1 lib2 lib3
all: prerequisites subdirs
subdirs: $(SUBDIRS)
lib3:
echo $(MAKELEVEL)
运行此生成文件将显示:
0
因此,如果MAKELEVEL将用于您的Makefile,则构建方式可能不同。例如,以这种方式定义变量会产生一些影响:
ifeq (1,${MAKELEVEL})
CFLAGS=-g
endif
第二个区别:如 gmake 手册中所述,默认情况下,只有来自环境或命令行的变量才会传递给递归调用。但是很少产生影响,因为如果变量的定义不依赖于上下文,那么变量将在内部调用中使用相同的值定义(要产生影响,您应该设置一个上下文相关变量,并在首次运行 Makefile 和运行递归调用时遇到不同的上下文(。
无论如何,当子目录中有其他 Makefile 时,人们经常使用递归调用(但这显然不是您的情况(。所以,我们经常遇到这个:
SUBDIRS = lib1 lib2 lib3
all: prerequisites
$(MAKE) -C subdirs
当然,这不能用all: prerequisites $SUBDIRS
代替,这是没有意义的。但这也许就是为什么有人写了你遇到的东西,而不是相反,没有内心的调用,错误地认为这是一种更常见的写东西的方式。