How to auto-increment assembly version using a custom MSBuild task

Published Friday, December 2, 2005 12:31 PM

The assembly version string has the format “major.minor.build.revision”, such as 2.0.50727.42.  Here is a sample on how to create a custom MSBuild task for Web Deployment Projects  to auto-increment the build and revision portion of your version strings with each build.

 

Web Deployment Projects provide a way to pre-compile a Visual Studio 2005 Web Site project into a single assembly.  It also allows you to specify the AssemblyFileVersion and AssemblyVersion for this assembly.

 

These values can be set in the Project Properties dialog of the Web Deployment Project.  Once set the following will be added to the Web Deployment Project file.

 

  <ItemGroup>

    <AssemblyAttributes Include="AssemblyFileVersion">

      <Value>2.0.0.0</Value>

    </AssemblyAttributes>

    <AssemblyAttributes Include="AssemblyVersion">

      <Value>2.0.0.0</Value>

      <AutoIncrement>true</AutoIncrement>

    </AssemblyAttributes>

  </ItemGroup>

 

The Microsoft.WebDeployment.targets file includes a target named GenerateAssemblyInfo that will use the @(AssemblyAttributes) item collection defined above to dynamically create an AssemblyInof.cs file.  This file is compiled into an assembly and passed to aspnet_merge.exe using its –copyattrs argument.

 

This is great if you want to fix the version to a specific value, but what if you want to generate a new build number with every build.  After all the format of version string is “major.minor.build.revision”.

 

To do this we’ll need to dynamically generate the AssemblyAttributes item collection at build time instead of statically declaring their values in the Web Deployment Project file.

 

First create a custom MSBuild task.  MSBuild tasks are classes derived from Microsoft.Build.Utilities.Task that override the Execute() method.  You can learn more about writing MSBuild tasks from How To: Write a Task

 

Here is a sample MSBuild task that will increment build and revision numbers. 

 

Contents of file IncrementBuildNumber.cs:

 

using System;

using System.IO;

using Microsoft.Build.Utilities;

using Microsoft.Build.Framework;

 

namespace MyTasks

{

    public class IncrementBuildNumber : Task

    {

        string m_fileName;      // Text file containing previous build number

        long m_buildNumber;     // Build number based on current date. 12/2/2005 would be 51202

        long m_revisionNumber;  // Revision number, increments within a day with each new day starting at 0

 

        /// <summary>

        /// MSBuild entry point into this task.

        /// </summary>

        public override bool Execute()

        {

            bool bSuccess = true;

            try

            {

                IncrementNumbers();

                Log.LogMessage(MessageImportance.Normal, "Build {0}.{1}", m_buildNumber, m_revisionNumber);

            }

            catch (Exception ex)

            {

                // Log Failure

                Log.LogErrorFromException(ex);

                Log.LogMessage(MessageImportance.High, "Failed to increment Build Number!");

                bSuccess = false;

            }

            return bSuccess;

        }

 

        /// <summary>

        /// Task argument set MSBuild project file

        /// </summary>

        [Required]

        public string File

        {

            get { return m_fileName; }

            set { m_fileName = value; }

        }

 

        /// <summary>

        /// Task output available to MSBuild

        /// </summary>

        [Output]

        public long BuildNumber

        {

            get { return m_buildNumber; }

            set { m_buildNumber = value; }

        }

 

        /// <summary>

        /// Task output available to MSBuild

        /// </summary>

        [Output]

        public long RevisionNumber

        {

            get { return m_revisionNumber; }

            set { m_revisionNumber = value; }

        }

 

        /// <summary>

        /// Increments Build Number and Build Revision

        /// based on the numbers saved in a text file.

        /// </summary>

        private void IncrementNumbers()

        {

            // Set build number to current date, 12/02/2005 == 51202

            DateTime dDate = DateTime.Now;

            m_buildNumber = dDate.Year % 2000 * 10000;

            m_buildNumber += dDate.Month * 100;

            m_buildNumber += dDate.Day;

 

            // Defualt build revision to 0

            m_revisionNumber = 0;

 

            // Check for a previous build and revision number

            if (System.IO.File.Exists(m_fileName))

            {

                StreamReader sr = new StreamReader(m_fileName);

                string previousBuild = sr.ReadLine();

                sr.Close();

 

                string[] previousNumbers = previousBuild.Split('.');

 

                if (m_buildNumber == long.Parse(previousNumbers[0]))

                    m_revisionNumber = long.Parse(previousNumbers[1]) + 1;

            }

 

            // Write the current build numbers to the file

            StreamWriter sw = new StreamWriter(m_fileName);

            sw.WriteLine(string.Format("{0}.{1}", m_buildNumber, m_revisionNumber));

            sw.Flush();

            sw.Close();

        }

    }

}

 

Compile file IncrementBuildNumber.cs into the assembly MyTasks.dll

 

csc /t:library /r:Microsoft.Build.Utilities.dll;Microsoft.Build.Framework.dll /out:MyTasks.dll IncrementBuildNumber.cs

 

Copy the MyTasks.dll into the same directory as the Web Deployment Project.

 

Now that we’ve got our custom MSBuild task we need to modify the Web Deployment Project file to use our new custom task.

 

You can edit the project file by right clicking on the Web Deployment Project in the solution explorer and selecting “Open Project File” from the shortcut menu.

 

Once opened add a <UsingTask> between the <Project> and the first <PropertyGroup> to register the new custom IncrementBuildNumber task with MSBuild.

 

<!--

  Microsoft Visual Studio 2005 Web Deployment Project

  http://go.microsoft.com/fwlink/?LinkId=55111

-->

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <UsingTask TaskName="MyTasks.IncrementBuildNumber" AssemblyFile="MyTasks.dll" />

  <PropertyGroup>

     

  </PropertyGroup>

 

Next override the BeforeBuild target with the code below.

 

 

  <Target Name="BeforeBuild">

    <IncrementBuildNumber

      File = "$(MSBuildProjectDirectory)\BuildNumbers.txt">

      <Output TaskParameter="BuildNumber" PropertyName="BuildNumber"/>

      <Output TaskParameter="RevisionNumber" PropertyName="RevisionNumber"/>

    </IncrementBuildNumber>

 

    <CreateItem Include="AssemblyFileVersion" AdditionalMetadata="Value=2.0.$(BuildNumber).$(RevisionNumber)">

      <Output ItemName="AssemblyAttributes" TaskParameter="Include" />

    </CreateItem>

 

    <CreateItem Include="AssemblyVersion" AdditionalMetadata="Value=2.0.$(BuildNumber).$(RevisionNumber)">

      <Output ItemName="AssemblyAttributes" TaskParameter="Include" />

    </CreateItem>

 

  </Target>

 

 

In this sample we’re using a BuildNumbers.txt file to store the build numbers from each build.  It is passed to the IncrementBuildNumber task through the File property.  The $(MSBuildProjectDirectory) property is a built in property provided by MSBuild that specifies the directory containing the project file.

 

The BuildNumber and BuildRevision values are then copied from our custom MSBuild task IncrementBuildNumbers as MSBuild properties using the <Output> tags.

      <Output TaskParameter="BuildNumber" PropertyName="BuildNumber"/>

      <Output TaskParameter="RevisionNumber" PropertyName="RevisionNumber"/>

 

At this point there are now 2 new MSBuild properties $(BuildNumber) and $(RevisionNumber).  The next step is to dynamically create the AssemblyAttributes version strings using these properties.

 

    <CreateItem Include="AssemblyFileVersion" AdditionalMetadata="Value=2.0.$(BuildNumber).$(RevisionNumber)">

      <Output ItemName="AssemblyAttributes" TaskParameter="Include" />

    </CreateItem>

 

    <CreateItem Include="AssemblyVersion" AdditionalMetadata="Value=2.0.$(BuildNumber).$(RevisionNumber)">

      <Output ItemName="AssemblyAttributes" TaskParameter="Include" />

    </CreateItem>

 

Once created they will be automatically picked up the GenerateAssemblyInfo target provided in the Web Deployment Project.

 

Be sure to delete the statically declared AssemblyAttributes Items for AssemblyFileVersion and AssemblyVersion.

 

Delete any of these entries from your project file.

    <AssemblyAttributes Include="AssemblyFileVersion">

      <Value>2.0.0.0</Value>

    </AssemblyAttributes>

    <AssemblyAttributes Include="AssemblyVersion">

      <Value>2.0.0.0</Value>

      <AutoIncrement>true</AutoIncrement>

    </AssemblyAttributes>

 

You will get a build error if forget to delete these statically declared values.

 

Now let’s build and see what happens.

 

 

Microsoft (R) Build Engine Version 2.0.50727.42

[Microsoft .NET Framework, Version 2.0.50727.42]

Copyright (C) Microsoft Corporation 2005. All rights reserved.

 

Build started 12/2/2005 8:46:42 AM.

__________________________________________________

Project "C:\MyProjects\MyWeb\MyWeb_deploy\MyWeb_deploy.wdproj" (default targets):

 

Target BeforeBuild:

    Build 51202.0

Target AspNetCompiler:

    C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_compiler.exe -v /MyWeb -p C:\MyProjects\MyWeb\MyWeb -u -f -d C:\MyProjects\MyWeb\MyWeb_deploy\Debug\

    Updateing web.config compilation debug = 'True' ...

    Successfully updated web.config compilation debug = 'True' ...

Target GenerateAssemblyInfo:

    Generating AssemblyInfo ...

    Setting [assembly: AssemblyFileVersion("2.0.51202.0")]

    Setting [assembly: AssemblyVersion("2.0.51202.0")]

    Successfully Generated AssebmlyInfo ...

    C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Csc.exe /out:C:\MyProjects\MyWeb\MyWeb_deploy\AssemblyInfo\Debug\AssemblyInfo.dll /target:library C:\MyProjects\MyWeb\MyWeb_deploy\AssemblyInfo\Debug\AssemblyInfo.cs

Target AspNetMerge:

    Running aspnet_merge.exe ...

    C:\Program Files\MSBuild\Microsoft\WebDeployment\v8.0\aspnet_merge.exe C:\MyProjects\MyWeb\MyWeb_deploy\Debug -o MyWeb -copyattrs C:\MyProjects\MyWeb\MyWeb_deploy\AssemblyInfo\Debug\AssemblyInfo.dll -debug

    Successfully merged 'C:\MyProjects\MyWeb\MyWeb_deploy\Debug'.

 

Build succeeded.

    0 Warning(s)

    0 Error(s)

 

Time Elapsed 00:00:10.29

 

 

Since this is the first build today it’s getting a build number based on the current date 51202 and the revision is 0.  What happens if we build again?

 

 

Microsoft (R) Build Engine Version 2.0.50727.42

[Microsoft .NET Framework, Version 2.0.50727.42]

Copyright (C) Microsoft Corporation 2005. All rights reserved.

 

Build started 12/2/2005 8:46:56 AM.

__________________________________________________

Project "C:\MyProjects\MyWeb\MyWeb_deploy\MyWeb_deploy.wdproj" (default targets):

 

Target BeforeBuild:

    Build 51202.1

Target AspNetCompiler:

    C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_compiler.exe -v /MyWeb -p C:\MyProjects\MyWeb\MyWeb -u -f -d C:\MyProjects\MyWeb\MyWeb_deploy\Debug\

    Updateing web.config compilation debug = 'True' ...

    Successfully updated web.config compilation debug = 'True' ...

Target GenerateAssemblyInfo:

    Generating AssemblyInfo ...

    Setting [assembly: AssemblyFileVersion("2.0.51202.1")]

    Setting [assembly: AssemblyVersion("2.0.51202.1")]

    Successfully Generated AssebmlyInfo ...

    C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Csc.exe /out:C:\MyProjects\MyWeb\MyWeb_deploy\AssemblyInfo\Debug\AssemblyInfo.dll /target:library C:\MyProjects\MyWeb\MyWeb_deploy\AssemblyInfo\Debug\AssemblyInfo.cs

Target AspNetMerge:

    Running aspnet_merge.exe ...

    C:\Program Files\MSBuild\Microsoft\WebDeployment\v8.0\aspnet_merge.exe C:\MyProjects\MyWeb\MyWeb_deploy\Debug -o MyWeb -copyattrs C:\MyProjects\MyWeb\MyWeb_deploy\AssemblyInfo\Debug\AssemblyInfo.dll -debug

    Successfully merged 'C:\MyProjects\MyWeb\MyWeb_deploy\Debug'.

 

Build succeeded.

    0 Warning(s)

    0 Error(s)

 

Time Elapsed 00:00:13.26

 

 

The build number stays as 51202 but the revision number has incremented to 1.  The revision number will continue to increment for each build during the day then starting tomorrow the build number will change to 51203 and the revision will reset to 0.

 

This is just one example of how to increment build numbers during a build.  In this case the build numbers are coming from a text file and being auto incremented by a custom MSBuild task.

 

There are other techniques that you could use.  For example MSBuild also exposes all environment variables from the command shell.  So if you primarily build from a command line or batch build environment you could simply set an environment variable for BuildNumber and RevisionNumber.

 

c:\set BuildNumber=51202

c:\set RevisionNumber=2

 

Using this technique you can go back to statically defining your AssemblyAttributes and simply reference the environment variables.

 

  <ItemGroup>

    <AssemblyAttributes Include="AssemblyFileVersion">

      <Value>2.0.$(BuildNumber).$(RevisionNumber)</Value>

    </AssemblyAttributes>

    <AssemblyAttributes Include="AssemblyVersion">

      <Value>2.0.$(BuildNumber).$(RevisionNumber)</Value>

    </AssemblyAttributes>

  </ItemGroup>

 

While simpler to implement it is not auto-incrementing the revision with each build, instead it’s relying on an external build process to set these values.  MSBuild is an extremely powerful build tool exposing many capabilities.  The techniques you use will vary depending on your needs.

 

Hope this helps,

Brad

Comments

# BradleyB said on Friday, December 2, 2005 6:55 PM

Thanks, that looks like a pretty complete task for modifying an existing AssemblyInfo file.

WebSites in VS05 generally don't have an AssemblyInfo file. Instead Web Deployment Projects are dynamically creating the AssemblyInfo file and compiling it into a separate assembly which is used by aspnet_merge.exe. This task is simply a way to change the AssemblyAttributes Item collection used by Web Deployment Projects.

Thanks for the pointer,
Brad.

# Manish Agrawal said on Saturday, December 10, 2005 11:51 AM

Hi Bradley,

For past 2 days I am continously trying to implement the steps you have mentioned in this post. The numbers are getting generated properly and also get echoed during Build process but the dll's generated in precompiledweb/bin only show version as 0.0.0.0

I don't know what I am missing, the only difference between my environment and the example you have given is, I am using MSBuild script i.e. .proj file to create the precompiledweb, which I am executing from the command prompt. As I am not having VS 2005, so I cannot use Web Deployment Project from inside it.

I think because of this GenerateAssemblyInfo task of WebDeployment project is not getting executed.

Please suggest..

Thanks,
--Manish Agrawal

# Mike Cao said on Wednesday, August 9, 2006 3:08 PM

I tried using the following..

<UsingTask TaskName="Org.IncrementBuildNumber" AssemblyFile="Org.Core.dll" />

with the file Org.Core.dll in the web deploy directory, and it threw an error saying it couldn't find the file. I then renamed the file to simply Org.dll and changed the UsingTask to AssemblyFile="Org.dll", and it ran fine. I believe it's a bug.

# Bryan Hughes said on Wednesday, January 3, 2007 2:14 PM

Since the new year I get this compile message

Error 1 Error emitting 'System.Reflection.AssemblyVersionAttribute' attribute -- 'The version specified '1.0.70103.2' is invalid' AssemblyInfo\Release\AssemblyInfo.cs 5 12 WISH_Debug

---------------------

Warning 2 Assembly generation -- The version '1.0.70103.2' specified for the 'file version' is not in the normal 'major.minor.build.revision' format CSC 1 1 WISH_Debug

What is the problem?

# Fred Wong said on Thursday, January 4, 2007 12:03 PM

The problem is the date portion is treated as a 16-bit number, and anything in 2007 will exceed the maximum unsigned integer value.

# Bryam said on Friday, January 12, 2007 11:36 AM

Does Assembly Versions have to be in 0.0.0.0 format or can they customized to (Major).(Minor).(yy).(MMDD).(Build)?

I am asking this because of the 07 break issue.

Thanks

Bryan

# may215 said on Thursday, January 18, 2007 3:32 AM

hi...

i get the following errors, about the following dll's:

"The type or namespace name 'Build' does not exist in the namespace 'Microsoft' (are you missing an assembly reference?"

Microsoft.Build.Framework.dll, Microsoft.Build.Utilities.dll.

I tried to take those dll's from "C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727" and put them in my BIN folder, or in the GAC, and still the same error, why is that?

thank you...

# Jill said on Wednesday, March 28, 2007 9:06 PM

I am building the buildnumber using YearWeekNumberWeekDay.  I get the error: "Input string was not in a correct format. Failed to increment Build Number!"  So, for example my value is 07133 which when converted to a long is 7133 for my build number.  Anyone know how to fix the error?

# Jill said on Sunday, April 1, 2007 4:09 PM

Above problem resolved.  But I have another question:  Has anyone used this technique to increment the version for a Web Service project?  I have it incrementing the assembly version, but the resulting DLL from the web services project remains at 1.0.0.0 after each and every build.

# Daniel Plomp said on Thursday, June 7, 2007 5:49 AM

Hi, thanks for the article!

One question: how can I show the version information of the compiled web project on a webpage, so that users can see if they work with the correct version?

Thanks,

Daniel

# gee_isnt_it_about_time said on Saturday, December 8, 2007 10:15 PM

Isn't it time ~somebody~ came up with a decent way to control version numbers associated with the build process????????

I have run into several different companies all ~home-brewing~ their own version incrementing scheme... let's get it together people... how long have version numbers been around now???

# Mike said on Thursday, February 28, 2008 9:08 AM

Specify 1.0.* but not in the web deploy gui, only in the build file. It will cause automatisc incrementing of build and revision numbers.

# narva said on Wednesday, July 23, 2008 6:41 AM

I have Visual Studio 8. I am trying to use this technique with one of my projects. It does nothing except change BuildNumber and RevisionNumber in BuildNumbers.txt file.

Any idea what might be wrong ???

# glassware said on Monday, August 18, 2008 1:05 PM

Yeah, this is a bit of a bummer.  I've followed the steps in your task system, and the "BuildnUmbers.txt" file is created appropriately, but the AssemblyFileVersion and AssemblyVersion attributes are being set to 0.0.0.0.  I've looked around for something else that might be setting them to blank, but I can't find anything.

I'm using Visual Studio 2008 and building using the "Build" menu.  Any ideas?

# marco said on Wednesday, October 8, 2008 10:13 AM

pretty nice, however is really disappointing that such a basic feature (present in the oooold VB6 IDE) is so complicated to obtain to need _code_.

disappointing, as many other aspects of VS2008.

# Andrew DeVries said on Saturday, December 6, 2008 11:09 AM

If you remove the AssemblyFileVersion Line from the AssemblyInfo file the Build Porcess does the work for you.

<Assembly: AssemblyVersion("1.0.*")>

' This line is commented out in VB <Assembly: AssemblyFileVersion("1.0.0.0")>

No the AssemblyVersion and File Version Will match and Assembly Verion will auto Increment for you.

Andy

# Saha said on Wednesday, May 6, 2009 5:54 AM

Folks / Daniel,

I have same question as Daniel :-: how can I show the version information of the compiled web project on a webpage, so that users can see if they work with the correct version?

Thanks in Advance,

~Saha

# blacxcom said on Thursday, January 7, 2010 11:14 AM

Nice info about assembly

# sam said on Tuesday, February 9, 2010 2:18 PM

It is only changing the build numbers in the BuildNumber.txt file. and not updating the AssemblyInfo.CS file. why?

# testdox said on Sunday, February 28, 2010 3:44 PM

You can use the VersionUpdater tool from testdox.com to perform simple version increments.

# kikus said on Sunday, June 13, 2010 9:39 AM

отлично сделано, интеретсно читать 98)

# astprofi said on Wednesday, August 4, 2010 9:17 AM

Ведущее направление деятельности нашей компании - это вывоз мусора на территории Москвы и Московской области. Мы работаем только при использовании современной техники из собственного автопарка и всеми видами отходов: строительного мусора, ТБО и других видов для нас не составляет проблемы. Заказать уборку мусора Вы можете в том числе и на ночное время, и на выходные дни.

Мы проводим политику комплексного подхода: в большинстве случаев для клиента важна общая задача поддержания чистоты подхозяйственной ему территории. Это означает, что в рамках этого подхода мы проводим не только работы по сбору ТБО, загрузке и его утилизации. Компания “Экопарк” имеет возможность реализовывать весь комплекс работ по механизированной уборке территории. Начиная с продажи контейнеров или сдачи бункеров / контейнеров в аренду, завершая уборкой строительного мусора со стройплощадок. Услуга вывоза мусора в таких случаях выступает лишь как часть общего проекта чистоты и уборки территории.

Мы осуществляем комплекс работ по механизированной уборке территории и вывозу мусора. Вывозить мусор мы будем как и с помощью 8-ми кубовых контейнеров, так и с помощью 26-кубовых. Мехуборка является достаточно сложным комплексом работ по сбору, погрузке и утилизации отходов, для которой требуются квалифицированные специалисты. Мы предоставим Вам решение по вывозу мусора в Москве - под ключ.

# awning crank handle said on Monday, April 18, 2011 2:45 AM

mrfwbp crank caaws

# Ute Joyne said on Friday, July 8, 2011 7:25 PM

Hey, cool world-wide-web web page. I seriously came upon this on Bing, and i'm stoked I did. I'm likely to definately be revisiting ideal appropriate here an excellent deal much more frequently. Wish I could add on the write-up and bring just a little bit substantially far a lot more for the table, but I'am just absorbing as substantially info as I can in the second.