Automatically increment the assemblys version
First of all let me say that credit goes to Christian Binder whose blog article was the reason why I got things working in the first place. Thank Chris! Since his howto is in german, I will translate most of it here. For reference, please visit his blog!
So this is what this article is about: Imagine you were doing nightly builds of your software. Sure enough you wouldn't want to touch every AssemblyInfo.cs or .vb every day only for manually incrementing the version number. The Assembly version number basically looks like this:
MajorVersion.MinorVersion.BuildNumber.Revision.
What you want to achieve is that you can see the Assembly version in the file properties in explorer so that you know which release build it refers to.
To know which release build the Assembly refers to, you need the buildnumber Team Build assigned to the release. The naming convention looks like this: Buildname + Date + Revision. So if you built your FooBar application today already the third time, its buildnumber would be FooBar_20090128.3. Easy, huh?
While FooBar's sources are labled with that buildnumber, the Assembly properties are not altered.
The settings you would want Team Build to alter are these:
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
Before I go into details about what Chris did to get things working, let me add the following:
My project consists of several solutions. I have libraries for example that are shared amongst several projects. This is why I only wanted to have build definitions that contained the main project and the referenced libraries. However, for automatically checking out and back in the AssemblyInfo files while editing them at build time, Chris uses the /recursive argument to recursively go through all subdirectories and touch all AssemblyInfo files. When having more than one workspace in your build definition that explicitly copy only specific projects to Team Builds' local workspace, that does not work.
However, he also shows how to create a list of all AssemblyInfo files within the folder structure. And I changed his TFS tasks so that instead of the recursive argument the list generated earlier is used. That works like a charm!
And this is how you extend the build process to let Team Build automatically set the version information:
1. Download and install AssemblyInfoTask
You need to get this custom build task. Chris references the original source at gotdotnet.com which does not exist anymore. Either search for the text AssemblyInfoTaskvers with google or try this link. It should redirect you to the download area on MSDN where you can get the lates setup release.
2. Import the task into your build project
Open Source Control in Visual Studio and make sure you have a local copy of the build project in your workspace. Then check out the TFSBuild.proj file (Unchanged, keep any existing lock) and open it. Add the following line right after the import of Microsoft.TeamFoundation.Build.targets:
<Import Project="$(MSBuildExtensionsPath)¥Microsoft¥AssemblyInfoTask¥Microsoft.VersionNumber.Targets"/>
3. Use a custom task for retrieving the revision number
Each buildnumber contains a revision: FooBar_20090128.3 What we want to do is using this revision number also as revision in our Assembly version. So we need to implement a custom build task that is able to extract the revision number from the buildnumber so that we can assign it to the Assemblys' revision number. At the end of this article, I will post the sources you will need.
Right after the import statement from above, register this custom build task by adding the following line:
<UsingTask TaskName="ExtractRevision.ExtractRevisionTask" AssemblyFile="ExtractRevision.dll"/>
4. Add a property group for setting the assembly information
To configure all assembly properties and format the number formats respectively, add the following lines to your project file:
<PropertyGroup>
<!– Assembly version properties. Add others here –>
<AssemblyMajorVersion>4</AssemblyMajorVersion>
<AssemblyMinorVersion>0</AssemblyMinorVersion>
<AssemblyBuildNumber>0</AssemblyBuildNumber>
<AssemblyRevision>0</AssemblyRevision>
<AssemblyFileMajorVersion>4</AssemblyFileMajorVersion>
<AssemblyFileMinorVersion>0</AssemblyFileMinorVersion>
<AssemblyFileBuildNumber>0</AssemblyFileBuildNumber>
<AssemblyFileRevision>0</AssemblyFileRevision>
<AssemblyBuildNumberType>DateString</AssemblyBuildNumberType>
<AssemblyBuildNumberFormat>MMdd</AssemblyBuildNumberFormat>
<AssemblyRevisionType>NoIncrement</AssemblyRevisionType>
<AssemblyRevisionFormat>0</AssemblyRevisionFormat>
<AssemblyFileBuildNumberType>DateString</AssemblyFileBuildNumberType>
<AssemblyFileBuildNumberFormat>MMdd</AssemblyFileBuildNumberFormat>
<AssemblyFileRevisionType>NoIncrement</AssemblyFileRevisionType>
<AssemblyFileRevisionFormat>0</AssemblyFileRevisionFormat>
<!– Dump the TFS BuildNumber to the Assembly Comment Prop –>
<AssemblyDescription>$(BuildNumber)</AssemblyDescription>
<!– TF.exe –>
<TF>"$(TeamBuildRefPath)¥..¥tf.exe"</TF>
<!– AssemblyInfo file –>
<AssemblyInfoSpec>AssemblyInfo.*</AssemblyInfoSpec>
</PropertyGroup>
5. Set the revision number
By altering the target CheckSettingsForEndToEndIteration, you can use the custom build task you have yet to implement for extracting the revision number from the buildnumber and add it to the assembly version:
<!– Use the ExtractRevionTask to get the RevionNumber out of the BuildNumber.
Update the AssemblyInfoTask Properties with the result BuildRevision. –>
<Target Name="CheckSettingsForEndToEndIteration">
<!–for 2008 use the GetBuildProperties Task to get the BuildNumber –>
<GetBuildProperties TeamFoundationServerUrl="$(TeamFoundationServerUrl)" BuildUri="$(BuildUri)">
<Output TaskParameter="BuildNumber" PropertyName="BuildNumber"></Output>
</GetBuildProperties>
<!– extract the BuildRevision –>
<ExtractRevisionTask BuildNumber="$(BuildNumber)">
<Output TaskParameter="BuildRevision" PropertyName="BuildRevision" />
</ExtractRevisionTask>
<!– update the AssemblyInfo Props –>
<CreateProperty Value="$(BuildRevision)">
<Output TaskParameter="Value" PropertyName="AssemblyRevision"/>
</CreateProperty>
<CreateProperty Value="$(BuildRevision)">
<Output TaskParameter="Value" PropertyName="AssemblyFileRevision"/>
</CreateProperty>
<!– just needed if you populate the AssemblyDescription too like in the sample –>
<CreateProperty Value="$(BuildNumber)">
<Output TaskParameter="Value" PropertyName="AssemblyDescription"/>
</CreateProperty>
</Target>
6. Check out and back in all AssemblyInfo.[cs|vb] files from your workspaces
Before you can alter the AssemblyInfo files, you need to let Team Build check them out:
<!– Use TF to check-out/In the assemblyinfo files to persist updated Version Info –>
<Target Name="AfterGet" Condition="'$(IsDesktopBuild)'!='true'">
<!– Set the AssemblyInfoFiles items dynamically –>
<CreateItem Include="$(SolutionRoot)¥**¥$(AssemblyInfoSpec)">
<Output ItemName="AssemblyInfoFiles" TaskParameter="Include" />
</CreateItem>
<Message Text="These AssemblyInfo.* files were found:"/>
<Message Text ="@(AssemblyInfoFiles)"/>
<Exec WorkingDirectory="$(SolutionRoot)"
Command="$(TF) checkout "@(AssemblyInfoFiles, '" "')""/>
</Target>
Right after the altered files have been compiled, you can check them back in:
<Target Name="AfterCompile" Condition="'$(IsDesktopBuild)'!='true'">
<Exec WorkingDirectory="$(SolutionRoot)"
Command="$(TF) checkin /comment:"Auto-Build: Version Update" /noprompt /override:"Auto-Build: Version Update" "@(AssemblyInfoFiles, '" "')""/>
</Target>
Just in case anything bad happens, there is a way of undoing the changes:
<!– 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 "@(AssemblyInfoFiles, '" "')""/>
</Target>
7. Implement the ExtractRevision custom build task
Simply create a new C# library project with the following class (and add a strong name to it by signing it in the project properties):
namespace ExtractRevision
{
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
public class ExtractRevisionTask : Task
{
public override bool Execute()
{
int indexOfDot = buildNumber.LastIndexOf(".");
if (indexOfDot != -1 & indexOfDot != buildNumber.Length – 1)
{
buildRevision = buildNumber.Substring(indexOfDot + 1, buildNumber.Length – (indexOfDot + 1));
}
else
{
//there is no char following the dot or we can't find a dot
buildRevision = "0″;
}
return true;
}
private string buildRevision;
private string buildNumber;
public string BuildNumber
{
set { buildNumber = value; }
}
[Output]
public string BuildRevision
{
get { return buildRevision; }
}
}
}
You need to check in the .dll file directly into your TeamBuildTypes folder on TFS.