首页 文章

在显式规则之前匹配模式规则

提问于
浏览
0

我试图在Makefile中为每个目标添加一些行为,而不修改目标 .

我目前的尝试是这样的:

%: $*
     @echo 'Logging $* target'
.PHONY: test
test:
     @echo 'Inside explicit test target'

当我运行 make test 时,我想匹配 % 模式规则,它将执行 test 作为先决条件( $* 扩展到模式词干),然后记录运行的目标 .

$ make test
Inside explicit test target
Logging test target

Instead ,会发生的事情是 make test 与显式的 test 目标相匹配(可能是因为它是一个更接近的匹配):

$ make test
Inside explicit test target

如何在不更改显式测试目标的情况下实现此功能?

编辑:另一次尝试......

.SECONDEXPANSION:
%: $$*
     @echo 'Logging $* target'

结果是

$ make test
make: Circular Makefile <- Makefile dependency dropped.
inside actual test target

2 回答

  • 0

    我出现在你自己的答案中,这让我击败了你,你只关心触发对命令行提到的目标的初步行动 - $(MAKECMDGOALS) . 从帖子中我得到了你想要"every target in a Makefile"的这样一个动作,其中包括命令行目标先决条件的所有目标,或者如果没有命令行目标,则包括默认目标 .

    无论如何,您可能仍然对解决更普遍的问题感兴趣 .

    您希望在每个目标的配方之前执行初步操作 . 您的问题是:如何在明确规则之前匹配模式规则?

    这是一种解决问题的XY方式,因为 make 只会在没有给出明确的配方的情况下查阅模式规则以找到制作目标的方法 . 例如,您知道 make 有一个预定义的模式规则,用于从 .c 文件生成 .o 文件 . 即便如此,如果我的makefile是:

    test.o:
        @echo $@
    

    然后 make 打印 test.o ,没有任何尝试找到 test.c 并编译它 . 如果我的make文件是:

    test.o: test.c
        @echo $@
    
    test.c:
        @echo $@
    

    然后 make 打印:

    test.c
    test.o
    

    不需要诉诸模式规则 . 但是如果我的makefile是:

    test.o: test.c
    

    然后 make 说:

    make: *** No rule to make target 'test.c', needed by 'test.o'. Stop
    

    所以你可以按照你的问题所假设的方式重新开始,因为只有当目标没有其他动作时,才能激发你想要从模式规则中激发的初步动作 .

    在这种情况下,两次发布尝试失败的原因是相当学术性的,您可能希望滚动到 The Chase .

    在您的第一次尝试中,使用:

    %: $*
        @echo 'Logging $* target'
    

    模式规则 - 由 make test 失业 - 相当于:

    %:
        @echo 'Logging $* target'
    

    因为 $* 仅假定配方中的值,而不是模式规则中的值 . 您可以通过制作makefile不提供配方的任何目标来使用此模式规则,例如, make nonsuch 将打印 Logging nonsuch target ;但那是没用的 .

    第二次尝试,包括:

    .SECONDEXPANSION:
    %: $$*
         @echo 'Logging $* target'
    

    创建您打算创建的规则是否正确 . 但该规则的含义是:

    <target>: <target>
         @echo 'Logging <target> target'
    

    使这条规则适用的每个目标成为其自身的先决条件 . 这将不可避免地导致所有此类目标的循环依赖性错误 . 如您所见,此循环不会影响您的 test 目标,因为它具有明确的配方并且不使用该规则 . 但它确实引发了令人惊讶的错误:

    make: Circular Makefile <- Makefile dependency dropped.
    

    这是因为 make 自动考虑的第一个目标是makefile本身 . 与 test 目标不同,您没有makefile的配方;所以模式规则适用于它,使makefile依赖于它自己 .

    The Chase

    您可以通过不同的方法实现您想要的目标 . 在实际项目中,很可能在任何makefile中都可以计算所有可能目标的列表 . 从这里你可以生成一个相应的辅助目标列表,比如target => target.prelim,其中target.prelim的唯一目的是激发目标所需的初步行动,当它应该而不是其他目的时;你可以得到 make 来生成一个order-only rules的列表,目标:| target.prelim,对于每个目标,这样在确定是否必须进行目标时不会考虑target.prelim,而是在需要进行目标时在目标之前进行 .

    这是一个例子:

    SRCS := main.c foo.c
    OBJS := $(SRCS:.c=.o)
    TARGETS := all prog $(OBJS)
    PRELIMS := $(patsubst %,%.prelim,$(TARGETS))
    
    define prelim_rule =
    $(1): | $(1).prelim
    endef
    
    $(foreach target,$(TARGETS),$(eval $(call prelim_rule,$(target))))
    
    .PHONY: all
    
    all: prog
    
    prog: $(OBJS)
        $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
    
    clean:
        rm -f $(OBJS) $(PRELIMS) prog
    
    %.prelim:
        @echo "Logging target $(@:%.prelim=%)"
        @touch $@
    

    一个示例会话:

    $ make
    Logging target all
    Logging target main.o
    cc    -c -o main.o main.c
    Logging target foo.o
    cc    -c -o foo.o foo.c
    Logging target prog
    cc    -o prog main.o foo.o 
    $ make
    make: Nothing to be done for 'all'.
    $ make clean
    rm -f main.o foo.o all.prelim prog.prelim main.o.prelim foo.o.prelim prog
    $ make main.o
    Logging target main.o
    cc    -c -o main.o main.c
    $ make main.o
    make: 'main.o' is up to date.
    $ # A prelim can't out-date its target...
    $ touch main.o.prelim
    $ make main.o
    make: 'main.o' is up to date.
    
  • 1

    我意识到这并没有按照我的要求回答我的问题,但它具有我想要的效果 - 尽可能在 Makefile 处理中执行 shell 命令 .

    MYVAR?=foo
    .PHONY: test
    test:
        @echo 'Inside test target'
    
    LOG=$(shell echo 'Logging $(MAKECMDGOALS), myvar=$(MYVAR)' > log)
    .SECONDEXPANSION:
    force: $$(LOG)
    

    LOG 是一个延迟变量,因此在Make评估 force 目标的先决条件列表之前不会展开 .

    在一个单一的Makefile,不需要 .SECONDEXPANSION: 部分,因为 force 目标在 MYVAR 设置后进行评估 .

    但是,如果我将 LOG 变量和 force 变量移动到子makefile中,那么在 MYVAR?= 行之前很容易 include subMakefile - 这是行不通的 .
    通过为 force 指定 .SECONDEXPANSION ,将删除对订购的依赖 .

相关问题