Producing Packages with Source Link

Claire Novotny

In our last post, we showed you how you can debug into the framework and dependencies that was produced with Source Link. In this post, we’ll show you how to add Source Link to your projects. This is beneficial both for public and internal projects.

At its most basic, Source Link generates a JSON file that maps raw source code locations to the source files in the build. This is most commonly an HTTPS URL to a file on GitHub, GitLab, Azure Repos, or Bitbucket. The JSON file gets embedded in the debug symbols, which the debugger uses to download the file on-demand. For non-public repos, this does not automatically grant anyone access to the source; the debugger would need to authenticate so existing permissions are maintained.

Source Link is distributed as a set of NuGet packages, one per repository host. It is intended to be used as a private reference (it is used during the build, it does not require runtime consumers to have a dependency on it).

You can enable Source Link experience in your own .NET project by setting a few properties and adding a PackageReference to a Source Link package:

<Project Sdk="Microsoft.NET.Sdk">
 <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>

    <!-- Publish the repository URL in the built .nupkg (in the NuSpec <Repository> element) -->
    <PublishRepositoryUrl>true</PublishRepositoryUrl>

    <!-- Embed source files that are not tracked by the source control manager in the PDB -->
    <EmbedUntrackedSources>true</EmbedUntrackedSources>

    <!-- Recommended: Embed symbols containing Source Link in the main file (exe/dll) -->
    <DebugType>embedded</DebugType>
  </PropertyGroup>
  <ItemGroup>
    <!-- Add PackageReference specific for your source control provider (see below) -->
  </ItemGroup>
</Project>

Deterministic Builds

Deterministic builds ensure that the same binary is produced regardless of the machine building it, including paths to sources stored in the symbols. While deterministic builds are enabled by default in .NET SDK projects, there is an extra property, ContinuousIntegrationBuild, to set on the build server to normalize stored file paths. These should not be enabled during local dev or the debugger won’t be able to find the local source files.

Therefore, you should use your CI system’s variable to set them conditionally. For Azure Pipelines, it looks like this

<PropertyGroup Condition="'$(TF_BUILD)' == 'true'">
  <ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>

For GitHub Actions, the variable is GITHUB_ACTIONS, so the result would be:

<PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">
  <ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>

Don’t Repeat Yourself

If you have a solution that has multiple projects in it, you can extract the common properties into a Directory.Build.props file at the solution directory. That way all projects have them added automatically.

If you distribute the library via a package published to NuGet.org, you should use embedded PDB’s so the debug information is always available with your library. Alternatively, you can build a symbol package and publish it to NuGet.org as well. This will make the symbols available on NuGet.org symbol server, where the debugger can download it when needed.

Source Control Providers

Source Link packages are currently available for the following source control providers.

Source Link package is a development dependency, which means it is only used during build. It is therefore recommended to set PrivateAssets to all on the package reference. This prevents consuming projects of your NuGet package from attempting to install Source Link.

github.com and GitHub Enterprise

For projects hosted by GitHub or GitHub Enterprise reference Microsoft.SourceLink.GitHub like so:

<ItemGroup>
  <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
</ItemGroup>

Azure Repos (former Visual Studio Team Services)

For projects hosted by Azure Repos in git repositories reference Microsoft.SourceLink.AzureRepos.Git:

<ItemGroup>
  <PackageReference Include="Microsoft.SourceLink.AzureRepos.Git" Version="1.0.0" PrivateAssets="All"/>
</ItemGroup>

Azure DevOps Server (former Team Foundation Server)

For projects hosted by on-prem Azure DevOps Server in git repositories reference Microsoft.SourceLink.AzureDevOpsServer.Git and add host configuration like so:

<ItemGroup>
  <PackageReference Include="Microsoft.SourceLink.AzureDevOpsServer.Git" Version="1.0.0" PrivateAssets="All"/>
</ItemGroup>

If your server is configured with non-empty IIS Virtual Directory, specify this directory in SourceLinkAzureDevOpsServerGitHost item like so:

<ItemGroup>
  <SourceLinkAzureDevOpsServerGitHost Include="server-name" VirtualDirectory="tfs"/>
</ItemGroup>

The Include attribute specifies the domain and optionally the port of the server (e.g. server-name or server-name:8080).

GitLab

For projects hosted by GitLab reference Microsoft.SourceLink.GitLab package:

<ItemGroup>
  <PackageReference Include="Microsoft.SourceLink.GitLab" Version="1.0.0" PrivateAssets="All"/>
</ItemGroup>

Bitbucket

For projects in git repositories hosted on Bitbucket.org or hosted on an on-prem Bitbucket server reference Microsoft.SourceLink.Bitbucket.Git package:

<ItemGroup>
  <PackageReference Include="Microsoft.SourceLink.Bitbucket.Git" Version="1.0.0" PrivateAssets="All"/>
</ItemGroup>

If your project is hosted by Bitbucket Server or Bitbucket Data Center older than version 4.7 you must specify SourceLinkBitbucketGitHost item group in addition to the package reference:

<ItemGroup>
  <SourceLinkBitbucketGitHost Include="bitbucket.yourdomain.com" Version="4.5"/>
</ItemGroup>

The item group SourceLinkBitbucketGitHost specifies the domain of the Bitbucket host and the version of Bitbucket. The version is important since URL format for accessing files changes with version 4.7. By default Source Link assumes new format (version 4.7+).

gitweb (pre-release)

For projects hosted on-prem via gitweb reference Microsoft.SourceLink.GitWeb package:

<ItemGroup>
  <PackageReference Include="Microsoft.SourceLink.GitWeb" Version="1.1.0-beta-20204-02" PrivateAssets="All"/>
</ItemGroup>

Summary

Source Link is easy to add to your projects and we highly recommend that all projects configure it by default.