So I’ve been trying to do some custom build numbering for my Team Build project and was confronted with a lot of let’s say “uninformation” and some great information also. I really didn’t want to create a custom task that I would need to maintain so I set on a long search for the Truth… needless to say I didn’t find it. But here is the information I found that should get you you to the right place.
There is a great custom task created by the MSBuild team that used to be on GotDotNet but is now MSDN Code Gallery. It’s called the AssemblyInfo Task there is currently a bug with that custom task that breaks is starting in 2007 and it has to do with the fact the build parts a.b.c.d can’t be bigger than 65k. You can find a great explaination here and here of the problem and solution. Once I worked that out I proceeded to Team Build.
There is a great reference post by Gautam Goenka (MSFT) about how to implement this custom task in Team Build I did exactly with he suggests in Using AssemblyInfo task with Team Build.
Now I was pondering, “Will this work and set the same number for ALL solution I’m building in my TFSBuild.proj file? Now that would be neat”. I first started with one solution and all worked great. I then put two solution and bam! exactly what I wanted, same number everywhere for each csproj or vbproj project compiled in all the solutions.
Note: If you have a mix of VB.NET and C# you might need to add stuff in the AfterGet to make sure you check-out all the AssemblyInfo.vb and AssemblyInfo.cs but that shouldn’t be too hard to do.
So all was good… well almost, Team Build creates is own build numbering something like BuildDefinition_yearmonthday.buildrun which is very nice but doesn’t match my shiny new a.b.c.d so what should I do to make them match, off to Internet again to find information about how to override the build name in Team Build. There is just such an override that we need to use it’s call BuildNumberOverrideTarget problem is I’m stuck in a catch 22 the task occurs before the build number gets created by the AssemblyInfo task so essentially I couldn’t use this. Off to the Internet again… Lots of people complain and try to solve this by changing the order of the overrides, calling some AssemblyInfo task twice once before to get a number and once after…yuck in the end it was not very nice. I needed a way to change the build number in Team Build but after the compile worked. So in the target AfterCompile that is used in Gautam’s post I added two things
<Target Name="AfterCompile" Condition="'$(IsDesktopBuild)'!='true'">
<Exec WorkingDirectory="$(SolutionRoot)"
Command="$(TF) checkin /comment:"Auto-Build: Version Update" /noprompt /override:"Auto-Build: Version Update" /recursive $(AssemblyInfoSpec)"/>
<RemoveDir Directories ="$(DropLocation)\$(BuildNumber)"/>
<CreateProperty Value ="$(BuildDefinitionName)_$(MaxAssemblyVersion)">
<Output TaskParameter ="Value" PropertyName ="BuildNumber"/>
<UpdateBuildNumberDropLocation
TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
BuildNumber="$(BuildNumber)"
DropLocation="$(DropLocation)\$(BuildNumber)" />
First thing we need to do is remove the original build drop location using the RemoveDir task.
Next, the CreateProperty create a new BuildNumber based on the number that was used in the AssemblyInfo Task so that’s exactly what we need now instead of BuildDefinition_yearmonthday.buildrun we have BuildDefinition_a.b.c.d.
Finally, the kicker I was searching in the Microsoft.TeamFoundation.Build.targets file in C:\Program Files\MSBuild\Microsoft\VisualStudio\TeamBuild directory and in there I found what I needed amongst all sort of very cool information about the tasks that are called by TeamBuild but my nugget was in the InitializeEndToEndIteration Target, which is called after the BuildNumberOverrideTarget. I noticed this nifty task called UpdateBuildNumberDropLocation with this comment on top
<!-- Update the build number and drop location in the database. This task will also create the drop directory
and grant the service account full rights on it.
Ah ah! I said to myself, this is exactly what I need. An what do you know, I updated my AfterCompile Target like above and the sweetness of victories came to me. Now the build number in the build report match and all the assembly compiled by my build definition have the same number. While the build is running at the beginning you will have the wrong number for a while (the old one) but at the end you should be good to go. The nice thing about that is it’s calling the same task the Team Build is calling so all the reports and links etc all work with the new number.
I hope this helps you guys I know I spend a lot of time trying to figure an elegant way to do this.
ET
Update: William Bartholomew one of my new fellow MVP sent me an email with this advice, add $(NoCICheckInComment) to the TF command (the first Exec) so that this particular check-in (of assemblyinfo change) won’t trigger and infinite loop of builds if you have a CI build on your build server. Also he add another suggestion on how to resolve the problem but it involves creating you own build task to update the number (which I didn’t want to do)
here what he suggests
1. Call the Version task from MSBuild Community Tasks (http://msbuildtasks.tigris.org) in BuildNumberOverrideTarget to allocate a new version number.
2. In the AfterGet (or BeforeCompile) extensibility tasks update the AssemblyInfo files using the $(BuildNumber). I actually wrote my own custom task to modify the AssemblyInfo files but if the AssemblyInfo Task allows you to pass in the version number you could use that too.
Finally I notice my other good friend Martin also has a nice blog post of this Aligning Build Numbers with Assembly Versions in TFS2008