如何打包用C#编写的通用Windows平台库,它只提供与体系结构相关的构建?为了便于说明,假设我为每个体系结构有条件地编译了一些特定于体系结构的代码(使用 #if ARM
和等价物)。
要清楚,我的库没有AnyCPU构建 - 只有x86,x64和ARM。
等效且可能更常见的情况是我依赖于外部库,该外部库仅作为特定于体系结构的构建提供(例如, Win2D)。为了保持上下文简单,让我们假设没有依赖关系,只涉及我自己的代码 - 解决方案应该以任何方式减少到相同的事情。
这是一系列问题和答案,记录了我对现代NuGet包创作主题的研究结果,特别关注NuGet 3引入的变化。您可能还对一些相关问题感兴趣:
这个答案建立在 .NET Framework库包装的原理 和 通用Windows平台库包装的原则。首先阅读链接的答案,以便更好地理解以下内容。
我将假设所有特定于体系结构的构建都暴露了相同的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
directory包含引用程序集,XML文档和资源文件,而体系结构特定的资产在运行时目录下构建。
大部分内容非常简单,可以使用基于以下模板创建的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安装在标准位置。要了解逻辑的详细信息,请参阅内联注释。
# 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包,使您的库可以在其所有特定于体系结构的变体中使用!请记住使用Release配置构建解决方案 所有 创建NuGet包之前的体系结构。
一个示例库和相关的打包文件 可在GitHub上找到。与此答案对应的解决方案是MultiArchitectureUwpLibrary。
这个答案建立在 .NET Framework库包装的原理 和 通用Windows平台库包装的原则。首先阅读链接的答案,以便更好地理解以下内容。
我将假设所有特定于体系结构的构建都暴露了相同的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
directory包含引用程序集,XML文档和资源文件,而体系结构特定的资产在运行时目录下构建。
大部分内容非常简单,可以使用基于以下模板创建的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安装在标准位置。要了解逻辑的详细信息,请参阅内联注释。
# 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包,使您的库可以在其所有特定于体系结构的变体中使用!请记住使用Release配置构建解决方案 所有 创建NuGet包之前的体系结构。
一个示例库和相关的打包文件 可在GitHub上找到。与此答案对应的解决方案是MultiArchitectureUwpLibrary。