仅当其依赖项实际更改时才重新编译的 Makefile

Bee*_*ope 4 makefile gnu-make

假设我有一个文件,它不是基于文件系统中的其他文件生成的,而是基于某些外部命令生成的,其输出可能会或可能不会更改。我可以在 makefile 中表达这个概念,并且仅在内容实际更改时才运行依赖目标吗?

具体来说,我有一个date.h包含当前日期、小时和分钟(但不包括秒)的文件,该文件是由如下规则生成的:

echo "#define DATE" '"' $$(date '+%D-%H:%M') '"' > date.h
Run Code Online (Sandbox Code Playgroud)

默认的“app”目标取决于date.h,整个 Makefile 如下所示:

app: date.h
    echo "Building app"
    touch app

date.h: FORCE
    echo "#define DATE" '"' $$(date '+%D-%H:%M') '"' > date.h

FORCE:
Run Code Online (Sandbox Code Playgroud)

现在FORCE目标date.h每次都会被重建,这会导致目标app总是过时并被重建。

另一方面,如果我不包含FORCE目标,则date.h只有在目标不存在时才会重建(例如,在清理后或最初克隆项目后),但此后不会再重建。

我想要的是 date.h 仅当其内容实际上被更改时才过时echo- 即,当分钟更改时,而不是其他情况。是否有某种方法告诉 make 仅考虑date.h磁盘上实际字节发生更改的情况,而不是按照时间戳进行更改,或者通过其他方式来实现此效果?

我想到了将日期回显到临时文件,然后date.h根据比较其内容进行有条件更新,但我不确定这是否是一个合理的方法。

Ros*_*dge 5

您在上一段中建议执行的操作是仅在特定依赖项实际更改时(而不是仅在该依赖项的时间戳发生更改时)有条件重建目标的常用方法。在生成特定依赖项的规则中,将其生成为临时文件,检查其内容是否已更改,如果已更改,则将规则的目标替换为临时文件。值得注意的是,从我记事起,GCC 就一直在其 makefile 中这样做。它生成源代码和头文件,其中包含表和模式识别器等内容,当它们的依赖项之一发生变化时,这些内容通常不会更改。停止重建依赖于这些生成文件的目标可以在构建时间上产生很大的差异。

因此,对于您的示例 makefile,您可以执行以下操作:

date.h: FORCE
        echo "#define DATE" '"' $$(date '+%D-%H:%M') '"' > date.h.tmp
        if test -r date.h;                                              \
        then                                                            \
                cmp date.h.tmp date.h || mv -f date.h.tmp date.h;       \
        else                                                            \
                mv date.h.tmp date.h;                                   \
        fi
Run Code Online (Sandbox Code Playgroud)

您可能还想查看使用move-if-changeGCC 源代码中包含的 shell 脚本。