Programming, technology, and CRM – from a Belgian programmer exiled to Missouri
  • rss
  • Home
  • Contact Me
  • Welcome

Automatic Assembly Version Number

Nicolas Galler | August 6, 2008

Under Properties\AssemblyInfo.cs you can set the version number – this shows up under the file’s details in Windows:

image

There is a nifty msbuild task that was distributed by Microsoft a while back called "AssemblyInfoTask" (I think it came with one of the Team Foundation Server releases) that can automatically set it to the current date which is very handy to quickly find out which version of a particular DLL is at a customer site.  However there are a few issues with the strategy:

  • because the version number is a 16-bit number it has some issues with assemblies built after 2007 (there is a work around for it – as you can see I only have month and day)
  • it updates AssemblyInfo.cs (no easy way to customize that) which is usually under version control… so this causes a lot of unnecessary commits and conflicts (trivial to resolve, but still annoying)
  • since it updates AssemblyInfo.cs every time it runs and not just when the version number needs to change it will cause the assembly to be rebuilt completely every time which makes the build process slower.

Alternatively there is a task in the "MSBuild Community Task" that can be used to generate a VersionInfo.cs file – just as good as AssemblyInfo but because this only contains the version it does not need to be checked into source control alleviating the 2nd issue above (at this point you comment out the AssemblyVersion attribute from the AssemblyInfo.cs file).

Another one from MSBuild Community Task is "SvnInfo" – it can be used to retrieve the Subversion revision number (there are other, equivalent tasks for other version control system).  Using this is just as good if not better than the date so we avoid the 1st issue (though this will come back when my SVN number gets above 65536!  But I have a long way to go for that).

There was still one issue which is the file would always be generated thus preventing the incremental build from shortening the build process as it is supposed to (in C or Java we can somewhat get away with that since the objects are built on a file-by-file basis but in C# they are always built into assemblies).

Therefore I created a very very simple task (don’t laugh, it is my first msbuild custom task!) that generates the file but replaces it only if the number is updated:

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Build.Utilities;
using System.IO;

namespace SSSWorld.MsBuild
{
    /// <summary>
    /// Task used to generate a VersionInfo file with the AssemblyVersion and AssemblyFileVersion
    /// attributes.
    /// Contains logic to avoid overwriting the file if it exists and the content is identical.
    /// </summary>
    public class GenerateVersionInfo : Task
    {
        public UInt16 MajorNumber { get; set; }
        public UInt16 MinorNumber { get; set; }
        public UInt16 RevisionNumber { get; set; }
        public UInt16 BuildNumber { get; set; }
        public String OutputFileName { get; set; }

        public GenerateVersionInfo()
        {
            MajorNumber = 0;
            MinorNumber = 0;
            RevisionNumber = 0;
            BuildNumber = 0;
            OutputFileName = null;
        }

        public override bool Execute()
        {
            String code = GenerateCode();
            String existingCode = null;
            if (File.Exists(OutputFileName))
                existingCode = File.ReadAllText(OutputFileName);
            if (!code.Equals(existingCode))
            {
                File.WriteAllText(OutputFileName, code);
                Log.LogMessage("Generated VersionInfo file {0}", OutputFileName);
            }
            else
            {
                Log.LogMessage("VersionInfo file {0} is up to date", OutputFileName);
            }

            return true;
        }

        private String GenerateCode()
        {
            StringBuilder buf = new StringBuilder();

            buf.AppendLine("// Code generated automatically by SSSWorld.MsBuild.GenerateVersionInfo")
                .Append("[assembly: System.Reflection.AssemblyVersion(\"")
                .Append(MajorNumber).Append(".")
                .Append(MinorNumber).Append(".")
                .Append(RevisionNumber).Append(".")
                .Append(BuildNumber == 0 ? "*" : BuildNumber.ToString())
                .AppendLine("\")]")
                .Append("[assembly: System.Reflection.AssemblyFileVersion(\"")
                .Append(MajorNumber).Append(".")
                .Append(MinorNumber).Append(".")
                .Append(RevisionNumber).Append(".")
                .Append(BuildNumber)
                .AppendLine("\")]");
            return buf.ToString();
        }
    }
}

And I adapted the AssemblyInfoTask target file to call on this task:

<?xml version="1.0" encoding="utf-8"?>
<!-- This targets file includes all the necessary information to automatically increment build numbers as part of
     a regular build process. To use it simply include it in your project file after any other includes. The typical
     include line looks like this:
     
     <Import Project="...\SSSWorld\msbuild\AssemblyVersion.target"/>
     
     and make sure you have a Properties\VersionInfo.cs file in your project (it will be overwritten by this task
     and should NOT be checked in version control)
  -->
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <UsingTask AssemblyFile="SSSWorld.MsBuild.dll" TaskName="SSSWorld.MsBuild.GenerateVersionInfo"/>

  <!-- These properties can be overridden to specify the major/minor version number -->
  <PropertyGroup>
    <MajorNumber Condition="'$(MajorNumber)'==''">1</MajorNumber>
    <MinorNumber Condition="'$(MinorNumber)'==''">0</MinorNumber>
    <!-- Default will be to get the SVN revision number as revision number -->
    <RevisionNumber  Condition="'$(RevisionNumber)'==''">0</RevisionNumber>
  </PropertyGroup>

  <!-- Re-define CoreCompileDependsOn to ensure the assemblyinfo files are updated before compilation. -->
  <PropertyGroup>
    <CoreCompileDependsOn>
      $(CoreCompileDependsOn);
      GenerateVersionInfoFile
    </CoreCompileDependsOn>
  </PropertyGroup>

  <Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>    

  <Target Name="GenerateVersionInfoFile">
    <SvnInfo LocalPath="." Condition="$(RevisionNumber) == 0">
      <Output TaskParameter="Revision" PropertyName="RevisionNumber"/>
    </SvnInfo>
    <GenerateVersionInfo OutputFileName="Properties\VersionInfo.cs"
      MajorNumber="$(MajorNumber)"
      MinorNumber="$(MinorNumber)"
      RevisionNumber="$(RevisionNumber)"/>
  </Target>
</Project>

What then needs to be done to get the version number into any project is to:

  • Remove AssemblyVersion and AssemblyFileVersion attribute from AssemblyInfo.cs
  • Add a VersionInfo.cs under the Properties folder of the project (does not have to contain anything as it will be overwritten automatically – and make sure it is NOT checked into source control)
  • Manually edit the project file and add the import line… Make sure the DLL is in the same directoy as the target file.
  • If desired, the Major and Minor version numbers can be specified in the project file by adding a “MajorNumber” or “MinorNumber” property under the <PropertyGroup> element (typically near the very top of the project file)

Optionally the following command can also be run to prevent the "unsafe project" warning dialog (replace with actual path of target file):

reg add HKLM\Software\Microsoft\VisualStudio\9.0\MSBuild\SafeImports /v AssemblyVersion /t REG_SZ /d "E:\Projects\SSSWorld\msbuild\AssemblyVersion.target"

Note that the key name is HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\9.0\MSBuild\SafeImports on Windows64.

Instead of using SvnInfo to get the revision number it can be obtained using the <Time> task, also part of the MSBuild Community Task.  Just make sure that the number is below 65536 as it needs to fit in 16 bits.

It is very simple but I have uploaded it to http://code.msdn.microsoft.com/AssemblyVersion in case it is useful to anybody else.  It was interesting at least to see how easy it was to create and use an msbuild custom task – I was relieved to find that it was not necessary to sign the assembly and install it to the GAC, and that you can use it right away.

Categories
Programming
Comments rss
Comments rss
Trackback
Trackback

« Web Mailmerge on Vista A multi-select picklist for MS CRM »

2 Responses to “Automatic Assembly Version Number”

  1. Nic Bedford says:
    April 7, 2009 at 5:51 am

    Hi, this looks really useful to me and I have kind of got it working, but how do I override MajorVersion and MinorVersion properties? (ideally from within VS2008) My version at the moment is always 1.0.SVN.0, i like to increment build and minor?

    Thanks in advance, Nic

  2. Nicolas Galler says:
    April 7, 2009 at 7:19 am

    Good question – it is not possible at this time to edit it from within Visual Studio. I’d like to be able to edit it in the VersionInfo.cs file and have the task read it back and detect that it was specified but right now it just does an overwrite and does not attempt to parse the content of the file.

    You can override the Major and Minor version numbers but it requires an edit to the .csproj file (adding the MajorNumber/MinorNumber properties under the PropertyGroup), and a fix to the AssemblyVersion.target that I initially provided (adding a condition on the properties) so that it lets the numbers through instead of overwriting them. I will update the blog article to reflect this.

    Thank you!

Leave a Reply

Click here to cancel reply.

Categories

  • Experiments (4)
  • Interesting (1)
  • MSCRM (1)
  • Programming (60)
  • Rant (3)
  • Saleslogix (34)
  • Tricks (8)
  • Uncategorized (23)

Post History

  • 2010
    • January (3)
  • 2009
    • March (2)
    • April (1)
    • May (3)
    • June (3)
    • July (1)
    • September (3)
    • October (2)
    • December (5)
  • 2008
    • January (9)
    • February (4)
    • March (9)
    • April (1)
    • May (5)
    • June (8)
    • July (1)
    • August (2)
    • September (1)
    • November (1)
    • December (3)
  • 2007
    • January (3)
    • February (7)
    • March (1)
    • April (3)
    • May (6)
    • June (2)
    • July (1)
    • August (2)
    • September (5)
    • October (3)
    • November (5)
    • December (4)
  • 2006
    • January (2)
    • September (1)
    • November (3)
    • December (4)
  • 2005
    • April (1)

Meta

  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
rss Comments rss valid xhtml 1.1 design by jide powered by Wordpress get firefox