天呐!你知道MSBuild都干了些什么
一個典型的.NET5.0項目文件是這樣的,看著非常簡潔:
<Project?Sdk="Microsoft.NET.Sdk.Web"><PropertyGroup><TargetFramework>net5.0</TargetFramework></PropertyGroup><ItemGroup><PackageReference?Include="Swashbuckle.AspNetCore"?Version="5.6.3"?/></ItemGroup></Project>但是,當(dāng)我們執(zhí)行“生成”時,卻可以看到輸出了大量日志,完全不知道這些目標(biāo)都是哪來的??
我們知道,生成操作實際是由MSBuild執(zhí)行的。
那么,MSBuild到底干了什么?
查看日志
雖然,只要你在選項里設(shè)置日志級別為“診斷”,項目生成時會輸出非常詳細的日志記錄:?
但是,這樣生成的文本日志量太大了,要找出需要的信息難如登天。
這時,我們可以使用“MSBuild結(jié)構(gòu)化日志查看器”,以可視化的方式查看日志。
安裝
查看器的安裝依賴Chocolatey。
首先,以管理員身份打開命令提示符,運行下列命令安裝Chocolatey:
Set-ExecutionPolicy?Bypass?-Scope?Process?-Force;?[System.Net.ServicePointManager]::SecurityProtocol?=?[System.Net.ServicePointManager]::SecurityProtocol?-bor?3072;?iex?((New-Object?System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))然后,運行下列命令安裝日志查看器:
choco?install?msbuild-structured-log-viewer生成日志
打開MSBuild Structured Log Viewer,選擇“Open Project/Solution”,打開我們新建的Web API示例項目WebApplication1.sln,點擊“Build”按鈕生成日志:
運行完成后,你應(yīng)該可以看到如下內(nèi)容:
點擊項目名稱左邊的箭頭展開后,可以看到MSBuild準(zhǔn)備執(zhí)行的所有目標(biāo),每個目標(biāo)中包含多個任務(wù):
灰色的表示跳過的目標(biāo),展開后可以看到跳過的原因。
下面,我們以bin\Debug\net5.0\Swashbuckle.AspNetCore.Swagger.dll文件怎么輸出的為例,演練如何分析日志。
分析日志
Copy任務(wù)
在左側(cè)Search Log窗口上方,輸入bin\Debug\net5.0\Swashbuckle.AspNetCore.Swagger.dll作為條件:
可以看到文件是由_CopyFilesMarkedCopyLocal目標(biāo)中的Copy任務(wù)生成的,選中后在中間Log窗口雙擊任務(wù)名,會在右側(cè)窗口顯示任務(wù)詳情,原來任務(wù)來源于MSBuild\Current\Bin\Microsoft.Common.CurrentVersion.targets文件。
Copy任務(wù)作用是將源文件ReferenceCopyLocalPaths復(fù)制到目標(biāo)文件$(OutDir)%(DestinationSubDirectory)%(Filename)%(Extension)
那么源文件和目標(biāo)文件的值,又是從哪來的呢?
OutDir屬性
我們可以輕易地查找到$(OutDir)的值等于bin\Debug\net5.0,卻沒看到bin\Debug\net5.0這個值是由誰賦給它的:
通過左側(cè)的Find In Files窗口,原來它來自于MSBuild\Current\Bin\amd64\Microsoft.Common.CurrentVersion.targets文件,從OutputPath賦值:
metaproj文件
OutputPath的值來源于同一個文件,等于$(BaseOutputPath)$(Configuration)\
而BaseOutputPath也來源于這個文件。但奇怪的是,Configuration卻來源于一個叫做WebApplication1.sln.metaproj的文件:
項目目錄下并沒有這個文件啊?!
隨后,我們在日志中找到這樣一條消息:
已生成元項目“D:\Codes\WebApplication1\WebApplication1.sln.metaproj”。而且,在WebApplication1.sln.metaproj中,我們還可以找到Rebuild目標(biāo):
而Rebuild又依賴于其他目標(biāo):
你還記得生成日志時,帶的/t:Rebuild參數(shù)嗎?
現(xiàn)在清楚了,MSBuild啟動時首先生成.metaproj文件,然后根據(jù)文件中的元數(shù)據(jù),按照依賴關(guān)系執(zhí)行目標(biāo)。
DestinationSubDirectory屬性
但是,%(DestinationSubDirectory)在日志里并沒有找到任何賦值的位置。
試著繼續(xù)探索原始文件來源,最終定位到了ResolvePackageAssets目標(biāo)下的ResolvePackageAssets任務(wù):
具體參數(shù)值對應(yīng)任務(wù)的輸出參數(shù)RuntimeAssemblies:
<Output?TaskParameter="RuntimeAssemblies"?ItemName="RuntimeCopyLocalItems"?/>查看dotnet/sdk/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageAssets.cs的源碼,RuntimeAssemblies的類型是ITaskItem[]。
ITaskItem定義如下:
public?interface?ITaskItem {string?ItemSpec?{?get;?set;?}int?MetadataCount?{?get;?}ICollection?MetadataNames?{?get;?}IDictionary?CloneCustomMetadata();void?CopyMetadataTo(ITaskItem?destinationItem);string?GetMetadata(string?metadataName);void?RemoveMetadata(string?metadataName);void?SetMetadata(string?metadataName,?string?metadataValue); }接著,我們找到這樣一段代碼:
if?(!string.IsNullOrEmpty(destinationSubDirectory)) {WriteMetadata(MetadataKeys.DestinationSubDirectory,?destinationSubDirectory); }DestinationSubDirectory原來是Metadata啊!
結(jié)論
根據(jù)上面的分析,可以梳理出bin\Debug\net5.0\Swashbuckle.AspNetCore.Swagger.dll文件如何輸出的整個流程:
MSBuild啟動,根據(jù)項目文件生成.metaproj文件
MSBuild根據(jù)/t參數(shù), 從.metaproj文件中讀取目標(biāo)
根據(jù)目標(biāo)的依賴關(guān)系,按順序執(zhí)行其他目標(biāo)
其中,ResolvePackageAssets目標(biāo)下的ResolvePackageAssets任務(wù)獲取項目所有依賴包的Metadata
再由_CopyFilesMarkedCopyLocal目標(biāo)中的Copy任務(wù)遍歷依賴包,根據(jù)Metadata復(fù)制文件到指定目錄下的指定文件名
現(xiàn)在,你可以跟同事show一下:我知道MSBuild干了什么!
如果你覺得這篇文章對你有所啟發(fā),請關(guān)注我的個人公眾號”My IO“,記住我!
總結(jié)
以上是生活随笔為你收集整理的天呐!你知道MSBuild都干了些什么的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: dotnet中的counters说明(三
- 下一篇: .Net Core with 微服务 -