循序渐进——NAnt构建实例
前言
NAnt,一款大名鼎鼎的.NET開源構建工具,功能強大,易于定制。
悲催的是開源的工具往往文檔匱乏,廣大程序猿們有時發(fā)現(xiàn)了看起來很酷的工具,可遲遲無法上手,時間就這么被殘酷地浪費掉了。
在園子里搜索了一下,講“持續(xù)集成”或者“每日構建”的不少,合我心意的不多,要么只能入門,要么起點太高。
正好這兩天不忙,學習了一下NAnt的使用方法,下面就由我來通過一個實例,演示利用NAnt搭建一個自動化構建環(huán)境。
通過本文的構建,最終實現(xiàn)的效果為:
首先從SVN下載最新代碼;利用NAnt編譯代碼;利用NUnit進行單元測試;生成單元測試結(jié)果報表以及代碼覆蓋率報表。
希望通過這篇文章,讓打算使用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,文件內(nèi)容如下:
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文件。
文件內(nèi)容如下:
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 <!-- 編譯結(jié)果 --> 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的構建配置文件了,下面對其中的內(nèi)容說明一下:
文件主要由兩種元素構成:property和target
property用來設置全局變量,以name屬性作為唯一標識,使用的時候用${變量名}來引用。
除了自定義的property,NAnt自己也內(nèi)建了一些全局變量,例如本例中出現(xiàn)的“nant.settings.currentframework”,用來指定當前工程使用的.NET Framework版本。
target是要執(zhí)行的動作,同樣適用name屬性作為唯一標識,depends屬性用來表示依存關系,例如
1 <target name="myexe" 2 depends="mylib">上面的配置表示“myexe”這個target執(zhí)行之前,要先保證mylib被執(zhí)行了。
target內(nèi)部可以包含很多Task標簽,表示這個target要執(zhí)行的任務,具體有哪些標簽的可以參照NAnt的幫助文檔。
最常用的就是csc標簽,用來編譯C#源碼。
大部分csc的屬性很好理解,這里強調(diào)幾個需要特別注意的:
target:這個可不是外層的target標簽哦,而是表示要生成什么類型的結(jié)果,本例中出現(xiàn)了library(類庫)、winexe(窗口程序),還可以設置為exe(控制臺程序)。
debug:設置成None的話,就只生成output指定的文件;如果設置成Full,則還會生成pdb文件,這個文件在我們下面進行代碼覆蓋率計算時需要,因此我們設置成Full。
csc的子標簽常用的有三種,本例中都出現(xiàn)了,分別是sources(源碼)、resources(資源文件)、references(引用),本文提供的實例應該很好理解,不做說明啦。
特別說明一下路徑中使用到的通配符**和*,他們都表示任意文字,區(qū)別是**只能用于代表目錄,并且可以代表任意級層次的目錄,*可以代表目錄與文件,但只能代表單級層次的內(nèi)容,例如:
test1/test2/test3.cs和test1/test2/test3/test4.cs都可以被test1/**/*.cs匹配,而test1/*/*.cs只能匹配到test1/test2/test3.cs
OK,build文件做好了,現(xiàn)在再做一個build.bat文件,內(nèi)容為:
1 cls 2 nant -buildfile:myproject.build -logfile:build.log事實上,這兩個參數(shù)都可選,只打一個命令“nant”也是可以的。
-buildfile參數(shù)用來指定build文件,如果不指定的話,會自動搜索當前目錄下擴展名為.build的文件,如果存在多個.build文件,則只執(zhí)行第一個。
-logfile參數(shù)用來輸出構建過程中的日志,直觀的說,就是我們在命令行窗口中看到的文字,都會被輸出到指定的日志文件中。
三、利用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直接執(zhí)行安裝文件,一路下一步。
將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>根據(jù)之前介紹的內(nèi)容,這些配置比較好理解了,下面還是挑需要注意的地方講解一下。
csc的debug屬性設置成了None,這是因為測試工程生成的dll不需要進行覆蓋率計算,因此不必生成pdb文件。
出現(xiàn)了copy標簽,顧名思義,用來拷貝文件。
需要注意flatten屬性,這個屬性設置成true的意思是,拷貝的文件,不考慮原文件的目錄結(jié)構,而是直接把原文件拷貝到目標文件夾下。如果設置成false,會把要拷貝的原文件的目錄結(jié)構一起帶過來的呦~
exec標簽,用來執(zhí)行一個外部程序。本例中用來調(diào)用OpenCover和ReportGenerator。
需要注意的地方:
1)NUnit是通過OpenCover來調(diào)用的,使用的是OpenCover的-target和-targetargs參數(shù)。
其中,-targetargs用來提供NUnit的執(zhí)行參數(shù),這里有點繞,希望注意。
2)NUnit可以同時對多個dll執(zhí)行測試,多個dll之間用空格隔開。
3)nunit2report標簽用來根據(jù)單元測試結(jié)果xml文件生成單元測試報表。
format屬性用來設定報表的形式,NoFrames表示將單元測試結(jié)果使用一個html文件來展示;而Frames會把各個單元測試項結(jié)果分別生成一個html。本例中是采用了生成單一文件的形式。
4)OpenCover生成的代碼覆蓋率計算結(jié)果文件是一個xml,需要交給ReportGenerator來生成報表
5)ReportGenerator也可以同時處理多個xml文件,利用-reports參數(shù),多個xml文件之間用分號隔開,例如:-reports:xml1;xml2
其他屬性嘛,一目了然啊,不羅嗦啦。
五、完成NAnt構建配置
經(jīng)過上述的配置,基本的自動化流程已經(jīng)設置好啦。再根據(jù)需要進行一些細節(jié)處的調(diào)整。最終的.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 <!-- 編譯結(jié)果 --> 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后記
本來覺得沒什么內(nèi)容,還特意選擇了比較簡單的場景用來演示,結(jié)果寫了一下午啊。好吧,我承認我的效率比較低,哈哈。
遺憾之處是還沒有集成StyleCop或者FxCop,等我學會了集成它們,再更新這篇文章。
總之,希望此文對需要的朋友有幫助。
文章如有疏漏之處,望讀者不吝賜教,板磚糞蛋盡管招呼。
轉(zhuǎn)載于:https://www.cnblogs.com/gaoyunpeng/archive/2013/05/29/3106439.html
總結(jié)
以上是生活随笔為你收集整理的循序渐进——NAnt构建实例的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQL 测验-自测结果
- 下一篇: 如何估算代码量_千万级用户的大型网站,应