ant 使用指南 与 知识汇总
2010-07-14 09:21
274 查看
一、Ant是什么?
Ant是一种基于Java和XML的build工具。
它可以帮助我们将项目开发过程中需要完成的各种步骤组织起来,通过一个简易的方式来构建整个项目。Ant究竟能做什么呢?这取决于Ant的任务(Task),Ant有哪些任务,就能完成哪些工作!一些典型的任务是:编译java源代码、运行java程序、拷贝文件或目录、将编译完成的类打包等等。
当然,Ant是一个开放的架构,任何人,都可以按照自己的方式,添加自己的任务(Task),这需要
遵守Ant的一些规范。
二、ANT的安装部署
1、 确保你的机器已经安装了JDK,确保你的path环境变量中包含有java虚拟机的运行程序,确保你的环境变量中有JAVA_HOME变量
2、 下载ant并解压
3、 设置ANT_HOME环境变量,指向ant解压根目录
4、 在path环境变量中,添加ANT_HOME/bin目录
5、 打开控制台,运行:ant
6、 如果能够运行ant(忽略这些异常:Buildfile: build.xml does not exist!),而不是出现诸如命令无法解释的错误,那么你的ant就安装成功了
三.Ant的运行靠的就是一个构建脚本,默认名称为build.xml,只要你构建一个这样的文件,按照规定把需要完成的工作任务写在这个配置文件中,然后在dos下运行ant就会执行这个文件,如果文件名不是默认的(例如mybuild.xml),则这样运行:ant -f mybuild.xml ,如果该配置文件不存在将会报错:Buildfile: build.xml does not exist!
三、ANT的组成(主要由三部分组成:Project、Target、Task)1、Project(项目)。基本属性:name、default、basedir。一个build.xml只能存在一个Project工程。
name :项目名称
default :缺省开始执行的Target
basedir :用于计算所有其他路径的基路径。
例1:<project name="dev" basedir="." default="dist">
2、Target。一个项目可以定义一个或多个Target、一个Target是一系列你想要执行的任务,而一个target一般包含若干task,如编译任务、打包任务、混淆任务。
Target的基本属性: name:target的名字,depends:该target依赖关系,description: target的描述信息。
例2:<target name="clean" depends="init" description="Clean dist and temp directories">
Ant在执行时以Target为单位,Target的depends属性又决定了哪个Target先执行,因此我们可以通过Target来构造编译顺序。
例3:<target name="copy" >
<target name="javac" depends="copy">
<target name="jar" depends="javac">
执行顺序是从下至上,依次执行,如果某个Target没有depends属性,那么就顺序执行。
例4:一个完整的Target:
<target name="compile" depends="replacesrc" description="Compiling Java source files">
<javac srcdir="temp/src"
destdir="temp/build"
debug="on"
deprecation="false"
optimize="true">
<classpath refid="classpath"/>
</javac>
</target>
3、Task。Ant执行的一系列任务是由Target构成的,而Target又是由数个小的Task构成的,task任务是最小的运行单位,我们可以把copy、delete、mkdir等操作认为是一个小任务。
(1)copy任务。
A.拷贝单个文件:
<copy file="Demo/manifest.mf" todir="temp/build/META-INF" />
解释:把一个文件拷贝到拷贝相关文件夹中。
B.拷贝文件夹:
<copy todir="temp/build">
<fileset dir="temp/classes"/>
</copy>
解释:把temp/build目录下的所有文件,拷贝到temp/classes目录中去
C.替换拷贝
<copy todir="temp/build">
<fileset dir="temp/classes" includes="*.class" />
<filterset>
<filter token="@Time@" value="${app.time}"/>
</filterset>
</copy>
解释: <filterset>过滤集,可以将temp/classes文件夹下的存在@Time@标记的文件,替换为变量${app.time}值。这样在完成拷贝的同时也完成了替换任务。
(2)mkdir任务和delete任务
A.单独的delete和mkdir任务
<delete dir="temp/src"/>
<delete file="temp/build/Thumbs.db" />
<mkdir dir="temp/src"/>
B. 带有条件的删除任务
<delete>
<fileset dir="store" includes="*.*"/>
<fileset dir="source" excludes="*.java"/> </delete>
解释:include表示要删除哪些文件;而excludes表示排除的文件;
(3)replace替换任务
A.替换某一文件中的字符串
<replace file ="base/testing.txt">
token="@temp@"
value="${newstring}"
</replace>
解释:token是需要替换的标记;value是新值,将testing.txt文件中的@temp@替换为新值。
B.替换某个文件夹中存在特定标记的文件
<replace dir="temp" token="@CHARSET@" value="${webapp.charset}"/>
解释:temp目录中文件,如果有存在@CHARSET@标记的,将被替换为${webapp.charset}变量值。
C.批量替换
<replace dir="dist" includes="${app.project}.jad" encoding="UTF-8">
<replacefilter token="@NAME@" value="${app.name}"/>
<replacefilter token="@VENDOR@" value="${app.vendor}"/>
<replacefilter token="@MIDLET@" value="${app.midlet}"/>
<replacefilter token="@JAR@" value="${app.project}"/>
<replacefilter token="@FILESIZE@" value="${size}"/>
<replacefilter token="@DESCRIPTION@" value="${app.description}"/> <replacefilter token="@PRICE@" value="${app.price}"/>
</replace>
(4) javac任务 ,编译源程序
<target name="Compile" description="Compile Java source files">
<javac srcdir="src"
destdir="temp/classes"
bootclasspath="${compile.classpath}"
target="1.1"
debug="on"
deprecation="on"
includes="*.java"
optimize="true"
encoding="UTF-8"/>
</target>
解释:bootclasspath参数(启动类库):它已经包含了jre/lib目录下的rt.jar,以及我们自定义的类库。如果使用classpath参数,仅指我们定义的类库;
(5) java任务 。为运行Java应用程序,需要使用java任务
A.运行一个类
<target name="run" description="Run the program">
<java classname="${main}" classpath="${classpath}"/>
</target>
解释:classname表示要运行的主类。
B.运行某一特定类,并加上运行参数。
<java fork="true" classname="proguard.ProGuard" classpath="${proguard.classpath}">
<arg line="-libraryjars ${proguard.classpath}"/>
<arg line="-injars temp/${app.project}_tmp.jar"/>
<arg line="-outjar temp/${app.project}_obf.jar"/>
<arg line="-defaultpackage ''"/>
<arg line="-dontusemixedcaseclassnames"/>
<arg line="-keep public class ${app.midlet}"/>
</java>
解释:fork参数:为true时,在新的JVM实例中运行,不影响当前JVM工作
如何减少文件的size?
使用3.3.2版本的proguard.jar,然后在混淆参数中添加两行代码:
<arg line = "-overloadaggressively"/>
<arg line="-keepclasseswithmembers public class ${app.midlet} {public void startApp();public void destroyApp(boolean);}"/>
这样可以保证startApp和destroyApp不被打上Final修饰符,同时还能更大的压缩代码,不过要求Midlet类中startApp和destroyApp方法保持上面的形式。
(6)JAR任务
将编译好的CLASS文件打成JAR包,这是JAR任务的工作:
<target name="jar" depends="Compile" description="Build jar file">
<jar jarfile="dist/${name}.jar"
basedir="${tmp/class}"
manifest="tmp/res/manifest.mf"
manifestencoding="UTF-8">
<fileset dir="res" includes="*.*"/> <!-- 将res目录中的文件也打进JAR包 -->
</jar>
</target>
(7)exec任务,用以调用外部程序
<exec executable="${LIB_PATH}/preverify.exe">
<arg line="-classpath ${compile.classpath} -d temp/build temp/obfuscate"/>
</exec>
四、ANT的参数部分:
-quiet(不显示大多数build信息)
-verbose(显示构建过程的每个操作的详细信息)
-buildfile filename.xml 构建特定的xml文件
-Dproperty=value 设定特别的外部参数,外部参数可以传递给内部变量
-find filename.xml 它会让ANT自动查找当前的目录、父目录中的xml文件,直到失败
例:
ant -buildfile test.xml
使用当前目录下的test.xml运行Ant
ant -buildfile test.xml dist
使用当前目录下的test.xml运行Ant,执行一个叫做dist的target。
ant -buildfile test.xml -Dbuild=build/classes dist
使用当前目录下的test.xml运行Ant,执行一个叫做dist的target,并设定build变量的值为build/classes。
ANT最佳实践
在Ant出现之前,构建和部署Java应用需要使用包括特定平台的脚本、Make文件、各种版本的IDE甚至手工操作的“大杂烩”。现在,几乎所有的开源Java项目都在使用Ant,大多数公司的内部项目也在使用Ant。Ant在这些项目中的广泛使用自然导致了读者对一整套Ant最佳实践的迫切需求。
本文总结了我喜爱的Ant技巧或最佳实践,多数是从我亲身经历的项目错误或我听说的其他人经历的 “恐怖”故事中得到灵感的。比如,有人告诉我有个项目把XDoclet 生成的代码放入带有锁定文件功能的版本控制工具中。当开发者修改源代码时,他必须记住手工检出(Check out)并锁定所有将要重新生成的文件。然后,手工运行代码生成器,只到这时他才能够让Ant编译代码,这一方法还存在如下一些问题:
生成的代码无法存储在版本控制系统中。
Ant(本案例中是Xdoclet)应该自动确定下一次构建涉及的源文件,而不应由程序员手工确定。
Ant的构建文件应该定义好正确的任务依赖关系,这样程序员就不必为了完成构建而不得不按照特定顺序调用任务。
当我开始一个新项目时,我首先编写Ant构建文件。Ant文件明确地定义构建的过程,并被团队中的每个程序员使用。本文所列的技巧基于这样的假定:Ant构建文件是一个必须仔细编写的重要文件,它应在版本控制系统中得到维护,并被定期进行重构。下面是我的十五大Ant最佳实践。
首先也是最重要的,花费时间格式化你的XML让它看上去很清晰。不论XML是否美观,Ant都可以工作。但是丑陋的XML很难令人读懂。倘若你在任务之间留出空行,有规则的缩进,每行文字不超过90列左右,那么XML令人惊讶地易读。再加上使用能够高亮XML语法的优秀编辑器或IDE工具,你就不会有阅读的麻烦。
同样,精选含意明确、容易读懂的词汇来命名任务和属性。比如,dir.reports就比rpts好。特定的编码规范并不重要,只要拿出一套规范并坚持使用就行。
[root dir]
| build.xml
+--src
+--lib (包含第三方 JAR包)
+--build (由 build任务生成)
+--dist (由 build任务生成)
当build.xml在顶级目录时,假设你处于项目某个子目录中,只要输入:ant -find compile 命令,不需要改变工作目录就能够以命令行方式编译代码。参数-find告诉Ant寻找存在于上级目录中的build.xml并执行。
即使你把项目划分为多个构建文件,也应使程序员能够在项目根目录下找到核心build.xml。尽管该文件只是将实际构建工作委派给下级构建文件,也应保证该文件可用。
<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任务。当程序员仅仅执行编译任务或其他任务时,他们不需要构建文件事先执行既令人讨厌又没有必要的清空任务。要相信程序员能够确定何时需要清空所有文件。
当系统变得庞大后,稍不注意就可能将依赖于客户端的代码引入到服务端。这是因为典型的IDE项目文件编译任何文件都使用单一的classpath。而Ant能让你更有效地控制构建活动。
设计你的Ant构建文件编译大型项目的步骤:首先,编译公共应用代码,将编译结果打成JAR包文件。然后,编译上一层的项目代码,编译时依靠第一步产生的JAR文件。不断重复这一过程,直到最高层的代码编译完成。
分步构建强化了任务从属关系管理。如果你工作在底层Java框架上,偶然引用到高层的GUI模板组件,这时代码不需要编译。这是由于构建文件在编译底层框架时在源路径中没有包含高层GUI面板组件的代码。
<project name="sample" default="compile" basedir=".">
<path id="classpath.common">
<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="classpath.common"/>
</path>
<target name="compile.common" depends="prepare">
<javac destdir="${dir.build}" srcdir="${dir.src}">
<classpath refid="classpath.common"/>
<include name="com/oreilly/common/**"/>
</javac>
</target>
</project>
当项目不断增长构建日益复杂时,这一技术越发体现出其价值。你可能需要为编译不同层次的应用定义各自的文件路径,比如运行单元测试的、运行应用程序的、运行Xdoclet的、生成JavaDocs的等等不同路径。这种组件化路径定义的方法比为每个任务单独定义路径要优越得多。否则,很容易丢失任务从属关系的轨迹。
应该定期检查任务的从属关系以保证构建工作得到正确执行。大的构建文件随着时间推移趋向于增加更多的任务,所以到最后可能由于不必要的从属关系导致构建工作非常困难。比如,你可能发现在程序员只需编译一些没有使用EJB的GUI代码时又重新生成了EJB代码。
以“优化”的名义忽略任务的从属关系是另一种常见的错误。这种错误迫使程序员为了得到恰当的结果必须记住并按照特定的顺序调用一串任务。更好的做法是:提供描述清晰的公共任务,这些任务包含正确的任务从属关系;另外提供一套“专家”任务让你能够手工执行个别的构建步骤,这些任务不提供完整的构建过程,但是让那些专家用户在快速而恼人的编码期间能够跳过某些步骤。
<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-b8"/>
<property name="jdom.jar" value="jdom.jar"/>
<property name="jdom.jar.withpath"
value="${jdom.home}/build/${jdom.jar}"/>
etc...
</project>
或者你可以使用属性文件:
<project name="sample" default="compile" basedir=".">
<property file="sample.properties"/>
etc...
</project>
在属性文件 sample.properties中:
dir.build=build
dir.src=src
jdom.home=../java-tools/jdom-b8
jdom.jar=jdom.jarjdom.jar.withpath=${jdom.home}/build/${jdom.jar}
用一个独立的文件定义属性是有好处的,它可以清晰地定义构建中的可配置部分。另外,在开发者工作在不同操作系统的情况下,你可以在不同的平台上提供该文件的不同版本。
如果你部署开放源码项目,应该提供包含编译代码所需的所有JAR文件的发行版本。当然,这是在遵守许可协议的基础上。对于内部项目,相关的JAR文件都应在版本控制系统的管理中,并捡出(check out)到大家都知道的位置。
当你必须引用外部路径时,应将路径定义为属性。使程序员能够用适合他们自己的机器环境的参数重载这些属性。你也可以使用以下语法引用环境变量:
<property environment="env"/>
<property name="dir.jboss" value="${env.JBOSS_HOME}"/>
除构建文件之外,你还应在版本控制中维护第三方JAR文件。同样,这使你能够重新构建旧版本的软件。这也能够更容易保证所有开发者拥有一致的JAR文件,因为他们都是同构建文件一起从版本控制系统中捡出的。
通常应避免在版本控制系统中存放构建成果。倘若你的源代码很好地得到了版本控制,那么通过构建过程你能够重新生成任何版本的产品。
IDE的问题是一个关于团队一致性和重现性的问题。几乎所有的IDE设计初衷都是为了提高程序员的个人生产率,而不是开发团队的持续构建。典型的IDE要求每个程序员定义自己的项目文件。程序员可能拥有不同的目录结构,可能使用不同版本的库文件,还可能工作在不同的平台上。这将导致出现这种情况:在Bob那里运行良好的代码,到Sally那里就无法运行。
不管你的开发团队使用何种IDE,一定要建立所有程序员都能够使用的Ant构建文件。要建立一个程序员在将新代码提交版本控制系统前必须执行Ant构建文件的规则。这将确保代码是经过同一个Ant构建文件构建的。当出现问题时,要使用项目标准的Ant构建文件,而不是通过某个IDE来执行一个干净的构建。
程序员可以自由选择任何他们习惯使用的IDE工具或编辑器。但是Ant应作为公共基线以保证代码永远是可构建的。
一个最常用的方法是写一个Ant任务,按照期望的目录结构把一大堆文件拷贝到临时目录中,然后生成压缩文件。这不是最有效的方法。使用zipfileset属性是更好的解决方案。它让你从任何位置选择文件,然后把它们按照不同目录结构放进压缩文件中。以下是一个例子:
<ear earfile="${dir.dist.server}/payroll.ear"
appxml="${dir.resources}/application.xml">
<fileset dir="${dir.build}" includes="commonServer.jar"/>
<fileset dir="${dir.build}">
<include name="payroll-ejb.jar"/>
</fileset>
<zipfileset dir="${dir.build}" prefix="lib">
<include name="hr.jar"/>
<include name="billing.jar"/>
</zipfileset>
<fileset dir=".">
<include name="lib/jdom.jar"/>
<include name="lib/log4j.jar"/>
<include name="lib/ojdbc14.jar"/>
</fileset>
<zipfileset dir="${dir.generated.src}" prefix="META-INF">
<include name="jboss-app.xml"/>
</zipfileset>
</ear>
在这个例子中,所有JAR文件都放在EAR文件包的lib目录中。hr.jar和billing.jar是从构建目录拷贝过来的。因此我们使用zipfileset属性把它们移动到EAR文件包内部的lib目录。prefix属性指定了其在EAR文件中的目标路径。
构建文件应该只在与输出文件相关联的输入文件发生变化时执行任务。一个构建文件在不必执行诸如编译、拷贝或其他工作任务的时候执行这些任务是低效的。当项目规模增长时,即使是小的低效工作也会成为大的问题。
ant compile
其实开发人员都很熟悉Ant,并且完全能够自己键入ant compile。请不要仅仅为了调用Ant而使用特定平台的脚本。这只会使其他人在首次使用你的脚本时增加学习和理解的烦扰。除此之外,你不可能提供适用于每个操作系统的脚本,这是真正烦扰其他用户的地方。
生成并维护构建脚本不是一项富有魅力的工作,但却是一项必需的工作。一个好的Ant构建文件将使你能够集中到更喜欢的工作——写代码中去!
Ant是一种基于Java和XML的build工具。
它可以帮助我们将项目开发过程中需要完成的各种步骤组织起来,通过一个简易的方式来构建整个项目。Ant究竟能做什么呢?这取决于Ant的任务(Task),Ant有哪些任务,就能完成哪些工作!一些典型的任务是:编译java源代码、运行java程序、拷贝文件或目录、将编译完成的类打包等等。
当然,Ant是一个开放的架构,任何人,都可以按照自己的方式,添加自己的任务(Task),这需要
遵守Ant的一些规范。
二、ANT的安装部署
1、 确保你的机器已经安装了JDK,确保你的path环境变量中包含有java虚拟机的运行程序,确保你的环境变量中有JAVA_HOME变量
2、 下载ant并解压
3、 设置ANT_HOME环境变量,指向ant解压根目录
4、 在path环境变量中,添加ANT_HOME/bin目录
5、 打开控制台,运行:ant
6、 如果能够运行ant(忽略这些异常:Buildfile: build.xml does not exist!),而不是出现诸如命令无法解释的错误,那么你的ant就安装成功了
三.Ant的运行靠的就是一个构建脚本,默认名称为build.xml,只要你构建一个这样的文件,按照规定把需要完成的工作任务写在这个配置文件中,然后在dos下运行ant就会执行这个文件,如果文件名不是默认的(例如mybuild.xml),则这样运行:ant -f mybuild.xml ,如果该配置文件不存在将会报错:Buildfile: build.xml does not exist!
三、ANT的组成(主要由三部分组成:Project、Target、Task)1、Project(项目)。基本属性:name、default、basedir。一个build.xml只能存在一个Project工程。
name :项目名称
default :缺省开始执行的Target
basedir :用于计算所有其他路径的基路径。
例1:<project name="dev" basedir="." default="dist">
2、Target。一个项目可以定义一个或多个Target、一个Target是一系列你想要执行的任务,而一个target一般包含若干task,如编译任务、打包任务、混淆任务。
Target的基本属性: name:target的名字,depends:该target依赖关系,description: target的描述信息。
例2:<target name="clean" depends="init" description="Clean dist and temp directories">
Ant在执行时以Target为单位,Target的depends属性又决定了哪个Target先执行,因此我们可以通过Target来构造编译顺序。
例3:<target name="copy" >
<target name="javac" depends="copy">
<target name="jar" depends="javac">
执行顺序是从下至上,依次执行,如果某个Target没有depends属性,那么就顺序执行。
例4:一个完整的Target:
<target name="compile" depends="replacesrc" description="Compiling Java source files">
<javac srcdir="temp/src"
destdir="temp/build"
debug="on"
deprecation="false"
optimize="true">
<classpath refid="classpath"/>
</javac>
</target>
3、Task。Ant执行的一系列任务是由Target构成的,而Target又是由数个小的Task构成的,task任务是最小的运行单位,我们可以把copy、delete、mkdir等操作认为是一个小任务。
(1)copy任务。
A.拷贝单个文件:
<copy file="Demo/manifest.mf" todir="temp/build/META-INF" />
解释:把一个文件拷贝到拷贝相关文件夹中。
B.拷贝文件夹:
<copy todir="temp/build">
<fileset dir="temp/classes"/>
</copy>
解释:把temp/build目录下的所有文件,拷贝到temp/classes目录中去
C.替换拷贝
<copy todir="temp/build">
<fileset dir="temp/classes" includes="*.class" />
<filterset>
<filter token="@Time@" value="${app.time}"/>
</filterset>
</copy>
解释: <filterset>过滤集,可以将temp/classes文件夹下的存在@Time@标记的文件,替换为变量${app.time}值。这样在完成拷贝的同时也完成了替换任务。
(2)mkdir任务和delete任务
A.单独的delete和mkdir任务
<delete dir="temp/src"/>
<delete file="temp/build/Thumbs.db" />
<mkdir dir="temp/src"/>
B. 带有条件的删除任务
<delete>
<fileset dir="store" includes="*.*"/>
<fileset dir="source" excludes="*.java"/> </delete>
解释:include表示要删除哪些文件;而excludes表示排除的文件;
(3)replace替换任务
A.替换某一文件中的字符串
<replace file ="base/testing.txt">
token="@temp@"
value="${newstring}"
</replace>
解释:token是需要替换的标记;value是新值,将testing.txt文件中的@temp@替换为新值。
B.替换某个文件夹中存在特定标记的文件
<replace dir="temp" token="@CHARSET@" value="${webapp.charset}"/>
解释:temp目录中文件,如果有存在@CHARSET@标记的,将被替换为${webapp.charset}变量值。
C.批量替换
<replace dir="dist" includes="${app.project}.jad" encoding="UTF-8">
<replacefilter token="@NAME@" value="${app.name}"/>
<replacefilter token="@VENDOR@" value="${app.vendor}"/>
<replacefilter token="@MIDLET@" value="${app.midlet}"/>
<replacefilter token="@JAR@" value="${app.project}"/>
<replacefilter token="@FILESIZE@" value="${size}"/>
<replacefilter token="@DESCRIPTION@" value="${app.description}"/> <replacefilter token="@PRICE@" value="${app.price}"/>
</replace>
(4) javac任务 ,编译源程序
<target name="Compile" description="Compile Java source files">
<javac srcdir="src"
destdir="temp/classes"
bootclasspath="${compile.classpath}"
target="1.1"
debug="on"
deprecation="on"
includes="*.java"
optimize="true"
encoding="UTF-8"/>
</target>
解释:bootclasspath参数(启动类库):它已经包含了jre/lib目录下的rt.jar,以及我们自定义的类库。如果使用classpath参数,仅指我们定义的类库;
(5) java任务 。为运行Java应用程序,需要使用java任务
A.运行一个类
<target name="run" description="Run the program">
<java classname="${main}" classpath="${classpath}"/>
</target>
解释:classname表示要运行的主类。
B.运行某一特定类,并加上运行参数。
<java fork="true" classname="proguard.ProGuard" classpath="${proguard.classpath}">
<arg line="-libraryjars ${proguard.classpath}"/>
<arg line="-injars temp/${app.project}_tmp.jar"/>
<arg line="-outjar temp/${app.project}_obf.jar"/>
<arg line="-defaultpackage ''"/>
<arg line="-dontusemixedcaseclassnames"/>
<arg line="-keep public class ${app.midlet}"/>
</java>
解释:fork参数:为true时,在新的JVM实例中运行,不影响当前JVM工作
如何减少文件的size?
使用3.3.2版本的proguard.jar,然后在混淆参数中添加两行代码:
<arg line = "-overloadaggressively"/>
<arg line="-keepclasseswithmembers public class ${app.midlet} {public void startApp();public void destroyApp(boolean);}"/>
这样可以保证startApp和destroyApp不被打上Final修饰符,同时还能更大的压缩代码,不过要求Midlet类中startApp和destroyApp方法保持上面的形式。
(6)JAR任务
将编译好的CLASS文件打成JAR包,这是JAR任务的工作:
<target name="jar" depends="Compile" description="Build jar file">
<jar jarfile="dist/${name}.jar"
basedir="${tmp/class}"
manifest="tmp/res/manifest.mf"
manifestencoding="UTF-8">
<fileset dir="res" includes="*.*"/> <!-- 将res目录中的文件也打进JAR包 -->
</jar>
</target>
(7)exec任务,用以调用外部程序
<exec executable="${LIB_PATH}/preverify.exe">
<arg line="-classpath ${compile.classpath} -d temp/build temp/obfuscate"/>
</exec>
四、ANT的参数部分:
-quiet(不显示大多数build信息)
-verbose(显示构建过程的每个操作的详细信息)
-buildfile filename.xml 构建特定的xml文件
-Dproperty=value 设定特别的外部参数,外部参数可以传递给内部变量
-find filename.xml 它会让ANT自动查找当前的目录、父目录中的xml文件,直到失败
例:
ant -buildfile test.xml
使用当前目录下的test.xml运行Ant
ant -buildfile test.xml dist
使用当前目录下的test.xml运行Ant,执行一个叫做dist的target。
ant -buildfile test.xml -Dbuild=build/classes dist
使用当前目录下的test.xml运行Ant,执行一个叫做dist的target,并设定build变量的值为build/classes。
ANT最佳实践
在Ant出现之前,构建和部署Java应用需要使用包括特定平台的脚本、Make文件、各种版本的IDE甚至手工操作的“大杂烩”。现在,几乎所有的开源Java项目都在使用Ant,大多数公司的内部项目也在使用Ant。Ant在这些项目中的广泛使用自然导致了读者对一整套Ant最佳实践的迫切需求。
本文总结了我喜爱的Ant技巧或最佳实践,多数是从我亲身经历的项目错误或我听说的其他人经历的 “恐怖”故事中得到灵感的。比如,有人告诉我有个项目把XDoclet 生成的代码放入带有锁定文件功能的版本控制工具中。当开发者修改源代码时,他必须记住手工检出(Check out)并锁定所有将要重新生成的文件。然后,手工运行代码生成器,只到这时他才能够让Ant编译代码,这一方法还存在如下一些问题:
生成的代码无法存储在版本控制系统中。
Ant(本案例中是Xdoclet)应该自动确定下一次构建涉及的源文件,而不应由程序员手工确定。
Ant的构建文件应该定义好正确的任务依赖关系,这样程序员就不必为了完成构建而不得不按照特定顺序调用任务。
当我开始一个新项目时,我首先编写Ant构建文件。Ant文件明确地定义构建的过程,并被团队中的每个程序员使用。本文所列的技巧基于这样的假定:Ant构建文件是一个必须仔细编写的重要文件,它应在版本控制系统中得到维护,并被定期进行重构。下面是我的十五大Ant最佳实践。
1. 采用一致的编码规范
Ant用户有的喜欢有的痛恨其构建文件的XML语法。与其跳进这一令人迷惑的争论中,不如让我们先看一些能保持XML构建文件简洁的方法。首先也是最重要的,花费时间格式化你的XML让它看上去很清晰。不论XML是否美观,Ant都可以工作。但是丑陋的XML很难令人读懂。倘若你在任务之间留出空行,有规则的缩进,每行文字不超过90列左右,那么XML令人惊讶地易读。再加上使用能够高亮XML语法的优秀编辑器或IDE工具,你就不会有阅读的麻烦。
同样,精选含意明确、容易读懂的词汇来命名任务和属性。比如,dir.reports就比rpts好。特定的编码规范并不重要,只要拿出一套规范并坚持使用就行。
2. 将build.xml放在项目根目录中
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并执行。
3. 使用单一的构建文件
有人喜欢将一个大项目分解成几个小的构建文件,每个构建文件分担整个构建过程的一小部分工作。这确实是看法不同的问题,但是应该认识到,将构建文件分割会增加对整体构建过程的理解难度。要注意在单一构建文件能够清楚表现构建层次的情况下不要过工程化(over-engineer)。即使你把项目划分为多个构建文件,也应使程序员能够在项目根目录下找到核心build.xml。尽管该文件只是将实际构建工作委派给下级构建文件,也应保证该文件可用。
4. 提供良好的帮助说明
应尽量使构建文件自文档化。增加任务描述是最简单的方法。当你输入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>
5. 提供清除任务
每个构建文件都应包含一个清除任务,用来删除所有生成的文件和目录,使系统回到构建文件执行前的初始状态。执行清空任务后还存在的文件都应处在版本控制系统的管理之下。比如:<target name="clean"
description="Destroys all generated files and dirs.">
<delete dir="${dir.build}"/>
<delete dir="${dir.dist}"/>
</target>
除非是在产生整个系统版本的特殊任务中,否则不要自动调用clean任务。当程序员仅仅执行编译任务或其他任务时,他们不需要构建文件事先执行既令人讨厌又没有必要的清空任务。要相信程序员能够确定何时需要清空所有文件。
6. 使用ANT管理任务从属关系
假设你的应用由Swing GUI组件、Web界面、EJB层和公共应用代码组成。在大型系统中,你需要清晰地定义每个Java包属于系统的哪一层。否则任何一点修改都要被迫重新编译成百上千个文件。糟糕的任务从属关系管理会导致过度复杂而脆弱的系统。改变GUI面板的设计不应造成Servlet和EJB的重编译。当系统变得庞大后,稍不注意就可能将依赖于客户端的代码引入到服务端。这是因为典型的IDE项目文件编译任何文件都使用单一的classpath。而Ant能让你更有效地控制构建活动。
设计你的Ant构建文件编译大型项目的步骤:首先,编译公共应用代码,将编译结果打成JAR包文件。然后,编译上一层的项目代码,编译时依靠第一步产生的JAR文件。不断重复这一过程,直到最高层的代码编译完成。
分步构建强化了任务从属关系管理。如果你工作在底层Java框架上,偶然引用到高层的GUI模板组件,这时代码不需要编译。这是由于构建文件在编译底层框架时在源路径中没有包含高层GUI面板组件的代码。
7. 定义并重用文件路径
如果文件路径在一个地方一次性集中定义,并在整个构建文件中得到重用,那么构建文件更易于理解。以下是这样做的一个例子:<project name="sample" default="compile" basedir=".">
<path id="classpath.common">
<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="classpath.common"/>
</path>
<target name="compile.common" depends="prepare">
<javac destdir="${dir.build}" srcdir="${dir.src}">
<classpath refid="classpath.common"/>
<include name="com/oreilly/common/**"/>
</javac>
</target>
</project>
当项目不断增长构建日益复杂时,这一技术越发体现出其价值。你可能需要为编译不同层次的应用定义各自的文件路径,比如运行单元测试的、运行应用程序的、运行Xdoclet的、生成JavaDocs的等等不同路径。这种组件化路径定义的方法比为每个任务单独定义路径要优越得多。否则,很容易丢失任务从属关系的轨迹。
8. 定义恰当的任务从属关系
假设dist任务从属于jar任务,那么哪个任务从属于compile任务哪个任务从属于prepare任务呢?Ant构建文件最终定义了任务的从属关系图,它必须被仔细地定义和维护。应该定期检查任务的从属关系以保证构建工作得到正确执行。大的构建文件随着时间推移趋向于增加更多的任务,所以到最后可能由于不必要的从属关系导致构建工作非常困难。比如,你可能发现在程序员只需编译一些没有使用EJB的GUI代码时又重新生成了EJB代码。
以“优化”的名义忽略任务的从属关系是另一种常见的错误。这种错误迫使程序员为了得到恰当的结果必须记住并按照特定的顺序调用一串任务。更好的做法是:提供描述清晰的公共任务,这些任务包含正确的任务从属关系;另外提供一套“专家”任务让你能够手工执行个别的构建步骤,这些任务不提供完整的构建过程,但是让那些专家用户在快速而恼人的编码期间能够跳过某些步骤。
9.使用属性
任何需要配置或可能发生变化的信息都应作为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-b8"/>
<property name="jdom.jar" value="jdom.jar"/>
<property name="jdom.jar.withpath"
value="${jdom.home}/build/${jdom.jar}"/>
etc...
</project>
或者你可以使用属性文件:
<project name="sample" default="compile" basedir=".">
<property file="sample.properties"/>
etc...
</project>
在属性文件 sample.properties中:
dir.build=build
dir.src=src
jdom.home=../java-tools/jdom-b8
jdom.jar=jdom.jarjdom.jar.withpath=${jdom.home}/build/${jdom.jar}
用一个独立的文件定义属性是有好处的,它可以清晰地定义构建中的可配置部分。另外,在开发者工作在不同操作系统的情况下,你可以在不同的平台上提供该文件的不同版本。
10. 保持构建过程独立
为了最大限度的扩展性,不要应用外部路径和库文件。最重要的是不要依赖于程序员的CLASSPATH设置。取而代之的是,在构建文件中使用相对路径并定义自己的路径。如果你引用了绝对路径如C:/java/tools,其他开发者未必使用与你相同的目录结构,所以就无法使用你的构建文件。如果你部署开放源码项目,应该提供包含编译代码所需的所有JAR文件的发行版本。当然,这是在遵守许可协议的基础上。对于内部项目,相关的JAR文件都应在版本控制系统的管理中,并捡出(check out)到大家都知道的位置。
当你必须引用外部路径时,应将路径定义为属性。使程序员能够用适合他们自己的机器环境的参数重载这些属性。你也可以使用以下语法引用环境变量:
<property environment="env"/>
<property name="dir.jboss" value="${env.JBOSS_HOME}"/>
11. 使用版本控制系统
构建文件是一个重要的制品,应该像代码一样进行版本控制。当你标记你的代码时,也应用同样的标签标记构建文件。这样当你需要回溯到旧版本并进行构建时,能够使用相应版本的构建文件。除构建文件之外,你还应在版本控制中维护第三方JAR文件。同样,这使你能够重新构建旧版本的软件。这也能够更容易保证所有开发者拥有一致的JAR文件,因为他们都是同构建文件一起从版本控制系统中捡出的。
通常应避免在版本控制系统中存放构建成果。倘若你的源代码很好地得到了版本控制,那么通过构建过程你能够重新生成任何版本的产品。
12. 把Ant作为“最小公分母”
假设你的开发团队使用IDE工具,当程序员通过点击图标就能够构建整个应用时为什么还要为Ant而烦恼呢?IDE的问题是一个关于团队一致性和重现性的问题。几乎所有的IDE设计初衷都是为了提高程序员的个人生产率,而不是开发团队的持续构建。典型的IDE要求每个程序员定义自己的项目文件。程序员可能拥有不同的目录结构,可能使用不同版本的库文件,还可能工作在不同的平台上。这将导致出现这种情况:在Bob那里运行良好的代码,到Sally那里就无法运行。
不管你的开发团队使用何种IDE,一定要建立所有程序员都能够使用的Ant构建文件。要建立一个程序员在将新代码提交版本控制系统前必须执行Ant构建文件的规则。这将确保代码是经过同一个Ant构建文件构建的。当出现问题时,要使用项目标准的Ant构建文件,而不是通过某个IDE来执行一个干净的构建。
程序员可以自由选择任何他们习惯使用的IDE工具或编辑器。但是Ant应作为公共基线以保证代码永远是可构建的。
13. 使用zipfileset属性
人们经常使用Ant产生WAR、JAR、ZIP和 EAR文件。这些文件通常都要求有一个特定的内部目录结构,但其往往与你的源代码和编译环境的目录结构不匹配。一个最常用的方法是写一个Ant任务,按照期望的目录结构把一大堆文件拷贝到临时目录中,然后生成压缩文件。这不是最有效的方法。使用zipfileset属性是更好的解决方案。它让你从任何位置选择文件,然后把它们按照不同目录结构放进压缩文件中。以下是一个例子:
<ear earfile="${dir.dist.server}/payroll.ear"
appxml="${dir.resources}/application.xml">
<fileset dir="${dir.build}" includes="commonServer.jar"/>
<fileset dir="${dir.build}">
<include name="payroll-ejb.jar"/>
</fileset>
<zipfileset dir="${dir.build}" prefix="lib">
<include name="hr.jar"/>
<include name="billing.jar"/>
</zipfileset>
<fileset dir=".">
<include name="lib/jdom.jar"/>
<include name="lib/log4j.jar"/>
<include name="lib/ojdbc14.jar"/>
</fileset>
<zipfileset dir="${dir.generated.src}" prefix="META-INF">
<include name="jboss-app.xml"/>
</zipfileset>
</ear>
在这个例子中,所有JAR文件都放在EAR文件包的lib目录中。hr.jar和billing.jar是从构建目录拷贝过来的。因此我们使用zipfileset属性把它们移动到EAR文件包内部的lib目录。prefix属性指定了其在EAR文件中的目标路径。
14. 测试Clean任务
假设你的构建文件中有clean和compile的任务,执行以下的测试。第一步,执行ant clean;第二步,执行ant compile;第三步,再执行ant compile。第三步应该不作任何事情。如果文件再次被编译,说明你的构建文件有问题。构建文件应该只在与输出文件相关联的输入文件发生变化时执行任务。一个构建文件在不必执行诸如编译、拷贝或其他工作任务的时候执行这些任务是低效的。当项目规模增长时,即使是小的低效工作也会成为大的问题。
15. 避免特定平台的Ant封装
不管什么原因,有人喜欢用简单的、名称叫做compile之类的批文件或脚本装载他们的产品。当你去看脚本的内容你会发现以下内容:ant compile
其实开发人员都很熟悉Ant,并且完全能够自己键入ant compile。请不要仅仅为了调用Ant而使用特定平台的脚本。这只会使其他人在首次使用你的脚本时增加学习和理解的烦扰。除此之外,你不可能提供适用于每个操作系统的脚本,这是真正烦扰其他用户的地方。
总结
太多的公司依靠手工方法和特别程序来编译代码和生成软件发布版本。那些不使用Ant或类似工具定义构建过程的开发团队,花费了太多的时间来捕捉代码编译过程中出现的问题:在某些开发者那里编译成功的代码,到另一些开发者那里却失败了。生成并维护构建脚本不是一项富有魅力的工作,但却是一项必需的工作。一个好的Ant构建文件将使你能够集中到更喜欢的工作——写代码中去!
相关文章推荐
- Git 与 GitHub使用指南汇总
- ant 使用指南
- ant 使用指南
- ant使用指南
- Eclipse快速上手指南之使用Ant
- Eclipse快速上手指南之使用Ant
- Eclipse快速上手指南之使用ANT
- 进程经常使用小知识汇总
- Ant使用指南
- Eclipse快速上手指南之使用ANT
- ant 使用指南
- ant 使用指南
- java提高篇(一)拓展篇 java知识汇总---IO流的使用规律总结(含代码示例)浅显易懂
- Ant介绍以及基本使用指南
- Ant基本使用指南
- ant使用指南详细入门教程
- ant 使用指南
- Eclipse快速上手指南之使用ANT
- ant 使用指南
- SVN和Subclipse安装和使用指南汇总