繁体   English   中英

当依赖项列表更改时重新链接 Makefile 目标

[英]Relink Makefile target when list of dependencies changes

我在 Makefile 中看到的一个常见问题是,当依赖项列表更改时,不一定要重新链接可执行文件(和库)。 例如:

SRCS=$(wildcard *.c)
CPPFLAGS=-MMD -MP
target: $(SRCS:.c=.o)
        $(CC) $(LDFLAGS) $^ -o $@
-include $(SRCS:.c=.d)

到目前为止,一切都很好:它会自动处理对源文件及其包含的依赖项的任何更改。 如果添加了一个文件(由于通配符,这意味着只需将其添加到目录中,但显式列表将具有相同的问题),make 会看到它并重新链接目标。 然而,当一个源文件被删除时,make 并不知道目标文件需要重建。

我如何让 make(特别是 gmake)来处理这种情况?

诚然,这并不完全是一种常见情况,因为删除文件很可能意味着也必须更改其他文件,这无论如何都会强制重新链接,但我以前见过这种情况。

我提出了两种解决方案,但都不是理想的。 首先,如果我们使源列表显式而不是通配符,我们可以使目标依赖于 Makefile。 更改 Makefile 以删除源文件会导致目标重新链接。 这有两个问题。 首先,您必须在链接之前从 $^ 中删除 Makefile,这有点难看(但可以通过过滤器删除)。 其次,这个 Makefile 旨在成为一个模板,包含在其他 Makefile 中(指定源和目标)——我们必须让目标依赖于那个Makefile。 啊。

第二种解决方案是包含一些类似如下的逻辑:

SRCS=$(wildcard *.c)
CPPFLAGS=-MMD -MP
target: $(SRCS:.c=.o)
        $(CC) $(LDFLAGS) $^ -o $@
        @echo '$$(if $$(filter-out $$(SRCS:.c=.o), $(SRCS:.c=.o)), .PHONY:target)' > target.d
-include $(SRCS:.c=.d)
-include target.d

这将生成一个 target.d 文件,用于跟踪依赖项列表并在删除任何内容时强制重建目标。 它有效,但它很丑陋。 它也不能解释由包含的 Makefile 指定的任何其他非源依赖项。

有没有官方的方法可以做到这一点,或者至少有更好的方法?

(顺便说一句,如果可能的话,我还想在删除源文件时清理关联的对象和依赖项文件。通过扩展我的第二个解决方案并使其更加丑陋,这可能是可能的。)

要正确地做到这一点,您可能不得不求助于Paul Smith 的 Advanced Auto-Dependency Generation Paul 是 GNU make 的当前维护者,并描述了所有与依赖生成相关的常见问题(及其解决方案!)。

还推荐了他网站上的其他白皮书。

我使用他的技巧和窍门已经好几年了,而且——即使它偶尔会变得有点复杂——研究它们每一秒都是值得的。

您需要更智能的 gmake 版本。 ElectricMake 是一个与 gmake 兼容的替代品,它包括一个分类帐功能,它基本上使最新检查比仅仅检查输出比输入新更复杂。 特别是,分类帐将散列目标的输入列表,如果该列表发生更改,它将导致重建输出:

# cat Makefile
out: a b c
        @echo out from $^
        @touch out
# emake --emake-ledger=timestamp
out from a b c
# emake --emake-ledger=timestamp
make: `out' is up to date.
# vi Makefile ;# Remove 'c' from the prereq list.
# cat Makefile
out: a b
        @echo out from $^
        @touch out
# emake --emake-ledger=timestamp
out from a b

如果你想尝试一下,你可以得到一个 eval copy

理想情况下,应该只有一个 make 模式规则来完成所有链接(好吧,一个用于可执行文件,一个用于共享库,一个用于存档)。 就像是:

# rules.mk
$(BUILD_DIR}/exe/% : rules.mk
    g++ -o $@ $(filter-out %.mk,$^)

接下来,当您定义构建目标时,使它们依赖于自己的 makefile:

# project_a.mk
$(BUILD_DIR}/exe/a : project_a.mk ${a_obj}

通常,此依赖项将包含在宏中:

define EXE_TARGET
  $(BUILD_DIR}/exe/${1} : ${2}
  $(BUILD_DIR}/exe/${1} : $(lastword $(MAKEFILE_LIST))
endef

所以在 project_a.mk 中它会做:

# project_a.mk
$(eval $(call EXE_TARGET,a,${a_obj}))

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM