【譯】在 Linux 上不安裝 Mono 構建 .NET Framework 類庫

  • 2019 年 10 月 3 日
  • 筆記

在這篇文章中,我展示了如何在 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 支援的方法。並且在 .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

 

原文鏈接:https://andrewlock.net/using-reference-assemblies-to-build-net-framework-libararies-on-linux-without-mono/