UPDATE: Please see MSBuild Extension Pack
24 July 2008 UPDATE: Part 2 is available.
I’ve lost count of the number of blog posts, forum questions and internal emails that I have seen regarding problems with versioning assemblies in TFS. I’ve made various responses to them and in many said I would put together some thoughts to clarify my thinking on the matter… I’ve finally got round to doing it…
I believe the root cause of the problem is that many users have tried to migrate their existing versioning logic and code to work with TFS, blinded by the wealth of functionality that TFS has brought to the development process.
The truth is that TFS provides the basic fundamentals of iterative versioning out the box.
<major version>.<minor version>.<build number>.<revision>
My first piece of general advice is that you don’t keep the two in sync. The assembly version should rather be of the format:
<major version>.<minor version>.0.0
The main reason for this is that strong name signatures include the assembly version, which means that if you release an updated assembly, the code that references it will need to be updated too (unless you go the policy route, but let’s keep things simple).
The AssemblyFileVersion is the attribute that should be updated with every build you perform, and therein lays the question. How?
I would estimate that in 99% of builds, the format of the build number will be
<Fixed major version>.<Fixed minor version>.< Calculated build number>.<Iterative revision>
The Fixed parts are easy and they will most likely equate to those used in the AssemblyVersion. The calculated part is really up to you. It could be a date format, the number of days since a give milestone etc. The iterative part is where the trouble lies.
Let’s look at some tools for versioning. In the past, before TFS, you may have used the SDC Tasks for versioning, or your own bespoke solution. With the first release of TFS, a popular solution was, and still is, the AssemblyInfo Task. All these solutions need to provide the same essential logic.
They need to calculate your build number and provide a unique incremental value for every build. To ensure that the incremental value is unique, most versioning solutions check out a file, alter it and check the file back in.
This is the operation that causes the most problems. When I first used the AssemblyInfo task, I encountered numerous painful ‘cannot determine the workspace’ errors. I could easily resolve those issues today, but with little experience and understanding of TFS back then, I and many others felt the pain. If that error didn’t catch you, then you were likely to be caught by checkin policies or duplicate attributes. All issues can be resolved, but too many people have had a hard time with this task.
So, if you managed to get around the AssemblyInfo task issues, or your own problems with creating a task that checked out a file, incremented the value and then checked it in again, well done… and welcome to contiguous merging issues! TFS 2005 / 2008 don’t support multiple pending merge entries for a single file, so you have to perform contiguous merging. This isn’t a big issue with versioning tasks, however the problem with continuously checking out and in the versioning files(s) is that you get loads of noise on the pending merges table and if you do merge multiple version files, you will likely have to manually resolve the final version.
Many people have questioned why Microsoft didn’t release a basic versioning task with TFS, and in fact it appears that the next version of TFS may include such functionality. But why wait? The functionality is already there, it just needs to be used.
Don’t do it
My second piece of general advice is don’t check your versioning files in and out during the build process. As discussed above, it creates noise and hassle with merges. As long as you’re versioning logic can be related to the TFS Build Number, I see no benefit in having this source control operation take place. Stop the obsession!
TFS provides a build number for every build it initiates. The format is
To accomplish versioning, use the <revision> part of the build number. TFS will provide an incremented revision for every build, whether the previous build succeeded or not. All you need to do is provide a task that takes the build number as input, then emits your version number as output. The logic within the task is up to you.
The lightweight TFS Versioning Tasks provide functionality to easily create builds with a calculated build number in a date or elapsed format.
Because your build is based on the build number, you can easily locate the label associated with the build.
- Don’t increment the AssemblyVersion
- Do increment the AssemblyFileVersion
- Don’t perform source control operations for versioning
- Do base your build number iteration on the TFS Build Number
- Do base your build number calculation on the TFS Build Number
I hope that clarifies things.