Ant工具
Ant是一種基於Java的build工具。理論上來講,它有些相似於(Unix)C中的make ,但沒有make的缺陷。目前的最新版本爲:Ant 1.9.4
[1]
。
既然咱們已經有了make, gnumake, nmake, jam以及其餘的build工具爲何還要要一種新的build工具呢?由於Ant的原做者在多種(硬件)平臺上開發
軟件時, 沒法忍受這些工具的限制和不便。相似於make的工具本質上是基於shell(語言)的:他們計算依賴關係,而後執行命令(這些命令與你在命令行敲的命令 沒太大區別)。這就意味着你能夠很容易地經過使用OS特有的或編寫新的(命令)程序擴展該工具;然而,這也意味着你將本身限制在了特定的OS,或特定的 OS類型上,如Unix。
Makefile也很可惡。任何使用過他們的人都碰到過可惡的tab問題。Ant的原做者常常這樣問本身:「是否個人命令不執行只是由於在個人tab前有一個空格?!!」。相似於jam的工具很好地處理了這類問題,可是(用戶)必須記住和使用一種新的格式。
Ant就不一樣了。與基於shell命令的擴展模式不一樣,Ant用Java的類來擴展。(用戶)沒必要編寫shell命 令,配置文件是基於XML的,經過調用target樹,就可執行各類task。每一個task由實現了一個特定Task接口的對象來運行。(若是你對Ant 一點概念都沒有的話,可能看不懂這一節,沒有關係,後面會對target,task作詳細的介紹。你若是沒有太多的時間甚至能夠略過這一節,而後再回來瀏 覽一下這裏的介紹,那時你就會看懂了。一樣,若是你對make之類的工具不熟悉也不要緊,下面的介紹根本不會用到make中的概念。)
必須認可,這樣作,在構造shell命令時會失去一些特有的表達能力。如`find . -name foo -exec rm {}`,但卻給了你跨平臺的能力-你能夠在任何地方工做。若是你真的須要執行一些shell命令,Ant有一個<exec> task,這個task容許執行特定OS上的命令。
Ant的概念
當一個代碼項目大了之後,每次從新編譯,打包,測試等都會變得很是複雜並且重複,所以
c語言中有make
腳本來幫助這些工做的批量完成。在Java 中應用是平臺無關性的,固然不會用平臺相關的make
腳本來完成這些
批處理任務了,ANT自己就是這樣一個流程
腳本引擎,用於自動化調用程序完成項目的編譯,打包,測試等。除了基於JAVA是平臺無關的外,
腳本的格式是基於XML的,比make腳原本說還要好維護一些。
每一個ant
腳本(缺省叫build.xml)中設置了一系列任務(target):好比對於一個通常的項目可能須要有如下任務。
* 任務1:usage 打印本
腳本的幫助信息(缺省)
* 任務2:clean <-- init 清空初始化環境
* 任務3:javadoc <-- build <-- init 生成JAVADOC
* 任務4:jar <-- build <-- init 生成JAR
* 任務5:all <-- jar + javadoc <-- build <-- init 完成以上全部任務:jar javadoc
而多個任務之間每每又包含了必定的依賴關係:好比把整個應用打包任務(jar)的這個依賴於編譯任務(build),而編譯任務又依賴於整個環境初始化任務(init)等。
注:我看到不少項目的ant
腳本中 的命名基本上都是一致的,好比:編譯通常叫build或者compile;打包通常叫jar或war;生成文檔通常命名爲 javadoc或javadocs;執行所有任務all。在每一個任務的中,ANT會根據配置調用一些外部應用並配以相應參數執行。雖然ANT可調用的外部 應用種類很是豐富,但其實最經常使用的就2,3個:好比javac javadoc jar等。
Ant與makefile的比較
Makefile 有一些不足之處,好比不少人都會碰到的煩人的Tab問題。最初的Ant開發者屢次強調」只是我在Tab前面加了一個空格,因此個人命令就不能執行」。有一 些工具在必定程度上解決了這個問題,但仍是有不少其餘的問題。Ant則與通常基於命令的工具備所不一樣,它是Java類的擴展。Ant運行須要的XML格式 的文件不是Shell命令文件。它是由一個Project組成的,而一個Project又可分紅可多target,target再細分又分紅不少 task,每個task都是經過一個實現特定接口的java類來完成的。
Ant的優勢
Ant是Apache
軟件基金會JAKARTA目錄中的一個子項目,它有如下的優勢。
跨平臺性。Ant是純
Java語言編寫的,所以具備很好的跨平臺性。
操做簡單。Ant是由一個內置任務和可選任務組成的。Ant運行時須要一個XML文件(構建文件)。
Ant經過調用target樹,就能夠執行各類task。每一個task實現了特定接口對象。因爲Ant構建文件時XML格式的文件,因此很容易維護和書寫,並且結構很清晰。
Ant能夠集成到開發環境中。因爲Ant的跨平臺性和操做簡單的特色,它很容易集成到一些開發環境中去。
Ant 開發
Ant的構建文件
當開始一個新的項目時,首先應該編寫Ant構建文件。構建文件定義了構建過程,並被團隊開發中每一個人使用。Ant構 建文件默認命名爲build.xml,也能夠取其餘的名字。只不過在運行的時候把這個命名看成參數傳給Ant。構建文件能夠放在任何的位置。通常作法是放 在項目頂層目錄中,這樣能夠保持項目的簡潔和清晰。下面是一個典型的項目層次結構。
(1) src存放源文件。
(2) class存放編譯後的文件。
(3) lib存放第三方JAR包。
(4) dist存放打包,發佈之後的代碼。
Ant構建文件是XML文件。每一個構建文件定義一個惟一的項目(Project元素)。每一個項目下能夠定義不少目標(target元素),這些目標之間能夠有依賴關係。當執行這類目標時,須要執行他們所依賴的目標。
每一個目標中能夠定義多個任務,目標中還定義了所要執行的任務序列。Ant在構建目標時必須調用所定義的任務。任務定義了Ant實際執行的命令。Ant中的任務能夠爲3類。
(1) 核心任務。核心任務是Ant自帶的任務。
(2) 可選任務。可選任務實來自第三方的任務,所以須要一個附加的JAR文件。
(3) 用戶自定義的任務。用戶自定義的任務是用戶本身開發的任務。
1.<project>標籤
每一個構建文件對應一個項目。<project>標籤時構建文件的根標籤。它能夠有多個內在屬性,就如代碼中所示,其各個屬性的含義分別以下。
(1) default表示默認的運行目標,這個屬性是必須的。
(2) basedir表示項目的基準目錄。
(3) name表示項目名。
(4) description表示項目的描述。
每一個構建文件都對應於一個項目,可是大型項目常常包含大量的子項目,每個子項目均可以有本身的構建文件。
2.<target>標籤
一個項目標籤下能夠有一個或多個target標籤。一個target標籤能夠依賴其餘的target標籤。
例如,有一個target用於
編譯程序,另外一個target用於生成
可執行文件。在生成
可執行文件以前必須先編譯該文件,所以可執行文件的target依賴於編譯程序的target。Target的全部屬性以下。
(1).name表示標明,這個屬性是必須的。
(2).depends表示依賴的目標。
(3)if表示僅當屬性設置時才執行。
(4)unless表示當屬性沒有設置時才執行。
(5)description表示項目的描述。
Ant的depends屬性指定了target的執行順序。Ant會依照depends屬性中target出現順序 依次執行每一個target。在執行以前,首先須要執行它所依賴的target。程序中的名爲run的target的depends屬性compile,而 名爲compile的target的depends屬性是prepare,因此這幾個target執行的順序是 prepare->compile->run。
一個target只能被執行一次,即便有多個target依賴於它。若是沒有if或unless屬性,target總會被執行。
3.<mkdir>標籤
該標籤用於建立一個目錄,它有一個屬性dir用來指定所建立的目錄名,其代碼以下:
<mkdir dir=」${class.root}」/>
經過以上代碼就建立了一個目錄,這個目錄已經被前面的property標籤所指定。
4<jar>標籤
該標籤用來生成一個JAR文件,其屬性以下。
(1) destfile表示JAR文件名。
(2) basedir表示被歸檔的文件名。
(3) includes表示被歸檔的文件模式。
(4) excludes表示被排除的文件模式。
5.<javac標籤>
該標籤用於編譯一個或一組java文件,其屬性以下。
(1).srcdir表示源程序的目錄。
(3).include表示被編譯的文件的模式。
(4).excludes表示被排除的文件的模式。
(6).debug表示包含的調試信息。
(7).optimize表示是否使用優化。
(8).verbose 表示提供詳細的輸出信息。
(9).fileonerror表示當碰到錯誤就自動中止。
6.<java>標籤
(1).classname 表示將執行的類名。
(2).jar表示包含該類的JAR文件名。
(3).classpath所表示用到的類路徑。
(4).fork表示在一個新的
虛擬機中運行該類。
(5).failonerror表示當出現錯誤時自動中止。
(6).output 表示輸出文件。
(7).append表示追加或者覆蓋默認文件。
7.<delete>標籤
該標籤用於刪除一個文件或一組文件,其屬性以下。
(1)/file表示要刪除的文件。
(2).dir表示要刪除的目錄。
(3).includeEmptyDirs 表示指定是否要刪除空目錄,默認值是刪除。
(4).failonerror 表示指定當碰到錯誤是否中止,默認值是自動中止。
(5).verbose表示指定是否列出所刪除的文件,默認值爲不列出。
8.<copy>標籤
該標籤用於文件或文件集的拷貝,其屬性以下。
(2).tofile 表示目標文件。
(3).todir 表示目標目錄。
(4).overwrite 表示指定是否覆蓋目標文件,默認值是不覆蓋。
(5).includeEmptyDirs 表示制定是否拷貝空目錄,默認值爲拷貝。
(6).failonerror 表示指定如目標沒有發現是否自動中止,默認值是中止。
(7).verbose 表示制定是否顯示詳細信息,默認值不顯示。
Ant的數據類型
在構建文件中爲了標識文件或
文件組,常常須要使用數據類型。數據類型包含在org.apache.tool.ant.types包中。下面鏡簡單介紹構建文件中一些經常使用的數據類型。
1. argument 類型
由Ant構建文件調用的程序,能夠經過<arg>元素向其傳遞
命令行參數,如apply,exec和java任
務都可接受嵌套<arg>元素,能夠爲各自的過程調用指定參數。如下是<arg>的全部屬性。
(1).values 是一個命令參數。若是參數中有空格,但又想將它做爲單獨一個值,則使用此屬性。
(2).file表示一個參數的文件名。在構建文件中,此文件名相對於當前的工做目錄。
(4).path表示路徑。
2.environment 類型
由Ant構建文件調用的
外部命令或程序,<env>元素制定了哪些
環境變量要傳遞給正在執行的系統命令,<env>元素能夠接受如下屬性。
(2).path表示
環境變量的路徑。Ant會將它轉換爲一個本地約定。
(3).value 表示
環境變量的一個直接變量。
注意 file path 或 value只能取一個。
3.filelist類型
Filelist 是一個支持命名的文件列表的
數據類型,包含在一個filelist類型中的文件不必定是存在的文件。如下是其全部的屬性。
(1).dir是用於計算絕對文件名的目錄。
(2).files 是用逗號分隔的文件名列表。
(3).refid 是對某處定義的一個<filelist>的引用。
注意 dir 和 files 都是必要的,除非指定了refid(這種狀況下,dir和files都不容許使用)。
4.fileset類型
Fileset
數據類型定義了一組文件,並一般表示爲<fileset>元素。不過,許多ant任務構建成了隱式的fileset,這說明他們支持全部的fileset屬性和嵌套元素。如下爲fileset 的屬性列表。
(1).dir表示fileset 的基目錄。
(2).casesensitive的值若是爲false,那麼匹配文件名時,fileset不是區分大小寫的,其默認值爲true.
(3).defaultexcludes 用來肯定是否使用默認的排除模式,默認爲true。
(4).excludes 是用逗號分隔的須要派出的文件模式列表。
(5).excludesfile 表示每行包含一個排除模式的文件的文件名。
(6).includes 是用逗號分隔的,須要包含的文件模式列表。
(7).includesfile 表示每行包括一個包含模式的文件名。
5.patternset 類型
Fileset 是對文件的分組,而patternset是對模式的分組,他們是緊密相關的概念。<patternset>支持4個屬性:includes excludex includexfile 和 excludesfile,與fileset相同。Patternset 還容許如下嵌套元素:include,exclude,includefile 和 excludesfile.
6.filterset 類型
Filterset定義了一組過濾器,這些過濾器將在文件移動或複製時完成文件的
文本替換。
主要屬性以下:
(1).begintoken 表示嵌套過濾器所搜索的記號,這是標識其開始的字符串。
(2).endtoken表示嵌套過濾器所搜索的記號這是標識其結束的字符串。
(3).id是過濾器的惟一標誌符。
(4).refid是對構建文件中某處定義一個過濾器的引用。
7.Path類型
Path元素用來表示一個類路徑,不過它還能夠用於表示其餘的路徑。在用做幾個屬性時,路經中的各項用分號或冒號隔開。在構建的時候,此
分隔符將代替當前平臺中全部的路徑分隔符,其擁有的屬性以下。
(1).location 表示一個文件或目錄。Ant在內部將此擴展爲一個
絕對路徑。
(2).refid 是對當前構建文件中某處定義的一個path的引用。
(3).path表示一個文件或路徑名列表。
8.mapper類型
Mapper類型定義了一組輸入文件和一組輸出文件間的關係,其屬性以下。
(1).classname 表示實現mapper類的類名。當內置mapper不知足要求時,用於建立定製mapper.
(2).classpath表示查找一個定製mapper時所用的類型路徑。
(3).classpathref是對某處定義的一個類路徑的引用。
(4).from屬性的含義取決於所用的mapper.
(5).to屬性的含義取決於所用的mapper.
(6).type屬性的取值爲identity,flatten glob merge regexp 其中之一,它定義了要是用的內置mapper的類型。
Ant的安裝
解包後在系統可執行
路徑中加入指向ant的bin的路徑就能夠了,好比能夠在GNU/Linux上把如下配置加入/etc/profile中:
export ANT_HOME=/home/ant
export JAVA_HOME=/usr/java/j2sdk1.4.1
export PATH=$PATH:$JAVA_HOME/bin:$ANT_HOME/bin
Windows 下的安裝:
下載後解壓到某個目錄我這裏以D:\apache-ant-1.7.1爲例子:
添加path全局
環境變量:D:\apache-ant-1.7.1\bin
這樣在command line就能夠運行ant命令了
測試:運行->cmd/command->ant 若是沒有安裝成功則回報找不到這個命令,安裝成功會有相關的提示信息顯示。
這樣執行ant 後,若是不指定配置文件ant會缺省找build.xml這個配置文件,並根據配置文件執行任務,缺省的任務設置能夠指向最經常使用的任務,好比: build,或指向打印幫助信息:usage,告訴用戶有那些
腳本選項可使用。
Ant的運行
安裝好Ant而且配置好路徑以後,在命令行中切換到構建文件的目錄,輸入Ant命令就能夠運行Ant.若沒有指定任何參數,Ant會在
當前目錄下 查詢build.xml文件。若是找到了就用該文件做爲構建文件。若是使用了 –find 選項,Ant 就會在上級目錄中找構建文件,直至到達文件系統的根目錄。若是構建文件的名字不是build.xml ,則Ant運行的時候就可使用 –buildfile file,這裏file 指定了要使用的構建文件的名稱,示例以下:
Ant以下說明了表示
當前目錄的構建文件爲build.xml 運行 ant 執行默認的目標。
Ant –buildfile test.xml
使用當前目錄下的test.xml 文件運行Ant ,執行默認的目標
Ant版本
2013年03月11日,Apache Ant 1.9 發佈
[2]
例子:
<?xml version="1.0" encoding="UTF-8"?> <project name="ss" basedir=".."> <property name="this.ss.Projectfolder" value="${basedir}/ss" /> <property name="this.ss.bin" value="${this.ss.Projectfolder}/bin" /> <property name="this.ss.Package" value="${this.ss.Projectfolder}/Package" /> <property name="this.ss.Package.Jar" value="${this.ss.Projectfolder}/Package/Jar" /> <property name="this.ss.Package.War" value="${this.ss.Projectfolder}/Package/War" /> <property name="this.ss.Package.Zip" value="${this.ss.Projectfolder}/Package/Zip" /> <path id="this.ss.classpath"> <fileset dir="${basedir}/root/WEB-INF/lib"> <include name="**/*.jar" /> </fileset> <fileset dir="${basedir}/lib"> <include name="**/*.jar" /> </fileset> </path> <target name="ss.clean" description="清除生成的目錄結構"> <echo message="清除生成的目錄結構" /> <delete dir="${this.ss.Projectfolder}" /> </target> <target name="ss.prepare" description="初始化目錄結構" depends="ss.clean"> <echo message="初始化目錄結構" /> <mkdir dir="${this.ss.Projectfolder}" /> <mkdir dir="${this.ss.bin}" /> <mkdir dir="${this.ss.bin}/classes" /> <mkdir dir="${this.ss.bin}/test" /> <mkdir dir="${this.ss.Package}" /> <mkdir dir="${this.ss.Package.Jar}" /> <mkdir dir="${this.ss.Package.War}" /> <mkdir dir="${this.ss.Package.Zip}" /> </target> <target name="ss.code.compile" description="在工程目錄中編譯產品代碼" depends="ss.prepare"> <echo message="在工程目錄中編譯" /> <javac srcdir="${basedir}/src/com" destdir="${this.ss.bin}/classes" debug="on" includeantruntime="on" encoding="utf-8" nowarn="yes" memorymaximumsize="1024M" fork="true"> <classpath refid="this.ss.classpath" /> </javac> <copy todir="${this.ss.bin}/classes"> <fileset dir="${basedir}/src"> <exclude name="**/*.java" /> </fileset> </copy> <copy todir="${this.ss.bin}/classes"> <fileset dir="${basedir}/src/properties"> <include name="license.lic" /> <include name="log4j.properties" /> <include name="log4j.xml" /> <include name="oscache.properties" /> </fileset> </copy> <native2ascii src="${basedir}/src/properties" dest="${this.ss.bin}/classes" encoding="UTF-8" includes="**/*.zh" ext="_zh.properties" /> <native2ascii src="${basedir}/src/properties" dest="${this.ss.bin}/classes" encoding="UTF-8" includes="**/*.zh" ext=".properties" /> </target> <target name ="ss.revision" depends="ss.code.compile"> <copy todir="${basedir}/root" flatten="true" preservelastmodified="true"> <fileset dir="/usr/CIWork/CIWork/jobs/ss_601/builds"> <include name="**/revision.txt"/> </fileset> </copy> </target> <target name="ss.version" > <delete file="/usr/CIWork/CIWork/jobs/ss_601/workspace/root/version.txt"/> <exec executable="cmd" failonerror="true"> <arg line="/c /usr/CIWork/CIWork/jobs/ss_601/workspace/root/version.bat" /> </exec> <delete file="/usr/CIWork/CIWork/jobs/ss_601/workspace/root/version.bat"/> <delete file="/usr/CIWork/CIWork/jobs/ss_601/workspace/root/revision.txt"/> </target> <target name="ss.Packagewar" description="打war包" depends="ss.revision"> <war destfile="${this.ss.Package}/War/ss.war" compress="true" > <classes dir="${this.ss.bin}/classes"> </classes> <fileset dir="${basedir}/root"> <exclude name="WEB-INF/classes/**/*.*" /> </fileset> </war> </target> <target name="ss.Packagezip" description="打Zip包" depends="ss.Packagewar"> <tstamp> <format property="flag" pattern="yyyyMMddHHmm" /> </tstamp> <zip destfile="${this.ss.Package}/Zip/ss-${flag}.zip" compress="true" update="true"> <fileset dir="${this.ss.Package}/War"> <include name="ss.war" /> </fileset> </zip> </target> <target name="ss.build" description="執行構建" depends="ss.clean,ss.Packagezip"> </target> </project>