Assembly File Version Auto-Increment 'Magic'

For those who do not have the time or the resources to maintain TFS-based continuous integration environment and leverage its very powerful MSBuild functionality, here's a relatively simple way to auto-increment your assembly file version.

You may be wondering, "Why not just set up AssemblyInfo.cs to auto-increment for you? The latest version of Visual Studio (2013 atm) already does this." Well, yes, it does. But I don't like the semi-random numbers Visual Studio assigns. 

Sure, I can do this: 

[assembly: AssemblyVersion("2.27.*")]

And looking at the Properties window of the built DLL you see the following:

I don't know about you, but those numbers don't mean much to me. I want to have more control over these numbers and bring more semantic meaning to it them. For example, I want to see something like 2.27.5.10 and I want to have the last number automatically increment with every single build. Inspired by Walkthrough: How to increment AssemblyFileVersion automatically at each build using T4 by Bruno Tagliapietra, I found a relatively simple way to do just that.

First, I start with a T4 template named AssemblyFileVersion.tt that will generate a new AssemblyFileVersion.cs file every time it executes and auto-increments the last number. You can place this at the root level of your project. Here are the contents of my AssemblyFileVersion.tt file: 

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ import namespace="System.IO" #>
<#@ output extension=".cs" #>
<#
    int major = 0; 
    int minor = 0; 
    int build = 0; 
    int revision = 0; 
 
    try
    {
        using(var f = File.OpenText(Host.ResolvePath("AssemblyFileVersion.cs")))
        {
            string maj = f.ReadLine().Replace("//","");
            string min = f.ReadLine().Replace("//","");
            string b = f.ReadLine().Replace("//","");
            string r = f.ReadLine().Replace("//","");
 
            major = int.Parse(maj); 
            minor = int.Parse(min); 
            build = int.Parse(b); 
            revision = int.Parse(r) + 1; 
        }
    }
   catch
    {
        major = 1; 
        minor = 0; 
        build = 0; 
        revision = 0; 
    }
#>
//<#= major #>
//<#= minor #>
//<#= build #>
//<#= revision #>
// 
// This code was generated by a tool. Any changes made manually will be lost
// the next time this code is regenerated.
// 
 
using System.Reflection;
 
[assembly: AssemblyFileVersion("<#= major #>.<#= minor #>.<#= build #>.<#= revision #>")]

Admittedly, it's a bit hacky. But the simplest way to read in the current version is using the first four commented out lines. I'm fine with this approach, but feel free to customize yours as you see fit. Also, note that if the AssemblyFileVersion.cs doesn't exist yet, it'll generate one with version 1.0.0.0.

The next step is to execute the T4 template on every build automatically (without having to manually Run Custom Tool). For this, I leverage the Pre-Build event command line option of the project's properties, Build Events section, like so:

set textTemplatingPath="%CommonProgramFiles(x86)%\Microsoft Shared\TextTemplating\$(VisualStudioVersion)\texttransform.exe"
if %textTemplatingPath%=="\Microsoft Shared\TextTemplating\$(VisualStudioVersion)\texttransform.exe" set textTemplatingPath="%CommonProgramFiles%\Microsoft Shared\TextTemplating\$(VisualStudioVersion)\texttransform.exe"
%textTemplatingPath% "$(ProjectDir)AssemblyFileVersion.tt"

The only issue I've ran into here is related to source control locking the file, thus it can't be overwritten when T4 executes. For this, we I need to make sure the file is first checked out for editing. Using TFS in my case, this line does the trick:

"$(DevEnvDir)tf.exe" checkout "$(ProjectDir)AssemblyFileVersion.cs"

And all together now:

"$(DevEnvDir)tf.exe" checkout "$(ProjectDir)AssemblyFileVersion.cs"
set textTemplatingPath="%CommonProgramFiles(x86)%\Microsoft Shared\TextTemplating\$(VisualStudioVersion)\texttransform.exe"
if %textTemplatingPath%=="\Microsoft Shared\TextTemplating\$(VisualStudioVersion)\texttransform.exe" set textTemplatingPath="%CommonProgramFiles%\Microsoft Shared\TextTemplating\$(VisualStudioVersion)\texttransform.exe"
%textTemplatingPath% "$(ProjectDir)AssemblyFileVersion.tt"

Now build your project/solution. 

Check your built DLL's properties and you'll see something like this (eventually, but at first you'll start with 1.0.0.0):

I've manually updated the top commented out lines of AssemblyFileVersion.cs to start with 2.27.5. And the 10 represents the number of builds I've had since then. This is very basic and of course the script could evolve to be smart enough to auto-increment more than just the last number. Enjoy and let me know if you've come up with a slicker solution.

No Comments