This project is read-only.


By default, Team Build does not do anything to modify your Assembly versions. To create a build that has build numbers the same as the assembly versions then some customization is required to your TFSBuild.proj file.


The method involves two basic steps:-
  • Customize the build number to be based on the desired assembly version.
  • Modify the AssemblyInfo files of your project to include the version.

For more details, see

In TFS2008 there is a convenient target for you to override to generate your custom build numbers called "BuildNumberOverrideTarget". The important thing is that each build number must be unique, therefore a good rule of thumb is to use something like BuildDefinitionName_1.0.0.1234. Inside the BuildNumberOverrideTarget you simply set "BuildNumber" property to be what you want. Here is an example:-

<Target Name="BuildNumberOverrideTarget"> 
  <!-- Create a custom build number, matching the assembly version -->      
  <Message Text="Loading last build number from file &quot;$(DropLocation)\buildnumber.txt&quot;" /> 

  <IncrementingNumber NumberFile="$(DropLocation)\buildnumber.txt"> 
    <Output TaskParameter="NextNumber" PropertyName="VersionBuild" /> 


  <Message Text="Build number set to &quot;$(BuildNumber)&quot;" />  

The first thing we do is call a quick custom task that increments the build number stored in the passed file. This is done while keeping a lock on the file itself in case two builds tried to update the same file at the same time. We then take this new number and build the BuildNumber based upon that value. The code for the Incrementing Number task is very simple and is given below:-

using System;
using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

namespace Teamprise.Tasks
    /// <summary>
    ///   A simple task to increment the number stored in a passed file.
    /// </summary>
    public class IncrementingNumber : Task

        public override bool Execute()
            NextNumber = IncrementNumber();
            return true;

        public int IncrementNumber()
            using (FileStream fs = new FileStream(NumberFile, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
                StreamReader reader = new StreamReader(fs);

                long pos = 0;
                String line = reader.ReadLine();
                // ignore comments at start of file.
                while (line != null && line.StartsWith("#"))
                    pos = pos + line.Length + System.Environment.NewLine.Length;
                    line = reader.ReadLine();

                int number = -1;
                if (line != null)
                    number = Int32.Parse(line);
                NextNumber = number + 1;

                // Rewind the file stream back to the beginning of the number part.
                fs.Position = pos;

                StreamWriter writer = new StreamWriter(fs);
            return NextNumber;

        public string NumberFile { get; set; }

        public int NextNumber


You compile this code into an assembly of your choice that lives alongside the TFSBuild.proj file in the build configuration folder in source control and is this loaded using the UsingTask call at the begging of your MSBuild project, i.e.

<UsingTask TaskName="Teamprise.Tasks.IncrementingNumber" 
           AssemblyFile="Teamprise.Tasks.dll" />

The next thing that we have to do is to take the new version and force this into the assemblyinfo files. Many people prefer the AssemblyInfo files stored in source control to have a certain well defined number for each release branch (i.e., and make it the build server that adds the build server versions into them. Other people like to check these back into source control - if you do that, be sure to check them in with the special comment of "NO_CI" to ensure that the check-in does not trigger any CI builds potentially putting you into an infinite loop of building.

So, we modify our assembly version files after they have been downloaded from source control using a technique borrowed from Richard Banks, our interpretation of this is given below:-

  <AssemblyInfoFiles Include="$(SolutionRoot)\**\assemblyinfo.cs" /> 
<Target Name="AfterGet"> 
  <!-- Update all the assembly info files with generated version info --> 
  <Message Text="Modifying AssemblyInfo files under &quot;$(SolutionRoot)&quot;." /> 
  <Attrib Files="@(AssemblyInfoFiles)" Normal="true" /> 
  <FileUpdate Files="@(AssemblyInfoFiles)"                                 
              ReplacementText="AssemblyVersion(&quot;$(VersionMajor).$(VersionMinor).$(VersionService).$(VersionBuild)&quot;)]" /> 
  <FileUpdate Files="@(AssemblyInfoFiles)" 
              ReplacementText="AssemblyFileVersion(&quot;$(VersionMajor).$(VersionMinor).$(VersionService).$(VersionBuild)&quot;)]" /> 
  <Message Text="AssemblyInfo files updated to version &quot;$(VersionMajor).$(VersionMinor).$(VersionService).$(VersionBuild)&quot;" /> 

As you can see, we are making use of the custom Attrib task that is provided by the essential MSBuild Community Tasks to set the files to read/write and then we are calling the MSBuild Community Task FileUpdate to do a couple of regular expression search replaces on the appropriate parts of the files.

And that's about all that needs to be done. Now our builds have nice incrementing numbers that have the version number included that is the same as the assembly info files.


Last edited Nov 2, 2010 at 7:02 PM by wbarthol, version 2


No comments yet.