如何实现一个记忆上次构建目标的Makefile?

6

假设您有一个Makefile文件,其中包含两个伪目标'all'和'debug'。'debug'目标的目的是构建与'all'相同的项目,但使用了一些不同的编译开关(例如-ggdb)。由于这些目标使用了不同的编译开关,如果在它们之间切换,您显然需要重新构建整个项目。但GNUmake并不会自动识别这一点。

因此,如果您键入make all,您将会得到:

Building ...
...

如果你输入 make debug, 你将会得到以下结果。
make: Nothing to be done for `debug'.

所以我的问题是:如何在Makefile中实现一个干净的解决方案来注意到上次构建使用了不同的伪目标或不同的编译开关,而不是您当前想要的那个?如果它们不同,则Makefile会重新构建所有内容。

3个回答

3
将构建产品放入不同的目录树中(当然要保留源代码的一份副本)。这样,您总是可以进行短时间编译以获得最新的构建版本,无论是调试版还是发布版(甚至其他版本)。也不会有混淆的可能性。 编辑: 以上是其大致内容的简述。
src := 1.c 2.c 3.c
bare-objs := ${src:%.c=%.o}
release-objs := ${bare-objs:%=Release/%}
debug-objs := ${bare-objs:%=Debug/%}

Release/prog: ${release-objs}
Debug/prog: ${debug-objs}

${release-objs}: Release/%.o: %.c # You gotta lurve static pattern rules
    gcc -c $< -o $@

${debug-objs}: Debug/%.o: %.c
    gcc -c $< -o $@

Release/prog Debug/prog:
    gcc $^ -o $@

.PHONY: all
all: Release/prog ; echo $@ Success

.PHONY: debug
debug: Debug/prog ; echo $@ Success

(免责声明:未经测试,甚至没有运行过make命令。)

好了,现在你可以安全地使用-j参数,例如make -j5 all debug。有许多明显的样板代码等待整理。


相当简单。我可以想象这在大多数情况下都可行。如果可能的话,我仍然更喜欢只构建一个目标。 - Elliot Cameron
@Elliot:在这种情况下,当构建“debug”目标时,首先删除“all”目标。这样做比在树中有两个目标之一更少出错。你确定自己足够了解树当前持有哪个目标吗? - bobbogo
我发现使用递归make调用是实现这一功能的最简单方法。例如: debug: @$(MAKE) all TARGET_DIR="$(DEBUG_DIR)" FLAGS="-ggdb" - Elliot Cameron
@Elliot: 我不是递归make的粉丝,原因有很多。总有其他机制(目标特定变量 debug: TARGET_DIR := debugdir/;或者间接引用 TARGET-debug := debugdir/ 然后在配方中使用 ${TARGET-$@}(我更喜欢后一种表示方法))。 - bobbogo
@bobbogo:我在实现这两个选项时遇到了困难。 我的调试目标仅为debug:$(OUT_FILE),因为它依赖于一个依赖于一堆对象文件的可执行文件。 为“debug”设置特定于目标的变量似乎不起作用,而且我不确定如何对许多不同目标进行间接操作。 - Elliot Cameron
@Elliot:好的,我已经添加了一个草图。你还是想只构建一个目标吗? - bobbogo

2

保留不同的对象文件集合(如bobbogo的解决方案)可能是最好的方法,但如果出于某种原因您不想这样做,您可以使用空文件作为标记,以指示您上次构建可执行文件的方式:

%-marker:
        @rm -f $(OBJECTS) *-marker
        @touch $@

debug: GCCFLAGS += -ggdb

debug: SOMEOTHERFLAG = WHATEVER

all debug: % : %-marker
        @echo making $@
        @$(MAKE) -S GCCFLAGS='$(GCCFLAGS)' SOMEOTHERFLAG='$(SOMEOTHERFLAG)' main

这个想法还有其他变体;你可以有一个包含标志设置的小文件,让 makefile 构建并 include。那很聪明,但实际上并不比这更干净。


哇...太棒了,聪明绝顶。我得研究一下那些Makefile语法! - Elliot Cameron

1
唯一干净的解决方案是将差异合并到目标名称中。例如,您可以定义一个变量 $(DEBUG),并在所有依赖于编译步骤的目标中始终使用它。

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