觸發檔案

2007 年 4 月 26 日

使用 make 進行建置時,您可以透過比較輸出檔案和輸入檔案的修改日期,來判斷是否需要執行作業。對於編譯等作業(a.out 取決於 foo.c),這種方式很有效,但有時輸出較難判斷。

一個範例是執行測試 - 輸出的內容是什麼?其中一個輸出是測試報告。測試報告的目標可以將報告檔案的日期與可執行檔和任何測試資料檔案的日期進行比較。這樣一來,我們可以確保只有在有變更時才會執行測試。

大部分時間,輸出檔案都包含有用的資訊。但是,為了判斷是否需要執行目標,您實際上並不在乎輸出檔案的內容,只在乎它的日期。因此,make 腳本中常見的慣用語法是一個空檔案,它只用作時間標記。我稱之為「觸發檔案」,因為它通常只會透過 unix touch 指令進行操作,而這個指令只會更新檔案的修改時間。

當您嘗試比較一系列檔案的日期時,觸發檔案通常很有用。如果您的輸出是一整個檔案樹,更新觸發檔案會比瀏覽整個檔案樹查看更新時間來得快。

觸發檔案是 make 的常見且自然的慣用語法,但對於 ant 來說則較不常見。不過,它們仍然經常很有用。這一點在我最近幾天研究 Hibernate 的 HQL DomainSpecificLanguage 如何實作時,特別令我印象深刻。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 屬性。如果這個屬性為 true,則不會執行主要的 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 任務中。