How to debug NuGet packages using SourceLink
Using a NuGet package is a very convenient way to add a dependency to your project. However, when you have an issue with a NuGet package and you want to debug it, it's hard because you don't have the source code.
Lots of NuGet packages have their source code on GitHub, so it would be very convenient to automatically get the code from the GitHub repository. This is what SourceLink provides. It adds some metadata to the PDB file to remap the local files to the files on GitHub, so Visual Studio can download the files when needed.
The most downloaded NuGet package Newtonsoft.Json
now uses SourceLink, so you can step into the package code from Visual Studio. Let's see how you can do the same for your projects!
#Create a NuGet package with SourceLink enabled
SourceLink is very simple to enable in your build. All you have to do is to add a reference to the NuGet package Microsoft.SourceLink.GitHub
. You can also add some optional properties to control SourceLink.
<Project>
<PropertyGroup>
<!-- Optional: Declare that the Repository URL can be published to NuSpec -->
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<!-- Optional: Embed source files that are not tracked by the source control manager to the PDB -->
<!-- This is useful if you generate files during the build -->
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<!-- Generate symbol packages (.snupkg) -->
<!-- You must publish both packages, the package that contains the DLL (.nupkg) and the one that contains the symbols (.snupkg) -->
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
<ItemGroup>
<!-- Required if your repository is on GitHub -->
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-*" PrivateAssets="All"/>
<!-- Required if your repository is on VSTS -->
<!--<PackageReference Include="Microsoft.SourceLink.Vsts.Git" Version="1.0.0-*" PrivateAssets="All"/>-->
<!-- Required if your repository is on GitLab -->
<!--<PackageReference Include="Microsoft.SourceLink.GitLab" Version="1.0.0-*" PrivateAssets="All"/>-->
</ItemGroup>
</Project>
If you want to use deterministic build, you can use the Deterministic
and ContinuousIntegrationBuild
elements to use a deterministic way to compute paths:
<Project>
<PropertyGroup>
<Deterministic>true</Deterministic>
<!-- The condition may change depending on the build system you use -->
<!-- Uncomment if you build using Azure DevOps -->
<!-- https://learn.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml&WT.mc_id=DT-MVP-5003978#system-variables -->
<ContinuousIntegrationBuild Condition="'$(TF_BUILD)' == 'true'">True</ContinuousIntegrationBuild>
<!-- Uncomment if you build using GitHub Actions -->
<!-- https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables -->
<ContinuousIntegrationBuild Condition="'$(GITHUB_ACTIONS)' == 'true'">True</ContinuousIntegrationBuild>
<!-- Uncomment if you build using GitLab -->
<!-- https://docs.gitlab.com/ee/ci/variables/predefined_variables.html -->
<ContinuousIntegrationBuild Condition="'$(GITLAB_CI)' == 'true'">True</ContinuousIntegrationBuild>
</PropertyGroup>
...
You can find more information on the GitHub repository of SourceLink: https://github.com/dotnet/sourcelink/#using-sourcelink
Then you need to create the NuGet packages:
dotnet pack --configuration Release
Finally, you need to push both packages:
dotnet nuget push "package.nupkg" --api-key "<Insert your API Key>" --source https://api.nuget.org/v3/index.json --force-english-output
dotnet nuget push "package.snupkg" --api-key "<Insert your API Key>" --source https://api.nuget.org/v3/index.json --force-english-output
#Use SourceLink in Visual Studio
In Visual Studio, you need to uncheck Enable Just My Code
:
Then, when you want to step into a method from a DLL with SourceLink enabled, you get the popup:
Then, you can continue debugging:
#Test SourceLink is enabled for a NuGet package
You can test you package is well configured using the SourceLink global tool. If you don't know what is a global tool, you can check my previous post about it. First, install the tool:
dotnet tool install --global sourcelink
C:\Users\mezia>sourcelink --help
Source Code On Demand
Usage: [options] [command]
Options:
-h|--help Show help information
Commands:
print-documents print the documents stored in the pdb or dll
print-json print the Source Link JSON stored in the pdb or dll
print-urls print the URLs for each document based on the Source Link JSON
test test each URL and verify that the checksums match
Use " [command] --help" for more information about a command.
- Print the mapping document in the pdb file:
PS> sourcelink print-json meziantou.framework.1.4.2\lib\netstandard2.0\Meziantou.Framework.pdb
{"documents":{"D:\\a\\1\\s\\*":"https://raw.githubusercontent.com/meziantou/Meziantou.Framework/58eabf679afa09951ee38d59d038339c2d552b05/*"}}
- Test that the files are downloadable:
PS> sourcelink test meziantou.framework.1.4.2\lib\netstandard2.0\Meziantou.Framework.pdb
sourcelink test passed: meziantou.framework.1.4.2\lib\netstandard2.0\Meziantou.Framework.pdb
- Print the sha1 of every file in the pdb:
PS> sourcelink print-documents meziantou.framework.1.4.2\lib\netstandard2.0\Meziantou.Framework.pdb
da677f59aa0a2b2e266e869ebebae275f49adfc0 sha1 csharp D:\a\1\s\src\Meziantou.Framework\Utilities\AssemblyUtilities.cs
783e778429958369673e2a6baac5560af28c2c98 sha1 csharp D:\a\1\s\src\Meziantou.Framework\Utilities\ByteArrayExtensions.cs
753671f42091a5fe5dd71c2db4ae9bd34838c4a2 sha1 csharp D:\a\1\s\src\Meziantou.Framework\Utilities\CultureInfoUtilities.cs
a2e7fcc4b5535b5f3861d74fce424088ab6c6001 sha1 csharp D:\a\1\s\src\Meziantou.Framework\Utilities\DateTimeUtilities.cs
bb19ae0d30831c17c0bcc709a74c07bb6b87ae7a sha1 csharp D:\a\1\s\src\Meziantou.Framework\Utilities\DebounceExtensions.cs
3067c4d797a2fd9edae131a946ef584bf4c32760 sha1 csharp D:\a\1\s\src\Meziantou.Framework\Utilities\EnumerableExtensions.cs
a9124ea95b058ac8d4f46fc8fff801d9082bcefa sha1 csharp D:\a\1\s\src\Meziantou.Framework\Utilities\ExceptionExtensions.cs
...
- Print the URLs of every file in the pdb:
PS> sourcelink print-urls meziantou.framework.1.4.2\lib\netstandard2.0\Meziantou.Framework.pdb
da677f59aa0a2b2e266e869ebebae275f49adfc0 sha1 csharp D:\a\1\s\src\Meziantou.Framework\Utilities\AssemblyUtilities.cs
https://raw.githubusercontent.com/meziantou/Meziantou.Framework/58eabf679afa09951ee38d59d038339c2d552b05/src/Meziantou.Framework/Utilities/AssemblyUtilities.cs
783e778429958369673e2a6baac5560af28c2c98 sha1 csharp D:\a\1\s\src\Meziantou.Framework\Utilities\ByteArrayExtensions.cs
https://raw.githubusercontent.com/meziantou/Meziantou.Framework/58eabf679afa09951ee38d59d038339c2d552b05/src/Meziantou.Framework/Utilities/ByteArrayExtensions.cs
753671f42091a5fe5dd71c2db4ae9bd34838c4a2 sha1 csharp D:\a\1\s\src\Meziantou.Framework\Utilities\CultureInfoUtilities.cs
https://raw.githubusercontent.com/meziantou/Meziantou.Framework/58eabf679afa09951ee38d59d038339c2d552b05/src/Meziantou.Framework/Utilities/CultureInfoUtilities.cs
a2e7fcc4b5535b5f3861d74fce424088ab6c6001 sha1 csharp D:\a\1\s\src\Meziantou.Framework\Utilities\DateTimeUtilities.cs
https://raw.githubusercontent.com/meziantou/Meziantou.Framework/58eabf679afa09951ee38d59d038339c2d552b05/src/Meziantou.Framework/Utilities/DateTimeUtilities.cs
bb19ae0d30831c17c0bcc709a74c07bb6b87ae7a sha1 csharp
d19483e16d67ae4da6e044d3fdeb35dd87480282 sha1 csharp C:\Users\buildguest\AppData\Local\Temp\.NETStandard,Version=v2.0.AssemblyAttributes.cs embedded
...
BTW, you can see that the file AssemblyAttributes.cs
is embedded in the PDB because of the attribute <EmbedUntrackedSources>true</EmbedUntrackedSources>
.
Another way to validate the NuGet package is to use NuGet Package Explorer:
#Conclusion
SourceLink is very useful if you publish a NuGet package where the code is hosted on GitHub, GitLab, or VSTS. It allows the consumers of your package to step into the code from Visual Studio and start debugging the code. It's so easy to setup, that you have no reason to not add it to your project.
Do you have a question or a suggestion about this post? Contact me!