循序渐进——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来生成报表
5)ReportGenerator也可以同时处理多个xml文件,利用-reports参数,多个xml文件之间用分号隔开,例如:-reports:xml1;xml2
其他属性嘛,一目了然啊,不罗嗦啦。
五、完成NAnt构建配置
经过上述的配置,基本的自动化流程已经设置好啦。再根据需要进行一些细节处的调整。最终的.build文件如下:
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>
View Code
后记
本来觉得没什么内容,还特意选择了比较简单的场景用来演示,结果写了一下午啊。好吧,我承认我的效率比较低,哈哈。
遗憾之处是还没有集成StyleCop或者FxCop,等我学会了集成它们,再更新这篇文章。
总之,希望此文对需要的朋友有帮助。
文章如有疏漏之处,望读者不吝赐教,板砖粪蛋尽管招呼。
转载于:https://www.cnblogs.com/gaoyunpeng/archive/2013/05/29/3106439.html
循序渐进——NAnt构建实例相关推荐
- 使用Nant构建入门
前言: NAnt是一项开源的项目,利用NAnt,你可以方便快捷地对自己.NET项目或解决方案进行自动构建.与VS中可视化化极强的项目和解决方案管理相比,NAnt只能命令行的方式操作,但是这并不影响Na ...
- 弹弹堂服务器如何修改,弹弹堂端游服务端+手工弹弹堂游戏客户端+GM管理后台+附安裝构建实例教程...
弹弹堂端游服务端+手工弹弹堂游戏客户端+GM管理后台+附安裝构建实例教程 系统centos 7.2 64位 安装宝塔 yum install -y wget && wget -O in ...
- Dockerfile指令详解镜像构建实例说明
Dockerfile使用总结 Dockerfile是用来构建镜像的文本文件,里面包含了一条条用于构建镜像所需的指令和说明. Dockerfiel文件中的每一层指令都是描述如何在上一层的基础上进行该层的 ...
- 阿里云云企业网(CEN)构建实例
公司的跨国业务尝试使用了AlibabaCloud的云企业网(后简写为CEN)加速,记录一下搭建实例的整个过程,以供拾遗. 构建CEN加速前,已存在私有云暴露的https互联网访问入口,也就是需要进行跨 ...
- Maven多模块构建实例
创建coffee-parent项目 New->Maven Project 创建coffee-web项目 右键coffee-parent项目->New->Project... 注意:需 ...
- 基于CentOS中PXE网络环境构建实例
2019独角兽企业重金招聘Python工程师标准>>> NO.1########################### prepare software: system-confi ...
- 用java编写弹弹堂的游戏_游戏源码仿弹弹堂端游服务端_手工弹弹堂游戏客户端_GM管理后台_附安裝构建实例教程...
系统centos 7.2 64位 安装宝塔 yum install -y wget && wget -O install.shhttp://download.bt.cn/install ...
- [转]在.NET环境中实现每日构建(Daily Build)--NAnt篇
本文转自:http://dragon.cnblogs.com/archive/2005/07/29/203189.html 前言 关于每日构建这个话题,也已经有很多很好的文章讨论了.本文的写作过程 ...
- 【QGIS入门实战精品教程】9.1:QGIS构建泰森多边形(Thiessen Polygon)实例精解
泰森多边形是进行快速插值和分析地理实体影响区域的常用工具.例如,用离散点的性质描述多边形区域的性质,用离散点的数据计算泰森多边形区域的数据.泰森多边形可用于定性分析.统计分析和临近分析等. 参考教程: ...
最新文章
- 抛出一个nullpointerexception_Java 14 发布了,再也不怕 NullPointerException 了!
- Java创建数组的三种方法
- python经典案例-20个Python练手经典案例,能全做对的人确实很少!
- iOS系列教程 目录 (持续更新...)
- 关于标签系统的又一点想法。
- JNative用法注意事项
- linux traceroute命令详解_详解Linux系统路由跟踪指令traceroute语法、工作原理和实例说明...
- vue 写门户网站_你不得不知道的Vue项目技巧
- 强烈安利 uTools 我的生产力工具
- this指向_前端必须知道的this指向问题
- 【2016北京集训测试赛(七)】自动机 (思考题)
- VC 2012 编译出错,避免运行老代码
- 教你如何使用pr语音自动生成字幕,pr自动识别声音添加字幕
- js下载activex
- Ubuntu系统各种文件的颜色代表的意义
- 经典算法系列之不死神兔
- HDU1253-胜利大逃亡
- python查找文字在图片中的位置_使用Python识别图片中的中/英文字
- JavaScript知识点复习总结
- mod_security简要安装设置指南
热门文章
- Android AsyncTask简单用法
- all resources based on handshake
- 我不想just talk talk
- unity2019,打包APK时的gradle错误问题
- 【转】early-z、z-culling、hi-z、z-perpass
- .NET和UNITY版本问题
- 从 SPIR-V 到 ISPC:将 GPU 计算转化为 CPU 计算
- python3.5连接MySQL
- [Leetcode]160. Intersection of Two Linked Lists
- 关于在linux下搭建VSFTP下匿名不能上传的问题