project.json 和 UWP

重要

此内容已弃用。 项目应使用 PackageReference 格式。 了解如何 将 project.json 项目迁移到 PackageReference

本文档介绍在 NuGet 3+ 中使用功能的包结构(Visual Studio 2015 及更高版本)。 通过将 .nuspecminClientVersion 属性设置为 3.1,可以声明您需要此处所述的功能。

向现有包添加 UWP 支持

如果已有包并且想要添加对 UWP 应用程序的支持,则无需采用此处所述的打包格式。 仅当需要所描述的功能并且愿意仅与更新到 NuGet 客户端版本 3+ 的客户端一起使用时,才需要采用此格式。

我已以 netcore45 为目标

如果您已经针对 netcore45,并且也不需要使用这里的功能,那么就无需执行任何操作。 UWP 应用程序可以使用 netcore45 包。

我想利用 Windows 10 特定 API

在这种情况下,您需要将 uap10.0 目标框架标识符(TFM 或 TxM)添加到包中。 在包中创建一个新文件夹,并将已编译为使用 Windows 10 的程序集添加到该文件夹中。

我不需要 Windows 10 特定的 API,但想要新的 .NET 功能,或者还没有 NetCore45。

在这种情况下,需将 dotnet TxM 添加到你的包文件中。 与其他 TxM 不同,dotnet 并不意味着表面面积或平台。 它说明你的包能够在任何依赖项可运行的平台上运行。 使用 dotnet TxM 生成包时,可能需要在 .nuspec中具有更多特定于 TxM 的依赖项,因为需要定义所依赖的 BCL 包,例如 System.TextSystem.Xml等。这些依赖项在定义包工作的位置。

如何找出依赖项

可通过两种方法来确定要列出的依赖项:

  1. 使用 NuSpec 依赖项生成器第三方 工具。 该工具自动化执行该过程,并在构建过程中使用依赖包更新您的 .nuspec 文件。 它通过 NuGet 包提供,NuSpec.ReferenceGenerator

  2. 最费力的方法是使用 ILDasm 来检查 .dll,以了解运行时实际需要的程序集。 然后确定它们各自来自哪个 NuGet 包。

有关帮助创建支持 dotnet TxM 的包的功能的详细信息,请参阅 project.json 主题。

重要

如果包适用于 PCL 项目,我们强烈建议创建一个 dotnet 文件夹,以避免警告和潜在的兼容性问题。

目录结构

使用此格式的 NuGet 包具有以下已知文件夹和行为:

文件夹 行为
构建 包含此文件夹中的 MSBuild 目标和属性文件以不同的方式集成到项目中,否则不会更改。
工具 install.ps1uninstall.ps1 未运行。 init.ps1 像往常一样工作。
内容 不会自动将内容复制到用户的项目中。 计划在以后的版本中支持项目中包含内容。
利布 对于许多包,lib 的工作方式与 NuGet 2.x 相同,但提供了更广泛的选项,可以在其中使用不同的名称,并改进了在使用包时选取正确子文件夹的逻辑。 但是,与 ref结合使用时,lib 文件夹包含实现 ref 文件夹中程序集定义的外围区域的程序集。
参考 ref 是一个可选文件夹,其中包含定义要编译的应用程序的公共图面(公共类型和方法)的 .NET 程序集。 此文件夹中的程序集可能没有实现,它们仅用于为编译器定义接口范围。 如果包没有 ref 文件夹,则 lib 既是引用程序集,又是实现程序集。
运行时 runtimes 是一个可选文件夹,其中包含特定于操作系统的代码,例如 CPU 体系结构和特定于平台或依赖于平台的二进制文件。

包中的 MSBuild 目标和属性文件

NuGet 包可以包含 .targets.props 文件,这些文件导入到安装包的任何 MSBuild 项目中。 在 NuGet 2.x 中,这是通过将 <Import> 语句注入到 .csproj 文件中来完成的,而在 NuGet 3.0 中,没有特定的“安装到项目”操作。 而是包还原过程将写入两个文件 [projectname].nuget.props[projectname].NuGet.targets

MSBuild 知道查找这两个文件,并在项目生成过程的开头和末尾附近自动导入它们。 这提供与 NuGet 2.x 非常相似的行为,但有一个主要区别:在这种情况下,目标/属性文件的顺序没有保证。 但是,MSBuild 确实提供了通过 <Target> 定义的 BeforeTargetsAfterTargets 属性对目标进行排序的方法(请参阅 Target Element (MSBuild)

Lib 和 Ref

lib 文件夹的行为在 NuGet v3 中没有显著更改。 但是,所有程序集都必须位于以 TxM 命名的子文件夹中,并且不能再直接放置在 lib 文件夹下。 TxM 是包中给定资产应该适用于的平台的名称。 从逻辑上讲,这些是目标框架标识符(TFM)的扩展,例如 net45net46netcore50dnxcore50 都是 TxM 的示例(请参阅 目标框架)。 TxM 可以指框架(TFM)以及其他特定于平台的领域。 例如,UWP TxM (uap10.0) 表示 UWP 应用程序的 .NET 范围以及 Windows 接口。

示例库结构:

lib
├───net40
│       MyLibrary.dll
└───wp81
        MyLibrary.dll

lib 文件夹包含运行时使用的程序集。 对于大多数包,只需在每个目标 TxM 的 lib 下创建一个文件夹即可。

参考

有时,在编译期间应使用不同的程序集(.NET 引用程序集今天执行此作)。 对于这些情况,请使用名为 ref 的顶级文件夹(简称为“引用程序集”)。

大多数包作者不需要 ref 文件夹。 这对于需要为编译和 IntelliSense 提供一致的外围应用但针对不同的 TxM 有不同的实现的包非常有用。 最大的用例是作为在 NuGet 上传送 .NET Core 的一部分而生成的 System.* 包。 这些包具有各种实现,这些实现正在通过一组一致的 ref 程序集进行统一。

机械上,ref 文件夹中包括的程序集是传递给编译器的引用程序集。 对于已使用 csc.exe 的程序集,我们将传递给 C# /reference 选项 开关。

ref 文件夹的结构与 lib相同,例如:

└───MyImageProcessingLib
        ├───lib
        │   ├───net40
        │   │       MyImageProcessingLibrary.dll
        │   │
        │   ├───net451
        │   │       MyImageProcessingLibrary.dll
        │   │
        │   └───win81
        │           MyImageProcessingLibrary.dll
        │
        └───ref
            ├───net40
            │       MyImageProcessingLibrary.dll
            │
            └───portable-net451-win81
                    MyImageProcessingLibrary.dll

在此示例中,ref 目录中的程序集完全相同。

运行时

运行时文件夹包含在特定“运行时”上运行所需的程序集和本机库,这些程序集通常由作系统和 CPU 体系结构定义。 这些运行时使用 运行时标识符(RID)(例如 winwin-x86win7-x86win8-64等)进行标识。

本机帮助程序使用特定于平台的 API

以下示例展示了一个包,它在多个平台上有纯托管实现,但在 Windows 8 上使用本地助手,以便调用 Windows 8 特定的本地 API。

└───MyLibrary
        ├───lib
        │   └───net40
        │           MyLibrary.dll
        │
        └───runtimes
            ├───win8-x64
            │   ├───lib
            │   │   └───net40
            │   │           MyLibrary.dll
            │   │
            │   └───native
            │           MyNativeLibrary.dll
            │
            └───win8-x86
                ├───lib
                │   └───net40
                │           MyLibrary.dll
                │
                └───native
                        MyNativeLibrary.dll

鉴于上述软件包,将会发生以下情况:

  • 当不在 Windows 8 上时,将使用 lib/net40/MyLibrary.dll 程序集。

  • 当在 Windows 8 上使用 runtimes/win8-<architecture>/lib/MyLibrary.dll 时,native/MyNativeHelper.dll 会被复制到构建的输出中。

在上面的示例中,lib/net40 程序集纯粹是托管代码,而运行时文件夹中的程序集将 p/invoke 进入本机帮助程序程序集,以调用特定于 Windows 8 的 API。

只选取单个 lib 文件夹,因此,如果有运行时特定的文件夹,则选择该文件夹而非非运行时特定的 lib。 原生文件夹是增量的,如果存在,则会被复制到构建的输出中。

托管封装器

使用运行时的另一种方法是发布一个包,该包完全是一个托管包装,负责包裹本机程序集。 在此方案中,将创建如下所示的包:

└───MyLibrary
        └───runtimes
            ├───win8-x64
            │   ├───lib
            │   │   └───net451
            │   │           MyLibrary.dll
            │   │
            │   └───native
            │           MyImplementation.dll
            │
            └───win8-x86
                ├───lib
                │   └───net451
                │           MyLibrary.dll
                │
                └───native
                        MyImplementation.dll

在这种情况下,没有顶层 lib 文件夹,因为所有此包的实现都依赖于相应的本机程序集。 如果托管程序集(MyLibrary.dll)在这两种情况下完全相同,那么我们可以将其置于顶级 lib 文件夹中,但由于缺少本机程序集不会导致包在不是 win-x86 或 win-x64 的平台上安装包时失败,则会使用顶级库,但不会复制本机程序集。

为 NuGet 2 和 NuGet 3 编写包

如果您想创建一个可以被使用 packages.config 以及 project.json 的项目使用的包,那么请遵循以下事项:

  • Ref 和运行时仅在 NuGet 3 上工作。 NuGet 2 会忽略它们。

  • 不能依赖 install.ps1uninstall.ps1 来运行。 这些文件在使用 packages.config时执行,但在 project.json中将被忽略。 因此,您的程序包需要在无需其他组件运行的情况下可用。 init.ps1 仍在 NuGet 3 上运行。

  • 目标和道具的安装方式不同,因此请确保你的包在两个客户端上按预期工作。

  • lib 的子目录必须是 NuGet 3 中的 TxM。 不能将库放在 lib 文件夹的根目录中。

  • 不会使用 NuGet 3 自动复制内容。 包的使用者可以自行复制文件,或使用任务运行程序等工具自动复制文件。

  • NuGet 3 不会运行源和配置文件转换。

如果支持 NuGet 2 和 3,则 minClientVersion 应是包使用的 NuGet 2 客户端的最低版本。 通常情况下,现有包不需要更改。