如何使用 GNU Make 优化子目录的并行构建?

How to optimize parallel build of subdirectories using GNU Make?

我在一个大型项目中工作,我们有几个从中构建的子目录 libs/archives。大多数子目录彼此之间没有依赖关系,但有些有。

例如,这是一般形式:

TARG1=targ1/
TARG2=targ2/
TARG3=targ3/

SUBDIRS= lib1\
         lib2\
         lib3\
         ...

.PHONY: $(TARG1) $(TARG2) $(TARG3)

构建顺序必须如下:TARG1 -> TARG2-> TARG3 -> SUBDIRS

我们这样构建目标:

build_libs:
      $(MAKE) subdirs

subdirs: $(SUBDIRS)

$(SUBDIRS): $(TARG3)
      $(MAKE) -C $@

$(TARG3): $(TARG2)
      $(MAKE) -C $@

$(TARG2): $(TARG1)
      $(MAKE) -C $@

$(TARG1):
      $(MAKE) -C $@

现在,正如我所见,这将允许最大程度的并行化,因为在构建目标 (TARG1 -> TARG3) 时,Make 可以同时构建 SUBDIRS 中的所有库。但也许有更好的方法来优化这个?据我所知,这种方法的缺点是我们得到了一个更大的 Makefile,因为我们必须为每个目标指定一个配方,而每个目标都依赖于另一个目标。有什么方法可以避免这种情况并仍然允许完全并行化吗?

另一种方法是像这样构建目标:

SUBDIRS = targ1 targ2 targ3 lib1 lib2 lib3

subdirs:
        for dir in $(SUBDIRS); do \
          $(MAKE) -C $$dir; \
        done

现在这种方法的好处是只需按正确的顺序编写目标即可轻松指定依赖项。然而,Make 将无法充分利用并行化,因为它必须在移动到下一个子目录之前完成每个子目录。

也许我的假设是错误的,所以我问这个问题如何最好地组织目标以实现最佳并行化?作为附带问题,如何组织目标以避免代码重复?

您不必复制食谱,如果它们都一样的话。如果您不想,不必同时定义配方和先决条件。您可以将第一个示例重写为:

build_libs:
        $(MAKE) subdirs

$(SUBDIRS) $(TARG1) $(TARG2) $(TARG3):
        $(MAKE) -C $@

subdirs: $(SUBDIRS)
$(SUBDIRS): $(TARG3)
$(TARG3): $(TARG2)
$(TARG2): $(TARG1)

我真的不知道你为什么要 build_libs 递归;为什么不直接 build_libs: subdirs