Writing MSBuild Custom Task

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

Leave a Reply

Your email address will not be published. Required fields are marked *