一、MSBuild簡介
MSBuild是一種基于XML的建構引擎,用于在.NET Framework和.NET Core應用程式中自動化建構過程。它是Visual Studio的建構引擎,可在指令行或其他建構工具中使用。
MSBuild使用XML檔案格式來指定項目的編譯、測試和部署等各個階段的任務。這些XML檔案通常以.csproj檔案的形式存儲。每個項目檔案都包含一個或多個 PropertyGroup 元素和一個或多個 ItemGroup 元素。 PropertyGroup 元素聲明和定義屬性,例如目标架構、輸出路徑和程式集名稱等。 ItemGroup 元素建立項,例如NuGet包引用、檔案和資源等。
可以使用 dotnet build 指令或 msbuild 指令來運作MSBuild,它會自動查找與目前目錄相關聯的項目并執行建構過程。你還可以使用/t選項指定一個或多個目标,例如Clean、Build和Publish。
MSBuild支援條件和屬性組合,可以為不同的目标平台自定義建構過程。還可以使用自定義任務,例如運作代碼靜态分析器或壓縮建構輸出檔案等。
二、MSBuild應用場景
MSBuild可以用于多種場景,包括:
- 編譯源代碼 MSBuild可以編譯應用程式中的源代碼檔案,将它們編譯成二進制檔案,以便在部署時使用。它支援多種編譯選項,例如優化、生成調試資訊和設定警告等級等。
- 處理配置檔案和資源檔案 MSBuild還可以處理應用程式的配置檔案和資源檔案,例如App.config和Resources.resx檔案。它可以将這些檔案複制到應用程式目錄中,并将其包含在最終編譯的二進制檔案中。
- 安裝和注冊元件 如果應用程式依賴于其他元件或庫,MSBuild可以自動安裝和注冊這些元件,以確定應用程式能夠正常運作。它也可以從NuGet包管理器中下載下傳和安裝依賴項。
- 打包和部署應用程式 MSBuild可以将應用程式打包成zip檔案或其他格式,以友善部署到其他環境中。它還可以自動部署應用程式到遠端伺服器或雲平台上,例如Azure或AWS。
- 自定義任務 除了執行預定義的任務之外,MSBuild還可以編寫自定義任務,以執行特定的建構任務。例如,你可以編寫一個任務來執行代碼靜态分析或壓縮建構輸出檔案。
三、MSBuild基礎知識
以下是一些MSBuild基礎知識:
屬性
屬性是聲明和定義變量的方式。可以使用 SetProperty 元素或 PropertyGroup元素來定義屬性。例如:
<PropertyGroup>
<MyProperty>MyValue</MyProperty>
</PropertyGroup>
項
項用于表示一個或多個檔案或其他資源。可以使用 ItemGroup 元素和具有Include屬性的元素來建立項。例如:
<ItemGroup>
<Compile Include="*.cs" />
</ItemGroup>
任務
任務是執行建構過程中的特定操作的方式。可以使用 Task 元素和具有TaskName屬性的元素來聲明和定義任務。例如:
<TaskName Parameter1="Value1" Parameter2="Value2" />
運作建構
要在指令行上運作MSBuild,請使用以下指令:
msbuild MyProject.csproj /t:Build /p:Configuration=Debug
該指令将運作名為 "Build" 的建構目标,并使用名為 "Debug" 的配置檔案來建構項目。
四、MSBuild進階用法
MSBuild還有許多進階用法,包括:
- 自定義屬性和條件
在項目檔案中定義自定義屬性和條件,例如定義一個自定義屬性來指定建構輸出目錄:
<Project>
<PropertyGroup>
<OutputDirectory>bin\$(Configuration)\</OutputDirectory>
</PropertyGroup>
...
<Target Name="Build">
<MakeDir Directories="$(OutputDirectory)" />
...
</Target>
</Project>
此時,可以在通用屬性檔案中定義屬性值,然後在不同的項目檔案中包含該通用屬性檔案,以便重複使用自定義屬性。
- 目标依賴關系
在項目檔案中聲明目标之間的依賴關系,例如,在兩個目标之間添加依賴關系:
<Project>
...
<Target Name="Build">
...
</Target>
<Target Name="Test" DependsOnTargets="Build">
...
</Target>
</Project>
這樣,MSBuild在執行 "Test" 目标時,會先執行 "Build" 目标。
- 自定義任務
編寫自定義任務來執行建構過程中的特定任務。例如,使用 MSBuildCommunityTasks 執行 FTP 上傳:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="FTPTask" AssemblyFile="MSBuild.Community.Tasks.dll" />
<Target Name="UploadFile">
<FTPTask ServerHost="ftp.example.com"
RemoteFile="upload.txt"
LocalFile="c:\files\upload.txt"
Username="user"
Password="pass" />
</Target>
</Project>
這個示例中,定義了一個名為 "FTPTask" 的自定義任務,然後在 "UploadFile" 目标中調用該任務來執行 FTP 上傳。
- 條件表達式
使用條件表達式根據條件執行不同的任務。例如,根據平台類型選擇不同的建構配置:
<Project>
...
<Choose>
<When Condition="'$(Platform)' == 'x86'">
<PropertyGroup>
<DefineConstants>DEBUG;X86</DefineConstants>
...
</PropertyGroup>
</When>
<When Condition="'$(Platform)' == 'x64'">
<PropertyGroup>
<DefineConstants>DEBUG;X64</DefineConstants>
...
</PropertyGroup>
</When>
</Choose>
...
</Project>
此示例使用條件表達式 <Choose> 和 <When> 标記,通過判斷 $(Platform) 變量的值來選擇執行不同的 PropertyGroup。
- 自定義建構日志
自定義建構日志以記錄建構過程中的詳細資訊。例如,将建構日志輸出到檔案:
<Project>
<PropertyGroup>
<LogDirectory>logs\</LogDirectory>
</PropertyGroup>
<Target Name="Build">
<Exec Command="dotnet build MyApp.csproj" />
</Target>
<Target Name="LogBuild" AfterTargets="Build">
<Message Text="Writing build log to $(LogDirectory)\build.log" Importance="high" />
<WriteLinesToFile File="$(LogDirectory)\build.log" Lines="$([System.DateTime]::Now.ToString()) - Build succeeded." />
</Target>
</Project>
此示例中,定義了一個名為 "LogBuild" 的目标,并在執行 "Build" 目标之後調用該目标。在 "LogBuild" 目标中,使用 <Message> 元素将日志輸出到控制台,使用 <WriteLinesToFile> 元素将日志寫入日志檔案。
五、MSBuild最佳實踐
以下是MSBuild的一些最佳實踐及代碼示例:
- 使用命名屬性組,以便更好地組織和管理屬性:
<Project>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>DEBUG;TRACE</DefineConstants>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
...
</PropertyGroup>
...
</Project>
在這個示例中,屬性都被包含在一個名為 "Debug|AnyCPU" 的 PropertyGroup 中。這樣可以更好地組織和管理屬性,并且可以使用條件表達式來根據需要選擇不同的屬性組。
- 避免寫死檔案路徑,而是使用相對路徑和通用屬性:
<Project>
<ItemGroup>
<Compile Include="src\**\*.cs" />
...
</ItemGroup>
<PropertyGroup>
<OutputPath>bin\$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), Build.props)\)</OutputPath>
</PropertyGroup>
...
</Project>
在這個示例中,使用相對路徑 "src***.cs" 來指定需要編譯的源代碼檔案,并使用通用屬性來擷取輸出路徑,以避免寫死路徑并提高可移植性。
- 使用預定義的 MSBuild 目标和任務,以便重複使用現有功能:
<Project>
...
<Target Name="Clean">
<MSBuild Projects="$(MSBuildThisFileFullPath)"
Targets="CleanSolution"
Properties="Configuration=$(Configuration)" />
</Target>
...
</Project>
在這個示例中,使用現有的 MSBuild 目标和任務來執行 "Clean" 目标。這樣可以重複使用現有功能并提高建構效率。
- 使用條件表達式來支援多個版本和平台:
<Project>
...
<Choose>
<When Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
<PropertyGroup>
<DefineConstants>NET_CORE_3_1</DefineConstants>
...
</PropertyGroup>
</When>
<When Condition="'$(TargetFramework)' == 'net5.0'">
<PropertyGroup>
<DefineConstants>NET_5_0</DefineConstants>
...
</PropertyGroup>
</When>
</Choose>
...
</Project>
在這個示例中,使用條件表達式根據目标架構選擇執行不同的操作。這樣可以更好地支援多個版本和平台,并保持代碼的簡潔和易于維護。
- 禁用 Visual Studio 的自動格式化選項,以避免與代碼格式化工具沖突:
<Project>
...
<PropertyGroup Condition="'$(BuildingInsideVisualStudio)' == 'true'">
<EnableEditorConfig>false</EnableEditorConfig>
</PropertyGroup>
...
</Project>
在這個示例中,使用 EnableEditorConfig 屬性來禁用 Visual Studio 的自動格式化選項,以避免與代碼格式化工具沖突。這樣可以保持代碼格式化的一緻性,減少錯誤和不必要的麻煩。
參考文檔:https://learn.microsoft.com/zh-cn/visualstudio/msbuild/msbuild-concepts?view=vs-2022