使用 GNU 制作处理项目间的依赖关系



我有两个用GNU构建的项目。项目 B 取决于项目 A 的产出之一。我们称之为输出。因此,项目 B 的 Makefile 包含如下内容:

target:  ../ProjectA/OUTPUT
do stuff

为了确保 OUTPUT 是最新的,我希望 B 的 Makefile 启动 A 的制作,然后只有在 OUTPUT 被重建(或者已经比目标更新(时才"做事"。

我首先尝试这样做:

../ProjectA/OUTPUT:
(cd ../ProjectA; $(MAKE) OUTPUT)

但是由于没有为 OUTPUT 提供依赖关系,make 将简单地假设它是最新的(如果它存在并且不会运行子 make(。

将输出声明为假可确保子版本运行:

.PHONY: ../ProjectA/OUTPUT
../ProjectA/OUTPUT:
(cd ../ProjectA; $(MAKE) OUTPUT)

但现在 make 将忽略 OUTPUT 上的实际日期,并始终认为它比目标更新,这意味着"做事"将始终执行。

我发现唯一有效的方法是在 B 的 Makefile 中复制 OUTPUT 的整个依赖树。这太可怕了,无法想象。只有稍微不那么可怕的是将 OUTPUT 的依赖树放在一个单独的文件中,两个项目的 Makefile 都包含该文件。

有没有更好的方法来做我想做的事?

你可以添加一个虚假的目标,用一个配方来制作另一个项目,这样即使没有什么可做的,它总是被制作的,然后启动第二个子制作来制作你的实际目标:

.PHONY: all
all:
$(MAKE) -C ../ProjectA OUTPUT
$(MAKE) target
target: ../ProjectA/OUTPUT
do stuff

不是很优雅,效率也不是很高,但它应该做你想做的事:

  1. 始终尝试重建../ProjectA/OUTPUT
  2. 如果../ProjectA/OUTPUT更改,也重建target

还有另一种方法,更优雅,更高效,但它使用了一个相当晦涩的GNU make特性:双冒号规则(参见手册的这一部分(,以一种相当不寻常的方式,它肯定不是为此发明的:

target: ../ProjectA/OUTPUT
do stuff
../ProjectA/OUTPUT::
$(MAKE) -C ../ProjectA OUTPUT

所以,如果你不喜欢晦涩难懂,更喜欢第一个。至少,它更容易理解,没有人会通过删除这个额外的冒号来破坏它,这肯定是一个错字......

好吧,我不知道双冒号规则是这样工作的。这确实很有用。

只是为了记录,有一种方法可以让每次尝试重建../ProjectA/OUTPUT,这有点不那么晦涩(?基本上,您使用中间.PHONY目标。

.PHONY: FORCE
FORCE: ;
../ProjectA/OUTPUT: FORCE
${MAKE} -C ${@D} ${@F}

由于FORCE,make将意识到OUTPUT可能已经过时了。它将运行配方,然后检查OUTPUT是否实际更新。

(FWIW 我喜欢双冒号配方。对我来说似乎干净多了。

最新更新