如何打包面向通用 Windows 平台的多体系结构 .NET 库?
How to package a multi-architecture .NET library that targets the Universal Windows Platform?
如何打包用 C# 编写的仅提供依赖于体系结构的构建的通用 Windows 平台库?为了说明起见,假设我有一些特定于体系结构的代码有条件地为每个体系结构编译(使用 #if ARM
和等效项)。
需要说明的是,我的库不存在 AnyCPU 版本 - 只有 x86、x64 和 ARM。
一种等效且可能更常见的情况是我依赖于仅作为特定于体系结构的构建提供的外部库(例如 Win2D)。为了保持上下文简单,我们假设没有依赖关系并且只涉及我自己的代码 - 无论哪种方式,解决方案都应该简化为相同的东西。
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:
这个答案建立在 and the 的基础上。请先阅读链接的答案以更好地理解以下内容。
我假设您所有的 architecture-specific 构建都暴露相同的 API 表面,只有那些 API 的实现不同。
此方案的主要复杂之处在于构建工具链 需要 一个 AnyCPU 程序集用于 compile-time 引用解析,即使此程序集从未在运行时使用过。由于您的场景没有 AnyCPU 构建输出,我们需要找到解决方法。此处适用的概念是引用程序集 - AnyCPU 程序集仅在 compile-time 处用于引用验证。因此,要发布您的库,您需要创建一个参考程序集并按如下所述打包资产。
为简单起见,我假设您的库不依赖于其他 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 文档和资源文件,而 architecture-specific 资产位于运行时目录下。
其中大部分都非常简单,可以使用基于以下模板创建的 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 程序集,它定义了与运行时程序集包含的相同 类 和方法。它的主要目的是为编译器提供一个可使用的引用,以便编译器可以验证所有方法调用实际上都是在运行时存在的引用方法。参考程序集中的实际代码(如果有)未用于任何用途。
由于您所有的 architecture-specific 构建都公开相同的 API 表面,我们可以简单地获取其中任何一个并指示编译器将其用作参考程序集。 Windows SDK 包含一个名为 CorFlags.exe 的实用程序,可用于将 x86 程序集转换为 AnyCPU 程序集,使之成为可能。
下面是一个包创建脚本,用于在打包库之前创建所需的参考程序集。它假定 Windows SDK 安装在标准位置。要了解逻辑的详细信息,请参阅内联注释。
# 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 包,使您的库能够在其所有 architecture-specific 变体中使用!请记住在创建 NuGet 程序包之前使用所有 体系结构的发布配置来构建您的解决方案。
示例库和相关打包文件available on GitHub。这个答案对应的解决方案是MultiArchitectureUwpLibrary.
如何打包用 C# 编写的仅提供依赖于体系结构的构建的通用 Windows 平台库?为了说明起见,假设我有一些特定于体系结构的代码有条件地为每个体系结构编译(使用 #if ARM
和等效项)。
需要说明的是,我的库不存在 AnyCPU 版本 - 只有 x86、x64 和 ARM。
一种等效且可能更常见的情况是我依赖于仅作为特定于体系结构的构建提供的外部库(例如 Win2D)。为了保持上下文简单,我们假设没有依赖关系并且只涉及我自己的代码 - 无论哪种方式,解决方案都应该简化为相同的东西。
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:
这个答案建立在
我假设您所有的 architecture-specific 构建都暴露相同的 API 表面,只有那些 API 的实现不同。
此方案的主要复杂之处在于构建工具链 需要 一个 AnyCPU 程序集用于 compile-time 引用解析,即使此程序集从未在运行时使用过。由于您的场景没有 AnyCPU 构建输出,我们需要找到解决方法。此处适用的概念是引用程序集 - AnyCPU 程序集仅在 compile-time 处用于引用验证。因此,要发布您的库,您需要创建一个参考程序集并按如下所述打包资产。
为简单起见,我假设您的库不依赖于其他 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 文档和资源文件,而 architecture-specific 资产位于运行时目录下。
其中大部分都非常简单,可以使用基于以下模板创建的 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 程序集,它定义了与运行时程序集包含的相同 类 和方法。它的主要目的是为编译器提供一个可使用的引用,以便编译器可以验证所有方法调用实际上都是在运行时存在的引用方法。参考程序集中的实际代码(如果有)未用于任何用途。
由于您所有的 architecture-specific 构建都公开相同的 API 表面,我们可以简单地获取其中任何一个并指示编译器将其用作参考程序集。 Windows SDK 包含一个名为 CorFlags.exe 的实用程序,可用于将 x86 程序集转换为 AnyCPU 程序集,使之成为可能。
下面是一个包创建脚本,用于在打包库之前创建所需的参考程序集。它假定 Windows SDK 安装在标准位置。要了解逻辑的详细信息,请参阅内联注释。
# 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 包,使您的库能够在其所有 architecture-specific 变体中使用!请记住在创建 NuGet 程序包之前使用所有 体系结构的发布配置来构建您的解决方案。
示例库和相关打包文件available on GitHub。这个答案对应的解决方案是MultiArchitectureUwpLibrary.