好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

NAnt构建实例

NAnt构建实例

NAnt构建实例

前言

NAnt,一款大名鼎鼎的.NET开源构建工具,功能强大,易于定制。

悲催的是开源的工具往往文档匮乏,广大程序猿们有时发现了看起来很酷的工具,可迟迟无法上手,时间就这么被残酷地浪费掉了。

在园子里搜索了一下,讲“持续集成”或者“每日构建”的不少,合我心意的不多,要么只能入门,要么起点太高。

正好这两天不忙,学习了一下NAnt的使用方法,下面就由我来通过一个实例,演示利用NAnt搭建一个自动化构建环境。

通过本文的构建,最终实现的效果为:

首先从SVN下载最新代码;利用NAnt编译代码;利用NUnit进行单元测试;生成单元测试结果报表以及代码覆盖率报表。

希望通过这篇文章,让打算使用NAnt进行自动化构建的同袍尽快上手。

本文中利用到的工具

NAnt(v0.92)

NUnit(v2.6.2)

OpenCover(v4.0.804)

ReportGenerator(v1.8.1.0)

TortoiseSVN(v1.7.10)

一、利用TortoiseSVN检出项目源码

1、获得工具

TortoiseSVN的项目地址如下:

http://sourceforge.net/projects/tortoisesvn/

2、安装工具

运行安装包,一路下一步即可。

3、检出源码

连接到你自己的代码服务器,检出源码。鉴于TortoiseSVN的易用性相当不错,我就不再罗嗦介绍具体的源码检出方法了,毕竟这并不是本文的重点。

本例中,假设代码服务器上面我们要构建的工程的地址为:http://192.168.1.1/myproject

假设源码检出到本地路径:D:\source\myproject

二、利用NAnt编译C#工程

1、获得工具

NAnt的项目地址如下:

NAnt: http://sourceforge.net/projects/nant/

NAntContrib: http://sourceforge.net/projects/nantcontrib/

NAnt不用多说。NAntContrib是NAnt的扩展,在本例中,需要利用它来生成单元测试报表和SVN控制。

2、安装工具

将NAnt的bin文件夹包含的文件拷贝出来,本例中放置在D:\Tools\NAnt

之后,将NAntContrib的bin文件夹包含的文件也拷贝到D:\Tools\NAnt

在任意位置,建立一个文件 nant.bat,文件内容如下:

 1   @echo   off 
 2  "D:\Tools\NAnt\NAnt.exe" %*

然后,将nant.bat文件剪切到C:\WINDOWS目录下

运行cmd.exe,在命令行窗口中敲入命令“nant -help”,如果看到NAnt的帮助信息,则说明安装成功。

3、编译源码

首先,在刚刚检出的源码根目录(D:\source\myproject)下建立一个名字为myproject.build的xml文件。

文件内容如下:

  1   <?  xml version="1.0" encoding="utf-8"   ?> 
  2   <  project   name  ="myproject"   default  ="build"   basedir  ="."  > 
  3       <  property   name  ="nant.settings.currentframework"   value  ="net-3.5"  /> 
  4       <!--   源码路径   --> 
  5       <  property   name  ="dir.source"   value  ="D:\source\myproject"   /> 
  6       <  property   name  ="dir.source.myexe"   value  ="${dir.source}\myexe"   /> 
  7       <  property   name  ="dir.source.mylib"   value  ="${dir.source}\mylib"   /> 
  8       <  property   name  ="file.ico.myexe"   value  ="${dir.source.exe}\myexe.ico"   /> 
  9       <!--   编译结果   --> 
 10       <  property   name  ="dir.release"   value  ="D:\Release"   /> 
 11       <  property   name  ="dir.bin"   value  ="${dir.release}\bin"   /> 
 12       <  property   name  ="file.exe.myexe"   value  ="${dir.bin}\myexe.exe"   /> 
 13       <  property   name  ="file.lib.mylib"   value  ="${dir.bin}\mylib.dll"   /> 
 14       <  target   name  ="build" 
 15               depends  ="compile"  > 
 16       </  target  > 
 17       <  target   name  ="compile" 
 18               depends  ="mylib,myexe"  > 
 19       </  target  > 
 20       <  target   name  ="mylib"  > 
 21           <  csc   target  ="library" 
 22                output  ="${file.lib.mylib}" 
 23                debug  ="Full" 
 24                optimize  ="true" 
 25                define  ="TRACE" 
 26                platform  ="AnyCPU" 
 27                warninglevel  ="4" 
 28                rebuild  ="true" 
 29                filealign  ="512"  > 
 30               <  sources  > 
 31                   <  include   name  ="${dir.source.mylib}\**\*.cs"   /> 
 32               </  sources  > 
 33           </  csc  > 
 34       </  target  > 
 35       <  target   name  ="myexe" 
 36               depends  ="mylib"  > 
 37           <  csc   target  ="winexe" 
 38                output  ="${file.exe.myexe}" 
 39                debug  ="Full" 
 40                optimize  ="true" 
 41                define  ="TRACE" 
 42                platform  ="AnyCPU" 
 43                warninglevel  ="4" 
 44                rebuild  ="true" 
 45                filealign  ="512" 
 46                win32icon  ="${file.ico.myexe}"  > 
 47               <  sources  > 
 48                   <  include   name  ="${dir.source.myexe}\**\*.cs"   /> 
 49               </  sources  > 
 50               <  resources  > 
 51                   <  include   name  ="${dir.source.myexe}\**\*.resx"   /> 
 52               </  resources  > 
 53               <  references  > 
 54                   <  include   name  ="${file.lib.mylib}"   /> 
 55               </  references  > 
 56           </  csc  > 
 57       </  target  > 
 58   </  project  > 

这就是NAnt的构建配置文件了,下面对其中的内容说明一下:

文件主要由两种元素构成:property和target

property用来设置全局变量,以name属性作为唯一标识,使用的时候用${变量名}来引用。

除了自定义的property,NAnt自己也内建了一些全局变量,例如本例中出现的“nant.settings.currentframework”,用来指定当前工程使用的.NET Framework版本。

target是要执行的动作,同样适用name属性作为唯一标识,depends属性用来表示依存关系,例如

 1   <  target   name  ="myexe" 
 2           depends  ="mylib"  > 

上面的配置表示“myexe”这个target执行之前,要先保证mylib被执行了。

target内部可以包含很多Task标签,表示这个target要执行的任务,具体有哪些标签的可以参照NAnt的帮助文档。

最常用的就是csc标签,用来编译C#源码。

大部分csc的属性很好理解,这里强调几个需要特别注意的:

target:这个可不是外层的target标签哦,而是表示要生成什么类型的结果,本例中出现了library(类库)、winexe(窗口程序),还可以设置为exe(控制台程序)。

debug:设置成None的话,就只生成output指定的文件;如果设置成Full,则还会生成pdb文件,这个文件在我们下面进行代码覆盖率计算时需要,因此我们设置成Full。

csc的子标签常用的有三种,本例中都出现了,分别是sources(源码)、resources(资源文件)、references(引用),本文提供的实例应该很好理解,不做说明啦。

特别说明一下路径中使用到的通配符**和*,他们都表示任意文字,区别是**只能用于代表目录,并且可以代表任意级层次的目录,*可以代表目录与文件,但只能代表单级层次的内容,例如:

test1/test2/test3.cs和test1/test2/test3/test4.cs都可以被test1/**/*.cs匹配,而test1/*/*.cs只能匹配到test1/test2/test3.cs

OK,build文件做好了,现在再做一个build.bat文件,内容为:

 1   cls 
 2  nant -buildfile :myproject .build -logfile :build .log

事实上,这两个参数都可选,只打一个命令“nant”也是可以的。

-buildfile参数用来指定build文件,如果不指定的话,会自动搜索当前目录下扩展名为.build的文件,如果存在多个.build文件,则只执行第一个。

-logfile参数用来输出构建过程中的日志,直观的说,就是我们在命令行窗口中看到的文字,都会被输出到指定的日志文件中。

三、利用NAnt自动更新代码

在我们文章的开始,我们是使用TortoiseSVN客户端来检出代码的,但我们想自动化,所以这个动作,也可以交给NAnt来完成。

1、修改.build文件

在.build文件中追加一个target,如下

 1   <  target   name  ="update"  > 
 2       <  svn   command  ="update"  
 3            destination  ="${dir.source}"  
 4            uri  ="http://192.168.1.1/myproject"  
 5            verbose  ="true" 
 6            quiet  ="false" 
 7            /> 
 8   </  target  > 

然后,再把update动作追加到动作序列里:

 1   <  target   name  ="build" 
 2           depends  ="update,compile"  > 

齐活儿~

四、利用NAnt进行单元测试并生成报表

1、获取工具

NUnit: http://sourceforge.net/projects/nunit/

OpenCover: https://opencover.codeplex.com/

ReportGenerator: https://reportgenerator.codeplex.com/

2、安装工具

NUnit直接执行安装文件,一路下一步。

将OpenCover的解压缩出来,本例中放置在D:\Tools\OpenCover

将ReportGenerator的bin文件夹包含的文件拷贝出来,本例中放置在D:\Tools\ReportGenerator

3、修改.build文件

在.build文件中追加target,如下

  1   <  target   name  ="mylib.test" 
  2           depends  ="mylib"  > 
  3       <  csc   target  ="library" 
  4            output  ="${file.lib.mylib.test}" 
  5            debug  ="None" 
  6            optimize  ="true" 
  7            define  ="TRACE" 
  8            platform  ="AnyCPU" 
  9            warninglevel  ="4" 
 10            rebuild  ="true" 
 11            filealign  ="512"  > 
 12           <  sources  > 
 13               <  include   name  ="${dir.source.mylib.test}\**\*.cs"   /> 
 14           </  sources  > 
 15           <  references  > 
 16               <  include   name  ="${file.lib.mylib}"   /> 
 17               <  include   name  ="${file.lib.nunit.framework}"   /> 
 18           </  references  > 
 19       </  csc  > 
 20       <  copy   todir  ="${dir.bin}"   flatten  ="true"  > 
 21           <  fileset  > 
 22               <  include   name  ="${file.lib.nunit.framework}"   /> 
 23           </  fileset  > 
 24       </  copy  > 
 25   </  target  > 
 26   <  target   name  ="myexe.test" 
 27           depends  ="myexe"  > 
 28       <  csc   target  ="library" 
 29            output  ="${file.lib.myexe.test}" 
 30            debug  ="None" 
 31            optimize  ="true" 
 32            define  ="TRACE" 
 33            platform  ="AnyCPU" 
 34            warninglevel  ="4" 
 35            rebuild  ="true" 
 36            filealign  ="512"  > 
 37           <  sources  > 
 38               <  include   name  ="${dir.source.myexe.test}\**\*.cs"   /> 
 39           </  sources  > 
 40           <  references  > 
 41               <  include   name  ="${file.exe.myexe}"   /> 
 42               <  include   name  ="${file.lib.mylib}"   /> 
 43               <  include   name  ="${file.lib.nunit.framework}"   /> 
 44           </  references  > 
 45       </  csc  > 
 46       <  copy   todir  ="${dir.bin}"   flatten  ="true"  > 
 47           <  fileset  > 
 48               <  include   name  ="${file.lib.nunit.framework}"   /> 
 49           </  fileset  > 
 50       </  copy  > 
 51   </  target  > 
 52   <  target   name  ="test" 
 53           depends  ="mylib.test,myexe.test"  > 
 54       <  exec   program  ="OpenCover.Console.exe"   basedir  ="${dir.exe.opencover}"  > 
 55           <  arg   value  ="-register:user"   /> 
 56           <  arg   value  ="-target:${file.exe.nunit}"   /> 
 57           <  arg   value  ="-targetargs:${file.lib.myexe.test} ${file.lib.mylib.test} /result:${file.xml.test.result} /framework:net-3.5 /noshadow"   /> 
 58           <  arg   value  ="-output:${file.xml.test.coverage}"   /> 
 59       </  exec  > 
 60       <  nunit2report   format  ="NoFrames"   todir  ="${dir.report}\NUnit"   verbose  ="true"  > 
 61           <  fileset  > 
 62               <  include   name  ="${file.xml.test.result}"   /> 
 63           </  fileset  > 
 64       </  nunit2report  > 
 65       <  mkdir   dir  ="${dir.report}"   /> 
 66       <  exec   program  ="ReportGenerator.exe"   basedir  ="${dir.exe.repotegenerator}"  > 
 67           <  arg   value  ="-reports:${file.xml.test.coverage}"   /> 
 68           <  arg   value  ="-targetdir:${dir.report}\OpenCover"   /> 
 69       </  exec  > 
 70   </  target  > 

根据之前介绍的内容,这些配置比较好理解了,下面还是挑需要注意的地方讲解一下。

csc的debug属性设置成了None,这是因为测试工程生成的dll不需要进行覆盖率计算,因此不必生成pdb文件。

出现了copy标签,顾名思义,用来拷贝文件。

需要注意flatten属性,这个属性设置成true的意思是,拷贝的文件,不考虑原文件的目录结构,而是直接把原文件拷贝到目标文件夹下。如果设置成false,会把要拷贝的原文件的目录结构一起带过来的呦~

exec标签,用来执行一个外部程序。本例中用来调用OpenCover和ReportGenerator。

需要注意的地方:

1)NUnit是通过OpenCover来调用的,使用的是OpenCover的-target和-targetargs参数。

其中,-targetargs用来提供NUnit的执行参数,这里有点绕,希望注意。

2)NUnit可以同时对多个dll执行测试,多个dll之间用空格隔开。

3)nunit2report标签用来根据单元测试结果xml文件生成单元测试报表。

format属性用来设定报表的形式,NoFrames表示将单元测试结果使用一个html文件来展示;而Frames会把各个单元测试项结果分别生成一个html。本例中是采用了生成单一文件的形式。

4)OpenCover生成的代码覆盖率计算结果文件是一个xml,需要交给ReportGenerator来生成报表

其他属性嘛,一目了然啊,不罗嗦啦。

五、完成NAnt构建配置

经过上述的配置,基本的自动化流程已经设置好啦。再根据需要进行一些细节处的调整。最终的.build文件如下:

  View Code

 1   <?  xml version="1.0" encoding="utf-8"   ?> 
   2   <  project   name  ="myproject"   default  ="build"   basedir  ="."  > 
   3       <  property   name  ="nant.settings.currentframework"   value  ="net-3.5"  /> 
   4       <!--   需要利用到的工具   --> 
   5       <  property   name  ="dir.exe.opencover"   value  ="D:\Tools\OpenCover"   /> 
   6       <  property   name  ="dir.exe.repotegenerator"   value  ="D:\Tools\ReportGenerator"   /> 
   7       <  property   name  ="file.lib.nunit.framework"   value  ="C:\Program Files\NUnit 2.6.2\bin\framework\nunit.framework.dll"   /> 
   8       <  property   name  ="file.exe.nunit"   value  ="C:\Program Files\NUnit 2.6.2\bin\nunit-console-x86.exe"   /> 
   9       <!--   源码路径   --> 
  10       <  property   name  ="dir.source"   value  ="D:\source\myproject"   /> 
  11       <  property   name  ="dir.source.myexe"   value  ="${dir.source}\myexe"   /> 
  12       <  property   name  ="dir.source.myexe.test"   value  ="${dir.source}\myexe.test"   /> 
  13       <  property   name  ="dir.source.mylib"   value  ="${dir.source}\mylib"   /> 
  14       <  property   name  ="dir.source.mylib.test"   value  ="${dir.source}\mylib.test"   /> 
  15       <  property   name  ="file.ico.myexe"   value  ="${dir.source.exe}\myexe.ico"   /> 
  16       <!--   编译结果   --> 
  17       <  property   name  ="dir.release"   value  ="D:\Release"   /> 
  18       <  property   name  ="dir.bin"   value  ="${dir.release}\bin"   /> 
  19       <  property   name  ="file.exe.myexe"   value  ="${dir.bin}\myexe.exe"   /> 
  20       <  property   name  ="file.lib.myexe.test"   value  ="${dir.bin}\myexe.test.dll"   /> 
  21       <  property   name  ="file.lib.mylib"   value  ="${dir.bin}\mylib.dll"   /> 
  22       <  property   name  ="file.lib.mylib.test"   value  ="${dir.bin}\mylib.test.dll"   /> 
  23       <  property   name  ="file.pdb.myexe"   value  ="${dir.bin}\myexe.pdb"   /> 
  24       <  property   name  ="file.pdb.mylib"   value  ="${dir.bin}\mylib.pdb"   /> 
  25       <!--   单元测试   --> 
  26       <  property   name  ="dir.report"   value  ="${dir.release}\report"   /> 
  27       <  property   name  ="dir.result"   value  ="${dir.release}\result"   /> 
  28       <  property   name  ="file.xml.test.result"   value  ="${dir.result}\myproject-results.xml"   /> 
  29       <  property   name  ="file.xml.test.coverage"   value  ="${dir.result}\myproject-coverage.xml"   /> 
  30       <  target   name  ="build" 
  31               depends  ="update,compile,test,clean"  > 
  32       </  target  > 
  33       <  target   name  ="update"  > 
  34           <  svn   command  ="update"  
  35                destination  ="${dir.source}"  
  36                uri  ="http://192.168.1.1/myproject"  
  37                verbose  ="true" 
  38                quiet  ="false" 
  39                /> 
  40       </  target  > 
  41       <  target   name  ="compile" 
  42               depends  ="mylib,mylib.test,myexe,myexe.test"  > 
  43       </  target  > 
  44       <  target   name  ="mylib"  > 
  45           <  csc   target  ="library" 
  46                output  ="${file.lib.mylib}" 
  47                debug  ="Full" 
  48                optimize  ="true" 
  49                define  ="TRACE" 
  50                platform  ="AnyCPU" 
  51                warninglevel  ="4" 
  52                rebuild  ="true" 
  53                filealign  ="512"  > 
  54               <  sources  > 
  55                   <  include   name  ="${dir.source.mylib}\**\*.cs"   /> 
  56               </  sources  > 
  57           </  csc  > 
  58       </  target  > 
  59       <  target   name  ="mylib.test" 
  60               depends  ="mylib"  > 
  61           <  csc   target  ="library" 
  62                output  ="${file.lib.mylib.test}" 
  63                debug  ="None" 
  64                optimize  ="true" 
  65                define  ="TRACE" 
  66                platform  ="AnyCPU" 
  67                warninglevel  ="4" 
  68                rebuild  ="true" 
  69                filealign  ="512"  > 
  70               <  sources  > 
  71                   <  include   name  ="${dir.source.mylib.test}\**\*.cs"   /> 
  72               </  sources  > 
  73               <  references  > 
  74                   <  include   name  ="${file.lib.mylib}"   /> 
  75                   <  include   name  ="${file.lib.nunit.framework}"   /> 
  76               </  references  > 
  77           </  csc  > 
  78           <  copy   todir  ="${dir.bin}"   flatten  ="true"  > 
  79               <  fileset  > 
  80                   <  include   name  ="${file.lib.nunit.framework}"   /> 
  81               </  fileset  > 
  82           </  copy  > 
  83       </  target  > 
  84       <  target   name  ="myexe" 
  85               depends  ="mylib"  > 
  86           <  csc   target  ="winexe" 
  87                output  ="${file.exe.myexe}" 
  88                debug  ="Full" 
  89                optimize  ="true" 
  90                define  ="TRACE" 
  91                platform  ="AnyCPU" 
  92                warninglevel  ="4" 
  93                rebuild  ="true" 
  94                filealign  ="512" 
  95                win32icon  ="${file.ico.myexe}"  > 
  96               <  sources  > 
  97                   <  include   name  ="${dir.source.myexe}\**\*.cs"   /> 
  98               </  sources  > 
  99               <  resources  > 
 100                   <  include   name  ="${dir.source.myexe}\**\*.resx"   /> 
 101               </  resources  > 
 102               <  references  > 
 103                   <  include   name  ="${file.lib.mylib}"   /> 
 104               </  references  > 
 105           </  csc  > 
 106       </  target  > 
 107       <  target   name  ="myexe.test" 
 108               depends  ="myexe"  > 
 109           <  csc   target  ="library" 
 110                output  ="${file.lib.myexe.test}" 
 111                debug  ="None" 
 112                optimize  ="true" 
 113                define  ="TRACE" 
 114                platform  ="AnyCPU" 
 115                warninglevel  ="4" 
 116                rebuild  ="true" 
 117                filealign  ="512"  > 
 118               <  sources  > 
 119                   <  include   name  ="${dir.source.myexe.test}\**\*.cs"   /> 
 120               </  sources  > 
 121               <  references  > 
 122                   <  include   name  ="${file.exe.myexe}"   /> 
 123                   <  include   name  ="${file.lib.mylib}"   /> 
 124                   <  include   name  ="${file.lib.nunit.framework}"   /> 
 125               </  references  > 
 126           </  csc  > 
 127           <  copy   todir  ="${dir.bin}"   flatten  ="true"  > 
 128               <  fileset  > 
 129                   <  include   name  ="${file.lib.nunit.framework}"   /> 
 130               </  fileset  > 
 131           </  copy  > 
 132       </  target  > 
 133       <  target   name  ="test" 
 134               depends  ="mylib.test,myexe.test"  > 
 135           <  exec   program  ="OpenCover.Console.exe"   basedir  ="${dir.exe.opencover}"  > 
 136               <  arg   value  ="-register:user"   /> 
 137               <  arg   value  ="-target:${file.exe.nunit}"   /> 
 138               <  arg   value  ="-targetargs:${file.lib.myexe.test} ${file.lib.mylib.test} /result:${file.xml.test.result} /framework:net-3.5 /noshadow"   /> 
 139               <  arg   value  ="-output:${file.xml.test.coverage}"   /> 
 140           </  exec  > 
 141           <  nunit2report   format  ="NoFrames"   todir  ="${dir.report}\NUnit"   verbose  ="true"  > 
 142               <  fileset  > 
 143                   <  include   name  ="${file.xml.test.result}"   /> 
 144               </  fileset  > 
 145           </  nunit2report  > 
 146           <  mkdir   dir  ="${dir.report}"   /> 
 147           <  exec   program  ="ReportGenerator.exe"   basedir  ="${dir.exe.repotegenerator}"  > 
 148               <  arg   value  ="-reports:${file.xml.test.coverage}"   /> 
 149               <  arg   value  ="-targetdir:${dir.report}\OpenCover"   /> 
 150           </  exec  > 
 151       </  target  > 
 152       <  target   name  ="clean"  > 
 153           <  delete   dir  ="${dir.result}"   /> 
 154           <  delete  > 
 155               <  fileset  > 
 156                   <  include   name  ="${file.lib.myexe.test}"   /> 
 157                   <  include   name  ="${file.lib.mylib.test}"   /> 
 158                   <  include   name  ="${file.pdb.myexe}"   /> 
 159                   <  include   name  ="${file.pdb.mylib}"   /> 
 160                   <  include   name  ="${dir.release}\bin\nunit.framework.dll"   /> 
 161               </  fileset  > 
 162           </  delete  > 
 163       </  target  > 
 164   </  project  > 

后记

本来觉得没什么内容,还特意选择了比较简单的场景用来演示,结果写了一下午啊。好吧,我承认我的效率比较低,哈哈。

遗憾之处是还没有集成StyleCop或者FxCop,等我学会了集成它们,再更新这篇文章。

总之,希望此文对需要的朋友有帮助。

文章如有疏漏之处,望读者不吝赐教,板砖粪蛋尽管招呼。

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于NAnt构建实例的详细内容...

  阅读:49次