此答案基于principles of .NET Framework library packaging 和principles of Universal Windows Platform library packaging。首先阅读链接的答案以更好地理解以下内容。
我假设您所有特定于架构的构建都公开相同的 API 表面,只是这些 API 的实现有所不同。
此方案的主要复杂之处在于构建工具链需要一个 AnyCPU 程序集来解析编译时引用,即使此程序集从未在运行时使用。由于您的方案没有 AnyCPU 构建输出,因此我们需要找到一种解决方法。此处适用的概念是参考程序集 - AnyCPU 程序集仅在编译时用于参考验证。因此,要发布您的库,您需要创建一个参考程序集并按如下所述打包资产。
为简单起见,我假设您的库不依赖于其他 NuGet 包。在实践中不太可能出现这种情况,但依赖管理已经包含在上面链接的其他答案中,因此从这个答案中省略了。
所需的 NuGet 包结构如下:
+---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 文档和资源文件,而特定于体系结构的资产则在 runtimes 目录下构建。
其中大部分都非常简单,可以通过使用基于以下模板创建的 nuspec 文件来完成:
<?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 程序集,它定义了运行时程序集包含的相同类和方法。它的主要目的是为编译器提供一个可以使用的引用,因此编译器可以验证所有方法调用实际上是引用将在运行时存在的方法。参考程序集中的实际代码(如果有)不用于任何用途。
由于所有特定于架构的构建都公开了相同的 API 表面,因此我们可以简单地采用其中任何一个并指示编译器将其用作参考程序集。 Windows SDK 包含一个名为 CorFlags.exe 的实用程序,可用于将 x86 程序集转换为 AnyCPU 程序集,使之成为可能。
下面是一个包创建脚本,它在打包库之前创建所需的参考程序集。它假定 Windows SDK 安装在标准位置。要了解逻辑的详细信息,请参阅内联 cmets。
# 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"
}
您可能需要调整脚本中的路径以匹配解决方案中使用的约定。
运行此脚本以创建一个 NuGet 包,使您的库能够在其所有特定于体系结构的变体中使用!请记住在创建 NuGet 包之前使用所有架构的发布配置构建您的解决方案。
一个示例库和相关的打包文件是available on GitHub。这个答案对应的解决方案是MultiArchitectureUwpLibrary。