.NET 10新特性:打包独立和原生AOT .NET工具到NuGet
介绍
这是"探索.NET 10预览版"系列的第7篇文章。在本文中,我们将探讨.NET 10中添加的平台特定.NET工具功能支持。这个新功能允许您以多种不同方式打包工具——包括独立部署、裁剪和使用原生AOT——反映了当今发布.NET应用程序的多种不同方式。
.NET工具部署方式
在深入新功能之前,我们首先需要了解当前发布.NET应用程序的一些方式:
- 框架依赖可执行文件:目标机器必须安装.NET运行时,发布的应用程序较小
- 独立可执行文件:发布的应用程序包含所有需要的.NET运行时二进制文件,体积很大
- 裁剪的独立可执行文件:二进制文件被裁剪以移除未使用的代码,显著减小发布应用程序的大小
- 原生AOT可执行文件:原生提前编译应用程序启动快速、内存占用小,不需要在主机上安装.NET运行时
另一个需要考虑的维度是部署是"平台无关"还是"平台特定"。
.NET 10中的新部署模型
在.NET 10之前,只能使用"框架依赖"部署模型发布.NET工具。.NET 10预览版6宣布支持使用更多部署模型发布.NET工具:
- 框架依赖、平台无关(当前工作方式)
- 框架依赖、平台特定
- 独立、平台特定
- 裁剪、平台特定
- 原生AOT编译、平台特定
示例应用程序
为了测试,我基于Chet Husk的multi-rid-tool创建了一个示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<CopyOutputSymbolsToPublishDirectory>false</CopyOutputSymbolsToPublishDirectory>
<PackAsTool>true</PackAsTool>
<ToolCommandName>sayhello</ToolCommandName>
<PackageId>sayhello</PackageId>
<PackageVersion>1.0.0</PackageVersion>
<Authors>Andrew Lock</Authors>
<Description>A tool that says hello</Description>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Spectre.Console" Version="0.50.0" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.8" />
</ItemGroup>
</Project>
|
Program.cs包含确保使用依赖项的简单代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
using Microsoft.Data.Sqlite;
using Spectre.Console;
using var connection = new SqliteConnection("Data Source=:memory:");
connection.Open();
var command = connection.CreateCommand();
command.CommandText = "SELECT 'world'";
using var reader = command.ExecuteReader();
while (reader.Read())
{
var name = reader.GetString(0);
var figlet = new FigletText($"Hello {name}!")
.Centered()
.Color(Color.Green);
AnsiConsole.Write(figlet);
}
|
框架依赖、平台无关
对于上述应用程序,默认情况下,当您运行dotnet pack
时,将获得框架依赖、平台无关的包:
1
|
dotnet pack -o ./artifacts/packages/agnostic
|
这会产生一个单一的大包,包含每个目标框架的重复文件,每个目标框架包含所有支持平台的本地文件。
框架依赖、平台特定
要生成平台特定的NuGet包,我们只需在项目中添加以下属性组:
1
2
3
4
5
|
<!-- specific -->
<PropertyGroup>
<RuntimeIdentifiers>linux-x64;linux-arm64;win-x64;win-arm64;any</RuntimeIdentifiers>
<PublishSelfContained>false</PublishSelfContained>
</PropertyGroup>
|
运行打包命令:
1
|
dotnet pack -o ./artifacts/packages/specific
|
这次我们得到6个不同的包:
- 一个用于工具的"顶级"NuGet包
- 项目文件中指定的4个特定运行时ID的包
- 一个"any"包,用于没有专用包的平台
独立、平台特定
对于下一个排列,我们构建一个独立应用程序,将.NET运行时捆绑在包中:
1
2
3
4
5
|
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<RuntimeIdentifiers>linux-x64;linux-arm64;win-x64;win-arm64</RuntimeIdentifiers>
<PublishSelfContained>true</PublishSelfContained>
</PropertyGroup>
|
独立、裁剪
要创建裁剪的NuGet包,您只需要在我们之前的配置上添加<PublishTrimmed>
属性:
1
2
3
4
5
6
7
|
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<RuntimeIdentifiers>linux-x64;linux-arm64;win-x64;win-arm64</RuntimeIdentifiers>
<PublishSelfContained>true</PublishSelfContained>
<!-- 👇 Add this -->
<PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>
|
原生AOT
我们将工具更新为原生AOT构建,为了减小包的大小,我们还将剥离符号:
1
2
3
4
5
6
7
8
|
<PropertyGroup>
<RuntimeIdentifiers>linux-x64;linux-arm64;win-x64;win-arm64;</RuntimeIdentifiers>
<PublishSelfContained>true</PublishSelfContained>
<PublishTrimmed>true</PublishTrimmed>
<!-- 👇 Add these -->
<PublishAot>true</PublishAot>
<StripSymbols>true</StripSymbols>
</PropertyGroup>
|
为原生AOT发布比其他包更复杂,因为您只能在运行的同一平台上进行原生AOT编译。
限制和建议
所有新的平台特定包类型的主要限制是它们仅在您使用.NET 10 SDK时有效。如果您尝试安装任何使用版本2的<DotNetCliTool>
元素的包,您将收到类似以下的错误。
考虑到这个要求,根据您创建的.NET工具的具体情况,创建平台特定工具的好处可能有限。
总结
在本文中,我描述了.NET 10预览版6和7中支持的新平台特定.NET工具包。.NET 10 SDK允许您轻松创建"根"元包,这些元包委托给您的.NET工具的平台特定版本。如果您的应用程序具有本地依赖项,这尤其有用,因为它可以显著减小每个包的大小。
此外,您可以创建独立、裁剪或原生AOT编译版本的应用程序。这些包意味着您不再受制于消费者为您的工具安装正确版本的运行时(尽管目前确实意味着他们必须安装.NET 10 SDK)。