.NET 10平台特定工具与旧版SDK兼容性深度解析

本文深入探讨了.NET 10中平台特定.NET工具的新特性,包括自包含和原生AOT编译工具的优势与局限,并提供了在保持对旧版.NET SDK兼容性的同时利用这些新功能的实用解决方案和实现方法。

支持旧版.NET SDK上的平台特定.NET工具

这是"探索.NET 10预览版"系列的第8篇文章。

.NET 10中.NET工具的演进

在.NET 10中,我们突然有了新特性:

  • dotnet tool exec/dotnet dnx/dnx允许运行.NET工具而无需显式安装
  • 平台特定工具允许将工具打包为自包含甚至原生AOT编译的工具

这些新特性对工具作者意味着什么?

使用dnx的一次性工具运行

作为包作者,您不需要过多考虑这个特性。无论您如何编写包,消费者端的功能都基本相同。

平台特定工具

平台特定工具具有更多细微差别,因为它需要更改.nupkg包本身,而不仅仅是SDK特性。

平台特定工具的三个主要优势:

  1. 更简单的支持矩阵
  2. 减少包大小
  3. 原生AOT工具的更快启动

1. 使用自包含工具简化支持矩阵

使用自包含或NativeAOT工具,整个支持矩阵问题就消失了。您只需要支持单个目标框架,即打包到包中的框架。

2. 减少包大小

由于您只为一个平台和框架打包工具,您可能能够显著减小结果包的大小。

3. 更快启动

如果您原生AOT编译了应用程序,您将始终受益于更快的启动特性。

平台特定工具的缺点

默认情况下,使用平台特定工具意味着消费者必须使用.NET 10 SDK。如果他们在使用早期SDK时尝试安装平台特定工具,会遇到错误。

问题源于包含在.nupkg包中的DotnetToolSettings.xml文件,它告诉.NET SDK如何运行您的应用程序。

对于.NET 9及以下版本,该文件如下:

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<DotNetCliTool Version="1">
  <Commands>
    <Command Name="sayhello" EntryPoint="MultiRid.dll" Runner="dotnet" />
  </Commands>
</DotNetCliTool>

而对于.NET 10平台特定工具,这些使用模式Version=2和不同的Runner:

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<DotNetCliTool Version="2">
  <Commands>
    <Command Name="sayhello" EntryPoint="MultiRid" Runner="executable" />
  </Commands>
</DotNetCliTool>

困境:工具作者应该使用平台特定包吗?

作为工具作者,您需要回答两个问题:

  • 我的工具会从平台特定包中受益吗?
  • 要求用户使用.NET 10 SDK是个问题吗?

两全其美?同一包中的框架依赖和平台特定工具

折衷解决方案如下:

  • 将框架依赖的平台无关版本工具打包到最低支持的目标框架工具文件夹中
  • 在net10.0文件夹中,打包指向所有其他平台特定包的Version=2 DotnetToolSettings.xml文件

使用.NET 10 SDK安装或运行此工具时,SDK将下载包,读取net10.0文件夹中的xml文件,然后立即获取并运行相应的平台特定包。

对于使用早期版本SDK的用户,SDK寻找最高的兼容工具文件夹并读取其包含的xml文件,这是一个标准的Version=1文件。

案例研究1:Datadog dd-trace .NET工具

对于dd-trace工具,平台特定包似乎没有意义:

  • ❌ 更简单的支持矩阵:无论采用哪种方法,我们都要为所有TFM发货
  • ❌ 减少包大小:使用平台特定包时,大小实际上更糟
  • ❌ 原生AOT工具的更快启动:我们不将工具作为NativeAOT工具发货

案例研究2:sleep-pc .NET工具

对于sleep-pc工具,折衷包方法效果很好:

  • 框架依赖的平台无关工具在net8.0文件夹中
  • 在net10.0文件夹中有指向原生AOT编译的sleep-pc.win-x64包的链接
  • “根"包只有17KB

实现折衷包

基本步骤:

  1. 定位所有需要的运行时。必须至少定位.NET 10加上一个或多个旧运行时
  2. 向.csproj文件添加条件,以便仅将自包含/原生AOT属性应用于net10.0目标
  3. 基于RuntimeIdentifier的值有条件地设置<TargetFramework>/<TargetFrameworks>
  4. 运行dotnet pack生成根包,然后为每个支持的运行时运行dotnet pack -r <runtime>

示例.csproj文件展示了具体实现方法。

替代折衷方案

Chet Husk在Aspire CLI中采用了略有不同的方法:

  • 工具定位单个目标框架,任何.NET 9或以下版本
  • 框架依赖的平台无关版本工具嵌入在"根"包中
  • 嵌入根包中的DotnetToolSettings.xml文件被调整为看起来像是Version=1文件和Version=2文件之间的交叉

总结

在本文中,我讨论了新的平台特定NuGet包支持对包作者意味着什么,以及如何在仍然支持.NET 10之前SDK版本用户的同时利用它们。

我描述了一个潜在的折衷方案,其中.NET 10 SDK用户可以使用平台特定工具,而.NET 10之前SDK用户继续使用框架依赖的平台无关版本工具。这有一些权衡,但在某些情况下可能仍然值得。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计