制作:针对一组目标运行通用的前/后规则

时间:2018-10-08 13:52:10

标签: graph makefile dependencies

我想构建以下依赖关系图,但是prepost没有伪像:

make dependency graph

在创建/更新abc中的任何一个之前,命令pre应该运行一次,然后post应该运行一次。两者均不且最好不产生伪像。当然,如果a b c中的任何一个已更改,则应该仅运行。这应该全部由虚假的all目标触发,即a永远不会独立运行。

使用仅订购先决条件a: | pre无济于事,因为这些先后总是运行。使post依赖于a b c是行不通的,因为这样post并不会一直运行,因为pre不会创建工件。

如果这是不可能的,并且毕竟需要工件,那么a(两者中比较有趣的)如何仅在依赖于它的目标发生变化的情况下运行?

注意: a: a.in a.dependency @echo Creating a @mkabc a.in > a 等是正常的makefile目标(可以独立调用),例如:

<TargetFramework>netstandard2.0</TargetFramework>

2 个答案:

答案 0 :(得分:2)

只有一种方法可以强制在构建目标X之前执行命令,但是仅在需要构建目标X的情况下,才将命令作为目标X的配方中的第一件事。一种在GNU make中操作依赖关系图的方法,这样make可以确定是否需要构建目标,如果需要,可以在配方运行之前首先构建其他目标。

因此,您肯定在这里必须使用递归make,将用于构建pre目标的命令放入其他目标的配方中。但是,这当然会导致您不希望多次构建它。

解决该问题的一种方法是使用eval来耍花招。试试这个(未试用):

BUILD_PRE = $(shell $(MAKE) -j1 pre >/dev/null)

post: a b c
        echo $@

pre:
        echo $@

a b c:
        $(BUILD_PRE)$(eval BUILD_PRE =)
        touch $@

.PHONY: pre post

abc的规则中,我们首先扩展BUILD_PRE变量,该变量通过shell调用进行递归生成调用。然后eval扩展将重置BUILD_PRE的值,使其为空;这意味着在bc的后续规则中,第一行将扩展为空字符串,并且pre将不再运行。

您可能会问,为什么我们在这里需要使用shell?我们不能只使用:

BUILD_PRE = $(MAKE) -j1 pre

使第一个配方包含递归标记吗?问题是它不能与并行make一起使用。假设要构建的第一个目标尝试是a(当然,它将始终如此)。该配方将包含一个递归的make调用,make将启动它。但是,如果您使用的是-j,make不会等待该配方完成:它将尝试尝试启动bc。由于BUILD_PRE现在为空,您只能获得pre的一个内部版本,但是bc并不等待pre完成。

通过使用shell函数,在展开任何其他配方之前,展开配方时将强制完成递归调用。

我应该说,我怀疑这可能有一些奇怪的事情。特别是当make 通常调用递归构建时,它会进行一些设置等操作,而通过shell调用递归构建时不会发生。但是,它应该可以工作。

编辑:带有“ +”前缀的最终Makefile,用于标记递归调用:

all: allabc

BUILD_PRE = $(shell $(MAKE) pre)
BUILD_POST =

pre:
    @echo PRE abc >&2

post:
    @echo POST abc >&2

allabc: a b c
    @+$(BUILD_POST) > /dev/null

a:
    +$(BUILD_PRE)$(eval BUILD_PRE = )
    touch "$@"
    $(eval BUILD_POST = $$(MAKE) post)
b:
    +$(BUILD_PRE)$(eval BUILD_PRE = )
    touch "$@"
    $(eval BUILD_POST = $$(MAKE) post)
c:
    +$(BUILD_PRE)$(eval BUILD_PRE = )
    touch "$@"
    $(eval BUILD_POST = $$(MAKE) post)

clean:
    rm -f a b c

答案 1 :(得分:1)

不确定我是否了解所有详细信息,但是假设您想在调用make all时构建5个目标,并显示所显示的依赖关系(也许还有ab和{{1 }}),例如:

c