.NET环境中实现每日构建--NAnt篇

前言

关于每日构建这个话题,也已经有很多很好的文章讨论了。本文的写作过程中也参考了这些文章。本文之所以继续这个题目,是因为在查阅了网上的资源后,发现没有一个比较通用的过程。所以本文就主要讨论了利用NAnt构建一个通用日编译的方案。利用这个方案,日编译的维护者可以不需要对每个要编译的方案都要做很多维护。只要定义一个属性文件就可以了。

 

关键词: Daily Build, NAnt

1.       简介

1.1.      每日构建的优点:

每日构建(Daily Build)也可称为持续集成(Continuous Integration),强调完全自动化的、可重复的创建过程,其中包括每天运行多次的自动化测试。每日构建的作用日益显得重要。它让开发者可以每天进行系统集成,从而减少了开发过程中的集成问题。

持续集成可以减少集成阶段"捉虫"消耗的时间,从而最终提高生产力。它使得绝大多数bug在引入的同一天就可以被发现。而且,由于一天之中发生变动的部分并不多,所以可以很快找到出错的位置。

1.2.      每日构建完成的任务

实现自动化每日构建需要做以下几部分的工作:

l          使创建过程完全自动化,让任何人都可以只输入一条命令就完成系统的创建。

l          使测试完全自动化,让任何人都可以只输入一条命令就运行一套完整的系统测试。

l          确保所有人都可以得到最新、最好的可执行文件。

2.       每日构建所使用的工具

在.NET环境下建立每日构建可以使用一系列开源工具:

Nant: 完成代码的自动编译,自动运行测试工具。http://nant.sourceforge.net/builds/

NantContrib:自动从源码库中获取源代码。http://nantcontrib.sourceforge.net/nightly/builds/

NUnit2Report:将NUnit测试工具产生的XML报告转换为HTML报告形式。http://NUnit2Report.sourceforge.net

VSS:Visual Source Safe,微软源码管理工具

Draco.NET: 用于自动检测VSS中源代码变动情况,调用Nant完成自动编译

http://sourceforge.net/projects/draconet/

下载所需的工具后,按照如下步骤进行安装:

在服务器上安装VSS源码管理工具

安装下载的Draco Server 和 Draco Web,修改安装后的Draco Web目录下的web.config文件,设置正确的Draco Server安装路径

将NAnt、NAntContrib、NUnit2Report压缩包解压,将三个Bin目录中的内容复制到一个公用目录,比如D:\DailyBuildTools,然后将该路径加入系统的Path路径列表中,具体为“控制面板-〉系统属性-〉环境变量-〉Path”

3.       NAnt自动脚本

NAnt脚本实现了每日构建的主体功能,它具体分为下面几部分

l          定义每日构建所需的一些环境变量,比如从VSS上下载的源码的保存目录,发布目录等

l          清除旧的代码并从VSS源码库中下载最新源代码

l          编译源代码并运行测试代码集

l          将编译后的目标代码拷贝到发布目录进行发布

为了尽可能少的改动NAnt的脚本文件,简化日常维护的工作量,我们把一些对所有项目都基本相同的过程抽取出来,如环境变量定义,清除旧代码获取新代码,编译源代码,对目标代码进行发布的过程都可以写成通用的脚本,而一个具体项目的每日构建脚本则调用通用过程完成

本文采取的目录体系如下所示:

D:\DailyBuild\

<project1>\Source:存放<project1>源代码的目录

<project1>\Build:存放<project1>编译后的目标代码的目录

<project1>\Publish:存放<project1>的WEB发布文件的目录

<project1>\log:存放<project1>的日志文件

 

3.1.      Nant的基础知识

l          Nant脚本代码文件的基本结构

<?xml version="1.0" encoding="gb2312"?>

<project name="Projects" default="prebuild">

<target name="prebuild" depends="namecheck,clean " description="…">

……

</target>

<target name="namecheck" >

……

</target>

</project>

说明:encoding="gb2312"使得脚本文件可以支持中文

<project>标签定义了项目属性,一个脚本文件只能有一个项目定义

default="prebuild"说明该项目缺省从prebuild任务开始执行

<target>标签定义了一项任务,任务是Nant脚本具体执行动作的最小单元

depends="namecheck,clean "说明该任务执行前需要namecheck和clean任务先执行

description描述了该任务的一些说明性信息

l          定义变量

<property name="<变量名>" value="$<变量值>"/>

如上所示,定义变量使用<property>标签,name属性定义了变量的名称,value属性定义变量的值,其中name属性可以使用字母、数字、点号、下划线等符号,而value属性可以使用字符串或是已经定义的变量,Nant内建的函数等,

要使用已经定义的变量,可以用${<变量名>},要使用内建函数,可以使用${<函数名称>}

如: <property name="solution.basedir" value="${core.basedir}\${solution.name}"/>

使用了已定义变量core.basedir和solution.name来定义变量solution.basedir;

<property name="curdir" value="${directory::get-current-directory()}"/>

使用了NAnt内建函数directory::get-current-directory()来定义curdir变量

3.2.      定义环境变量

定义环境变量的脚本代码写在Common。Config文件里

主要有以下几类信息的定义:

l          每日构建所在的根目录

<property name="curdir" value="${directory::get-current-directory()}"/>

<property name="core.basedir" value="${curdir}"/>

说明:${directory::get-current-directory()}内建函数获取当前文件所在路径信息

l          被编译的解决方案的目录结构,和前面提到的目录体系一致

<property name="solution.basedir" value="${core.basedir}\${solution.name}"/>

<property name="solution.source" value="${solution.basedir}\source"/>

<property name="solution.build" value="${solution.basedir}\build"/>

<property name="solution.log" value="${solution.basedir}\log"/>

说明:以上代码是定义了要编译的解决方案的目录结构信息,其中${solution.name}是由外部传入的解决方案的名称,后面的代码将根据该名称在日编译的根目录下生成和solution.name指定的名称同名的目录,并在该目录下生成source,buld,log等子目录

l          VSS源代码管理系统的基本信息

<!--vss数据库登录信息-->

<property name="vss.username" value="autobuild"/>

<property name="vss.password" value="autobuild"/>

<!--vss数据库所在的位置-->

<property name="vss.dbpath" value="\\10.136.238.231\vss\srcsafe.ini"/>

<!--vss中工程的根目录-->

<property name="vss.basepath" value="$/"/>

说明:定义了和VSS源码管理系统相关的一些信息,其中VSS数据库所在位置可以是网络路径,也可以是本地路径

l                  <编译时的一些参数

<!--编译版本号-->

<property name="build.number" value="1.0"/>

<!--决定编译是Debug版本还是Release版本-->

<property name="build.configuration" value="Release"/>

 

3.3.      建立目录结构,获取源代码

脚本代码写在CheckSource.build.xml文件里

l          包含在Common.config文件里定义的公共变量

<include buildfile="common.config"/>

l          检查是否存在solution.name变量

<target name="namecheck" description="检查solution.name变量是否设置">

<!--检查解决方案名称是否已经定义-->

<ifnot test="${property::exists('solution.name')}">

<fail message="未定义解决方案名称solution.name"/>

</ifnot>

<!--去掉可能的空格字符-->

<property name="solution.name" value="${string::trim(solution.name)}"/>

<!--检查solution.name变量是否为空字符-->

<if test="${string::get-length(solution.name)==0}">

<fail message="未定义解决方案的名称solution.name"/>

</if>

</target>

说明:${property::exists('<变量名>')}是NAnt内建函数,用于测试某变量是否存在

${string::get-length(<字符串变量>)==0}测试字符串的长度是否为0

<ifnot test=<逻辑表达式> … </ifnot>:如果test表达式值为假,执行<ifnot>标签内的代码

<if test=<逻辑表达式> … </if>:如果test表达式值为假,执行<if>标签内的代码

l          建立解决方案的目录结构

<target name="clean" depends="namecheck" description="移除旧目录,建立新目录">

<!--删除旧的解决方案代码所在目录-->

<delete dir="${solution.basedir}" failοnerrοr="false"/>

<!--重新建立目录-->

<mkdir dir="${solution.basedir}\" failοnerrοr="false"/>

<mkdir dir="${solution.source}" failοnerrοr="false"/>

<mkdir dir="${solution.build}" failοnerrοr="false"/>

<mkdir dir="${solution.log}" failοnerrοr="false"/>

</target>

说明:delete和mkdir标签内的failonerror属性表示即使操作文件夹的过程中出现了错误,也忽略错误向下执行

l          获取源代码:

从VSS上获取解决方案<solution.name>的源代码

<target name="getsourcecode">

<!--检查从VSS上下载解决方案的路径是否设定-->

<!-- 如果不定义vss.projectpath,则缺省为solution.name  -->

<ifnot test="${property::exists('vss.projectpath')}">

<property name="vss.projectpath" value="${solution.name}"/>

</ifnot>

<vssget

user="${vss.username}"

password="${vss.password}"

localpath="${solution.source}"

recursive="true"

replace="true"

dbpath="${vss.dbpath}"

path="${vss.basepath}${vss.projectpath}"

/>

</target>

说明:<vssget>标签是NAntContrib的语法,用来从VSS源码管理器上下载源代码,user和password属性表示登录VSS服务器的信息;Localpath属性是指下载的源代码存放的路径;recursive="true"表示递归获取代码;replace="true"表示如果本地有重复文件,则进行覆盖;dbpath定义VSS的srcsafe.ini文件的路径信息,包括srcsafe.ini文件名;path定义了要获取的源代码在VSS数据库中的路径,一般都是以$/为根目录。

3.4.      编译源代码

l          编译命令

编译解决方案的命令为

<solution  solutionfile="…" configuration="…" outputdir="…">

<webmap>

<map url="… " path="…"/>

<map url="… " path="…"/>

</webmap>

</solution>

其中solutionfile属性表明了要编译的解决方案文件的路径信息,即以"sln"为扩展名的文件,

configuration属性表明要编译的是发行版还是调试版,取值为"Release"或"Debug"

outputdir表明了编译后的动态链接库或可执行文件存放的目录

solution中的嵌套标签<webmap>用于当解决方案含有WEB项目的情况,有几个WEB项目,就有几项<map>标签,map标签中的url属性为WEB项目的*.csproj文件的WEB路径,path则为该*.csproj文件所在磁盘上的物理路径,例如,解决方案中有WEB项目exam,则map标签为 <map url="http://localhost/exam/exam.csproj" path="c:\exam\exam.csproj"

l          根据解决方案名称获取解决方案文件的路径信息

<target name="build" description="编译解决方案">

<!--       查找解决方案文件名         -->

<foreach item="File" property="filename">

<in>

<items>

<include name="**\${solution.name}.sln"/>

</items>

</in>

<do>

<!--根据文件名设置解决方案的名称-->

<property name="solution.file" value="${filename}"/>

</do>

</foreach>

说明:<foreach>标签是NAnt中处理循环的命令,item="File"说明foreach进行循环处理的对象是文件,<include>中的name变量表示要查找的文件信息,"**\"表示查找路径包括子目录。Foreach的属性property="<变量名>"表示查找到的文件路径信息保存在该变量中,可以在<do>标签中引用.foreach每查找到一项符合条件的Item,都会执行<do>标签中的代码,以上代码执行的结果就是查找到指定名称的解决方案文件,供后面编译代码使用

l          获取解决方案中WEB项目的路径信息

如果解决方案中含有WEB项目,则其编译命令和不含WEB项目的解决方案编译有所区别,所以要区别对待。如果解决方案含有多个WEB项目,则可以让用户将多个WEB项目的名称放在一个变量中,如solution.webprojects,以逗号或分号或空格做分隔符。然后将项目名称分别提取出来,根据Web项目的个数决定solution命令的形式,代码如下

<!--将solution.webprojects中用",",";"或" "分隔的Web工程名提取出来,

分别设为webproject1,webproject2              -->

<if test="${property::exists('solution.webprojects')}">

<foreach item="String" in="${solution.webprojects}" delim=";, " property="project">

<if test="${property::exists('webproject1')}">

<property name="webproject2" value="${project}"/>

</if>

<ifnot test="${property::exists('webproject1')}">

<property name="webproject1" value="${project}"/>

</ifnot>

</foreach>

</if>

以上代码中foreach标签的属性item="String" in="${solution.webprojects}" delim=";, " property="project"表明循环对象是字符串,对in所代表的字符串

如果设定solution.webprojects="webprj1;webprj2”,则以上代码执行的结果是定义了两个变量webproject1 ="webproj1"和webproject2 ="webproj2"

l          查找WEB工程名

根据前面从solution.webprojects中提取出来的webproj1和webproj2变量,查找该WEB工程的文件名

<!--       查找WEB工程文件名 -->

<if test="${property::exists('webproject1')}">

<echo message="test ${webproject1}" />

<foreach item="File" property="filename">

<in>

<items>

<include name="**\${webproject1}.csproj"/>

</items>

</in>

<do>

<!--根据Web项目的名称获取Web项目文件路径,可以处理两个Web项目的情况-->

<echo message="WebProject file=${filename}"/>

<property name="webproject1.file" value="${filename}"/>

</do>

</foreach>

</if>

同理可以处理存在第二个WEB工程项目的情况,设置webproject2.file变量

l          编译解决方案

最后是编译解决方案,分别根据无WEB项目,有2个WEB项目,有一个WEB项目的三种情况处理

下面仅列出有两个WEB项目的情况

<!-- 存在2个Web工程 -->

<if test="${property::exists('webproject2')}">

<solution

solutionfile="${solution.file}"

configuration="${build.configuration}"

outputdir="${solution.build}"

>

<webmap>

<map

url="http://localhost/${webproject1}/${webproject1}.csproj"

path="${webproject1.file}"

/>

<map

url="http://localhost/${webproject2}/${webproject2}.csproj"

path="${webproject2.file}"

/>

</webmap>

</solution>

</if>

3.5.      运行测试代码

l          测试命令

NAnt中关于测试的命令是<NUnit2>标签

<nunit2>

<formatter type="Xml" usefile="true"

extension=".xml" outputdir="…"

/>

<test assemblyname="…" haltonfailure="false" />

</nunit2>

说明:<formatter>标签中,type="Xml"表明了根据测试结果生成XML结构化信息,usefile="true"表明使用文件保存测试结果,extension=".xml"表明生成的文件扩展名为xml,outputdir指出了文件将被保存到哪个目录

Test标签中的assemblyname表明了被测试的dll程序集的路径信息,haltonfailure="false"表明即使测试没有通过仍然继续执行脚本文件

这样在测试命令完成后,会在outputdir指出的目录下生成一个XML形式的报告文件,为了增加测试结果的可读性,可以使用另一个工具NUnit2Report,将测试结果转换为直观的HTML文件。具体命令如下

<nunit2report out="<文件名>" todir="<输出目录" >

<fileset>

<includes name="<文件匹配符>" />

</fileset>

</nunit2report>

说明:includes标签用来搜索符合条件的XML文件,转换出来的HTML文件保存为out指出的文件名,todir指出了HTML文件将保存的目录信息

<if test="${property::exists('solution.testprojects')}">

<foreach item="String" in="${solution.testprojects}" delim=";, " property="project">

<property name="testfile" value="${solution.build}\${project}.dll"/>

<nunit2>

<formatter type="Xml" usefile="true"

extension=".xml" outputdir="${solution.build}" />

<test assemblyname="${testfile}" haltonfailure="false" />

</nunit2>

<nunit2report out="${project}.html" todir="${solution.log}" >

<fileset>

<includes name="${solution.build}\*.xml" />

</fileset>

</nunit2report>

</foreach>

</if>

3.6.      进行WEB发布

WEB发布主要针对有WEB工程项目的解决方案,其实现原理为利用NAnt的拷贝命令,将WEB工程下除了源代码,资源代码,VSS信息文件外的其他文件和编译后的程序集拷贝到发布目录,最后设置WEB虚拟路径以供WEB访问的过程。

设置WEB虚拟路径的命令为

<mkiisdir dirpath="<物理路径>" vdirname="<虚拟路径>"/>

说明:设WEB项目发布在C:\Intepub\wwwroot\Exam,访问该WEB项目用地址http://127.0.0.1/Example/default.aspx,则<物理路径>为"C:\Intepub\wwwroot\Exam",虚拟路径为"Example"(此处略去详细代码)。

下载示例代码

转载于:https://www.cnblogs.com/gxh973121/archive/2006/04/18/378661.html

在.NET环境中实现每日构建--NAnt篇相关推荐

  1. 【转载】在.NET环境中实现每日构建--NAnt篇

    前言 关于每日构建这个话题,也已经有很多很好的文章讨论了. 本文的写作过程中也参考了这些文章.本文之所以继续这个题目,是因为在查阅了网上的资源后,发现没有一个比较通用的过程.所以本文就主要讨论了利用 ...

  2. 在.NET环境中实现每日构建(Daily Build)--ccnet,MSBuild篇

    每日构建,对我们团队来说一个全新的概念.随着项目开发的进展,在开发过程需要及时反馈一些BUG和功能要求的处理情况.而在这种情况下每天或隔一段时间Build一个版本,工作量还是比较大的,所以就特别有必要 ...

  3. [转]在.NET环境中实现每日构建(Daily Build)--NAnt篇

    本文转自:http://dragon.cnblogs.com/archive/2005/07/29/203189.html   前言 关于每日构建这个话题,也已经有很多很好的文章讨论了.本文的写作过程 ...

  4. window中搭建jenkins_Windows环境中的jenkins构建时报错

    按照第三章的讲解进行安装配置后,最后尝试连接进行验证时,可以走通.但是到第五章实际实现自动化部署任务时,按照课程的讲解配置完后,构建时报错:Started by user adminRunning a ...

  5. 平台软件每日构建总结

    作者:朱金灿 来源:http://blog.csdn.net/clever101 1.背景 在以往的软件研发过程中,存在一个比较严重的问题:测试版本发布周期长,严重时长达两个月无法发布测试版本.为了有 ...

  6. 入门级----测试的执行、环境的搭建、每日构建、测试记录和跟踪、回归测试、测试总结和报告...

    测试用例的准备,都是为了执行测试准备的. 测试环境的搭建 (1)测试数据:有些测试需要使用大批量的数据,例如容量测试.压力测试等.根据产品的具体测试要求,可能需要在数据库表插入大量的数据,准备大量的文 ...

  7. 在生产环境中,阿里云如何构建高性能云原生容器网络?(含 PPT 下载)

    作者 | 溪恒  阿里云技术专家 直播完整视频回顾:https://www.bilibili.com/video/BV1nC4y1x7mt/ 关注"阿里巴巴云原生"公众号,后台回复 ...

  8. 姓名学中萍字无根 怎么解释_无根Buildah的工作原理:在非特权环境中构建容器

    姓名学中萍字无根 怎么解释 在以前的文章中,包括无根Podman如何工作? ,我谈到了Podman ,该工具使用户可以管理Pod,容器和容器图像. Buildah是用于构建与Podman互补的Open ...

  9. 郭瑞东 | 如何制造更聪明的人工智能?让人工生命在复杂环境中进化

    来源:集智俱乐部 作者:郭瑞东 我们都知道大脑是控制身体的中枢,或者夸张点说,身体只是大脑的傀儡.然而事实果真如此吗?认知科学指出,人类并不总是"先知后行",很多时候也会" ...

最新文章

  1. 力所能及的做些自己喜欢和有趣的事
  2. VTK:可视化算法之BluntStreamlines
  3. 规格参数组查询的代码实现
  4. 银行利率是5.45%,房贷是4.9%,有钱该提前还房贷还是存款?
  5. 题目1089:数字反转(简单数字转换)
  6. 2020十大新消费人群
  7. Python编程思想是什么?
  8. 智慧社区互动教室_利用研究与社区互动
  9. 3分钟创建一个游戏类容器应用【华为云分享】
  10. 基于JAVA+SpringMVC+Mybatis+MYSQL的航班预订系统
  11. ubuntu装指定分区_Ubuntu安装时怎样分区
  12. 小米android手机怎么刷机,小米手机要刷机如何进入recovery模式
  13. python 无法定位程序输入点_系统提示无法定位程序输入点于动态链接库的解决方案【图文】-太平洋电脑网PConline-太平洋电脑网...
  14. HPE comms CTO:我们的单位在增长,即使我们的客户不是
  15. idea光标移至行尾快捷键——End键不能移至行尾的解决办法
  16. ES7241D低功耗音频ADC芯片##小爱音响
  17. 计算机系统基础实验报告
  18. 百度巨变的2019年,都经历了什么鬼?
  19. 跨专业保研上交计算机,新闻学到经济学,跨专业保研血泪史
  20. ubc计算机二学位培养方案,UT/McGill/UBC CS二学位(主要介绍多大二学位了)

热门文章

  1. mysql根据外键多条件查询_MySQL练习-主外键多表查询
  2. R语言︱文本挖掘——词云wordcloud2包
  3. Java中Spring面试基础题20190302
  4. 注解Annotation
  5. vmware虚拟机中常见的3种网络模式
  6. cisco 交换机设置时区、时间、同步日志本地时间等操作
  7. PHP经验——PHPDoc PHP注释的标准文档(翻译自Wiki)
  8. win8.1使用及优化
  9. nginx 作为Web缓存服务器
  10. Oracle从非归档模式变成归档模式