This approach is for those who have the following requirements:
- Use a Primary.Secondary.Maintenance.Build (P.S.M.B) version number format
- Want the build name to include the version number
- Want the assemblies "stamped" with version number
- Don't want to checkin the increments to AssemblyInfo.cs files for every build
- Don't want to modify the .csproj file for their projects
To illustrate:
- Our build definition name format is: [product abbreviation]-[p].[s]{.[m]}-[type]. The type indicates a Dev, Test, Rel, etc... build.
- An example build definition name is: KApp-3.2-Dev.
- Our build number format is:$(BuildDefinitionName)$(Rev:.rrr)
- From this we get build names like: Kapp-3.2-Test.012
The approach is as follows:
- Parse the P.S, and if used, .M from the build definition name. Actually, I parse it from the build folder name because it seemed easier than getting the build definition name.
- Get the build number from TFS
- Assemble the version number
- Run over all the AssemblyInfo files and use RegX to replace the file and version number
The details are shown in the code below:
NOTE: for more information on how to implement custom activities in Team Build 2010, see http://www.ewaldhofman.nl/?tag=/build+2010+customization
<!-- code formatted by http://manoli.net/csharpformat/ -->
using System;
using System.Activities;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
using Microsoft.TeamFoundation.Build.Client;
namespace BuildTasks.Activities
{
[BuildActivity(HostEnvironmentOption.Agent)]
public sealed class UpdateAssemblyInfoFiles : CodeActivity
{
// The file mask of all files for which the build number of the
// assembly version must be increased
[RequiredArgument]
public InArgument<string> AssemblyInfoFileMask { get; set; }
// The SourcesDirectory as initialized in the build process template
[RequiredArgument]
public InArgument<string> SourcesDirectory { get; set; }
public InArgument<string> BuildNumber { get; set; }
public InArgument<string> BuildDirectory { get; set; }
public OutArgument<string> TextOut { get; set; } // debug code
protected override void Execute(CodeActivityContext context)
{
// Obtain the runtime value of the input arguments
string sourcesDirectory = context.GetValue(this.SourcesDirectory);
string assemblyInfoFileMask = context.GetValue(this.AssemblyInfoFileMask);
string buildNumber = context.GetValue(this.BuildNumber);
string buildDirectory = context.GetValue(this.BuildDirectory);
// Parse build definition name from build directory
int nameStart = (buildDirectory.LastIndexOf(@"\") + 1);
string buildDefinitionName = buildDirectory.Substring(nameStart);
// Parse the primary.secondary.maintenance version numbers from the build definition name
// The format of the build definition name is [product abbreviation].P.S{.M}-[Build Type]
// Find the start and end of the build definition version number
int verStart = (buildDefinitionName.IndexOf("-") + 1);
int verEnd = buildDefinitionName.IndexOf("-", verStart);
// Parse out the version number
string version = buildDefinitionName.Substring(verStart, (verEnd - verStart));
// Count the number of decimal points in the version number
MatchCollection charColl = Regex.Matches(version, @"[\.]+");
int numD = Convert.ToInt16(charColl.Count);
// Get the version numbers
string p = string.Empty;
string s = string.Empty;
string m = string.Empty;
int primaryVer = 0;
int secondaryVer = 0;
int maintenanceVer = 0;
int d1 = version.IndexOf(".");
p = version.Substring(0, d1);
if (numD == 1)
{
s = version.Substring((d1 + 1), (version.Length - (d1 + 1)));
}
else
if (numD == 2)
{
int d2 = version.IndexOf(".", (d1 + 1));
s = version.Substring((d1 + 1), (d2 - (d1 + 1)));
m = version.Substring((d2 + 1), (version.Length - (d2 + 1)));
}
else
{
throw new ArgumentException("ERROR - the number of decimal points should be only 1 or 2, not: " + numD);
}
// debug code: use build definition verbosity of diagnostic to view in build report
context.SetValue<string>(this.TextOut, "(debug) - build Number = "
+ buildNumber
+ ", buildDirectory = "
+ buildDirectory
+ ", buildDefinitionName = "
+ buildDefinitionName
+ ", version = "
+ version
+ ", p = "
+ p
+ ", s = "
+ s
+ ", m = "
+ m);
// Set values of the build definition version number
primaryVer = Convert.ToInt16(p);
secondaryVer = Convert.ToInt16(s);
// leave maintenanceVer == 0 if not used in buildDefinitionName
if (m != string.Empty)
{
maintenanceVer = Convert.ToInt16(m);
}
// Parse the build increment number from the actual build number
int buildIncrementNumberStart = (buildDefinitionName.Length + 1);
string buildIncrementValue = buildNumber.Substring(buildIncrementNumberStart, (buildNumber.Length - buildIncrementNumberStart));
//remove any leading zeros
while (buildIncrementValue.StartsWith("0"))
{
buildIncrementValue = buildIncrementValue.Remove(0, 1);
}
int buildIncrementNumber = Convert.ToInt16(buildIncrementValue);
// Enumerate over all version attributes
foreach (string attribute in new string[] { "AssemblyVersion", "AssemblyFileVersion" })
{
// Define the regular expression to find (which is for example 'AssemblyVersion("1.0.0.0")' )
Regex regex = new Regex(attribute + @"\(""\d+\.\d+\.\d+\.\d+""\)");
// Get all AssemblyInfo files
foreach (string file in Directory.EnumerateFiles(sourcesDirectory, assemblyInfoFileMask, SearchOption.AllDirectories))
{
string text = File.ReadAllText(file); // Read the text from the AssemblyInfo file
Match match = regex.Match(text); // Search for the first occurance of the version attribute
if (match.Success) // When found
{
// Update build increment number in the version and write out to AssemblyInfo files
Version newVersion = new Version(primaryVer, secondaryVer, maintenanceVer, buildIncrementNumber);
// Get file attributes
FileAttributes fileAttributes = File.GetAttributes(file);
// Set file to read only
File.SetAttributes(file, fileAttributes & ~FileAttributes.ReadOnly);
// Replace the version number
string newText = regex.Replace(text, attribute + "(\"" + newVersion.ToString() + "\")");
// Write the new text in the AssemblyInfo file
File.WriteAllText(file, newText);
// restore the file's original attributes
File.SetAttributes(file, fileAttributes);
}
}
}
}
}
}