创建 RID 特定、自包含和 AOT 的 .NET 工具

本文适用于: ✔️.NET SDK 10 及更高版本

打包适用于特定平台和体系结构的 .NET 工具,以便分发原生、快速且优化的应用程序。 借助此功能,可以更轻松地为命令行工具(如 MCP 服务器或其他特定于平台的实用程序)分发优化的应用程序。

概述

从 .NET SDK 10 开始,可以创建面向特定作系统环境的 .NET 工具(由运行时标识符 (RID)表示)。 这些工具可以是:

  • RID 特定:针对特定操作系统和体系结构进行编译。
  • 自包含:包括 .NET 运行时,不需要单独的 .NET 安装。
  • 本机 AOT:使用提前编译来加快启动速度,减少内存占用。

安装该工具时,用户不会注意到差异。 .NET CLI 会自动为其平台选择并安装最佳包。

选择加入特定于 RID 的打包

若要创建特定于 RID 的工具,请使用以下 MSBuild 属性之一配置项目:

RuntimeIdentifiers 属性

用于 RuntimeIdentifiers 指定工具支持的平台:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <PackAsTool>true</PackAsTool>
    <ToolCommandName>mytool</ToolCommandName>
    <RuntimeIdentifiers>win-x64;linux-x64;osx-arm64</RuntimeIdentifiers>
  </PropertyGroup>
</Project>

ToolPackageRuntimeIdentifiers 属性

或者,可以使用 ToolPackageRuntimeIdentifiers 来进行特定于工具的 RID 配置:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <PackAsTool>true</PackAsTool>
    <PublishAot>true</PublishAot>
    <ToolCommandName>mytool</ToolCommandName>
    <ToolPackageRuntimeIdentifiers>win-x64;linux-x64;osx-arm64</ToolPackageRuntimeIdentifiers>
  </PropertyGroup>
</Project>

使用以分号分隔的 RID 值列表。 有关运行时标识符的列表,请参阅 RID 目录

提供依赖于框架的版本

选择启用特定于 RID 的工具打包时,.NET SDK 会为每个指定的 RID 创建自包含包。 但是,你丢失了依赖于框架的默认打包功能,这些功能可在任何安装了 .NET 运行时的平台上使用。

若要提供框架依赖版本以及 RID 特定的包,请将其添加到anyToolPackageRuntimeIdentifiers列表:

<PropertyGroup>
  <PublishTrimmed>true</PublishTrimmed>
  <ToolPackageRuntimeIdentifiers>win-x64;osx-arm64;linux-x64;any</ToolPackageRuntimeIdentifiers>
</PropertyGroup>

此配置创建五个包:

  • 一个包含所有可用子包的顶级指针包。
  • 三个特定于 RID 的包(win-x64osx-arm64linux-x64)是独立且精简的。
  • 一个与 RID 无关的包(any)依赖于框架,需要安装 .NET 运行时。

用户安装工具时:

  • win-x64osx-arm64linux-x64 系统上,自包含的裁剪包会被下载并执行。
  • 在其他任何操作系统或体系结构上,将使用依赖any框架的程序包。

此方法为通用平台提供优化的自包含二进制文件,同时通过依赖于框架的回退保持与不太常见的平台的兼容性。

何时使用 RuntimeIdentifiersToolPackageRuntimeIdentifiers

同时RuntimeIdentifiersToolPackageRuntimeIdentifiers使工具参与 RID 特定的打包,但它们的作用略有不同:

在以下情况下使用 RuntimeIdentifiers

  • 希望项目不仅仅作为工具,而是从整体上生成和发布特定于 RID 的应用。
  • 你主要针对 CoreCLR(非 AOT),或者希望标准 SDK 的行为,即使用单个 dotnet pack 会生成多个特定于 RID 的包。
  • 可以为部分 RID 条件化PublishAot,但您仍然需要为RuntimeIdentifiers中的每个 RID 提供基于 CoreCLR 的包。

在以下情况下使用 ToolPackageRuntimeIdentifiers

  • 只想为工具打包定义特定于 RID 的行为,而无需更改项目为其他部署方案生成的方式。
  • 你正在使用本机 AOT 并计划为每个 RID dotnet pack -r <RID> AOT 二进制文件。
  • 你需要一个 混合模型 ,其中一些 RID 获得本地 AOT,而另一些则退回到可移植的 CoreCLR 实现。

Notes:

  • 顶级指针包指定可用的 RID 特定包。 如果指定 ToolPackageRuntimeIdentifiers,它将确定工具的 RID,否则将使用 RuntimeIdentifiers
  • ToolPackageRuntimeIdentifiers 应等于 RuntimeIdentifiers 中的某个 RID 或它们的子集
  • PublishAot=true,仅当为特定 RID 打包时,才会生成特定于 RID 的包(例如, dotnet pack -r linux-x64)。
  • 仅当生成 OS 和目标 OS 匹配时,才支持原生 AOT 生成(PublishAot=true)。

将您的工具打包成软件包

打包过程因是否使用 AOT 编译而异。 若要从项目生成 NuGet 包或 .nupkg 文件,请运行 dotnet pack 命令。

特定于 RID 的自包含工具

运行 dotnet pack 一次:

dotnet pack

此命令创建多个 NuGet 包:

  • 一个包对应每个 RID: <packageName>.<RID>.<packageVersion>.nupkg
    • 示例: mytool.win-x64.1.0.0.nupkg
    • 示例: mytool.linux-x64.1.0.0.nupkg
    • 示例: mytool.osx-arm64.1.0.0.nupkg
  • 一个与 RID 无关的指针包: <packageName>.<packageVersion>.nupkg
    • 示例: mytool.1.0.0.nupkg

AOT 工具

对于具有 AOT 编译的工具(<PublishAot>true</PublishAot>),必须为每个平台单独打包。

本机 AOT 的平台要求

本机 AOT 编译要求 SDK RID 的操作系统(OS)部分与目标 RID 的操作系统(OS)匹配。 SDK 可以跨编译不同的体系结构(例如 x64 到 ARM64),但不能跨作系统(例如 Windows 到 Linux)。

这意味着你有多个用于构建本机 AOT 包的选项:

  • 仅针对开发计算机生成:仅支持您所用开发操作系统的本机 AOT。
  • 使用适用于 Linux 版本的容器:如果使用的是 macOS 或 Windows,请使用容器为 Linux 进行交叉编译。 例如,使用 mcr.microsoft.com/dotnet/sdk:10.0-noble-aot 容器映像。
  • 跨计算机协调构建:使用 GitHub Actions 或 Azure DevOps Pipelines 等 CI/CD 系统在不同的操作系统上构建。

无需在同一台计算机或同时构建所有 RID 特定的包。 只需在发布顶级包之前生成并发布它们。

打包本地 AOT(提前编译)工具

在任何平台上一次性打包顶层包:

dotnet pack

在相应平台上为每个特定的 RID 打包,例如:

dotnet pack -r linux-x64

必须在操作系统与目标 RID 的操作系统相匹配的平台上运行每个特定于 RID 的 pack 命令。 有关本机 AOT 编译所需的先决条件的更多信息,请参阅 本机 AOT 部署

如果设置为PublishAottrue,打包行为将发生更改:

  • dotnet pack 生成 顶级指针包 (包类型 DotnetTool)。
  • 仅当显式传递 -r <RID>(例如, dotnet pack -r linux-x64dotnet pack -r osx-arm64) 时,才会生成特定于 RID 的 AOT 包。

混合 AOT + CoreCLR 打包模式(示例)

某些工具需要两个世界的最佳功能:

  • 高优先级平台子集的本机 AOT(取决于工具)。
  • 可移植的 CoreCLR 回退,适用于本机 AOT 构建未覆盖的平台。

可以使用以下模式实现此“混合”模型:

  1. 为本机 AOT 和特定于工具的 RID 配置工具。

    在项目文件中,使用 ToolPackageRuntimeIdentifiers 并启用 PublishAot

    例如:

    <ToolPackageRuntimeIdentifiers>osx-arm64;linux-arm64;linux-x64;any</ToolPackageRuntimeIdentifiers>
    <PublishAot>true</PublishAot>
    
  2. 创建指针包。

    运行 dotnet pack 一次(在任何平台上)以构建指向与 RID 相关特定包的顶级包。

    dotnet pack
    
  3. 构建为所选 RID 的本机 AOT 包。

    原生 AOT 编译需要在目标平台上构建。 使用 dotnet pack -r <RID> 在匹配的平台上构建每个启用了 AOT 的 RID 包。

例如:

dotnet pack -r linux-x64
  1. 创建 CoreCLR 回退包。

    若要提供通用回退,请在打包 any RID 时不使用 AOT:

    dotnet pack -r any -p:PublishAot=false
    

    这会生成可在没有专用 AOT 生成的平台上运行的可移植 CoreCLR 包(例如 yourtool.any.<version>.nupkg)。

注释

还可以使用 .NET SDK 10.0-noble-aot 容器映像从支持 Linux 容器的任何主机生成和打包 Linux 本机 AOT 工具。 例如:

  • mcr.microsoft.com/dotnet/sdk:10.0-noble-aot

当开发计算机不本地运行 Linux 时,这很有用。

在此混合配置中:

  • 指针包 (yourtool.<version>.nupkg) 引用两者:
    • 特定于 RID 的本机 AOT 包(例如,yourtool.osx-arm64yourtool.linux-x64)。
    • 作为备用的 CoreCLR 包 any
  • .NET CLI 在用户运行dotnet tool installdnx命令时,会自动为其平台选择最合适的包。

示例: dotnet10-hybrid-tool

存储库dotnet10-hybrid-tool演示了这种混合打包模式,其中包含针对osx-arm64linux-arm64linux-x64的本机 AOT 包,以及用于any RID 的 CoreCLR 回退包(例如,当没有可用的 AOT 版本时在 Windows 上使用)。

可以自行安装和试用该工具:

dotnet tool install -g dotnet10-hybrid-tool
dotnet10-hybrid-tool

该工具报告其运行时框架说明、运行时标识符(RID)和编译模式(本机 AOT 或 CoreCLR)。

本机 AOT 平台上的示例输出:

Hi, I'm a 'DotNetCliTool v2' tool!
Yes, I'm quite fancy.

Version: .NET 10.0.2
RID: osx-arm64
Mode: Native AOT

在一个使用 CoreCLR 回退的平台上的示例输出:

Hi, I'm a 'DotNetCliTool v2' tool!
Yes, I'm quite fancy.

Version: .NET 10.0.2
RID: win-x64
Mode: CoreCLR

这使其成为一种有用的方法,用于实验特定于 RID 的 AOT 编译工具以及 CoreCLR 的回退行为。

发布您的工具

发布特定于 RID 的工具包时,.NET CLI 使用顶级包的版本号来选择匹配的 RID 特定包。 这意味着:

  • 所有特定于 RID 的包必须具有与顶级包完全相同的版本。
  • 在顶级包可用之前,所有包必须发布到包源。

若要确保发布过程顺利,请确保:

  1. 首先发布所有RID专用包:

    dotnet nuget push yourtool.win-x64.1.0.0.nupkg
    dotnet nuget push yourtool.linux-x64.1.0.0.nupkg
    dotnet nuget push yourtool.osx-arm64.1.0.0.nupkg
    dotnet nuget push yourtool.any.1.0.0.nupkg
    
  2. 最后发布顶级包:

    dotnet nuget push yourtool.1.0.0.nupkg
    

最后发布顶级包可确保当用户安装工具时,所有引用的 RID 特定包都可用。 如果用户在发布所有 RID 包之前安装工具,安装将失败。

安装和运行工具

是否使用特定于 RID 的打包工具,对于用户而言是透明的实现细节。 无论工具开发人员是否选择采用特定于 RID 的打包,都以相同的方式安装和运行工具。

要全局安装一个工具:

dotnet tool install -g mytool

安装后,可以直接调用它:

mytool

还可以使用 dnx 助手,该助手在 Node.js 生态系统中的行为与 npx 类似:如果工具尚未存在,它会通过单次操作下载并启动工具。

dnx mytool

当工具使用特定于 RID 的打包时,.NET CLI 会自动为平台选择正确的包。 无需指定 RID - CLI 会从系统推断它并下载相应的 RID 特定包。

另请参阅