Embedding mercurial revision information in Visual Studio c# projects automatically
- by Mark Booth
Original Problem
In building our projects, I want the mercurial id of each repository to be embedded within the product(s) of that repository (the library, application or test application).
I find it makes it so much easier to debug an application ebing run by custiomers 8 timezones away if you know precisely what went into building the particular version of the application they are using. As such, every project (application or library) in our systems implement a way of getting at the associated revision information.
I also find it very useful to be able to see if an application has been compiled with clean (un-modified) changesets from the repository. 'Hg id' usefully appends a + to the changeset id when there are uncommitted changes in a repository, so this allows is to easily see if people are running a clean or a modified version of the code.
My current solution is detailed below, and fulfills the basic requirements, but there are a number of problems with it.
Current Solution
At the moment, to each and every Visual Studio solution, I add the following "Pre-build event command line" commands:
cd $(ProjectDir)
HgID
I also add an HgID.bat file to the Project directory:
@echo off
type HgId.pre > HgId.cs
For /F "delims=" %%a in ('hg id') Do <nul >>HgID.cs set /p = @"%%a"
echo ; >> HgId.cs
echo } >> HgId.cs
echo } >> HgId.cs
along with an HgId.pre file, which is defined as:
namespace My.Namespace {
/// <summary> Auto generated Mercurial ID class. </summary>
internal class HgID {
/// <summary> Mercurial version ID [+ is modified] [Named branch]</summary>
public const string Version =
When I build my application, the pre-build event is triggered on all libraries, creating a new HgId.cs file (which is not kept under revision control) and causing the library to be re-compiled with with the new 'hg id' string in 'Version'.
Problems with the current solution
The main problem is that since the HgId.cs is re-created at each pre-build, every time we need to compile anything, all projects in the current solution are re-compiled. Since we want to be able to easily debug into our libraries, we usually keep many libraries referenced in our main application solution. This can result in build times which are significantly longer than I would like.
Ideally I would like the libraries to compile only if the contents of the HgId.cs file has actually changed, as opposed to having been re-created with exactly the same contents.
The second problem with this method is it's dependence on specific behaviour of the windows shell. I've already had to modify the batch file several times, since the original worked under XP but not Vista, the next version worked under Vista but not XP and finally I managed to make it work with both. Whether it will work with Windows 7 however is anyones guess and as time goes on, I see it more likely that contractors will expect to be able to build our apps on their Windows 7 boxen.
Finally, I have an aesthetic problem with this solution, batch files and bodged together template files feel like the wrong way to do this.
My actual questions
How would you solve/how are you solving the problem I'm trying to solve?
What better options are out there than what I'm currently doing?
Rejected Solutions to these problems
Before I implemented the current solution, I looked at Mercurials Keyword extension, since it seemed like the obvious solution. However the more I looked at it and read peoples opinions, the more that I came to the conclusion that it wasn't the right thing to do.
I also remember the problems that keyword substitution has caused me in projects at previous companies (just the thought of ever having to use Source Safe again fills me with a feeling of dread *8').
Also, I don't particularly want to have to enable Mercurial extensions to get the build to complete. I want the solution to be self contained, so that it isn't easy for the application to be accidentally compiled without the embedded version information just because an extension isn't enabled or the right helper software hasn't been installed.
I also thought of writing this in a better scripting language, one where I would only write HgId.cs file if the content had actually changed, but all of the options I could think of would require my co-workers, contractors and possibly customers to have to install software they might not otherwise want (for example cygwin).
Any other options people can think of would be appreciated.
Update
Partial solution
Having played around with it for a while, I've managed to get the HgId.bat file to only overwrite the HgId.cs file if it changes:
@echo off
type HgId.pre > HgId.cst
For /F "delims=" %%a in ('hg id') Do <nul >>HgId.cst set /p = @"%%a"
echo ; >> HgId.cst
echo } >> HgId.cst
echo } >> HgId.cst
fc HgId.cs HgId.cst >NUL
if %errorlevel%==0 goto :ok
copy HgId.cst HgId.cs
:ok
del HgId.cst
Problems with this solution
Even though HgId.cs is no longer being re-created every time, Visual Studio still insists on compiling everything every time. I've tried looking for solutions and tried checking "Only build startup projects and dependencies on Run" in Tools|Options|Projects and Solutions|Build and Run but it makes no difference.
The second problem also remains, and now I have no way to test if it will work with Vista, since that contractor is no longer with us.
If anyone can test this batch file on a Windows 7 and/or Vista box, I would appreciate hearing how it went.
Finally, my aesthetic problem with this solution, is even strnger than it was before, since the batch file is more complex and this there is now more to go wrong.
If you can think of any better solution, I would love to hear about them.