Essential MSBuild: .NET 工具生成引擎概述
過去幾年大家一直都在使用 .NET Core(有這么久嗎?)并且都知道“生成系統”經歷了重大改變,不論是終止對 Gulp 的內置支持,還是放棄 Project.json。對于我這個專欄作家來說,這些變化一直很棘手,因為我不想我親愛的讀者花了很多時間來了解這些功能和詳情,最后卻只能使用短短幾個月。這就是為什么我所有的 .NET Core 相關文章都以基于 Visual Studio .NET 4.6 的 *.CSPROJ 文件為基礎撰寫,這些文件引用 .NET Core 中的 NuGet 包,而非實際編譯的 .NET Core 項目。
本月,我很榮幸向大家宣布 .NET Core 項目的項目文件已經確定為(你會相信嗎) MSBuild 文件。但這是與前幾代 Visual Studio 不同的 MSBuild 文件,是經過改進和簡化的 MSBuild 文件。這是一個包括 Project.json 所有功能(而不陷入波形括號與尖括號的宗教戰爭),但是配備自 Visual Studio 2005 以來我們知曉(并且可能喜愛?)的傳統 MSBuild 文件的工具支持的文件。總之,這些功能包括開源、跨平臺兼容性、簡化的人類可編輯的格式以及完全現代的 .NET 工具支持(包括通配符文件引用)。
工具支持
要清楚,MSBuild 始終支持通配符等功能,但是現在 Visual Studio 工具也同樣支持。換句話說,MSBuild 最大的亮點在于它被緊密集成為所有新 .NET 工具—DotNet.exe、Visual Studio 2017、Visual Studio Code 和 Visual Studio for Mac—的生成系統基礎并且支持 .NET Core 1.0 和 .NET Core 1.1 運行時。
.NET 工具和 MSBuild 之間強大耦合的巨大優勢在于你創建的任何 MSBuild 文件與所有 .NET 工具兼容,并且可從任何系統生成。
用于 MSBuild 集成的 .NET 工具通過 MSBuild API 而非命令行過程耦合。例如,執行 .NET CLI command Dotnet.exe Build 不會在內部生成 msbuild.exe 過程。但是,這不會調用過程中的 MSBuild API 來執行工作(MSBuild.dll 和 Microsoft.Build.* 程序集)。即便如此,不論工具如何,輸出在各平臺之間均相似,因為存在共享的日志記錄框架,所有 .NET 工具均在其中注冊。
*.CSPROJ/MSBuild 文件結構
正如我所提到的,文件格式本身被簡化為極簡。它支持通配符、項目和 NuGet 包引用以及多個框架。此外,在 Visual Studio 過去創建的項目文件中發現的項目類型 GUID 已經消失。
圖 1?顯示 *.CSPROJ/MSBuild 文件示例。
圖 1 CSProj/MSBuild 文件基本示例
<Project>? <PropertyGroup>??? <TargetFramework>netcoreapp1.0</TargetFramework>? </PropertyGroup>? <ItemGroup>??? <Compile Include="**\*.cs" />??? <EmbeddedResource Include="**\*.resx" />? </ItemGroup>? <ItemGroup>??? <PackageReference Include="Microsoft.NETCore.App">????? <Version>1.0.1</Version>??? </PackageReference>??? <PackageReference Include="Microsoft.NET.Sdk">????? <Version>1.0.0-*</Version>????? <PrivateAssets>All</PrivateAssets>??? </PackageReference>? </ItemGroup>? <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /></Project>讓我們來詳細地回顧下結構和功能:
簡化了的標頭: 首先要注意的是根元素僅僅是一個項目元素。甚至對命名空間和版本屬性的需要也已經消失:
ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"(盡管它們是通過候選發布版本工具創建的。)? 同樣地,甚至導入常見屬性的需要也是可選的:
<Import Project=? "$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />項目引用: 從項目文件可以將條目添加到項目組元素:
NuGet 包:
項目引用:
程序集引用:
直接的程序集引用應該屬于例外情況,因為通常優先采用 NuGet 引用。
通配符包括: 可以通過通配符將編譯的代碼文件和資源文件均包括在內。
<Compile Include="**\*.cs" /><EmbeddedResource Include="**\*.resx" /><Compile Remove="CodeTemplates\**" /><EmbeddedResource Remove="CodeTemplates\**" />但是,你仍可使用刪除屬性選擇要忽略的特定文件。(注意對通配符的支持很多時候被稱為通配。)
多重目標: 要找出你所針對的平臺,可以使用屬性組、TargetFramework 元素以及輸出類型(可選):
<PropertyGroup>? <TargetFramework>netcoreapp1.0</TargetFramework>? <TargetFramework>netstandard1.3</TargetFramework></PropertyGroup>鑒于這些條目,每個目標的輸出將被生成到 bin\Debug or bin\Release 目錄(取決于你指定的配置)。如果目標不止一個,則生成執行會將輸出置于與目標框架名稱對應的文件夾中。
無項目類型 GUID: 注意不再需要包括識別項目類型的項目類型 GUID。
Visual Studio 2017 集成
至于 Visual Studio 2017,Microsoft 繼續提供用于編輯 CSPROJ/MSBuild 項目文件的富 UI。例如,圖 2?顯示了使用 CSPROJ 文件列表加載的 Visual Studio 2017,它在圖 1?的基礎上稍做修改,包括 netcoreapp1.0 和 net45 的目標框架元素、Microsoft.Extensions.Configuration、Microsoft.NETCore.App 和 Microsoft.NET.Sdk 的包引用以及對 MSBuild 的程序集引用和對 SampleLib 的項目引用。???????????????????????????????????????????????????????????????????????????????????????????????????????????
?
圖 2 解決方案資源管理器是基于 CSProj 文件的富 UI
注意在解決方案資源管理器中的依賴項樹中,每個依賴項類型—程序集、NuGet 包或項目引用—如何擁有相應的組節點。
此外,Visual Studio 2017 支持項目和解決方案的動態重新加載。例如,如果新文件被加載到項目目錄—與其中一個通配符相匹配的目錄—Visual Studio 2017 會自動檢測更改并在解決方案資源管理器中顯示文件。同樣地,如果你從 Visual Studio 項目排除一個文件(通過 Visual Studio 菜單選項或 Visual Studio 屬性窗口),Visual Studio 將相應地自動更新項目文件。(例如,它將添加一個 <Compile Remove="CommandLine.cs" /> 元素來避免編譯項目中的 CommandLine.cs 文件。)?
此外,將自動檢測對項目文件的編輯并將其重新加載到 Visual Studio 2017。事實上,解決方案資源管理器中的 Visual Studio 項目節點現在支持內置編輯 <Project File> 菜單選項,通過此選項可在 Visual Studio 編輯窗口中打開項目文件,而無需首先請求卸載項目。
還支持在 Visual Studio 2017 中進行內置遷移,將項目轉換為新的 MSBuild 格式。如果你接受其提示,你的項目將自動從 Project.json/*.XPROJ 類型升級到 MSBUILD/*.CSPROJ 類型。注意此類升級將破壞與 Visual Studio 2015 .NET Core 的向后兼容性,所以在他人使用 Visual Studio 2015 的同時,不能讓團隊成員在 Visual Studio 2017 中處理同一個 .NET Core 項目。
MSBuild
有一點需要提醒你注意的是,2016 年 3 月,Microsoft 在 GitHub 上將 MSBuild 發布為開源 (github.com/Microsoft/msbuild) 并將其貢獻給了 .NET Foundation (dotnetfoundation.org)。對于到 Mac 和 Linux 的平臺可移植性,將 MSBuild 設為開源有利于回到正軌,最終允許其成為所有 .NET 工具的基礎生成引擎。
與之前發現的 CSPROJ\MSBuild 文件 PackageReference 元素不同,MSBuild 版本 15 除了開源和跨平臺未引入太多其他功能。實際上,通過比較命令行幫助,結果顯示選項相同。對于那些尚不熟悉它的人來說,下面列出了你應熟悉的 syntax MSBuild.exe [options] [project file] 的最常用選項:
/target:<target>:在項目文件中找出要與其可能擁有的任何依賴項一起執行的生成目標(縮寫為 /t)。
/property:<n>=<v>: 設置或替代任何項目屬性(在項目文件的 ProjectGroup 元素中找到)。例如,你可以使用該屬性來更改配置或輸出目錄,如在 /property:Configuration=Release;OutDir=bin\ 中(縮寫為 /p)。
/maxcpucount[:n]: 指定要使用的 CPU 的數量。默認情況下,msbuild 在單個 CPU 上運行(單線程)。如果可以同步的話,則可以通過指定并發級別來增加數量。如果在不提供值的情況下指定 /maxcpucount 選項,msbuild 將使用計算機上的處理器數量。
/preprocess[:file]: 通過初始化所有包含的目標來生成聚合的項目文件。這樣在出現問題時有利于調試。
@file: 提供一個(或多個)包含選項的響應文件。此類文件在單獨的行上擁有命令行選項(注釋要添加“#”前綴)。默認情況下,MSBuild 將從生成的第一個項目或解決方案導入名為 msbuild.rsp 的文件。響應文件有助于找出不同的生成屬性和目標,具體取決于生成的環境(研發、測試、生產)等。
Dotnet.exe
大約一年前引入了 .NET 的 dotnet.exe 命令行并將其作為生成、構建和運行基于 .NET Core 的項目的跨平臺機制。正如我所提到的,dotnet.exe 已經得到更新,現在很大程度上依賴于MSBuild 作為內部引擎來在可行的情況下處理大量工作。
以下是對各種命令的概述:
dotnet new: 創建你的初始項目。此項目生成器開箱即用,支持 Console、Web、Lib、MSTest 和 XUnitTest 項目類型。但是將來有望提供自定義模板,從而能夠生成你自己的項目類型。(這一功能可用時,新的命令將不再依賴于 MSBuild 生成項目。)
dotnet restore: 讀取在項目文件中指定的項目依賴項并下載任何缺失的 NuGet 包和其中發現的工具。項目文件本身可以被指定為參數或從當前目錄暗示(如果當前目錄中的項目文件不止一個,則要求指定要使用哪個)。注意因為恢復利用 MSBuild 引擎處理工作,所以 dotnet 命令允許其他 MSBuild 命令行選項。
dotnet build: 在項目文件中調用 MSBuild 引擎執行生成目標(默認情況下)。如同恢復命令一樣,你可以將 MSBuild 參數傳遞到 dotnet 生成命令。例如,命令 dotnet build /property:configuration=Release 將觸發要輸出的 Release 生成,而不是 Debug 生成(默認情況下)。同樣地,你可以使用 /target(或 /t)指定 MSBuild 目標。例如,dotnet build /t:compile 命令將運行編譯目標。
dotnet clean: 刪除所有生成輸出,以便執行完全生成而非增量生成。
dotnet migrate: 將基于 Project.json/*.XPROJ 的項目升級為 *.CSPROJ/MSBuild 格式。
dotnet publish: 連同任何依賴項將所有生成輸出組合到單個文件夾,從而將其暫存以部署到另一臺計算機。這對不僅包含編譯輸出和依賴包,而且包含 .NET Core 運行時本身的自包含部署來說尤其有用。自包含應用程序沒有設定特定版本的 .NET 平臺已經安裝到了目標計算機上的任何前提條件。
dotnet run: 啟動 .NET 運行時并托管項目和/或編譯的程序集以執行你的程序。注意對于 ASP.NET 來說,編譯并不如可以托管項目本身一樣必要。
在執行 msbuild.exe 和 dotnet.exe 之間存在重大重疊,需要你選擇運行哪個。如果你生成的是默認 msbuild 目標,則僅可從項目目錄內執行命令“msbuild.exe”,它將為你編譯并輸出目標。等同的 dotnet.exe 命令為“otnet.exe msbuild”。 另一方面,如果你運行的是“清理”目標,則命令是“msbuild.exe /t:clean”(對于 MSBuild)和“dotnet.exe clean”(對于 dotnet)。此外,兩個工具均支持擴展名。MSBuild 擁有在項目文件本身內和通過 .NET 程序集的綜合性擴展性框架(請參閱?bit.ly/2flUBza)。同樣地,可以擴展 dotnet,但對此的建議基本都涉及擴展 MSBuild 和一些規定的步驟。
雖然我喜歡 dotnet.exe 這個想法,但是最后它似乎未提供太多的 MSBuild 優勢,MSBuild 不支持的除外(其中 dotnet new 和 dotnet run 可能是最重要的)。最后,我認為通過 MSBuild,可以輕松地執行簡單的操作,同時在需要時仍可執行復雜的操作。此外,通過提供合理的默認值,即使是 MSBuild 中復雜的操作也可以被簡化。最后,是 dotnet 還是 MSBuild 更可取由個人喜好決定,而時間將會告訴我們通常會為 CLI 前端選用哪個開發社區。
global.json:
雖然 Project.json 功能已遷移到 CSPROJ,但仍完全支持 global.json。文件允許指定項目目錄和包目錄并識別要使用的 SDK 版本。下面是 global.json 文件的示例:
{"projects": [ "src", "test" ],"packages": "packages","sdk": {"version": "1.0.0-preview3","runtime": "clr","architecture": "x64"? } }有三個部分與 global.json 文件的主要目的一致:
projects: 識別 .NET 項目所在的根目錄。項目節點對于調試 .NET Core 源代碼來說極為關鍵。在克隆源代碼后,你可以將目錄添加到項目節點,然后 Visual Studio 將在解決方案中自動將其作為項目上載。
packages: 指示 NuGet 包文件夾的位置。
sdk: 指定要使用的運行時的版本。
總結
在本文中,我寬泛地概述了在 .NET 工具套件中使用 MSBuild 的所有情形。結束之前,請允許我基于我使用成千上萬行 MSBuild 處理項目的經驗,為大家提供些許建議。別落入使用 XML MSBuild 架構等松散類型聲明性語言編寫腳本的圈套。這不是它的用途。項目文件應該是相對較薄的包裝器,能夠識別生成目標之間的順序和依賴項。如果你的 MSBuild 項目文件太大,那么維護起來可能會比較痛苦。請勿耽擱太久,立即將其重構為可調試和可輕松通過裝置測試的 C# MSBuild 任務。
原文地址:https://msdn.microsoft.com/zh-cn/magazine/mt791801
.NET社區新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注
總結
以上是生活随笔為你收集整理的Essential MSBuild: .NET 工具生成引擎概述的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Visual Studio 2017 离
- 下一篇: 个性化配置你的SQL Server on