Makefile:具有非显式输出文件名的隐式规则会破坏规则匹配

Makefile: implicit rule with non-explicit output file name breaks rule matching

我现在的情况是我希望对多种文件类型进行全部预处理(如果它们的名称表明)。

例如:

main.c              # gets compiled normally by %.c rule
somethingelse.c.m4  # gets preprocessed, then dealt with by %.c rule
somethingother.h.m4 # gets preprocessed (then included by .c files)

我尝试了什么:

%: %.m4
  m4 "$<" > "$@"

但我收到错误消息:

make: *** No rule to make target 'somethingelse.o', needed by 'a.out'.  Stop.

如您所见,这在规则 after the m4 中失败了。

很确定它无法确定曾经有一个 .c 文件从此隐式规则生成,因此无法匹配 %.o: %.c 文件。

这当然有效:

%.c: %.c.m4
  m4 "$<" > "$@"
%.h: %.h.m4
  m4 "$<" > "$@"

但是在添加案例时它变得相当重复而且很快。

有什么方法可以让它与匹配这些文件的单个规则一起工作吗?

在有人告诉我 C 预处理器之前:这里的 .c/.h 文件只是一个“众所周知”/“最小”的例子,实际上我正在处理各种自定义文件格式,但适用完全相同的逻辑和“隐式规则链接”情况。

Pretty sure it can't figure out that there'd ever be a .c file generated from this implicit rule, thus is unable to match the %.o: %.c one.

不,我认为答案更简单。每 the manual,

for performance reasons make will not consider non-terminal match-anything rules (i.e., ‘%:’) when searching for a rule to build a prerequisite of an implicit rule

这正是你的情况。 somethingelse.c 文件未明确命名为任何规则的目标或先决条件;它仅被视为隐含规则的先决条件。您的 match-anything 规则不是最终规则,因此甚至不考虑构建该文件。

说明书对terminal vs. non-terminal match-anything rules说的比较多,但这里相关的要点是:

  • 通过使用双冒号(而不是单个冒号)将目标与先决条件分开,终端匹配任何规则与非终端规则区别开来,并且
  • 只有当所有先决条件都存在时,终端规则才适用。

所有先决条件都存在的要求对于问题中出现的情况不是问题,我发现切换到终端匹配任何规则都可以解决问题。例如,这个完整的 Makefile ...

OBJECTS = main.o somethingelse.o

foo: $(OBJECTS)
        $(CC) -o $@ $^

%:: %.m4
        m4 $< > $@

... 为我工作,从合适的 main.csomethingelse.c.m4.

构建 foo