Summary
On SDKs that bundle SourceLink as implicit SDKs (.NET 8+), adding an explicit <PackageReference Include=\"Microsoft.SourceLink.GitHub\" PrivateAssets=\"all\" /> silently disables SourceLink rather than enabling or replacing it. Build succeeds with no warnings, but resulting packages have:
- No SourceLink JSON in the PDB
- No PathMap normalization (paths remain non-deterministic)
Reproducible debug entry is still set, so the PE looks deterministic by header, but embedded source paths are not mapped
The docs (both the README and many blog posts/templates) still instruct users to add this package. On a modern SDK, following that guidance breaks SourceLink.
Environment
- .NET SDK:
10.0.202 (also confirmed in 11.0.100-preview.2)
Microsoft.SourceLink.GitHub NuGet package: 10.0.202
- Central Package Management via
Directory.Packages.props with GlobalPackageReference
- Strong-named, multi-targeted library (
netstandard2.0;net8.0;net9.0;net10.0)
Reproduction
Directory.Packages.props:
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<GlobalPackageReference Include=\"Microsoft.SourceLink.GitHub\" Version=\"10.0.202\" PrivateAssets=\"all\" />
</ItemGroup>
</Project>
Directory.Build.props:
<Project>
<PropertyGroup>
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
</PropertyGroup>
</Project>
Then dotnet build -c Release. Inspect the produced PDB — no SourceLink custom debug information entry (CC110556-A091-4D38-9FEC-25AB9A351A6A), no obj/*/*.sourcelink.json file.
Root cause trace
- NuGet restores
Microsoft.SourceLink.GitHub directly and imports its build/Microsoft.SourceLink.GitHub.props/.targets.
- Its transitive deps (
Microsoft.SourceLink.Common, Microsoft.Build.Tasks.Git) resolve in project.assets.json as \"buildTransitive/_._\": {} — the empty-assets marker — because developmentDependency=true packages don't flow their build assets through a PrivateAssets=all parent. Common's InitializeSourceControlInformation.targets / Microsoft.SourceLink.Common.targets never get imported via NuGet.
- NuGet still generates the
PkgMicrosoft_SourceLink_Common path property (from restore), even though its build assets are _._.
- The SDK-bundled
Microsoft.NET.Sdk.SourceLink.props contains:
<SuppressImplicitGitSourceLink Condition=\"'$(PkgMicrosoft_SourceLink_Common)' != ''\">true</SuppressImplicitGitSourceLink>
<ImportGroup Condition=\"'$(SuppressImplicitGitSourceLink)' != 'true'\">
<Import Project=\"...\Microsoft.Build.Tasks.Git\build\Microsoft.Build.Tasks.Git.props\" />
<Import Project=\"...\Microsoft.SourceLink.Common\build\Microsoft.SourceLink.Common.props\" />
<Import Project=\"...\Microsoft.SourceLink.GitHub\build\Microsoft.SourceLink.GitHub.props\" />
...
</ImportGroup>
Because PkgMicrosoft_SourceLink_Common is now set, SuppressImplicitGitSourceLink=true fires → SDK's bundled Common and Microsoft.Build.Tasks.Git imports are skipped.
Net result: GitHub.targets is imported alone, without the LocateRepository task or the InitializeSourceControlInformationFromSourceControlManager target. @(SourceRoot) is never populated → _GenerateSourceLinkFile does nothing → /sourcelink: switch is never passed to csc → no SourceLink CDI in PDB → paths are not PathMap-normalized.
Full preprocess (msbuild -pp) and PE-debug-directory verification available in thomhurst/TUnit#5579.
Fix that worked for us
Remove the explicit Microsoft.SourceLink.GitHub PackageReference entirely. The SDK then auto-imports Common + Build.Tasks.Git + GitHub from $(DotnetRoot)/sdk/<ver>/Sdks/Microsoft.SourceLink.*, and everything works.
Suggested improvements
Any one of these would have made this far easier to diagnose:
- Docs: Update the README to say "on .NET SDK 8+, don't add
Microsoft.SourceLink.GitHub — it's implicit; add it only for older SDKs or custom hosts."
- Warning: Have
Microsoft.SourceLink.GitHub.targets emit a warning if it's imported but Microsoft.SourceLink.Common.targets hasn't been imported (detectable by a well-known property set in Common's targets). This would catch the buildTransitive/_._ scenario.
- Suppression condition: Narrow the SDK's
SuppressImplicitGitSourceLink check so it only fires when Common's build assets actually imported, not merely when the Pkg* path property is set (since NuGet sets that even for _._ assets).
Happy to contribute a docs PR if helpful.
Summary
On SDKs that bundle SourceLink as implicit SDKs (.NET 8+), adding an explicit
<PackageReference Include=\"Microsoft.SourceLink.GitHub\" PrivateAssets=\"all\" />silently disables SourceLink rather than enabling or replacing it. Build succeeds with no warnings, but resulting packages have:Reproducibledebug entry is still set, so the PE looks deterministic by header, but embedded source paths are not mappedThe docs (both the README and many blog posts/templates) still instruct users to add this package. On a modern SDK, following that guidance breaks SourceLink.
Environment
10.0.202(also confirmed in11.0.100-preview.2)Microsoft.SourceLink.GitHubNuGet package:10.0.202Directory.Packages.propswithGlobalPackageReferencenetstandard2.0;net8.0;net9.0;net10.0)Reproduction
Directory.Packages.props:Directory.Build.props:Then
dotnet build -c Release. Inspect the produced PDB — no SourceLink custom debug information entry (CC110556-A091-4D38-9FEC-25AB9A351A6A), noobj/*/*.sourcelink.jsonfile.Root cause trace
Microsoft.SourceLink.GitHubdirectly and imports itsbuild/Microsoft.SourceLink.GitHub.props/.targets.Microsoft.SourceLink.Common,Microsoft.Build.Tasks.Git) resolve inproject.assets.jsonas\"buildTransitive/_._\": {}— the empty-assets marker — becausedevelopmentDependency=truepackages don't flow their build assets through aPrivateAssets=allparent. Common'sInitializeSourceControlInformation.targets/Microsoft.SourceLink.Common.targetsnever get imported via NuGet.PkgMicrosoft_SourceLink_Commonpath property (from restore), even though its build assets are_._.Microsoft.NET.Sdk.SourceLink.propscontains:PkgMicrosoft_SourceLink_Commonis now set,SuppressImplicitGitSourceLink=truefires → SDK's bundledCommonandMicrosoft.Build.Tasks.Gitimports are skipped.Net result:
GitHub.targetsis imported alone, without theLocateRepositorytask or theInitializeSourceControlInformationFromSourceControlManagertarget.@(SourceRoot)is never populated →_GenerateSourceLinkFiledoes nothing →/sourcelink:switch is never passed to csc → no SourceLink CDI in PDB → paths are not PathMap-normalized.Full preprocess (
msbuild -pp) and PE-debug-directory verification available in thomhurst/TUnit#5579.Fix that worked for us
Remove the explicit
Microsoft.SourceLink.GitHubPackageReference entirely. The SDK then auto-imports Common + Build.Tasks.Git + GitHub from$(DotnetRoot)/sdk/<ver>/Sdks/Microsoft.SourceLink.*, and everything works.Suggested improvements
Any one of these would have made this far easier to diagnose:
Microsoft.SourceLink.GitHub— it's implicit; add it only for older SDKs or custom hosts."Microsoft.SourceLink.GitHub.targetsemit a warning if it's imported butMicrosoft.SourceLink.Common.targetshasn't been imported (detectable by a well-known property set in Common's targets). This would catch thebuildTransitive/_._scenario.SuppressImplicitGitSourceLinkcheck so it only fires when Common's build assets actually imported, not merely when thePkg*path property is set (since NuGet sets that even for_._assets).Happy to contribute a docs PR if helpful.