触摸文件

2007 年 4 月 26 日

在使用 make 进行构建时,您可以通过比较输出文件和输入文件的修改日期来确定是否需要执行操作。对于像编译(a.out 依赖于 foo.c)这样的操作,这种方法很有效,但有时输出更难识别。

一个例子是运行测试 - 那里有什么输出?一个输出是测试报告。然后,测试报告的目标可以比较报告文件的日期与可执行文件和任何测试数据文件的日期。这样,我们就可以确保只有在发生更改时才运行测试。

大多数情况下,输出文件包含有用的信息。但为了确定是否需要运行目标,您实际上并不关心输出文件的内容,只关心它的日期。因此,make 脚本中的一种常见习惯用法是一个空文件,它只用作时间标记。我称之为“触摸文件”,因为它通常只由 unix touch 命令操作,该命令只更新文件的修改时间。

当您尝试比较一系列文件的日期时,触摸文件通常很有用。如果您的输出是整个文件树,更新触摸文件比遍历整个树查看更新时间更快。

触摸文件是 make 中一种常见且自然的习惯用法,但在 ant 中不太常见。但是,它们仍然经常有用。在过去几天里,我检查了 Hibernate 的 HQL 领域特定语言 的实现方式时,这一点尤其让我印象深刻。HQL 的核心是三个 Antlr 解析器,它们的语法由三个语法文件定义。如果这些语法文件中的任何一个发生更改,则需要重新生成解析器源代码。

以下是此 ant 源代码

<target name="init.antlr" 
        depends="init" 
        description="Check ANTLR dependencies.">
  <uptodate property="antlr.isUpToDate" 
            targetfile="${dir.out.antlr-package}/.antlr_run">
    <srcfiles dir="${dir.grammar}" includes="*.g"/>
  </uptodate>
</target>

<target name="antlr" 
        depends="init.antlr" 
        unless="antlr.isUpToDate" 
        description="Generate ANTLR parsers.">
  <taskdef name="antlrtask" 
           classname="org.apache.tools.ant.taskdefs.optional.ANTLR">
    <classpath>
      <fileset dir="${dir.lib}">
        <include name="ant-antlr-*.jar"/>
        <include name="antlr-*.jar"/>
      </fileset>
    </classpath>
  </taskdef>
  <mkdir dir="${dir.out.antlr-package}" />
  <antlrtask target="${dir.grammar}/hql.g" 
             outputdirectory="${dir.out.antlr-package}" />
  <antlrtask target="${dir.grammar}/hql-sql.g" 
             outputdirectory="${dir.out.antlr-package}" />
  <antlrtask target="${dir.grammar}/sql-gen.g" 
             outputdirectory="${dir.out.antlr-package}" />
  <touch file="${dir.out.antlr-package}/.antlr_run"/>
</target>

请注意,init.antlr 任务根据特定的 .antlr_run 文件设置 antlr.isUpToDate 属性。如果此属性为真,则主 antlr 任务不会运行。在 antlr 任务结束时,它会触摸 .antlr.run 文件,该文件为空。

在 Hibernate 的主构建中,这是使用的任务。因此,只有在需要时才会生成解析器源文件。如果您确实想要强制重新生成文件,则有一个单独的目标

<target name="antlr.regen" 
        depends="init,cleanantlr,antlr" 
        description="Regenerate all ANTLR generated code." />

<target name="cleanantlr" 
        depends="init" 
        description="Clean up the generated ANTLR parsers.">
  <delete dir="${dir.out.antlr-package}"/>
</target>

请注意,此目标通过声明对 cleanAntlr 任务的依赖关系来实现其目标。它没有指示对 init.antlr 的依赖关系,因为该依赖关系已存在于 antlr 任务中。