.NET 分发打包

随着 .NET 5(和 .NET Core)以及更高版本在越来越多的平台上可用,了解如何对使用它的应用和库进行打包、命名和版本控制非常有用。 这样,无论用户选择在哪里运行 .NET,包维护人员均可以帮助确保获得一致的体验。 本文对以下用户非常有用:

  • 尝试从源生成 .NET。
  • 想要更改 .NET CLI,但更改可能会影响生成的布局或包。

磁盘布局

安装时,.NET 包含一些组件,这些组件在文件系统中排列如下:

{dotnet_root}                    (0)              (*)
├── dotnet                       (1)
├── LICENSE.txt                  (8)
├── ThirdPartyNotices.txt        (8)
├── host                                          (*)
│   └── fxr                                       (*)
│       └── <fxr version>        (2)
├── sdk                                           (*)
│   └── <sdk version>            (3)
├── sdk-manifests                (4)              (*)
│   └── <sdk feature band version>
├── library-packs                (20)             (*)
├── metadata                     (4)              (*)
│   └── workloads
│       └── <sdk feature band version>
├── template-packs               (4)              (*)
├── packs                                         (*)
│   ├── Microsoft.AspNetCore.App.Ref              (*)
│   │   └── <aspnetcore ref version>     (11)
│   ├── Microsoft.NETCore.App.Ref                 (*)
│   │   └── <netcore ref version>        (12)
│   ├── Microsoft.NETCore.App.Host.<rid>          (*)
│   │   └── <apphost version>            (13)
│   ├── Microsoft.WindowsDesktop.App.Ref          (*)
│   │   └── <desktop ref version>        (14)
│   ├── NETStandard.Library.Ref                   (*)
│   │   └── <netstandard version>        (15)
│   ├── Microsoft.NETCore.App.Runtime.<rid>       (*)
│   │   └── <runtime version>            (18)
│   ├── Microsoft.AspNetCore.App.Runtime.<rid>    (*)
│   │   └── <aspnetcore version>         (18)
│   └── runtime.<rid>.Microsoft.DotNet.ILCompiler (*)
│       └── <runtime version>            (19)
├── shared                                        (*)
│   ├── Microsoft.NETCore.App                     (*)
│   │   └── <runtime version>     (5)
│   ├── Microsoft.AspNetCore.App                  (*)
│   │   └── <aspnetcore version>  (6)
│   ├── Microsoft.AspNetCore.All                  (*)
│   │   └── <aspnetcore version>  (6)
│   └── Microsoft.WindowsDesktop.App              (*)
│       └── <desktop app version> (7)
└── templates                                     (*)
│   └── <templates version>      (17)
/
├── etc/dotnet
│       └── install_location     (16)
├── usr/share/man/man1
│       └── dotnet.1.gz          (9)
└── usr/bin
        └── dotnet               (10)
  • (0) {dotnet_root} 是所有 .NET 主版本和次要版本的共享根。 如果安装了多个运行时,它们将共享 {dotnet_root} 文件夹,例如 {dotnet_root}/shared/Microsoft.NETCore.App/6.0.11{dotnet_root}/shared/Microsoft.NETCore.App/7.0.0{dotnet_root} 文件夹的名称应与版本无关,也就是说,只是 dotnet

  • (1) dotnet 主机(也称为“muxer”)有两个不同角色:激活运行时以启动应用程序,及激活 SDK 以向其分派命令。 主机是本机可执行文件 (dotnet.exe)。

主机只有一个,不过大部分的其他组件都在带有版本的目录中(2、3、5 和 6)。 这意味着系统上可存在多个版本,因为它们是并排安装的。

  • (2) host/fxr/<fxr version> 包含主机使用的框架解析逻辑。 主机采用已安装的最新 hostfxr。 在执行 .NET 应用程序时,hostfxr 负责选择合适的运行时。 例如,为 .NET 7.0.0 生成的应用程序会使用 7.0.5 运行时(如果可用)。 同样,hostfxr 在开发期间也会选择适当的 SDK。

  • (3) sdk/<sdk version> SDK(也称为“工具”)是一组托管工具,可用于编写和生成 .NET Core 库和应用程序。 SDK 包括 .NET CLI、托管的语言编译器、MSBuild 及相关生成任务和目标、NuGet、新项目模板等。

  • (4) sdk-manifests/<sdk 功能区段版本> 可选工作负载安装所需的资产的名称和版本将在此文件夹中存储的工作负载清单中进行维护。 文件夹名称是 SDK 的功能区段版本。 因此,对于 SDK 版本(如 7.0.102),此文件夹仍然命名为 7.0.100。 安装工作负荷后,会根据需要为工作负荷的资产创建以下文件夹: 元数据模板包。 如果工作负载应安装在用户路径而不是 dotnet 文件夹中,则发行版可创建一个空的 /metadata/workloads/<sdkfeatureband>/userlocal 文件。 有关详细信息,请参阅 GitHub 问题 dotnet/installer#12104

“共享” 文件夹包含框架。 共享框架提供一组位于中心位置的库,从而让不同的应用程序使用。

  • (5) shared/Microsoft.NETCore.App/<runtime version> 此框架包含 .NET 运行时和支持托管库。

  • (6) shared/Microsoft.AspNetCore.{App,All}/<aspnetcore version> 包含 ASP.NET Core 库。 已开发且支持 Microsoft.AspNetCore.App 下的库(作为 .NET 项目的一部分)。 Microsoft.AspNetCore.All 下的库是一个超集,其中还包含第三方库。

  • (7) shared/Microsoft.Desktop.App/<desktop app version> 包含 Windows 桌面库。 在非 Windows 平台上不包含此项。

  • (8) LICENSE.txt 和 ThirdPartyNotices.txt 分别是 .NET 许可证和 .NET 中使用的第三方库的许可证。

  • (9,10) dotnet.1.gz, dotnet dotnet.1.gz 是 dotnet 手册页。 dotnet 是指向 dotnet 主机 (1) 的符号链接。 这些文件安装在已知位置用于系统集成。

  • (11,12) Microsoft.NETCore.App.Ref 和 Microsoft.AspNetCore.App.Ref 分别描述了 x.y 版本 .NET 和 ASP.NET Core 的 API。 针对这些目标版本进行编译时,将使用这些包。

  • (13) Microsoft.NETCore.App.Host.<rid> 包含平台 rid 的本机二进制文件。 将 .NET 应用程序编译为适用于该平台的本机二进制文件时,将使用此二进制文件作为模板。

  • (14) Microsoft.WindowsDesktop.App.Ref 介绍 Windows 桌面应用程序 x.y 版本的 API。 在针对该目标进行编译时,将使用这些文件。 在非 Windows 平台上不提供此项。

  • (15) NETStandard.Library.Ref 描述了 netstandard x.y API。 在针对该目标进行编译时,将使用这些文件。

  • (16)“/etc/dotnet/install_location”是一个包含 {dotnet_root} 完整路径的文件 。 该路径可能以换行符结尾。 根路径为 /usr/share/dotnet 时无需添加此文件。

  • (17) templates 包含 SDK 使用的模板。 例如,dotnet new 在此处查找项目模板。

  • (18) Microsoft.NETCore.App.Runtime.<rid>/<runtime version>,Microsoft.AspNetCore.App.Runtime.<rid>/<aspnetcore 版本>这些文件支持构建自包含应用程序。 这些目录包含指向 (2)、(5) 和 (6) 中文件的符号链接。

  • (19) 运行时。<rid>。Microsoft.DotNet.ILCompiler/<runtime 版本> 这些文件可为目标平台生成 NativeAOT 应用程序。

  • (20) 库包 包含 NuGet 包文件。 SDK 配置为将此文件夹用作 NuGet 源。 下面介绍了 .NET 生成提供的 NuGet 包列表。

标记为 (*) 的文件夹被多个包使用。 某些包格式(例如,rpm)需要对此类文件夹进行特殊处理。 包维护人员必须处理这个问题。

添加到 library-packs (20) 的包文件可以是Microsoft不会为目标平台分发的包。 这些文件也可以是Microsoft分发的包,并提供 library-packs 从源生成的包以满足平台包分发准则。 .NET 内部版本包括以下包:

包名称 由 Microsoft 发布 需要
Microsoft.DotNet.ILCompiler.<version>.nupkg
Microsoft.NET.ILLink.Tasks.<version>.nupkg
NativeAOT

.NET 版本控制基于运行时组件 [major].[minor] 版本号。 SDK 版本采用相同的 [major].[minor],并有一个独立的 [patch],它为 SDK 合并了功能和修补语义。 例如:SDK 版本 7.0.302 是支持 7.0 运行时的 SDK 的第 3 个功能版本的第 2 个补丁版本。 有关版本控制的工作原理的详细信息,请参阅 .NET 版本控制概述

一些包在自己的名称中就包含一部分版本号。 这允许你安装特定版本。 版本名称中不包含版本的剩余部分。 这允许 OS 包管理器更新这些包(例如,自动安装安全修补程序)。 支持的包管理器特定于 Linux。

下面列出了推荐的包:

  • dotnet-sdk-[major].[minor] - 安装特定运行时的最新 SDK

    • 版本:<sdk 版本>
    • 示例:dotnet-sdk-7.0
    • 包含: (3)、(4)、(18)、(20)
    • 依赖项:dotnet-runtime-[major].[minor]aspnetcore-runtime-[major].[minor]dotnet-targeting-pack-[major].[minor]aspnetcore-targeting-pack-[major].[minor]netstandard-targeting-pack-[netstandard_major].[netstandard_minor]dotnet-apphost-pack-[major].[minor]dotnet-templates-[major].[minor]
  • dotnet-sdk-aot-[major].[minor] - 为平台 NativeAOT 安装 SDK 组件

    • 版本:<sdk 版本>
    • 示例: dotnet-sdk-aot-9.0
    • 包含: (19)
    • 依赖项:dotnet-sdk-[major].[minor].NET 运行时依赖的库的编译器工具链和开发人员包
  • aspnetcore-runtime-[major].[minor] - 安装特定 ASP.NET Core 运行时

    • 版本:<aspnetcore 运行时版本>
    • 示例:aspnetcore-runtime-7.0
    • 包含: (6)
    • 依赖项:dotnet-runtime-[major].[minor]
  • dotnet-runtime-deps-[major].[minor](可选)- 安装运行自包含应用程序的依赖项

    • 版本:<运行时版本>
    • 示例:dotnet-runtime-deps-7.0
    • 依赖项:特定于分发的依赖项
  • dotnet-runtime-[major].[minor] - 安装特定运行时

    • 版本:<运行时版本>
    • 示例:dotnet-runtime-7.0
    • 包含: (5)
    • 依赖项:dotnet-hostfxr-[major].[minor]dotnet-runtime-deps-[major].[minor]
  • dotnet-hostfxr-[major].[minor] - 依赖项

    • 版本:<运行时版本>
    • 示例:dotnet-hostfxr-7.0
    • 包含: (2)
    • 依赖项:dotnet-host
  • dotnet-host - 依赖项

    • 版本:<运行时版本>
    • 示例: dotnet-host
    • 包含: (1),(8),(9),(10),(16)
  • dotnet-apphost-pack-[major].[minor] - 依赖项

    • 版本:<运行时版本>
    • 包含: (13)
  • dotnet-targeting-pack-[major].[minor] - 允许面向非最新的运行时

    • 版本:<运行时版本>
    • 包含: (12)
  • aspnetcore-targeting-pack-[major].[minor] - 允许面向非最新的运行时

    • 版本:<aspnetcore 运行时版本>
    • 包含: (11)
  • netstandard-targeting-pack-[netstandard_major].[netstandard_minor] - 允许面向 netstandard 版本

    • 版本:<sdk 版本>
    • 包含: (15)
  • dotnet-templates-[major].[minor]

    • 版本:<sdk 版本>
    • 包含:(17)

以下两个元包是可选的。 它们为最终用户带来了价值,因为它们将顶级包 (dotnet-sdk) 抽象化,从而简化了整套 .NET 包的安装。 这些元包引用特定的 .NET SDK 版本。

  • dotnet[major] - 安装指定的 SDK 版本

    • 版本:<sdk 版本>
    • 示例:dotnet7
    • 依赖项:dotnet-sdk-[major].[minor]
  • dotnet - 安装由发行版确定为主要版本的特定 SDK 版本,通常是最新的可用版本

    • 版本:<sdk 版本>
    • 示例:dotnet
    • 依赖项:dotnet-sdk-[major].[minor]

dotnet-runtime-deps-[major].[minor] 需要了解发行版特定依赖项 。 因为发行版生成系统可能能够自动派生包,所以包是可选的,如果选择,会将这些依赖项直接添加到 dotnet-runtime-[major].[minor] 包中。

当包内容位于受版本控制的文件夹下时,包名称 [major].[minor] 与受版本控制的文件夹名称匹配。 对于所有包(除 netstandard-targeting-pack-[netstandard_major].[netstandard_minor] 外),这也与 .NET 版本匹配。

包间的依赖关系应使用“等于或大于”版本要求 。 例如,dotnet-sdk-7.0:7.0.401 要求 aspnetcore-runtime-7.0 >= 7.0.6。 这使用户可以通过根包(例如 dnf update dotnet-sdk-7.0)升级其安装。

大多数分发都需要从源中构建所有项目。 这对包有一些影响:

  • 不能简单地从源生成 shared/Microsoft.AspNetCore.All 下的第三方库。 因此 aspnetcore-runtime 包中省略了该文件夹。

  • 使用 nuget.org 中的二进制项目填充了 NuGetFallbackFolder。 它应保留为空。

多个 dotnet-sdk 包可能会为 NuGetFallbackFolder 提供同样的文件。 若要避免包管理器出现问题,这些文件应完全相同(包括校验和、修改日期等等)。

调试包

调试内容应打包在以调试命名的包中,该包遵循本文前面描述的 .NET 包拆分要求。 例如,dotnet-sdk-[major].[minor] 包的调试内容应包含在名为 dotnet-sdk-dbg-[major].[minor] 的包中。 应该将调试内容安装到与二进制文件相同的位置。

下面是几个二进制文件示例:

{dotnet_root}/sdk/<sdk version> 目录中,需要以下两个文件:

  • dotnet.dll - 随 dotnet-sdk-[major].[minor] 包一起安装
  • dotnet.pdb - 随 dotnet-sdk-dbg-[major].[minor] 包一起安装

{dotnet_root}/shared/Microsoft.NETCore.App/<runtime version> 目录中,需要以下两个文件:

  • System.Text.Json.dll - 随 dotnet-runtime-[major].[minor] 包一起安装
  • System.Text.Json.pdb - 随 dotnet-runtime-dbg-[major].[minor] 包一起安装

{dotnet_root/shared/Microsoft.AspNetCore.App/<aspnetcore version> 目录中,需要以下两个文件:

  • Microsoft.AspNetCore.Routing.dll - 随 aspnetcore-runtime-[major].[minor] 包一起安装
  • Microsoft.AspNetCore.Routing.pdb - 随 aspnetcore-runtime-dbg-[major].[minor] 包一起安装

从 .NET 8.0 开始,由源生成生成的所有 .NET 调试内容(PDB 文件)都可以在名为 dotnet-symbols-sdk-<version>-<rid>.tar.gz 的 tarball 中找到。 此存档包含子目录中的 PDB,这些 PDB 与 .NET SDK tarball 的目录结构 (dotnet-sdk-<version>-<rid>.tar.gz) 相匹配。

虽然调试 tarball 中提供了所有调试内容,但并非所有调试内容都同样重要。 最终用户最感兴趣的是 shared/Microsoft.AspNetCore.App/<aspnetcore version>shared/Microsoft.NETCore.App/<runtime version> 目录的内容。

sdk/<sdk version> 下的 SDK 内容对于调试 .NET SDK 工具集非常有用。

以下包是推荐的调试包:

  • aspnetcore-runtime-dbg-[major].[minor] - 安装特定 ASP.NET Core 运行时的调试内容

    • 版本:<aspnetcore 运行时版本>
    • 示例:aspnetcore-runtime-dbg-8.0
    • 包含:(6) 的调试内容
    • 依赖项:aspnetcore-runtime-[major].[minor]
  • dotnet-runtime-dbg-[major].[minor] - 安装特定运行时的调试内容

    • 版本:<运行时版本>
    • 示例:dotnet-runtime-dbg-8.0
    • 包含:(5) 的调试内容
    • 依赖项:dotnet-runtime-[major].[minor]

以下调试包是可选的:

  • dotnet-sdk-dbg-[major].[minor] - 安装特定 SDK 版本的调试内容
    • 版本:<sdk 版本>
    • 示例:dotnet-sdk-dbg-8.0
    • 包含:(3)、(4)、(18) 的调试内容
    • 依赖项:dotnet-sdk-[major].[minor]

调试 tarball 还在 packs 下包含一些调试内容,代表 shared 下内容的副本。 在 .NET 布局中,packs 目录用于生成 .NET 应用程序。 没有调试方案,因此不应将调试内容打包在调试 tarball 中的 packs 下。

生成包

dotnet/source-build 存储库中说明了如何生成 .NET SDK 的源 tarball 及其所有组件。 源版本存储库中的输出内容符合本文第一部分中所描述的布局。