由多个目标共享的依赖性仅被调用一次。
为什么?
在这个很小的Makefile
示例中,我希望deploy-everywhere
将staging-repo
代码部署到staging,并将prod-repo
代码部署到prod。
相反,staging和prod均获得staging-repo
代码,这是公认的次优条件。
这似乎是因为clone-repo
作为deploy-to-prod
的一部分在第二次被检查时被跳过/修剪,所以似乎发生了(请参阅下面的-d
日志输出)。
为什么会发生这种情况,我可以改变这种行为吗?
$ cat Makefile
.PHONY: deploy-everywhere deploy-to-prod deploy-to-staging clone-repo
deploy-everywhere: deploy-to-staging deploy-to-prod
@echo "deployed everywhere"
deploy-to-prod: repo="prod-repo"
deploy-to-prod: clone-repo
@echo "deployed to prod (from $(repo))"
deploy-to-staging: repo="staging-repo"
deploy-to-staging: clone-repo
@echo "deployed to staging (from $(repo))"
clone-repo:
@echo "clone-repo $(repo)"
这里是运行make
的输出:
$ make deploy-everywhere
clone-repo staging-repo
deployed to staging (from staging-repo)
deployed to prod (from prod-repo) <<<<< THIS IS A LIE, WE NEVER CLONED THE PROD REPO!!
deployed everywhere <<<< RIIIIGHT.
额外日志:
$ make -d deploy-everywhere
...
Successfully remade target file `deploy-to-staging'.
Considering target file `deploy-to-prod'.
File `deploy-to-prod' does not exist.
Pruning file `clone-repo'. <<<< WHY? WHY?!?!?!?! WHY.
Finished prerequisites of target file `deploy-to-prod'.
Must remake target `deploy-to-prod'.
...
我正在运行GNU Make 3.81:
$ make --version
GNU Make 3.81
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
This program built for i386-apple-darwin11.3.0
这是设计使然。 Makefile中的目标更像名词而不是动词。
您可以通过几种方法实现所需的目标。这是一个:
deploy-to-prod: clone-prod-repo
@echo "deployed to prod (from prod-repo)"
deploy-to-staging: clone-staging-repo
@echo "deployed to staging (from staging-repo)"
clone-%-repo:
@echo "clone $*-repo"
EDIT:好,clone
操作采用多个参数。这是一种方法:
deploy-to-prod: PARAM1=ProdOne
deploy-to-prod: PARAM2=ProdTwo
deploy-to-prod: clone-prod-repo
@echo "deployed to prod (from prod-repo)"
deploy-to-staging: PARAM1=StagingFirst
deploy-to-staging: PARAM2=StagingSecond
deploy-to-staging: clone-staging-repo
@echo "deployed to staging (from staging-repo)"
clone-%-repo:
@echo clone $*-repo with $(PARAM1) and $(PARAM2)
这是另一个:
define clone-repo
@echo clone using $(1)
@echo and $(2) in some way
endef
deploy-to-prod:
@echo $@
$(call clone-repo, ProdOne, ProdTwo)
deploy-to-staging:
@echo $@
$(call clone-repo, StagingFirst, StagingSecond)
[为Beta的解决方案增加一些解释:make
规则的程序化契约是它是文件系统中永久实体的不透明更新,从而满足“小于”]](小于)在关联树中的关系。您的构造OTOH通过将其作为参数来从make
中隐藏依赖项。您试图规避的问题是make
中没有针对非文件系统存在的实体(在您的情况下是修订控制系统)的“小于”概念。我唯一的问题是,为什么您不简单地将clone
调用添加到每个deploy
目标-如果您确实想将其作为“小于”依赖项来解决(即,仅在本地存储库非-已存在或更旧),则需要进行一些make
编程。
在您的第一个评论后编辑:如果我说对了,您对PHONY
的想法就有点偏离了(不是全部,只是一点)。 PHONY
并不是要在不同的make
运行之间促进某些事情,它是在依赖关系树中产生短路的一种方法。给定一条链foo -> bar -> phony_baz
(foo取决于bar取决于phony_baz),在make
是主要目标的情况下,foo
将首先查看bar
以了解它是否比phony_baz
早,而phony_baz
本身具有make
隐式属性,当将其作为与目标的先决条件进行比较时,始终会产生“ younger_than”(现在是重要部分:) 依赖树。在树的其他分支中所有后续的phony_baz
遇到都将完全相反,从而在假目标处终止评估。这与依存关系图中任何其他节点的行为没有什么不同-make
假定我上面提到的合同已得到履行,并且没有采取重复行动的理由。这是clone-repo
虽不是.PHONY
,但第二次不触发的原因-您的设置实际上是有向无环图,其中两个边在同一节点处接合(这很好,只是值得注意),而不是树。 Beta的第一个解决方案是通过参数化规则动态创建两个完全不相关的节点,从而将形状返回到树中。