git - 确保已生成但 checkin 的文件在 git 同步后不会重建

标签 git makefile

我不确定这是一个 git 问题还是一个 make 问题,或者两者都是,但这里是......

假设您有一个由生成器程序 gen.py 生成的文件 foo.cpp。如果修改了生成器程序,则必须再次运行它才能生成新版本的 foo.cpp

编码此行为的 makefile 规则可能如下1:

foo.cpp: gen.py
    gen.py

对于 gen.py 上的积极开发,我认为这是理想的选择。

但是,请考虑通过 git(例如在 github 或其他类似网站上)远程提供项目的情况。用户将下载并制作您的项目。

处理生成文件的传统方法不是全部检查它们:不要在 git all 中包含 foo.cpp (将其添加到您的 `.gitignore),并且当用户构建时您的项目,将生成该文件。

但是,另一种方法将生成的文件foo.cpp包含在git中。如果更改其中一个而不更新另一个,则 gen.pyfoo.cpp 可能会“不同步”2,但还有一些其他优点:

  • 只想构建项目但从不更新 gen.py 的用户不需要具备运行 gen.py 的先决条件(例如 Python)。
  • 未修改的构建速度更快,因为生成步骤不运行。
  • 在暂存/提交期间,您会获得有关 gen.py 隐含的 foo.cpp 更改的直接反馈,因为该文件将成为 diff 的一部分。<

我对检查生成的文件与始终在本地生成文件的优点不感兴趣。您可能会认为对于某些项目,已做出 checkin 生成文件的决定,并且该决定没有争议。

在这种情况下,我的问题是:

对于新克隆项目或同步涉及两个文件的提交的用户,如何确保 make 不会尝试生成 foo.cpp ?默认情况下,git 同步文件时会使用当前时间,因此 foo.cpp 和 gen.py 将具有相同的时间戳,并且 foo.cpp 会具有相同的时间戳。 code> 将被重建。

我不能要求用户更改他们的 git 配置。


1 也许 foo.cpp 会有额外的依赖项,这也将作为先决条件出现,但这是“基本情况”。

2 一个合理的方法是通过 git hook 强制它们同步。

最佳答案

您可以比较输出,并且仅在不同时更新目标。例如,您的gen.py会将其输出写入标准输出,因此通常您会有这样的规则:

foo.cpp: gen.py
        ./gen.py > $@

您可以将其更改为:

foo.cpp: gen.py
        ./gen.py > <a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="b591f59bc1d8c5" rel="noreferrer noopener nofollow">[email protected]</a>
        cmp -s $@ <a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="b793f799c3dac7" rel="noreferrer noopener nofollow">[email protected]</a> && rm -f <a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="7f5b3f510b120f" rel="noreferrer noopener nofollow">[email protected]</a> || mv -f <a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="16325638627b66" rel="noreferrer noopener nofollow">[email protected]</a> $@

这里的缺点是,直到您DO更新 foo.cpp gen.py配方将始终运行。但是没有依赖的目标foo.cpp将被构建,因为它的时间戳没有改变。

如果这很昂贵并且您想解决这个问题,您将不得不做一些更复杂的事情:如果比较是正确的(没有区别)那么您需要重置 gen.py 上的时间戳使其与目标相同;这确保了目标在未来将不再被认为是过时的。像这样的事情:

foo.cpp: gen.py
        ./gen.py > <a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="597d19772d3429" rel="noreferrer noopener nofollow">[email protected]</a>
        if test -e $@ && cmp -s $@ <a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="1e3a5e306a736e" rel="noreferrer noopener nofollow">[email protected]</a>; then \
            rm -f <a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="f7d3b7d9839a87" rel="noreferrer noopener nofollow">[email protected]</a>; \
            touch -r $@ gen.py; \
        else \
            mv -f <a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="2206620c564f52" rel="noreferrer noopener nofollow">[email protected]</a> $@; \
        fi

如果您无法轻松更改 gen.py 的输出文件名那么你就必须做一些更恶心的事情,比如重命名 $@<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="8ca8cca2e3e0e8" rel="noreferrer noopener nofollow">[email protected]</a>在运行之前./gen.py然后根据需要将其改回或使用新的。

预计到达时间如果您只是想避免运行 gen.py如果是相同的版本,这更像是一个 Git 问题,而不是 makefile 问题,因为你真正要问的是如何知道这些文件是否在同一个 Git 提交中。这很简单,但正如我下面的评论所说,我认为这实际上并不正确:

foo.cpp: gen.py
        tver=$$(git log -n1 --oneline $@ 2>/dev/null); \
        pver=$$(git log -n1 --oneline $< 2>/dev/null); \
        if test -z "$$tver" || test -z "$$pver" || test "$$tver" != "$$pver"; then ./gen.py; fi

关于git - 确保已生成但 checkin 的文件在 git 同步后不会重建,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53233870/

相关文章:

makefile - 产生混合的隐式和普通规则错误

git - 如何处理实验性非 merge git 分支?

git - 无法从我的本地 ubuntu 机器推送到 aws ec2

windows - 如何在windows cmd上执行git log?

windows - git-bash $PATH 无法用空格解析 windows 目录

c++ - 链接 GMP 库时出错

c - 如何在 gcc 中验证变量在某些函数中与 hton 一起使用

git - 从 Phabricator 修订中恢复已删除的 git 分支

ant - 如果 makefile 失败则停止 ant 构建

vim - 制作时如何继续编辑文件?