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

发布于 2024-11-26 20:23:20 字数 1176 浏览 0 评论 0原文

我在 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 指定的任何其他非源依赖项。

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

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

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. 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. However, when a source file is removed, make doesn't know that the target needs to be rebuilt.

How do I get make (gmake, specifically) to handle this situation?

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. Changing the Makefile to remove a source file then causes the target to be relinked. 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). 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. 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. It works, but it's ugly. It also can't account for any additional non-source dependencies specified by an including 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.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

倾城月光淡如水﹏ 2024-12-03 20:23:20

为了正确地做到这一点,您可能必须求助于 Paul Smith 的高级自动依赖生成。 Paul 是 GNU make 的当前维护者,并且描述了有关依赖项生成的所有常见问题(及其解决方案!)。

他的网站上的其他白皮书也被推荐。

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

To do it properly, you'll probably have to resort to Paul Smith's 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.

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.

后eg是否自 2024-12-03 20:23:20

您需要一个更智能的 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

如果您想尝试一下,您可以 获取评估副本

You need a smarter version of 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. 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.

暖风昔人 2024-12-03 20:23:20

理想情况下,应该只有一项 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}))

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). 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:

# 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
$(eval $(call EXE_TARGET,a,${a_obj}))
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文