gnu make“删除中间文件”

Posted

技术标签:

【中文标题】gnu make“删除中间文件”【英文标题】:gnu make "Removing intermediate files" 【发布时间】:2018-05-06 22:33:10 【问题描述】:

我有以下规则

define compile_c
$(ECHO) "CC $<"
$(Q)$(CC) $(CFLAGS) -c -MD -o $@ $<
@# The following fixes the dependency file.
@# See http://make.paulandlesley.org/autodep.html for details.
@# Regex adjusted from the above to play better with Windows paths, etc.
@$(CP) $(@:.o=.d) $(@:.o=.P); \
  $(SED) -e 's/#.*//' -e 's/^.*:  *//' -e 's/ *\\$$//' \
      -e '/^$$/ d' -e 's/$$/ :/' < $(@:.o=.d) >> $(@:.o=.P); \
  $(RM) -f $(@:.o=.d)
endef

vpath %.c . $(TOP)
$(BUILD)/%.o: %.c $(BUILD)/%.pp
    $(call compile_c)

vpath %.c . $(TOP)

$(BUILD)/%.pp: %.c
    $(ECHO) "PreProcess $<"
    $(Q)$(CC) $(CFLAGS) -E -Wp,-C,-dD,-dI -o $@ $<

构建完成后,GNU make 会显示Removing intermediate files... 并删除我确实想要的所有.pp 文件。

为什么要这样做? 我该如何阻止它?

【问题讨论】:

您用于生成依赖项的 URL 已过时,可能会在某个时候消失。您应该使用正确的 URL:make.mad-scientist.net/papers/… 【参考方案1】:

由于您使用的是 GNU Make,您可以对您的 Makefile 进行以下调整:

.PRECIOUS: $(BUILD)/%.pp  # ADD THIS LINE
$(BUILD)/%.pp: %.c
    $(ECHO) "PreProcess $<"
    $(Q)$(CC) $(CFLAGS) -E -Wp,-C,-dD,-dI -o $@ $<

documentation 对.PRECIOUS 指令有这样的说法:

.PRECIOUS 所依赖的目标被给予以下特殊处理:如果 make 在其配方执行期间被杀死或中断,则不会删除目标。

[...]

另外,如果目标是一个中间文件,它在不再需要后不会被删除,就像通常所做的那样。

[...]

您还可以将隐式规则的目标模式(例如“%.o”)列为特殊目标 .PRECIOUS 的先决条件文件,以保留由目标模式与该文件名匹配的规则创建的中间文件。

这样做的好处是不会创建不需要的附加规则。您想要做的事情也更清楚:保留重新创建可能很昂贵的珍贵中间文件。

【讨论】:

我个人会避免使用.PRECIOUS,除非它真的是你想要的。它当然不应该用作上述答案中声明目标的随意替代品。仔细考虑文档所说的内容:如果 make 被杀死或中断,则不会删除目标。如果您正在编译一个大文件并使用^C 怎么办?这会中断 make 并且也会中断你的编译器。一些编译器会清理一半创建的目标,但其他编译器不会。由于半创建的目标具有较新的时间戳,因此不会重新创建它。这可能是一个很难找到的问题。 .SECONDARY 比 .PRECIOUS 好!【参考方案2】:

如果您搜索“gnu make intermediate files”,您会立即在 GNU make 手册部分 Chains of Implicit Rules 中找到有关其发生原因的答案。

它还告诉您如何避免它:如果在 makefile 中将文件作为目标或先决条件提及,则该文件不能是中间文件。

因此,只需在某处将您的 .pp 文件列为某些规则的先决条件即可。它不一定是曾经被调用过的规则。您在这里没有提供足够的 makefile,我们无法提供完整的答案,但它会是这样的:

all_pps: $(ALL_OBJECTS:.o=.pp)

假设你有一个变量 ALL_OBJECTS 包含你所有的 .o 文件。

【讨论】:

如果我搜索“gnu make intermediate files”。我找到了这个帖子。该帖子本身保证了它在 Google 上的成功! all_pps: $(ALL_OBJECTS:.o=.pp) 不起作用,因为只考虑最后一个 .pp 文件! 也许我不明白@NickHuang 的评论,但据我所知这是不正确的。【参考方案3】:

我认为最好的解决方案是使用.SECONDARY 特殊目标。只需添加这一行:

.SECONDARY:

引用manual:

.SECONDARY 没有先决条件会导致所有目标都被视为次要目标(即,没有目标被删除,因为它被认为是中间目标)。

为什么这比将目标作为一次性目标的先决条件更好?这更加混乱,并且必须为可能使用模式规则生成的每组文件明确地完成。

为什么这比.PRECIOUS 更好?这会导致文件在使用.DELETE_ON_ERROR 时被保留,即使它们的配方失败。后者对于避免失败的配方很重要,因为这些配方会留下错误的输出,然后被后续的make 调用视为当前的。 IMO,你总是想要.DELETE_ON_ERROR,但.PRECIOUS 打破了它。

【讨论】:

【参考方案4】:

这是一个最终让 PRECIOUS 为我工作的细节。您提供给 PRECIOUS 的模式必须是完全在创建中间文件的规则中使用的模式。我想保存所有以 moc_ 为前缀的文件。一开始我用 .PRECIOUS: moc_% 无济于事。然后我尝试了 .PRECIOUS: moc_%.cpp 这成功了。

【讨论】:

以上是关于gnu make“删除中间文件”的主要内容,如果未能解决你的问题,请参考以下文章

GNU Make 和通配符 - 缺少文件

GNU Make - 尾随“/”被删除?

使用 GNU make 编译启用配置文件的包

GNU make 如何管理文件版本控制?

GNU make:规则专题

GNU Make:%与虚线文件名不匹配?