Writing MSBuild Custom Task
March 13, 2008 on 8:16 pm | In MSBuild, Continuous Integration, DotNet |Scenario: we have Subversion server to manage our source code and a build server (CruiseControl.NET) to manage our deployment. We have decided to automatically set the SVN revision number to our assembly version.
[assembly: System.Reflection.AssemblyVersion(“1.2.3.0″)] [assembly: System.Reflection.AssemblyFileVersion(“1.2.3.0″)]
So we will replace the last 0 with the current Subversion revision number. How to do this? One way to achieve this is to modify the AssemblyInfo.cs and read the modified number from that exists. The file modification is easy with MSBuild Community Task FileUpdate
<FileUpdate Files=“Cic.P001001PropertiesAssemblyInfo.cs” Regex=“(d+).(d+).(d+).(d+)” ReplacementText=“$1.$2.$3.$(RevisionNumber)” />
But how to read it back? Well there is no easy way. I have written a custom MSBuidl task to achieve this like this:
<AssemblyInfoReader Path=“PropertiesAssemblyInfo.cs” Property=“AssemblyVersion“> <Output TaskParameter=“Value” ItemName=“ApplicationVersion” /> </AssemblyInfoReader>
Writing a custom MSBuild task is fairly easy. You have to Reference Microsoft.Build.Framework and Microsoft.Build.Utilities and implement Microsoft.Build.Framework.ITask. Just like this:
namespace Cic.MsBuildTasks { public class AssemblyInfoReader : Microsoft.Build.Framework.ITask { #region Private Varaibels private string path; private string property; private string value; #endregion #region Fields [Microsoft.Build.Framework.Required] public string Path { get { return path; } set { path = value; } } [Microsoft.Build.Framework.Required] public string Property { get { return property; } set { property = value; } } [Microsoft.Build.Framework.Output] public string Value { get { return this.value; } set { this.value = value; } } #endregion #region ITask Members private Microsoft.Build.Framework.IBuildEngine engine; Microsoft.Build.Framework.IBuildEngine Microsoft.Build.Framework.ITask.BuildEngine { get { return engine; } set { engine = value; } } bool Microsoft.Build.Framework.ITask.Execute() { string message; value = string.Empty; try { value = MyReadAssemblyInfoProperty(); message = string.Format( “AssemblyInfo property {0} read. Property value {1}”, property, value); } catch (System.Exception e) { message = string.Format( “Error reading AssemblyInfo property {0}. Error: {1}”, property, e.Message); } Microsoft.Build.Framework.BuildMessageEventArgs args = new Microsoft.Build.Framework.BuildMessageEventArgs( message, string.Empty, “AssemblyInfoReaderTask”, Microsoft.Build.Framework.MessageImportance.Normal); engine.LogMessageEvent(args); return true; } private Microsoft.Build.Framework.ITaskHost host; Microsoft.Build.Framework.ITaskHost Microsoft.Build.Framework.ITask.HostObject { get { return host; } set { host = value; } } #endregion #region Internals private string MyReadAssemblyInfoProperty() { string propertyValue; // Eraly return if (!System.IO.File.Exists(path)) return “”; foreach (string line in System.IO.File.ReadAllLines(path)) { if (line.Contains(property)) { try { propertyValue = line.Remove(0, line.IndexOf(‘”‘) + 1); propertyValue = propertyValue.Remove( propertyValue.LastIndexOf(‘”‘), propertyValue.Length - propertyValue.LastIndexOf(‘”‘)); // return matching property value return propertyValue; } catch { // Ignore errors } } } return string.Empty; } #endregion } }
9 Comments »
RSS feed for comments on this post. TrackBack URI
Leave a comment
Powered by WordPress with Pool theme design by Borja Fernandez.
Entries and comments feeds.
Valid XHTML and CSS. ^Top^



Two questions:
1. What is the significance of LogMessageEvent? Is that the way to return data to MSBuild? A little explanation (or link) might help there.
2. Is it necessary to declare the “host” property? Maybe I’m going blind, but I can’t see where it is used. ;-)
Comment by David White — April 14, 2008 #
Heya mate, think you need to escape the periods in your regex, otherwise they’ll match anything:
(d+)\.(d+)\.(d+)\.(d+)
Comment by Nick — July 29, 2008 #
Your blog is interesting!
Keep up the good work!
Comment by AlexM — August 16, 2008 #
[…] Writing MSBuild Custom Task […]
Pingback by Automate adding/using of the SVN revision to/as the version of a .net project, through a build step « theKindOfMe — April 27, 2009 #
[…] Writing MSBuild Custom Task […]
Pingback by Automate adding/using of the SVN revision to/as the version of a .net project, through a build target « theKindOfMe — April 27, 2009 #
Hmm. And what happens when your SVN revision number for your repository hits 65535?
Well, your AssemblyInfo won’t compile anymore, that’s what.
Comment by Wim Hollebrandse — September 29, 2009 #
@Wim Hollebrandse: Good point. Thanks! Learned this the hard way? ;-)
Comment by Marcin Kawalerowicz — October 3, 2009 #
Indeed Marcin,
Best thing is to use the AssemblyInformationalVersion attribute, as it doesn’t restrict you to 4 shorts (UInt16).
At the same time, when you stamp your assembly with it, it also allows you to retrieve the AssemblyInformationalVersion attribute using reflection.
An other common alternative is to use div and mod 10,000 respectively for the 3rd and 4th part of the version number.
Comment by Wim Hollebrandse — December 1, 2009 #
[…] Marcin Kawalerowicz’s Writing MSBuild Custom Task […]
Pingback by Rudamentary WiX Orphan Prevention | James Reuben Knowles — January 21, 2010 #