Vagif Abilov's blog on .NET

Using T4 templates to manage assembly version information

I wanted to configure version information generation for some .NET projects. It’s been a long time since I investigated available options, so I searched around hoping to find some simple way of doing this. What I’ve found didn’t look very encouraging: people write Visual Studio add-ins and custom MsBuild tasks just to obtain one integer number (okay, maybe two). This felt overkill for a small personal project.

The inspiration came from one of the StackOverflow discussions where somebody suggested that T4 templates could do the job. And of course they can. The solution requires a minimal effort and no Visual Studio or build process customization. Here what should be done:

1. Create a file with extension ".tt" and place there T4 template that will generate AssemblyVersion and AssemblyFileVersion attributes:

<#@ template language="C#" #>
// 
// 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: AssemblyVersion("1.0.1.<#= this.RevisionNumber #>")]
[assembly: AssemblyFileVersion("1.0.1.<#= this.RevisionNumber #>")]
<#+
    int RevisionNumber = (int)(DateTime.UtcNow - new DateTime(2010,1,1)).TotalDays;
#>

You will have to decide about version number generation algoritm. For me it was sufficient to auto-generate a revision number that is set to the number of days since January 1st, 2010. As you can see, the version generation rule is written in plain C#, so you can easily adjust it to your needs.

2. The file above should be placed in one of the projects. I created a new project with just this single file to make version management technique clear. When I build this project (actually I don’t even need to build it: saving the file is enough to trigger a Visual Studio action), the following C# is generated:

// 
// 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: AssemblyVersion("1.0.1.113")]
[assembly: AssemblyFileVersion("1.0.1.113")]

Yes, today it’s 113 days since January 1st, 2010. Tomorrow the revision number will change.

3. Next step is to remove AssemblyVersion and AssemblyFileVersion attributes from AssemblyInfo.cs files in all projects that should share the same auto-generated version information. Instead choose “Add existing item” for each projects, navigate to the folder with T4 template file, select corresponding “.cs” file and add it as a link. That will do!

What I like about this approach is that it is lightweight (no custom MsBuild tasks), and auto-generated version information is not added to source control. And of course using C# for version generation algorithm opens for algorithms of any complexity.

Comments

Kristof Mattei said:

Why does the class feature have to be added to the end of the file?

It's weird, how is the file processes? End to start?

# April 28, 2010 8:40 AM

Vagif Abilov said:

Kristof,

I think this is because of the way VS T4 engine expands it. Oleg Sych in his blog gives some details:

www.olegsych.com/.../t4-class-feature-blocks

As far as I understood it, if class feature block comes first, VS places it outside its auto generated GeneratedTextTransformation wrapped class, and the code generation fails. So we just have to accept it.

# April 28, 2010 9:42 AM

Adam said:

Nice solution, but is pretty worthless unless you get the T4 template to re-generate the class on every build:

stackoverflow.com/.../get-visual-studio-to-run-a-t4-template-on-every-build

# May 9, 2011 7:28 PM

Herb said:

I researched every solution out there and this is the ultimate solution, and here is my version.  I did have some issues end to end and had to refer to Adams comment.

1. review the following: msdn.microsoft.com/.../ee847423.aspx

2. That will cause a missing filetracker.dll issue which is resolved by placing the following at the VERY END of your project file.

<Project>

...

 <PropertyGroup>

   <TrackFileAccess>false</TrackFileAccess>

 </PropertyGroup>

</Project>

this is my transform entry in the same project file.

<PropertyGroup>

   <TransformOnBuild>true</TransformOnBuild>

   <OverwriteReadOnlyOutputFiles>true</OverwriteReadOnlyOutputFiles>

   <!-- Other properties can be inserted here -->

</PropertyGroup>

This is my entire TT as our build system had this auto build increment already in place and in an ini file.  I am glad I didn't have to do that work :)

<#@ template debug="false" hostspecific="true" language="C#" #>

<#@ output extension=".cs" #>

<#@ import namespace="System.IO" #>

<#@ import namespace="System.Text.RegularExpressions" #>

<#

string templateDirectory = Path.GetDirectoryName(Host.TemplateFile);

string outputFilePath = Path.Combine(templateDirectory, "..\\..\\..\\cm\\CMBuildVersion.ini");

string contents = File.ReadAllText(outputFilePath);

Regex versionRegex = new Regex(@"BuildVersion\=(?<version>\d+)");

   MatchCollection matches = versionRegex.Matches(contents);

string version = matches[0].Groups["version"].Value;

   RevisionNumber = Convert.ToInt32(version);

#>

using System.Reflection;

using System.Runtime.CompilerServices;

using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following

// set of attributes. Change these attribute values to modify the information

// associated with an assembly.

[assembly: AssemblyTitle("MotherApplication")]

[assembly: AssemblyDescription("")]

[assembly: AssemblyConfiguration("")]

[assembly: AssemblyCompany("")]

[assembly: AssemblyProduct("MotherApplication")]

[assembly: AssemblyCopyright("Copyright ©  2011")]

[assembly: AssemblyTrademark("")]

[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible

// to COM components.  If you need to access a type in this assembly from

// COM, set the ComVisible attribute to true on that type.

[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM

[assembly: Guid("0f9b5e08-08f2-446f-832e-3aeabb69abb2")]

// Version information for an assembly consists of the following four values:

//

//      Major Version

//      Minor Version

//      Build Number

//      Revision

//

// You can specify all the values or you can default the Revision and Build Numbers

// by using the '*' as shown below:

[assembly: AssemblyVersion("1.0.1.<#= this.RevisionNumber #>")]

[assembly: AssemblyFileVersion("1.0.1.<#= this.RevisionNumber #>")]

<#+

   int RevisionNumber = -1;

#>

# November 21, 2011 10:26 PM
Leave a Comment

(required) 

(required) 

(optional)

(required) 


Please add 2 and 3 and type the answer here: