24

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

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

hugefileB:
    touch $@

hugefileC:
    touch $@

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

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

有没有办法告诉'make'避免重新编译依赖项?

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

4

5 回答 5

32

使用GNU Make 的“中间文件”功能:

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

第一个区别是如果中间文件不存在会发生什么。如果一个普通文件 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.
于 2012-08-30T15:03:58.370 回答
3

如果将hugefileB和标记hugefileC中间文件,您将获得所需的行为:

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 会在构建结束时自动删除中间文件。

于 2012-08-30T15:04:36.407 回答
1

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

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

于 2012-08-30T14:40:13.587 回答
0

另请参阅.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 $@

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

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

于 2020-01-06T18:40:03.997 回答
-2

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

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

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

于 2012-08-30T14:46:50.637 回答