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

时间:2015-11-07 23:08:46

标签: makefile gnu-make

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

我目前的尝试是:

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

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

$ make test
Inside explicit test target
Logging test target

相反,会发生的事情是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 :(得分:1)

我出现在你自己的答案中,这打败了我 您只关心触发目标的初步操作 在命令行上提到 - $(MAKECMDGOALS)。从我发布的帖子 你希望对#34; Makefile&#34;中的每个目标都进行这样的操作 将包括命令行目标的先决条件的所有目标,或者 如果没有命令行目标,则为默认目标。

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

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

这是一种解决问题的XY方式,因为make会参考模式 规则只有在您明确指出时才能找到制作目标的方法 食谱。例如,您知道make具有预定义的模式规则 从.o文件创建.c文件。即便如此,如果我的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依赖 本身。

追逐

您可以通过不同的方法实现您想要的目标。在一个实际的项目中 很可能在任何makefile中你都可以计算出一个列表 所有可能的目标。从这里你可以生成一个相应的列表 辅助目标,比如,目标 =&gt; target.prelim ,其中 target.prelim 的唯一目的是挑衅,当它应该和不应该 否则,目标所需的初步行动;你可以make 生成order-only rules目标的列表:| target.prelim , 对于每个目标,在确定 target 时不会考虑 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 :(得分:0)

我意识到这并没有按照要求回答我的问题,但它有我想要的效果 - 尽可能在MoveEntity(id, newPosition)处理后期执行shell命令。

Makefile

MYVAR?=foo .PHONY: test test: @echo 'Inside test target' LOG=$(shell echo 'Logging $(MAKECMDGOALS), myvar=$(MYVAR)' > log) .SECONDEXPANSION: force: $$(LOG) 是一个延迟变量,因此在Make评估LOG目标的先决条件列表之前不会展开。

在单个Makefile中,不需要force部分,因为在.SECONDEXPANSION:设置后评估force目标。

但是,如果我将MYVAR变量和LOG变量移动到子制作文件中,force行之前很容易include subMakefile - 这不会工作。
通过为MYVAR?=指定.SECONDEXPANSION,可以消除对订购的依赖。