NAnt入门手册
一、引言
在Visual Studio.NET中,我们只要用一个简单的菜单的命令,就可以构造和编译一个包含大量子项目(例如,相互关联的Web页面集合、执行文件、DLL程序集,等等)的.NET项目。但是,对于大型、复杂的软件项目来说,依靠某个程序员去点击“编译”按钮有时是行不通的。如果有人不乐意在每一台机器上安装 VS.NET该怎么办?要是能够自动执行软件的构造过程,永远不必有人去点击“编译”按钮,那该多好!让构造过程自动化的好处很多,不过要做到这一点,必须要有适当的构造工具。
构造工具解决的是软件编译过程相关的问题。小型开发组编写一些简单的软件可能用不上构造工具——你只要启动编译器,将代码编译成二进制执行文件,就算大功告成。
但是,现在的软件一般都是组件化的,依赖于一个或多个子项目;而这些子项目又可能由许多不同的人编写,他们随时可能签入(Check In)代码的另一个版本。只要有一个组件编译失败,或者在构造时使用了一个过时的组件,整个项目就会被拖累。因此,在构造复杂的项目时,人们往往用构造工具来解决多人协作开发中面临的问题。
编译器会针对代码中存在的问题发出错误警告,但是,如果项目包含了一些二进制执行模块以及多个相互紧密依赖的组件,要找出真正引发错误的根源就相当困难。最好有一个工具能够理清应用对外部模块的依赖关系,一旦发现问题就提出警告。
这个工具就是Ant。Ant原先由Apache Jakarta Project设计,解决了许多现有构造工具(例如Unix环境中常见的make工具)存在的不足,其中很重要的一点是,常规的构造工具总是限定于特定的操作系统、开发环境或开发语言,与此相对,Ant是平台中立的。为保持平台中立,用来告诉Ant如何编译项目的文件(构造文件)也是XML格式。这意味着使用Ant工具不局限于某一特定的平台(当然,开发平台——Java、.NET等,以及Ant本身除外)。
除了平台中立这一优点之外,Ant的另一个优点是它的构造文件是声明式的(而不是过程式的)。这意味着我们不必编写一行代码就可以完成大量工作——大多数看来繁重的任务,都可以通过在XML文件中加入声明的方式完成。(如果构造过程极其复杂,必须借助程序逻辑才能完成,Ant也提供这方面的支持,允许用户编写代码扩展这个工具)。另外,由于构造文件是XML格式的,我们可以用任何XML编辑工具创建和修改。
Ant为.NET实现的版本叫NAnt。NAnt本身用C#写成,但可以用于任何.NET语言(NAnt的发行包中就有好几个C#、VB.NET和 JScript.NET的范例),甚至一个项目中可以结合运用多种. NET语言,如果你要构造一个VB.NET客户端应用,即使它依赖于多个由VB.NET和C#编写的程序集,NAnt也能够轻而易举地完成。如果你觉得这还不够,NAnt还可以运行多个编译器,例如,如果你想同时使用Microsoft的工具和Mono C#编译器(一个在Linux上运行C#和.NET软件的工具,参见《在Linux上运行C#和.NET》),NAnt也照办不误。
要使用NAnt,最好能够了解一些.NET命令行编译器的知识。本文以C#的编译器csc为例说明,但是,你可以方便地改用vbc或其他编译器——或者,如果你乐意的话,同时使用多个编译器。
二、NAnt入门
要使用NAnt,第一步当然是从NAnt网站下载这个工具。正在用NAnt作为整合和构造工具的开发者注意一下,最新的NAnt集成了优秀的单元测试工具 NUnit 2.0。NUnit 2.0在1.0的基础上作了重大的改进,如果你使用NUnit 2,应该使用最新的NAnt以充分发挥它的优势。
下面我们通过一个最简单的例子看看NAnt的使用过程——构造一个由单个执行文件组成的C#控制台程序。应用程序的代码如下:
public class HelloWorld { static void Main() { System.Console.WriteLine("Hello world."); } }
当然,对于这样一个简单的项目,用C#命令行编译器也可以很方便地编译,只要执行一个“csc *.cs”命令就可以了。编译得到的结果是一个二进制可执行文件HelloWorld.exe。要用NAnt完成同样的任务,首先要创建一个扩展名为. build的XML构造文件,下面是一个NAnt构造文件的例子default.build,它完成的任务与执行一个简单的csc编译命令一样:
【Listing 1:创建单个执行文件的简单NAnt构造脚本】
<?xml version="1.0"?> <project name="Hello World" default="build" basedir="."> <target name="build"> <csc target="exe" output="HelloWorld.exe"> <sources> <includes name="HelloWorld.cs"/> </sources> </csc> </target> </project>
创建好构造文件之后,执行nant命令就可以构造项目。只要当前目录包含default.build文件,且NAnt本身的执行文件在操作系统的 PATH环境变量中,执行nant命令后,NAnt就会分析default.build文件,完成default.build文件中指定的任务。
当然,对于这样一个只有一个类的项目,使用NAnt之类的工具实在是牛刀杀鸡、大材小用了。但是,如果我们要先构造执行文件然后立即执行它,或者先构造一个或多个相关模块,然后构造主执行程序,又该如何?对于这样的任务,运用Ant之类的构造工具就能节省大量的时间。
NAnt构造文件主要由目标(target)、任务(task)、相关性(dependency)三部分内容构成。一个task就是要求NAnt执行的一个任务,举例来说,NAnt支持的任务包括运行编译器、复制/删除文件、发送email,甚至还能够压缩一组文件(关于NAnt支持的完整任务清单,请参见这里)。
目标描述了一组要求NAnt执行的任务,它是一种将任务分成逻辑组的手段。例如,假设我们要求NAnt删除bin目录的内容、编译5个执行文件、把编译得到的二进制文件复制到某个位置,可以把这些动作组织成一个target。
相关性可以看作是两个target之间的关系。不过Listing 1只有一个target,它的名称是build,它的任务是运行编译器编译指定的源文件。把标记的default属性设置为build,NAnt就会处理名称为build的target。
在csc任务内有一个子节点,它指定了要编译的源文件。
三、定义相关性
现在我们加入第二个target——编译好HelloWorld.exe文件后立即执行。修改后的构造文件如Listing 2所示。
【Listing 2:包含两个相关target的构造脚本】
<?xml version="1.0"?> <project name="Hello World" default="run" basedir="."> <target name="build"> <csc target="exe" output="HelloWorld.exe"> <sources> <includes name="HelloWorld.cs"/> </sources> </csc> </target> <target name="run" depends="build"> <exec program="HelloWorld.exe"/> </target> </project>
新添加的target名叫run,只包含一个用来执行程序的动作exec,此外它还有一个对build目标的相关性。这个相关性表示,在执行run这个 target之前,必须先实现build这个target且必须执行成功。注意在节点中,我们把default属性由原来的build改成了run。由于 run依赖于build,因此确保了在运行应用之前先编译好应用。
如果由于某种原因build目标没有达到(通常是由于编译器发现了代码存在的错误),run目标也不会执行。你可以试验一下:先在HelloWorld 的代码中故意加入一个语法错误,然后再次运行NAnt,NAnt将把编译器的错误信息显示到控制台,可以方便地看出哪里出现了错误。
四、从头开始构造
如果有一个编译好的二进制文件比源文件还新,NAnt不会再执行编译操作——换句话说,NAnt不会编译任何无需编译的文件。此外,如果构造文件定义了多重相关性(即,二个或二个以上的组件依赖于另一个组件),NAnt很“聪明”,它只构造被依赖的组件一次,不会重复构造同一个组件。这种处理方式大大提高了构造大型项目所需的时间,但有的时候,人们需要能够说“不管我有什么,你都编译不误”的权利,也就是说,要能够清除所有已经编译好的二进制文件,从头开始构造。
为此,许多构造文件会包含一个clean目标,开发者可以利用它来清除上一次编译留下的所有文件。下面是一个包含clean目标的构造文件例子:
【Listing 3:包含clean目标的构造脚本】
<?xml version="1.0"?><project name="Hello World" default="run" basedir="."> <target name="build"> <mkdir dir="bin" /> <csc target="exe" output="bin\HelloWorld.exe"> <sources> <includes name="HelloWorld.cs"/> </sources> </csc> </target> <target name="clean"> <delete dir="bin" failonerror="false"/> </target> <target name="run" depends="build"> <exec program="bin\HelloWorld.exe"/> </target></project>
clean目标并不是每次构造时都要运行,只是偶尔需要运行一下。要运行clean目标,只需执行nant clean命令即可。nant clean命令要求NAnt只执行clean目标(也就是说,不会执行构造项目的操作,只是清除一下bin目录的内容)。另外还可以看到,这个修改之后的构造脚本包含了一个mkdir动作,用来创建bin子目录以存放编译好的二进制文件。如果既要清除bin目录,又要构造项目,执行命令:nant clean build。
五、执行单元测试
如果要将构造过程和其他操作结合,例如 email提醒和自动化的单元测试,NAnt也能够很好地完成。详细讨论NUnit单元测试框架已经超出了本文的范围,不过NAnt与NUnit确实协作得很好。Listing 4就是这样一个构造文件的例子,它构造一个应用,并把执行NUnit也作为构造过程中很自然的一部分。
【Listing 4:集成了单元测试的构造文件】
<?xml version="1.0"?><project name="NUnit Integration" default="test"> <property name="build.dir" value="\dev\src\myproject\" /> <target name="build"> <csc target="library" output="account.dll"> <sources> <includes name="account.cs" /> </sources> </csc> </target> <target name="test" depends="build"> <csc target="library" output="account-test.dll"> <sources> <includes name="account-test.cs" /> </sources> <references> <includes name="nunit.framework.dll" /> <includes name="account.dll" /> </references> </csc> <nunit2> <test assemblyname="${build.dir}account-test.dll" /> </nunit2> </target></project>
构造文件首先以一个property标记的形式指定项目文件的位置。把一些可能改变或可能要再次使用的值放入属性变量很有用,但不是必需的;属性通常在构造文件的开头声明,但如有必要,也可以改为通过命令行参数提供。在这个例子中,以属性的形式指定项目文件带来不少方便,因为在后面的构造过程中我们要把这些信息传递给NUnit。
接下来,构造文件依次构造出account.dll组件和测试工具account- test.dll。这两个构造过程都包含target="library"选项,这是告诉编译器我们要构造的是一个组件程序集,而不是一个.exe文件。另外,从Listing 4还可以看出,测试工具还通过references节点引用了两个它依赖的程序集——被测试的业务逻辑组件account.dll和NUnit框架。当我们构造的项目依赖于外部库时,就要用到这个节点。
构造好测试工具和业务逻辑组件后,构造脚本调用NUnit,并指定了包含测试组件的程序集的名称,要求生成一个XML格式的文件记录测试结果。
关于NUnit集成,有一点必须注意:如果你正在用NUnit 2.0,必须使用最新的NAnt版本,这是因为NUnit最近作了重大的修改,某些“稳定”的NAnt根本不能与NUnit 2.0一起运行,但最新的NAnt对NUnit 2.0的支持相当稳定。
希望本文介绍的NAnt知识对你有用。要想了解更多有关NAnt功能的信息,请参见NAnt的文档,特别是task reference,其中包括一个NAnt能够完成哪些任务的简明清单。
nant的使用(一)-安装
NAnt 是一个基于 .NET 的生成工具,与当前版本的 Visual Studio .NET 不同,它使得为您的项目创建生成过程变得非常容易。当您拥有大量从事单个项目的开发人员时,您不能依赖于从单个用户的座位进行生成。您也不希望必须定期手动生成该项目。您更愿意创建每天晚上运行的自动生成过程。NAnt 使您可以生成解决方案、复制文件、运行 NUnit 测试、发送电子邮件,等等。遗憾的是,NAnt 缺少漂亮的图形界面,但它的确具有可以指定应该在生成过程中完成哪些任务的控制台应用程序和 XML 文件。注意,MSBuild(属于 Visual Studio 2005 的新的生成平台)为每种健壮的生成方案进行了准备,并且由基于 XML 的项目文件以类似的方式驱动。
1、系统需求
使用Nant,需要具备以下一种CRL:
· Microsoft .NET Framework 1.0
· Microsoft .NET Framework 1.1
· Microsoft .NET Framework 2.0 Beta 1
· Mono 1.0.x
依赖的库文件
Nant使用了许多开源的第三方组件库,nant的最近版本中包含了这些组件,在安装nant时不需要做额外的工作。有关这些组件的更多信息请参考以下链接:
NUnit - Required for unit testing
NDoc - Required for documentation generation
SharpZipLib - Required for the zip and unzip tasks
2、安装
无论是源代码还是编译好的二进制文件,Nant都是可以使用的。编译好的二进制文件是建立工程所需要的,包括构建tasks, types 和 functions。
从二进制文件安装:
1) 下载nant-bin.zip 或nant-bin.tar.gz
2) 从机器上删除以前的旧版本
3) 解压缩下载的压缩文件到你期望安装NAnt的地方
4) 根据你的机器环境,构建一个脚本文件来运行Nant:
Ø 在.NET下运行Nant:
新建一个批处理文件,如nant.bat,在里面加入如下内容,注意把红色部分换成自己机器上的Nant的安装路径,然后运行这个批处理文件即可:
@echo off
"C:\Program Files\NAnt\bin\NAnt.exe" %*
Ø 在Mono下运行Nant:
Windows环境:同.NET
Linux / Cygwin:在你的文件系统的适当位置创建一个名称为nant的文件(例如/usr/local/bin),在文件中加入以下内容:
#!/bin/sh
exec mono /usr/local/nant/bin/NAnt.exe "$@"
确保nant有运行的权限,如:chmod a+x /usr/local/bin/nant
5) 打开命令行窗口,把目录切换到装有nant的文件夹,执行nant –help,如果安装正确,你就会看到以命令行选项显示的使用信息。
6) (选做)下载、安装NAnt-contrib或其他第三方扩展程序。
从源代码进行安装:
1) 下载nant-src.zip或nant-src.tar.gz
2) 从机器上删除以前的旧版本
3) 解压缩下载的压缩文件到你期望安装NAnt的地方
4) 打开命令行提示符窗口,把目录切换到你把文件解压缩到的地方
5) 根据你的机器环境,编译Nant:
Ø 在.NET下:
n GNU Make
make install MONO= MCS=csc prefix=installation-path
例如: make install MONO= MCS=csc prefix="C:\Program Files"
n NMake
nmake -f Makefile.nmake install prefix=installation-path
例如: nmake -f Makefile.nmake install prefix="C:\Program Files"
Ø 在Mono下:
§ GNU Make
make install prefix=installation-path
eg. make install prefix="C:\Program Files"
§ NMake
nmake -f Makefile.nmake install MONO=mono CSC=mcs prefix=installation-path
eg. nmake -f Makefile.nmake install MONO=mono CSC=mcs prefix=/usr/local/
这会生成一个bootstrap版本的nant,然后使用它生成、安装full版本的nant:installation-path/NAnt
6) 打开命令行窗口,把目录切换到装有nant的文件夹,执行nant –help,如果安装正确,你就会看到以命令行选项显示的使用信息。
7) (选做)下载、安装NAnt-contrib或其他第三方扩展程序。
3、运行例子程序
打开命令行窗口,把目录切换到装有nant的文件夹,输入以下命令行并执行,注意红色部分换成自己的例子程序的路径:
Nant –buildfile:..\examples\examples.build
成功会显示编译信息。
nant的使用(二)-基本原理之运行nant
1、运行nant
一旦安装了Nant,运行是很简单的,仅仅是输入nant。输入nant –help能够得到用法信息。
1) 指定build文件
没有指定文件时,NAnt会在当前文件夹中寻找.build文件,例如NAnt.build。如果找到了,NAnt就把它作为build文件使用。如果找到了一个以上的文件,你需要使用-buildfile选项(用法见下面)指定一个文件。
如果你使用-find选项,NAnt会到父目录中寻找build文件,如此继续,直至到达根目录。使用命令行选项-buildfile:file可以使NAnt使用其它的build文件。
2) 指定对象
你可以指定一个或多个将要被执行的对象。当忽略时,<project>标签的default属性指定的对象将被使用。
如果工程有描述存在,Projecthelp将把它打印出来,紧跟在描述后面的是工程的对象列表。
3) 设置属性
使用-D:property=value撤销build文件中指定的属性,其中property是属性的名称,value是属性的值。
4) 例子
Nant:在当前目录中使用*.build结尾的xml文件运行nant,使用缺省对象。
NAnt -buildfile:..\ProjectName.build:使用父目录下的ProjectName.build文件,,使用缺省对象。
Nant clean:在当前目录使用build文件运行nant,使用名称为clean对象。
NAnt -D:debug=false clean dist:使用当前目录中缺省的build文件运行nant,
当设置debug为false的时候,先使用clean对象,然后使用dist对象。
nant的使用(二)-基本原理之Build文件
Build文件
Nant的Build文件是用xml写的。每一个文件包含一个工程(project)和几个对象(target),每一个对象包含几个任务(taek)。下面是一个编译HelloWorld(c#)工程的简单build文件。
<?xml version="1.0"?>
<project name="Hello World" default="build" basedir=".">
<description>The Hello World of build files.</description>
<property name="debug" value="true" overwrite="false" />
<target name="clean" description="remove all generated files">
<delete file="HelloWorld.exe" failonerror="false" />
<delete file="HelloWorld.pdb" failonerror="false" />
</target>
<target name="build" description="compiles the source code">
<csc target="exe" output="HelloWorld.exe" debug="${debug}">
<sources>
<includes name="HelloWorld.cs" />
</sources>
</csc>
</target>
</project>
在这个例子里面,有"clean" and "build". 两个对象。缺省时,“build”任务会被调用。
例子
在examples 文件夹内,你能找到运行这些粒子所需的文件。
nant:在debug模式(缺省)下运行nant并构建工程。
nant clean:运行nant并删除已编译好的文件。
nant -D:debug=false:在non-debug 模式下运行nant并生成工程。尽管build文件的debug 属性为真(true),命令行中设置的值不会受影响,就像 <property> 任务中的“overwrite”属性被设置为假(false)。
重要提示:如果产生的文件的日期比源文件的日期早,将仅仅执行像编译器任务这样的任务。如果你在debug模式下编译HelloWorld工程,然后什么东西也不清除,在no-debug模式下重新编译,这种情况就会发生,因为nant工程不需要被重新生成。
nant的使用(二)-基本原理之工程
Projects
一个工程有如下属性:
属性 描述 是否 必须
name 工程的名字 否
default 设置缺省对象,当命令行中没有指定对象时,缺省的对象将被执行 否
basedir 基本目录.从这个目录开始,计算所有的路径,如果不设置,build文件的父目录将被使用. 否
可以在顶层元素<description>中对工程加以描述,使用命令行-projecthelp 的时候将使用这个描述,可以参见build文件一节中的详细介绍.
可以在顶层元素<description>中对工程加以描述,使用命令行-projecthelp 的时候将使用这个描述,可以参见build文件一节中的详细介绍.
每一个工程定义了0个或多个对象,每个对象是将被执行的一组任务,当nant运行的时候,你可以指定想要执行的对象,没有对象指定时,默认对象将被执行;当两者都没有时,仅仅执行工程的global tasks.