Tidbits

Intranet NuGet source sharing

Problem:
You have a private NuGet repository to share code between projects, including their .pdb.
You use source control in a private repository to maintain the code those NuGets are build from.
But when you load the NuGet in your project, you sometimes can’t step into its code.
Very annoying, especially when debugging.

What happened?
Someone else built the NuGet, on their machine, which means they used the sourcefiles on their machine.
You might have the same files on yours, but in a different path, so the debugger can’t find them.


To solve that issue you could just all agree on a neutral, standardized path for your code. That way the stored path will always match the source files on your machine, regardless of who built the NuGet. But this also requires you to keep your local source on the same version as the NuGets you’re using.

Then there is Source Link, which is exactly what we want, but might not work with your infrastructure.
At least to my knowledge it can’t deal with a NuGet and a git repository both entirely run in network folders. Yet.

Something you can absolutely do in that scenario however, is this somewhat convoluted build setup:

  1. Create custom network folder based on project and source control version
  2. Copy source files to custom network folder
  3. Configure PathMap to point to network folder
  4. Profit?
<Project>
  <Target Name="SourceLinker" BeforeTargets="BeforeBuild" Condition="$(Configuration)=='Release'">
    <!-- Define base path -->
    <PropertyGroup>
      <BasePath>\\someNetworkFolder\MaybeAddSolutionName</BasePath>
    </PropertyGroup>
 
    <!-- Get hash of current git commit -->
    <Exec Command="git rev-parse HEAD" IgnoreExitCode="true" ConsoleToMSBuild="true">
      <Output TaskParameter="ConsoleOutput" PropertyName="GitHash"/>
    </Exec>
 
    <!-- Set target path -->
    <PropertyGroup>
      <TargetPath>$(BasePath)\$(AssemblyName)\$(GitHash)</TargetPath>
    </PropertyGroup>
 
    <!-- Create directory -->
    <MakeDir Directories="$(TargetPath)"/>
 
    <!-- Determine files to copy -->
    <ItemGroup>
      <SourceFiles Include="@(Compile)" Exclude="obj\**"/>
      <TargetFiles Include="@(SourceFiles->'$(TargetPath)\%(Identity)')"/>
    </ItemGroup>
 
    <!-- Copy source files -->
    <Copy SourceFiles="@(SourceFiles)" DestinationFiles="@(TargetFiles)"/>
 
    <!-- Remap source path -->
    <PropertyGroup>
      <PathMap>$(MSBuildProjectDirectory)=$(TargetPath)</PathMap>
    </PropertyGroup>
  </Target>
</Project>

All you need to do to use this, is to adjust BasePath and import the target in your NuGet projects.
The easiest way being of course placing it in a file called Directory.Build.targets in your solutions root folder.
Also pay attention to the condition. Right now this target copies the source files for every Release build. You might want to change that to fit your needs.

Now when someone tries to step into the code your NuGet provides, the pdb will direct them to a shared network folder containing the source files for exactly the right version of the right assembly. Yay!

There is a catch to this:
Not every IDE will allow you look up source code based on the path in a pdb. I know it works well in Rider and Visual Studio + ReSharper. Pure Visual Studio let’s you jump into the code while debugging, but not from the code directly. Also, check your IDEs settings. Both Rider and VS block external source files by default.

Leave a comment