Producing Packages with Source Link

Avatar

Claire

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.

13 comments

Comments are closed. Login to edit/delete your existing comments

  • Andreas Ringlstetter
    Andreas Ringlstetter

    When will you add at least basic source link support to C++ projects?

    I mean it’s nice to see that someone is working on that essential feature at all, but it’s also blatant obvious that you folks don’t even try to maintain a common feature set between the different language frameworks.

  • Avatar
    Rehan Saeed

    I own several packages that have been using Source Link. The advice here seems a bit different to that found in https://github.com/dotnet/sourcelink. So here are a few questions:

    • The embedded debug type is recommended. Is there any reasoning behind that? What happens if I don’t include this?
    <!-- Recommended: Embed symbols containing Source Link in the main file (exe/dll) -->
    <DebugType>embedded</DebugType>
    • I’d like to understand a bit more about the file paths in deterministic builds. What file paths get normalised? I guess this means learning more about how source link works which was out of scope of this blog post.
    <PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">
      <ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
    </PropertyGroup>
  • Avatar
    Dominik Rauch

    Is it planned to include configurable source link “adapters” for internal Git repository servers? We have Bitbucket Server running, it would be nice to configure some kind of BaseUrl in the csproj and get the full support as well.

  • Avatar
    Darjan Bogdan

    Thanks for the article, it’s very helpful!

    Do you know, by any chance, if there is a plan for VisualStudio to support different authentication protocols (other than basic auth) for fetching the files?

    It’s kind of a blocker for us to use Source Links with private GitLab repositories (hosted on gitlab.com, we can’t add proxy), since GitLab doesn’t support basic auth. Unfortunately, it seems it won’t implement that support any time soon, or at all.

    Any info would be helpful, thanks!

  • Avatar
    Ian Yates

    Any chance for TFS-style source control, but still stored in Azure DevOps? I know it’s not in vogue, but it still works fine for us.

    • Avatar
      Steven Bone

      I asked the same question in the first post in this series, with a me-too from Gioacchino Piazzolla. There are at least three people in the world that would be interested in such a thing. I was really hoping we could get some comment from Microsoft here.

  • Avatar
    Sameer Kumar

    I have a solution (ApNetCore3.1) with 2 projects. My Azure DevOps builds were working fine until I integrated this post’s solution. Now I get this:

    C:\Program Files\dotnet\sdk\3.1.302\Sdks\NuGet.Build.Tasks.Pack\build\NuGet.Build.Tasks.Pack.targets(198,5) error NU5017: Cannot create a package that has no dependencies nor content.

    Any clue?
    Thanks!

  • Avatar
    Lastri Lastri

    A lot of our older code is in the old / original csproj format, and SourceLink will only work with the new (SDK) project format, so the first job is to convert the projects.
    Convert the csproj file. The new project format is very simple, so you can often just copy and paste an existing csproj file.

    @Delete the files that are no longer required (packages.config, Properties\AssemblyInfo.cs)
    @Add the SourceLink.Create.CommandLine and Microsoft.SourceLink.GitHub nuget packages.
    @Update the version numbers of the nuget packages

    Hasilkan Uang Dari Google News, Mudah Banget !.