title: “解讀 Microsoft.NET.Sdk 的源碼,你能定制各種奇怪而富有創意的編譯過程”
publishDate: 2018-06-30 13:55:39 +0800
date: 2019-07-02 19:42:50 +0800
categories: dotnet visualstudio nuget msbuild
version:
current: 中文
versions:
- 中文: #
- English: /post/read-microsoft-net-sdk-en.html
在 csproj 中,
Project
中的
Sdk
屬性是 MSBuild 15.0 開始支援的,也就是 Visual Studio 2017 開始支援。有了 Sdk 屬性的存在,MSBuild 編譯過程能夠擴充得非常強大,而不止是過去
Import
的一個
props
和
targets
檔案。
本文将介紹 Microsoft.NET.Sdk 的源碼,以及利用源碼中的一些線索來完成官方文檔中沒有提及的功能擴充。
This post is written in multiple languages. Please select yours:
{% include post-version-selector.html %}
Microsoft.NET.Sdk 源碼的位置
在計算機上全局搜尋
Microsoft.NET.Sdk
可以找到不同版本的多個 Sdk 目錄,由于我安裝了 .NET Core 3.0,是以找到的目錄是:
C:\Program Files\dotnet\sdk\3.0.100-preview6-012264
。當然,按照官網 How to: Reference an MSBuild Project SDK 的描述,如果自己實作了一套 Sdk,也可以以 NuGet 包的形式釋出。
[外鍊圖檔轉存失敗(img-xTkPKfnx-1563186795926)(/static/posts/2018-06-30-21-06-06.png)]
▲ 搜尋 Microsoft.NET.Sdk
[外鍊圖檔轉存失敗(img-gVttPC8k-1563186795939)(/static/posts/2018-06-30-21-08-25.png)]
▲ 我計算機上的 Sdk 檔案夾
Sdk 中的 NuGet 部分在 GitHub 上的倉庫位址:
- NuGet.Client/src/NuGet.Core at dev · NuGet/NuGet.Client
Microsoft.NET.Sdk 的目錄結構
在打開看
Microsoft.NET.Sdk
的目錄結構後,我們可以發現這幾乎就是 NuGet 包要求的目錄結構。
[外鍊圖檔轉存失敗(img-lB5oNtQi-1563186795940)(/static/posts/2018-06-30-21-09-29.png)]
關于 NuGet 包的目錄結構,我在下面兩篇文章中都有提到過:
- 如何建立一個基于 MSBuild Task 的跨平台的 NuGet 工具包
- 如何建立一個基于指令行工具的跨平台的 NuGet 工具包
官方對 NuGet 的目錄結構也有介紹:How to create a NuGet package from a convention-based working directory。
不過,Sdk 類型的 NuGet 包會多一個
Sdk
檔案夾。
[外鍊圖檔轉存失敗(img-YaHqfFoZ-1563186795943)(/static/posts/2018-06-30-21-10-19.png)]
Sdk
檔案夾中的
Sdk.props
和
Sdk.targets
是會被預設
Import
的,這一點在官方文檔 How to: Reference an MSBuild Project SDK - Visual Studio 中是有說明的,以下兩段代碼的含義相同:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net48</TargetFramework> </PropertyGroup> </Project>
<Project> <!-- Implicit top import --> <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" /> <PropertyGroup> <TargetFramework>net48</TargetFramework> </PropertyGroup> <!-- Implicit bottom import --> <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" /> </Project>
<Project>
<!-- Implicit top import -->
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
<PropertyGroup>
<TargetFramework>net48</TargetFramework>
</PropertyGroup>
<!-- Implicit bottom import -->
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
</Project>
由于這兩個檔案的預設引入,Sdk 可以完成非常多的編譯任務。而且通常 Sdk 帶有擴充性,使得我們可以很友善地對項目的編譯過程進行擴充,這一點在我前面提到了兩篇制作 NuGet 工具包的文章中都有說明。
Microsoft.NET.Sdk 的主要任務
在 Sdk 檔案夾中搜尋
Target
節點的個數,我得到了 174 個(随 .NET Core 2.1 釋出);不過有一些是同名的,會被重寫(類似于 C#/.NET 中的繼承和重寫);核心的并沒有那麼多。
-
用于收集CollectPackageReferences
收集到的所有依賴(也就是 NuGet 包依賴)PackageReference
-
核心的編譯過程CoreCompile
-
用于生成GenerateAssemblyInfo
檔案(以前可是手工寫的呢)AssemblyInfo.cs
-
用于将目前程式集打包成一個 NuGet 包Pack
-
在打包之前生成 nuspec 檔案GenerateNuspec
定制富有創意的編譯過程
下面是 Microsoft.NET.Sdk 中發現的一些富有創意的編譯過程:
<Target Name="DontRestore" BeforeTargets="Restore">
<Error Text="This project should not be restored" />
</Target>
▲ 如果有
Restore
,那麼讓你編譯不通過
<Target Name="ReferenceStaticLegacyPackage" BeforeTargets="CollectPackageReferences">
<ItemGroup>
<PackageReference Remove="LiteDB" />
<PackageReference Include="LiteDB" Version="2.0.2" />
</ItemGroup>
</Target>
▲ 這是我另外寫的一篇文章:阻止某個 NuGet 包意外更新
參考資料
- How to: Reference an MSBuild Project SDK - Visual Studio - Microsoft Docs