A colleague, Richard Banks, has previously blogged on this topic (http://www.richard-banks.org/2010/07/how-to-versioning-builds-with-tfs-2010.html), using custom activities and modifying the build workflow.
However, I also like the approach taken by John Robbins (http://www.wintellect.com/CS/blogs/jrobbins/archive/2010/06/15/9994.aspx).
John's approach is done entirely within the build file, using some of the new features of MSBuild 4.0, and therefore has no dependencies except what is already on a TFS build server.
Based heavily on John's work, I've created my own build targets that are based on the same core features but tailored to the way I like to work.
The build number is still based on the TFS build number, however rather than a base year of 2001 I pass it in as a property. This avoids the problem that a build on 31 Dec 2012 could be number 1.0.51231.1, whereas the one on 1 Jan 2013 would be 1.0.101.1. By setting the base year to the year your project starts you ensure your build numbers start low and increase.
(If you start reaching the end, after five years or so, you can always reset the base year after changing the minor version).
I also have an option to read the major and minor versions from the existing AssemblyInfo.cs file, rather than having them set in the build script, which I find a useful way to allow me to change the version number.
Like John's script, I only update AssemblyFileVersion, which can help in a multiple-project situation where there are version dependencies on strong names (such as in config files).
However, rather than a central shared version info file, I write the updated version number back into the projects AssemblyInfo.cs file.
The benefit of John's original approach is that you can have a separate project dependency that updates a central file for all builds, whereas with my approach you need to update each project's build (.csproj) file. On the other hand the negative with the original approach is that you need to change the structure of projects to point to the shared file, whereas I keep them self-contained with the original AssemblyInfo.cs file (similar to Richard's approach).
I only made changes to the C# project type, which is where I do most of my work, and so don't support all the project types that John's script does (VB.NET, C++, etc).
The only other output I have implemented is writing straight into a text file, which I find useful to copy to the output directory as a easy way to reference the build. This is particularly useful for web projects, where they are a directory full of .aspx files (and you can also hit the Version.txt file from a browser).
To use the script, include the TFSBuildNumber.targets file somewhere in your solution or project, then copy the example lines from Example.csproj to the .csproj files for projects you want to version.
To do this from within Visual Studio, first you need to right click and unload the project, then right click and open the file for editing. After pasting in the code, save and close the file, then reload the project.
The end of your .csproj file should look something like this (alter the path depending on where you placed the TFSBuildNumber.targets file):
... <!-- To modify your build process, add your task inside one of the targets below and uncomment it. Other similar extension points exist, see Microsoft.Common.targets. --> <PropertyGroup> <TFSBaseBuildYear>2010</TFSBaseBuildYear> </PropertyGroup> <Import Project="..\TFSBuildNumber.targets" /> <Target Name="BeforeBuild" DependsOnTargets="GetMajorMinorFromCSharpAssemblyInfoFile;WriteProjectTextAssemblyVersionFile;UpdateCSharpAssemblyInfoFile"> </Target> </Project>
One benefit of having the version numbers line up with the TFS build numbers is that given a particular DLL you can check the version number and then easily translate to the particular build it came from, e.g. (with a base year of 2010) the version number 1.0.924.5 comes from TFS build number 20100924.5.
If you want to use this in your project, download the TFSBuildNumber.targets file from the Essential Diagnostics project on CodePlex.
Download link seems to be broken, please fix looks like a great solution
Changed to point to the Essential Diagnostics project, which has a copy of the TFSBuildNumber.targets file.
We have used this process for multiple web apps. I am now trying to use it on a WPF client app. Compiles fine and updates the AssemblyInfo.cs file great. When I try to run the application I get an error that indicates something about an input parameter being int he incorrect format. I have tied it to the UpdateCSharpAssemblyInfoFile line in the csproj file. If I remove it the application runs but the assemblyinfo.cs file does not get updated. Any help?
Thanks, Doug
Following this method, my TFS Build throws the following error:
The expression “”21108″.SubString(4, 2)” cannot be evaluated. Index and length must refer to a location within the string. Parameter name: length
It seems that for some reason, it is not pulling the full year in the date (notice it omits the first 3 digits). I’m not sure what I need to change so that it does get the full date so that the substrings are correct.
Any ideas?
The code assumes the build number coming from TFS uses the standard pattern, which has the full year. If you have customised your build settings this may not be so, in which case you will have to update the build numbering code for the actual build number pattern you use, e.g. change the substring function.
(This would be specific to your build, based on the pattern you are using for build numbering; the generic configuration should match the default numbering used by TFS.)