如何打包面向通用 Windows 平台的多架构 .NET 库? [英] How to package a multi-architecture .NET library that targets the Universal Windows Platform?

查看:48
本文介绍了如何打包面向通用 Windows 平台的多架构 .NET 库?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何打包用 C# 编写的仅提供与体系结构相关的构建的通用 Windows 平台库?为了说明起见,假设我为每个架构有条件地编译了一些特定于架构的代码(使用 #if ARM 和等价物).

How do I package a Universal Windows Platform library written in C# that offers only architecture-dependent builds? For the sake of illustration, let's say that I have some architecture-specific code conditionally compiled in for each architecture (using #if ARM and equivalents).

需要明确的是,我的库不存在 AnyCPU 版本 - 只有 x86、x64 和 ARM.

To be clear, no AnyCPU build exists for my library - only x86, x64 and ARM.

一种等效且可能更常见的情况是,我依赖于仅作为特定于体系结构的构建提供的外部库(例如 Win2D).为了保持上下文简单,让我们假设没有依赖项,只涉及我自己的代码 - 无论哪种方式,解决方案都应该简化为相同的事情.

An equivalent and potentially more common situation is one where I have a dependency on an external library that is only provided as architecture-specific builds (e.g. Win2D). To keep the context simple, let's assume there are no dependencies and only my own code is involved - the solution should reduce to the same thing either way.

这是一系列问题和答案,记录了我对现代 NuGet 包创作主题的发现,特别关注 NuGet 3 引入的更改.您可能还对一些相关问题感兴趣:

This is a series of questions and answers that document my findings on the topic of modern NuGet package authoring, focusing especially on the changes introduced with NuGet 3. You may also be interested in some related questions:

推荐答案

这个答案建立在 .NET Framework 库打包原则通用 Windows 平台库打包原则.首先阅读链接的答案以更好地理解以下内容.

This answer builds upon the principles of .NET Framework library packaging and the principles of Universal Windows Platform library packaging. Read the linked answers first to better understand the following.

我将假设您所有特定于架构的构建都公开相同的 API 表面,只是这些 API 的实现不同.

I will assume that all your architecture-specific builds expose the same API surface, with only the implementation of those APIs differing.

这种情况的主要并发症是构建工具链需要一个 AnyCPU 程序集来进行编译时引用解析,即使该程序集在运行时从未使用过.由于您的场景没有 AnyCPU 构建输出,我们需要找到解决方法.这里适用的概念是参考程序集 - AnyCPU 程序集仅在编译时用于参考验证.因此,要发布您的库,您需要创建一个参考程序集并将资产打包,如下所述.

The main complication with this scenario is that the build toolchain requires an AnyCPU assembly for compile-time reference resolution, even if this assembly is never used at runtime. Since your scenario does not have AnyCPU build output, we need to find a workaround. The concept that applies here is of reference assemblies - AnyCPU assemblies only used at compile-time for reference validation. Therefore, to publish your library, you will need to create a reference assembly and package the assets as outlined below.

为简单起见,我假设您的库不依赖于其他 NuGet 包.在实践中不太可能出现这种情况,但依赖管理已经包含在上面链接的其他答案中,因此从这个答案中省略.

For simplicity, I will assume that your library has no dependencies on other NuGet packages. This is not likely to be the case in practice but dependency management is already covered by the other answers linked above and is therefore omitted from this answer.

所需的 NuGet 包结构如下:

The desired structure of the NuGet package is as follows:

+---ref
|   \---uap10.0
|       |   MultiArchitectureUwpLibrary.dll
|       |   MultiArchitectureUwpLibrary.pri
|       |   MultiArchitectureUwpLibrary.XML
|       |
|       \---MultiArchitectureUwpLibrary
|               ArchitectureControl.xaml
|               MultiArchitectureUwpLibrary.xr.xml
|
+---runtimes
|   +---win10-arm
|   |   \---lib
|   |       \---uap10.0
|   |               MultiArchitectureUwpLibrary.dll
|   |               MultiArchitectureUwpLibrary.pdb
|   |
|   +---win10-x64
|   |   \---lib
|   |       \---uap10.0
|   |               MultiArchitectureUwpLibrary.dll
|   |               MultiArchitectureUwpLibrary.pdb
|   |
|   \---win10-x86
|       \---lib
|           \---uap10.0
|                   MultiArchitectureUwpLibrary.dll
|                   MultiArchitectureUwpLibrary.pdb

如果您熟悉上面链接的答案,那么您应该已经知道所有文件,尽管在这种情况下目录结构相当不寻常.ref 目录包含参考程序集、XML 文档和资源文件,而特定于体系结构的资产在运行时目录下构建.

If you have familiarized yourself with the answers linked above, the files should all be known to you already, although the directory structure is rather unusual in this case. The ref directory contains the reference assembly, the XML documentation and the resource files, while the architecture-specific assets are structured under the runtimes directory.

其中大部分非常简单,可以通过使用基于以下模板创建的 nuspec 文件来完成:

Most of this is pretty straightforward and can be accomplished by using a nuspec file created based on the following template:

<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
    <metadata minClientVersion="3.2">
        <id>Example.MultiArchitectureUwpLibrary</id>
        <version>1.0.0</version>
        <authors>Firstname Lastname</authors>
        <description>Example of library that is published as a set of architecture-specific assmeblies for the UWP platform.</description>
    </metadata>
    <files>
        <!-- Architecture-independent reference library for use at compile-time; generated by the PowerShell script. -->
        <file src="..\bin\Reference\Release\MultiArchitectureUwpLibrary.dll" target="ref\uap10.0" />

        <!-- XML documentation file goes together with the reference library. -->
        <file src="..\bin\x86\Release\MultiArchitectureUwpLibrary.xml" target="ref\uap10.0" />

        <!-- Resource files go together with the reference library. -->
        <file src="..\bin\x86\Release\MultiArchitectureUwpLibrary.pri" target="ref\uap10.0" />
        <file src="..\bin\x86\Release\MultiArchitectureUwpLibrary\*" target="ref\uap10.0\MultiArchitectureUwpLibrary" />

        <!-- The architecture-specific files go in architecture-specific directories. -->
        <file src="..\bin\x86\Release\MultiArchitectureUwpLibrary.dll" target="runtimes\win10-x86\lib\uap10.0" />
        <file src="..\bin\x86\Release\MultiArchitectureUwpLibrary.pdb" target="runtimes\win10-x86\lib\uap10.0" />

        <file src="..\bin\x64\Release\MultiArchitectureUwpLibrary.dll" target="runtimes\win10-x64\lib\uap10.0" />
        <file src="..\bin\x64\Release\MultiArchitectureUwpLibrary.pdb" target="runtimes\win10-x64\lib\uap10.0" />

        <file src="..\bin\arm\Release\MultiArchitectureUwpLibrary.dll" target="runtimes\win10-arm\lib\uap10.0" />
        <file src="..\bin\arm\Release\MultiArchitectureUwpLibrary.pdb" target="runtimes\win10-arm\lib\uap10.0" />
    </files>
</package>

当然,缺少的部分是参考组件.幸运的是,这很容易解决——参考程序集是一个 AnyCPU 程序集,它定义了与运行时程序集包含的相同的类和方法.它的主要目的是为编译器提供一个可以使用的引用,因此编译器可以验证所有方法调用实际上都引用了将在运行时存在的方法.参考程序集中的实际代码(如果有)不用于任何用途.

The missing piece, of course, is the reference assembly. Thankfully, this is rather simple to solve - a reference assembly is an AnyCPU assembly that defines the same classes and methods that the runtime assemblies contain. Its main purpose is to provide the compiler a reference to work with, so the compiler can verify that all method calls are actually referencing methods that will exist at runtime. The actual code in the reference assembly (if any) is not used for anything.

由于所有特定于体系结构的构建都公开相同的 API 表面,因此我们可以简单地采用其中的任何一个并指示编译器将其用作参考程序集.Windows SDK 包含一个名为 CorFlags.exe 的实用程序,可用于将 x86 程序集转换为 AnyCPU 程序集,从而使这成为可能.

Since all your architecture-specific builds expose the same API surface, we can simply take any one of them and instruct the compiler to use it as the reference assembly. The Windows SDK contains a utility named CorFlags.exe that can be used to convert an x86 assembly to an AnyCPU assembly, making this possible.

下面是一个包创建脚本,它在打包库之前创建所需的参考程序集.它假定 Windows SDK 安装在标准位置.要了解逻辑的详细信息,请参阅内嵌注释.

Below is a package creation script that creates the required reference assemblies before packaging the library. It assumes the Windows SDK is installed in the standard location. To understand the details of the logic, see the inline comments.

# Any assembly matching this filter will be transformed into an AnyCPU assembly.
$referenceDllFilter = "MultiArchitectureUwpLibrary.dll"

$programfilesx86 = "${Env:ProgramFiles(x86)}"
$corflags = Join-Path $programfilesx86 "Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6 Tools\x64\CorFlags.exe"

If (!(Test-Path $corflags))
{
    Throw "Unable to find CorFlags.exe"
}

$solutionRoot = Resolve-Path ..\..
$topLevelDirectories = Get-ChildItem $solutionRoot -Directory
$binDirectories = $topLevelDirectories | %{ Get-ChildItem $_.FullName -Directory -Filter "bin" }

# Create reference assemblies, because otherwise the NuGet packages cannot be used.
# This creates them for all outputs that match the filter, in all output directories of all projects.
# It's a bit overkill but who cares - the process is very fast and keeps the script simple.
Foreach ($bin in $binDirectories)
{
    $x86 = Join-Path $bin.FullName "x86"
    $any = Join-Path $bin.FullName "Reference"

    If (!(Test-Path $x86))
    {
        Write-Host "Skipping reference assembly generation for $($bin.FullName) because it has no x86 directory."
        continue;
    }

    if (Test-Path $any)
    {
        Remove-Item -Recurse $any
    }

    New-Item $any -ItemType Directory
    New-Item "$any\Release" -ItemType Directory

    $dlls = Get-ChildItem "$x86\Release" -File -Filter $referenceDllFilter

    Foreach ($dll in $dlls)
    {
        Copy-Item $dll.FullName "$any\Release"
    }

    $dlls = Get-ChildItem "$any\Release" -File -Filter $referenceDllFilter

    Foreach ($dll in $dlls)
    {
        Write-Host "Converting to AnyCPU: $dll"

        & $corflags /32bitreq- $($dll.FullName)
    }
}

# Delete any existing output.
Remove-Item *.nupkg

# Create new packages for any nuspec files that exist in this directory.
Foreach ($nuspec in $(Get-Item *.nuspec))
{
    .\NuGet.exe pack "$nuspec"
}

您可能需要调整脚本中的路径以匹配您的解决方案中使用的约定.

You may need to adjust the paths in the script to match the conventions used in your solution.

运行此脚本以创建 NuGet 包,使您的库能够在其所有特定于体系结构的变体中使用!请记住在创建 NuGet 包之前使用所有架构的发布配置构建您的解决方案.

Run this script to create a NuGet package that enables your library to be used in all its architecture-specific variants! Remember to build your solution using the Release configuration for all the architectures before creating the NuGet package.

示例库和相关的打包文件在 GitHub 上提供.这个答案对应的解决方案是MultiArchitectureUwpLibrary.

A sample library and the relevant packaging files are available on GitHub. The solution corresponding to this answer is MultiArchitectureUwpLibrary.

这篇关于如何打包面向通用 Windows 平台的多架构 .NET 库?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆