Description
When developing a .NET debugger capable of targeting .NET Core applications, a library called dbgshim
is used to bootstrap the entire process. While dbgshim
is included with the .NET Runtime, it is generally recommended to ship dbgshim
with your application so that your debugger doesn't have to worry about locating dbgshim
on the target system; simply load your dbgshim
into your debugger, point it at a given process and ask "does this process host a CLR we can attach to?"
Microsoft has shipped a NuGet package for dbgshim
that includes sub-packages with native dbgshim
implementations for all of the various operating systems/architectures it supports.
If you attempt to reference this NuGet package however, neither dbgshim
nor the runtimes
directory inside the sub-packages are copied to your output folder, regardless of whether you are doing dotnet build
or dotnet pack
. For the purposes of developing an application, I expect these files should be copied to the output directory upon doing dotnet build
(so that you can find and load them in your application)
After doing some debugging, I have identified that the reason dbgshim
does not get copied to the output directory is that the version of dbgshim
in the package is considered to conflict with the version of dbgshim
included in the runtime, and thus the runtime always wins.
The sequence of events that lead up to the conflict being detected are as follows:
-
The ResolvePackageFileConflicts task is run
-
PlatformManifestReader reads a
PlatformManifest.txt
file for the current runtime -
As
dbgshim
is listed in this file, a conflict item is added for it -
ConflictResolver tries to resolve the platform dbgshim against the NuGet package dbgshim
-
PackageOverrideResolver is called upon to see if there are potentially any overrides that could be taken advantage of
-
As there are no overrides, the platform dbgshim wins by virtue of having a higher file version and you get the following error in the MSBuild log
Encountered conflict between 'Platform:dbgshim.dll' and 'CopyLocal:C:\Users\user.nuget\packages\microsoft.diagnostics.dbgshim.win-x64\7.0.430602\runtimes\win-x64\native\dbgshim.dll'. Choosing 'Platform:dbgshim.dll' because file version '8.0.23.36403' is greater than '7.0.8.30602'. (TaskId:82)
If DbgShim is removed from PlatformManifest.txt
, dbgshim
and/or the runtimes
folder is copied to the output directory upon building, as expected
In dotnet/sdk#2221 an issue similar to this is described, and there is a workaround in the code to deal with this, however it seems this workaround is predicated on having a "reference" of some kind to the conflicting assembly, which isn't the case when we've got a native, unreferenced library.
As a creative workaround, I attempted to add a target to "enhance" the list of package overrides with the goal of telling the conflict resolver to use my package as per this line
<Target Name="Hack" BeforeTargets="_HandlePackageFileConflicts">
<ItemGroup>
<PackageConflictOverrides Include="Microsoft.Diagnostics.DbgShim.win-x64">
<!-- Use an arbitrarily high number so we always win -->
<OverriddenPackages>
Microsoft.NETCore.App.Ref|99.0
</OverriddenPackages>
</PackageConflictOverrides>
</ItemGroup>
</Target>
Unfortunately, I found this does not work because the platform dbgshim
has a null
PackageVersion
, hence the check in this if
statement fails
I'm not sure whether I would recommend having dbgshim
simply be omitted from PlatformManifest.txt
, as this file may be used for other purposes as well. Arguably, dbgshim
is kind of a special case, so potentially there could just be special rules for how to handle conflicts against dbgshim.dll
, libdbgshim.so
and libdbgshim.dylib