Ant 下载、安装、使用、教程全面了解「建议收藏」
Eclipse 內置了 Ant 。 Ant 是一種類似于批處理程序的軟件包,它主要繁瑣的工作是編寫和調試自動處理腳本(一個 XML 文件),但只要有了這個腳本,我們就可以一鍵完成所有的設定工作。
本節還是以 myswt 這個應用程序項目的打包為例,用 Ant 來完成“編譯->打成 JAR 包->復制項目引用庫->復制本地化文件 swt-win32-3063.dll ->輸出 API 文檔”這五步。
1 、在 myswt 項目根目錄下,創建最主要的 build.xml 文件
<?xml version=1.0?>
<project name=myswt project default=api_doc>
<!– 定義目錄變量 –>
<property name=src.dir value=src />
<property name=bin.dir value=bin />
<property name=eclipse_plugins.dir value=c:/eclipse/plugins />
<property name=dist.dir value=d:/dist />
<property name=doc.dir value=${dist.dir}/api />
<property name=swt.dll value=swt-win32-3063.dll />
<!– 定義編譯文件時所引用的庫 –>
<path id=master-classpath>
<fileset dir=${eclipse_plugins.dir} id=project_lib>
<include name=org.eclipse.ui.workbench_3.0.1/workbench.jar/>
<include name=org.eclipse.swt.win32_3.0.1/ws/win32/swt.jar/>
<include name=org.eclipse.jface_3.0.0/jface.jar/>
<include name=org.eclipse.osgi_3.0.1/osgi.jar/>
<include name=org.eclipse.osgi_3.0.1/core.jar/>
<include name=org.eclipse.osgi_3.0.1/resolver.jar/>
<include name=org.eclipse.osgi_3.0.1/defaultAdaptor.jar/>
<include name=org.eclipse.osgi_3.0.1/eclipseAdaptor.jar/>
<include name=org.eclipse.osgi_3.0.1/console.jar/>
<include name=org.eclipse.core.runtime_3.0.1/runtime.jar/>
<include name=org.eclipse.jface.text_3.0.1/jfacetext.jar/>
<include name=org.eclipse.ui.workbench.compatibility_3.0.0/compatibility.jar/>
</fileset>
</path>
<!– 首任務(空) –>
<target name=init/>
<!– 編譯 –>
<target name=compile depends=init>
<delete dir=${bin.dir}/>
<mkdir dir=${bin.dir}/>
<!– 編譯源程序 –>
<javac srcdir=${src.dir} destdir=${bin.dir} target=1.4>
<classpath refid=master-classpath/>
</javac>
<!– 復制圖標目錄 –>
<mkdir dir=${bin.dir}/icons/>
<copy todir=${bin.dir}/icons>
<fileset dir=icons/>
</copy>
</target>
<!– 打包 –>
<target name=pack depends=compile>
<!– bin 目錄壓縮成 JAR 包 –>
<delete dir=${dist.dir}/>
<mkdir dir=${dist.dir} />
<jar basedir=${bin.dir} destfile=${dist.dir}/myswt.jar manifest=ant_manifes.txt>
<exclude name=**/*Test.* />
<exclude name=**/Test*.* />
</jar>
<!– 復制用到的庫 –>
<mkdir dir=${dist.dir}/lib />
<copy todir=${dist.dir}/lib>
<fileset refid=project_lib/>
</copy>
<!– 復制本地化文件 –>
<copy todir=${dist.dir} file=${swt.dll}/>
</target>
<!– 輸出 api 文檔 –>
<target name=api_doc depends=pack>
<delete dir=${doc.dir}/>
<mkdir dir=${doc.dir} />
<javadoc destdir=${doc.dir} author=true version=true use=true windowtitle=MySWT API>
<packageset dir=${src.dir} defaultexcludes=yes/>
<doctitle><![CDATA[<h1>MySWT Project</h1>]]></doctitle>
<bottom><![CDATA[<i>Document by ChenGang 2005.</i>]]></bottom>
</javadoc>
</target>
</project>
代碼說明:
( 1 ) property 項是定義變量,比如 <property name=swt.dll value=swt-win32-3063.dll /> ,就是定義一個變量: swt.dll=swt-win32-3063.dll 。以后用這個變量則是這樣: ${swt.dll} 。
一般盡量將今后可能會變動的目錄、文件等定義成變量,以方便維護。不象 Java 變量有類型的區分, Ant 變量是不區別目錄、文件等的,所以為了見名知意,在取變量名時,目錄都加“ dir ”后綴,這個后綴是可以任取名的。
下面給出本例用到的變量的含義:
l src.dir - Java 源文件路徑。 value=src 的 src 是一個相對路徑,它相對的是 build.xml 的所在目錄位置(即項目根目錄)。
l bin.dir - Java 編譯文件的輸出路徑
l eclipse_plugins.dir - eclipse 的 plugins 目錄
l dist.dir - 打包文件的存放目錄
l doc.dir - API 文檔的存放目錄,這里用到了 dist.dir 變量,直接寫 value=d:/dist/api 也未嘗不可。
l swt.dll - SWT 本地化文件。
( 2 ) <path id=master-classpath> ,定義編譯文件時所引用的庫,相當于 classpath 。 <fileset> 項表示一個文件集,再深入一層的 <include> 項,則表示此文件集下的文件,它們的路徑定位相對于 <fileset> 的 dir 屬性。 <fileset> 還有一個 id 屬性,在后面復制引用庫時會用到。
也許有讀者會問:“你是怎么知道要引用這些文件的?”回答:看項目根目錄下的“ .classpath ”文件,就可以知道本項目要引用那些庫了。實際上筆者是把 .classpath 復制一份后,然后用 Editplus 編輯而得。
( 3 )接下來開始定義一些任務。首任務一般都讓它為空(沒有具體任務內容): <target name=init/> 。
( 4 ) Ant 中的任務有著相互的依賴( depends )關系,這些依賴關系是通過 depends 屬性來定義的。當要執行一個任務時, Ant 先去執行這個任務的 depends 任務,……, Ant 就這樣一直往回找下去。比如:在本例的第二行 default=api_doc ,它定義了缺省任務是 api_doc (輸出 api 文檔)->此任務的 depends = pack (打包)-> pack 的 depends = compile (編譯)-> compile 的 depends=init (首任務), init 沒有 depends 。于是, Ant 就從 init 開始依次往回執行任務: init -> compile -> pack -> api_doc 。
如果你不想“輸出 api 文檔”,則將第二行的缺省任務定義成 default=pack 即可,這時整個任務鏈就拋開了 api_doc 。
( 5 ) <delete dir=${bin.dir}/> 刪除目錄。 <mkdir dir=${bin.dir}/> 新建目錄
( 6 )編譯源程序,如下
<javac srcdir=${src.dir} destdir=${bin.dir} target=1.4>
<classpath refid=master-classpath/>
</javac>
l srcdir - 源文件目錄,其子目錄中的源文件也會被 javac.exe 編譯。
l destdir - 編譯文件輸出目錄。
l target - 以 JDK1.4 為編譯目標。
l classpath - 編譯的 classpath 設置, refid 是指引用前面設定的 master-classpath 。
( 7 )將 icons (即 myswt/icons )目錄的文件,復制到 myswt/bin/icons 目錄中,如下:
<copy todir=${bin.dir}/icons>
<fileset dir=icons/>
</copy>
( 8 )將文件打成 JAR 包
<jar basedir=${bin.dir} destfile=${dist.dir}/myswt.jar manifest=ant_manifes.txt>
<exclude name=**/*Test.* />
<exclude name=**/Test*.* />
</jar>
l basedir - 源目錄。
l destfile - 目標目錄和打成 JAR 包名。
l manifest - 打包清單文件(后面給出其內容)。
l exclude - 使用了通配符將某一些文件排除不打包(主要是一些測試文件)。
( 9 )如下,將 project_lib 的文件復制到 d:/dist/lib 目錄中。 project_lib 是前面“定義編譯文件時所引用的庫”中的文件集的 id 。結果參數下圖 21.25
<copy todir=${dist.dir}/lib>
<fileset refid=project_lib/>
</copy>
( 10 )將本地化文件復制到 d:/dist 目錄中,如下:
<copy todir=${dist.dir} file=${swt.dll}/>
( 11 )輸出 API 文檔(結果參數下圖 21.26 )
<javadoc destdir=${doc.dir} author=true version=true use=true windowtitle=MySWT API>
<packageset dir=${src.dir} defaultexcludes=yes/>
<doctitle><![CDATA[<h1>MySWT Project</h1>]]></doctitle>
<bottom><![CDATA[<i>Document by ChenGang 2005.</i>]]></bottom>
</javadoc>
l destdir - 目標路徑 d:/dist/api
l packageset - 源文件目錄
l doctitle - 標題
l bottom - 標尾。
2 、創建打包清單
為了避免和原來的 manifes.txt 同名,在項目根目錄建立一個名為 ant_manifes.txt 的文件。這個文件內容中最長的是 Class-Path 項,沒有必要一個個字符的敲入,它可以由項目根目錄下的“ .classpath ”編輯而得。
ant_manifes.txt 內容如下:
Manifest-Version: 1.0
Main-Class: jface.dialog.wizard.WizardDialog1
Class-Path: ./lib/org.eclipse.ui.workbench_3.0.1/workbench.jar ./lib/org.eclipse.swt.win32_3.0.1/ws/win32/swt.jar
./lib/org.eclipse.jface_3.0.0/jface.jar ./lib/org.eclipse.osgi_3.0.1/osgi.jar ./lib/org.eclipse.osgi_
3.0.1/core.jar ./lib/org.eclipse.osgi_3.0.1/resolver.jar ./lib/org.eclipse.osgi_3.0.1/defaultAdaptor.ja
r ./lib/org.eclipse.osgi_3.0.1/eclipseAdaptor.jar ./lib/org.eclipse.osgi_3.0.1/console.jar ./lib/org.ecl
ipse.core.runtime_3.0.1/runtime.jar ./lib/org.eclipse.jface.text_3.0.1/jfacetext.jar ./lib/org.eclipse.u
i.workbench.compatibility_3.0.0/compatibility.jar
3 、如下圖 21.23 所示,選擇“ Ant 構建”來運行 Ant 。
圖 21.23 運行“ Ant 構建”
運行“ Ant 構建”后的結果如下圖 21.23 - 26 所示。
圖 21.24 控制臺的輸出
圖 21.25 輸出文件的目錄結構圖
圖 21.26 輸出的 API 文檔效果圖
4 、運行打包結果
除了清單文件 MANIFEST.MF 之外, myswt.jar 文件和 21.1 節所得的 myswt.jar 一樣。本節沒有創建 run.bat 批處理文件,而是用下圖 21.27 所示的“右擊 myswt.jar ->打開方式-> javaw ”的方式來運行 myswt.jar 。
圖 21.27 運行 myswt.jar
ant命令總結
1Ant是什么
ApacheAnt是一個基于Java的生成工具。
生成工具在軟件開發中用來將源代碼和其他輸入文件轉換為可執行文件的形式(也有可能轉換為可安裝的產品映像形式)。隨著應用程序的生成過程變得更加復雜,確保在每次生成期間都使用精確相同的生成步驟,同時實現盡可能多的自動化,以便及時產生一致的生成版本
2下載、安裝Ant
安裝Ant
下載.zip文件,解壓縮到c:/ant1.3(后面引用為%ANT_HOME%)
2.1在你運行Ant之前需要做一些配置工作。
?將bin目錄加入PATH環境變量。
?設定ANT_HOME環境變量,指向你安裝Ant的目錄。在一些OS上,Ant的腳本可以猜測ANT_HOME(Unix和WindosNT/2000)-但最好不要依賴這一特性。
?可選地,設定JAVA_HOME環境變量(參考下面的高級小節),該變量應該指向你安裝JDK的目錄。
注意:不要將Ant的ant.jar文件放到JDK/JRE的lib/ext目錄下。Ant是個應用程序,而lib/ext目錄是為JDK擴展使用的(如JCE,JSSE擴展)。而且通過擴展裝入的類會有安全方面的限制。
2.2運行Ant
運行Ant非常簡單,當你正確地安裝Ant后,只要輸入ant就可以了。
? 沒有指定任何參數時,Ant會在當前目錄下查詢build.xml文件。如果找到了就用該文件作為buildfile。如果你用-find選項。 Ant就會在上級目錄中尋找buildfile,直至到達文件系統的根。要想讓Ant使用其他的buildfile,可以用參數– buildfilefile,這里file指定了你想使用的buildfile。
?可以指定執行一個或多個target。當省略target時,Ant使用標簽<project>的default屬性所指定的target。
命令行選項總結:
ant[options][target[target2[target3]…]]
Options:
-helpprintthismessage
-projecthelpprintprojecthelpinformation
-versionprinttheversioninformationandexit
-quietbeextraquiet
-verbosebeextraverbose
-debugprintdebugginginformation
-emacsproducelogginginformationwithoutadornments
-logfilefileusegivenfileforlogoutput
-loggerclassnametheclassthatistoperformlogging
-listenerclassnameaddaninstanceofclassasaprojectlistener
-buildfilefileusespecifiedbuildfile
-findfilesearchforbuildfiletowardstherootofthefilesystemandusethefirstonefound
-Dproperty=valuesetpropertytovalue
例子
ant
使用當前目錄下的build.xml運行Ant,執行缺省的target。
ant-buildfiletest.xml
使用當前目錄下的test.xml運行Ant,執行缺省的target。
ant-buildfiletest.xmldist
使用當前目錄下的test.xml運行Ant,執行一個叫做dist的target。
ant-buildfiletest.xml-Dbuild=build/classesdist
使用當前目錄下的test.xml運行Ant,執行一個叫做dist的target,并設定build屬性的值為build/classes。
3編寫build.xml
Ant的buildfile是用XML寫的。每個buildfile含有一個project。
buildfile中每個task元素可以有一個id屬性,可以用這個id值引用指定的任務。這個值必須是唯一的。(詳情請參考下面的Task小節)
3.1Projects
project有下面的屬性:
AttributeDescriptionRequired
name項目名稱.No
default當沒有指定target時使用的缺省targetYes
basedir用于計算所有其他路徑的基路徑。該屬性可以被basedirproperty覆蓋。當覆蓋時,該屬性被忽略。如果屬性和basedirproperty都沒有設定,就使用buildfile文件的父目錄。No
項目的描述以一個頂級的<description>元素的形式出現(參看description小節)。
一個項目可以定義一個或多個target。一個target是一系列你想要執行的。執行Ant時,你可以選擇執行那個target。當沒有給定target時,使用project的default屬性所確定的target。
3.2Targets
一個target可以依賴于其他的target。例如,你可能會有一個target用于編譯程序,一個target用于生成可執行文件。你在生成可執行文件之前必須先編譯通過,所以生成可執行文件的target依賴于編譯target。Ant會處理這種依賴關系。
然而,應當注意到,Ant的depends屬性只指定了target應該被執行的順序-如果被依賴的target無法運行,這種depends對于指定了依賴關系的target就沒有影響。
Ant會依照depends屬性中target出現的順序(從左到右)依次執行每個target。然而,要記住的是只要某個target依賴于一個target,后者就會被先執行。
<targetname=”A”/>
<targetname=”B”depends=”A”/>
<targetname=”C”depends=”B”/>
<targetname=”D”depends=”C,B,A”/>
假定我們要執行targetD。從它的依賴屬性來看,你可能認為先執行C,然后B,最后A被執行。錯了,C依賴于B,B依賴于A,所以先執行A,然后B,然后C,最后D被執行。
一個target只能被執行一次,即時有多個target依賴于它(看上面的例子)。
如果(或如果不)某些屬性被設定,才執行某個target。這樣,允許根據系統的狀態(javaversion,OS,命令行屬性定義等等)來更好地控制build的過程。要想讓一個target這樣做,你就應該在target元素中,加入if(或unless)屬性,帶上target因該有所判斷的屬性。例如:
<targetname=”build-module-A”if=”module-A-present”/>
<targetname=”build-own-fake-module-A”unless=”module-A-present”/>
如果沒有if或unless屬性,target總會被執行。
可選的description屬性可用來提供關于target的一行描述,這些描述可由-projecthelp命令行選項輸出。
將你的tstamptask在一個所謂的初始化target是很好的做法,其他的target依賴這個初始化target。要確保初始化target是出現在其他target依賴表中的第一個target。在本手冊中大多數的初始化target的名字是”init”。
target有下面的屬性:
AttributeDescriptionRequired
nametarget的名字Yes
depends用逗號分隔的target的名字列表,也就是依賴表。No
if執行target所需要設定的屬性名。No
unless執行target需要清除設定的屬性名。No
description關于target功能的簡短描述。No
3.3Tasks
一個task是一段可執行的代碼。
一個task可以有多個屬性(如果你愿意的話,可以將其稱之為變量)。屬性只可能包含對property的引用。這些引用會在task執行前被解析。
下面是Task的一般構造形式:
<nameattribute1=”value1″attribute2=”value2″…/>
這里name是task的名字,attributeN是屬性名,valueN是屬性值。
有一套內置的(built-in)task,以及一些可選task,但你也可以編寫自己的task。
所有的task都有一個task名字屬性。Ant用屬性值來產生日志信息。
可以給task賦一個id屬性:
<tasknameid=”taskID”…/>
這里taskname是task的名字,而taskID是這個task的唯一標識符。通過這個標識符,你可以在腳本中引用相應的task。例如,在腳本中你可以這樣:
<script…>
task1.setFoo(“bar”);
</script>
設定某個task實例的foo屬性。在另一個task中(用java編寫),你可以利用下面的語句存取相應的實例。
project.getReference(“task1”).
注意1:如果task1還沒有運行,就不會被生效(例如:不設定屬性),如果你在隨后配置它,你所作的一切都會被覆蓋。
注意2:未來的Ant版本可能不會兼容這里所提的屬性,因為很有可能根本沒有task實例,只有proxies。
3.4Properties
一個project可以有很多的properties??梢栽赽uildfile中用propertytask來設定,或在Ant之外設定。一個 property有一個名字和一個值。property可用于task的屬性值。這是通過將屬性名放在”${“和”}”之間并放在屬性值的位置來實現的。例如如果有一個propertybuilddir的值是”build”,這個property就可用于屬性值:${builddir} /classes。這個值就可被解析為build/classes。
內置屬性
如果你使用了<property>task定義了所有的系統屬性,Ant允許你使用這些屬性。例如,${os.name}對應操作系統的名字。
要想得到系統屬性的列表可參考theJavadocofSystem.getProperties。
除了Java的系統屬性,Ant還定義了一些自己的內置屬性:
basedirproject基目錄的絕對路徑(與<project>的basedir屬性一樣)。
ant.filebuildfile的絕對路徑。
ant.versionAnt的版本。
ant.project.name當前執行的project的名字;由<project>的name屬性設定.
ant.java.versionAnt檢測到的JVM的版本;目前的值有”1.1″,“1.2”,“1.3”and“1.4”.
例子
<projectname=”MyProject”default=”dist”basedir=”.”>
<!–setglobalpropertiesforthisbuild–>
<propertyname=”src”value=”.”/>
<propertyname=”build”value=”build”/>
<propertyname=”dist”value=”dist”/>
<targetname=”init”>
<!–Createthetimestamp–>
<tstamp/>
<!–Createthebuilddirectorystructureusedbycompile–>
<mkdirdir=”${build}”/>
</target>
<targetname=”compile”depends=”init”>
<!–Compilethejavacodefrom${src}into${build}–>
<javacsrcdir=”${src}”destdir=”${build}”/>
</target>
<targetname=”dist”depends=”compile”>
<!–Createthedistributiondirectory–>
<mkdirdir=”${dist}/lib”/>
<!–Puteverythingin${build}intotheMyProject-${DSTAMP}.jarfile–>
<jarjarfile=”${dist}/lib/MyProject-${DSTAMP}.jar”basedir=”${build}”/>
</target>
<targetname=”clean”>
<!–Deletethe${build}and${dist}directorytrees–>
<deletedir=”${build}”/>
<deletedir=”${dist}”/>
</target>
</project>
3.5Path-likeStructures
你可以用”:”和”;”作為分隔符,指定類似PATH和CLASSPATH的引用。Ant會把分隔符轉換為當前系統所用的分隔符。
當需要指定類似路徑的值時,可以使用嵌套元素。一般的形式是
<classpath>
<pathelementpath=”${classpath}”/>
<pathelementlocation=”lib/helper.jar”/>
</classpath>
location屬性指定了相對于project基目錄的一個文件和目錄,而path屬性接受逗號或分號分隔的一個位置列表。path屬性一般用作預定義的路徑--其他情況下,應該用多個location屬性。
為簡潔起見,classpath標簽支持自己的path和location屬性。所以:
<classpath>
<pathelementpath=”${classpath}”/>
</classpath>
可以被簡寫作:
<classpathpath=”${classpath}”/>
也可通過<fileset>元素指定路徑。構成一個fileset的多個文件加入path-likestructure的順序是未定的。
<classpath>
<pathelementpath=”${classpath}”/>
<filesetdir=”lib”>
<includename=”**/*.jar”/>
</fileset>
<pathelementlocation=”classes”/>
</classpath>
上面的例子構造了一個路徑值包括:${classpath}的路徑,跟著lib目錄下的所有jar文件,接著是classes目錄。
如果你想在多個task中使用相同的path-likestructure,你可以用<path>元素定義他們(與target同級),然后通過id屬性引用--參考Referencs例子。
path-likestructure可能包括對另一個path-likestructurede的引用(通過嵌套<path>元素):
<pathid=”base.path”>
<pathelementpath=”${classpath}”/>
<filesetdir=”lib”>
<includename=”**/*.jar”/>
</fileset>
<pathelementlocation=”classes”/>
</path>
<pathid=”tests.path”>
<pathrefid=”base.path”/>
<pathelementlocation=”testclasses”/>
</path>
前面所提的關于<classpath>的簡潔寫法對于<path>也是有效的,如:
<pathid=”tests.path”>
<pathrefid=”base.path”/>
<pathelementlocation=”testclasses”/>
</path>
可寫成:
<pathid=”base.path”path=”${classpath}”/>
命令行變量
有些task可接受參數,并將其傳遞給另一個進程。為了能在變量中包含空格字符,可使用嵌套的arg元素。
AttributeDescriptionRequired
value一個命令行變量;可包含空格字符。只能用一個
line空格分隔的命令行變量列表。
file作為命令行變量的文件名;會被文件的絕對名替代。
path一個作為單個命令行變量的path-like的字符串;或作為分隔符,Ant會將其轉變為特定平臺的分隔符。
例子
<argvalue=”-l-a”/>
是一個含有空格的單個的命令行變量。
<argline=”-l-a”/>
是兩個空格分隔的命令行變量。
<argpath=”/dir;/dir2:/dir3″/>
是一個命令行變量,其值在DOS系統上為/dir;/dir2;/dir3;在Unix系統上為/dir:/dir2:/dir3。
References
buildfile元素的id屬性可用來引用這些元素。如果你需要一遍遍的復制相同的XML代碼塊,這一屬性就很有用--如多次使用<classpath>結構。
下面的例子:
<project…>
<target…>
<rmic…>
<classpath>
<pathelementlocation=”lib/”/>
<pathelementpath=”${java.class.path}/”/>
<pathelementpath=”${additional.path}”/>
</classpath>
</rmic>
</target>
<target…>
<javac…>
<classpath>
<pathelementlocation=”lib/”/>
<pathelementpath=”${java.class.path}/”/>
<pathelementpath=”${additional.path}”/>
</classpath>
</javac>
</target>
</project>
可以寫成如下形式:
<project…>
<pathid=”project.class.path”>
<pathelementlocation=”lib/”/>
<pathelementpath=”${java.class.path}/”/>
<pathelementpath=”${additional.path}”/>
</path>
<target…>
<rmic…>
<classpathrefid=”project.class.path”/>
</rmic>
</target>
<target…>
<javac…>
<classpathrefid=”project.class.path”/>
</javac>
</target>
</project>
所有使用PatternSets,FileSets或path-likestructures嵌套元素的task也接受這種類型的引用。
4.1File(Directory)類
4.1.1Mkdir
?創建一個目錄,如果他的父目錄不存在,也會被同時創建。
?例子:
<mkdirdir=”build/classes”/>
?說明:如果build不存在,也會被同時創建
4.1.2Copy
?拷貝一個(組)文件、目錄
?例子:
1.拷貝單個的文件:
<copyfile=”myfile.txt”tofile=”mycopy.txt”/>
2.拷貝單個的文件到指定目錄下
<copyfile=”myfile.txt”todir=”../some/other/dir”/>
3.拷貝一個目錄到另外一個目錄下
<copytodir=”../new/dir”>
<filesetdir=”src_dir”/>
</copy>
4.拷貝一批文件到指定目錄下
<copytodir=”../dest/dir”>
<filesetdir=”src_dir”>
<excludename=”**/*.java”/>
</fileset>
</copy>
<copytodir=”../dest/dir”>
<filesetdir=”src_dir”excludes=”**/*.java”/>
</copy>
5.拷貝一批文件到指定目錄下,將文件名后增加。Bak后綴
<copytodir=”../backup/dir”>
<filesetdir=”src_dir”/>
<mappertype=”glob”from=”*”to=”*.bak”/>
</copy>
6.拷貝一組文件到指定目錄下,替換其中的@標簽@內容
<copytodir=”../backup/dir”>
<filesetdir=”src_dir”/>
<filterset>
<filtertoken=”TITLE”value=”FooBar”/>
</filterset>
</copy>
4.1.3Delete
?刪除一個(組)文件或者目錄
?例子
1.刪除一個文件
<deletefile=”/lib/ant.jar”/>
2.刪除指定目錄及其子目錄
<deletedir=”lib”/>
3.刪除指定的一組文件
<delete>
<filesetdir=”.”includes=”**/*.bak”/>
</delete>
4.刪除指定目錄及其子目錄,包括他自己
<deleteincludeEmptyDirs=”true”>
<filesetdir=”build”/>
</delete>
4.1.4Move
?移動或重命名一個(組)文件、目錄
?例子:
1.移動或重命名一個文件
<movefile=”file.orig”tofile=”file.moved”/>
2.移動或重命名一個文件到另一個文件夾下面
<movefile=”file.orig”todir=”dir/to/move/to”/>
3.將一個目錄移到另外一個目錄下
<movetodir=”new/dir/to/move/to”>
<filesetdir=”src/dir”/>
</move>
4.將一組文件移動到另外的目錄下
<movetodir=”some/new/dir”>
<filesetdir=”my/src/dir”>
<includename=”**/*.jar”/>
<excludename=”**/ant.jar”/>
</fileset>
</move>
5.移動文件過程中增加。Bak后綴
<movetodir=”my/src/dir”>
<filesetdir=”my/src/dir”>
<excludename=”**/*.bak”/>
</fileset>
<mappertype=”glob”from=”*”to=”*.bak”/>
</move>
4.2Java相關
4.2.1Javac
?編譯java原代碼
?例子
1.<javacsrcdir=”${src}”
destdir=”${build}”
classpath=”xyz.jar”
debug=”on”
/>
編譯${src}目錄及其子目錄下的所有。Java文件,。Class文件將放在${build}指定的目錄下,classpath表示需要用到的類文件或者目錄,debug設置為on表示輸出debug信息
2.<javacsrcdir=”${src}:${src2}”
destdir=”${build}”
includes=”mypackage/p1/**,mypackage/p2/**”
excludes=”mypackage/p1/testpackage/**”
classpath=”xyz.jar”
debug=”on”
/>
編譯${src}和${src2}目錄及其子目錄下的所有。Java文件,但是package/p1/**,mypackage/p2/**將被編譯,而 mypackage/p1/testpackage/**將不會被編譯。Class文件將放在${build}指定的目錄下,classpath表示需要用到的類文件或者目錄,debug設置為on表示輸出debug信息
3.<propertyname=”classpath”value=”.;./xml-apis.jar;../lib/xbean.jar;./easypo.jar”/>
<javacsrcdir=”${src}”
destdir=”${src}”
classpath=”${classpath}”
debug=”on”
/>
路徑是在property中定義的
4.2.2java
?執行指定的java類
?例子:
1.<javaclassname=”test.Main”>
<classpath>
<pathelementlocation=”dist/test.jar”/>
<pathelementpath=”${java.class.path}”/>
</classpath>
</java>
classname中指定要執行的類,classpath設定要使用的環境變量
2.<pathid=”project.class.path”>
<pathelementlocation=”lib/”/>
<pathelementpath=”${java.class.path}/”/>
<pathelementpath=”${additional.path}”/>
</path>
<target…>
<rmic…>
<classpathrefid=”project.class.path”/>
</rmic>
</target>
4.3打包相關
4.3.1jar
?將一組文件打包
?例子:
1.<jardestfile=”${dist}/lib/app.jar”basedir=”${build}/classes”/>
將${build}/classes下面的所有文件打包到${dist}/lib/app.jar中
2.<jardestfile=”${dist}/lib/app.jar”
basedir=”${build}/classes”
includes=”mypackage/test/**”
excludes=”**/Test.class”
/>
將${build}/classes下面的所有文件打包到${dist}/lib/app.jar中,但是包括mypackage/test/所有文件不包括所有的Test.class
3.<jardestfile=”${dist}/lib/app.jar”
basedir=”${build}/classes”
includes=”mypackage/test/**”
excludes=”**/Test.class”
manifest=”my.mf”
/>
manifest屬性指定自己的META-INF/MANIFEST.MF文件,而不是由系統生成
4.3.2war
?對Jar的擴展,用于打包Web應用
?例子:
?假設我們的文件目錄如下:
thirdparty/libs/jdbc1.jar
thirdparty/libs/jdbc2.jar
build/main/com/myco/myapp/Servlet.class
src/metadata/myapp.xml
src/html/myapp/index.html
src/jsp/myapp/front.jsp
src/graphics/images/gifs/small/logo.gif
src/graphics/images/gifs/large/logo.gif
?下面是我們的任務的內容:
<wardestfile=”myapp.war”webxml=”src/metadata/myapp.xml”>
<filesetdir=”src/html/myapp”/>
<filesetdir=”src/jsp/myapp”/>
<libdir=”thirdparty/libs”>
<excludename=”jdbc1.jar”/>
</lib>
<classesdir=”build/main”/>
<zipfilesetdir=”src/graphics/images/gifs”
prefix=”images”/>
</war>
?完成后的結果:
WEB-INF/web.xml
WEB-INF/lib/jdbc2.jar
WEB-INF/classes/com/myco/myapp/Servlet.class
META-INF/MANIFEST.MF
index.html
front.jsp
images/small/logo.gif
images/large/logo.gif
4.3.3ear
?用于打包企業應用
?例子
<eardestfile=”${build.dir}/myapp.ear”appxml=”${src.dir}/metadata/application.xml”>
<filesetdir=”${build.dir}”includes=”*.jar,*.war”/>
</ear>
4.4時間戳
在生成環境中使用當前時間和日期,以某種方式標記某個生成任務的輸出,以便記錄它是何時生成的,這經常是可取的。這可能涉及編輯一個文件,以便插入一個字符串來指定日期和時間,或將這個信息合并到JAR或zip文件的文件名中。
這種需要是通過簡單但是非常有用的tstamp任務來解決的。這個任務通常在某次生成過程開始時調用,比如在一個init目標中。這個任務不需要屬性,許多情況下只需<tstamp/>就足夠了。
tstamp不產生任何輸出;相反,它根據當前系統時間和日期設置Ant屬性。下面是tstamp設置的一些屬性、對每個屬性的說明,以及這些屬性可被設置到的值的例子:
屬性說明例子
DSTAMP設置為當前日期,默認格式為yyyymmdd20031217
TSTAMP設置為當前時間,默認格式為hhmm1603
TODAY設置為當前日期,帶完整的月份2003年12月17日
例如,在前一小節中,我們按如下方式創建了一個JAR文件:
<jardestfile=”package.jar”basedir=”classes”/>
在調用tstamp任務之后,我們能夠根據日期命名該JAR文件,如下所示:
<jardestfile=”package-${DSTAMP}.jar”basedir=”classes”/>
因此,如果這個任務在2003年12月17日調用,該JAR文件將被命名為package-20031217.jar。
還可以配置tstamp任務來設置不同的屬性,應用一個當前時間之前或之后的時間偏移,或以不同的方式格式化該字符串。所有這些都是使用一個嵌套的format元素來完成的,如下所示:
<tstamp>
<formatproperty=”OFFSET_TIME”
pattern=”HH:mm:ss”
offset=”10″unit=”minute”/>
</tstamp>
上面的清單將OFFSET_TIME屬性設置為距離當前時間10分鐘之后的小時數、分鐘數和秒數。
用于定義格式字符串的字符與java.text.SimpleDateFormat類所定義的那些格式字符相同
4.5執行SQL語句
?通過jdbc執行SQL語句
?例子:
1.<sql
driver=”org.gjt.mm.mysql.Driver”
url=”jdbc:mysql://localhost:3306/mydb”
userid=”root”
password=”root”
src=”data.sql”
/>
2.<sql
driver=”org.database.jdbcDriver”
url=”jdbc:database-url”
userid=”sa”
password=”pass”
src=”data.sql”
rdbms=”oracle”
version=”8.1.”
>
</sql>
只有在oracle、版本是8.1的時候才執行
4.6發送郵件
?使用SMTP服務器發送郵件
?例子:
<mailmailhost=”smtp.myisp.com”mailport=”1025″subject=”Testbuild”>
<fromaddress=”me@myisp.com”/>
<toaddress=”all@xyz.com”/>
<message>The${buildname}nightlybuildhascompleted</message>
<filesetdir=”dist”>
<includesname=”**/*.zip”/>
</fileset>
</mail>
?mailhost:SMTP服務器地址
?mailport:服務器端口
?subject:主題
?from:發送人地址
?to:接受人地址
?message:發送的消息
?fileset:設置附件
====================================================================
在ANT 出現之前,編譯和部署Java應用需要使用包括特定平臺的腳本、Make文件、不同的IDE以及手工操作等組成的大雜燴?,F在,幾乎所有的開源Java項目都在使用Ant,許多公司的開發項目也在使用Ant。Ant的大量使用,也自然帶來了對總結Ant最佳實踐的迫切需求。
本文總結了我喜好的Ant最佳實踐,很多是從親身經歷的項目錯誤,或從其他開發者的“恐怖”故事中得到的靈感的。比如,有人告訴我有個項目將XDoclet生成的代碼放入鎖定文件的版本控制工具中。單開發者修改源代碼時,他必須記住手工檢出(Checkout)并鎖定所有將要重生成的文件。然后,手工運行代碼生成器,當他能夠讓Ant編譯代碼時,這一方法還存在一些問題:
生成的代碼無法存儲在版本控制系統中
Ant(本案例中是Xdoclet)應該自動確定下一次構建涉及的源文件,而不應由程序員人工確定。
Ant的構建文件應該定義好正確的任務依賴關系,這樣程序員不必按照特定順序調用任務。
當我開始一個新項目時,我首先編寫Ant構建文件。文件定義構建的過程,并為團隊中的每個程序員都使用。本文所有的最佳實踐假設Ant構建文件是一個必須精心編寫的重要文件,它應在版本控制系統中得到維護,并定期進行重構。下面是我的十五大Ant最佳實踐。
1.采用一致的編碼規范
Ant用戶不管是喜歡還是痛恨XML構建文件的語法,都愿意跳進這一迷人的爭論中。讓我們先看一些保持XML構建文件簡潔的方法。
首先,也是最重要的,化費時間格式化你的XML讓它看上去很清晰。不過XML是否美觀,Ant都可以工作。但是丑陋的XML很難讀懂。倘若你在任務之間留出空行,有規則的縮進,每行文字不超過90列,那么XML令人驚訝的易讀。再加上好的編輯器或IDE高亮相應的語句,你就不會有如何閱讀的麻煩。同樣,精選有意義明確、容易讀懂的詞匯來命名任務和屬性。比如,dir.reports就比rpts好。并不需要特定的編碼規范,只要有一種規范并堅持使用就好。
2.將build.xml放在項目根目錄中
Ant構建文件build.xml可以放在如何位置,但是放在項目頂層目錄中可以保持項目簡潔。這是最普遍的規范,使開發者能夠在根目錄找到它。同時,也能夠容易了解項目中不同目錄之間的邏輯關系。以下是一個典型的項目層次:
[rootdir]|build.xml+–src+–lib(包含第三方JAR包)+–build(由build任務生成)+–dist(由build任務生成)
當build.xml在頂級目錄時,倘若你在項目某個子目錄中,只要輸入:ant-findcompile命令,不需要改變工作目錄就能夠以命令行方式編譯代碼。參數-find告訴Ant尋找存在于上級目錄中的build.xml并執行。
3.使用單一構建文件
有人喜歡將一個大項目分解到幾個小的構建文件,每個構建文件分擔整個構建過程的一小部分工作。但是應該認識到,將構建文件分割會增加對整個構建過程的理解難度。要注意在單一構建文件能夠清楚表現構建層次的情況下,不要過工程化(over-engineer)。
即使你把項目劃分為多個構建文件,也應使程序員能夠在項目根目錄下找到核心build.xml。盡管該文件只是將實際構建工作委派給下級構建文件,也應保證該文件可用。
4.提供良好的幫助說明
應盡量使構建文件自文檔化。增加任務描述是最簡單的方法。當你輸入ant-projecthelp時,你就可以看到帶有描述的任務清單。比如,你可以這樣定義任務:
<targetname=”compile”description=”Compilescode,outputgoestothebuilddir.”>
最簡單的規則是對所有你希望程序員通過命令行直接調用的任務都加上描述。對于一般用來執行中間處理過程的內部任務,比如生成代碼或建立輸出目錄等,就無法使用描述屬性。
這時,可以通過在構建文件中加入XML注釋來處理。或者專門定義一個help任務,當程序員輸入anthelp時來顯示詳細的使用說明。
<targetname=”help”description=”Displaydetailedusageinformation”><echo>Detailedhelp…</echo></target>
5.提供清空任務
每個構建文件都應包含一個清空任務,刪除所有生成的文件和目錄,使系統回到構建文件執行前的初始狀態。執行清空任務后還存在的文件應處在版本控制系統的管理下。
比如:
<targetname=”clean”description=”Destroysallgeneratedfilesanddirs.”><deletedir=”${dir.build}”/><deletedir=”${dir.dist}”/></target>
除非是在產生整個系統版本的特殊任務中,否則不要自動調用clean任務。當程序員僅僅執行編譯任務或其他任務時,他們不需要構建文件事先執行即令人討厭有沒有必要的清空任務。要相信程序員能夠確定何時需要清空所有文件。
6.使用ANT管理任務從屬關系
假設你的應用由SwingGUI組件、Web界面、EJB層和公共應用代碼組成。在大型系統中,你需要清晰地定義Java包屬于系統的哪一層。否則如何一點修改都要重新編譯成千上百個文件。任務從屬關系管理差會導致過度復雜而脆弱的系統。改變GUI面板的設計不應造成Servlet和EJB的重編譯。
當系統變得龐大后,稍不注意就可能將依賴于客戶端的代碼引入到服務端。這是因為IDE在編譯文件時使用單一的classpath。Ant讓你更有效地控制構建活動。
設計你的構建文件編譯大型項目的步驟:首先,編譯公共應用代碼,將編譯結果打成JAR包文件。然后,編譯上一層的項目代碼,編譯時依靠第一步產生的JAR文件。不斷重復這一過程,直到最高層的代碼編譯完成。
分步構建強化了任務從屬關系管理。如果你工作在底層Java框架上,引用高層的GUI模板組件,這時代碼不需要編譯。這是由于構建文件在編譯底層框架時,在源路徑中沒有包含高層GUI面板組件的代碼。
7.定義并重用文件路徑
如果文件路徑在一個地方集中定義,并在整個構建文件中得到重用,那么構建文件更易于理解。以下是這樣做的一個例子:
<projectname=”sample”default=”compile”basedir=”.”><pathid=”classpath.common”><pathelementlocation=”${jdom.jar.withpath}”/>…etc</path><pathid=”classpath.client”><pathelementlocation=”${guistuff.jar.withpath}”/><pathelementlocation=”${another.jar.withpath}”/><!–reusethecommonclasspath–><pathrefid=”classpath.common”/></path><targetname=”compile.common”depends=”prepare”><javacdestdir=”${dir.build}”srcdir=”${dir.src}”><classpathrefid=”classpath.common”/><includename=”com/oreilly/common/**”/></javac></target></project>
當項目不斷增長,構建日益復雜時,這一技術越發體現出其價值。你可能為編譯不同層次的應用定義各自的文件路徑,比如運行單元測試的、運行應用程序的、運行 Xdoclet的、生成JavaDocs的等等不同路徑。這種組件化路徑定義的方法比為每個任務單獨定義路徑要優越得多。否則,很容易丟失任務任務從屬關系的軌跡。
8.定義恰當的任務參數關系
假設dist任務從屬于jar任務,那么哪個任務從屬于compile任務,哪個任務從屬于prepare任務呢?Ant構建文件最終定義了任務的從屬關系圖,它必須被仔細地定義和維護。應該定期檢查任務的從屬關系以保證構建工作得到正確執行。大的構建文件隨著時間推移趨向于增加更多的任務,所以到最后由于不必要的從屬關系導致構建工作非常困難。比如,你可能發現在程序員只是需要編譯一些沒有使用EJB的GUI代碼時,重新生成EJB代碼。
以“優化”的名義忽略任務的從屬關系是另一種常見的錯誤。這種錯誤迫使程序員為了得到恰當的結果必須記住并按照特定的順序調用一串任務。更好的做法是:提供描述清晰的公共任務,這些任務包含正確的任務從屬關系;另外提供一套“專家”任務讓你能夠手工執行個別的構建步驟,這些任務不提供完整的構建過程,但是讓那些專家在快速而惱人的編碼期間跳過某些步驟
9.使用配置屬性
任何需要配置或可能發生變化的信息都應作為Ant屬性定義下來。對于在構建文件中多次出現的值也同樣處理。屬性既可以在構建文件頭部定義,也可以為了更好的靈活性而在單獨的屬性文件中定義。以下是在構建文件中定義屬性的樣式:
<projectname=”sample”default=”compile”basedir=”.”><propertyname=”dir.build”value=”build”/><propertyname=”dir.src”value=”src”/><propertyname=”jdom.home”value=”../java-tools/jdom-b8″/><propertyname=”jdom.jar”value=”jdom.jar”/><propertyname=”jdom.jar.withpath”value=”${jdom.home}/build/${jdom.jar}”/>etc…</project>
或者你可以使用屬性文件:
<projectname=”sample”default=”compile”basedir=”.”><propertyfile=”sample.properties”/>etc…</project>
在屬性文件sample.properties中:
dir.build=builddir.src=srcjdom.home=../java-tools/jdom-b8jdom.jar=jdom.jarjdom.jar.withpath=${jdom.home}/build/${jdom.jar}
用一個獨立的文件定義屬性是有好處的,它可以清晰地定義構建中的可配置部分。另外,在開發者工作在不同操作系統的情況下,你可以在不同的平臺上提供該文件的不同版本。
10.保持構建過程獨立
為了最大限度的擴展性,不要應用外部路徑和庫文件。最重要的是不要依賴于程序員的CLASSPATH設置。取而代之的是,在構建文件中使用相對路徑并定義自己的路徑。如果你引用了絕對路徑如C:/java/tools,其他開發者未必使用與你相同的目錄結構,所以就無法使用你的構建文件
如果你部署開發源碼項目,應該提供包括所有需要的JAR文件的發行版本,當然是在遵守許可協議的基礎上。對于內部項目,相關的JAR文件都應在版本控制系統的管理中,并撿出到大家都知道的位置。
當你不得不應用外部路徑時,應將路徑定義為屬性。使程序員能夠涌適合他們自己的機器的參數重載這些屬性。你也可以使用以下語法引用環境變量:
<propertyenvironment=”env”/><propertyname=”dir.jboss”value=”${env.JBOSS_HOME}”/>
11.使用版本控制系統
構建文件是一個重要的文件,應該象代碼一樣進行版本控制。當你標記你的代碼時,也應用同樣的標簽標記構建文件。這樣當你需要回溯構建舊版本的軟件時,能夠使用相對應的舊版本構建文件。
除構建文件之外,你還應在版本控制中維護第三方JAR文件。同樣,這使你能夠重新構建舊版本的軟件。這也能夠更容易保證所有開發者擁有一致的JAR文件,因為他們都是同構建文件一起從版本控制系統中撿出的。
通常應避免在版本控制系統中存放構建輸出品。倘若你的源代碼很好地得到了版本控制,那么通過構建過程你能夠重新生成任何版本的產品。
12.把Ant作為“最小公分母”
假設你的開發團隊使用IDE,為什么要為程序員通過點擊圖標就能夠構建整個應用而煩惱呢?
IDE 的問題在團隊中是一個關于一致性和重現性的問題。幾乎所有的IDE設計初衷都是為了提高程序員的個人生產率,而不是開發團隊的持續構建。典型的IDE要求每個程序員定義自己的項目文件。程序員可能擁有不同的目錄結構,可能使用不同版本的庫文件,還可能工作在不同的平臺上。這將導致出現這種情況:在A那里運行良好的代碼,到B那里就無法運行。
不管你的開發團隊使用何種IDE,一定要建立所有程序員都能夠使用的Ant構建文件。要建立一個程序員在將新代碼提交版本控制系統前必須執行Ant構建文件的規則。這將確保代碼是經過同一個Ant構建文件構建的。當出現問題時,要使用項目標準的 Ant構建文件,而不是通過某個IDE來執行一個干凈的構建。
程序員可以自由選擇任何他們習慣使用的IDE。但是Ant應作為公共基線以保證永遠是可構建的。
13.使用zipfileset屬性
人們經常使用Ant產生WAR、JAR、ZIP和EAR文件。這些文件通常都要求有一個特定的內部目錄結構,但其往往與你的源代碼和編譯環境的目錄結構不匹配。
一個最常用的方法是寫一個Ant任務按照期望的目錄結構把一大堆文件拷貝到臨時目錄中,然后生成壓縮文件。這不是最有效的方法。使用zipfileset屬性是更好的解決方案。它讓你從任何位置選擇文件,然后把它們按照不同目錄結構放進壓縮文件中。以下是一個例子:
<earearfile=”${dir.dist.server}/payroll.ear”appxml=”${dir.resources}/application.xml”><filesetdir=”${dir.build}”includes=”commonServer.jar”/><filesetdir=”${dir.build}”><includename=”payroll-ejb.jar”/></fileset><zipfilesetdir=”${dir.build}”prefix=”lib”><includename=”hr.jar”/><includename=”billing.jar”/></zipfileset><filesetdir=”.”><includename=”lib/jdom.jar”/><includename=”lib/log4j.jar”/><includename=”lib/ojdbc14.jar”/></fileset><zipfilesetdir=”${dir.generated.src}”prefix=”META-INF”><includename=”jboss-app.xml”/></zipfileset></ear>
在這個例子中,所有JAR文件都放在EAR文件包的lib目錄中。hr.jar和billing.jar是從構建目錄拷貝過來的。因此我們使用zipfileset屬性把它們移動到EAR文件包內部的lib目錄。prefix屬性指定了其在EAR文件中的目標路徑。
14.運行Clean構建任務的測試
假設你的構建文件中有clean和compile的任務,執行以下的測試。第一步,執行antclean;第二步,執行antcompile;第三步,再執行antcompile。第三步應該不作任何事情。如果文件再次被編譯,說明你的構建文件有問題。
構建文件應該只在與輸出文件相關聯的輸入文件發生變化時,才應該執行任務。一個構建文件在不必執行諸如編譯、拷貝或其他工作任務的時候執行這些等任務是低效的。當項目規模增長時,即使是小的低效工作也會成為大的問題。
15.避免特定平臺的Ant包
不管什么原因,有人喜歡用簡單的、名稱叫做compile之類的批文件或腳本裝載他們的產品。當你去看腳本的內容,你會發現以下內容:
antcompile
其實開發人員熟悉Ant,并且完全能夠自己鍵入antcompile。請不要僅僅為了調用Ant而使用特定平臺的腳本。這只會使其他人在首次使用你的腳本時,增加學習和理解的煩擾。除此之外,你不可能提供適用于每個操作系統的腳本,這是真正煩擾其他用戶的地方。
總結
太多的公司依靠手工方法和程序來編譯代碼和生成軟件發布版本。那些不使用Ant或類似工具定義構建過程的開發團隊,花費了令人驚異的時間來捕捉代碼編譯過程中出現的問題,這些在某些開發者那里編譯成功的代碼,到另一些開發者那里卻失敗了。
生成并維護構建腳本不是一項迷人的工作,但卻是一項必需的工作。一個好的Ant構建文件將使你集中到更喜歡的工作——寫代碼中!
無所不能的“螞蟻”–Ant
說他無所不能,好像有點夸張,但是用過Ant之后,感覺真的是只有想不到沒有作不到.Ant,原作者選擇他作為軟件名字的意思是指”令一個簡潔的工具”(Another Neat Tool),而這個真正的名字現在去很少為人所知,但這絲毫不影響他成為最優秀的構建工具.
現在開始我將進入一個”螞蟻”的世界,通過例子,真真正正去了解他!
文章參考資料可以到http://www.manning.com/antbook去下載
Ant的最好學習資料<<使用Ant進行Java開發>>
Ant的官方網站:http://ant.apache.org/
Ant的最新版本:Ant 1.6.5
本文所有的例子運行的環境:JDK1.4.2,Ant1.6.2,eclipse3.0
一.使用Ant運行Java程序
我們先從簡單的Hello學起,目錄結構如下
project–
|src–
| |–org.ant.chapter1.Hello
|bin
|build.xml
以后的例子大多采用此目錄結構,特例會額外聲明
build.xml文件
<?xml version="1.0"?> <project name="project" default="run"> <target name="compile"> <javac destdir="bin" srcdir="src"></javac> </target> <target name="run" depends="compile"> <java classname="org.ant.chapter1.Hello"> </java> </target> </project> |
從結構來看構建文件很簡單,里面的內容大家也一定能夠看得懂,可以看出Ant的核心任務就是target,一個Ant文件有多個target組成,而這些target之間,又有相互的依賴關系–depends,運行的時候默認運行project中指定的target.
javac–編譯java文件 java–運行java文件
使用eclipse中集成的Ant運行build.xml文件(當然,也可以將ANT_HOME加到Path中,在命令行中運行)
Buildfile: D:/MyEclipse/workspace/sad/build.xml compile: run: [java] Working directory ignored when same JVM is used. [java] Could not find org.ant.chapter1.Hello. Make sure you have it in your classpath [java] at org.apache.tools.ant.taskdefs.ExecuteJava.execute(ExecuteJava.java:166) [java] at org.apache.tools.ant.taskdefs.Java.run(Java.java:705) [java] at org.apache.tools.ant.taskdefs.Java.executeJava(Java.java:177) [java] at org.apache.tools.ant.taskdefs.Java.execute(Java.java:83) [java] at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:275) [java] at org.apache.tools.ant.Task.perform(Task.java:364) [java] at org.apache.tools.ant.Target.execute(Target.java:341) [java] at org.apache.tools.ant.Target.performTasks(Target.java:369) [java] at org.apache.tools.ant.Project.executeTarget(Project.java:1214) [java] at org.eclipse.ant.internal.ui.antsupport.InternalAntRunner.run(InternalAntRunner.java:379) [java] at org.eclipse.ant.internal.ui.antsupport.InternalAntRunner.main(InternalAntRunner.java:135) BUILD SUCCESSFUL Total time: 703 milliseconds |
,java入門的經典錯誤,ClassNotDefException,可見是classpath設置問題,而觀察得到compile成功運行,所以我們在run-target里面加入classpath的配置
<?xml version="1.0"?> <project name="project" default="run"> <target name="compile"> <javac destdir="bin" srcdir="src"></javac> </target> <target name="run" depends="compile"> <java classname="org.ant.chapter1.Hello"> <classpath path="bin"></classpath> </java> </target> </project> |
運行
Buildfile: D:/MyEclipse/workspace/sad/build.xml compile: run: [java] Hello World! BUILD SUCCESSFUL Total time: 672 milliseconds |
成功!!第一個Ant應用完成,有人會說:用IDE運行豈不是更簡單,但是你要知道運行java程序只是Ant的一個小小的功能,后面我們會看到Ant的更強大的功能!
下一篇文章將介紹java程序運行的擴展及用Ant運行tomcat!
java程序運行的擴展
1.帶有參數的應用程序運行
Ant在<java>任務中提供了<arg>元素,<arg>有四種屬性value,file,line,path
public class Hello {
public static void main(String[] args) {
System.out.println("Hello " + args[0]);
File file = new File(args[1]);
String[] filename = file.list();
for(int i = 0; i < filename.length; i++) {
System.out.println(filename[i]);
}
}
}
|
build.xml
<?xml version="1.0"?>
<project name="project" default="run">
<property name="run.classpath" value="bin"></property>
<target name="compile">
<javac destdir="bin" srcdir="src"></javac>
</target>
<target name="run" depends="compile">
<java classname="org.ant.chapter1.Hello">
<classpath path="${run.classpath}"></classpath>
<arg value="Ant"/>
<arg file="D:/rag"/>
</java>
</target>
</project>
|
Buildfile: D:/MyEclipse/workspace/sad/build.xml compile: run: [java] Hello Ant [java] hello.dat BUILD SUCCESSFUL Total time: 734 milliseconds |
2.控制新的JVM
一般的<java>任務都運行在當前的JVM中,單一些特定的情況下將Ant運行在新的JVM下面,這時只需要將<java>中的一個屬性fork設置為true就可以了.
我們知道,java命令提供了許多的運行參數,用于指定JVM的屬性,同樣在Ant也提供相應的屬性,看例子:
<?xml version="1.0"?>
<project name="project" default="run">
<property name="run.classpath" value="bin"></property>
<property name="Search.JVM.extra.args" value="-Xincgc"></property>
<target name="compile">
<javac destdir="bin" srcdir="src"></javac>
</target>
<target name="run" depends="compile">
<java classname="org.ant.chapter1.Hello" fork="true"
maxmemory="64m">
<classpath path="${run.classpath}"></classpath>
<jvmarg line="${Search.JVM.extra.args}"/>
<arg value="Ant"/>
<arg file="D:/rag"/>
</java>
</target>
</project>
|
3.運行jar文件,使用failonerror處理錯誤
<java>同樣提供了運行jar文件的屬性
MANIFEST.MF
Mainfest-Version: 1.0 Created-By: myth Sealed: false Main-Class: org.ant.chapter1.Hello |
build.xml
<?xml version="1.0"?>
<project name="project" default="run">
<property name="run.classpath" value="bin"></property>
<property name="Search.JVM.extra.args" value="-Xincgc"></property>
<target name="compile">
<javac destdir="bin" srcdir="src"></javac>
</target>
<target name="jar" depends="compile">
<jar destfile="test.jar" update="true"
manifest="MANIFEST.MF">
<fileset dir="bin">
<include name="**/*.class"/>
</fileset>
</jar>
</target>
<target name="run" depends="jar">
<java fork="true" maxmemory="64m" jar="test.jar">
<classpath path="${run.classpath}"></classpath>
<jvmarg line="${Search.JVM.extra.args}"/>
<arg value="Ant"/>
<arg file="D:/rag"/>
</java>
</target>
</project>
|
Buildfile: D:/MyEclipse/workspace/sad/build.xml compile: jar: [jar] Updating jar: D:/MyEclipse/workspace/sad/test.jar run: [java] Hello Ant [java] hello.dat BUILD SUCCESSFUL Total time: 875 milliseconds |
在某些情況下,我們不希望由于一些不重要的任務構建失敗,而導致整個構建的失敗,所以Ant提供了一個共同的屬性-failonerror,多數任務的默認值為failοnerrοr=”true”,既當此任務構建失敗時,失敗信息會傳遞給控制臺,并導致build failed,failonerror只支持在新的JVM里運行.
<target name="run" depends="jar">
<java fork="true" maxmemory="64m" jar="test.jar"
failοnerrοr="false" >
<classpath path="${run.classpath}"></classpath>
<jvmarg line="${Search.JVM.extra.args}"/>
<arg value="Ant"/>
<arg file="D:/rag"/>
</java>
</target>
|
Buildfile: D:/MyEclipse/workspace/sad/build.xml compile: jar: run: [java] java.lang.NullPointerException [java] at org.ant.chapter1.Hello.main(Hello.java:27) [java] Hello Ant [java] Exception in thread "main" [java] Java Result: 1 BUILD SUCCESSFUL Total time: 984 milliseconds |
可以看出雖然run構建失敗,但是Ant還是執行了,原來的jar文件,并且BUILD SUCCESSFUL!!
使用Ant運行tomcat
Ant使用<exec>任務運行本地程序,先看一個例子:
<?xml version="1.0"?> <project name="project" default="run"> <target name="run"> <exec executable="cmd"> <arg value="/C a.bat"/> </exec> </target> </project> |
a.bat
@echo off echo Hello >> a.txt |
運行完后,會在根目錄生成a.txt文件,里面內容為Hello
下面我們來運行tomcat
<?xml version="1.0"?>
<project name="project" default="tomcat-start">
<property name="tomcat.dir" value="c:/Tomcat5"></property>
<target name="tomcat-start">
<exec dir="${tomcat.dir}/bin" executable="cmd">
<env key="CATALINA_HOME" path="${tomcat.dir}"/>
<arg value="/C startup.bat"/>
</exec>
</target>
<target name="tomcat-stop">
<exec dir="${tomcat.dir}/bin" executable="cmd">
<env key="CATALINA_HOME" path="${tomcat.dir}"/>
<arg value="/c shutdown.bat"/>
</exec>
</target>
</project>
|
成功!!
四.使用Ant進行Junit測試
我們除了使用java來直接運行junit之外,我們還可以使用junit提供的junit task與ant結合來運行。涉及的幾個主要的ant task如下:
l <junit>,定義一個junit task
l <batchtest>,位于<junit>中,運行多個TestCase
l <test>,位于<junit>中,運行單個TestCase
l <formatter>,位于<junit>中,定義一個測試結果輸出格式
l <junitreport>,定義一個junitreport task
l <report>,位于<junitreport>中,輸出一個junit report
運行Junit需要jakarta-ant-1.4-optional.jar和Junit.jar包,因為這兩個包用于支持ant task–<junit>的,所以不能在build.xml文件中加載,需要將他們放到ANT_HOME中去.使用eclipse可以按照一下步驟加入:
Windows-Preference-Ant-Runtime-Ant Home Entries
下面看一個Junit測試的例子:
|
可以看出Junit的使用基本和java差不多, printsummary允許輸出junit信息,當然Ant提供formatter屬性支持多樣化的junit信息輸出.Ant包含三種形式的formatter:
brief:以文本格式提供測試失敗的詳細內容;
plain:以文本格式提供測試失敗的詳細內容以及每個測試的運行統計;
xml:以xml格式提供擴展的詳細內容,包括正在測試時的Ant特性,系統輸出,以及每個測試用 例的系統錯誤.
使用formatter時建議將printsummary關閉,因為他可能對formatter的生成結果產生影響,并多生成一份同樣的輸出.當然我們可以使用formatter將輸出結果顯示在console中:
<formatter type=”brief” usefile=”false”/>
Junit支持多個formatter同時存在:
<formatter type=”brief” usefile=”false”/>
<formatter type=”xml”/>
使用xml我們可以得到擴展性更強的信息輸出,這時在<test>中要設定todir來指定xml的輸出路徑.
在通常情況下我們不可能一個一個來處理junit,所以Ant提供了<batchtest>,可以在他里面嵌套文件集(fileset)以包含全部的測試用例.
對于大量的用例,使用控制臺輸出,或者使用文件或xml文件來作為測試結果都是不合適的,Ant提供了<junitreport>任務使用XSLT將xml文件轉換為HTML報告.該任務首先將生成的XML文件整合成單一的XML文件,然后再對他進行轉換,這個整合的文件默認情況下被命名為:TESTS-TestSuites.xml.
|
<report>元素指示轉換過程中生成有框架(frames)或者無框架的類似與javadoc格式的文件,并保存到todir所在的目錄下面.(由于xalan對于JDK1.4以上的版本存在問題,所以要生成HTML文件需要以下步驟:現在最新的xalan,在%JAVA_HOME%/jre/lib中建立文件夾endorsed.將xalan中的jar文件copy到里面).
下面看一個完整的例子:
|
生成的文檔:
點擊Properties超鏈接會彈出一個窗口顯示在測試運行時全部的Ant特性,這對于跟蹤由環境和配置造成的失敗是非常便利的!
五.使用Ant運行本地程序
1.使用Ant運行windows的批處理文件
要在Ant內運行一個外部程序,應使用<exec>任務.它允許你執行下列操作:
l 指定程序名和要傳入的參數.
l 命名運行目錄.
l 使用failonerror標志來控制當應用程序失敗時是否停止構建.
l 指定一個最大程序持續時間,時間超過則中止程序.任務在這時被認為是失敗,但是至少構建會中止,而不是掛起,這對于自動構建是至關重要的.
l 將輸出存到一個文件或特性.
l 指定java調用本地程序時需要預先設定的環境變量.
下面來看一個例子:
批處理文件:
Test.bat
|
build.xml
|
使用executable元素標記指定使用的命令,具體用法可以在命令行下面輸入help cmd查看.如果你希望在運行批處理發生錯誤時中止構建需要設定failοnerrοr=”on”.加入你的外部程序在某個時刻掛起,也許是在與遠程站點對話,而你不希望構建永遠被掛起,Ant提供了timeout這個屬性,他是一個以毫秒為單位的數字.下面看一下如何使用Ant來運行tomcat.
啟動tomcat需要兩個環境變量CATALINA_HOME, JAVA_HOME,如果你在環境變量中已經設定,在Ant中就不需要進行處理,如果沒有需要使用<env>屬性來設定,你也可以使用<env>屬性覆蓋你以前的環境變量.
|
2.使用Ant運行shell文件
由于windowsXP的cmd默認沒有安裝ps,bash等命令,所以我們需要借助的三方的軟件來實現這個功能,這里使用cgywin,將cgywin的bin目錄加到環境變量的Path里面(下面使用Ant運行cvs也會用到).
|
3.使用Ant運行cvs
Ant內置cvs屬性,可以很方便的使用cvs:
|
如果你的Documents and Settings中有.cvspass文件,那么可以不用設定cvsroot,Ant會自動尋找.
六.工程的打包部署
工程的打包,主要就是文件的操作,下面通過例子簡單介紹一下文件的移動,復制和刪除.
|
需要說明的是文件刪除的時候可能這個文件正在被別人是用而無法刪除,所以要用failonerror來標記,文件的復制是時間戳敏感的,如果拷貝的文件比原文件要老,那么Ant將不會執行copy,解決的辦法是將overwrite屬性設置為true,由于移動文件是復制文件的一個子類,所以它們的原理基本相同.
前面已經例舉過一個jar文件打包的例子,下面主要介紹war文件的打包.Ant提供war文件打包的屬性.<war>任務是<jar>任務的子類,但是他也提供了一些特有的屬性:
|
可以看出war任務提供了許多WEB應用程序的特有屬性,只要你指定了這些文件,war任務就會自動將他們放到正確的位置.
部署是項目發布的過程,Ant支持FTP,Email,本地和遠程等幾種部署模式,但是Ant并不內置對一些部署的支持,需要第三方的庫.
optional.jar 也可能是這樣的名字: jakarta-ant-1.4.1.optional.jar
netcomponents.jar <ftp>和<telnet>需要
activation.jar <mail>需要
mail.jar <mail>需要
下面只以本地部署為例,服務器為tomcat.
由于tomcat支持熱部署,可以將webapp文件下的war文件自解壓縮,所以最簡單的部署方式是將工程打成war包后直接copy到webapp目錄下面.另一種方法是使用tomcat的管理員身份,在manager頁面裝載和刪除應用,這種方法比較復雜,也比較正規,他也是遠程部署的基礎.
|
可以看出只要將上面的localhost換成目標的ip地址就可以實現tomcat的遠程部署.
ANT打包內存溢出及JDK版本過低編繹失敗解決
,<javac classpathref=”project.class.path” debug=”true” deprecation=”true” destdir=”${dest}” nowarn=”false” target=”1.6″ >在此標簽中增加fork=”true” memoryMaximumSize=”512m”就行了—也可以改更大
ANT編繹時出現以下錯誤:
please download the original output file to see more info—一般是因為JDK版本過低,請在環境變量中設置高版本的JDK的JAVA_HOME及相應的PATH變量.
總結
以上是生活随笔為你收集整理的Ant 下载、安装、使用、教程全面了解「建议收藏」的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【转】UIAutomation
- 下一篇: 在线客服系统源码(PHP完全开源版)