【问题标题】:telling 'make' to ignore dependencies when the top target has been created在创建顶部目标时告诉“make”忽略依赖项
【发布时间】:2023-03-20 09:53:01
【问题描述】:

我正在运行以下类型的管道:

digestA: hugefileB hugefileC
    cat $^ > $@
    rm $^

hugefileB:
    touch $@

hugefileC:
    touch $@

目标 hugefileBhugefileC 非常大,需要很长时间来计算(并且需要 Make 的强大功能)。但是一旦创建了 digestA,就无需保留其依赖项:它会删除这些依赖项以释放磁盘空间。

现在,如果我再次调用 'make',hugefileBhugefileC 将被重建,而 digestA 已经可以了。

有什么方法可以告诉'make'避免重新编译依赖项?

注意:我不想在“digestA”的规则中构建两个依赖项。

【问题讨论】:

  • 您只想在digestA 不存在时重建它,对吗?
  • @Beta "只有在 digestA 不存在时才想重建它":是的

标签: makefile gnu-make


【解决方案1】:

使用 GNU Make 的"intermediate files" 功能:

中间文件使用它们的规则重新制作,就像所有其他文件一样。但是中间文件有两种不同的处理方式。

第一个区别是如果中间文件不存在会发生什么。如果一个普通文件 b 不存在,并且 make 考虑一个依赖于 b 的目标,它总是创建 b 然后从 b 更新目标。 但如果 b 是一个中间文件,那么 make 就可以不用管它了。它不会打扰更新 b 或最终目标,除非 b 的某些先决条件比该目标更新或有其他原因更新该目标。

第二个区别是,如果 make 确实创建 b 是为了更新其他内容,它会在不再需要 b 后删除它。 因此,在 make 之前不存在的中间文件在 make 之后也不存在。 make 通过打印 rm -f 命令向您报告删除了它正在删除的文件。

通常,如果在 makefile 中将文件作为目标或先决条件提及,则该文件不能是中间文件。 但是,您可以通过将文件列为特殊目标 .INTERMEDIATE 的先决条件来将文件显式标记为中间文件。即使以其他方式明确提及该文件,这也会生效。

您可以通过将中间文件标记为辅助文件来防止自动删除它。 为此,请将其列为特殊目标.SECONDARY 的先决条件。当一个文件是次要文件时,make 不会仅仅因为该文件不存在就创建它,但 make 不会自动删除该文件。将文件标记为次要文件也将其标记为中间文件。

因此,在 Makefile 中添加以下行就足够了:

.INTERMEDIATE : hugefileB hugefileC

第一次调用make:

$ make
touch hugefileB
touch hugefileC
cat hugefileB hugefileC > digestA
rm hugefileB hugefileC

还有下一次:

$ make
make: `digestA' is up to date.

【讨论】:

  • 非常感谢。这是非常有帮助的。此处引用了您的解决方案:plindenbaum.blogspot.com/2012/08/…
  • 您是否了解为什么 .secondary 不是所有目标的默认行为?
  • @Pierre,嗯,听起来很有趣,但经过一番思考,我无法回答你的问题。手册中唯一稍微相关的部分says:“.SECONDARY 没有先决条件会导致所有目标都被视为次要目标(即,没有目标被删除,因为它被认为是中间目标)。”但它并没有说明默认行为背后的原因。
【解决方案2】:

如果您将hugefileBhugefileC 标记为intermediate files,您将获得您想要的行为:

digestA: hugefileB hugefileC
        cat $^ > $@

hugefileB:
        touch $@

hugefileC:
        touch $@

.INTERMEDIATE: hugefileB hugefileC

例如:

$ gmake
touch hugefileB
touch hugefileC
cat hugefileB hugefileC > digestA
rm hugefileB hugefileC
$ gmake
gmake: `digestA' is up to date.
$ rm -f digestA
$ gmake
touch hugefileB
touch hugefileC
cat hugefileB hugefileC > digestA
rm hugefileB hugefileC

请注意,您不再需要显式的rm $^ 命令——gmake 会在构建结束时自动删除中间文件。

【讨论】:

    【解决方案3】:

    我建议您创建由hugefileBhugeFileC 目标创建的伪缓存文件。

    然后让digestA 依赖这些缓存文件,因为您知道它们不会再次更改,直到您手动调用昂贵的目标。

    【讨论】:

      【解决方案4】:

      另见.PRECIOUS

      .PRECIOUS : hugefileA hugefileB
      

      .珍贵

      .PRECIOUS 所依赖的目标被给予以下特殊处理:如果 make 在其配方执行期间被杀死或中断,则不会删除目标。请参阅中断或终止 make。此外,如果目标是中间文件,则在不再需要后不会像通常那样将其删除。请参阅隐式规则链。在后一方面,它与 .SECONDARY 特殊目标重叠。

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

      编辑:在重新阅读问题时,我发现您不想保留大文件;也许这样做:

      digestA : hugefileA hugefileB
          grep '^Subject:' %^ > $@
          for n in $^; do echo > $$n; done
          sleep 1; touch $@
      

      它在使用之后截断大文件,然后在一秒钟后触及输出文件,只是为了确保输出比输入更新,并且在删除空的大文件之前,此规则不会再次运行。

      不幸的是,如果只删除摘要,则运行此规则将创建一个空摘要。您可能需要添加代码来阻止它。

      【讨论】:

        【解决方案5】:

        正确的做法是不删除文件,因为这样会删除make用来确定是否重建文件的信息。

        将它们重新创建为空文件并没有帮助,因为make 将假定空文件已完全构建。

        如果有一种方法可以合并摘要,那么您可以从每个大文件中创建一个,然后将其保留,然后自动删除大文件,因为它是一个中间文件。

        【讨论】:

        • 问题明确指出,由于文件很大,提问者不希望删除这些文件。 (PS,我认为从提问者说“我正在运行以下类型的管道”这一事实来看,这意味着提供的代码只是示例代码,而提问者实际上并不是在创建空文件。)
        猜你喜欢
        • 1970-01-01
        • 2014-07-28
        • 2014-11-27
        • 2019-08-13
        • 1970-01-01
        • 2011-06-20
        • 2017-11-11
        • 2011-04-04
        • 1970-01-01
        相关资源
        最近更新 更多