NAnt NAnt 入门手册

liuxue.gu@hotmail.com · 2008年06月18日 · 3 次阅读

一、引言

在 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"?>

创建好构造文件之后,执行 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"?>

新添加的 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"?>

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"?>

构造文件首先以一个 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 是一个基于 .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

成功会显示编译信息。

1、运行 nant

一旦安装了 Nant,运行是很简单的,仅仅是输入 nant。输入 nant –help 能够得到用法信息。

1) 指定 build 文件

没有指定文件时,NAnt 会在当前文件夹中寻找.build 文件,例如 NAnt.build。如果找到了,NAnt 就把它作为 build 文件使用。如果找到了一个以上的文件,你需要使用-buildfile 选项(用法见下面)指定一个文件。

如果你使用-find 选项,NAnt 会到父目录中寻找 build 文件,如此继续,直至到达根目录。使用命令行选项-buildfile:file 可以使 NAnt 使用其它的 build 文件。

2) 指定对象

你可以指定一个或多个将要被执行的对象。当忽略时,标签的 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 对象。

Build 文件

Nant 的 Build 文件是用 xml 写的。每一个文件包含一个工程(project)和几个对象(target),每一个对象包含几个任务(taek)。下面是一个编译 HelloWorld(c#)工程的简单 build 文件。

<?xml version="1.0"?> The Hello World of build files.

在这个例子里面,有"clean" and "build". 两个对象。缺省时,“build” 任务会被调用。 例子

在 examples 文件夹内,你能找到运行这些粒子所需的文件。

nant:在 debug 模式(缺省)下运行 nant 并构建工程。

nant clean:运行 nant 并删除已编译好的文件。

nant -D:debug=false:在 non-debug 模式下运行 nant 并生成工程。尽管 build 文件的 debug 属性为真(true),命令行中设置的值不会受影响,就像 任务中的 “overwrite” 属性被设置为假(false)。

重要提示:如果产生的文件的日期比源文件的日期早,将仅仅执行像编译器任务这样的任务。如果你在 debug 模式下编译 HelloWorld 工程,然后什么东西也不清除,在 no-debug 模式下重新编译,这种情况就会发生,因为 nant 工程不需要被重新生成。

Projects

一个工程有如下属性:

属性 描述 是否 必须 name 工程的名字 否 default 设置缺省对象,当命令行中没有指定对象时,缺省的对象将被执行 否 basedir 基本目录.从这个目录开始,计算所有的路径,如果不设置,build 文件的父目录将被使用. 否

可以在顶层元素中对工程加以描述,使用命令行-projecthelp 的时候将使用这个描述,可以参见 build 文件一节中的详细介绍.

可以在顶层元素中对工程加以描述,使用命令行-projecthelp 的时候将使用这个描述,可以参见 build 文件一节中的详细介绍.

每一个工程定义了 0 个或多个对象,每个对象是将被执行的一组任务,当 nant 运行的时候,你可以指定想要执行的对象,没有对象指定时,默认对象将被执行;当两者都没有时,仅仅执行工程的 global tasks.

需要 登录 后方可回复。