Apache Ant™ 實戰

在生產開發系統中使用 Apache Ant

史提夫·勞倫
最後更新 2005-03-16

簡介

Apache Ant 可以在團隊開發過程中成為一個無價的工具,或者它也可以成為我們稱之為開發的持續危機中的另一個問題來源。本文包含一些策略和戰術,以便充分利用 Ant。它在某些地方相當輕浮,並且幾乎沒有任何實際的 Ant XML 範例。缺乏範例是完全故意的,它降低了文件維護成本。所涵蓋的大部分概念不需要 XML 表示提供的詳細資訊,因為我們關注的是流程,而不是語法。最後,請注意,這裡的評論僅是建議,需要根據您自己的需求進行自訂,而不是關於應該做什麼和不應該做什麼的嚴格規則。

首先,以下是本文涵蓋專案的一些假設

這一切意味著沒有時間花在把事情做好上,您無法嚴格控制團隊其他成員的工作方式,而且開發過程通常更像是混亂最小化,而不是其他任何事情。Ant 在此類專案中的作用是確保建置、測試和部署流程順利進行,讓您解決所有其他問題。

核心實務

釐清您希望 Ant 做什麼

Ant 並非萬靈丹。它只是您可用的開發工具庫中另一顆生鏽的子彈。它的主要目的是加速 Java 專案的建置和部署。您當然可以擴充 Ant 以執行 Java 能執行的任何工作:例如,很容易想像撰寫一個影像處理工作,藉由縮小和重新壓縮 jpeg 檔案來協助網站部署。但那會超出 Ant 真正要執行的界線,因此應審慎考慮。

Ant 也是 IDE 的絕佳輔助工具;一種執行所有部署管理以及乾淨、自動化建置的方法。但一個好的現代 IDE 本身就是一個生產力工具,您應該繼續使用。Ant 只是讓團隊在 IDE 選擇上擁有更多自由,例如「您可以在開發中使用任何您想要的,但部署建置使用 Ant」。現在,許多現代開源和商業 IDE 都包含 Ant 支援(包括 jEdit、Forte、Eclipse 和 IDEA),開發人員可以使用一個很棒的 IDE,而 Ant 則提供嚴謹且可攜式的建置流程,整合到工具中。

定義標準目標

當您有多個子專案時,請定義一組標準目標。介面和實作 jar 檔案分開的專案可以考慮使用 implintf 目標,並針對偵錯版本使用個別的 debug-impldebug-intf 目標。當然,還有無所不在的 clean 目標。

使用標準目標名稱,很容易建置包含的 Ant 建置檔案,只要使用 ant 工作將工作交給下方的類別即可。例如,可以從父目錄將 clean 目標傳遞給 intfimpl 子目錄

<target name="clean"  depends="clean-intf, clean-impl">
</target>

<target name="clean-intf" >
    <ant dir="intf" target="clean" />
</target>

<target name="clean-impl">
    <ant dir="impl" target="clean" />
</target>  
如果您給予目標一個 description 標籤,然後呼叫 ant -projecthelp,它會將所有工作列為「主要目標」,並將所有沒有描述的工作列為子目標。因此,描述所有進入點非常有用,即使在專案變得龐大且複雜之前。

透過新工作擴充 Ant

如果 Ant 無法執行您要執行的動作,您可以使用 execjava 工作或 內嵌腳本 來擴充它。在一個有許多 build.xml 檔案的專案中,您很快就會發現,有一個單一集中位置來實作功能可以降低維護成本。一開始,透過 Java 程式碼實作工作擴充似乎需要額外的努力,但會帶來額外的優點:- 在某種程度上,就是這種功能解耦,也就是「任務」和使用宣告「建置檔案」的分離,讓 Ant 成功。如果你必須在 Make 或 IDE 中完成複雜的工作,你會有一個讓所有人都害怕的雜亂 makefile,或是一個非常脆弱的 IDE 組態。但 Ant 任務是可以重複使用和在所有 Ant 使用者之間共用的。Ant 中許多核心和選用任務,你現在所做的或將會依賴的任務,都是由嘗試解決自己迫切問題的人所撰寫的。

擁抱自動化測試

(或者「及早責備,經常責備」)

Ant 讓你呼叫 JUnit 任務,它會單元測試你的團隊所撰寫的程式碼。自動化測試一開始看起來像是額外的負擔,但 JUnit 讓撰寫單元測試變得如此容易,你幾乎沒有理由不這麼做。花時間學習如何使用 JUnit,撰寫測試案例,並在 Ant 的「test」目標中整合它們,這樣你的團隊每天或每小時的建置就可以自動套用測試。使用 Ant 進行 Java 開發 中可以免費下載的章節之一,會教你如何從 Ant 內部使用 JUnit。

一旦你加入從 SCM 系統擷取程式碼的方法,無論是作為 Ant 任務、在某些 shell 腳本或批次檔案中,或透過某些持續整合工具。整合測試程式碼可以是純粹的 Ant 任務,在任何專門執行該任務的電腦上執行。這對於驗證建置和單元測試在不同於一般開發電腦的目標上運作非常理想。例如,即使沒有開發人員願意在有選擇的情況下使用 Win95/Java1.1 組合,但仍然可以使用它。

系統測試比單元測試更難自動化,但如果你可以撰寫 Java 程式碼來對系統的大部分施加壓力,即使程式碼無法作為 JUnit 任務執行,那麼 java 任務可以用來呼叫它們。最好指定你想要為這些測試使用新的 JVM,這樣重大的崩潰才不會中斷整個建置。Junit 擴充功能,例如用於網頁的 HttpUnit,以及用於 J2EE 和 servlet 測試的 Cactus,有助於擴充測試架構。為了適當地測試,你仍然需要投入大量精力讓它們與你的專案一起運作,並衍生出絕佳的單元、系統和回歸測試,但你的客戶會因為你出貨運作良好的軟體而愛你。

學習使用並喜愛 Ant 的附加元件

Ant 發行版並非 Ant 宇宙的極限,它僅僅是個開端。查看 外部工具和任務頁面 以取得最新清單。以下是其中一些。

跨平台 Ant

Ant 是迄今為止跨平台 Java 開發和測試的最佳基礎。但如果您不注意,就有可能產生僅能在一個平台上運作的建置檔案,甚至只限於一個工作站。

跨平台 Ant 的常見障礙是使用不可攜式的命令列工具 (exec 任務)、路徑問題,以及硬式編碼在事物的所在位置。

命令列應用程式:Exec / Apply

外部呼叫的問題在於,並非所有功能都能在跨平台上找到,而那些通常有不同名稱的功能,DOS 的後代通常會在檔案結尾處預期 .exe.bat。如果您在命名命令時明確包含副檔名 (不要!),這可能會很糟;但當它讓您在專案的同一個 bin 目錄中保留可執行檔的 Unix 和 DOS 版本而不會產生名稱衝突時,這會很好。

命令列呼叫任務讓您指定您希望程式碼在上面執行的平台,因此您可以為您鎖定的每個平台撰寫不同的任務。或者,平台差異可以在 Ant 呼叫的某些外部程式碼中處理。這可以是新任務中編譯的 Java,或外部腳本檔案。

跨平台路徑

Unix 路徑使用正斜線來區分目錄,並使用冒號來區分項目。因此,"/bin/java/lib/xerces.jar:/bin/java/lib/ant.jar" 是 Unix 中的路徑。在 Windows 中,路徑必須使用分號分隔符,冒號用於指定磁碟機,反斜線分隔符為 "c:\bin\java\lib\xerces.jar;c:\bin\java\lib\ant.jar"

平台之間的這種差異(實際上,整個 Java 類別路徑範例)可能會造成數小時的樂趣。

Ant 減少了路徑問題;但並未完全消除它們。您也需要付出一些努力。處理路徑名稱的規則是「處理類似 DOS 的路徑名稱」、「處理類似 Unix 的路徑」。磁碟機 -「C:」- 在基於 DOS 的方塊中處理,但將它們放入 build.xml 檔案中會破壞所有可移植性。相對檔案路徑更具可移植性。分號可用作路徑分隔符 - 如果您的 Ant 呼叫包裝程式包含命令列中定義的屬性中的一系列 jar,則此事實很有用。在建置檔案中,您可能會發現透過列出個別檔案(使用 location= 屬性)或在類別路徑定義中包含 *.jar 的檔案集來建立類別路徑會更好。

還有一個 PathConvert 任務,它可以將完全解析的路徑放入屬性中。為什麼要這樣做?因為這樣您就可以使用該路徑以其他方式 - 例如將其作為參數傳遞給您正在呼叫的某些應用程式,或使用替換任務將其修補到本機化指令碼或批次檔案中。

請注意,DOS 下屬的檔案系統不區分大小寫(除了針對 NTFS 執行的 WinNT POSIX 子系統的模糊差錯),而且 Windows 假裝所有具有四個或更多字母的檔案副檔名也是三個字母的副檔名(請在您的 Java 目錄中嘗試 DELETE *.jav,以查看此情況的災難性範例)。

Ant 關於大小寫敏感性的政策是實現底層檔案系統的任何內容,以及處理檔案副檔名的方式是 *.jav 找不到任何 .java 檔案。當然,Java 編譯器區分大小寫 - 您不能在「examplethree.java」中實作類別「ExampleThree」。

某些任務僅在一個平台上執行 - Chmod 是經典範例。這些任務通常只會在不受支援的平台上產生警告訊息 - 目標的其他任務仍會被呼叫。其他任務會降低其在平台或 Java 版本上的功能。特別是,任何調整檔案時間戳記的任務都無法在 Java 1.1 上正確執行。可以這樣做的任務 - 例如 GetTouchUnjar/Unwar/Unzip - 會降低其在 Java1.1 上的功能,通常會改用目前的時戳記。

最後,Perl 是用來包裝跨平台 Java 呼叫,而非批次檔案的好地方。它包含在大部分 Unix 發行版中,而且是 ActiveState 的 Win32 平台 的簡單下載。具有 .pl 副檔名的 Perl 檔案,第 1 行註解中的常見 Unix 路徑至 perl,並標示為可執行,可以在 Windows、OS/2 和 Unix 上執行,因此可以從 Ant 呼叫而不會有問題。Perl 程式碼可以保留以解決其自己的平台問題。重新散發 Perl 程式碼時,別忘了將檔案的行尾設定為適當的平台;fixCRLF 可以為您執行這項工作。

團隊開發流程

即使每個團隊成員都可以選擇他們的 IDE/編輯器,甚至作業系統,您需要在每個方塊上設定功能基準。特別是,JDK 和 jar 檔需要完全同步。理想情況下,選擇所有開發人員/目標系統上可用的最新穩定 Java/JDK 版本,並堅持使用一段時間。考慮指定一個人作為所有工具的聯絡人,特別是在每晚都有新的建置可用的開源工具。除非有需要,否則這些工具應該只在每月更新,或在正式版本發布時更新。

另一個好策略是使用統一的目錄樹,並在該樹中加入額外的工具。所有參考都可以相對於該樹。如果預期團隊成員將專案中的目錄加入他們的路徑,則命令列工具可以包含在其中,包括 Ant exec 工作呼叫的那些工具。將所有內容置於原始碼控制之下,您便可以從 CVS 或等效工具中取得建置/執行環境的一站式商店。

使用 Ant 部署

Ant 和 Make 等較舊工具之間的一個重大差異在於,在 Ant 中,將 Java 部署到遠端網站的流程已經相當成熟。這是因為我們現在都必須這麼做,所以許多人已經努力讓這些工作變得更容易。

Ant 可以 JarTarZip 檔案以供部署,而 War 工作則延伸 jar 工作以改善 servlet 部署。Jlink 是 jar 產生檔案,讓您可以合併多個子 jar 檔。這對於建置流程非常理想,其中子專案會產生個別的 jar 檔,但最終輸出是合併的 jar 檔。Cab 可以用於 Win32 方塊,以建置 cab 檔案,如果您仍必須針對 IE 部署,這將很有用。

ftp 工作讓您可以將東西移至伺服器。小心不要在建置檔案中放入 ftp 密碼,具有嚴格存取控制的屬性檔案會稍微好一點。FixCRLF 工作通常是有用的中間步驟,如果您需要調整檔案的行尾。WebDav 工作已經討論很長一段時間,它將提供更安全的上傳至網路伺服器,但它仍在待辦事項清單中。有傳言說在 jakarta-slide 函式庫中有一個這樣的任務。由於 MacOS X、Linux 和 Windows XP 都支援 WebDAV 檔案系統,您甚至可以使用 copy 透過防火牆進行部署。

EJB 部署由 ejb 任務提供協助,而 serverdeploy 套件可以部署到多個伺服器。Ant 的普及性鼓勵供應商製作自己的部署任務,並隨伺服器重新分發。例如,Tomcat4.1 安裝包含用於部署、取消部署和重新載入 Web 應用程式的任務。

最後,當然還有使用 CopyCopydir 將檔案複製到目的地,或使用 Mail 或支援附件的 MimeMail 將檔案傳送給人員或處理程序。在一個專案中,我們的團隊甚至使用 Ant 透過建置,接著執行一組長 Copy 任務來建置 CD 映像,結果出乎意料地好,肯定比我們將檔案寄到 myrealbox.com 上的免費電子郵件服務,然後從遠端的 Web 瀏覽器拉取檔案來得容易,而我們是在透過 SSH 執行 WinNT 遠端桌面連線,再透過 SSH 進行通道連線。

目錄結構

目錄樹的結構非常仰賴專案。以下是可以用作起點的一些目錄配置模式。所有 jakarta 專案都遵循大致類似的樣式,這使得從一個專案輕鬆瀏覽到另一個專案,並在需要時輕鬆清理。

簡單專案

專案包含子目錄
bin 共用二進位檔、指令碼 - 將其放在路徑上。
build 這是建置的樹狀結構;Ant 建立它,並可以在「清理」專案中清空它。
dist 發行版本輸出會放在這裡;目錄是在 Ant 中建立,而清理會清空它
doc 手工製作的文件
lib 匯入的 Java 函式庫會放在這個目錄中
src 原始碼會放在這個樹狀結構下,以符合套件名稱的階層結構。<javac> 的相依性規則需要這樣做。
bin、lib、doc 和 src 目錄應該在原始碼控制之下。輕微的變化包括要包含在發行版本 jar 中的額外內容樹狀結構 - inf 檔案、影像等。這些也可以放在原始碼下,其中 metadata 目錄用於 web.xml 和類似的清單,而 web 資料夾用於 Web 內容 - JSP、html、影像等。將內容保存在這個資料夾(或子階層)中,可以在部署前更輕鬆地測試連結。部署映像的實際製作,例如 war 檔案,可以交由適當的 Ant 任務處理:不需要完全根據部署階層對原始碼樹狀結構進行建模。

Javadoc 輸出可以導向 build/ 下方的 doc/ 資料夾,或導向 doc/javadoc

介面和實作分離

如果介面從實作程式碼中分離,那麼只要為介面目錄提供一個單獨的建置路徑,或更好的是,僅在 jar 建構中:一個 jar 存放介面,一個 jar 存放實作,即可透過微小的變更來支援此功能。

鬆散耦合的子專案

在鬆散耦合方法中,多個專案可以擁有自己的樹狀結構副本,並擁有自己的原始碼存取權限。需要考慮的一個差異是,所有專案只有一個 bin 和 lib 目錄的執行個體。這有時很好 - 它有助於同步 xerces.jar 的副本,有時很糟糕 - 它可以在單元測試完成之前更新基礎 jar 檔案。

若要在子專案中仍然擁有單一建置,請使用父層 build.xml 檔案,它會呼叫進入子專案。

如果不同的團隊擁有不同的程式碼存取/承諾權限,這種樣式會運作良好。風險在於,透過給予子專案額外的迴旋餘地,您可能會得到不相容的原始碼、函式庫、建置程序,並增加您的工作負載和整體的整合悲劇。

保留對相當鬆散整合的專案集合的控制的唯一方法,是擁有完全自動化的建置和測試程序,以驗證所有內容仍然相容。Sam Ruby 為所有 apache java 函式庫執行一個,並在發生問題時寄電子郵件給所有人;您的專案可能可以使用 Cruise Control 來進行自動化、持續、背景建置程序。

整合的子專案

緊密耦合的專案在同一個樹狀結構中擁有所有原始碼;不同的專案擁有不同的子目錄。建置檔案可以移到這些子目錄(例如 src/com/iseran/coresrc/com/iseran/extras),或保留在頂端 - 使用名為 core.xmlextras.xml 的獨立建置檔案。

如果每個人都彼此信任,且子專案並非過於龐大或複雜,則此專案樣式會很有效。風險在於,隨著專案進展,可能會需要拆分為更鬆散結合的設計,但等到意識到這一點時,排程壓力和交錯的建置檔案會讓執行拆分變得幾乎不可能。如果發生這種情況,請持續執行,直到有時間重構專案目錄結構為止。

Ant 更新政策

一旦開始使用 Ant,您應該制定一個政策,說明團隊何時以及如何更新其副本。一個簡單的政策是「在任何高壓力里程碑之後的每個官方版本,將所有不重要的任務(例如睡眠和看日光)置於後燃器」。這可讓您免於在開發期間 Ant 經歷的變更和偶爾出現的不穩定性。其主要缺點是它讓您與 Ant 不斷新增的新任務和功能隔離。

更新通常需要變更 build.xml 檔案。大多數變更都旨在向後相容,但有時必須進行不相容的變更。這就是為什麼在重大里程碑後的平靜期進行更新很重要的原因。這也是為什麼在 CVS 樹中包含 ant.jar 和相關檔案有助於確保舊版本的軟體仍可建置的原因。

最積極的策略是取得 Ant 來源的每週或每日快照,建置它並使用它。這會強制您更定期地調整 build.xml 檔案,因為新的任務和屬性可能需要一段時間才能穩定下來。您真的必須想要新功能、享受無償的額外工作或樂於讓同事感到不安,才能採取這種方法。

一旦你開始使用新的任務擴充 Ant,你會突然發現拉取定期建置變得更誘人。最新的 Ant 建置永遠都是撰寫擴充功能的最佳平台,因為你可以利用基礎類別的定期增強功能。它也可以防止你浪費時間在已經完成的事情上。一個新提交的任務,例如與 EJB 引擎、SOAP 伺服器交談或僅將文字檔案轉換成大寫,可能幾乎完全符合你的需求,因此請採用它、增強它並提供增強功能給全世界。這肯定比在 Ant 0.8 中孤立地開始處理你的「文字大小寫轉換器」任務、在六個月後宣布它的存在,然後發現你得到的不是讚美而是對現有實作的有用指標來得好。參與這個流程的最後一個好處是,它讓你的任務更容易加入 Ant CVS 樹狀結構,提前 Ant 採用你為讓專案運作而需要的所有變更的日期。如果發生這種情況,你可以恢復到官方 Ant 版本,並繼續處理所有其他危機。

你也應該加入 dev 郵件清單,因為這是其他開發人員張貼其工作、問題和經驗的地方。訊息量可能相當大:一天超過 40 則訊息,因此請考慮將它轉發到你不常使用的電子郵件地址。而且不要讓團隊中的每個人都訂閱;它可能會造成過多的干擾。

使用 Ant 安裝。

因為 Ant 可以讀取環境變數、複製、解壓縮和刪除檔案以及進行 java 和作業系統呼叫,所以它可以用於簡單的安裝任務。例如,tomcat 的安裝程式可以擷取環境變數 TOMCAT_HOME、停止 tomcat 執行,並將 war 檔案複製到 TOMCAT_HOME/webapps。它甚至可以重新啟動 tomcat,但建置不會完成,直到 tomcat 退出,這可能不是想要的結果。

使用 Ant 的好處首先在於,相同的安裝目標可以用於你的本機建置檔案(透過 install.xml 檔案的 ant 呼叫),其次在於,撰寫基本的安裝目標相當容易。這種方法的缺點是,目的地必須正確預先安裝最新版本的 Ant,而且 Ant 不允許你妥善處理失敗,而一個好的安裝程式就是關於在事情出錯時進行處理,從檔案正在使用到 jar 版本不同。這表示 Ant 不適合封裝軟體,但它確實適用於部署和安裝到你的本機伺服器。

我參與的一個大型建置專案有一個 Bluestone 應用程式伺服器的 Ant 安裝建置檔案,它會關閉單一電腦上應用程式伺服器的所有四個執行個體,將新版本的 war 檔案(包含日期戳記和建置戳記)複製到檔案目錄,清除目前已部署的 war 版本,然後安裝新版本。因為 Bluestone 會依需求重新啟動 JVM,所以這個指令碼就是你部署網路服務所需要的全部。在防火牆後面的系統上,我們透過使用 ftp 任務複製出 war 和建置檔案,然後使用 telnet 任務遠端呼叫建置檔案,進而提升部署流程的層級。結果是我們已經自動化了從我們的 IDE(Jedit)或命令列重新編譯和重新部署到本機伺服器的流程,這非常有價值。想像一下在你的 IDE 工具列上按下一個按鈕,就能建置、單元測試、部署,然後功能測試你的網路應用程式。

我後來新增了一個額外的技巧,即一個 junit 測試案例,用於執行安裝檢查清單。透過測試驗證網路磁碟機的存取權限、伺服器之間近似的時鐘同步、DNS 功能、產生可執行檔的能力以及其他所有問題點,安裝指令碼可以在安裝期間自動執行系統健全測試並報告問題。[相同的測試也可以從 JMX MBean 呼叫,但那是另一個故事]。

因此,Ant 僅在您控制的伺服器特殊情況下,才不是真正安裝工具的替代品,但在這種情況下,它確實讓您能將遠端安裝與您的建置整合。

提示和技巧

get
get 任務可以擷取任何 URL,因此可以用於在建置過程中觸發遠端伺服器端程式碼,從遠端伺服器重新啟動到將簡訊/呼叫器訊息傳送至開發人員的手機。
i18n
國際化總是會造成問題。Ant 在此透過 native2ascii 任務提供協助,該任務可以將所有非 ASCII 字元轉譯為 Unicode。您可以使用它來撰寫包含字串(甚至註解)的 Java 檔案,並使用您自己的非 ASCII 語言,然後使用 native2ascii 在透過 javac 處理之前轉換為 ASCII。其餘的 i18n 和 l12n 則留給您處理...
使用屬性檔案
使用外部屬性檔案將每個使用者的設定檔保留在建置檔案之外,尤其是密碼。屬性檔案也可以用於根據單一屬性的值動態設定多個屬性,只需從來源屬性動態產生屬性檔名即可。它們也可以用作多個建置檔案中常數的來源。
使用 Jikes 加快編譯速度
jikes 編譯器 通常比 javac 快很多,會執行相依性檢查,而且錯誤訊息通常比較好(通常)。取得它。然後將 build.compiler 設定為「jikes」,以便在您的建置檔案中使用它。在您的建置檔案中明確執行此操作有點可疑,因為它要求整個團隊(和子專案)也使用 jikes,而這只有在小型封閉原始碼專案中才能控制。但是,如果您在環境中設定 ANT_OPTS = -Dbuild.compiler=jikes,則系統中所有建置都會自動使用 Jikes,而其他人則可以選擇自己的編譯器,或讓 ant 選擇適合目前 Java 版本的編譯器。
#include 目標以簡化多個 build.xml 專案
您可以使用 XML 解析器本身將 XML 檔案匯入建置檔案。這讓多專案開發程式可以透過參照共用程式碼,而不是剪貼重用。它也讓您可以建立一個標準任務檔案,可以隨著時間重複使用。由於匯入機制低於 Ant 辨識的層級,因此將其視為等同於「舊式」語言 C 和 C++ 的 #include 機制。

有兩種包含機制,一種適用於所有解析器的醜陋機制,以及一種乾淨的機制。醜陋的方法是 Ant1.5 及更早版本中唯一可用的方法:-

    <!DOCTYPE project [
      <!ENTITY propertiesAndPaths SYSTEM "propertiesAndPaths.xml">
      <!ENTITY taskdefs SYSTEM "taskdefs.xml">
    ]>  
    
        &propertiesAndPaths;
        &taskdefs;
Ant1.6 中較乾淨的方法是 <import> 任務,它會將整個建置檔案匯入其他專案。實體包含範例幾乎可以用兩個匯入陳述式取代:-
 <import file="propertiesAndPaths.xml">
 <import file="taskdefs.xml">
我們說幾乎是頂層宣告(屬性和任務定義)不會插入到 XML 檔案中,就在匯入陳述式所在的位置,而是新增到檔案的結尾。這是因為匯入程序會在主建置檔案被剖析後,在執行期間進行,而 XML 實體擴充則會在剖析程序期間處理。

<import> 任務會執行強大的動作,例如讓您覆寫目標,並使用 ant 屬性來命名要匯入檔案的位置。請參閱文件,以了解這些功能的具體資訊。

在您過度使用 XML 包含之前,請注意 ant 任務讓您可以在任何其他建置檔案中呼叫任何目標 - 並將您的所有屬性設定傳播到該目標。因此,您實際上可以擁有一組公用程式目標 - 「deploy-to-stack-a」、「email-to-team」、「cleanup-installation」,可以從您的任何主建置檔案中呼叫,也許使用細微變更的參數。確實,在幾個專案之後,您也許可以建立一個可重複使用的核心建置檔案,其中包含基本 Java 開發專案的核心目標 - 編譯、偵錯、部署 - 專案特定的建置檔案會使用自己的設定呼叫這些目標。如果您能達成此目標,那麼您絕對正在提升您的軟體成熟度階梯。經過一番努力,您也許可以從 SEI CMM 等級 0 組織「個人英雄主義還不夠」進步到 SEI CMM 等級 1,「專案只能因為個人英雄主義而成功」

注意,ant 會複製您的所有屬性,除非將 inheritall 屬性設定為 false。在該屬性存在之前,您必須小心命名所有建置檔案中的所有屬性定義,以防止呼叫者的屬性意外覆寫被呼叫的屬性,現在您只需要記得在 <<ant> 任務的所有使用中設定 inheritall="false" 即可。

透過 XSL 實作複雜的 Ant 建置
XSLT 可用於從來源 xml 檔案動態產生 build.xml 檔案,並使用 xslt 任務控制轉換。這是目前建議用於動態建立複雜建置檔案的策略。然而,它的使用顯然仍然相當罕見 - 這表示您將處於技術的最前沿。
變更呼叫指令碼
透過撰寫您自己的呼叫指令碼 - 使用 DOS、Unix 或 Perl 指令碼作為起點 - 您可以修改 Ant 的設定和行為以符合個別專案。例如,您可以使用 ANT_HOME 的替代變數作為基礎,以不同的方式延伸類別路徑,或從介面目錄中的所有 .jar 檔案動態建立新的命令列屬性「project.interfaces」。

擁有在 PROJECT_HOME 下的 CVS 控制函式庫樹上執行的自訂呼叫指令碼,也讓您可以在團隊中控制 Ant 版本 - 開發人員可以擁有其他 Ant 副本(如果他們想要的話),但 CVS 樹總是包含用於建置您的專案的 jar 組。

您也可以撰寫呼叫現有 Ant 指令碼的包裝指令碼。這是延伸它們的簡單方法。包裝指令碼可以新增額外的定義和命名明確的目標,重新定義 ANT_HOME,並通常讓開發更容易。請注意,Windows 中的「ant」實際上是「ant.bat」,因此應該從另一個批次檔案呼叫,並使用「CALL ant」陳述式 - 否則它永遠不會傳回您的包裝指令碼。

撰寫所有可從 Ant 呼叫的程式碼
這看起來有點奇怪且理想化,但其意思是你應該撰寫所有 Java 程式碼,彷彿它在未來某個時間點可能會被呼叫為函式庫。因此,不要在程式碼中深入放置對 System.exit() 的呼叫 - 如果你想要退出幾個函式,請改為引發例外,並讓 main() 處理它。

再進一步,考慮將 Ant Task 介面提供給程式碼,作為功能的次要、主要甚至唯一介面。Ant 實際上是 Java 應用程式的絕佳開機載入程式,因為它處理類別路徑設定,而且你可以重複使用所有內建的任務,進行前言和後記工作。有些專案,例如 XDoclet,只在 Ant 下執行,因為那是正確的位置。

使用 replace 任務以編程方式修改專案中的文字檔。
想像你的專案有一些原始檔 - BAT 檔、ASPX 頁面(!),任何需要在編譯時針對特定安裝進行靜態自訂的內容,例如由專案的某些屬性驅動,例如 JVM 選項或將錯誤導向的 URL。replace 任務可用於修改檔案,取代文字並建立針對該建置或目的地自訂的版本。當然,每個目的地的自訂應延遲到安裝,但如果你使用 Ant 進行遠端安裝,這突然變得可行。
使用郵件清單
有兩個與 Ant 相關的 郵件清單,使用者和開發人員。Ant 使用者是所有與使用 Ant 相關的問題都應該去的地方。安裝、語法、程式碼範例等 - 在那裡發布你的問題,或搜尋檔案,看看查詢是否已發布並獲得解答。Ant-developer 是 Ant 開發進行的地方 - 因此,它不是發布「我在建置專案時收到編譯錯誤」或「我如何建立 zip 檔」等內容的地方。另一方面,如果你實際上正在擴充 Ant,那麼這是詢問有關如何新增新任務、變更現有任務的問題,以及發布你的工作成果的理想場所,如果你希望將它們納入 Ant 原始碼樹。

組合所有部分

在這個世界中,Ant 建置程序看起來像什麼樣子?假設一個單一目錄結構以簡化,則建置檔案應該包含許多頂層目標 子專案「web」、「bean-1」、「bean-2」可以給予它們自己的建置檔案 - web.xmlbean-1.xmlbean-2.xml - 具有相同的進入點。如果它們是程序的一部分,則應考慮與資料庫、網站影像等相關的額外頂層任務。

偵錯/發布切換可以使用在編譯任務之前呼叫的名為「初始化目標」來處理,這些任務定義適當的屬性。Antcall 在這裡是技巧,因為它允許您在建置檔案中設定兩個屬性初始化路徑。

內部目標應用于建置程序

偵錯和發布之間的切換可以透過讓 init-release 有條件地依賴於屬性(例如設定 release.build)來完成 :-
<target name="init-release" if="release.build">
    <property name="build.debuglevel" value="lines,source"/>    
  </target>
然後您有依賴目標(例如「編譯」)依賴於這個條件目標;在那裡設定「預設」屬性,然後實際使用該屬性。由於 Ant 屬性是不可變的,因此如果執行發布目標,其設定將覆寫預設值
<target name="compile" depends="init,init-release">
    <property name="build.debuglevel" value="lines,vars,source"/> 
    <echo>debug level=${build.debuglevel}</echo>
    <javac destdir="${build.classes.dir}"
       debug="true"
       debuglevel="${build.debuglevel}"
       includeAntRuntime="false"
       srcdir="src">
      <classpath refid="compile.classpath"/>
    </javac>
  </target>
因此,我們現在有一個建置,其中發布模式僅包含檔案名稱和行偵錯資訊(對於錯誤報告很有用),而開發系統也包含變數。

定義專案名稱屬性很有用,可以在 init 任務中呼應它。這讓您可以在多檔案建置中找出中斷哪個 Ant 檔案。

哪些內容會進入內部 Ant 任務取決於您自己的專案。一個非常重要的策略是「透過參照降低路徑重新定義」 - 您可以透過給予它們 ID,然後透過「refid」屬性參照它們來重複使用路徑,您應該只需要在檔案中定義一次共用類別路徑;檔案集可以類似地重複使用。

一旦您設定好目錄結構,並定義 Ant 任務,就可以開始編碼。早期優先事項必須是設定自動化測試程序,因為這不僅有助於確保程式碼運作,它驗證建置程序正在運作。

如此而已。當新增原始檔時,不應需要變更建置檔,只有在您想要變更交付成果或建置程序的一部分時才需要變更。在某個時間點,您可能想要大量重新建構整個建置程序、重新建構專案等,但即使如此,您擁有的建置檔應作為分割建置檔程序的基礎,只要將共用屬性拉到所有建置檔都讀取的屬性檔中,保持目標名稱統一,並持續使用專案即可。重新建構原始碼控制系統通常是更困難的工作。

Ant 的限制

在您開始採用 Ant 作為建置程序的唯一機制之前,您需要了解它不具備的功能。

它不是指令碼語言

Ant 讓您宣告您想要執行的動作,並先針對平台和類別庫進行一些測試,以執行一些特定平台的建置。它不讓您指定如何處理錯誤(監聽器類別可以執行此動作),或支援複雜的條件式陳述。

如果您的建置需要處理例外狀況,請查看 sound 監聽器,作為撰寫您自己的監聽器類別的簡單範例。複雜的條件式陳述可以透過讓其他項目執行測試,然後建置適當的 Ant 任務來處理。XSLT 可用於此目的。

它不是 Make

Make 的一些功能,特別是推論規則和相依性檢查,未包含在 Ant 中。這是因為它們是執行建置的「不同」方式。Make 要求您陳述相依性和建置步驟,Ant 希望您陳述任務和它們之間的順序,任務本身可以執行相依性檢查,也可以不執行。使用 Jikes 的完整 Java 建置非常快速,因此相依性檢查相對來說是可爭議的,而許多其他任務(但並非全部)會在執行動作之前,比較原始檔和目標檔的時間戳記。

它不打算成為人類使用的友善語言

XML 不是資訊對人類友善的表示方式。它是對程式而言合理的表示方式,而文字編輯器和原始碼管理系統都可以很好地處理它。但是,複雜的 Ant 檔可能會變得難看,因為 XML 有點難看,而複雜的建置很複雜。使用 XML 註解,讓您上個月編寫的檔案在您回到檔案時仍然有意義,如果您喜歡,請使用適當的 XML 編輯器編輯檔案。

大型專案仍會快速變得複雜

大型軟體專案會產生其自身的複雜性,包含相互依賴的函式庫、漫長的測試週期、困難的部署程序,以及許多人各自處理解決方案的個別部分。這甚至發生在期限迫近、整合問題變得無可克服、週末在工作量方面與平日難以區分,且團隊中有一半的人停止與另一半的人交談之前。Ant 可能簡化建置和測試程序,並可以消除全職的「makefile 工程師」角色,但這並不表示有人可以停止「擁有建置」。負責建置的意義必須超過在他們的系統上輸入「ant all」,這表示他們需要設定要使用的建置工具、一般目標、屬性名稱和檔案的標準,並一般監督子專案的建置程序。在小型專案中,您不需要這麼做 - 但請記住:小型專案在您不注意時會變成大型專案。如果您從一點點程序開始,那麼您可以在需要時擴充它。如果您從沒有開始,那麼在您需要時將會太遲。

您仍然需要軟體專案的所有其他基礎部分

如果您沒有原始程式碼管理系統,您將會陷入困境。如果您沒有將所有內容都放在 SCM 中,包括網頁、依賴的 jar、安裝檔案,您仍然會陷入困境,只是時間早晚的問題。CVS 實際上是免費的,而且與 Ant 搭配使用時運作良好,但 Sourcesafe、Perforce、Clearcase 和 StarTeam 也有 Ant 任務。這些任務讓您可以自動增加建置計數器,並自動執行檔案更新程序。

您也需要某種變更控制程序,以抵抗不受控的功能蔓延。Bugzilla 是此目的的簡單且低成本工具,使用 Ant 和持續測試程序可以快速演進程式碼,以適應那些不可避免的變更。

結尾

軟體開發應該是有趣的。身處於一個緊迫專案的漩渦中,承受著整合的壓力,並試圖在瘋狂的期限內編寫所有程式碼,這可能會很有趣,而且絕對令人興奮。在這個過程中加入一些自動化功能可能會讓事情變得不那麼混亂,也稍微不那麼有趣,但這是一個開始讓您掌控自己的開發流程。您仍然可以玩得很開心,您應該不必擔心太多事情,縮短建置/測試/部署週期,並有更多時間花在功能擴充或滑雪等重要事情上。所以,出去玩得開心!

進一步閱讀

關於作者

Steve Loughran 是一位企業研發實驗室的研究科學家,目前正在休假,為了一個有趣的目標,在不可能的期限內建置生產 Web 服務。他也是 Apache Ant 和 Apache Axis 的提交者,以及使用 Ant 進行 Java 開發的合著者。他認為如果您喜歡這份文件,您會愛上那本書,因為它不僅解釋了 Ant,還深入探討了流程、部署和最佳實務以及其他讓 Ant 真正有用的領域。(直接重寫手冊會比較容易,但那樣不會那麼有用或有趣)。

有關此文件的相關問題,請使用 Ant 郵件清單。