ant 是 java 方面有用的建構工具,貫穿開發的編譯,打包,測試,部署等各個階段,其實對于前端開發,如果合理利用同樣能夠大幅減少繁瑣的建構過程,擺脫各個平台晦澀異化的批處理腳本。
1.檔案與目錄準備操作
主要包括前期的一些準備工作:例如最常用的将源碼複制到建構目錄,準備進一步的操作,主要利用 copy 任務
<copy todir="目的地目錄"
encoding="讀取編碼"
outputencoding="寫入編碼">
<fileset dir="檔案來源目錄"
includes="**/*(檔案名稱正則)"/>
</copy>
其中 fileset 表示無序檔案集合,若要求有序,可替換為 filelist 有序清單以及廣義的 path。 注意點為 fileset 的 includes 與 excludes 屬性,其中 ** 比對0個或多個目錄,*比對0個或多個檔案。在copy過程中也可進行一些過濾處理,設定過濾器集合對源碼進行一些标記替換,如加入建構時間:
<!--設定日期格式-->
<tstamp>
<format property="timestamp.isoformat"
pattern="yyyy-MM-dd' 'HH:mm:ss" locale="en"/>
</tstamp>
<copy ...">
<fileset ... />
<filterset>
<filter token="TIMESTAMP" value="${timestamp.isoformat}"/>
</filterset>
</copy>
即将源碼中的預設标記 @[email protected],替換為目前時間。其它還有 mkdir , move 等類似對對應批處理指令的加強。
2.壓縮
對應于編譯型語言建構的編譯階段,壓縮優化 源碼 ,推薦 css采用 yuicompressor ,JavaScript采用 google closure-compiler 。ant 可以通過 java 任務來友善地調用外部 java 程式,并且在同一 jvm 内運作也避免了通過 exec 執行外部程式的效率問題。不過由于壓縮器每次隻能針對單個源檔案進行操作,這時就需要使用 ant 的 批處理任務(bulk task):apply
<apply executable="java"
dest="目的地"
failοnerrοr="true"
parallel="并行執行"
>
<fileset dir="css源位址"
includes="**/*.css"/>
<arg line="-jar"/>
<arg path="yuicompressor.jar"/>
<arg line="--charset ${charset}"/>
<srcfile/>
<arg line="-o"/>
<targetfile/>
<mapper type="regexp" from="^(.*)\.(css|js)$" to="\1-min.\2"/>
</apply>
兩個比較關鍵的元素為 srcfile 與 targetfile ,srcfile 注意用fileset的每個檔案替換掉 srcfile,并且經過 mapper 轉換後,用轉換後包含完整路徑的檔案名替換掉 targetfile,達到了對檔案集合中的每個檔案逐一執行 yuicompressor 的目的。
3.打包
對于前端代碼,打包的意義在于減少http連結數 ,主要用到的 ant 任務:concat ,将多個檔案合并為一個檔案
<concat destfile="目的檔案"
encoding="讀取編碼"
outputencoding="寫入編碼">
<filelist .../>
</concat>
同 copy 類似,若要求合并後的檔案内容順序,則使用filelist,否則使用fileset即可。
注意:
windows 下儲存 utf-8 編碼的檔案時常帶 bom 标記 ,而多個 bom 檔案如果使用 concat 原封不動合并起來,那麼實際上該合并後的腳本是文法錯誤的,這時合并時需要使用 filterchain 來消除檔案中的 bom 标記:
<concat destfile="z-pkg.js" encoding="utf-8" outputencoding="utf-8">
<path location="x"/>
<path location="y"/>
<filterchain>
<deletecharacters chars="" />
</filterchain>
</concat>
而另一方面 ie 載入 utf-8 格式的檔案如果不帶 bom ,則會有奇異問題(緩存情況下的動态加載報錯),這是最好合并後再使用 header 把 bom 加上:
<concat destfile="z-pkg.js" encoding="utf-8" outputencoding="utf-8">
<header filtering="no" trimleading="yes"></header>
<path location="x"/>
<path location="y"/>
<filterchain>
<deletecharacters chars="" />
</filterchain>
</concat>
4.部署編碼注意
2010-09-25 update:
打包與壓縮隻是單單對文本進行變換處理,并沒有改變檔案的編碼,而最終部署到應用環境時,常常要求對編碼的普适性,在中文環境中則不可避免會遇到各種各樣的亂碼,還好 javascript 支援 unicode 轉義字元 ,
In string literals, regular expression literals, and identifiers, any character (code unit) may also be expressed as a Unicode escape sequence consisting of six characters, namely \u plus four hexadecimal digits.
通過 native2ascii 任務(yui compressor 可忽略這步),可以将代碼中的中文字元轉換為 "\uxxxx" 對應的 unicode 表示。
<native2ascii
encoding="${charset}"
src="${build.dir}"
dest="${build.dir}"
includes="**/*-pkg-min.js"
>
<mapper type="regexp" from="^(.*)\.js$" to="\1-ascii.js"/>
</native2ascii>
屬性同 copy 部分,mapper 同 apply 部分。
5.分支與循環
如果想要對建構過程進行細粒度的控制,例如根據依賴條件進行特定的建構,則可使用 ant contrib 第三方任務包的 if 任務 。if的判斷條件可以直接使用ant的condition 任務的條件。
xmlns:ac="antlib:net.sf.antcontrib"
<ac:if>
<!--能否上網?-->
<http url="http://www.github.com"/>
<ac:then>
<!--能-->
</ac:then>
<ac:else>.
<!--不能-->
</ac:else>
</ac:if>
另外如果代碼依賴于目錄組織結構,互相間具備平行關系,則可使用循環 for 任務,依次對所有目錄進行操作,使得建構具備良好的擴充性。
<ac:for param="biz">
<path>
<dirset dir="循環目錄集合父目錄" includes="*"/>
</path>
<sequential>
<!--通過 @{biz} 得到單個枚舉-->
</sequential>
</ac:for>
6.多子產品組織
當項目中多個子產品間建構檔案具備明顯的重複目标(target)時,可通過 import 任務,利用 template 模式抽像公共目标為模闆,各個子子產品引入公共模闆後進行特化的目标重寫或者更簡單的屬性重定義。
公共模闆:
<project>
<target name="common">
取 屬性 ${p1} 進行操作
</target>
</project>
子子產品
<project>
定義 p1 屬性
導入公共目标模闆
<import ...>
</project>
子子產品可繼承公共模闆的所有目标,隻需定義相應屬性即可。
7.多子產品建構
目前為止,一個項目的所有子子產品都有了自己獨立的建構檔案,若我們想一次将整個項目建構,則需要對所有的子子產品運作各自的建構,這時就需要 subant 任務,運作建構檔案的集合。
<subant target="所有的建構目标" inheritall="子建構同父建構屬性獨立">
<fileset dir="所有建構檔案來源" includes="**/build.xml"/>
</subant>
其中fileset 即為建構檔案的集合。
8.擴充 ant 指令
待續
總結
ant 初始是作為聲明式語言而設計,而由于其接口開放性,自從出現了 ant contrib 等第三方任務包,使其具備了過程化,越來越接近于傳統的批處理腳本,并且由于 java 天生的跨平台性以及 xml 完善的描述性文法,相信在 java 建構之外也會有廣闊的天地。