zoukankan      html  css  js  c++  java
  • Ant构建项目

    前言:

    Ant是java世界里第一个具有里程碑意义的项目构建工具。

    官网地址:http://ant.apache.org/

    文档地址(Ant task):http://ant.apache.org/manual/index.html

    扩展工具地址(比如checkstyle和clover):http://ant.apache.org/external.html

    本文并不打算长篇累牍的描述Ant的使用方法,因为官网已经够清楚,也有多位大神发过博文,这里只描述核心概念和一些基本实例,本文还会继续编辑,希望大家多提宝贵意见。

     

    1,Ant的安装

    从官网下载(http://ant.apache.org/bindownload.cgi)包,解压到任意目录,将bin目录加入到环境变量,一般推荐使用英文路径,并且先建立ANT_HOME的方式,以相对路径设置环境变量。(这是个好习惯)

    2,Ant的核心概念

    构建文件是以XML文件来描述,这就意味着,你可以通过xsd查看所有的熟悉和配置方式,这非常有用,当然eclipse支持Ant提示。

    每个构建文件包含一个工程(project),其实是对一个项目概念的抽象,在Eclipse插件开发当中,也有这样的抽象。

    每个工程包含若干个目标(target),目标是构成Ant构建文件的基本单元,在以后的Maven(另一种强大的项目管理工具)中类似于任务的概念。

    目标可以依赖于其他目标(depends),可以称为项目内的依赖,Ant的目标可以构成链式,树形,网状的配置结构,目标的合理依赖可以使Ant文件有良好的扩展性和复用性。

    易于扩展,很多优秀项目已经实现与Ant的整合,比如checkstyle,junit,clover。

    目标的任务可以调用另一个工程的目标。(在一个项目中,可以引用另一个项目的配置,可以称为项目级别的依赖)

    3,环境

    环境准备:Eclipse + Ant 1.7 + jdk1.5+

    项目准备:
        使用Eclipse建立一个maven目录结构的项目或者普通的Java工程。
        普通java项目:在classpath上显示的源目录为src
        Maven目录结构项目:普通java项目建立后,在classpath上删除src源目录,并在src目录下新建类似于Maven工程的目录结构。

    4,简单Ant文件 

    说明:我不会对每一个标签和属性做解释。最佳实践都属个人参考或总结,如有疏漏或错误,请高手指正并留下您的建议或指导。

    如下是一份简单的通用构建的build.xml,并附上build.properties。

    1),资源文件的配置
       最佳实践:首先,尽量较少的定义非关联的属性,而采用相对引用的方式。其次,尽量采用直观而明确的名称定义属性,保持层次感和语义的完整性
       顺便提到一句,对于稍大项目的构建,如果是构建依赖的基础包目录,尽量应该采取按功能模块或者相互依赖的关系分成独立的目录,在不破坏整个项目结构下,子项目能够采用引入最少依赖的方式完成构建。
    build.properties:

    #依赖资源包根目录
    dependency.lib.path=../../web-libs
    dependency.resource.path=${dependency.lib.path}/resources
    #编译源依赖包
    dependency.lib.main.path=${dependency.lib.path}/main
    #编译测试依赖包
    dependency.lib.test.path=${dependency.lib.path}/test
    #工具包目录
    dependency.lib.tool.path=${dependency.lib.path}/tool
    
    #源目录 目前定义为 maven的默认目录格式,方便之后介绍maven
    src.main.java=src/main/java
    src.main.resources=src/main/resources
    src.test.java=src/test/java
    src.test.resources=src/test/resources

     
    2),构建文件的配置
       最佳实践:target任务可以相互调用,我们采用depends属性来控制这个依赖,如果依赖的比较复杂,那么我们的Ant文件可能会偏向于倒立的树形结构,并且分支可能有所交叉,这从结构上并不是一个很好的脚本设计
    由于Ant脚本是顺序执行的,我们也最好依照这个特点,尽量避免target的交叉依赖,也就是完成一个target,和完成其它的target不要有太多的交集。还有一个非常重要的概念,很多target原则上都是需要清理的,我们应当树立一种清理的意识,比如我们在新建一个目录的时候,最好先清理这个目录,因为你不清楚之前的一次构建是什么时候或者构建是否过期。

    build.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <project name="common-web-demo" default="build" basedir=".">
     <description>common project automatic build script</description>
    
     <!--加载资源文件 -->
     <property file="build.properties" />
    
     <!-- 源目录classpath-->
     <path id="main.classpath">
      <fileset dir="${dependency.lib.main.path}" includes="**/*.jar" />
     </path>
    
     <!--测试目录classpath -->
     <path id="test.classpath">
      <fileset dir="${dependency.lib.main.path}" includes="**/*.jar" />
      <fileset dir="${dependency.lib.test.path}" includes="**/*.jar" />
      <fileset dir="${dependency.lib.tool.path}" includes="**/*.jar" />
     </path>
    
     <!--工具包classpath -->
     <path id="tool.classpath">
      <fileset dir="${dependency.lib.tool.path}" includes="**/*.jar" />
     </path>
    
     <!--工具包任务定义 -->
     <taskdef resource="net/sf/antcontrib/antlib.xml">
      <classpath refid="tool.classpath" />
     </taskdef>
    
     <target name="clean">
      <delete dir="${build.target.dir}" />
      <mkdir dir="${build.target.dir}" />
     </target>
    
     <!-- 主目录编译清理  -->
     <target name="main.clean">
      <delete dir="${build.target.bin.dir}" />
      <mkdir dir="${build.target.bin.dir}" />
     </target>
    
     <!-- 主目录编译  -->
     <target name="main.compile" depends="main.clean">
      <javac srcdir="${src.main.java}" destdir="${build.target.bin.dir}" source="1.5" target="1.5" debug="on">
       <classpath refid="main.classpath" />
      </javac>
      <copydir src="${src.main.resources}" dest="${build.target.bin.dir}" />
     </target>
    
     <!--主目录打包 -->
     <target name="package.main.jar" depends="main.compile">
      <jar destfile="${build.target.dir}/${build.target.assembly.name}.jar">
       <fileset dir="${build.target.bin.dir}" />
      </jar>
     </target>
    
     <!-- === 循环构建 === -->
     <target name="build" depends="clean,package.main.jar" />
    </project>


    xml编写和标签解释:
    1,关于xml的编写:在独立标签的编写上,应该选择倾向于<el .../> 而不是<el></el>,很多开源的Java框架都推荐前者的写法,比如Spring。
    2,project:default属性指定默认执行target(命令行), basedir定义构建目标路径 basedir="."指定为当前项目路径(SystemUtils.getUserDir()),其实对于构建一个独立的项目或者一个模块下的多个项目,这样设定相对路径是一个很好的做法。
    3,target(目标):一个build.xml最为核心的标签,它可以调用其它的target,也可以调用一些扩展的target,target的名称不可重复,在名称编写上也最好遵照某些规则建立分组,这样在一个Ant文件的Outline视图中可以很清晰的分辨结构和依赖关系。目前官网上的文档的task of list命令基本上都属于target的范畴。
    4,特别说明关于上述build.xml中工具包任务的定义,也就是antcontrib,它定义了一些非常有用的扩展功能,比如for for each,更有意思的是try catch,try catch真没用过,有兴趣的可以试试。

    task浅析:
    1,property :资源标签,它能定义单个属性,也可以加载多个属性。定义单个:<property name="life" value="time" />;加载多个:<property file="build.properties"/>
    2,taskdef :定义扩展,定义扩展有两种方式,一种是引入扩展标签,也就是上面的xml当中的格式,另一种可以自定义为<typedef name="urlset" classname="com.mydomain.URLSet"/> 和具体的处理类挂钩,后一种我不太了解。
    3,关于delete 与 mkdir一类有关文件的操作,包括fileset这类提供作用域的标签。这些在Ant官网中介绍的非常详细,命令众多,就不一一列举了(关于删除命令,个人推荐delete,因为它的容错性要高一些,在目录不存在时不会有任何动作)。
    4,javac,Ant整个构建体系当中最为核心的task之一,比较值得关注的属性有debug="on",source="1.5" ,前者打开debug,后者定义编译级别。
    5,jar,打包命令,既然有jar,自然就有zip,war,ear了,它们大同小异。
    6,fock 一个重要的属性,在javac junit很多相对比较耗资源的任务当中都有这个属性,只要开启,一般的意思就是采用独立jvm执行编译或运行测试等等之类,对于出现内存不足的错误很有效果。当然小项目不需要这么做,大一点的项目也可以通过调整Ant和虚拟机的内存来实现。

     

    5 相对复杂的Ant文件

       实现的功能:编译,打包,测试运行,代码检查,覆盖率检查。

     1) 主要插件:
        junit
        checkstyle 
        clover
       
    2)build.properties

    #依赖资源包根目录
    dependency.lib.path=../../web-libs
    dependency.resource.path=${dependency.lib.path}/resources
    #编译源依赖包
    dependency.lib.main.path=${dependency.lib.path}/main
    #编译测试依赖包
    dependency.lib.test.path=${dependency.lib.path}/test
    #工具包目录
    dependency.lib.tool.path=${dependency.lib.path}/tool
    
    test.jar.names=junit-4.10,spring-test-3.0.5.RELEASE
    
    #源目录
    src.main.java=src/main/java
    src.main.resources=src/main/resources
    src.test.java=src/test/java
    src.test.resources=src/test/resources
    
    src.webapp.folder=src/main/webapp
    
    #构建主目录
    build.target.dir=target
    #构建源编译目录
    build.target.bin.dir=${build.target.dir}/bin
    #构建测试编译目录
    build.target.test.bin.dir=${build.target.dir}/test-bin
    #Junit 测试记录
    build.target.junit.record.dir=${build.target.dir}/test-record
    
    clover.db.dir=${build.target.dir}/clover-db
    
    #测试报告
    build.target.test.report.dir=${build.target.dir}/output
    junit.test.report.dir=${build.target.test.report.dir}/junit.report
    checkstyle.report.dir=${build.target.test.report.dir}/checkstyle.report
    clover.report.dir=${build.target.test.report.dir}/clover.report
    
    #构建目标包名称
    build.target.assembly.name=common-web-demo


    3)build.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project name="common-web-demo" default="build" basedir=".">
     <description>common project automatic build script</description>
    
     <!--加载资源文件 -->
     <property file="build.properties" />
    
     <!-- 源目录classpath-->
     <path id="main.classpath">
      <fileset dir="${dependency.lib.main.path}" includes="**/*.jar" />
     </path>
    
     <!--测试目录classpath -->
     <path id="test.classpath">
      <fileset dir="${dependency.lib.main.path}" includes="**/*.jar" />
      <fileset dir="${dependency.lib.test.path}" includes="**/*.jar" />
      <fileset dir="${dependency.lib.tool.path}" includes="**/*.jar" />
     </path>
    
     <!--工具包classpath -->
     <path id="tool.classpath">
      <fileset dir="${dependency.lib.tool.path}" includes="**/*.jar" />
     </path>
    
     <!--工具包任务定义 -->
     <taskdef resource="net/sf/antcontrib/antlib.xml">
      <classpath refid="tool.classpath" />
     </taskdef>
    
     <target name="clean">
      <delete dir="${build.target.dir}" />
      <mkdir dir="${build.target.dir}" />
     </target>
    
     <!-- 主目录编译清理  -->
     <target name="main.clean">
      <delete dir="${build.target.bin.dir}" />
      <mkdir dir="${build.target.bin.dir}" />
     </target>
    
     <!-- 主目录编译  -->
     <target name="main.compile" depends="main.clean">
      <javac srcdir="${src.main.java}" destdir="${build.target.bin.dir}" source="1.5" target="1.5" debug="on">
       <classpath refid="main.classpath" />
      </javac>
      <copydir src="${src.main.resources}" dest="${build.target.bin.dir}" />
     </target>
    
     <!--主目录打包 -->
     <target name="package.main.jar" depends="main.compile">
      <jar destfile="${build.target.dir}/${build.target.assembly.name}.jar">
       <fileset dir="${build.target.bin.dir}" />
      </jar>
     </target>
    
     <!-- === 循环构建 === -->
     <target name="build" depends="clean,package.main.jar">
    
     </target>
    
     <!-- 测试目录清理 -->
     <target name="test.clean">
      <delete dir="${build.target.test.bin.dir}" />
      <mkdir dir="${build.target.test.bin.dir}" />
     </target>
    
     <!-- 测试编译 -->
     <target name="test.compile" depends="test.clean">
      <javac srcdir="${src.main.java}" destdir="${build.target.test.bin.dir}" source="1.5" target="1.5" debug="on">
       <classpath refid="test.classpath" />
      </javac>
      <copydir src="${src.main.resources}" dest="${build.target.test.bin.dir}" />
      <javac srcdir="${src.test.java}" destdir="${build.target.test.bin.dir}" source="1.5" target="1.5" debug="on">
       <classpath refid="test.classpath" />
      </javac>
      <copydir src="${src.test.resources}" dest="${build.target.test.bin.dir}" />
     </target>
    
     <target name="test.record.clean">
      <delete dir="${build.target.junit.record.dir}" />
      <mkdir dir="${build.target.junit.record.dir}" />
     </target>
    
     <path id="test.run.classpath">
      <path refid="test.classpath" />
      <pathelement path="${build.target.test.bin.dir}" />
     </path>
    
     <!-- 运行Junit测试 -->
     <target name="junit.test" depends="test.compile,test.record.clean">
      <junit printsummary="true" fork="true" showoutput="true" dir="${basedir}">
       <classpath>
        <path refid="test.run.classpath">
        </path>
       </classpath>
       <formatter type="xml" />
       <batchtest fork="true" todir="${build.target.junit.record.dir}">
        <fileset dir="${build.target.test.bin.dir}">
         <include name="org/wit/ff/dao/*Test.class" />
        </fileset>
       </batchtest>
      </junit>
     </target>
    
     <!--生成测试报告目录 -->
     <target name="report.clean">
      <delete dir="${build.target.test.report.dir}" />
      <mkdir dir="${build.target.test.report.dir}" />
     </target>
    
     <!-- 生成Junit报告目录 -->
     <target name="junit.report.clean">
      <delete dir="${junit.test.report.dir}" />
      <mkdir dir="${junit.test.report.dir}" />
     </target>
    
     <!-- Junit 测试报告 -->
     <target name="junit.report" depends="junit.report.clean,junit.test">
      <junitreport todir="${build.target.junit.record.dir}">
       <fileset dir="${build.target.junit.record.dir}" includes="**/TEST-*.xml" />
       <report todir="${junit.test.report.dir}" />
      </junitreport>
     </target>
    
     <!-- checkstyle报告 -->
     <target name="checkstyle.report.clean">
      <delete dir="${checkstyle.report.dir}" />
      <mkdir dir="${checkstyle.report.dir}" />
     </target>
    
     <target name="checkstyle.report" depends="checkstyle.report.clean">
      <taskdef resource="checkstyletask.properties" classpath="${dependency.lib.tool.path}/checkstyle-5.3-all.jar" />
      <checkstyle config="${dependency.resource.path}/test_ii_checks.xml" failureProperty="checkstyle.failure" failOnViolation="false">
       <formatter type="xml" tofile="${checkstyle.report.dir}/checkstyle.xml" />
       <fileset dir="${src.main.java}" includes="**/*.java" />
       <fileset dir="${src.test.java}" includes="**/*.java" />
      </checkstyle>
      <style in="${checkstyle.report.dir}/checkstyle.xml" out="${checkstyle.report.dir}/checkstyle.html" style="${dependency.resource.path}/checkstyle.xsl" />
     </target>
    
     <!-- clover覆盖率报告 -->
     <taskdef resource="cloverlib.xml" classpath="${dependency.lib.tool.path}/clover.jar" />
     <taskdef resource="cloverjunitlib.xml" classpath="${dependency.lib.tool.path}/clover.jar" />
    
     <target name="clover.db.init" >
      <delete dir="${clover.db.dir}" />
      <mkdir dir="${clover.db.dir}" />
     </target>
    
     <!-- clover数据库,其实就是类执行信息存储单元 -->
     <target name="with.clover" depends="clover.db.init">
      <clover-setup initstring="${clover.db.dir}/coverage.db">
       <statementContext name="log" regexp="^log\..*" />
       <statementContext name="iflog" regexp="^if \(log\.is.*" />
       <methodcontext name="main" regexp="public static void main\(String\[\] args\).*" />
      </clover-setup>
     </target>
    
     <target name="clover.report.clean" >
      <delete dir="${clover.report.dir}" />
      <mkdir dir="${clover.report.dir}" />
     </target>
    
     <!-- 生成Clover测试报告-->
     <target name="clover.report" depends="clover.report.clean">
      <!-- 非常简洁的循环语句 -->
      <foreach target="clover.single.report" param="prop" list="html;xml" delimiter=";" />
     </target>
    
     <!-- 类型参数化的clover 报告 -->
     <target name="clover.single.report">
      <clover-report initstring="${clover.db.dir}/coverage.db">
       <current outfile="${clover.report.dir}/clover.${prop}" title="${build.target.assembly.name}" summary="true">
        <format type="${prop}" filter="log,iflog,main" />
        <fileset dir="${src.main.java}">
         <exclude name="org/wit/ff/dao/Base*.java" />
         <exclude name="org/wit/ff/dao/impl/Base*.java" />
         <exclude name="org/wit/ff/model/*Entity.java" />
         <exclude name="org/wit/ff/dao/*DAO.java" />
        </fileset>
        <fileset dir="${src.test.java}">
         <exclude name="org/wit/ff/test/Base*.java" />
        </fileset>
       </current>
      </clover-report>
     </target>
    
     <!-- html 格式clover 报告 -->
     <target name="clover.html">
      <clover-html-report outdir="clover_html" title="${build.target.assembly.name}" />
     </target>
    
     <!-- xml 格式clover 报告 -->
     <target name="clover.xml">
      <clover-report>
       <current outfile="coverage.xml">
        <format type="xml" />
       </current>
      </clover-report>
     </target>
    
     <!-- 运行所有目标-->
     <target name="test" depends="with.clover,build,junit.report,checkstyle.report,clover.report" />
    
    </project>

    结束语:

      关于Ant依赖的思考

      通常情况下,当我们使用Ant构建项目的时候,我们需要依赖某个目录下的包来完成构建,对于很多很多个使用Ant脚本构建的项目而言,可能整个依赖包的目录会非常的庞大,虽然我们可以依据模块将依赖分为多个子目录,但是在实际的运行构建中,通常还是依赖了多余的jar,甚至有时候依赖了很多不需要的jar。实际上要解决这个问题,我们可以使用原生的Ant精确定义classpath,但是如果依赖过多,ant文件太过庞大,远不如**/*.jar好管理,很多情况下,模块的划分也是为了更简化classpath的定义。但是这些其实远远不够,因为项目之间的依赖,如果也是用Ant来解决的话看起来会有些复杂了,也难以控制,因为你不得不修改脚本或者修改properties文件。并且会产生很多人为因素的问题,比如依赖包需要同步时还得拷贝。
      关于Ant的精确依赖,其实有一种方法。eg:首先将依赖配置到一个properties文件,然后通过Ant引入;其次使用antcotrib的定义任务for将所有的依赖拷贝到本地或者本项目的目录之下(hudson在构建Ant项目的时候就是采取这种策略)。最后按照普通Ant脚本编写就可以了。
      虽说如此,但Ant的依赖控制难以精确的确是事实,并且它作为一个自定义程序化构建的工具,还是需要不少的脚本开发工作。虽然Ivy(Spring源码也采用这种构建方式)的出现也能对依赖进行很好的管理,但是一个更有力的工具,Maven显然更具竞争力。

  • 相关阅读:
    Ghost Button制作教程及设计趋势分析
    onhashchange事件--司徒正美
    window.location.hash属性介绍
    优质UI的7条准则(一)
    当在浏览器地址栏输入一个网址的时候,究竟发生了什么?
    全球最快的JS模板引擎
    眨眼登录表单
    DIV+CSS规范命名
    es6--export,import
    es6--class
  • 原文地址:https://www.cnblogs.com/fangfan/p/2834606.html
Copyright © 2011-2022 走看看