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

时间:2017-06-04 22:18:35

标签: makefile gnu-make

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

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

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

默认的'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:

现在,FORCE目标会导致date.h每次重建,这会导致app始终过期并重建。

另一方面,如果我不包含FORCE目标,那么date.h只会在不存在的情况下重建(例如,在清理之后或最初克隆项目之后) ),但之后再也不会。

我想要的是date.h只有在其内容实际由echo更改时才会过时 - 也就是说,当分钟发生变化时,而不是其他情况。是否有一些方法可以告诉make只考虑date.h如果它的实际字节在磁盘上发生了变化,而不是通过时间戳或其他方式来实现这种效果?

将日期回复到一个临时文件,然后基于比较它们的内容有条件地更新date.h让我大吃一惊,但我不确定这是否是一种合理的方法。

2 个答案:

答案 0 :(得分:3)

您在上一段中建议的做法是,只有在特定依赖项实际发生变化时才有条件地重建目标,而不仅仅是该依赖关系的时间戳已经改变。在生成特定依赖关系的规则中,将其生成为临时文件,检查其内容是否已更改,如果是,则使用临时文件替换规则的目标。值得注意的是,只要我记得,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

您可能还希望使用GCC源代码中包含的move-if-change shell脚本。

答案 1 :(得分:0)

我找到了另一种方法,如何解决这个问题。这个想法是这样的:您检查一个临时文件,该文件与日期和时间值一起保存。因此,您第一次收到tmp/YYMMDDHHMM这样的文件。再次运行Makefile时,会检查是否存在此文件。如果是,请不要创建新的/不要更新date.h。一分钟后,如果再次运行Makefile,则会生成一个新文件tmp/YYMMDDHHMM,因此date.h会更新:

TMP_DIR := tmp
RUN_OR_NOT := $(shell date '+%y%m%d%H%M')

all: ${TMP_DIR}/date.h

tmp_dir:
        mkdir -p ${TMP_DIR}

${TMP_DIR}/${RUN_OR_NOT}: tmp_dir
        if [ ! -f "${TMP_DIR}/${RUN_OR_NOT}" ]; then \
                echo "" > ${TMP_DIR}/${RUN_OR_NOT}; \
        fi;

${TMP_DIR}/date.h: ${TMP_DIR}/${RUN_OR_NOT}
        # create date.h
        @echo "Run date.h target, which creates date.h"
        touch ${TMP_DIR}/date.h

.PHONY: all special