Apache Ant™ 任務設計指南

本文檔涵蓋如何撰寫 Apache Ant 任務,以符合納入 Ant 發行版的標準。即使您撰寫任務供個人使用,您也可能會發現本文檔很有用,因為本文檔所探討的問題在這種情況下仍然存在。

不要中斷現有的建置

即使您發現 Ant 有些非常可怕的問題,且很容易修復,但如果您的修復中斷了現有的建置檔案,那麼我們就會有問題。確保每個建置檔案都能正常運作是所有變更的目標之一。舉例來說,Ant 1.5 會在字串中傳遞單一美元符號「$」;Ant 1.4 及更早版本會將其移除。為了進行此修復,我們首先必須撰寫測試套件來公開目前的行為,然後變更某些內容,以便傳遞單一「$」,但雙重「$$」會對應到「$」以維持向後相容性。

不要中斷 Java API

Ant 的任務可以由第三方程式和任務使用。我們不能進行會中斷 API 的變更。這包括
  1. 移動類別,但沒有留下向後相容的外觀。
  2. 刪除類別。
  3. 刪除方法或欄位,或降低其可存取性。
  4. 變更 setAttribute(Type) 方法的簽章。如果您需要新增限制性類型,請新增一個新屬性,並將其置於原始屬性上方的來源中。XML 對應器會取得限制性類型,舊程式仍然可以使用舊類型。
  5. 不要變更語意。至少不要大幅變更。畢竟,所有錯誤修正都是語意的隱含變更。

使用內建的輔助類別

Ant 包含輔助任務來簡化大部分的工作。基於開發、維護和程式碼大小的原因,使用它們比自行開發要好得多。

執行

執行會在 Ant 支援的所有平台下產生不同的程式,處理 Java 版本問題和平台問題。請務必使用此類別來呼叫其他程式。

Java、ExecuteJava

這些類別可以用於在不同的 VM 中產生 Java 程式(它們使用執行)或在同一個 VM 中產生 Java 程式,並使用或不使用不同的類別載入器。從此衍生任務時,通常對使用者有利的是允許指定類別路徑,並將分支設定為選用屬性。

專案和相關類別

專案、FileUtils、JavaEnvUtils 都具有輔助函式,可執行觸摸檔案、複製檔案等動作。使用這些函式,而不是自行編寫程式碼或嘗試使用可能較不穩定且較難使用的任務。

遵守 Sun/Java 風格指南

Ant 程式碼庫旨在具備單一的統一編碼標準,而該標準就是 Sun Java 編碼指南

並不是說它們比其他替代方案更好,但它們是一個標準,而且它們是其他任務中持續使用的標準。程式碼在符合這些標準之前,不會納入資料庫中。

如果您正在撰寫供個人或組織使用的任務,您可以自由使用任何您喜歡的樣式。但使用 Sun Java 樣式將有助於您熟悉其他 Ant 來源,這可能很重要。

一個重要的規則是「沒有標籤」。改用四個空格。不是兩個,不是八個,是四個。即使您的編輯器被設定為有四個空格的標籤,許多其他編輯器都沒有。空格在編輯器和平台之間具有更高的相容性。一些 IDE(JEdit)可以突出顯示標籤,以防止您意外插入標籤。

在主 ant 目錄中有一個 Ant 建置檔案 check.xml,它會在 Ant 的原始碼中執行 checkstyle

屬性和元素

使用 Ant 基於內省的屬性對應到 Java 資料類型,而不是將您的所有屬性實作為 setFoo(String) 並自己對應到 int、boolean 或 File。這可以節省您的工作,讓 Java 呼叫者以類型安全的方式使用您,並讓 Xdocs 文件產生器找出參數是什麼。

Ant 1.x 任務在屬性命名方面非常不一致——有些任務使用 source,有些使用 src。以下是首選屬性名稱的清單

failonerror boolean 用於控制執行失敗是否應擲回 BuildException 或僅列印錯誤。參數驗證失敗應始終擲回錯誤,無論此標誌如何。
destdir 輸出的目標目錄
destfile 輸出的目標檔案
srcdir 來源目錄
srcfile 來源檔案

是的,這是一個非常簡短的清單。至少嘗試與核心任務保持模糊的一致性。

支援類別路徑

如果您需要外部程式庫,請嘗試讓使用者可以為您的任務提供類別路徑,而不是讓他們將所有內容新增到 ANT_HOME/lib 目錄。這讓使用者可以將外部程式庫保留在他們基於 Ant 的專案中,而不是強迫所有使用者變更他們的 Ant 系統設定。

設計用於受控的重複使用

保持成員變數為私人。如果需要子類別的讀取權限,請新增存取方法,而不是變更成員的可存取性。這使子類別能夠存取內容,但仍與實際實作分離。

Ant 中另一個常見的重複使用機制是讓一個任務建立和設定另一個任務。這相當簡單。Ant 的 API 中有可用的設施,讓任務可以由它們熟悉的名稱(「java」、「exec」等)實例化。建議您不要使用這種方法,因為使用者覆寫名稱以指向完全不同的類別的可能性非常真實。使用直接建構函式呼叫(或反射)來實例化您的子任務。自 Ant 1.6.3 以來,您可以呼叫 org.apache.tools.ant.Task#bindToOwner() 將輔助任務「遮罩」為其父任務。

自行執行相依性檢查

Make 在其整合相依性檢查方面優於 Ant;make 呼叫的命令列應用程式不需要執行自己的工作。Ant 任務確實必須執行自己的相依性工作,但如果可以執行,那麼就能執行得很好。良好的相依性感知任務可以在建置檔案中沒有明確相依性資訊的情況下找出相依性,並且足夠聰明地找出真正的相依性,或許透過一點檔案剖析。 depends 任務就是最好的範例。有些 zip/jar 任務也相當不錯,因為它們可以在需要時更新封存檔。大多數任務只比較來源和目標時間戳記,然後從那裡開始執行。未執行任何相依性檢查的任務無法像它們所能做的那樣對使用者提供幫助,因為它們不必要的執行工作可能會滲透到整個建置、測試和部署流程。

支援適當的 Java 版本

Ant 1.5 及更早版本設計為支援 Java 1.1。Ant 1.6 及更高版本設計為支援 Java 1.2:建置於其上,在其上執行。Ant 1.8 需要 Java 1.4;1.9 需要 1.5(「JDK 5」)。有時任務的功能在較舊或較新的環境中會降低,通常是基於函式庫限制;此類行為變更必須始終在文件記錄中註明。

有問題的是依賴於較新 Java 版本中功能的程式碼,例如 JDK 7 中的 java.nio.file.Path。也要注意舊類別中新增的方法;任何程式碼都不能直接使用這些方法,而且仍然可以在較舊的系統上編譯和執行。如果要使用現有類別中的新方法,則必須透過反射使用它,並以某種方式處理 NoSuchMethodException

如果程式碼根本無法在較舊版本的 Java 上執行,該怎麼辦?可能會發生這種情況。將任務設為選用任務,並透過修改 build.xml 將編譯限制在較新的 JDK(或更新版本)上,這可能會不錯。更好的方法是使用反射在執行時連結到類別。

類似的考量也適用於新的語言功能,例如 JDK 7 字串切換陳述式。

在巢狀文字中明確展開屬性

基於歷史原因,會呼叫 addText(String text) 來設定任務的巢狀文字,而不會進行任何屬性展開。請呼叫 Project.replaceProperties() 手動執行此操作。如果您忘記,您會造成一個問題,而無法在不中斷使用者的建置檔案的情況下修復它。

重構

如果對任務所做的變更使其過於難以處理,請將其拆分成更簡潔的設計,重構程式碼,並提交不僅是功能擴充,還有更簡潔的任務。在 Ant 流程中經常出現的一種常見設計模式是採用轉接器模式,其中一個基底類別(例如 Javac 或 Rmic)一開始很簡單,然後會因為支援多個後端而變得複雜:javac、jikes、jvc。將可編程前端從提供後端的類別中拆分出來的重構會簡化設計,並讓新增新的後端變得容易許多。但要執行此操作,需要讓前端的介面和行為保持相同,並確保沒有任何子類別直接存取資料成員,因為這些資料成員可能不存在於重構的設計中。這就是擁有私人資料成員如此重要的原因。

我們不能做的一件事是移動現有的任務或刪除它們。請記住,Ant 有一個 Java API 以及一個 XML 語言。我們不想中斷那個 API,或任何繼承現有 Ant 任務的內容。重構時,您需要在原始類別所在的位置保留門面,這樣現有的程式碼才不會中斷。

測試

查看 ant/src/testcases,您會找到運送 Ant 任務的 JUnit 測試,以了解如何執行以及對新任務的期望。它們大多數都很基本,而且您無疑可以為您的任務做得更好——請隨時這樣做!

一組寫得很好的測試案例會在開發過程中中斷 Ant 任務,直到程式碼實際完成。稍後出現的每個錯誤都應該新增一個測試案例來展示問題並修復它。

測試案例是開發過程中測試任務的好方法。在 ant 原始碼樹中呼叫 'build run-test',將執行所有 ant 測試,以驗證您的變更不會中斷任何內容。若要測試單一任務,請使用一次性 ant run-single-test -Dtestcase=${testname},其中 ${testname} 是您的測試類別名稱。

測試案例也由提交者用於驗證變更和修補程式是否符合它們的說法。如果您有測試案例,它會大幅提升您的可信度。準確地說,我們討厭沒有測試案例的提交,因為這表示我們必須自己撰寫它們。只有在我們需要任務或它被認為對許多使用者來說至關重要時,才會執行此操作。

還要記住,Ant 1.x 被設計為在 Java 1.2 上編譯和執行,因此您應該在 Java 1.2 以及您使用的任何後續版本上進行測試。您應該能夠從 Sun 下載舊的 SDK 以達到此目的。

最後,在開始開發您的專案之前和之後,執行完整的 build test,以確保您沒有意外中斷其他任何內容。

文件

沒有文件,任務就無法使用。因此,請記得提供一個簡潔且清楚的 html(很快,xml)頁面,以類似於現有任務的樣式描述任務。它應該包含屬性和元素的清單,以及至少一個任務的工作範例。許多使用者會將範例剪貼到他們的建置檔案中作為起點,因此請讓範例實用並測試它們。

您可以使用 proposal/xdocs 中的 xdocs 內容,從來源的 javadoc 自動產生您的文件頁面;這讓生活更輕鬆,並且會讓轉換到由 xdoclet 產生的完整文件建置程序變得非常簡單。

授權和版權

提交給 Apache 專案的任何程式碼都必須與 Apache 授權相容,而提交的行為必須視為已將提交的程式碼隱含授權給 Apache 軟體基金會。

這很重要。

Apache 相當自由放任的授權目前不認為與自由軟體基金會的 GPL 或較寬鬆的 GPL(Gnu 專案)相容。這些授權有更嚴格的條款「copyleft」,而 Apache 授權中沒有這些條款。這允許個人和組織在 Apache 函式庫和來源之上建置商業和閉源應用程式。

由於 Gnu GPL 授權會立即延伸涵蓋任何它被納入其中的較大型應用程式(或在 LGPL 的情況下是函式庫),因此 Ant 團隊無法將任何基於 GPL 或 LGPL 來源的任務納入 Ant 程式碼庫。您可以自由提交,但它會被禮貌而堅定地拒絕。

如果您透過 import 或反射連結到 GPL 或 LGPL 函式庫,您的任務必須在相同的條款下授權。因此連結到 (L)GPL 程式碼的任務無法進入 Apache 管理的程式碼庫。呼叫此類程式碼的任務可以使用「exec」或「java」任務來執行程式,因為您此時只是執行它們,而不是連結到它們。

即使我們無法將您的任務納入 Apache 程式碼庫,我們仍然可以指出您託管它的位置;只要提交一個指向您的任務的 xdocs/external.html 的 diff 即可。

如果您的任務直接連結到專有程式碼,我們有不同的問題:建置任務真的非常困難。請使用反射。

不要重複發明輪子

我們都做過這件事:撰寫並提交任務,只為了發現它已經在另一個任務的小角落實作,或者它已經由其他人提交但尚未提交。您可以透過了解最新 CVS 樹中的內容來避免這種情況;持續取得每日來源更新,查看手動變更,並訂閱 dev 郵件清單。

如果您正在考慮撰寫任務,將您的想法張貼到清單上會很有幫助,您會得到其他人的見解,也許還有一些半寫好的任務可以執行基礎工作,所有這些都不需要撰寫一行程式碼。

提交到 Ant

提交 Ant 任務的基本機制是將它郵寄到 dev 郵件清單。加入這個清單很有幫助,因為您會看到其他提交,以及關於您自己提交的任何辯論。

您可以使用下列任一種方法建立您的修補程式檔(提交者建議使用第一種)

patch 應作為郵件附件寄出,郵件標題為 [PATCH],主旨中有一個獨特的單行摘要。檔案名稱/工作和變更通常就足夠了。將變更包含為附件非常重要,因為太多郵件程式會重新格式化貼上的文字,這會破壞 patch。

然後,您等待其中一位提交者提交 patch,如果認為這樣做是適當的。錯誤修正會很快進行,其他變更通常會在進行(可能已修改的)提交之前引發一些討論。

新的提交應以 [SUBMIT] 進行。郵件守護程式會拒絕任何超過 100KB 的郵件,因此應將任何大型更新壓縮。如果您的提交大於此大小,何不將其拆分成不同的工作。

我們也希望將提交新增到 bugzilla,這樣它們就不會遺失。請先使用有意義的名稱提交報告,然後將檔案新增為附件進行提交。請使用 CVS diff 檔案!

如果您在幾週後沒有收到任何消息,請提醒郵件清單。有時,非常好的提交會在其他問題的雜訊中遺失。在產品的新點版本發布之前尤其如此。在那個時候,除了錯誤修正之外的任何事情都傾向於被忽略。

檢查清單

在提交 patch 和新工作之前,您應驗證以下事項。事情不必完美;在提交 patch 或提交之前可能需要進行幾次反覆運算,並且可以在過程中處理這些項目。但在提交程式碼時,包括文件和一些測試案例在內的所有內容都將完成,因此事先完成這些工作可以節省時間。提交者對有測試案例的 patch 和提交會更有好感,而文件有助於說明工作的理由。

提交 patch 之前的檢查清單

提交新工作前的檢查清單