我們在前面幾篇文章之中,將專案的建置過程整合了許多測試及分析的流程,
讓每天都能夠得到專案最新的測試狀況,以及各種靜態程式碼分析報表,
而若要在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上,
未來就算要修改建置的流程或升級工具程式,也只需要改一個檔案就可以套用到所有專案!
關於今天的內容,如果有任何問題歡迎大家一起討論!