
PEARL: The source file pathnames that the compiler generates via the CallerFileName attribute (and also wrtes in .pdb files, a.k.a. symbol files) are absolute pathnames, meaning that they start from the drive letter, and they include the path to the developer's home folder and the kitchen sink.
This causes the following problems:
- The source file pathnames are only meaningful on the filesystem of the computer on which the code was compiled.
- The generated .pdb files can only be used on the computer on which the code was compiled.
- The source file pathnames embedded in the binaries disseminate sensitive information, such as the developer's username, to the whole wild world.
- The source file pathnames are too darn long, wasting precious real estate in the debug output window, where we have to prefix each log line with the source file pathname so that the line is clickable.
The last problem could be remedied programmatically, by converting each source
file pathname to a relative pathname before emitting it to the debug output
window, (and it would work, with a caveat addressed below,) but the full
source pathnames would still be stored in the executable and in the .pdb
files, so this remedy would only address the last problem; the other
problems remain.
The C# compiler supports a little-known property called 'PathMap', which can be used to fix this.
See: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-options/advanced#pathmap
(Note that Microsoft calls it an "advanced" compiler option.)
PEARL: The documentation for the `PathMap` compiler option starts with a conspicuous note which says:
Note: Specifying PathMap prevents breakpoints from working in local debug builds. Only set PathMap for production or continuous integration builds.
This note is wrong, first of all because if you mess up the mapping, it is not only breakpoints that will stop working; all kinds of weird things will start happening while debugging, such as Visual Studio opening up a file dialog asking you to locate the source files for various lines of code.
Most importantly, this note is wrong because `PathMap` can be used to point to the exact same files by relative instead of absolute pathname, and everything continues to work just fine. (This is what the tooling should have been doing by default in the first place.) Problems arise only if `PathMap` is used to map files to locations that do not exist in the file system, but then the note is exactly as useless as a note saying that if you point a gun to your foot and pull the trigger, you will be shooting yourself in the foot.
Whether you shorten source file pathnames with code at runtime, or use PathMap to
shorten them at compile-time, you have to be aware of another pitfall:
PEARL:
Clicking on a line prefixed with a source file pathname in the output window
does not work if the pathname is relative to the solution directory; Visual
Studio only understands relative pathnames if they are relative to the
directory of the startup project! This poses a serious problem considering
that a solution may contain multiple executable projects, so the directory to
which source file pathnames must be relative to changes depending on which
project we launch.
At first this problem seems insurmountable, but luckily it can be solved, too.
To properly map source file pathnames from absolute to relative:
- Make sure that all of your project directories are immediate children of the solution directory.
- Make sure that all of your project names are identical to their respective directory names.
- Map each absolute source file pathname by replacing the `$(MSBuildProjectDirectory)` part with `./../$(MSBuildProjectName)`.
Cover image: The MSBuild logo from https://github.com/dotnet/msbuild/tree/main/branding
No comments:
Post a Comment