Writing MSBuild Custom Task

March 13, 2008 on 8:16 pm | In MSBuild, Continuous Integration, DotNet | 3 Comments

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.csRegex=(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.csProperty=AssemblyVersion>
    <Output TaskParameter=ValueItemName=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
    }
}


Continuous Integration article in SDJ

September 3, 2007 on 7:24 pm | In MSBuild, Continuous Integration, DotNet | No Comments

I would like to invite all my polish speaking readers to online version of my article about continuous integration in .NET. It was published in July 2007 in Software Developers Journal. You can find it in the download section of SDJ web page. You can read there about:

  • setting up CI server with CruiseControl.NET
  • creating one click build script with MSBuild
  • incorporating unit testing and code coverage into CI process
  • using WebDashboard and CCTray to control the process
  • introducing code analyze with FxCop
  • creating install file and deploying the project.

Powered by WordPress with Pool theme design by Borja Fernandez.
Entries and comments feeds. Valid XHTML and CSS. ^Top^