_From @bording on July 27, 2017 3:12_
When a NuGet package includes PDBs in its lib folder, the legacy project system copies the PDB as well as the assembly into the build output folder.
The new project system only copies the assembly.
We've recently added source link support , and when using the new project system with a .NET Core target, everything works fine since the PDB is referenced correctly from the global NuGet package folder.
However, because the PDB isn't copied for a .NET Framework target, we don't get any of the benefit of our sourcelinked PDB.
It would be great if the new project system would behave like the legacy project system in this case, and also copy the PDB into the project's build output folder.
_Copied from original issue: dotnet/project-system#2667_
EDIT: We are also using this issue to track copying XML files
https://github.com/wli3/sdk/commit/0fefc410cda31fd15cb131bb5ff2147a285bd4ac
an end to end repo/test for this bug
https://github.com/dotnet/sdk/blob/98ea6b2a1701ee9e2e3dd8122c77d5a2adcc5bb7/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.PackageDependencyResolution.targets#L501
decide what to copy local, there should be a FileGroup called something like "DebugSymbol". However, FileGroup are from NuGet
We need NugGet.ProjectModel to tell they are debug symbol
If we add a FileGroup for .pdbs to the asset file, it probably makes sense to add one for the .xml documentation files too.
Maybe even for everything that msbuild considers to be related file extensions:
c#
private string[] _relatedFileExtensions = new string[] { ".pdb", ".xml", ".pri" };
@wli3 Is there any more progress on this?
Really would like this being fixed. Having dependent PDBs is crucial for in-production diagnostics + debugging.
I figured out a workaround which can be pasted into the csproj:
<!-- https://github.com/dotnet/sdk/issues/1458 -->
<PropertyGroup>
<AllowedReferenceRelatedFileExtensions>.pdb</AllowedReferenceRelatedFileExtensions>
</PropertyGroup>
<Target Name="AddReferenceRelatedPathsToCopyLocal" AfterTargets="ResolveAssemblyReferences">
<ItemGroup>
<ReferenceCopyLocalPaths Include="@(_ReferenceRelatedPaths)" />
</ItemGroup>
</Target>
Heads up: this workaround will be broken by a change which has just been merged: https://github.com/dotnet/sdk/pull/1725#issuecomment-347672884
@nguerrera wants to fix this issue in the same release so I'll wait and see.
Any status update on this? I see it's now been moved to the 2.2.0 milestone.
@nguerrera There is no milestone on #1725, but am I correct in thinking that those changes haven't been included in a release yet?
@bording You are correct. They have been included only in unofficial 2.2.0 daily builds.
Small update: I am currently looking at optimization of how we resolve assets and I think I have a way to replicate the related file semantics of RAR, but using runtime assets correctly and without needing to probe on disk and without actually needing nuget changes. Depending on how things go, it is possible that 2.2.0 preview 1 will ship with #1725 but not the fix for this, but the plan or record is that we don't RTM 2.2 in that state.
If you could squeeze it in: It would be really cool to have metadata about satellite assemblies available as well. This would allow whipping up some targets to filter satellites coming from NuGet by locale. This has come up in https://github.com/dotnet/cli/issues/8060 and https://stackoverflow.com/questions/47942818/how-to-turn-off-language-specific-output-for-net-core-applications.
Will do.
Which release of VS is this currently slated for? I'm keeping an eye out so I can update instructions so that people know how to use NuGet packages that contain PDBs for source-stepping.
Adding @livarcocc for the question about VS release. I don't know if the schedule is set yet.
Is 15.6 definitely out of the question?
Yes, 15.6 is out of the question. This is being tracked for used to be called 2.2.0, but will actually be 2.1.300, which will not ship at the same time as 15.6.
@nguerrera How is the transition to the new SDK versioning scheme happening? Currently we have 2.1.4, so what's the next release going to be? Are there going to be additional 2.1 SDKs that still have the 2.0 runtime? Is it known which SDK release will be the one that includes the 2.1 runtime?
I'm trying to get a feel for how far out 2.1.300 is because not having PDBs copied is a pretty big pain point.
@bording see https://github.com/dotnet/designs/pull/29 for the new versioning scheme.
@dasMulli I follow that repo, so I've seen that already, but thanks for the link!
My questions were more about things that aren't explicitly called out in that document. It implies that 2.1.300 will be the first version to include the 2.1 runtime. It also mentions 2.1.100. Would that be an intermediate feature release, or would that be a release from the servicing branch?
I'd really like to see this scheduled sooner than the SDK release tied to the 2.1 runtime, so I'm trying to see if my understanding of the new versioning scheme and the transition to it is correct.
This essentially kills source link support for any projects using the new .NET Core project system targeting the .NET Framework. That is a large percentage. Please fix it soon. I added a test case in https://github.com/Microsoft/msbuild/issues/2920.
I just released SourceLink 2.8.0 which has the workaround in SourceLink.Copy.PdbFiles
. Just add a reference.
<Project>
<ItemGroup>
<PackageReference Include="SourceLink.Copy.PdbFiles" Version="2.8.0" PrivateAssets="All" />
</ItemGroup>
</Project>
I am working on this now
I am taking a slight detour to fix some more perf issues in ResolvePackageAssets first. Doing it in the other order will cause the fix for this to negatively impact perf. This is still coming soon.
Note to self: Make sure to check publish behavior: pdbs from packages should be published unless symbol publishing is disabled. This applies to more to .NET Core, not just .NET Framework.
@jnm2 Hi, the .pdb workaround works great. Is there a similar work around for .mdb? Thanks
@ocapio Yes, just replace with <AllowedReferenceRelatedFileExtensions>.pdb;.mdb</AllowedReferenceRelatedFileExtensions>
in the snippet above.
@jnm2 Thanks. Though, it looks like it ignores other extensions outside of .pdb. Perhaps, it's the version of dotnet that I am using this may not be integrated into Visual Studio (using 15.6.4)?
I'm not familiar with .mdb files. Can you point me to an example NuGet package containing the .mdb? And maybe an example project using the package where the workaround is failing to copy the .mdb (but would copy a .pdb)?
I typically just use http://msbuildlog.com to poke my head around inside and deduce the workaround.
I'm using an older version of mono, can calling pdb2mdb to convert pdb to mdb. Then in the csproj, I have
<ItemGroup>
<None Include="bin\$(Configuration)\net46\Example.dll.mdb" Pack="true" PackagePath="lib\net46" />
<None Include="bin\$(Configuration)\netstandard1.4\Example.dll.mdb" Pack="true" PackagePath="lib\netstandard1.4" />
</ItemGroup>
This puts the .mdb file into the nuget package. Actually, looks like it can be any file.
Unfortunately, I do not have a good example package/project since that is sort of confidential with my company. I'll see if I can create one outside. Though, you should be able to create a package given the method above that is working for me.
Easy enough to add an .mdb to a nupkg to test. What about the consuming csproj; is that the new SDK-style csproj? Are you using Mono's MSBuild 15?
@jnm2 Yes. I'm using the new SDK csproj. We have a custom Mono using MSBuild 15.
Finally got back to this and there are some complications with conflict resolution and publish, but I hope to push through them. Need to think about the best solution for a bit.
I found an alternate solution. For the packages that has the .mdb (or what
ever file the app wants to consume) , add a .target file (if none exist) to
do the copy at the postbuild event in the package. So, the contents of the
package gets copied over rather than trying to get the app that is
consuming the package to get the file. This assumes that you have control
of the package.
On Tue, Apr 3, 2018 at 10:55 AM, Nick Guerrera notifications@github.com
wrote:
Finally got back to this and there are some complications with conflict
resolution and publish, but I hope to push through them. Need to think
about the best solution for a bit.—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/dotnet/sdk/issues/1458#issuecomment-378340060, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AZzCSOtGC48vSDzXYc5kL9Sx-olGQTXNks5tk7epgaJpZM4OkxUT
.
So, unfortunately, this has been moved out one release to 2.1.400, which will ship in VS 15.8 timeframe.
In the meantime, here is a workaround that will work with all SDK versions up to and including 2.1.300:
<Target Name="_ResolveCopyLocalNuGetPackagePdbs"
Condition="$(CopyLocalLockFileAssemblies) == true"
AfterTargets="ResolveReferences">
<ItemGroup>
<ReferenceCopyLocalPaths
Include="@(ReferenceCopyLocalPaths->'%(RootDir)%(Directory)%(Filename).pdb')"
Condition="'%(ReferenceCopyLocalPaths.NuGetPackageId)' != ''
and Exists('%(RootDir)%(Directory)%(Filename).pdb')" />
</ItemGroup>
</Target>
This workaround is also more correct than the one above that piggy backs on MSBuild ResolveAssemblyReferences (RAR) because it will find pdb in lib/ folder when RAR sees only the ref/ folder.
Folks following this issue, please try the above workaround with:
And let me know if it works for you.
(EDIT: Missing qualification on ReferenceCopyLocalPaths.NuGetPackageId added to workaround. It would fail if there were non-nuget references previously.)
If you're wondering why we didn't use a simple target like above workaround in the product as the fix, the main issue is performance, which was a major focus of the 2.1.300 release. Probing like this was shown to be a bottleneck in RAR (hence why the RAR improvements for packages broke the RAR-based workarounds). In this case, we actually have all of the information about which pdbs exist in the NuGet assets file and so it can be done without probing on each incremental build. However, implementing that has some subtle interactions between features and was too big of a change to get in to 2.1.300 release.
Workaround is working which is great.
SourceLink.Copy.PdbFiles 2.8.3 has the updated workaround.
<Project>
<ItemGroup>
<PackageReference Include="SourceLink.Copy.PdbFiles" Version="2.8.3" PrivateAssets="All" />
</ItemGroup>
</Project>
@nguerrera, @paulomorgado is reporting that the workaround isn't copying files to the output folder for him. The pdb files need to be there for source link enabled debugging to work.
https://github.com/ctaggart/SourceLink/blob/2.8.2/SourceLink.Copy.PdbFiles/SourceLink.Copy.PdbFiles.targets
https://github.com/ctaggart/SourceLink/blob/2.8.3/SourceLink.Copy.PdbFiles/SourceLink.Copy.PdbFiles.targets
https://github.com/ctaggart/SourceLink/pull/352
@ctaggart @paulomorgado Can you provide a project that includes the workaround and doesn't behave as you'd expect?
Sorry if I was not clear, @ctaggart. The PDBs are copied. What I was trying to do is also copy the XML doc files and that doesn't work.
I've added this to my .csproj:
<AllowedReferenceRelatedFileExtensions>$(AllowedReferenceRelatedFileExtensions);.xml</AllowedReferenceRelatedFileExtensions>
but the XML files are not copied.
What am I doing wrong?
The workaround by @nguerrera worked in a nuget project (one nuget catches pdbs from another nuget it depends upon).
But this workaround does not work for a VS extension which depends upon a nuget and wants to include the nuget's pdbs into vsix.
Is it supposed to work there?
as a workaround for VSIX packaging I modified the instructions by @nguerrera this way:
<PropertyGroup>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<Target Name="_ResolveCopyLocalNuGetPackagePdbs"
Condition="$(CopyLocalLockFileAssemblies) == true"
AfterTargets="ResolveReferences"
BeforeTargets="GetVsixSourceItems">
<ItemGroup>
<VSIXCopyLocalReferenceSourceItem
Include="@(ReferenceCopyLocalPaths->'%(RootDir)%(Directory)%(Filename).pdb')"
Condition="Exists('%(RootDir)%(Directory)%(Filename).pdb')" />
</ItemGroup>
</Target>
and it seems to work for my VSIXes.
The modifications are:
CopyLocalLockFileAssemblies
definition which seems to be missing in VSIX project.BeforeTargets="GetVsixSourceItems"
as we need the list at hand before the VSIX gets packaged.%(ReferenceCopyLocalPaths.NuGetPackageId)' != ''
condition as it fails for some reason in my VSIX project.ReferenceCopyLocalPaths
to VSIXCopyLocalReferenceSourceItem
.@paulomorgado Workaround for xml files and PDBs would be:
<Target Name="_ResolveCopyLocalNuGetPackagePdbsAndXml"
Condition="$(CopyLocalLockFileAssemblies) == true"
AfterTargets="ResolveReferences">
<ItemGroup>
<ReferenceCopyLocalPaths
Include="@(ReferenceCopyLocalPaths->'%(RootDir)%(Directory)%(Filename).pdb')"
Condition="'%(ReferenceCopyLocalPaths.NuGetPackageId)' != ''
and Exists('%(RootDir)%(Directory)%(Filename).pdb')" />
<ReferenceCopyLocalPaths
Include="@(ReferenceCopyLocalPaths->'%(RootDir)%(Directory)%(Filename).xml')"
Condition="'%(ReferenceCopyLocalPaths.NuGetPackageId)' != ''
and Exists('%(RootDir)%(Directory)%(Filename).xml')" />
</ItemGroup>
</Target>
For now, I got this working like this:
<Target Name="CopyXmlDocumentationToOutput" AfterTargets="CoreCompile">
<ItemGroup>
<__XmlDocumentationFiles Include="@(ReferencePathWithRefAssemblies->'%(Identity)')" Condition="Exists($([System.IO.Path]::ChangeExtension(%(Identity), '.xml')))">
<XmlDocumentationPath>$([System.IO.Path]::ChangeExtension(%(Identity), '.xml'))</XmlDocumentationPath>
</__XmlDocumentationFiles>
</ItemGroup>
<ItemGroup>
<__XmlDocumentationFiles Include="@(ReferencePathWithRefAssemblies->'%(Identity)')" Condition="Exists($([System.IO.Path]::ChangeExtension(%(Identity), '.pdb')))">
<XmlDocumentationPath>$([System.IO.Path]::ChangeExtension(%(Identity), '.pdb'))</XmlDocumentationPath>
</__XmlDocumentationFiles>
</ItemGroup>
<Copy SourceFiles="@(__XmlDocumentationFiles->'%(XmlDocumentationPath)')" DestinationFolder="$(OutDir)" SkipUnchangedFiles="true" />
</Target>
But yours looks better.
Do these changes go in the project in NuGet or the project using the NuGet package?
The workaround goes in the project that is using the package. A good place to put it is in Directory.Build.targets so that your project files are not cluttered and so that you can apply it to multiple projects in a solution.
https://docs.microsoft.com/en-us/visualstudio/msbuild/customize-your-build
It's not working for me.
I have tried, Build, Debug and Release, and also Publish to a new directory. When I publish I get the DLLs but not the XML file from the source Nuget directory
Which workaround are you using. This one : https://github.com/dotnet/sdk/issues/1458#issuecomment-401497095 ?
What is your TargetFramework?
netcoreapp2.1
VS 15.7.5
I upgraded VS to 15.7.6 I still don't get xml from nuget references when I publish
Ok, the difference is the netcoreapp TFM. CopyLocalLockFileAssemblies will be false there. Removing the condition on that should work, but then you'll get junk xml/pdb in your non publish output without the dll. I need to look at how to make this work for publish without causing that. I have an idea and will follow up today.
I removed CopyLocalLockFileAssemblies=true and nothing changed on a build or publish.
I have
<Target Name="_ResolveCopyLocalNuGetPackagePdbs" AfterTargets="ResolveReferences">...
in a Directory.Build.props file in the same directory as the solution file
Ok, give me a chance to play with this later today. Note that this issue was about .net framework projects, and we're in to different (but related) territory.
I looked at this and I understand the issue, but I'm not ready to share a workaround just yet. Hopefully I will have something for you tomorrow.
Is there progress on this? I'm nearing the point where I'll attempt to copy the XML in a post build event, just to get the xml files
@bcbeatty Nick went on a well-deserved vacation this week and next, we apologize for the inconvenience.
I apologize for the long delay here. Indeed I was on vacation and then this got lost when I returned. I now have a workaround for the publish case as well.
There are two issues:
On platforms that copy nuget dlls during build (.NET Framework), the .pdb and .xml files are not copied to build output.
When publishing for any platform, nuget .pdb and .xml files are not copied to the publish output
<Target Name="_ResolvePublishNuGetPackagePdbsAndXml"
AfterTargets="RunResolvePublishAssemblies">
<ItemGroup>
<ResolvedFileToPublish
Include="@(ResolvedAssembliesToPublish->'%(RootDir)%(Directory)%(Filename).pdb')"
RelativePath="$([System.IO.Path]::ChangeExtension(%(ResolvedAssembliesToPublish.DestinationSubPath), '.pdb'))"
DestinationSubPath="$([System.IO.Path]::ChangeExtension(%(ResolvedAssembliesToPublish.DestinationSubPath), '.pdb'))"
Condition="'%(ResolvedAssembliesToPublish.PackageName)' != ''
and Exists('%(RootDir)%(Directory)%(Filename).pdb')" />
<ResolvedFileToPublish
Include="@(ResolvedAssembliesToPublish->'%(RootDir)%(Directory)%(Filename).xml')"
RelativePath="$([System.IO.Path]::ChangeExtension(%(ResolvedAssembliesToPublish.DestinationSubPath), '.xml'))"
DestinationSubPath="$([System.IO.Path]::ChangeExtension(%(ResolvedAssembliesToPublish.DestinationSubPath), '.xml'))"
Condition="'%(ResolvedAssembliesToPublish.PackageName)' != ''
and Exists('%(RootDir)%(Directory)%(Filename).xml')" />
</ItemGroup>
</Target>
Side note: These are different because the code paths for build and publish for nuget binaries are different today, but in the 3.0 timeframe we are looking at unifying them (cc @peterhuene). I further expect that this unification will make the fix easier so I'm putting this in 3.0 milestone given that and the fact that the 2.x bar is quite high now.
@peterhuene to make sure he is aware of this difference.
@nguerrera The configuration you posted is working for me. When I publish, all of the nuget .xml files are correctly published. However, none of the files are pulled when running a regular build. I tried the workaround in described in comment https://github.com/dotnet/sdk/issues/1458#issuecomment-420460129 but none of the xml files are showing up when I build. Did I miss something?
I'm Using netcoreapp2.2 as the target framework
Any progress on this? I wasted lot of time before understanding that sourcelink works with netstandard libraries referenced by netcoreapp projects, but it doesn't work when targeting the full net framework (even with the new csproj
format).
PS: the workaround by @nguerrera works like a charm!
I used the target in this comment and on my Mac Book it all worked great. Moved the code to Docker and it didn't work. The PDB files get there, but the xml files don't. I'm a Swashbuckle user...
Here's the weird part, I ran this
ARG SWARM_REGISTRY
FROM ${SWARM_REGISTRY}/dotnet:2.1-sdk AS build-env
ARG VERSION
WORKDIR /app
COPY . ./
RUN dotnet publish /p:Version=${VERSION} -c Release -o out
RUN ls -lR ~/.nuget/packages
When i look at the nuget packages folders, none of them have the xml files. I downloaded my own nupkg file and extracted it, and the xml file is there. Somehow on the Docker image, the dotnet restore is not extracting the xml file to disk, so then they never get copied to the publish folder.
I specifically looking for PDB/XML files on dependencies to be copied to the publish folder. The main deployable had the proper PDB and XML files copied.
Sean
Somehow on the Docker image, the dotnet restore is not extracting the xml file to disk, so then they never get copied to the publish folder.
In that case, I'd file a bug on https://github.com/nuget/home showing the repro steps where the xml isn't extracted.
Submitted the issue here: https://github.com/NuGet/Home/issues/7805
Directly invoke from cli (Only from cmd,Not powershell)
dotnet pack /p:AllowedOutputExtensionsInPackageBuildOutputFolder=\".dll;.exe;.winmd;.json;.pri;.xml;.pdb\"
Why not from PowerShell, @humhei?
Also from powerShell,
replace \
to \\
Update:
Seems not work in powershell,
I don't know why 😄
how about dotnet pack '/p:AllowedOutputExtensionsInPackageBuildOutputFolder=.dll;.exe;.winmd;.json;.pri;.xml;.pdb'
?
@nguerrera I'm still pasting the https://github.com/dotnet/sdk/issues/1458#issuecomment-401497095 workaround into new projects. How does this timeline fit in with the general symbol distribution picture now that snupkg exists? Should we still be waiting for this to be fixed?
@jnm2 Do you have an updated version of your workaround? Could you please post it? Thank you!
I just moved to embedded pdbs
@FrederickBrier
Do you have an updated version of your workaround? Could you please post it? Thank you!
I'm not sure; it was @nguerrera's workaround. @nguerrera?
I cannot seem to find an article on how to use embedded pdb(s). Could you please send me a link? The topic has been chatted about for a while and there is a lot of chaff obscuring how to use it.
Found it. In the project Properties, in the Build tab, the Advanced... button, Debugging information. Thank you. Hope this works. Thank you.
@FrederickBrier make your you check what changes that UI made to the csproj. i have found it often only applies to the currently selected config. ie it may have wrapped the change in a "is release" condition, which would not be what you want
Any update on this issue? Not bringing over XML doc files and PDB files is a very frustrating for development and troubleshooting.
Sorry that issue has been a long saga. It turns out that to fix it properly we need some NuGet changes. I've started talking to the NuGet team and hope to have something proposed soon.
Thank you Simon, I did verify the changes in my branch. It did specify the Debug configuration. We use git. I am now able to debug, using the "Embedded" approach, but if I clean by symbol cache and delete the .vs directory, it doesn't find the source automatically. I have to provide that either via the pop up dialog or add each of the include .nuget/packages/ source directories to the solution properties debug sources. The sources are in the unpacked .nuget/packages///src directory. Shouldn't the "embedded" DLL contain a reference to NuGet repository, or the Visual Studio debugger remember where it pulled the DLL from? If I do not clean the symbol cache and the .vs directory, and the sources changes, it pulls up the old source.
sorry for the noise to others in the thread...
@FrederickBrier inside the same sln embedded should work seamlessly. you should not mess with the .vs folder. if you are consuming that project as a nuget in another solution you want to use https://github.com/dotnet/sourcelink#using-source-link-in-net-projects
so you pick your nuget package based on your source system
but if you are having problems with that approach it is probably better to raise a new issue
This will impact .NET Core 3.0+ projects by default as well as .NET Framework since they no longer run NuGet dependencies from the NuGet cache for non-publish builds. The same workarounds will work there.
I just moved to embedded pdbs
FYI tried that on VS2017 version 15.9.12 but it didn't find the embedded PDB alas.
(Debugging with nuget should be seamless - it shouldn't take days of messing about with hacks trying to find some incantation that artifactory, nuget, cake, dotnet core/ framework and visual studio 2017/19 can all agree on... and don't get me started on Octopus not supporting sourcelink yet. This is accidental complexity and really _needs_ to be eliminated.)
got the same issue.can't use source link.because the pdb file not in the output folder.
Sorry that issue has been a long saga. It turns out that to fix it properly we need some NuGet changes. I've started talking to the NuGet team and hope to have something proposed soon.
Any further news?
Can someone please confirm if issue https://github.com/dotnet/core/issues/3587 is a duplicate of this one?
Yes, it is.
I'll ask again here then for the sake of moving this discussion forward; Is this being worked on? Is it slated for any future milestone/release? Is there a way for us mere non-MS mortals to track needed changes (such as the mentioned NuGet team changes)?
Sorry yet again. We recently met with nuget and discussed possible changes to project.assets.json in more detail, to include the information we would need. The next step we agreed to would be to draft a short proposal on the linked nuget issue. I started on that and then got interrupted by release issues. I will try to have the draft proposal up ASAP.
I don’t think we have a specific release targeted yet.
I'm not sure if it's helpful to add here, but this is another workaround (it is mentioned once or twice above, but without explicit instructions on how to actually do it):
Instead of
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
, use <DebugType>Embedded</DebugType>
instead, in the same place in the project file.
This doesn't just put the PDB inside the NuGet file, it puts it inside the DLL (where VS sees and uses it fine, with no other workarounds required). This is not 100% equivalent to workarounds which make a separate PDB work, and may not be suitable for some circumstances, but it is one other convenient way to get NuGet debugging off the ground.
Except for .NET Framework, @MikeBeaton.
@paulomorgado why "Except for .NET Framework"? i though embedded symbols worked on classic .net
<DebugType>embedded</DebugType>
works for all targets as long as a recent C# compiler is used.
@KirillOsenkov i always assumed there was some minimum runtime requirement, given there must be code that converts those embedded symbols to a usable stack trace (with line numbers) inside the runtime
I think it's 4.6.1 for desktop and .NET Core > 1.1 but @tmat can confirm definitively since he implemented it.
would be great if someone could point to the official doco for that. my google foo failed me when i searched
Missed that.
Good point, the doc here doesn't mention embedded pdbs: https://github.com/dotnet/core/blob/master/Documentation/diagnostics/portable_pdb.md#fn1
@tmat do we need a bug to track documenting embedded pdbs?
Embedded PDBs are the same as Portable PDB, wrt runtime support.
@tmat but that doesnt help someone googling "dotnet embedded symbols runtime support". "Embedded==Portable for this scenario" is not knowledge most people would have
@MikeBeaton's https://github.com/dotnet/sdk/issues/1458#issuecomment-576699393 today totally nerd sniped me. I am so glad though, because I figured out a related workaround that works for my use case.
My project file now includes this code:
<PropertyGroup>
<DebugSymbols>True</DebugSymbols>
<DebugType>Embedded</DebugType>
<EmbedAllSources>True</EmbedAllSources>
</PropertyGroup>
As I understand it,
DebugSymbols = True
says to create debug symbols (which by itself would cause a PDB file to be created),DebugType = Embedded
says to put those debug symbols in the DLL (instead of creating a PDB file to put them in), andEmbedAllSources = True
says to include the source code with the debug symbols (the debug symbols include a reference to the source code, and the simplest place to go looking for that source code is right next to the debug symbols in the PDB file).Using all three together creates a DLL containing the binary code, the debug symbols, and the source code.
I found this solution while looking through the Source Link project, especially the section titled All Source Files, which links to a section on another page titled EmbedAllSources and says
Set
EmbedAllSources
totrue
to instruct the build system to embed all project source files into the generated PDB.
Yup, this is the best (includes both the symbols and the sources in the .dll), if you don't mind the slightly increased size of the .dll. It is well compressed though.
@bender2k14 Will that solution be sufficient for proper SourceLink experience, without using any SourceLink packages? Are there any runtime limitations?
@paulomorgado why the downvote?
@cmeeren You don't need SourceLink if you're prepared to put up with the overhead (and other implications, e.g. if non-open source) of having all your source inside your PDB. With <DebugType>Embedded</DebugType>
that PDB is, itself, inside the DLL. SourceLink is a clever way to have a PDB which tells VS how to get the source from a git repo at a certain version, so not needed at all for source level debugging if the source is in the PDB already.
Also not obvious is that, because it's fiddly to make sure that you have committed all the changes you are building, SourceLink only really makes sense as part of a process to build your package on a CI server. @bender2k14 's version including the source as well avoids any such issues, so might make more sense if you're not using CI.
@bender2k14 or anyone: <DebugSymbols>True</DebugSymbols>
doesn't seem to be doing anything for me. It can be omitted or even set to False
and I still get an embedded PDB in the NuGet file which supports source level debugging anyway. I'm building a very simple test project, with dotnet pack -c release
.
EDIT: From playing about a bit, it seems that <DebugType>
is all that's needed, it's the equivalent of "VS/project properties/Build/Advanced/Debugging information" and can be "none", "full", "pdbonly", "portable" or "embedded", it fully determines whether or not a PDB file is generated, and <DebugSymbols>
is not needed here? (Any more?)
EDIT: Seems to be this: https://github.com/Microsoft/msbuild/issues/2169 . Not clear (to me?) if it's a bug or a feature.
Also not obvious is that, because it's fiddly to make sure that you have committed all the changes you are building, SourceLink only really makes sense as part of a process to build your package on a CI server. @bender2k14 's version including the source as well avoids any such issues, so might make more sense if you're not using CI.
The Source Link documentation also mentions this case and (somewhat implicitly) suggests a solution is to use Source Link to obtain the code committed into a repository and also use
<EmbedUntrackedSources>True</EmbedUntrackedSources>
to embed the code generated (and then compiled) during a build.
Also https://github.com/ctaggart/SourceLink/wiki#sourcelinkcreate-msbuild-property
In general these tools are meant to be run only on build servers.
@bender2k14 or anyone:
<DebugSymbols>True</DebugSymbols>
doesn't seem to be doing anything for me. It can be omitted or even set toFalse
and I still get an embedded PDB in the NuGet file which supports source level debugging anyway.
Before posting https://github.com/dotnet/sdk/issues/1458#issuecomment-576961024, I considered determining if all three options are needed together or if some of them are redundant. I decided that I like it better to include all three because I find it more readable.
To more directly answer your question, I don't understand what each of the possible pairs of DebugSymbols
and DebutType
would mean. My first impression is that DebugType
is being used to convey more than "one thing", but I could be wrong about that.
Look here, where another project (with authors who probably know better than us, certainly than me, about these packages!) agreed not to use DebugSymbols
. It seems to be redundant in practice if you use DebugType
(as per the code shown in the MSBuild issue).
@bender2k14 Will that solution be sufficient for proper SourceLink experience, without using any SourceLink packages? Are there any runtime limitations?
As I see it, whether or not to include the source code in the PDB file is a question of security. If the source code is publically available, then including the source with the PDB file seems like the correct choice. Then it depends on how the PDB file will be used (e.g. internally or externally).
On the other hand, whether or not to include the PDB file in the DLL seems to be an optimization question to me.
...And as I type this, @MikeBeaton linked to a related PR review where @SimonCropp addressed this question of optimization better than I would have. In short, he and I agree that this optimization is not worth the added complexity.
Niether of us are answering the part of @cmeeren's question which asks:
Are there any runtime limitations?
even though we're both quoting it! The answer is: yes. In more detail, the answer is: newer runtimes only. In even more detail: I don't know; the question is already raised but not answered in the full thread above.
Oh, yes...thanks for the reminder. I had intended to but got distracted by the link you shared to that PR review.
Are there any runtime limitations?
I don't know. I wouldn't expect it to have an impact on runtime performance. I don't know what else to be concerned about. My plan is to use these settings to make the development process more efficient _now_ and make adjustments _in the future_ if necessary.
So in summary, is it correct that the current recommendation to get around this issue (as well as for anyone who, like me, think the SourceLink optimization costs much more than it optimizes) is to use the following?
<PropertyGroup>
<DebugType>Embedded</DebugType>
<EmbedAllSources>True</EmbedAllSources>
</PropertyGroup>
I don't know. I wouldn't expect it to have an impact on runtime performance. I don't know what else to be concerned about.
I was thinking about runtime support, not performance.
@MikeBeaton
Also https://github.com/ctaggart/SourceLink/wiki#sourcelinkcreate-msbuild-property
In general these tools are meant to be run only on build servers.
Please refer to https://github.com/dotnet/sourcelink for official guidelines.
It is true that the most common case for using Source Link is on a build server. However, it also works for binaries built on a dev machine albeit with the caveat that you need to make sure all changes are committed and pushed to the remote. It is useful for folks who do not have build server or in some scenarios where you share binaries and PDBs built on your machine with someone and they need to debug them.
Also, it is always a good practice to avoid differences between build server and local build as much as possible, so that you can easily reproduce issues you see on build server locally.
albeit with the caveat that you need to make sure all changes are committed and pushed to the remote
That was exactly what I was thinking of, but that seems too easy to miss or mess up, and then the source line information would still be there but pointing to the wrong versions of the files. Which couldn't happen if done in CI. Sorry for putting a link to the predecessor version of the project, but is there actually info about the advantages of CI (specifically for SourceLink!), and about the caveat you rightly mention, within https://github.com/dotnet/sourcelink ?
PS @cmeeren @tmat It seems to me that the current best solution, if you are not using CI and especially if you are using a private NuGet feed, is definitely to include sources in PDBs and PDBs in DLLs. (It's much simpler to set up, it doesn't require any workarounds to consume, it doesn't require you to remember to commit before building to not have broken debugging info, and it doesn't require a symbol server.)
[edited]
That said, it seems that the official dotnet/sourcelink/microsoft view is that for public projects it is, now, preferable to use SourceLink in .snupkg
files (which are a different system, and aren't affected by this problem of not copying PDBs directly from the NuGet package). As far as they are concerned, it is all very well to distribute a couple of NuGet files which are 20-30% larger to your own team, but for NuGet files for public consumption, they recommend not enforcing that additional load on everyone, for the sake of the few people who will want to debug into your package.
@MikeBeaton, @SimonCropp cleared that for me.
@MikeBeaton
It is all very well to distribute a couple of NuGet files which are 40% larger to your own team, but for NuGet files for public consumption,
i think there is a bit of fud in your phrasing. in my testing assembles were 20-30% larger, although i am sure 40% is possible. but constantly referring to what seems like the top end of the range would seem to be disingenuous to people trying to make a decisions on an approach that best suites them
I don't have a dog in this fight, I'm just trying to work out what's best (and what's recommended, and whether that makes sense) from the limited information out there!
I got 40% from your own figures (which I linked to to try to make that clear)!
Using embedded symbols means the package goes from 171KB => 242KB.
But I did wonder afterwards whether that was the entire NuGet file or (probably, from what you're now saying) just the increase in the PDB.
I've updated my post above and two answers on SO where I've been trying to summarise what I've been finding, to say 20-30%.
I make a workaround for this. https://github.com/guogangj/PdbAndXmlExtractor
It seem that the discussion here shifted into .pdb specifics, but what about
We are also using this issue to track copying XML files
I'm fine with using EmbedAllSources, but is there a proper workaround for .xml? It's currently impossible to share a model to be exposed via OAS/Swagger via a Nuget package.
@Leon99 - Where do the files need to be copied to, and would something similar to the below help? It's edited down from a .nuspec
file I wrote for a side project recently; it works fine to copy arbitrary files to an arbitrary named location in the user's project, and these can optionally be marked as part of the build for new style projects. I'm imagining similar settings could be shoe-horned into a .proj
file.
<?xml version="1.0"?>
<package>
<metadata>
<id>...</id>
<version>...</version>
<contentFiles>
<!-- new style projects -->
<files include="**/UserDir/*.*" buildAction="None" />
</contentFiles>
</metadata>
<files>
<!-- new style projects -->
<file src="build\file1.x" target="contentFiles\any\any\UserDir" />
<file src="build\file2.y" target="contentFiles\any\any\UserDir" />
<!-- old style projects -->
<file src="build\file1.x" target="content\UserDir" />
<file src="build\file2.y" target="content\UserDir" />
</files>
</package>
Bringing this all the way back to the start. Is there a work around for the following (what I would think is a pretty standard configuration)
I have some first party projects that I pack into nuget packages, including symbol files.
We have a private nuget server on artifactory but no symbol server.
We upload our nuget packages to the nuget server on artifactory
Products use those nuget packages. Running dotnet publish
on the main product copies the symbol files of the product, but not the first party nuget packages.
Anyone have a workaround?
Does this not work anymore, or is it just lost in all the conversations?
https://github.com/dotnet/sdk/issues/1458#issuecomment-420456386
Does this not work anymore, or is it just lost in all the conversations?
Completely lost in the conversation. Sorry about that, I thought I had read it all.
If I read it correct, I need to add those code snippets (one for build one for publish) to the csproj files of all my projects. Is that correct?
I would try putting it in a Directory.Build.targets file. See https://docs.microsoft.com/en-us/visualstudio/msbuild/customize-your-build?view=vs-2019. This way you can have it in one place and have it apply to all projects underneath the folder holding the Directory.Build.targets.
THANK YOU @nguerrera !!! The workarounds and the Directory.Build.targets file worked!
The Directory.Build.targets file was news to me, so thank you for showing me this too!
Appreciate it!!!!
"... in the 3.0 timeframe we are looking at unifying them"
@nguerrera IIRC these workarounds are still needed with current latest dotnet sdk (3.1.201) but will this get fixed in a future drop?
I did a quick test with the current latest dotnet sdk and it seems only the first workaround is needed, i.e. for _builds_, and the second workaround for _publish_ makes no difference as all xml and pdb files are already staged to the publish output directory.
I've added a small proposal for the nuget change that could back a proper fix here: https://github.com/NuGet/Home/issues/5926#issuecomment-640784497
So as this issue doesn't seem to be fixed anymore, can somebody please give a short summary of the easiest and most versatile workaround to finally get PDB files from referenced NuGet packages into deployment and see complete stack traces in production? (I didn't read the 163 hidden items...)
So as this issue doesn't seem to be fixed anymore, can somebody please give a short summary of the easiest and most versatile workaround to finally get PDB files from referenced NuGet packages into deployment and see complete stack traces in production?
Do you only care about PDB files to the extent that they allow you to have "complete stack traces" in production? What do you mean by "complete stack traces"? Do you specifically mean the stack traces include lines numbers (like in this example)?
If so, then I think the simplest solution is to forgo PDB files and instead directly embed the needed debug symbols directly into the DLLs. This is achieved by putting this code in your project file.
<PropertyGroup>
<DebugSymbols>True</DebugSymbols>
<DebugType>Embedded</DebugType>
</PropertyGroup>
I say a bit more about this in https://github.com/dotnet/sdk/issues/1458#issuecomment-576961024
If so, then I think the simplest solution is to forgo PDB files and instead directly embed the needed debug symbols directly into the DLLs. This is achieved by putting this code in your project file.
<PropertyGroup> <DebugSymbols>True</DebugSymbols> <DebugType>Embedded</DebugType> </PropertyGroup>
I would take that one step further (which I needed to do to allow stepping into the source while debugging) is to also embed the source, these are the settings I always use (which include warnings as errors, since I don't like any warnings too)
<PropertyGroup>
<DebugType>embedded</DebugType>
<DebugSymbols>true</DebugSymbols>
<EmbedAllSources>true</EmbedAllSources>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningsAsErrors />
</PropertyGroup>
I would take that one step further (which I needed to do to allow stepping into the source while debugging) is to also embed the source...
I recommend that for your open course projects and during development of your closed source projects, but maybe not released into production like @ygoe was asking about. It depends on who has access to them in production and what level of security and secrecy the project is supposed to have.
Is it necessary with <DebugSymbols>true</DebugSymbols>
when you have <EmbedAllSources>true</EmbedAllSources>
? If so, why?
Is it necessary with
<DebugSymbols>true</DebugSymbols>
when you have<EmbedAllSources>true</EmbedAllSources>
? If so, why?
My understanding is that Debug symbols map the binary to the source code - and if you have the source available on your machine you should be able to then also step into the code while debugging.
But if your source is somewhere else, the you would embed source as well so you can step into it.
So yes both needed as far as I understand - but hey never hurts to just remove the symbols and try it out.
We use it in house since we publish our shared libraries to a private NuGet repo and want to be able to step into the code without having to load the whole project, etc. Our build system has both a dev and release version (which doesn't include symbols or source) and when we seem to be having problems in the shared libraries, we point to the dev version...
Also it should be said that this really should only be for development environments and not production...
Stepping into doesn't seem to use the embedded source code. So I don't know what it's actually good for.
Embedding the symbols alone does show file names and line numbers in stack traces, so that's the solution. PDB files are not necessary anymore.
Stepping into doesn't seem to use the embedded source code. So I don't know what it's actually good for.
It does. I've been relying on it for a while.
I think it's clear from this discussion that the various options on how to do this properly are a complete mess.
In my case, I got 95% of the way through Source Link support just to find out Azure DevOps doesn't support it yet, so I'm stuck with @bender2k14's approach of embedding everything. (I publish only private packages.)
FYI - here's what my NuGet projects look like:
<!-- Source Link -->
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<IncludeSymbols>true</IncludeSymbols>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<!-- Once the Source Link bug is resolved, swap this line with the ones below it -->
<!--<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>-->
<EmbedAllSources>True</EmbedAllSources>
<DebugType>embedded</DebugType>
That way I'm ready to move up to Source Link when Azure DevOps supports it. https://github.com/dotnet/sourcelink#alternative-pdb-distribution
It does. I've been relying on it for a while.
Well, I've observed it differently. So it doesn't always or everywhere.
@ygoe You may already know this and I don't know if it will help, but I found that the Visual Studio Modules window Debug/Windows/Modules (shortcut CTRL+ALT+U) (only available while code is running) is very useful for debugging source availability problems.
It shows which modules have loaded symbols and helps you figure out which ones haven't, and why not. I found it worked best for me to try it first with a commercial package for which I knew correctly configured source was available (from a source server), and then compare and contrast that with your own project that's not working.
I apologize for the long delay here. Indeed I was on vacation and then this got lost when I returned. I now have a workaround for the publish case as well.
There are two issues:
On platforms that copy nuget dlls during _build_ (.NET Framework), the .pdb and .xml files are not copied to _build_ output.
- This is what this issue originally tracked.
- A workaround for that was provided here: #1458 (comment)
When publishing for any platform, nuget .pdb and .xml files are not copied to the _publish_ output
- This is what we've been discussing in the more recent comments.
- A workaround for that is as follows:
<Target Name="_ResolvePublishNuGetPackagePdbsAndXml" AfterTargets="RunResolvePublishAssemblies"> <ItemGroup> <ResolvedFileToPublish Include="@(ResolvedAssembliesToPublish->'%(RootDir)%(Directory)%(Filename).pdb')" RelativePath="$([System.IO.Path]::ChangeExtension(%(ResolvedAssembliesToPublish.DestinationSubPath), '.pdb'))" DestinationSubPath="$([System.IO.Path]::ChangeExtension(%(ResolvedAssembliesToPublish.DestinationSubPath), '.pdb'))" Condition="'%(ResolvedAssembliesToPublish.PackageName)' != '' and Exists('%(RootDir)%(Directory)%(Filename).pdb')" /> <ResolvedFileToPublish Include="@(ResolvedAssembliesToPublish->'%(RootDir)%(Directory)%(Filename).xml')" RelativePath="$([System.IO.Path]::ChangeExtension(%(ResolvedAssembliesToPublish.DestinationSubPath), '.xml'))" DestinationSubPath="$([System.IO.Path]::ChangeExtension(%(ResolvedAssembliesToPublish.DestinationSubPath), '.xml'))" Condition="'%(ResolvedAssembliesToPublish.PackageName)' != '' and Exists('%(RootDir)%(Directory)%(Filename).xml')" /> </ItemGroup> </Target>
Side note: These are different because the code paths for build and publish for nuget binaries are different today, but in the 3.0 timeframe we are looking at unifying them (cc @peterhuene). I further expect that this unification will make the fix easier so I'm putting this in 3.0 milestone given that and the fact that the 2.x bar is quite high now.
Hi @nguerrera: this workaround for publishing does not seem to work anymore as of https://github.com/dotnet/sdk/pull/2646. After looking into those changes, by using _ResolveCopyLocalAssetsForPublish
instead of RunResolvePublishAssemblies
and _ResolvedCopyLocalPublishAssets
instead of ResolvedAssembliesToPublish
seems to get it to work again.
Would you say this is this still a valid workaround? Thank you!
@danieljbfaustino I've forgotten most of the details here to be honest. I think it would be worth finding a way to avoid _
prefixed ("private") dependencies in the workaround, but that would require some studying. cc @dsplaisted @marcpopMSFT to see if someone on the current team can take a look.
I just found this thread after wasting whole day traying to figure out why debugging is not working. Generally speaking my feeling of debugging nugets is knowledge that is spread in many places and basic things like this still not working after 3 years? Some basic developer experience like this should be fixed or mentioned on MSDN.
I'll leave it to @dsplaisted to confirm the current workaround. From a prioritization perspectives, this is one of the items we plan on looking at once we finish our current .NET 5.0 planned work and we'll be working with our NuGet partners to enable this in the near future.
@marcpopMSFT is this something that can be added to documentation somewhere? When I (we) hunt for how to solve this issue, we have to piece things together from the SDK, SourceLink, Visual Studio, and NuGet written documentation. It is quite a mess.
@KathleenDollard may be able to help with the documentation.
I figured out a workaround which can be pasted into the csproj:
<!-- https://github.com/dotnet/sdk/issues/1458 --> <PropertyGroup> <AllowedReferenceRelatedFileExtensions>.pdb</AllowedReferenceRelatedFileExtensions> </PropertyGroup> <Target Name="AddReferenceRelatedPathsToCopyLocal" AfterTargets="ResolveAssemblyReferences"> <ItemGroup> <ReferenceCopyLocalPaths Include="@(_ReferenceRelatedPaths)" /> </ItemGroup> </Target>
I tried this workaround, but it seems not to work atleast with a netcoreapp3.1 application. Is there an up to date workaround to get the pdbs of packages copied to the output?
@TFTomSun see https://github.com/dotnet/sdk/issues/1458#issuecomment-420456386. I add the target to Directory.Build.targets
.
@TFTomSun https://github.com/dotnet/sdk/issues/1458#issuecomment-667037150
@marcpopMSFT will the fixes only be in .NET 6, or will they also be back-ported to .NET 5 post release?
I've flagged it for discussion in our next triage meeting as we haven't firmly committed to this or a timeframe yet.
Whenever we fix this, it is likely that it will be only in the latest SDK, but it would be fixed when you are targeting previous versions of .NET. So if we fix it in the .NET 6 SDK you would need that SDK, but when you have that SDK it would likely be fixed for your .NET 5 projects.
@dsplaisted but the .NET 6 SDK will be in preview for the next year and most of us will be pinned to stable, at most .NET 5 [SDK]. 😖
@gitfool @czd890 That workaround is for publishing only, right? What about the source link scenario with embedded pdbs in the package? I have a netcoreapp3.1 test project. I would like to debug into a package with source link support and embedded pdbs (because the package is not published on nuget.org). The dll is copied to the output, but the pdb not, therefore the source link debugging doesn't work.
@TFTomSun I use the workaround I linked to to debug step into dependent packages; using dotnet core 3.1, SourceLink with embedded pdbs, etc. Works great.
@TFTomSun maybe you're missing the subtlety - not surprising given the mess - that there are two workarounds in the comment I linked to. The first workaround is for during _build_ and was originally only needed for .NET Framework targets _but_ has also been needed since .NET Core 3.0+ targets. The second workaround, as specified directly in the linked comment, or with the proposed changes, is for during _publish_. Note however, that I found the second workaround was not needed with dotnet 3.1 sdk.
In summary, I'm only using the first workaround for during _build_ and this is what will copy your pdb and xml files to the build output, which will enable debugging into dependent packages to work. I'm using a Directory.Build.targets
with the following:
<Project>
<!-- https://github.com/dotnet/sdk/issues/1458#issuecomment-420456386 -->
<Target Name="_ResolveCopyLocalNuGetPackagePdbsAndXml" Condition="$(CopyLocalLockFileAssemblies) == true" AfterTargets="ResolveReferences">
<ItemGroup>
<ReferenceCopyLocalPaths
Include="@(ReferenceCopyLocalPaths->'%(RootDir)%(Directory)%(Filename).pdb')"
Condition="'%(ReferenceCopyLocalPaths.NuGetPackageId)' != '' and Exists('%(RootDir)%(Directory)%(Filename).pdb')" />
<ReferenceCopyLocalPaths
Include="@(ReferenceCopyLocalPaths->'%(RootDir)%(Directory)%(Filename).xml')"
Condition="'%(ReferenceCopyLocalPaths.NuGetPackageId)' != '' and Exists('%(RootDir)%(Directory)%(Filename).xml')" />
</ItemGroup>
</Target>
</Project>
@gitfool You're right, I missed that. Thanks alot. I'll try that tomorrow
Tracked in https://github.com/NuGet/Home/issues/9783. @zkat would ya'll be able to prioritize this prior to 6.0 timeframe in one of the 16.+ releases?
@gitfool
Thank you for posting it. I have tried that workaround and it was copying only .pdb files. But, if I remove the condition (Condition="$(CopyLocalLockFileAssemblies) == true") it is copying both .pdf and .xml files.
I could not find a nice documentation on CopyLocalLockFileAssemblies. I would like to understand what difference it makes if I do not add this condition.
taking some notes here for future people that land in this hell. i find myself fighting this every 6 months or so and seemingly have the most correct solution so far (for my use-case)
my current reqs:
notes on my latest hurdles that i just figured out:
ENV NUGET_XMLDOC_MODE none
as described at https://github.com/NuGet/Home/issues/7805#issuecomment-465553011modified PackageReference
style:
<PackageReference Include="xxxxxx.Models" Version="$(CoreVersion)">
<CopyToOutputDirectory>lib/netstandard2.1/*</CopyToOutputDirectory>
</PackageReference>
block for performing the copying at applicable stages:
<Target Name="CopyPackages_Build" AfterTargets="Build">
<Message Importance="high" Condition="%(PackageReference.CopyToOutputDirectory) != ''" Text="CopyPackages_Build - Copying packages from: $(NugetPackageRoot)$([System.String]::Copy('%(PackageReference.Identity)').ToLower())/%(PackageReference.Version)/%(PackageReference.CopyToOutputDirectory) to $(OutDir)" />
<ItemGroup>
<PackageReferenceFiles Condition="%(PackageReference.CopyToOutputDirectory) != ''" Include="$(NugetPackageRoot)$([System.String]::Copy('%(PackageReference.Identity)').ToLower())/%(PackageReference.Version)/%(PackageReference.CopyToOutputDirectory)" />
</ItemGroup>
<Copy SourceFiles="@(PackageReferenceFiles)" DestinationFolder="$(OutDir)" />
</Target>
<Target Name="CopyPackages_PrepareForPublish" BeforeTargets="PrepareForPublish">
<Message Importance="high" Condition="%(PackageReference.CopyToOutputDirectory) != ''" Text="CopyPackages_PrepareForPublish - Copying packages from: $(NugetPackageRoot)$([System.String]::Copy('%(PackageReference.Identity)').ToLower())/%(PackageReference.Version)/%(PackageReference.CopyToOutputDirectory) to $(PublishDir)" />
<ItemGroup>
<PackageReferenceFiles Condition="%(PackageReference.CopyToOutputDirectory) != ''" Include="$(NugetPackageRoot)$([System.String]::Copy('%(PackageReference.Identity)').ToLower())/%(PackageReference.Version)/%(PackageReference.CopyToOutputDirectory)" />
</ItemGroup>
<Copy SourceFiles="@(PackageReferenceFiles)" DestinationFolder="$(PublishDir)" />
</Target>
by all means, smack me around if this is an overly complicated solution.
@marcpopMSFT https://github.com/nuget/home/issues/5926#issuecomment-725756398
Most helpful comment
I figured out a workaround which can be pasted into the csproj: