【譯】在 Linux 上不安裝 Mono 構建 .NET Framework 類庫
在這篇文章中,我展示瞭如何在Linux上構建針對.NET Framework版本的.NET專案,而不使用Mono。通用使用微軟新發布的 Mocrosoft.NETFramework.ReferenceAssemblies NuGet 包,您將不需要安裝除 .NET Core SDK 之外的任何其他軟體包!總的來說,在 Linux 系統上構建 .NET Framework 類庫時,只需要在你的專案檔案中新增下面的節點即可:
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" PrivateAssets="All" Version="1.0.0-preview.2" />
背景:在 Linux 上構建 full-framework 類庫
如果您正在構建 .NET Standard NuGet 軟體包,並且希望為使用者提供最佳體驗(並避免一些依賴性),那麼您將需要檢視有關跨平臺定位的建議。那裡有很多可以做和不可以做的建議,但我傾向於將其歸結為:如果您的目標是任何版本的 .NET Standard,那麼您至少需要以下目標框架:
<TargetFrameworks>netstandard2.0;net461;net472</TargetFrameworks>
如果您的目標是.NET Standard 1.x,那麼將其新增到構建目標中時,重要的是包含兩個 .NET Framework 目標以避免 .NET Standard 2.0 墊片出現問題。
這就引出了一些問題 - 完整的 .NET 框架目標理論上只能在 Windows 上構建。在上一篇文章中,我展示瞭如何通過安裝 Mono 並使用它提供的程式集來解決這個問題。在那篇文章中,我也展示了在 Linux 上執行 .NET Framework 測試套件。到目前為止,這對我來說非常有效,但它有幾個缺點:
- 它需要你安裝 Mono
- 它需要新增一個有點hacky .props檔案
- 它沒有得到官方的支援,所以如果它不起作用,你就得自己想辦法
.props 檔案將 FrameworkPathOverride MSBuild 變數設定為 Mono 引用程式集,這是我們能夠構建的方式。但正如 Jon Skeet在評論中指出的那樣,Mono 實際上並不是必需的。我們只需要一種方法來輕鬆獲取要編譯的引用程式集。那就是微軟提供的 Microsoft.NETFramework.ReferenceAssemblies 包。
通過 NuGet 使用程式集 Microsoft.NETFramework.ReferenceAssemblies
在我看到 Muhammad Rehan Saeed 的這條推文之前,我完全沒有注意到 Microsoft.NETFramework.ReferenceAssemblies NuGet 包。這是一條非常棒的資訊,構建 full .NET Framework 類庫只需要在 Linux 上安裝 .NET Core SDK !
我們來舉個例子吧。
您可以使用 dotnet new classlib 建立一個 .NET Core 類庫,它將提供一個如下所示的 .csproj 檔案:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFrameworks>netstandard2.0</TargetFrameworks> </PropertyGroup> </Project>
修改 .csproj 專案檔案,新增更多的構建目標:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFrameworks>netstandard2.0;net461;net472</TargetFrameworks> </PropertyGroup> </Project>
如果此時嘗試在 Linux 上使用 dotnet build ,您將看到類似以下內容的錯誤:
/usr/share/dotnet/sdk/2.1.700/Microsoft.Common.CurrentVersion.targets(1175,5): error MSB3644: The reference assemblies for framework ".NETFramework,Version=v4.6.1" were not found. To resolve this, install the SDK or Targeting Pack for this framework version or retarget your application to a version of the framework for which you have the SDK or Targeting Pack installed. Note that assemblies will be resolved from the Global Assembly Cache (GAC) and will be used in place of reference assemblies. Therefore your assembly may not be correctly targeted for the framework you intend.
終於到了見證奇蹟的時刻。新增 Microsoft.NETFramework.ReferenceAssemblies NuGet 包到你的專案檔案:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFrameworks>netstandard2.0;net461;net472</TargetFrameworks> </PropertyGroup> <!-- Add this reference--> <ItemGroup> <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" PrivateAssets="All" Version="1.0.0-preview.2" /> </ItemGroup> </Project>
再次執行 dotnet build ,竟然構建成功了。
Build succeeded. 0 Warning(s) 0 Error(s)
使用 PrivateAssets 特性可防止 Microsoft.NETFramework.ReferenceAssemblies 包“洩漏”到依賴專案或已釋出的 NuGet 包中;它只是一個構建時依賴。
只需將軟體包新增到專案中就可以獲得比安裝 Mono 更好的體驗。最重要的是,這將是從現在開始支援的方法。但它變得更好 - 在 .NET Core 3.0 SDK 中,必要時 .NET Core SDK 將自動引用此包,所以,理論上根本不需要我們手工編輯專案檔案新增配置節點去構建 .NET Framework 專案!
它是如何工作的:a meta-package, a .targets, and lots of dlls
如果你對包的工作方式感興趣,我建議你閱讀相關的問題,但我會在這裡提供一個高階的大綱。
我們將從 Microsoft.NETFramework.ReferenceAssemblies NuGet 包本身開始。這個包是一個不包含程式碼的元包,但是對於每個受支援的 .NET Framework 版本都依賴於不同的 NuGet 包:
例如:以 .NET Framework 4.6.1 為目標時將依賴 Microsoft.NETFramework.ReferenceAssemblies.net461。此方法可以確保您只會下載到與目標框架一致的 NuGet 包。
如果你開啟一個特定 Framework 的 NuGet 包,你會在 build 資料夾中發現兩件事:
- 一個 .targets 檔案
- 一個 .NETFramework 資料夾,其中包含引用的全部程式集(> 100MB)
.targets 檔案與我上一篇文章中的 .props 檔案的用途類似 - 它告訴 MSBuild 在哪裡可以找到框架庫。以下示例來自.NET 4.6.1包:
<Project> <PropertyGroup Condition=" ('$(TargetFrameworkIdentifier)' == '.NETFramework') And ('$(TargetFrameworkVersion)' == 'v4.6.1') "> <TargetFrameworkRootPath>$(MSBuildThisFileDirectory)</TargetFrameworkRootPath> <!-- FrameworkPathOverride is typically not set to the correct value, and the common targets include mscorlib from FrameworkPathOverride. So disable FrameworkPathOverride, set NoStdLib to true, and explicitly reference mscorlib here. --> <EnableFrameworkPathOverride>false</EnableFrameworkPathOverride> <NoStdLib>true</NoStdLib> </PropertyGroup> <ItemGroup Condition=" ('$(TargetFrameworkIdentifier)' == '.NETFramework') And ('$(TargetFrameworkVersion)' == 'v4.6.1') "> <Reference Include="mscorlib" Pack="false" /> </ItemGroup> </Project>
這會將 TargetFrameworkRootPath 引數設定為包含 .targets 檔案的資料夾。MSBuild 遍歷 NuGet 包的資料夾結構(.NETFramework \ v4.6.1),找到dll,並找到引用程式集:
我已經使用.NET Core 2.1 SDK 和 2.2 SDK 測試了這些軟體包,並且它的工作時間都非常出色。試試看!
Reference
- GitHub Issue: Build for desktop framework on non-windows platforms
- PR for the Microsoft.NETFramework.ReferenceAssemblies package design
- Blog post: Building .NET Framework ASP.NET Core apps on Linux using Mono and the .NET CLI
- Example of replacing approach from previous post to use approach in this post
&n