appach ant简介

ant作为目前中国最受欢迎的开源构建工具,广泛应用于java工程构建。也许你会感到很疑惑,js作为动态语言,并不需要编译过程,为什么需要ant这样的构建工具呢?ant能够帮助前端工程师解决那些问题呢?

为什么前端工程师需要ant?

来看个经典的应用场景:
在你的页面中需要引用不少的jquery插件,比如plug-1.js、plug-2.js、plug-3.js,yslow关于前端性能优化的第一建议就是页面应该保持尽量少的js和css引用。明显这个场景违背了这个建议,所以我们需要合并这些js插件,合并成plug-combine.js文件,传统的做法是人肉合并,以前本教程作者也是这么干的,但遇到了如下烦恼,plug-1.js是自己写的插件,经常要更新这个文件的代码,每次更新完都要重新人肉更新下plug-combine.js,作为一个懒人,教程作者在想,“有没有一个工具可以帮我自动合并文件呢?当然合并后能够自动调用压缩工具压缩文件就更棒了!”。ant这个神奇的工具就是用来帮前端偷这样的懒的!

ant能帮前端干些什么呢?

  • 合并js/css文件;
  • 调用YUI Compressor自动压缩css文件,调用Google Closure Compiler自动压缩js;
  • 快速批量复制/删除指定文件;
  • 调用jsDoc工具,自动生成js文档;
  • 连接FTP,将代码快速发布到指定服务器;
  • 将文件自动上传到svn上;
  • 自动打包成zip文件
  • ...

ant强大得超乎你的想象,ant提供了一整套的任务列表帮你从重复耗时的构建流程中解脱出来!看到ant这只神奇的蚂蚁能做如此多的事,心动了吗?那么跟着教程作者来学学如何使用ant。
教程作者要强调的一点是ant很简单,只要你有足够的耐心,1天内就可基本掌握其使用方法。

Ant的安装和简单使用

1.下载ant

你下载下来的ant,应该包含如下文件:

2.安装java运行环境

ant是java程序,所以依赖于java运行环境,如果你的机子已经安装JDK,那么请跳过这一步,如果没有请先下载JDK,传送门在此
(PS:请运行“CMD”,然后键入“javac”,如果出现如下界面说明java环境OK。)

如果出现“’javac’ 不是内部或外部命令,也不是可运行的程序或批处理文件。”,那么请设置下java环境变量。

3.配置环境变量

右击“计算机”,点击“系统设置”,出现如下界面:

新建系统变量,变量名:ANT_HOME,变量值:d:\soft\ant,变量值指向你本机ant的解压目录,请勿直接copy这个变量值。

修改变量:path,在最后添加:%ANT_HOME%\bin;

4.试运行ant

在”CMD”界面,输入“ant”,如果出现以下内容,说明配置成功!

ant版的hello world!

在d盘新建个ant-demo的目录。
在该目录下新建个build.xml,代码如下:

  1. <?xml version="1.0"?>
  2. <project name="refund" default="build">
  3.     <target name="build">
  4.         <echo>Hello world!</echo>
  5.         <concat destfile="a_b.js">
  6.             <path path="a.js"/>
  7.             <path path="b.js"/>
  8.         </concat>
  9.     </target>
  10. </project>

再准备二个用于合并的js文件,比如a.js和b.js,(随便在二个文件中加些js代码)。
进入cmd界面,敲入ant,留意必须先将目录指向build.xml的根目录。

你将成功输出a_b.js文件,目录结构如下;

build.xml中的代码是什么意思呢?请看下文分解

构建文件入门

1.什么是构建文件?

构建文件是ant执行工程构建的入门文件,构建的所有任务都必须只能写在构建文件内,构建文件必须是符合标准的xml文件,默认的构建文件为build.xml,当你键入“ant”命名执行时,默认执行build.xml

标准构建文件模板:

  1. <?xml version="1.0"?>
  2. <project name="refund" default="build">
  3.     <target name="build">
  4.  
  5.     </target>
  6. </project>

关键点说明

  • 第一行为xml声明,必须存在;
  • 有且仅有一个project根标签;
  • default属性执行默认目标;
  • target(任务目标);
  • task:任务,是构建文件的最小执行单位,是具体干活的标签;
  • property:特性,或理解为属性。

2.新的demo代码

改变下上一篇的demo,增加点复杂点:创建一个src目录,将合并后的文件放在src目录下,并在合并后的文件内顶部添加一行自定义的合并注释。
新的demo中的build.xml:

  1. <?xml version="1.0"?>
  2. <project name="refund" default="build">
  3.     <property name="concat.note" value="//合并自a.js和b.js" />
  4.     <target name="build">
  5.         <echo>生成src目录</echo>
  6.         <mkdir dir="src"/>
  7.         <echo>开始合并文件</echo>
  8.         <concat destfile="src/a_b.js">
  9.             <header trimleading="yes">${concat.note}
  10.             </header>
  11.             <path path="a.js"/>
  12.             <path path="b.js"/>
  13.         </concat>
  14.     </target>
  15. </project>

在“CMD”界面运行“ant”命令,结果如下:(请确保你的demo中有a.js和b.js)

成功运行后你就会发现生成了一个src目录,目录下有个a_b.js文件,文件的第一行注释是//合并自a.js和b.js,完全符合我们构建要求!
这个demo的代码会比上一篇教程复杂了不少,教程作者来逐个说明下。

property标签

property标签为特性标签,你可以理解为编程语言中的属性或者变量,它起到的作用和变量是类似的,赋值后的property,可以在之后的目标任务内方便的引用。
property有二种数据元素:

  • 以名—值对出现的简单数据定义形式,比如demo中的
  • dataType复杂数据元素,比如fileset,日后会讲解到。

如何引用property呢?
非常简单,在你需要引用的地方使用${property名}即可,比如demo中的${concat.note}。
property的location属性
property除了value属性外,还有个location属性,起到的作用类似,区别是location属性用来保存路径,location带了路径转换功能,会将路径转成绝对路径,如果你的路径是/,它会自动转换成\,这个属性日后会经常用到。比如下面的代码:

  1. <property name="refund.dir" location="d:/ant-demo/"/>
  2.     <property name="refund.dir2" location="../"/>
  3.     <target name="build">
  4.         <echo>${refund.dir}</echo>
  5.         <echo>${refund.dir2}</echo>
  6.     </target>

看看输出结果,留意教程作者取的是代码片段,请根据实际情况做下修改。

target标签

target标签非常的重要,“目标”标签可以理解为一系列任务标签的容器,是对任务的隐式说明,构建文件允许出现多个target,教程作者的建议是使用更细的target,比如demo中的代码可以修改为:

  1. <?xml version="1.0"?>
  2. <project name="refund" default="build">
  3.     <property name="concat.note" value="//合并自a.js和b.js" />
  4.     <target name="build" depends="mkdir,concat">
  5.  
  6.     </target>
  7.     <target name="mkdir">
  8.         <echo>生成src目录</echo>
  9.         <mkdir dir="src"/>
  10.     </target>
  11.     <target name="concat">
  12.         <echo>开始合并文件</echo>
  13.         <concat destfile="src/a_b.js">
  14.             <header trimleading="yes">${concat.note}
  15.             </header>
  16.             <path path="a.js"/>
  17.             <path path="b.js"/>
  18.         </concat>
  19.     </target>
  20. </project>

这里教程作者将一个target拆成了多个target,留意属性depends。
depends用于处理目标依赖,比如这里“build”依赖于“mkdir”和“concat”目标,那么ant会优先执行“mkdir”和“concat”然后才执行“build”。这种依赖机制是ant非常重要的处理模式!体现出ant的灵活性!

task任务标签

任务是ant构建文件的最小构建块,是构建的实施者,demo中的echo、concat都是任务标签,那么ant能完成多少个任务呢,或者说ant有多少构建功能呢?请猛击这里查看,(有个task list)。看了这个任务清单,我想你可以体会到ant究竟有多强大!
教程作者不会对所有的任务标签进行讲解,只讲解前端常用的几个关键任务标签,下一篇教程将详细讲解concat、mkdir、copy等标签用法。

基础任务标签

前面二篇教程,主要讲解了ant的基础概念以及二个简单的实际demo,今天这篇文章,教程作者带大家认识下前端常用的几个ant常用任务标签。

1.concat:合并文件

concat标签非常常用,我们前端使用ant的一个核心任务,就是合并js/css文件以减少http请求。

1.1 concat的主要属性

属性

说明

destfile

合并后的文件的目标路径,包含文件名

overwrite

是否允许覆盖目标文件,默认是允许

outputencoding

输出的目标文件的编码

全部属性请看ant的英文文档。

1.2 header子标签

第二篇教程的demo,concat标签内有个header标签,这个标签是用来修改输出的目标文件的头部内容,比如你可以在目标前添加一行合并信息注释等,比如下面的代码:

  1. <concat destfile="src/a_b.js">
  2.             <header trimleading="yes">//合并自a.js和b.js
  3.             </header>
  4.             <path path="a.js"/>
  5.             <path path="b.js"/>
  6.         </concat>

header有个trimleading用于清理行空白的属性比较关键,其他属性教程作者也不太理解,旧不翻译了,有兴趣的看英文文档。

1.3 concat的一些demo

合并指定路径的文件:

  1. <concat destfile="src/a_b.js">
  2.             <path path="a.js"/>
  3.             <path path="b.js"/>
  4.         </concat>

(PS:path子标签的作用在于指定文件路径。)
合并特定的文件集合:

  1. <concat destfile="test.js">
  2.             <fileset dir="src" includes="*.js"/>
  3.         </concat>

合并src目录下的所有文件,fileset这个标签非常重要,在讲解dataType时会重点说明。
给目标文件增加文字:
利用header标签,demo已经贴出,不再重复贴出。

2.mkdir:创建一个新的目录

比较简单,只有一个属性dir,用来指定创建的目录路径,不只是名称哦。
贴个demo:

  1. <mkdir dir="${dist}/lib"/>

在${dist}(属性引用)下创建一个名为lib的目录。

3.copy:复制指定文件到指定位置

3.1.copy主要属性

属性

说明

file

必有属性,用于复制的源文件,除非存在fileset等dataType

tofile

将文件复制到该路径

todir

将文件复制到该目录

outputencoding

目标文件的编码

3.2.copy的demo

  1. <copy file="a.js" tofile="src/a.js"/>

将a.js文件复制到src目录下,这里你可以修改文件名哦。

  1. <copy file="a" todir="src"/>

将a.js文件复制到src目录下。

  1. <copy todir="build">
  2.     <fileset dir="src" excludes="**/*.css"/>
  3.   </copy>

将src的文件(排除css文件)复制到build目录下。excludes=”**/*.css”的含义在讲解fileset会说明。

4.delete:删除指定文件(目录)

4.1.delete的主要属性

属性

说明

file

删除的目标文件

dir

删除的目标目录

verbose

是否显示每个删除的目标文件名称

quiet

当设置为true,删除的文件或目录出现错误时不抛出任何异常,正常情况下会有删除失败说明

4.2.delete的demo

  1. <delete file="/lib/ant.jar"/>
  2. <delete dir="lib"/>

删除ant.jar文件和lib目录。

  1. <delete>
  2.     <fileset dir="." includes="**/*.bak"/>
  3.   </delete>

删除根目录下的所有后缀是.bak的文件。

5.echo:向ant控制台打印消息

echo在调试ant任务时非常有用,也可以在任务执行后打印一些任务完成提示消息等。
实际上echo也可以把消息打印到指定文件,形成build日志,只需要指定file参数即可。

属性

说明

message

想要打印的消息

file

消息打印到指定文件

append

将消息追加到已经存在的文件

level

指定消息的类型,有”error”, “warning”, “info”, “verbose”, “debug”

来看典型的echo任务代码:

  1. <echo message="Hello, world"/>
  2. <echo>Hello, world</echo>
  3. <dirname property="current.dir" file="./"/>
  4. <echo>${current.dir}</echo>

echo打印property属性值,是个非常常用的调试技巧。
上述代码中有个dirname,这个任务是用来做啥的呢?接下去看。

6.dirname:获取文件的目录路径

dirname只有二个参数:

属性

说明

file

文件路径

property

指定属性名称

来看下使用代码:

  1. <?xml version="1.0"?>
  2. <project name="test" default="build">
  3.     <target name="build">
  4.         <dirname property="antfile.dir" file="${ant.file}"/>
  5.         <property name="antfile2.dir" location="${ant.file}"/>
  6.         <echo message="${antfile.dir}"/>
  7.         <echo>${antfile2.dir}</echo>
  8.     </target>   
  9. </project>

ant.file这个属性是系统自带的,为当前运行的构建文件路径。
教程作者这里将dirname和property进行比较,二者的输出有明显的区别,dirname只输出目录,property输出完成文件路径,如下图

7.available:验证文件、目录、类的存在性

available任务在防止ant构建时因为文件或目录不存在导致的错误时非常有用。

属性

说明

property

属性名

value

属性值,默认为true

file

需要验证的文件

type

file的类型,验证目录(type=”dir”) or 验证文件 (type=”file”)

示例代码

  1. <?xml version="1.0"?>
  2. <project name="test" default="build">
  3.     <target name="build">
  4.         <dirname property="antfile.dir" file="${ant.file}"/>
  5.         <property name="ui" location="${antfile.dir}/ui.js"/>
  6.         <available file="${ui}" type="file" property="is"/>
  7.         <echo>${is}</echo>
  8.     </target>
  9.  
  10. </project>

available的value值永远为一个布尔值。

8.get:从url中获取文件

get用于快速将远程文件保存到指定位置,不止支持http:协议,ftp:,jar:也都是可以的。

属性

说明

src

源url

dest

目标路径

verbose

是否显示下载进度(100 Kb显示一个“.”)

usetimestamp

显示最后修改时间

username

‘BASIC’验证页面的用户名

password

‘BASIC’验证页面的密码

来看个demo代码:

  1. <?xml version="1.0"?>
  2. <project name="test" default="build">
  3.     <target name="build">
  4.         <dirname property="antfile.dir" file="${ant.file}"/>
  5.         <get src="http://www.haorooms.com/" dest="${antfile.dir}/36ria-index.html" verbose="true" usetimestamp="true"/>
  6.     </target>   
  7. </project>

运行后,如下图:

如果要把一批url保存下来该如何处理呢?

  1. <get dest="downloads">
  2.   <url url="http://ant.apache.org/index.html"/> 
  3.   <url url="http://ant.apache.org/faq.html"/>
  4. </get>

9.local:增加个局部属性

为了更好说明local用法,教程作者先贴出demo代码:

  1. <property name="foo" value="foo"/>
  2.     <target name="step1">
  3.         <echo>Before local: foo is ${foo}</echo>
  4.         <local name="foo"/>
  5.         <property name="foo" value="bar"/>
  6.         <echo>After local: foo is ${foo}</echo>
  7.     </target>
  8.  
  9.     <target name="step2" depends="step1">
  10.         <echo>In step2: foo is ${foo}</echo>
  11.     </target>

上述代码会输出如下内容:

  1. step1:
  2.      [echo] Before local: foo is foo
  3.      [echo] After local: foo is bar
  4.  
  5. step2:
  6.      [echo] In step2: foo is foo

在step1目标中我们定义了,类似编程语言中的局部变量,改变后foo的输出值为bar,不再是原来定义的foo,有意思的是原来的 值不会发生改变,step2目标验证了这一点。也就是说在step1内部定义了local 后,foo这个property的修改不会影响外部property的值。
local这个任务在sequential(循环)中非常常用,讲到sequential时再演示其用法。

10.touch:创建文件,并可以修改文件修改时间

属性

说明

file

文件名称

datetime

指定文件修改时间

  1. <touch file="myfile"/>

创建一个myfile文件,最后修改时间为当前时间。

  1. <touch file="myfile" datetime="18/10/2010 2:02 pm"/>

创建一个myfile文件,最后修改时间为18/10/2010 2:02 pm。

property标签

property(属性)这个标签很常用,当你在build.xml中定义了property,任务可以随意的引用该property,比如以下代码:

  1. <property name="author" value="minghe"/>
  2.  
  3. <target name="test">
  4.  
  5.     <echo>${author}</echo>
  6.  
  7. </target>

当你要引入property,只要使用${property}语法即可。

properties文件的使用

考虑以下需求:build.xml需要提供给第三方使用,第三方本机的代码路径一般都不一样,如果让第三方自己去修改build.xml的路径配置(假如在你的build.xml中有类似 的定义),正好该用户不懂得ant,那么面对build.xml,他将无所适从。能不能将配置独立成一个文件,用户只要改那个配置文件即可呢?

ant的.properties文件恰恰解决这个需求。

在build.xml同级目录下建立个build.properties(文件名任意);

键入如下示例代码:

  1. assets.dir = d:\www\htdocs\ant-demo

打开你的build.xml,插入如下代码:

  1. <property file="build.properties"/>

property的file属性指向build.properties文件,那么ant在执行build.xml前会去找这个文件,build.properties内的属性会覆盖build.xml内的同名属性。

假设你的build.xml有如下property:

  1. <property name="assets.dir" location="c:\demo"/>

你可以试下${assets.dir},输出的将是build.properties文件内property值。

value属性和location属性的区别

这二个属性很容易混淆,不少朋友向教程作者询问,有了value,为什么还需要location呢?二者的作用不是一样的吗?

是的,value和location的功能是接近的,都是property的值,但有些小的区别,如果你的property存储的是路径,那么教程作者推荐使用location属性,location属性会对路径进行转换,永远返回绝对路径形式。来看实际代码:

  1. <dirname property="current.dir" file="${ant.file}"/>
  2. <property name="assets.dir" location="${current.dir}/2.0/"/>
  3. <property name="assets.dir2" value="${current.dir}/2.0/"/>
  4. <target name="location">
  5.         <echo>location:${assets.dir}</echo>
  6.         <echo>value:${assets.dir2}</echo>
  7. </target>

输出的结果如下:

留意标黄部分的差异,当你使用location属性时,值内所有的“/”都会更换为“\”(即绝对路径的形式),而且还会去掉路径中多余的“/”。所以当你使用location属性存储路径信息时更为安全。

dataType的用法

这是ant理论部分的最后一章,也是非常重要的一章,教程作者将介绍ant中常用的几种dataType的用法。
dataType可以理解为复杂数据集合,比如特定规则的文件列表集合。

ant的所有dataType标签

  • fileset:定义一个文件集合列表,这些文件必须确实存在
  • filelist:定义一个文件集合列表,这些文件无需确实存在
  • path:以某种在不同操作系统间可移植的方式指定路径
  • patternset:将一组模式分组在一起
  • argument:对于由一个ant构建文件调用的程序,向其传递命令行参数(标签名是arg)
  • filterlist:将一组过滤器分组在一起
  • mapper:定义一组输入文件和一组输出文件间的复杂关系
  • environment:于由一个ant构建文件调用的程序,向其传递环境变量

必须掌握的是:filesetfilelistpathargumentpatternset

fileset

fileset和filelist(下面会讲到),功能上非常相近,都是用于获取一组文件列表,二者的不同之处在于fileset内的文件必须存在,filelist则没这要求;fileset的属性也比filelist更为的丰富,所以功能上更为的强大,一般使用fileset来获取文件。

属性

说明

dir

fileset的基目录

dir

fileset的基目录

casesensitive

如果为false,那么匹配文件名时不区分大小写,默认为true

defaultexcludes

是否使用默认的排除模式,默认为true。默认的排除模式会把一些版本控制的文件夹或文件排除

excludes

排除指定的文件列表(用逗号隔开)

excludesfile

排除指定的文件

includes

需要包含的文件列表(用逗号隔开)

includesfile

包含指定的文件

接下来来看几个demo,假设demo目录如图所示:

任务1:合并uploader目录下的所有文件
  1. <?xml version="1.0"?>
  2. <project name="uploader" default="build-all">
  3.     <!--基目录-->
  4.     <dirname property="current.dir" file="${ant.file.uploader}"/>
  5.     <property name="uploader.dir" location="${current.dir}/uploader" />
  6.     <target name="build-all">
  7.         <echo>dir:${current.dir}</echo>
  8.         <concat destfile="uploader.js" encoding="utf8" outputencoding="utf8">
  9.             <fileset dir="${uploader.dir}" includes="**/*.js" />
  10.         </concat>
  11.     </target>
  12. </project>

concat(合并)这个任务标签,教程作者之前有讲过了,与之前写的demo不同的是,concat内增加了fileset。由于我们的需求是合并uploader目录下的所有js,所以uploader下文件不少,明显不可能单独一个个写path,这时候就需要用到fileset,获取文件列表集合。

  1. <fileset dir="${uploader.dir}" includes="**/*.js" />

fileset会获取${uploader.dir}(该属性引用指向uploader目录)下所有的js,通过includes=”**/*.js”包含所有js。
也许你会对**/*感到疑惑,教程作者下面做个说明。

包含和排除模式语法:
  • *:匹配0或多个字符,比如*.js匹配minghe.js、taobao.js但不匹配minghe.css
  • ?:匹配1个字符,比如mingh?.js,匹配minghe.js、minghg.js,但不匹配minghe36.js
  • **:匹配0或多个目录,比如/minghe/**,匹配/minghe/下的任意目录,**/*.js匹配任意目录下的所有js文件
使用patternset标签

除了使用属性外,fileset还支持0到n个嵌套patternset元素,比如excludeinclude等,关于patternset会在下面讲解到,这里先贴出修改后的demo。

  1. <target name="build-all2">
  2.         <echo>build-all2</echo>
  3.         <concat destfile="uploader2.js" encoding="utf8" outputencoding="utf8">
  4.             <fileset dir="${uploader.dir}" >
  5.                 <include name="**/*.js" />
  6.                 <exclude name="demo/*.js" />
  7.             </fileset>
  8.         </concat>
  9.     </target>

这里的demo代码,与上一个demo相比,增加了一个排除文件模式,demo目录下的js文件,显然我们不需要,可以通过exclude标签予以排除

filelist

filelist通常与dependset任务一同使用,dependset任务用于将一个或多个源文件和1个或多个目标文件相比较,如果源文件存在更新或者删除,那么所有目标文件将将被删除。dependset用于确保文件的依赖性。

filelist的属性

属性

说明

dir

fileset的基目录

files

用逗号隔开的文件列表

refid

对某处定义的一个filelist的引用,这对于希望多次复用一个定义的文件列表,非常有用。

filelist的使用率不高,教程作者这里偷个懒,不再制作个独立的demo。

path

path在java打包编译中常用到,可以以属性(多个路径用冒号或分号隔开)或嵌套元素的方式。
path用于转换路径为当前用户操作系统可兼容的路径。(path dataType并不只有 path元素,还有classpath等形式标签)。

当path以标签的形式出现时,拥有以下属性:

属性

说明

location

表示一个文件或目录

path

一个文件和路径名列表,以冒号或分号隔开

refid

定义path的一个引用

patternset

patternset与fileset是紧密结合的,常作为fileset的嵌套元素出现,比如includeexclude标签。

  1. <target name="build-all3">
  2.         <echo>build-all2</echo>
  3.         <concat destfile="uploader2.js" encoding="utf8" outputencoding="utf8">
  4.             <fileset dir="${uploader.dir}" >
  5.                 <include name="**/*.js" if="test" />
  6.                 <exclude name="demo/*.js" unless="demo" />
  7.             </fileset>
  8.         </concat>
  9.     </target>

留意代码中的if和unless属性,if=”test”可以理解为如果存在test属性,就读取文件,unless=”demo”可以理解为只有未设置demo属性时才读取文件。

argument

arg标签在apply、exec、java标签内嵌套,用于传递命名行参数。
先贴出日后会说明的demo代码:

  1. <apply executable="java" verbose="true" dest="${combines.dir}" failonerror="true" parallel="false">
  2.             <fileset dir="${combines.dir}" includes="**/*.js"/>
  3.             <arg line="-jar"/>
  4.             <arg path="${current.dir}/closure-compiler/compiler.jar"/>
  5.             <arg line="--charset gbk"/>
  6.             <arg value="--warning_level"/>
  7.             <arg value="QUIET"/>
  8.             <arg value="--js"/>
  9.             <srcfile/>
  10.             <arg value="--js_output_file"/>
  11.             <targetfile/>
  12.             <mapper type="regexp" from="^(.*)\.source\.js$" to="\1-min.js"/>
  13. </apply>

上述代码是调用google压缩器压缩js源文件,apply嵌套多个arg标签,比如下面的代码:

  1. <arg path="${current.dir}/closure-compiler/compiler.jar"/>
  2.             <arg line="--charset gbk"/>
  3.             <arg value="--warning_level"/>
  4.             <arg value="QUIET"/>
  5.             <arg value="--js"/>

执行时会合并成一个命名行,类似–charset gbk –warning_level QUIET –js。

ant结合ant结合yui-compressor和closure-compiler和closure-compiler

接下来几篇教程作者将开始讲解ant的实战部分的教程。今天介绍是ant如何结合前端必备的yui-compressor或closure-compiler的用法。

ant与yui-compressor

为什么yui-compressor需要和ant结合使用?

yui-compressor已经批处理工具,右键可以压缩文件,已经颇为方便了,为什么还需要ant去调用yui-compressor呢?
思考以下场景:

  1. 你需要批量压缩10个或更多的js或css文件(一个一个人肉点右键?)
  2. 你需要经常维护一批js或css文件,每次发布都人肉右键?

作为典型的懒人,教程作者渴望有一种工具,运行一次帮我批量压缩文件,然后使用FTP发布到服务器上。ant就是这样的工具。

接下来看下如何使用ant调用yui-compressor

教程作者已经准备好demo文件(是教程作者正在写的异步文件上传组件),但是务必修改下build.xml文件中的yui-compressor和closure-compiler的路径。

定义属性:源码文件路径,css路径,yuicompressor.jar的路径

  1. <property name="uploader.dir" location="${current.dir}/../uploader/"/>
  2.     <!--css路径-->
  3.     <property name="css.path" location="${uploader.dir}/uploader.css"/>
  4.     <!--yuicompressor程序路径-->
  5.     <property name="yuicompressor.path" location="${current.dir}/yuicompressor/yuicompressor.jar"/>

任务1:压缩uploader/uploader.css到uploader-min.css

  1. <target name="minify-css">
  2.         <apply executable="java" verbose="true" dest="${uploader.dir}/../"  failonerror="true" parallel="false">
  3.             <fileset dir="${uploader.dir}" includes="uploader.css"/>
  4.             <arg line="-jar"/>
  5.             <arg path="${yuicompressor.path}"/>
  6.             <arg line="--charset gbk"/>
  7.             <arg value="--type"/>
  8.             <arg value="css"/>
  9.             <arg value="-o"/>
  10.             <targetfile/>
  11.             <mapper type="glob" from="*.css" to="*-min.css"/>
  12.         </apply>
  13.     </target>

minify-css任务执行yuicompressor.jar,apply标签(dataType),教程作者在上一篇教程中并没有讲到。apply用于执行java程序。留意dest属性,用于指定输出目录,是必须的。
子元素fileset用于提取源文件,这里只需要uploader/uploader.css这个文件。
arg标签上一篇教程教程作者已经讲过了,最后会合并成1行命名。

  • -jar:yuicompressor.jar路径
  • –charset:输出文件编码
  • –type:文件压缩类型,css或js
  • -o:输出路径

代码中的mapper标签(dataType)的用途是什么?
mapper定义了一组源文件和一组目标文件的关联方式。共有三个关键属性:

  • type:关联方式,值有glob、identity、flattern、merge、regexp
  • from:源文件
  • to:目标文件

关键看下type属性,当值为glob时,表示基于简单的通配模式(比如*、**)确定文件名。大多数情况glob属性已经够用了。
yuicompressor需要使用mapper来确定批量输出文件规则,教程作者的demo代码很简单,将*.css输出成*-min.css即可。
尝试执行ant minify-css,如果构建顺利的话如下图:

任务2:压缩uploader目录下的js文件为*-min.js

  1. <!--压缩js-->
  2.     <target name="minify-js">
  3.         <apply executable="java" dest="${uploader.dir}/../">
  4.             <fileset dir="${uploader.dir}" includes="*.js"/>
  5.             <arg line="-jar"/>
  6.             <arg path="${yuicompressor.path}"/>
  7.             <arg value="-o"/>
  8.             <targetfile/>
  9.             <mapper type="glob" from="*.js" to="*-min.js"/>
  10.         </apply>
  11.     </target>

如果执行顺利的话将输出以下文件:

ant与closure-compiler

ant执行closure-compiler与yui-compressor基本上一样。closure-compiler的js压缩比yui-compressor强悍,教程作者一般使用closure-compiler压缩js文件,使用yui-compressor压缩css文件。接下来来看下closure-compiler的调用语法。
任务:合并几个js文件,并压缩合并后的文件
合并uploader/base.js、uploader/render.sj、uploader/renderUploader.js为uploader.source.js,并压缩成uploader-min.js。

  1. <target name="closure" depends="concat,minify-js-closure">
  2.  
  3.   </target>
  4.     <!--合并js-->
  5.     <target name="concat">
  6.         <concat destfile="${uploader.dir}/../uploader.source.js">
  7.             <path path="${uploader.dir}/base.js"/>
  8.             <path path="${uploader.dir}/render.js"/>
  9.             <path path="${uploader.dir}/renderUploader.js"/>
  10.         </concat>
  11.     </target>
  12.     <!--使用closure压缩js-->
  13.     <target name="minify-js-closure">
  14.         <apply executable="java" verbose="true" dest="${uploader.dir}/../" failonerror="true" parallel="false">
  15.             <fileset dir="${uploader.dir}/../" includes="**/*.js"/>
  16.             <arg line="-jar"/>
  17.             <arg path="${closure.path}"/>
  18.             <arg line="--charset gbk"/>
  19.             <arg value="--warning_level"/>
  20.             <arg value="QUIET"/>
  21.             <arg value="--js"/>
  22.             <srcfile/>
  23.             <arg value="--js_output_file"/>
  24.             <targetfile/>
  25.             <mapper type="regexp" from="^(.*)\.source\.js$" to="\1-min.js"/>
  26.         </apply>
  27.     </target>

关于concat任务标签,之前的教程已经讲过乐,教程作者不再累述。重点看下minify-js-closure这个任务。
closure-compiler的调用参数与yui-compressor有一些不同,输出不是使用-o参数,而是使用–js_output_file。
教程作者这里也演示了mapper标签中type=”regexp”的情况,使用正则规则来匹配源文件和目标问题。
(PS:补充个正则知识,(.*)为建立分组捕获,to=”\1-min.js”中的\1,使用该分组。)

>ant结合jsdoc构建js文档

上一篇教程教程作者演示了如何使用ant自动压缩文件,今天讲解ant如何ant结合jsdoc构建js文档。
JsDoc Toolkit 是一个将抽离js代码中的注释形成文档的程序,利用ant,你可以自动化打包js文档,需要用到的工具是jsdoc-toolkit-ant-task。调用的过程其实非常简单,整个过程类似ant调用yui-compressor。
下面的代码中,教程作者通过构建kissy-form库(教程作者和其他同事一起写的form组件集,目前还在编码阶段)的文档,演示jsdoc的用法。

1.准备

假设你已经下载了jsdoc-toolkit-ant-task,且保证你的js代码风格符合jsdoc要求,比如下面的代码:

  1. var MyClass = Class.create( 
  2. /** @lends MyClass# */ 
  3. { 
  4.     /**
  5.      * Description of constructor.
  6.      * @class Description of class.
  7.      * @constructs
  8.      */ 
  9.     initialize : function(arg0, arg1) { 
  10.         //... 
  11.     }
  12.     /** A method. */ 
  13.     myFunc : function() { 
  14.     }
  15.     /** An instance field. */ 
  16.     myVar : 123 
  17. })
  18.  
  19. // ... and if you want to add class fields ... 
  20.  
  21. Object.extend(MyClass
  22. /** @lends MyClass */ 
  23. { 
  24.     /** A class method. */ 
  25.     classFunc : function() { 
  26.     } 
  27. });

详尽的jsdoc语法说明,请看jsdoc的wiki

2.定义property

  1. <!--jsdoc-toolkit所在目录-->
  2.     <property name="jsdoc.dir" location="jsdoc-toolkit/" />
  3.     <!--源代码目录-->
  4.     <property name="src.dir" location="../src/" />
  5.     <!--文档输出目录-->
  6.     <property name="output.dir" location="../doc/" />

请根据你本机的情况自行修改目录。

3.调用jsdoc

  1. <target name="render">
  2.         <taskdef name="jsdoctoolkit" classname="uk.co.darrenhurley.ant.tasks.JsDocToolkit" classpath="${jsdoc.dir}/jsdoc-toolkit-ant-task-1.1.2.jar;${jsdoc.dir}/java/classes/js.jar"></taskdef>
  3.         <jsdoctoolkit template="jsdoc" jsdochome="${jsdoc.dir}/" outputdir="${output.dir}/" inputdir="${src.dir}" encoding="utf-8">
  4.  
  5.         </jsdoctoolkit>
  6.     </target>

target目标下有二个任务:taskdef定义jsdockit的程序路径,jsdoctoolkit执行真正的构建任务。
接下来教程作者简单说明下其关键属性。

属性

说明

template

文档模板

outputdir

文档输出路径

inputdir

js源码目录

encoding

输出文档页面编码,默认是utf-8

depth

程序遍历源码目录的深度,默认是10

完成的构建代码如下:

  1. <?xml version="1.0"?>
  2. <!--
  3. haorooms博客
  4. -->
  5. <project name="doc" default="render" basedir=".">
  6.     <!--jsdoc-toolkit所在目录-->
  7.     <property name="jsdoc.dir" location="jsdoc-toolkit/" />
  8.     <!--源代码目录-->
  9.     <property name="src.dir" location="../src/" />
  10.     <!--文档输出目录-->
  11.     <property name="output.dir" location="../doc/" />
  12.     <target name="render">
  13.         <taskdef name="jsdoctoolkit" classname="uk.co.darrenhurley.ant.tasks.JsDocToolkit" classpath="${jsdoc.dir}/jsdoc-toolkit-ant-task-1.1.2.jar;${jsdoc.dir}/java/classes/js.jar"></taskdef>
  14.         <jsdoctoolkit template="jsdoc" jsdochome="${jsdoc.dir}/" outputdir="${output.dir}/" inputdir="${src.dir}" encoding="utf-8">
  15.  
  16.         </jsdoctoolkit>
  17.     </target>
  18. </project>

下面是教程作者构建成功后目录情况:

有机会再给大家讲解常用的jsdoc语法。

项目build实战

前面8篇ant教程,基本上没有太复杂的地方,在实际项目build中会遇到些复杂的场景。今天这篇教程,教程作者将通过一个真实的项目build过程,来综合演练下ant的用法,同时演示下如何处理ant中的循环任务。

项目简介

项目名:KF(KISSY-FORM)
用途:kissy的form组件包
源码地址:https://github.com/minghe/kissy-form
KF目前还在开发阶段,完成的组件只有Uploader(异步文件上传),日后会发布更多的form组件,有兴趣的朋友可以关注下。
Uploader场景足够复杂,足以覆盖ant的大部分常用用法,请先下载KF源码到本地,找到Uploader的build.xml文件(form/src/uploader/build.xml)。

build目标

build(构建后的打包发布文件)必须和src(源码目录)目录结构保持一致。

  • 合并src下的所有js为render-pkg.js(排除themes、demo、auth、tests目录下的文件)
  • 将render-pkg.js移动到build目录下,并改名为render.js
  • copy一份themes目录到build/form/uploader
  • 遍历themes下的每个目录,将子目录下的所有js合并成index-pkg.js
  • 删除除了index-pkg.js外的所有js文件
  • 将index-pkg.js改名为index.js
  • 压缩build目录下的所有js文件
  • copy一份uploader.swf文件到build目录下

任务是不是很多,也许你会觉得教程作者搞得这么复杂很蛋疼,实际上这些任务跟教程作者实际要实现的构建目标有关,不是本文重点,这里不讨论。
接下来来看教程作者是如何实现这些目标的。
由于需要用到第三方类库ant-contrib-1.0b3.jar,请先下载该文件到你的ant/lib目录下。

ant实战

合并src下的所有js为render-pkg.js(排除themes、demo、auth、tests目录下的文件)
  1. <target name="concat">
  2.         <concat destfile="render-pkg.js" encoding="${charset}" outputencoding="${charset}">
  3.             <fileset dir="${src.dir}" includes="**/*.js" excludes="tests/*.js,demo/**/*.js,auth/*.js,themes/**/*.js"/>
  4.             <filterchain>
  5.                 <deletecharacters chars="&#xFEFF;"/>
  6.             </filterchain>
  7.         </concat>
  8.     </target>
将render-pkg.js移动到build目录下,并改名为render.js
  1. <!--移动合并后源文件到build目录下-->
  2. <target name="move">
  3.     <move file="render-pkg.js" tofile="${uploader.build.dir}/render.js"/>
  4. </target>

改名操作不一定要用rename标签,使用move标签也可以间接实现。

copy一份themes目录到build/form/uploader,并删除目录下的html文件
  1. <!--移动模板的源码文件到build目录下-->
  2.     <target name="move-theme">
  3.         <copy todir="${uploader.build.dir}/themes" overwrite="true">
  4.             <fileset dir="${src.dir}/themes"/>
  5.         </copy>
  6.         <delete>
  7.             <fileset dir="${uploader.build.dir}/themes" includes="**/*.html"/>
  8.         </delete>
  9.     </target>

这部分的目标也非常简单,使用copy和delete即可实现。

遍历themes下的每个目录,将子目录下的所有js合并成index-pkg.js。删除除了index-pkg.js外的所有js文件。>将index-pkg.js改名为index.js

好,进入本文的重点内容,这个任务目标有个难点,就是如何在ant中处理循环?

如上图所示,build和src下的themes目录必须保持一致,而且必须遍历每个子目录合并该目录的js,所以需要用到循环。这时候你也许会想到编程语言中常用的for,ant有for标签吗?很影响ant并不是编程语言,没有直接的for标签,但通过第三方类库可以实现for循环,我们来看代码。
请确保你的本机上的ant下的lib目录下有ant-contrib-1.0b3.jar文件

增加新的命名空间
  1. <project name="uploader.build" default="build" basedir="." xmlns:ac="antlib:net.sf.antcontrib">
  2. </project>

给你的project增加ac命名空间:mlns:ac=”antlib:net.sf.antcontrib”

遍历themes下的每个目录
  1. <target name="build-theme" depends="move-theme">
  2.         <ac:for param="file">
  3.             <path>
  4.                 <fileset dir="${uploader.build.dir}/themes" includes="**/index.js"/>
  5.             </path>
  6.             <sequential>
  7.                 <ac:var name="var.dir" unset="true"/>
  8.                 <dirname property="var.dir" file="@{file}"/>
  9.                 <echo>concat ${var.dir}</echo>
  10.             </sequential>
  11.         </ac:for>
  12.     </target>

使用ac:for开启循环,param为参数名,供后面循环时更换文件路径时使用。

  1. <path>
  2.    <fileset dir="${uploader.build.dir}/themes" includes="**/index.js"/>
  3. </path>

定义文件路径。取每个子目录下的index.js。

  1. <sequential>
  2.                 <ac:var name="var.dir" unset="true"/>
  3.                 <dirname property="var.dir" file="@{file}"/>
  4.                 <echo>concat ${var.dir}</echo>
  5.             </sequential>

sequential标签,与for标签配合使用,标志循环开始。
ac:var定义var.dir变量(即子目录路径)可更改,留意ant定义的property是无法更改的。
dirname获取文件对应的子目录路径。
接下来具体执行任务:

  1. <concat destfile="${var.dir}/index-pkg.js" encoding="${charset}" outputencoding="${charset}">
  2.                     <fileset dir="${var.dir}" includes="*.js"/>
  3.                     <filterchain>
  4.                         <deletecharacters chars="&#xFEFF;"/>
  5.                     </filterchain>
  6.                 </concat>
  7.                 <delete>
  8.                     <fileset dir="${var.dir}/" includes="*.js" excludes="index-pkg.js"/>
  9.                 </delete>
  10.                 <rename src="${var.dir}/index-pkg.js" dest="${var.dir}/index.js"/>
压缩build目录下的所有js文件
  1. <target name="minify" depends="crlf">
  2.         <apply executable="java" verbose="true" dest="${uploader.build.dir}" failonerror="true" parallel="false">
  3.             <fileset dir="${uploader.build.dir}" includes="**/*.js"/>
  4.             <arg line="-jar"/>
  5.             <arg path="${compiler}"/>
  6.             <arg line="--charset utf8"/>
  7.             <arg value="--warning_level"/>
  8.             <arg value="QUIET"/>
  9.             <arg value="--js"/>
  10.             <srcfile/>
  11.             <arg value="--js_output_file"/>
  12.             <targetfile/>
  13.             <mapper type="regexp" from="^(.*)\.js$" to="\1-min.js"/>
  14.         </apply>
  15.     </target>

完整代码如下:

  1. <project name="uploader.build" default="build" basedir="." xmlns:ac="antlib:net.sf.antcontrib">
  2.     <description>Uploader Build File</description>
  3.  
  4.     <property name="charset" value="utf-8"/>
  5.     <property name="tool.dir" location="../../../tool"/>
  6.     <property name="compiler" location="${tool.dir}/closure-compiler/compiler.jar"/>
  7.     <property name="form.build.dir" location="../../../build/form"/>
  8.     <property name="src.dir" location="."/>
  9.     <property name="uploader.build.dir" location="${form.build.dir}/uploader"/>
  10.  
  11.     <!--合并源码文件,排除模板js-->
  12.     <target name="concat">
  13.         <concat destfile="render-pkg.js" encoding="${charset}" outputencoding="${charset}">
  14.             <fileset dir="${src.dir}" includes="**/*.js" excludes="tests/*.js,demo/**/*.js,auth/*.js,themes/**/*.js"/>
  15.             <filterchain>
  16.                 <deletecharacters chars="&#xFEFF;"/>
  17.             </filterchain>
  18.         </concat>
  19.     </target>
  20.     <!--移动合并后源文件到build目录下-->
  21.     <target name="move">
  22.         <move file="render-pkg.js" tofile="${uploader.build.dir}/render.js"/>
  23.     </target>
  24.     <!--删除build目录下的所有文件-->
  25.     <target name="clean-build">
  26.         <delete>
  27.             <fileset dir="${uploader.build.dir}" includes="**/*.js,**/*.css,**/*.swf"/>
  28.         </delete>
  29.     </target>
  30.     <!--移动uploader.swf-->
  31.     <target name="move-swf">
  32.         <copy file="plugins/ajbridge/uploader.swf" todir="${uploader.build.dir}/plugins/ajbridge"/>
  33.     </target>
  34.     <!--移动模板的源码文件到build目录下-->
  35.     <target name="move-theme">
  36.         <copy todir="${uploader.build.dir}/themes" overwrite="true">
  37.             <fileset dir="${src.dir}/themes"/>
  38.         </copy>
  39.         <delete>
  40.             <fileset dir="${uploader.build.dir}/themes" includes="**/*.html"/>
  41.         </delete>
  42.     </target>
  43.     <!--构建模板,合并模板下的js文件,生成index-pkg.js,删除源文件,将index-pkg.js改成index.js-->
  44.     <target name="build-theme" depends="move-theme">
  45.         <ac:for param="file">
  46.             <path>
  47.                 <fileset dir="${uploader.build.dir}/themes" includes="**/index.js"/>
  48.             </path>
  49.             <sequential>
  50.                 <ac:var name="var.dir" unset="true"/>
  51.                 <dirname property="var.dir" file="@{file}"/>
  52.                 <echo>concat ${var.dir}</echo>
  53.                 <concat destfile="${var.dir}/index-pkg.js" encoding="${charset}" outputencoding="${charset}">
  54.                     <fileset dir="${var.dir}" includes="*.js"/>
  55.                     <filterchain>
  56.                         <deletecharacters chars="&#xFEFF;"/>
  57.                     </filterchain>
  58.                 </concat>
  59.                 <delete>
  60.                     <fileset dir="${var.dir}/" includes="*.js" excludes="index-pkg.js"/>
  61.                 </delete>
  62.                 <rename src="${var.dir}/index-pkg.js" dest="${var.dir}/index.js"/>
  63.             </sequential>
  64.         </ac:for>
  65.     </target>
  66.     <!--压缩前去除页面多余空白-->
  67.     <target name="crlf">
  68.         <fixcrlf includes="*.js" srcdir="${uploader.build.dir}" encoding="utf8" eol="crlf"></fixcrlf>
  69.     </target>
  70.     <!--压缩脚本-->
  71.     <target name="minify" depends="crlf">
  72.         <apply executable="java" verbose="true" dest="${uploader.build.dir}" failonerror="true" parallel="false">
  73.             <fileset dir="${uploader.build.dir}" includes="**/*.js"/>
  74.             <arg line="-jar"/>
  75.             <arg path="${compiler}"/>
  76.             <arg line="--charset utf8"/>
  77.             <arg value="--warning_level"/>
  78.             <arg value="QUIET"/>
  79.             <arg value="--js"/>
  80.             <srcfile/>
  81.             <arg value="--js_output_file"/>
  82.             <targetfile/>
  83.             <mapper type="regexp" from="^(.*)\.js$" to="\1-min.js"/>
  84.         </apply>
  85.     </target>
  86.     <target name="build" depends="concat,clean-build,move,build-theme,minify,move-swf">
  87.     </target>
  88. </project>
目录

快捷键说明

m
拆合教程目录
—>
下一页
<—
上一页