在.NET 环境中实现每日构建 NAnt 篇 2007年02月07日星期三 11:49 前言
关于每日构建这个话题,也已经有很多很好的文章讨论了。本文的写作过程中也参考了这些文章。本文之所以继续这个题目,是因为在查阅了网上的资源后,发现没有一个比较通用的过程。所以本文就主要讨论了利用 NAnt 构建一个通用日编译的方案。利用这个方案,日编译的维护者可以不需要对每个要编译的方案都要做很多维护。只要定义一个属性文件就可以了。
关键词: Daily Build, NAnt
1.1. 每日构建的优点:
每日构建(Daily Build)也可称为持续集成(Continuous Integration),强调完全自动化的、可重复的创建过程,其中包括每天运行多次的自动化测试。每日构建的作用日益显得重要。它让开发者可以每天进行系统集成,从而减少了开发过程中的集成问题。
持续集成可以减少集成阶段"捉虫"消耗的时间,从而最终提高生产力。它使得绝大多数 bug 在引入的同一天就可以被发现。而且,由于一天之中发生变动的部分并不多,所以可以很快找到出错的位置。
1.2. 每日构建完成的任务
实现自动化每日构建需要做以下几部分的工作:
l 使创建过程完全自动化,让任何人都可以只输入一条命令就完成系统的创建。
l 使测试完全自动化,让任何人都可以只输入一条命令就运行一套完整的系统测试。
l 确保所有人都可以得到最新、最好的可执行文件。
在.NET 环境下建立每日构建可以使用一系列开源工具:
Nant: 完成代码的自动编译,自动运行测试工具。[url]http://nant.sourceforge.net/builds//url][
NantContrib:自动从源码库中获取源代码。[url]http://nantcontrib.sourceforge.net/nightly/builds//url][
NUnit2Report:将 NUnit 测试工具产生的 XML 报告转换为 HTML 报告形式。[url]http://NUnit2Report.sourceforge.net/url][
VSS:Visual Source Safe,微软源码管理工具
Draco.NET: 用于自动检测 VSS 中源代码变动情况,调用 Nant 完成自动编译
[url]http://sourceforge.net/projects/draconet//url][
下载所需的工具后,按照如下步骤进行安装:
在服务器上安装 VSS 源码管理工具
安装下载的 Draco Server 和 Draco Web,修改安装后的 Draco Web 目录下的 web.config 文件,设置正确的 Draco Server 安装路径
将 NAnt、NAntContrib、NUnit2Report 压缩包解压,将三个 Bin 目录中的内容复制到一个公用目录,比如 D:\DailyBuildTools,然后将该路径加入系统的 Path 路径列表中,具体为 “控制面板-〉系统属性-〉环境变量-〉Path”
NAnt 脚本实现了每日构建的主体功能,它具体分为下面几部分
l 定义每日构建所需的一些环境变量,比如从 VSS 上下载的源码的保存目录,发布目录等
l 清除旧的代码并从 VSS 源码库中下载最新源代码
l 编译源代码并运行测试代码集
l 将编译后的目标代码拷贝到发布目录进行发布
为了尽可能少的改动 NAnt 的脚本文件,简化日常维护的工作量,我们把一些对所有项目都基本相同的过程抽取出来,如环境变量定义,清除旧代码获取新代码,编译源代码,对目标代码进行发布的过程都可以写成通用的脚本,而一个具体项目的每日构建脚本则调用通用过程完成
本文采取的目录体系如下所示:
D:\DailyBuild\
\Source:存放源代码的目录
\Build:存放编译后的目标代码的目录
\Publish:存放的 WEB 发布文件的目录
\log:存放的日志文件
3.1. Nant 的基础知识
l Nant 脚本代码文件的基本结构
<?xml version="1.0" encoding="gb2312"?>
……
……
说明:encoding="gb2312"使得脚本文件可以支持中文
标签定义了项目属性,一个脚本文件只能有一个项目定义
default="prebuild"说明该项目缺省从 prebuild 任务开始执行
标签定义了一项任务,任务是 Nant 脚本具体执行动作的最小单元
depends="namecheck,clean "说明该任务执行前需要 namecheck 和 clean 任务先执行
description 描述了该任务的一些说明性信息
l 定义变量
使用了已定义变量 core.basedir 和 solution.name 来定义变量 solution.basedir;
使用了 NAnt 内建函数 directory::get-current-directory() 来定义 curdir 变量
3.2. 定义环境变量
定义环境变量的脚本代码写在 Common。Config 文件里
主要有以下几类信息的定义:
l 每日构建所在的根目录
说明:${directory::get-current-directory()}内建函数获取当前文件所在路径信息
l 被编译的解决方案的目录结构,和前面提到的目录体系一致
说明:以上代码是定义了要编译的解决方案的目录结构信息,其中 ${solution.name}是由外部传入的解决方案的名称,后面的代码将根据该名称在日编译的根目录下生成和 solution.name 指定的名称同名的目录,并在该目录下生成 source,buld,log 等子目录
l VSS 源代码管理系统的基本信息
说明:定义了和 VSS 源码管理系统相关的一些信息,其中 VSS 数据库所在位置可以是网络路径,也可以是本地路径
l <编译时的一些参数
3.3. 建立目录结构,获取源代码
脚本代码写在 CheckSource.build.xml 文件里
l 包含在 Common.config 文件里定义的公共变量
l 检查是否存在 solution.name 变量
<!--检查解决方案名称是否已经定义-->
<!--去掉可能的空格字符-->
<!--检查 solution.name 变量是否为空字符-->
说明:${property::exists('<变量名>')}是 NAnt 内建函数,用于测试某变量是否存在
${string::get-length(<字符串变量>)==0}测试字符串的长度是否为 0
… :如果 test 表达式值为假,执行标签内的代码
… :如果 test 表达式值为假,执行标签内的代码
l 建立解决方案的目录结构
<!--删除旧的解决方案代码所在目录-->
<!--重新建立目录-->
说明:delete 和 mkdir 标签内的 failonerror 属性表示即使操作文件夹的过程中出现了错误,也忽略错误向下执行
l 获取源代码:
从 VSS 上获取解决方案的源代码
<!--检查从 VSS 上下载解决方案的路径是否设定-->
<!-- 如果不定义 vss.projectpath,则缺省为 solution.name -->
<vssget
user="${vss.username}"
password="${vss.password}"
localpath="${solution.source}"
recursive="true"
replace="true"
dbpath="${vss.dbpath}"
path="${vss.basepath}${vss.projectpath}"
/>
说明:标签是 NAntContrib 的语法,用来从 VSS 源码管理器上下载源代码,user 和 password 属性表示登录 VSS 服务器的信息;Localpath 属性是指下载的源代码存放的路径;recursive="true"表示递归获取代码;replace= "true"表示如果本地有重复文件,则进行覆盖;dbpath 定义 VSS 的 srcsafe.ini 文件的路径信息,包括 srcsafe.ini 文件名; path 定义了要获取的源代码在 VSS 数据库中的路径,一般都是以 $/为根目录。
3.4. 编译源代码
l 编译命令
编译解决方案的命令为
其中 solutionfile 属性表明了要编译的解决方案文件的路径信息,即以"sln"为扩展名的文件,
configuration 属性表明要编译的是发行版还是调试版,取值为"Release"或"Debug"
outputdir 表明了编译后的动态链接库或可执行文件存放的目录
solution 中的嵌套标签用于当解决方案含有 WEB 项目的情况,有几个 WEB 项目,就有几项标签,map 标签中的 url 属性为 WEB 项目的.csproj 文件的 WEB 路径,path 则为该.csproj 文件所在磁盘上的物理路径,例如,解决方案中有 WEB 项目 exam,则 map 标签为 <map url="http://localhost/exam/exam.csproj" ... am\exam.csproj"
l 根据解决方案名称获取解决方案文件的路径信息
<!-- 查找解决方案文件名 -->
<!--根据文件名设置解决方案的名称-->
说明:标签是 NAnt 中处理循环的命令,item="File"说明 foreach 进行循环处理的对象是文件,< include>中的 name 变量表示要查找的文件信息,"**\"表示查找路径包括子目录。Foreach 的属性 property="<变量名>"表示查找到的文件路径信息保存在该变量中,可以在标签中引用.foreach 每查找到一项符合条件的 Item,都会执行标签中的代码,以上代码执行的结果就是查找到指定名称的解决方案文件,供后面编译代码使用
l 获取解决方案中 WEB 项目的路径信息
如果解决方案中含有 WEB 项目,则其编译命令和不含 WEB 项目的解决方案编译有所区别,所以要区别对待。如果解决方案含有多个 WEB 项目,则可以让用户将多个 WEB 项目的名称放在一个变量中,如 solution.webprojects,以逗号或分号或空格做分隔符。然后将项目名称分别提取出来,根据 Web 项目的个数决定 solution 命令的形式,代码如下
以上代码中 foreach 标签的属性 item="String" in="${solution.webprojects}" delim=";, " property="project"表明循环对象是字符串,对 in 所代表的字符串
[url]http://hi.baidu.com/xiao0856/blog/item/aae75d23eada2e529822ed16.html/url][