One common requirement with any decent sized multi-version product is to automatically update the version numbers of the binaries on a regular basis. This is generally achieved by updating the AssemblyInfo.cs (or other language equivalent ) files.

 

There are a couple of ways to do this:

1.       Assign one developer to remember to increment the numbers on a daily basis. Yes, don’t get mad at me. I know this sucks. I just listed it here as an option.

2.       Use the nightly build (or equivalent tool) to do this automatically.

3.        

If you use Team Foundation Server to manage your daily (or nightly builds), you can use one custom task developed by the nice people at Microsoft. The task is aptly named AssemblyInfoTask. This task used to be hosted at www.gotdotnet.com but since it closed people often to go www.codeplex.com to search for it. Well, it now resides at code.msdn.com. The direct link to the project is http://code.msdn.microsoft.com/AssemblyInfoTaskvers.

Once you download it and follow the instructions and set it up, you will run into the next problem. You will see an error similar to this:

Error emitting 'System.Reflection.AssemblyVersionAttribute' attribute -- 'The version specified '1.0.08102.1' is invalid

 

This is an interesting problem. See, we have had version numbering since ages. During those early days, when disk space was a premium, a good developer never wasted space. So, they ended up using Int16 for the build number (the 3rd set of numbers). This caused a huge problem with the arrival of 2007.

The solution for this problem is described here: http://blogs.msdn.com/msbuild/archive/2007/01/03/fixing-invalid-version-number-problems-with-the-assemblyinfotask.aspx

 

Ok. Now you have got this build to happen on your dev box. How do you set this up on your build server?

 

Simple. Note: The project documentation says to add it to the *.csproj file (or *.vbproj file). DON’T DO THAT if you are going to use the build server for nightly builds. Let’s open your TFSBuild.proj file (again, if you used the defaults, it will be under your root dir of the project/TeamBuildTypes/Release (or Debug). Let’s check out this file and open it.

At the very top of the file (after comments and start), you will see a line like:

  <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\TeamBuild\Microsoft.TeamFoundation.Build.targets" />

 

ADD the following line after this:

  <Import Project="$(MSBuildExtensionsPath)\Microsoft\AssemblyInfoTask\Microsoft.VersionNumber.Targets"/>

 

But this is not all. If you run the build now, you will see errors of File not accessible. The reason, as you would have guessed, is that AssemblyInfo.* files would be under source control as read only and could not be saved. The solution now is to check out these files before the compile gets executed and check them back in after compile is done.

Here is the sample config on how to do this. Note that in my example below, both C# and VB AssemblyInfo files will get covered.

 

Step 1: In the <PropertyGroup> section, add the following:

  <PropertyGroup>

    <!-- TF.exe -->

    <TF>&quot;$(TeamBuildRefPath)\..\tf.exe&quot;</TF>

 

    <!-- AssemblyInfo file spec -->

    <AssemblyInfoSpec>AssemblyInfo.*</AssemblyInfoSpec>

  </PropertyGroup>

The first line defines an alias for tf.exe to checkout/checkin the files.

The second line defines a file pattern.

 

Step 2: We need to add <targets>.  You will need to add these lines towards the end of the project (after the <ItemGroup> sections.

 

  <Target Name="AfterGet" Condition="'$(IsDesktopBuild)'!='true'">

    <!-- Set the AssemblyInfoFiles items dynamically -->

    <CreateItem Include="$(SolutionRoot)\**\$(AssemblyInfoSpec)">

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

    </CreateItem>

 

    <Exec WorkingDirectory="$(SolutionRoot)"

          Command="$(TF) checkout /recursive $(AssemblyInfoSpec)"/>

  </Target>

 

Hopefully, it’s apparent what’s happening here.  The target tells the build system to execute this task after getting all the source code. The CreateItem creates an array of files that meet the file spec we defined earlier. The xec checks these out.

 

Now that we have added the checkout, let’s check the files in after AssemblyInfoTask has done the edits. Let’s add the following lines:

 

  <Target Name="AfterCompile" Condition="'$(IsDesktopBuild)'!='true'">

    <Exec WorkingDirectory="$(SolutionRoot)"

      Command="$(TF) checkin /comment:&quot;Auto-Build: Version Update&quot; /noprompt /override:&quot;Auto-Build: Version Update&quot; /recursive "/>

  </Target>

 

Good so far. What happens if you build broke. Ah-ha, you see where I am going.

  <!-- In case of Build failure, the AfterCompile target is not executed. Undo the changes -->

  <Target Name="BeforeOnBuildBreak" Condition="'$(IsDesktopBuild)'!='true'">

    <Exec WorkingDirectory="$(SolutionRoot)"

          Command="$(TF) undo /noprompt /recursive $(AssemblyInfoSpec)"/>

  </Target>

 

Now we have covered this properly. Let’s check this file in and fire a build. If you have enabled check-in mails, you should get a mail that will tell about AssemblyInfo files getting updated.

 

Happy building.