Issues using nuget packages with C# libraries compiled for specific architecture.
William-Froelich opened this issue · 3 comments
Details about Problem
I am creating two component libraries, one in C++ and one in C# for Windows UWP in a single solution. The libraries will be packaged separately but there will be a dependency such that the C# library depends on C++ library which requires the C# be built architecture-specific.
The resulting C# nuget package will not allow compilation of any architecture other than the one that matches the compile flags in the ref .winmd file. If x86 is chosen for the ref, then only x86 will work. Manually stripping the 32BITREQ flag with corflags does allow for a workaround.
Is this the expected way of handling architecture-specific C# libraries? If so it should probably be documented. If not, is my configuration wrong? Is this just not a normal use case?
NuGet.exe - Version (4.8.1.5435)
dotnet.exe - Version (2.1.801)
Visual Studio 2017/Visual Studio 2019 Pro
Win10 v1903 Enterprise
Detailed repro steps so we can see the same problem
- Create a native Windows Runtime Component (Universal Windows - C++/CX)
- Create a C# Universal Windows Component Library in the same solution
- Create a dependency in the C# library on the Native component
3a. Remove "Any CPU" build configuration and replace with x86 and x64 configurations (It won't build due to the mismatch without this step) - Create .nuspec for both libraries and package separately.
- Generate and test the generated nuget packages locally (if you packaged the x64 .winmd in ref, then use x86 or vice versa)
Other suggested things
I saw a discussion on a similar issue and read that I can manually strip the CPU requirements by running corflags /32BITREQ-
I can confirm that taking this step did make the nuget package work when tested, however, this seems like it shouldn't be a manual step. Either my configuration is wrong or there's a missed case.
Verbose Logs
1>------ Build started: Project: NugetPackageTest, Configuration: Release x86 ------
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Current\Bin\Microsoft.Common.CurrentVersion.targets(2106,5): error MSB3270: There was a mismatch between the processor architecture of the project being built "x86" and the processor architecture of the reference "C:\Users\WilliamF3\.nuget\packages\Csharp.Ui.Component.Library\0.0.1\lib\uap10.0\csharp_project.winmd", "AMD64". This mismatch may cause runtime failures. Please consider changing the targeted processor architecture of your project through the Configuration Manager so as to align the processor architectures between your project and references, or take a dependency on references with a processor architecture that matches the targeted processor architecture of your project.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Sample Project
I can provide a skeleton of this setup if needed but I cannot share the original project.
native Library .nuspec:
<?xml version="1.0"?>
<package >
<metadata>
<id>Name Removed</id>
<version>0.0.1</version>
<title>Name Removed</title>
<authors>Nobody Inc.</authors>
<owners>Nobody Inc.</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Native Library</description>
<releaseNotes>Initial Release</releaseNotes>
<copyright>Copyright 2019</copyright>
<tags>windows uwp</tags>
</metadata>
<files>
<!-- WinMd and IntelliSense files -->
<file src="..\x64\Release\native_project\native_project.winmd" target="lib\uap10.0"/>
<!-- 64 bit Release DLL and Resource files -->
<file src="..\x64\Release\native_project\native_project.dll" target="runtimes\win10-x64\native"/>
<file src="..\x64\Release\native_project\native_project.pri" target="runtimes\win10-x64\native"/>
<!-- 32 bit Release DLL and Resource files -->
<file src="bin\x64\Release\x86\native_project\native_project.dll" target="runtimes\win10-x86\native"/>
<file src="bin\x64\Release\x86\native_project\native_project.pri" target="runtimes\win10-x86\native"/>
<!-- .targets Used to specify supported architectures mappings-->
<file src="native_project.targets" target="build\native"/>
</files>
</package>
C# UWP component Library nuspec:
<?xml version="1.0"?>
<package >
<metadata>
<id>Csharp.Ui.Component.Library</id>
<version>0.0.1</version>
<title>UI Elements</title>
<authors>Nobody Inc.</authors>
<owners>Nobody Inc.</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>The UI elements For the native library</description>
<releaseNotes>Initial Release</releaseNotes>
<copyright>Copyright 2019</copyright>
<tags>uwp windows</tags>
<dependencies>
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.2.8" />
</dependencies>
</metadata>
<files>
<!-- WinMd and IntelliSense files -->
<!-- This is manually copied and stripped of the 32BITREQ flag -->
<file src="csharp_project.winmd" target="ref\uap10.0" />
<file src="bin\x64\Release\csharp_project.winmd" target="runtimes\win10-x64\native" />
<file src="bin\x64\Release\csharp_project.pdb" target="runtimes\win10-x64\native" />
<file src="bin\x64\Release\csharp_project.pri" target="runtimes\win10-x64\native" />
<file src="bin\x64\Release\x86\csharp_project\csharp_project.winmd" target="runtimes\win10-x86\native" />
<file src="bin\x64\Release\x86\csharp_project\csharp_project.pdb" target="runtimes\win10-x86\native" />
<file src="bin\x64\Release\x86\csharp_project\csharp_project.pri" target="runtimes\win10-x86\native" />
<!-- XAML controls -->
<file src="bin\x64\Release\csharp_project.xr.xml" target="runtimes\win10-x64\native\csharp_project" />
<file src="bin\x64\Release\x86\csharp_project.xr.xml" target="runtimes\win10-x86\native\csharp_project" />
<!-- Assets -->
<file src="Assets\**" target="runtimes\win10-x64\native\csharp_project\Assets" />
<file src="Assets\**" target="runtimes\win10-x86\native\csharp_project\Assets" />
<!-- .targets Used to specify supported architectures mappings-->
<file src="csharp_project.targets" target="build\uap10.0" />
</files>
</package>
Yes, the behaviour you see is expected. The compiler only allows references to assemblies that are compatible with the project being compiled. The files under ref/
will be passed to the compiler, regardless of the architecture of the project being compiled, and there are no RID specific sub-folders unlike the runtimes/
folder.
.winmd
files support AnyCPU, even though UWP executables do not. Therefore, you could compile a AnyCPU version to put in your ref/
folder in the nupkg, and keep the other architectures in the specific runtimes/
folders. If your project contains a lot of code, you could use "create reference winmd" to create a smaller file to put in ref/
.
Alternatively, as you already found, removing the flag that makes the assembly architecture specific works since the ref/
file won't be used at runtime.
Ok, but it seems that it's impossible to generate an "any CPU" version of a winmd if the library has dependencies on native code, unless there's something obvious I am missing. The link step will fail because the project has a dependency that doesn't match. I suppose one could copy the generated .winmd at that point but it is technically a build error.
Is there a way to just skip the linking step for it? I couldn't find info on the topic "create reference winmd". Is there a discussion or link I should be checking out ?