iT邦幫忙

DAY 18
2

使用Jenkins打造.Net CI Server系列 第 18

CI Server 18 - 使用MsBuild控制建置流程

我們在前面幾篇文章之中,將專案的建置過程整合了許多測試及分析的流程,
讓每天都能夠得到專案最新的測試狀況,以及各種靜態程式碼分析報表,
而若要在Jenkins上建立專案的話,雖然我們也可以直接使用複製專案的功能,
但需要手動更改單元測試或是靜態程式碼分析掃描的目標(ex cs檔或dll),
而若我們事先透過MsBuild建置好流程,並將可能變更的部分都抽出成config,
這樣一來就算要建立新的每日建置,也只要複製新專案的config就可以完成,
而要變更流程時,也可以一次性質的套用到所有的專案之中,
減少了很多繁複的修改工作。
※建立新專案的每日建置
因為我們之前已經建立過包含各種測試分析流程的專案,
所以我們若要新增每日建置時可以使用複製的方式來建立,

但在建立完成之後我們會有許多東西要修改,而且是分散不集中的,

而且如果要增加一個靜態程式碼分析的項目,我們需要手動修改某個專案,
就必須手動一個一個修改,既耗費時間又容易出錯,
如果可以透過一個的建置專案來負責整個建置流程,
我們只要負責參數的部分,那會讓維護的工作更加的輕鬆愉快。

※分析建置流程
我們一個專案的建置大概包含以下項目 (包含Command line指令)

* 專案建置
		1. MsBuild		
			"C:\windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe" /p:Configuration=Debug "/p:Platform=Any CPU" CI-Sample.sln
	* 單元測試
		1. MsTest
			"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\MSTest.exe" /resultsfile:TestResult.trx /testsettings:Local.testsettings /testcontainer:CI-Sample.Test\bin\Debug\CI-Sample.Test.dll		
		2. Code Coverage
			"C:\CI_Tools\CodeCoverageConverter\CodeCoverageConverter.exe" TestResults coverage.xml
			"C:\CI_Tools\msxsl.exe" coverage.xml "C:\CI_Tools\MSTestCoverageToEmma.xsl" -o coverageResult.xml
	* 靜態程式碼分析
		1. StyleCop
			"C:\windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe" C:\CI_Tools\StyleCopSetting.xml
		2. FxCop
			"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\FxCopCmd.exe" /file:"CI-Sample\bin\Debug\CI-Sample.dll" /out:FxCopReport.xml
		3. Simian
			"C:\Tools\Simian\simian-2.3.33.exe" -formatter=xml:SimianReport.xml -threshold=3 -language=cs -excludes=**/*Test.cs "**\*.cs"
			exit 0
		4. Source Monitor
			"C:\Program Files (x86)\SourceMonitor\SourceMonitor.exe" /C "C:\CI_Tools\SourceMonitorCommand.xml"
			"C:\CI_Tools\msxsl.exe" SourceMonitorReport.xml "C:\CI_Tools\SourceMonitorSummaryGeneration.xsl" -o SourceMonitorSummaryGeneration.xml
			"C:\CI_Tools\msxsl.exe" SourceMonitorSummaryGeneration.xml "C:\CI_Tools\SourceMonitor.xsl" -o SourceMonitorResult.html

其實以上的指令我們已經可以寫成一個bat來做建置指令,
但我希望透過MsBuild來改寫,讓整個建置流程更加得清楚和易於修改,
接下來我們來分析上面的建置指令中有那些參數可能是或被抽換的,
主要可以歸類為三種

---* 專案內容 (將會獨立抽為專案的建置設定檔)
------1. sln檔案位置 (*.sln)
------2. 原始碼檔案位置 (*.cs, 給StyleCop掃描用)
------3. 建置產出的dll位置 (*.dll)
------4. unittest設定位置 (*.testsettings)
------5. unittest dll位置 (*.dll)
---* 工具程式及其路徑
------* MsBuild
---------1. MsBuild.exe路徑
------* MsTest
---------1. MsTest.exe路徑
---------2. MsTest Result存放位置
---------3. 將data.coverage轉換為xml的結果檔名
---------4. 將coverage產出的xml轉換為emma格式的結果檔名
------* StyleCop
---------1. StyleCop Task路徑
---------2. StyleCop結果檔名
------* FxCop
---------1. FxCopCmd.exe路徑
---------2. FxCop要掃描的規則
---------3. FxCop結果檔名
------* Simian
---------1. Simian.exe路徑
---------2. Simian結果檔名
---------3. Simian Threshold
---------4. Simian掃描的語言類型
------* Source Monitor
---------1. SourceMonitor.exe路徑
---------2. SourceMonitor結果檔名
---------3. SourceMonitor指令(xml內容)
---------4. SourceMonitor轉換檔-1
---------5. SourceMonitor轉換檔-2
------* 建置流程
---------1. 建置專案
---------2. 執行單元測試
---------3. 計算單元測試覆蓋率
---------4. StyleCop分析
---------5. FxCop分析
---------6. Simian分析
---------7. SourceMonitor分析

※撰寫建置設定檔
根據我們以上列出的內容,我們可以很輕鬆地寫出建置的設定檔,
主要由Property和Item所構成,我們並額外增加了各個步驟的開關

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!--Solution Path-->
  <PropertyGroup>
    <SolutionPath>$(MSBuildStartupDirectory)\CI-Sample.sln</SolutionPath>        
  </PropertyGroup>
  
  <!--Cs Files Path-->
  <PropertyGroup>
	<SourceCodesInclude>$(MSBuildStartupDirectory)\**\*.cs</SourceCodesInclude>
	<SourceCodesExclude>$(MSBuildStartupDirectory)\**\*Test.cs</SourceCodesExclude>
  </PropertyGroup>
  
  <!--Output Dlls-->
  <ItemGroup>
	<OutputDlls Include="$(MSBuildStartupDirectory)\CI-Sample\bin\Debug\CI-Sample.dll" />	
  </ItemGroup>
  
  <!--UnitTest Setting Path-->
  <PropertyGroup>
    <UnitTestSettingPath>$(MSBuildStartupDirectory)\Local.testsettings</UnitTestSettingPath>        
  </PropertyGroup>
    
  <!--Unit Test Dlls-->
  <ItemGroup>
	<UnitTestDlls Include="$(MSBuildStartupDirectory)\CI-Sample.Test\bin\Debug\CI-Sample.Test.dll" />
  </ItemGroup> 
  
  <!--Enable Setting-->
  <PropertyGroup>
	<EnableBuildProject>True</EnableBuildProject>
	<EnableUnitTest>True</EnableUnitTest>
	<EnableStyleCop>True</EnableStyleCop>
	<EnableFxCop>True</EnableFxCop>
	<EnableSimian>True</EnableSimian>
	<EnableSourceMonitor>True</EnableSourceMonitor>
  </PropertyGroup>
</Project>

再撰寫測試檔來測試是否有抓到想要的結果

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!--Import Config-->
	<Import Project="$(MSBuildProjectDirectory)\$(BuildSetting)"/>
	
	<Target Name="Build">	
		<Message Text="SolutionPath:"  Importance="high" />
		<Message Text="$(SolutionPath)" />		
		
		<Message Text="SourceCodes:" Importance="high"/>
		<Message Text="$(SourceCodesInclude)" />
		<Message Text="$(SourceCodesExclude)" />
		
		<Message Text="OutputDlls:" Importance="high"/>
		<Message Text="@(OutputDlls,'%0a')" />
		
		<Message Text="UnitTestSettingPath:" Importance="high"/>
		<Message Text="$(UnitTestSettingPath)" />
		
		<Message Text="UnitTestDlls:" Importance="high"/>
		<Message Text="@(UnitTestDlls,'%0a')" />		
		
		<Message Text="Finish" />
	</Target>	
</Project>

執行結果

※撰寫工具程式設定檔
接下來我們開始寫我們工具程式的設定檔

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
	<!--MsBuild Settings-->
	<PropertyGroup>
		<MsBuildPath>C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe</MsBuildPath>
	</PropertyGroup>

	<!--MsTest Settings-->
	<PropertyGroup>
		<MsTestPath>C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\MSTest.exe</MsTestPath>
		<MsTestResultOutputPath>$(MSBuildStartupDirectory)\TestResult.trx</MsTestResultOutputPath>
		<CodeCoverageResultXmlPath>$(MSBuildStartupDirectory)\CoverageReport.xml</CodeCoverageResultXmlPath>
		<CodeCoverageXslPath>C:\CI_Tools\MSTestCoverageToEmma.xsl</CodeCoverageXslPath>
	</PropertyGroup>

	<!--StyleCop Settings-->
	<UsingTask AssemblyFile="$(MSBuildExtensionsPath)\..\StyleCop 4.7\StyleCop.dll" TaskName="StyleCopTask"/>
	<PropertyGroup>		
		<StyleCopOutputPath>$(MSBuildStartupDirectory)\StyleCopReport.xml</StyleCopOutputPath>
	</PropertyGroup>

	<!--FxCop Settings-->
	<PropertyGroup>
		<FxCopPath>C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\FxCopCmd.exe</FxCopPath>
		<FxCopRulePath>C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\Rules</FxCopRulePath>
		<FxCopResultOutputPath>$(MSBuildStartupDirectory)\FxCopReport.xml</FxCopResultOutputPath>
	</PropertyGroup>

	<!--Simian Settings-->
	<PropertyGroup>
		<SimianPath>C:\Program Files (x86)\Simian\simian-2.3.33.exe</SimianPath>
		<SimianFormatter>xml:$(MSBuildStartupDirectory)\SimianReport.xml</SimianFormatter>
		<SimianThreshold>3</SimianThreshold>
		<SimianLanguage>cs</SimianLanguage>
	</PropertyGroup>

	<!--SourceMonitor Settings-->
	<PropertyGroup>
		<SourceMonitorPath>C:\Program Files (x86)\SourceMonitor\SourceMonitor.exe</SourceMonitorPath>
		<SourceMonitorResultPath>$(MSBuildStartupDirectory)\SourceMonitorReport.xml</SourceMonitorResultPath>
		<SourceMonitorCommand>
			<![CDATA[<sourcemonitor_commands>
        <write_log>false</write_log>
        <command>
          <project_language>CSharp</project_language>
          <project_file>$(MSBuildStartupDirectory)\SourceMonitorResult.smproj</project_file>
          <source_directory>$(MSBuildStartupDirectory)\</source_directory>
          <include_subdirectories>true</include_subdirectories>
          <parse_utf8_files>True</parse_utf8_files>
          <export>
            <export_type>2</export_type>
            <export_file>$(SourceMonitorResultPath)</export_file>
          </export>
        </command>
      </sourcemonitor_commands>]]>
		</SourceMonitorCommand>
		<SourceMonitorSummaryXslPath>C:\CI_Tools\SourceMonitorSummaryGeneration.xsl</SourceMonitorSummaryXslPath>
		<SourceMonitorResultXslPath>C:\CI_Tools\SourceMonitor.xsl</SourceMonitorResultXslPath>
	</PropertyGroup>

	<!--Tools Settings-->
	<PropertyGroup>
		<XslConverterPath>C:\CI_Tools\msxsl.exe</XslConverterPath>
		<CodeCoverageConverterPath>C:\CI_Tools\CodeCoverageConverter\CodeCoverageConverter.exe</CodeCoverageConverterPath>
	</PropertyGroup>
</Project>

同樣的撰寫測試檔來測試

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!--Import Config-->
	<Import Project="$(MSBuildProjectDirectory)\$(ToolSetting)"/>
	
	<Target Name="Build">	
		<Message Text="MsBuild:"  Importance="high" />
		<Message Text="$(MsBuildPath)" />		
		
		<Message Text="MsTest:" Importance="high"/>
		<Message Text="$(MsTestPath)" />
		<Message Text="$(MsTestResultOutputPath)" />
		<Message Text="$(CodeCoverageResultXmlPath)" />
		<Message Text="$(CodeCoverageXslPath)" />
		
		<Message Text="StyleCop:" Importance="high"/>
		<Message Text="$(StyleCopOutputPath)" />
		
		<Message Text="FxCop:" Importance="high"/>
		<Message Text="$(FxCopPath)" />
		<Message Text="$(FxCopRulePath)" />
		<Message Text="$(FxCopResultOutputPath)" />
		
		<Message Text="Simian:" Importance="high"/>
		<Message Text="$(SimianPath)" />
		<Message Text="$(SimianFormatter)" />
		<Message Text="$(SimianThreshold)" />
		<Message Text="$(SimianLanguage)" />
		
		<Message Text="SourceMonitor:" Importance="high"/>
		<Message Text="$(SourceMonitorPath)" />
		<Message Text="$(SourceMonitorResultPath)" />
		<Message Text="$(SourceMonitorCommand)" />
		<Message Text="$(SourceMonitorSummaryXslPath)" />
		<Message Text="$(SourceMonitorResultXslPath)" />
		
		<Message Text="Tools:" Importance="high"/>
		<Message Text="$(XslConverterPath)" />
		<Message Text="$(CodeCoverageConverterPath)" />
		
		<Message Text="Finish" />
	</Target>	
</Project>

執行結果

※撰寫建置流程
在完成設定檔之後,我們可以開始撰寫我們的流程檔,並將之前的設定引入進來,
比較需要注意的是兩個設定檔分別會在不同位置,
BuildSetting.xml會在專案的路徑中,並存入版本控制系統,
ToolSetting.xml則會跟建置流程檔在一起 (我統一放在C:\CI_Tools)

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
	<!--Import Config-->
	<Import Project="$(MSBuildProjectDirectory)\$(BuildSetting)"/>	
	<Import Project="$(MSBuildProjectDirectory)\$(ToolSetting)"/>	
	
	<Target Name="Build">
		<Message Text="INFO: Start Build Project, Enable Build Project: $(EnableBuildProject)"  Importance="high" />
		<CallTarget Targets="BuildProject"/>
		
		<Message Text="INFO: Start UnitTest, Enable UnitTest: $(EnableUnitTest)"  Importance="high" />
		<CallTarget Targets="UnitTest"/>
		
		<Message Text="INFO: Start StyleCop Scan, Enable StyleCop: $(EnableStyleCop)"  Importance="high" />
		<CallTarget Targets="StyleCop"/>
		
		<Message Text="INFO: Start FxCop Scan, Enable FxCop: $(EnableFxCop)"  Importance="high" />
		<CallTarget Targets="FxCop"/>
		
		<Message Text="INFO: Start Simian Scan, Enable Simian: $(EnableSimian)"  Importance="high" />
		<CallTarget Targets="Simian"/>
		
		<Message Text="INFO: Start SourceMonitor Analysis, Enable SourceMonitor: $(EnableSourceMonitor)" Importance="high" />
		<CallTarget Targets="SourceMonitor"/>
	</Target>

	<Target Name="BuildProject" Condition="'$(EnableBuildProject)' == 'True'">
		<Exec Command="$(MsBuildPath) /p:Configuration=Debug "/p:Platform=Any CPU" "$(SolutionPath)""/>
	</Target>	

	<Target Name="UnitTest" Condition="'$(EnableUnitTest)' == 'True'">  
		<!--Create TestContainer Parameters-->
		<CreateItem Include="@(UnitTestDlls->'/testcontainer:%(FullPath)',' ')">
			<Output TaskParameter="Include" ItemName="UnitTestDllFiles" />
		</CreateItem>			

		<!-- Run UnitTest-->
		<Exec Command=""$(MsTestPath)" "/resultsfile:$(MsTestResultOutputPath)" "/testsettings:$(UnitTestSettingPath)" "@(UnitTestDllFiles)"" />

		<!-- Convert CodeCoverage Report To Emma Format -->
		<Exec Command="$(CodeCoverageConverterPath) "$(MSBuildStartupDirectory)\TestResults" "$(CodeCoverageResultXmlPath)"" />
		<Exec Command="$(XslConverterPath) "$(CodeCoverageResultXmlPath)" $(CodeCoverageXslPath) -o "$(CodeCoverageResultXmlPath)"" />
	</Target>	
	
	<Target Name="StyleCop"  Condition="'$(EnableStyleCop)' == 'True'">	
		<!-- Create a collection of files to scan--> 
		<CreateItem Include="$(SourceCodesInclude)" Exclude="$(SourceCodesExclude)">
			<Output TaskParameter="Include" ItemName="StyleCopFiles" />
		</CreateItem>
		
		<StyleCopTask
			ProjectFullPath="$(MSBuildProjectDirectory)"
			SourceFiles="@(StyleCopFiles)"
			ForceFullAnalysis="true"			
			TreatErrorsAsWarnings="true"			
			OutputFile="$(StyleCopOutputPath)"
			CacheResults="true" />
	</Target>

	<Target Name="FxCop" Condition="'$(EnableFxCop)' == 'True'">
		<!--Create FxCop Parameters-->
		<CreateItem Include="@(OutputDlls->'/file:%(FullPath)',' ')">
			<Output TaskParameter="Include" ItemName="OutputDllFiles" />
		</CreateItem>
		
		<Exec Command=""$(FxCopPath)" "@(OutputDllFiles)" "/rule:$(FxCopRulePath)" "/out:$(FxCopResultOutputPath)" /searchgac" />
	</Target>

	<Target Name="Simian" Condition="'$(EnableSimian)' == 'True'">
		<Exec Command=""$(SimianPath)" "$(SourceCodesInclude)" "-formatter=$(SimianFormatter)" -threshold=$(SimianThreshold) -language=$(SimianLanguage) "-excludes=$(SourceCodesExclude)"" IgnoreExitCode="true" />
	</Target>

	<Target Name="SourceMonitor" Condition="'$(EnableSourceMonitor)' == 'True'">
		<!-- Create the command file -->
		<WriteLinesToFile File="$(MSBuildStartupDirectory)\SourceMonitorCommands.xml" Lines="$(SourceMonitorCommand)" Overwrite="true" />
		<!-- Execute SourceMonitor-->
		<Exec Command=""$(SourceMonitorPath)" /C "$(MSBuildStartupDirectory)\SourceMonitorCommands.xml"" />
		<!-- Convert SourceMonitor Report To Html-->
		<Exec Command="$(XslConverterPath) "$(SourceMonitorResultPath)" "$(SourceMonitorSummaryXslPath)" -o "$(MSBuildStartupDirectory)\SourceMonitorSummaryGeneration.xml"" />
		<Exec Command="$(XslConverterPath) "$(MSBuildStartupDirectory)\SourceMonitorSummaryGeneration.xml" "$(SourceMonitorResultXslPath)" -o "$(MSBuildStartupDirectory)\SourceMonitorResult.html"" />
	</Target>

</Project>

使用以下指令進行建置

msbuild "C:\Users\kirkchen\MakeFile\Jenkins\Build.csproj" /p:ToolSetting=ToolSettin
.xml /p:BuildSetting=BuildSetting.xml

執行結果

※總結
有了建置流程檔後,我們以後新增專案不在需要到處尋找沒改到的設定值,
只需要準備好BuildSetting.xml並跟專案一起存入版本控制系統中,
其他所有的建置流程和工具程式設定都統一由ToolSetting.xml和Build.csproj控管並存在CI Server上,
未來就算要修改建置的流程或升級工具程式,也只需要改一個檔案就可以套用到所有專案!
關於今天的內容,如果有任何問題歡迎大家一起討論!


上一篇
CI Server 17 - 使用MsBuild整合常用工作
下一篇
CI Server 19 - 使用Jenkins建置Asp.Net MVC專案
系列文
使用Jenkins打造.Net CI Server30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
ted99tw
iT邦高手 1 級 ‧ 2012-10-26 18:58:21

樓主發功果然了得!!

讚讚讚

我要留言

立即登入留言