我有两个目标foo
和bar
。既不依赖于另一个,但如果必须重建bar
,则必须在foo
之前完成。它们是gnu-make所谓的虚假目标,它们的规则总是在指定时执行。
目前,我们表达了一个主要目标,它依赖于这两个:
# user level targets
all: bar
@$(MAKE) foo
@echo all
alt: foo
@echo alt
# internal targets
foo:
@echo foo
bar: qux
@echo bar
qux:
@echo qux
@touch qux
我们有必要的行为:如果qux不是最新的:make bar
输出qux bar foo all
(按此顺序)和make alt
输出foo alt
;如果qux是最新的,make bar
输出bar foo all
和make alt
输出foo alt
。
这越来越不舒服,因为foo
必须专门处理(所有依赖于两者的目标必须以这种方式处理,foo
不能放在描述依赖关系的变量中bar
也存在,submake本身就是一个问题,必须维护命令行以传递其他变量)。我们现在有另一个目标必须以同样的方式处理,我正在寻找其他更方便的方法来处理结构。
注1:在实践中,我目前只使用gnu-make,但是对POSIX上的gnu-make扩展唯一已知的依赖是包含文件的可能性(可以广泛使用)。我更喜欢保持当前状态的东西(即广泛支持的构造),但如果不可能,只使用gnu-make扩展是可以接受的。
注2:gnu-make有一个 order-only-prerequisites 的概念,但它显然没有提供我们需要的东西。与
# user level targets
all: bar foo
@echo all
alt: foo
@echo alt
# internal targets
foo: | bar
@echo foo
bar:
@echo bar
make alt
也构建bar
(如果文件bar
存在,其日期不会影响重建foo
的决定,这是记录的行为)
注3:我想的越多,我认为在不使用递归调用的情况下使用make
解决此问题的可能性就越小。在我看来,它需要依赖图上的两个传递,一个用于确定必须构建的内容,一个用于确定排序,而我在make
行为中不知道任何事情都无法完成传递算法。
答案 0 :(得分:1)
基本上,您可以运行make -d -n
加上命令参数。输出将包含多行,如Must remake target 'clean'
。此信息会告诉您此次 make 是否会尝试同时构建foo
和 bar
。如果情况确实如此,只需添加一条规则即可实现所需的序列化。
草图:
this := $(lastword ${MAKEFILE_LIST})
ifndef DONTRECURSE
targets-that-will-get-remade := $(patsubst %',%,$(shell ${MAKE} -f ${this} ${MAKECMDGOALS} --debug=b -n DONTRECURSE=nosiree | grep -Po "Must remake target '\K.*'"))
endif
ifeq (bar foo,$(sort $(filter bar foo,${targets-that-will-get-remade})))
foo: bar
endif
.PHONY: foo bar
foo bar:
sleep 3
: $@
所以,你运行 make 。 DONTRECURSE
未设置,因此$(shell …)
运行。它使用相同的makefile和目标第二次运行 make ,但添加了-d
(调试)和-n
(实际上不运行配方)标志。 DONTRECURSE
设置为阻止 make 的第三个副本运行。
扩展所有这一系列make的目标列表将尝试在此次运行中构建。 (提取目标名称非常烦人 - 可能有一种更清洁的方式。)
如果此目标列表同时包含foo
和bar
,则只需添加foo: bar
依赖项。任务完成。使用sleep 3
(例如)时,-j4
行显示此序列化正常工作。