《Ant权威指南》笔记(一)

Ant的由来(序)

James Duncan Davidson当年用纯Java开发Tomcat的时候,不仅想让它跨平台运行,还想要在不同的操作系统上都能够进行开发和构建。这种较大的项目的编译构建过程是很复杂,需要用到很多工具和脚本(比如GNU make,Shell脚本,批处理文件等等)处理资源、处理依赖项、控制编译过程、打包(Jar,War),某些特别的Java技术如EJB、RMI在编译打包时还需要特别处理。James尝试了很多工具和方案,最后的结论就是太TM难用了。

首先是慢。举个栗子,编译java代码要用javac,javac其实就是包装了下java用于编译的Java工具类(JDK工具都是用纯Java写的,JDK目录下的那些exe文件实际上都是调用Java类来实现具体功能的)。但是用make,shell这些工具每次调用javac,都要开新进程创建新的VM实例,如果每编译一个文件调用一次javac,开销就老大了,不慢才怪呢。调用命令后还必须要解析控制台输出信息才能知道执行情况,更不可能使用Java提供的异常和错误信息来确定执行状态。

然后是累。make,shell脚本要跨平台太困难,在不同的系统上要用不同的版本;而且要编写shell脚本和make脚本(make只是shell基础上的语言扩展),得有linux编程基础,门槛还是比较高的;这类脚本编写起来容易出错(Makefile的tab问题能烦死人),难以调试。总之,如果构建过程用了一堆这样的脚本,维护起来肯定是又烦又累。慢的问题还能通过一次编译一批文件来缓解,但这个问题可就无解了。

James实在受不了了,就自己用Java开发了一个小工具,就是Ant,用来编译和打包Java项目。构建中用到JDK中工具时都是直接调用Java类,而不是从命令行调用,慢的问题就解决了。构建中直接向Java编译类提供或获取数据,编译过程中有什么问题,也能进行错误或异常处理。因为是用Java写的,天然就是可以跨平台的使用的。Ant从配置文件中读取数据来控制构建过程,刚开始的时候用Properties文件,但是Properties文件难用表达比较复杂的层次结构关系,当他把Ant解决方案设计成"工程-目标-任务"的层次结构时,改用XML作为构建配置文件,XML文件是很好理解和掌握的,学习门槛变得非常低。再后来又利用反射功能,支持自定义任务,Ant的功能就不再仅限于构建Java项目了,可以使用在更广泛的场合。至此Ant就基本成型了。

James完成这个工具之后,自己用着很爽并且在网上共享,然后就没怎么再关注了,直到后来有一天忽然发现老多人都已经在用他的这个杰作了。James说他很庆幸当时没有把精力一直花在这上面,否则可以能会不自觉的加入很多更本不必要的功能,让Ant变得臃肿复杂,反而不好了。

 

这个故事除了说了Ant的来源和特点外,还有一点启示:如果遇到问题,已有的工具都不能满足需求,就应当另找一个。如果不存在这样的工具,就自己动手创建一个。然后与大家共享,其他数以千计的人可能有着类似的难题。

 

第一章 Ant入门

ant的使用非常简单:安装好Ant后(最好配置Path环境变量),在构建文件中(默认名字为build.xml)配置好构建任务,然后调用ant命令,配置的任务就开始执行了。

 

1.编写构建文件

创建一个用于编译和打包Java项目的配置文件build.xml(名字可以随便起,如mybuild.xml,执行的时候用-f 选项指定这个文件就可以):

<?xml version="1.0" encoding="UTF-8" ?>
<project name="AntDemo" default="compile" basedir=".">
    <property name="src.dir" value="src" />
    <property name="build.dir" value="build" />
    <property name="build.classes" value="${build.dir}/classes" />
    <property name="build.lib" value="${build.dir}/lib"/>
    
    <target name="prepare"  description="create the build directories prior to the comile target">
        <mkdir dir="${build.dir}" />
        <mkdir dir="${build.classes}" />
        <mkdir dir="${build.lib}" />
    </target>
    
    <target name="clean" description="removes all generated files">
        <delete dir="${build.dir}" />
    </target>
    
    <target name="compile" depends="prepare" description="compiles all source code">
        <javac srcdir="${src.dir}" destdir="${build.classes}" />
    </target>
    
    <target name="jar" depends="compile" description="generates jar file ">
        <jar jarfile="${build.lib}/antDemo.jar" basedir="${build.classes}"  excludes="**/*Test.class"/>
    </target>

    <target name="all" depends="clean, jar" description="clean, compile, generate jar file"/>
</project>

 

这个配置文件很好理解,用任何文本编辑器都能编写,只要保证是一个合法的XML文件并且用了正确的标签和属性,就可以使用ant运行。

根据这个配置文件就可以看出。Ant把任何任务都分成了3层。

  • 最顶层的Project
  • 目标Target(可以在ant命令中调用的基本单位)。如clean, compile, jar 等,在命令行可以这样调用:ant clean或  ant clean jar。Target之间可以存在依赖关系,一个Target执行前会先执行它依赖的那些Target。Project可以设置一个默认的Targe, ant命令中不指定任何target时就调用这个默认的。
  • 任务Task。
  • 各种Task可以执行各种不同的具体任务。如mkdir, delete, javac, jar等。Task可以分3种,核心任务和可选任务有100多种,常用的功能全都覆盖了
    • 核心Task。Ant内置的任务
    • 可选任务。第三方提供的,把相应的Jar包放到Ant安装目录的lib目录下就能使用。
    • 自定义任务

Task详细说明可以查看官方文档: http://ant.apache.org/manual/tasklist.html

Ant文件中还能用Property标签配置属性值,在定义之后的其他地方就可以引用,避免硬编码。

所有的构建文件都要有且只能有一个<project>元素,其中至少要有一个<target> 元素。project的defualt属性没有默认值,如果这个属性没有设置,不指定Target运行ant不会调用任何target。

 

2.运行Ant

ant命令语法如下,详细选项说明列在后面。

ant [option [option...]]   [target [target...]]

 

调用Ant时默认会在当前目录中查找默认的构建文件名:build.xml。 

当然也可以用ant -f buildfile的方式手动指定构建文件(-f, -buildfile, -file都是等效的)。

调用Ant时可以指定一个或多个要执行的Target。如果不指定就调用Project标签default属性中配置的默认Target(上面这个例子中就是compile目标)。

 

以上面的构建配置文件为例,下面的几种调用都可以:

ant                              调用默认构建文件(build.xml)中的默认目标(compile)

ant -f mybuild.xml  jar          调用构建文件mybuild.xml中的jar目标

ant clean jar                    调用默认构建文件(build.xml)中的clean和jar目标。需要注意多个目标会按调用先后顺序执行。如果调用ant jar clean就是先编译打包(jar),然后全清理掉(clean),就白干了。

 

Ant执行时会按执行顺序显示每个Target的名字,也会显示每个任务的名字([任务名])和任务中输出的信息。直接调用ant的输出结果为:

Buildfile: e:\wsJava\AntDemo\build.xml

clean:
   [delete] Deleting directory e:\wsJava\AntDemo\build

prepare:
    [mkdir] Created dir: e:\wsJava\AntDemo\build
    [mkdir] Created dir: e:\wsJava\AntDemo\build\classes
    [mkdir] Created dir: e:\wsJava\AntDemo\build\lib

compile:
    [javac] Compiling 2 source files to e:\wsJava\AntDemo\build\classes

jar:
      [jar] Building jar: e:\wsJava\AntDemo\build\lib\antDemo.jar

all:

BUILD SUCCESSFUL
Total time: 0 seconds

 

3.查看构建文件中所有目标

构建文件中的目标description属性可有可无,除了方便人看外没什么卵用。有没有这个属性叫法也不一样的(没有实际作用),有该属性的叫主目标,没有的叫子目标。下面这个命令可以列出构建文件中的所有目标和description信息。

ant [-f BUILDFILE] -projecthelp  

 

4.Ant命令选项

  • -h,  -help   查看帮助信息
  • -p,  -projecthelp  查看构建文件中的所有目标信息
  • -version  显示Ant版本
  • -q,  -quiet   抑制并非由构建文件中echo任务所产生的大多数输出消息
  • -S,  -silent   只显示Task输出和构建失败信息
  • -v,  -verbose   显示构建过程中每个操作的详细消息, 不能和-debug同时使用
  • -d,  -debug     显示Ant和任务开发人员已经标志位调试信息的消息。不能与-verbose同时使用
  • -e,  -emacs     对日志消息进行格式化,使其能够Emacs的shell模式解析。具体就是打印任务消息时不缩排也不输出前面的 [任务名]
  • -diagnostics    显示对调试有用的信息
  • -f <file>,  -buildfile <file>, -file <file>   指定一个构建文件,而不是使用默认的build.xml
  • -D<property>=<value>    通过命令行向构建过程中传递属性值
  • -propertyfile <propertyfile>   从property文件中加载属性值并传递到构建过程
  • -s <file>,  -find <file>     指定Ant应当使用的构建文件,如果指定的filename文件在当前目录中没找到,就到父目录中进行搜索,直到到达文件系统的根,还找不到则构建失败。
  • -k,  -keep-going    执行不依赖失败目标的所有目标。
  • -lib <path>    指定查找jars和classes的目录
  • -l  <file>, -logfile <file>   将日志重定向到指定文件
  • -logger <class>   指定一个类来处理Ant的日志记录。该类必须实现了org.apache.tools.ant.BuildLogger接口
  • -listener <class>   为Ant设置一个监听类,将其增加到Ant的监听器列表中。Ant与IDE或其他程序集成时非常有用,后面会专门写这个。
  • -inputhandler <class>  指定用于处理输入请求的类
  • -main <class>   覆盖Ant正常的入口点
  • -noinput    不允许交互式输入
  • -autoproxy   Java1.5+,使用OS的代理设置
  • -nice number
  • -nouserlib
  • -noclasspath

 

第二章 安装和配置

基本使用

  • 下载ant发布包
  • 解压缩到一个目录既可
  • 将该目录下的子目录bin添加到PATH环境变量

 

高级配置

 留坑待填....

 

第三章 构建文件

构建文件需要根据具体项目的特性编写,不过同一类型的项目基本上是可以使用一套构建配置的。

Java提供了用于构建的Java工具库,使用Java语言编写Ant是最容易实现和维护的。XML有丰富的解析类库,并且被开发人员广泛使用,也能够表达Ant的数据模型,使用XML作为构建文件时最好的选择。

XML是一种树形的文档对象模型(DOM),其中的Project,Target等元素与Ant的模型组件相对应。

1.Ant的构建块

Project(工程)    任何构建文件的第一个元素必须是<project>标签,而且只能有一个。

  • name属性     工程的名字,也是构建文件的标识符。
  • default属性    运行Ant不指定Target时,默认执行的Target。可以设置成一个构建文件中定义的Target名字。推荐默认Target显示构建文件的帮助信息或者执行完成的构建。
  • basedir属性   定义工程的根目录,一般情况下都是" . ",也就是构建文件所在的目录(不是运行Ant命令是的目录)。在一个多层次的项目中,basedir还可以定义不同的参考点。

 

Target(目标)  一个Project可以包含多个Target,一个总的任务过程可以拆分成几个target,每个Target可以单独调用。可以把target理解成能够单独执行的一个个步骤(阶段)。具体怎么拆分这个总任务,拆分粒度是粗还是细,把哪些Task放在哪几个Target中,都是编写构建文件要考虑的问题。一般来说,粒度更小可以更灵活的组合,有些target失败也不会影响另一些的执行。但是粒度也不能太小,太小了会很琐碎不好维护。

  • name属性
  • depends属性
  • description属性

前面的build.xml实例中,编译打包拆成了两个target,如果每次这两个target都是一起被调用的,把他们放在一个target中也是可以的。

    <target name="build" depends="prepare">
        <javac srcdir="${src.dir}" destdir="${build.classes}" />
        <jar jarfile="${build.lib}/antDemo.jar" basedir="${build.classes}"  excludes="**/*Test.class"/>
    </target>

 

Task(任务)  任务是构建文件中的最小构建单位。通过Target把一个总过程组织成了几个大的Target目标(步骤),但是Target并不做任何具体的工作,Target下面有包含一些Task,所有的具体工作都是靠这些task来完成。Ant提供非常多的Task,如编译,大包,文件系统操作等等。Ant中每个任务对应于Ant对象模型中的一个Java对象,要自定义新的任务就是要编写执行该任务类然后提供给Ant调用。很多系统命令也都是用Task包装,而不是直接调用shell命令,在不同的操作系统上Task的使用方式是完全一致的。

构建文件中任务标签内部也可以有很多层次。但是Task内部的这些层次结构和Java类的层次结构没有任何关系了。

Task标签不再有统一的属性和子元素,内部层次完全取决于具体的任务。

 

2.数据元素(data element)

构建文件中除了与任务构建过程相关的元素外。还包括了保存数据的变量和抽象数据类型等元素。数据元素有两类:Property和DataType

Property

  • 表示字符串型的“键-值”对,只能用在可使用字符串的位置。
  • Propery和Java中的Property对象是兼容的,可以使用Property文件或JVM命令行-Dproperty=value选项,在运行ant时动态定义。
  • 可以使用${propName}的形式引用Property数据。

 

property数据定义和引用

<property name="data1" value="string value1"/>
<property name="data2" value="${data1} and value2">

 

加载config.properties文件中定义的property数据

<proppery file="config.properties"/>

 

通过命令行在运行时动态定义propery数据

ant mytarget -Dname=value

 

 

DataType

Property数据值都是字符串,ant并不知道这些字符串代表了什么对象。如果将包含很多个Jar文件的长串路径保存在Property数据中,就很容易出错,修改也不方便。

Ant还提供了很多种具体的数据类型(DataType),各种数据类型能够更清晰地描述特定类型的数据,如Path(路径), FileSet(文件集合,可以使用通配符)等,修改起来更加方便。

使用Property表示路径和使用Path/FileSet对象表示路径的对比:

<property name="classpath" value="${lib.dir}/j2ee.jar:${lib.dir}/servlet.jar:${lib.dir}/jasper.jar:........"/>
<path id="classpath">
  <fileset dir="${lib.dir}">
    <include name="j2ee.jar"/>
    <include name="servlet.jar"/>
    <include name="**/*.jar">
    .....
  </fileset>
</path> 

 

3.工程结构和构建文件

要编写工程的构建文件,必须要了解项目的结构。项目类型不同,项目结构往往存在较大的差异(如Web项目和GUI项目),没有最佳的工程组织模式。工程结构是比较复杂的,需要考虑跨平台(使用相对路径),依赖自包含无外部需求,功能模块和不同类型文件分离等等。

 

以下面的项目结构为例:

  • projectName
    • build.xml      构建文件
    • src/
      • api
      • module
    • doc/       项目相关文档(非JavaDoc文档,不能自动生成),如readme,license等
    • lib/        依赖的外部库,统一依赖
    • bin/   可选目录,包含安装、执行等脚本或者是难找的、定制的可执行工具(为跨平台,最好不要使用可执行程序)
    • build/     构建产出目录
      • classes/
      • doc/   javadoc产出
      • lib/    
      • bin/
    • dist/     最终用于发布的目录(内容一般都是从其他目录复制而来)
      • lib/
      • bin/
      • doc/
      • config/

 编写构建文件

 

posted @ 2015-09-08 20:50  XRacoon  阅读(1044)  评论(0编辑  收藏  举报