在Ant出現之前
構建和部署Java應用需要使用包括特定平台的腳本
Make文件
各種版本的IDE甚至手工操作的
大雜燴
現在
幾乎所有的開源Java項目都在使用Ant
大多數公司的內部項目也在使用Ant
Ant在這些項目中的廣泛使用自然導致了讀者對一整套Ant最佳實踐的迫切需求
本文總結了我喜愛的Ant技巧或最佳實踐
多數是從我親身經歷的項目錯誤或我聽說的其他人經歷的
恐怖
故事中得到靈感的
比如
有人告訴我有個項目把XDoclet 生成的代碼放入帶有鎖定文件功能的版本控制工具中
當開發者修改源代碼時
他必須記住手工檢出(Check out)並鎖定所有將要重新生成的文件
然後
手工運行代碼生成器
只到這時他才能夠讓Ant編譯代碼
這一方法還存在如下一些問題
生成的代碼無法存儲在版本控制系統中
Ant(本案例中是Xdoclet)應該自動確定下一次構建涉及的源文件
而不應由程序員手工確定
Ant的構建文件應該定義好正確的任務依賴關系
這樣程序員就不必為了完成構建而不得不按照特定順序調用任務
當我開始一個新項目時
我首先編寫Ant構建文件
Ant文件明確地定義構建的過程
並被團隊中的每個程序員使用
本文所列的技巧基於這樣的假定
Ant構建文件是一個必須仔細編寫的重要文件
它應在版本控制系統中得到維護
並被定期進行重構
下面是我的十五大Ant最佳實踐
采用一致的編碼規范 Ant用戶有的喜歡有的痛恨其構建文件的XML語法
與其跳進這一令人迷惑的爭論中
不如讓我們先看一些能保持XML構建文件簡潔的方法
首先也是最重要的
花費時間格式化你的XML讓它看上去很清晰
不論XML是否美觀
Ant都可以工作
但是丑陋的XML很難令人讀懂
倘若你在任務之間留出空行
有規則的縮進
每行文字不超過
列左右
那麼XML令人驚訝地易讀
再加上使用能夠高亮XML語法的優秀編輯器或IDE工具
你就不會有閱讀的麻煩
同樣
精選含意明確
容易讀懂的詞匯來命名任務和屬性
比如
dir
reports就比rpts好
特定的編碼規范並不重要
只要拿出一套規范並堅持使用就行
將buildxml放在項目根目錄中 Ant構建文件build
xml可以放在任何位置
但是放在項目頂級目錄中可以保持項目簡潔
這是最常用的規范
開發者能夠在頂級目錄中找到預期的build
xml
把構建文件放在根目錄中
也能夠使人容易了解項目目錄樹中不同目錄之間的邏輯關系
以下是一個典型的項目目錄層次
[root dir]
| build
xml
+
src
+
lib (包含第三方 JAR包)
+
build (由 build任務生成)
+
dist (由 build任務生成)
當build
xml在頂級目錄時
假設你處於項目某個子目錄中
只要輸入
ant
find compile 命令
不需要改變工作目錄就能夠以命令行方式編譯代碼
參數
find告訴Ant尋找存在於上級目錄中的build
xml並執行
使用單一的構建文件 有人喜歡將一個大項目分解成幾個小的構建文件
每個構建文件分擔整個構建過程的一小部分工作
這確實是看法不同的問題
但是應該認識到
將構建文件分割會增加對整體構建過程的理解難度
要注意在單一構建文件能夠清楚表現構建層次的情況下不要過工程化(over
engineer)
即使你把項目劃分為多個構建文件
也應使程序員能夠在項目根目錄下找到核心build
xml
盡管該文件只是將實際構建工作委派給下級構建文件
也應保證該文件可用
提供良好的幫助說明 應盡量使構建文件自文檔化
增加任務描述是最簡單的方法
當你輸入ant
projecthelp時
你就可以看到帶有描述的任務清單
比如
你可以這樣定義任務
<target name=
compile
description=
Compiles code
output goes to the build dir
>
最簡單的規則是把所有你想讓程序員通過命令行就可以調用的任務都加上描述
對於一般用來執行中間處理過程的內部任務
比如生成代碼或建立輸出目錄等
就無法使用描述屬性
這時
可以通過在構建文件中加入XML注釋來處理
或者專門定義一個help任務
當程序員輸入ant help時來顯示詳細的使用說明
<target name=
help
description=
Display detailed usage information
>
<echo>Detailed help
</echo></target>
提供清除任務 每個構建文件都應包含一個清除任務
用來刪除所有生成的文件和目錄
使系統回到構建文件執行前的初始狀態
執行清空任務後還存在的文件都應處在版本控制系統的管理之下
比如
<target name=
clean
description=
Destroys all
generated files and dirs
>
<delete dir=
${dir
build}
/>
<delete dir=
${dir
dist}
/>
</target>
除非是在產生整個系統版本的特殊任務中
否則不要自動調用clean任務
當程序員僅僅執行編譯任務或其他任務時
他們不需要構建文件事先執行既令人討厭又沒有必要的清空任務
要相信程序員能夠確定何時需要清空所有文件
使用ANT管理任務從屬關系 假設你的應用由Swing GUI組件
Web界面
EJB層和公共應用代碼組成
在大型系統中
你需要清晰地定義每個Java包屬於系統的哪一層
否則任何一點修改都要被迫重新編譯成百上千個文件
糟糕的任務從屬關系管理會導致過度復雜而脆弱的系統
改變GUI面板的設計不應造成Servlet和EJB的重編譯
當系統變得龐大後
稍不注意就可能將依賴於客戶端的代碼引入到服務端
這是因為典型的IDE項目文件編譯任何文件都使用單一的classpath
而Ant能讓你更有效地控制構建活動
設計你的Ant構建文件編譯大型項目的步驟
首先
編譯公共應用代碼
將編譯結果打成JAR包文件
然後
編譯上一層的項目代碼
編譯時依靠第一步產生的JAR文件
不斷重復這一過程
直到最高層的代碼編譯完成
分步構建強化了任務從屬關系管理
如果你工作在底層Java框架上
偶然引用到高層的GUI模板組件
這時代碼不需要編譯
這是由於構建文件在編譯底層框架時在源路徑中沒有包含高層GUI面板組件的代碼
定義並重用文件路徑 如果文件路徑在一個地方一次性集中定義
並在整個構建文件中得到重用
那麼構建文件更易於理解
以下是這樣做的一個例子
<project name=
sample
default=
compile
basedir=
>
<path id=
mon
>
<pathelement
location=
${jdom
jar
withpath}
/>
etc </path>
<path id=
classpath
client
>
<pathelement location=
${guistuff
jar
withpath}
/>
<pathelement location=
${another
jar
withpath}
/>
<!
reuse the common classpath
>
<path refid=
mon
/>
</path>
<target name=
mon
depends=
prepare
>
<javac destdir=
${dir
build}
srcdir=
${dir
src}
>
<classpath
refid=
mon
/>
<include
name=
com/oreilly/common/**
/>
</javac>
</target>
</project>
當項目不斷增長構建日益復雜時
這一技術越發體現出其價值
你可能需要為編譯不同層次的應用定義各自的文件路徑
比如運行單元測試的
運行應用程序的
運行Xdoclet的
生成JavaDocs的等等不同路徑
這種組件化路徑定義的方法比為每個任務單獨定義路徑要優越得多
否則
很容易丟失任務從屬關系的軌跡
定義恰當的任務從屬關系 假設dist任務從屬於jar任務
那麼哪個任務從屬於compile任務哪個任務從屬於prepare任務呢?Ant構建文件最終定義了任務的從屬關系圖
它必須被仔細地定義和維護
應該定期檢查任務的從屬關系以保證構建工作得到正確執行
大的構建文件隨著時間推移趨向於增加更多的任務
所以到最後可能由於不必要的從屬關系導致構建工作非常困難
比如
你可能發現在程序員只需編譯一些沒有使用EJB的GUI代碼時又重新生成了EJB代碼
以
優化
的名義忽略任務的從屬關系是另一種常見的錯誤
這種錯誤迫使程序員為了得到恰當的結果必須記住並按照特定的順序調用一串任務
更好的做法是
提供描述清晰的公共任務
這些任務包含正確的任務從屬關系
另外提供一套
專家
任務讓你能夠手工執行個別的構建步驟
這些任務不提供完整的構建過程
但是讓那些專家用戶在快速而惱人的編碼期間能夠跳過某些步驟
使用屬性 任何需要配置或可能發生變化的信息都應作為Ant屬性定義下來
對於在構建文件中多次出現的值也同樣處理
屬性既可以在構建文件頭部定義
也可以為了更好的靈活性而在單獨的屬性文件中定義
以下是在構建文件中定義屬性的樣式
<project name=
sample
default=
compile
basedir=
>
<property name=
dir
build
value=
build
/>
<property name=
dir
src
value=
src
/>
<property name=
jdom
home
value=
/java
tools/jdom
b
/>
<property name=
jdom
jar
value=
jdom
jar
/>
<
From:http://tw.wingwit.com/Article/program/Java/ky/201311/28340.html