在Makefile中找不到模式规则。

4
假设我有这个简约的 main.c 程序。
int main(void)
{
    return 0;
}

还有这个Makefile

.PHONY: %.run

%.run: %
    ./$<

如果我运行以下命令

make main.run

我希望能够从main.c文件中制作可执行文件(使用make的默认隐式规则%:%.c

  1. 运行main
  2. 成功退出

但是,我收到了以下错误信息

make: *** No rule to make target 'main.run'.  Stop.

我尝试禁用默认隐式规则,并使用以下设置自己的编译规则:
.SUFFIXES:

%: %.c
    $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $<

但这并没有帮助解决问题。

然而,在这两种情况下,如果我先运行make main,然后再运行make main.run,它就按预期工作。但运行两个命令正是我想通过我的%.run规则避免的。

当然,如果我用这个显式规则替换我的模式规则

main.run: main
    ./main

它正在工作。

另外,如果我将模式规则更改为%.x: %.c%.run: %.x,并使用可执行文件的.x后缀,这也可以工作,但是我不想要后缀。

当然,我的问题不是想知道在一个命令中编译和运行程序的正确方法,而是想知道为什么这个Makefile没有按照我期望的方式执行。


请展示没有隐式和“%”的工作版本。或者它也不起作用吗?顺便说一下,我没有看到任何东西在执行后删除主函数。 - Yunnosch
@Yunnosch,你说得对,我本来期望make会删除main文件,因为它是一个中间文件,但实际上并没有。然而,由于这不是主要问题,我根据你的评论将其从我的初始问题中删除了。我还添加了你要求的显式规则,当然是有效的。 - ovmjm
好问题。我可以在CentOS 5上重现。在你得到答案之后,也许你可以问一个关于rm main的后续问题,这发生在%.x版本中。 - Joseph Quinsey
我无法解释为什么这个makefile不能按照原样工作,但是如果你添加.INTERMEDIATE: main(至少在GNUMake 3.81中),它可以正常工作。 - Beta
如果你使用 gmake,你可以给你的源代码命名为可执行文件的名称(例如,foobar.c 对应可执行文件 foobar),并像这样使用 /dev/null 作为 makefile:make -f /dev/null foobar(完全依赖于隐式规则)。 - bobah
1个回答

1
似乎匹配任意规则对于先决条件也适用限制,尽管我找不到这个陈述1
如果可执行文件main已经存在,OP的makefile:
%.run: %
    ./$<

运行完美。解决问题的两种方法如下:

  • 添加一个明确的规则来构建main,或者
  • 添加一个目标为main的空规则;这将导致main被隐式模式匹配规则或明确规则构建。

我们提供四个解决方案。

1. 下面的makefile可行(在GNU make上测试过):

%.run: %
    ./$<

main:

这里的 main 单词是硬编码在 makefile 中的。您可以通过以下方式处理临时目标:

RUN_TARGETS = main.run foo.run bar.run $(filter %.run,$(MAKECMDGOALS))

%.run: %
    ./$<

$(RUN_TARGETS:.run=) dummy:

我们需要使用过滤器来确保类似于“make claen”这样的错别字返回错误信息。虚拟目标“dummy”用于避免空目标列表。
2. 另一种变化是使用静态模式规则,这似乎可以绕过此 bug/feature。
RUN_TARGETS = main.run foo.run bar.run $(filter %.run,$(MAKECMDGOALS))

$(RUN_TARGETS) dummy.run: %.run: %
    ./$<

3. 另一种方法是使用终端匹配规则,如果所有可执行文件都可以按照内置规则从单个C文件构建。这也会在运行后删除main,出于任何原因:

%.run: %
    ./$<

%:: %.c  # note double-colon, for terminal match-anything rule
    cc $< -o $@

4. 最后,完全解决问题的另一种方法是通过递归调用 make 来明确处理先决条件:

%.run:
    make $*
    ./$*

对于一些相关问题,请参考GNU make似乎忽略了中间文件的非终端通配规则强制make使用更具体的规则。请注意,将main作为一个无关规则的前提条件,例如:

%.run: %
    ./$<

garbage: main

似乎无法正常工作。

1 但是请参考Makefile匹配任何规则作为中间文件


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接