I would like to detail here a recent featurette we added to NDepend v2.9.1: Source files rebasing. Source files rebasing enables a common CI scenario where the code is compiled in one location and analyzed in another one.
NDepend needs to access source files during various scenario:
During source file parsing at analysis time, to fetch comments and cyclomatic complexity metrics values and to store code elements’ declarations locations.
When clicking Open my declaration in source code of a code element.
When clicking Compare older and newer versions of source file of a code element.
The magic of PDB files
To understand the need for Source files rebasing, let’s remind some details about how the .NET debugger works with source code.
In the .NET world, when one wants to bind with source code from IL code, one relies on PDB files (Program Database Files). PDB files are built at compile-time by the C#/VB.NET or C++ compiler. There is one PDB file per .NET assembly. PDB files contain binary information about where concrete methods (i.e methods that contain code, as opposed to abstract methods) are declared in source file and which piece of IL code corresponds to which sequence point. A sequence point is a contiguous excerpt of a source file that is considered as a unit of execution from the debugger point of view. Sequence points can thus be considered as debugger breakpoint because they are meant to be unit of execution.
As a side note, NDepend uses sequence points to compute the number of lines of code of a method. Doing so comes with several major benefits explained here:How do you count your number of Lines Of Code (LOC) ?
These source files’ paths contained in PDB represent essential information for the debugger. Indeed, the debugger relies on this info to bind source code with currently executed IL code. If you develop on several machines and thus move your source code from one machine to another, you might have stumble upon the following VisualStudio message that informs you that source file can’t be found based on the absolute file paths extracted from the PDB. You can then tell the debugger where the source file is and it will be smart enough to rebase further absolute source file path.
A variant is if a source file has been changed and the PDB files hasn’t been updated since (internally a hash code is used to make possible the synchronization check between source file content and PDB files’ sequence points).
Btw, as many others build process flaws, the fact that PDB files are not in-sync or source files can’t be found from PDB absolute source files paths are reported by the NDepend analysis. This feature is part of what we call Build Process health.
Source files rebasing within NDepend
Basically the underlying problematic is the same with NDepend: source code might have been compiled in folder A and maybe the analysis occurs on a different machine, where the source files are in folder B. The absolute source files’ paths extracted from the PDB are not anymore relevant and NDepend needs to rebase them, from A to B. This is possible thanks to the following analysis option:
For maximum flexibility, source file rebasing can be configured when analysis results have been loaded inside NDepend. This way, analysis results can be churned from any machine and the users can still go back to source files declaration at any time, no matter where the source files hierarchy is stored:
As in VisualStudio, VisualNDepend will be smart enough to ask the user if she wants to rebase a non-found source file and will apply rebasing delta for further source file declaration opening.
Another useful scenario is during 2 builds comparison. Usually, analysis results are done the same way and thus, source files paths are the same both in older and newer analysis results. In this condition, when trying to compare 2 versions of a source file NDepend tells that because the 2 paths are the same, it can’t compare the source files.
A solution to this problem is to fetch the older and the newer source files hierarchy from the source code repository and rebase both the older and newer application in VisualNDepend. This way, 2 versions of a source file can be compared properly.
All the NDepend path code relies on the NDepend.Helpers.FileDirectoryPath library. Each time I have a look at the extremely poor path handling support of the .NET Fx I am surprised that
- this library is not so popular
- there is no other equivalent library proposed (as far as I know)
- MS doesn’t seem to consider it as a major room for future improvement.
Don’t other developers need to handle complex path scenario or does everybody re-invent the wheel?
I think that all tool for .NET developers work more or less this way when it comes to source files rebasing, as you can read here for the excellent JetBrain dotTrace profiler.