简体   繁体   English

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

[英]Relink Makefile target when list of dependencies changes

A common problem I've seen with Makefiles is that executables (and libraries, for that matter) aren't necessarily relinked when the list of dependencies changes.我在 Makefile 中看到的一个常见问题是,当依赖项列表更改时,不一定要重新链接可执行文件(和库)。 For example:例如:

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

So far, so good: it automatically handles any changes to source files and their included dependencies.到目前为止,一切都很好:它会自动处理对源文件及其包含的依赖项的任何更改。 If a file is added (because of the wildcard, that means just adding it to the directory, but an explicit list would have the same problem), make sees it and relinks the target.如果添加了一个文件(由于通配符,这意味着只需将其添加到目录中,但显式列表将具有相同的问题),make 会看到它并重新链接目标。 However, when a source file is removed, make doesn't know that the target needs to be rebuilt.然而,当一个源文件被删除时,make 并不知道目标文件需要重建。

How do I get make (gmake, specifically) to handle this situation?我如何让 make(特别是 gmake)来处理这种情况?

Admittedly, it's not exactly a common situation, as removing a file will most likely mean that other files must be changed too, which would then force a relink anyway, but I've seen it happen before.诚然,这并不完全是一种常见情况,因为删除文件很可能意味着也必须更改其他文件,这无论如何都会强制重新链接,但我以前见过这种情况。

I've come up with two solutions, neither of which is ideal.我提出了两种解决方案,但都不是理想的。 First, if we make the source list explicit instead of wildcarded, we can make the target depend on the Makefile.首先,如果我们使源列表显式而不是通配符,我们可以使目标依赖于 Makefile。 Changing the Makefile to remove a source file then causes the target to be relinked.更改 Makefile 以删除源文件会导致目标重新链接。 This has two problems.这有两个问题。 First, you have to strip out Makefile from $^ before linking, which is a bit ugly (but do-able with filter-out).首先,您必须在链接之前从 $^ 中删除 Makefile,这有点难看(但可以通过过滤器删除)。 Second, this Makefile is intended to be a template, included by other Makefiles (which specify the sources and the target) -- we have to make the target depend on that Makefile instead.其次,这个 Makefile 旨在成为一个模板,包含在其他 Makefile 中(指定源和目标)——我们必须让目标依赖于那个Makefile。 Ugh.啊。

The second solution is to include some logic like the following:第二种解决方案是包含一些类似如下的逻辑:

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

This makes a target.d file that tracks the list of dependencies and forces the target to be rebuilt if anything is removed.这将生成一个 target.d 文件,用于跟踪依赖项列表并在删除任何内容时强制重建目标。 It works, but it's ugly.它有效,但它很丑陋。 It also can't account for any additional non-source dependencies specified by an including Makefile.它也不能解释由包含的 Makefile 指定的任何其他非源依赖项。

Is there an official way to do this, or at least a better way?有没有官方的方法可以做到这一点,或者至少有更好的方法?

(As an aside, I'd also like to clean up the associated object and dependency files when the source file is removed, if possible. That might be possible by extending my second solution and making it even uglier.) (顺便说一句,如果可能的话,我还想在删除源文件时清理关联的对象和依赖项文件。通过扩展我的第二个解决方案并使其更加丑陋,这可能是可能的。)

To do it properly, you'll probably have to resort to Paul Smith's Advanced Auto-Dependency Generation .要正确地做到这一点,您可能不得不求助于Paul Smith 的 Advanced Auto-Dependency Generation Paul is the current maintainer of GNU make, and has described all the common problems (and their solutions!) concerning dependency generation. Paul 是 GNU make 的当前维护者,并描述了所有与依赖生成相关的常见问题(及其解决方案!)。

The other white-papers on his website are recommended as well.还推荐了他网站上的其他白皮书。

I use his tips and tricks for several years now, and - even if it gets complicated a little occasionally - studying them has been worth every second.我使用他的技巧和窍门已经好几年了,而且——即使它偶尔会变得有点复杂——研究它们每一秒都是值得的。

You need a smarter version of gmake.您需要更智能的 gmake 版本。 ElectricMake is a gmake-compatible replacement that includes a ledger feature that basically makes the up-to-date check more sophisticated than just checking that the output is newer than the inputs. ElectricMake 是一个与 gmake 兼容的替代品,它包括一个分类帐功能,它基本上使最新检查比仅仅检查输出比输入新更复杂。 In particular, the ledger will hash the list of inputs for the target and if that list changes, it will cause the output to be rebuilt:特别是,分类帐将散列目标的输入列表,如果该列表发生更改,它将导致重建输出:

# 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

If you want to give it a try, you can get an eval copy .如果你想尝试一下,你可以得到一个 eval copy

Ideally, the should be only one make pattern rule to do all the linking (okay, one for executables, one for shared libraries and one for archives).理想情况下,应该只有一个 make 模式规则来完成所有链接(好吧,一个用于可执行文件,一个用于共享库,一个用于存档)。 Something like:就像是:

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

Next, when you define build targets make them depend on its own makefile:接下来,当您定义构建目标时,使它们依赖于自己的 makefile:

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

Normally, this dependency would be wrapped in a macro:通常,此依赖项将包含在宏中:

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

so in project_a.mk it'd do:所以在 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