MSBuild Wiki

4/28/2005 11:23:10 PM - Neil
MSBuild Wiki

Recent Changes
Lost and Found
Find References
Highlight Changes

Versions

Versions:

Extend MS Build With A New Task

Extend MSBuild with a New Task

Tasks provide the code that runs during the build process and are contained in targets. A library of common tasks is provided with

4/28/2005 11:23:10 PM - Neil
MSBuild and you can also create your own tasks.

In this example, information is provided about the two approaches you can use when implementing a task, registering tasks, and raising an event from a task. The example project that is provided builds a .dll file that can be used as an

4/28/2005 11:23:10 PM - Neil
MSBuild task to log a comment.

Tasks

Tasks provide the code that runs during the build process and are contained in targets. Examples of tasks include Copy, which copies one or more files, MakeDir, which creates a directory, and Csc, which compiles Visual C# source code files. Each task is implemented as a .NET class that implements the ITask interface, which is defined in the Microsoft.Build.Framework.dll assembly.

There are three approaches you can use when implementing a task:

  • Derive your class directly from ITask and implement the methods on this interface.
  • Derive your class from the helper class, Task, which is defined in the Microsoft.Build.Utilities.dll assembly.
  • Derive your class from ToolTask (in Microsoft.Build.Framework.dll) to provide functionality for a task that wraps a command line tool.

Choosing to derive from Task or ToolTask makes it easier to log events from your task.

In the case of ITask or Task, you must add to your class a method named Execute, which is the method that is called when the task runs. This method takes no parameters and returns a Boolean value: True if the task succeeded or False if it failed. The following example shows the simplest possible task — it performs no action and returns True.

    using System;
    using Microsoft.Build.Utilities;


    namespace MyTasks
    {
        public class SimpleTask : Task
        {
            public override bool Execute()
            {
                return true;
            }
        }
    }

The following project file runs this task:

    <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
        <Target Name="MyTarget">
            <SimpleTask />
        </Target>
    </Project>

Tasks can also get properties when they run if you create .NET properties on the task class.

4/28/2005 11:23:10 PM - Neil
MSBuild sets these properties immediately before calling the task's Execute method. To create a string property, use task code such as:

    using System;
    using Microsoft.Build.Utilities;


    namespace MyTasks
    {
        public class SimpleTask : Task
        {
            public override bool Execute()
            {
                return true;
            }


            private string myProperty;
            public string MyProperty
            {
                get { return myProperty; }
                set { myProperty = value; }
            }
        }
    }

The following project file runs this task:

    <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
        <Target Name="MyTarget">
            <SimpleTask MyProperty="Value for MyProperty" />
        </Target>
    </Project>

Registering Tasks

If a project is going to be able to run a task,

4/28/2005 11:23:10 PM - Neil
MSBuild must know how to locate the assembly that contains the task class. Tasks are registered using the UsingTask element, for example:

        <UsingTask 
                TaskName="MyTasks.SimpleTask" 
                AssemblyName="Microsoft.Build.Tasks"/>

The TaskName attribute is required, and it is recommended that, to avoid ambiguity, it is the fully qualified name of the class that implements the task. The AssemblyName attribute provides the name of the assembly that contains the class. The assembly name can also be the strong name, for example, SimpleTask, Version=1.0.2,Culture=neutral. A strong name is required for locating task assemblies that are placed in the global assembly cache. When AssemblyName is used,

4/28/2005 11:23:10 PM - Neil
MSBuild will discover the task assembly via standard fusion loading rules. If fusion loading rules do not apply to your task the AssemblFile attribute can be used. Assembly file provides the disk location of library which contains the assembly. The assembly file attribute consumes either a full path or a relative path, for example:

        <UsingTask 
                TaskName="MyTasks.SimpleTask" 
                AssemblyFile="Microsoft.Build.Tasks.dll"/>

Or

        <UsingTask 
                TaskName="MyTasks.SimpleTask" 
                AssemblyFile="c:\somediskpath\Microsoft.Build.Tasks.dll"/>

The

4/28/2005 11:23:10 PM - Neil
MSBuild file Microsoft.Common.tasks is a project file that contains a list of UsingTask elements that register all the tasks that are supplied with MSBuild. This file is automatically included when building every project. If a task that is registered in Microsoft.Common.tasks is also registered in the current project file, the current project file takes precedence, that is, you can override a default task with your own task that has the same name.

You can see a list of the tasks that are supplied with

4/28/2005 11:23:10 PM - Neil
MSBuild by viewing the contents of Microsoft.Common.tasks.

Raising Events from a Task

If your task derives from the Task helper class, you can use any of the following helper methods on the Task class to raise events that will be caught and displayed by any registered loggers:

    public override bool Execute()
    {
        Log.LogError("messageResource1", "1", "2", "3");
        Log.LogWarning("messageResource2");
        Log.LogMessage(MessageImportance.High, "A comment");
    …
    }

If your task derives directly from ITask, you can still raise such events but you must use the IBuildEngine interface. The following example shows a task that derives directly from ITask and raises a custom event:

    public class SimpleTask : ITask
    {
        private IBuildEngine buildEngine;
        public IBuildEngine BuildEngine
        {
            get{ return buildEngine; }
            set{ buildEngine = value; }
        }


        public override bool Execute()
        {
            BuildMessageEventArgs args = new BuildMessageEventArgs(
                MessageImportance.Normal, "message");


            BuildEngine.LogMessageEvent(args);
            return true;
        }
    }

Requiring Task Parameters to be Set

You can mark certain task properties as “required” so that any project file that runs the task must set values for these properties or the build will fail. Apply the [Required] attribute to the .NET property in your task as follows:

private string requiredProperty;

        [Required]
        public string RequiredProperty
        {
                get { return requiredProperty; }
                set { requiredProperty = value; }
        }

The [Required] property is defined in the Microsoft.Build.Framework namespace.

Allowing a project to read a property from a task

You can mark certain task properties as “output” so that a project that uses the task may read off the value into an msbuild property or item list after the task is done executing. Apply the [Output] attribute to the .NET property in your task as follows, e.g.:

        [Output]
        public ITaskItem[] CopiedFiles
        {
                get { return _copiedFiles; }
                set { _copiedFiles = value; }
        }

The [Output] property is defined in the Microsoft.Build.Framework namespace.

In this example, a project would retrieve the list like this, e.g.:

       <SomeTask Sources="@(Stuff)">
                <Output TaskParameter="CopiedFiles" ItemName="MyListOfCopiedFiles"/>
       </SomeTask>

Building a Project

To build SimpleTask1 from the command line, navigate to the SimpleTask1 directory and type:

        msbuild Simpletask1.proj

To build SimpleTask2 from the command line, navigate to the SimpleTask2 directory and type:

        msbuild SimpleTask2.proj

To build SimpleTask3.proj from the command line, navigate to the SimpleTask3 directory and type:

        msbuild SimpleTask3.proj

To build the example project that uses SimpleTask3, navigate to the directory that contains invokesimpletask3.proj and type:

        msbuild invokesimpletask3.proj

Project Files

Download Code - C#

Visual C# example

        <Project 
                xmlns="http://schemas.microsoft.com/developer/msbuild/2003">


                <UsingTask 
                        TaskName="MyTasks.SimpleTask3" 
                        AssemblyFile="SimpleTask3\bin\debug\SimpleTask3.dll"/>


                        <Target Name="MyTarget">
                                <SimpleTask3 MyProperty="Hello!"/>
                        </Target>
        </Project>


Custom Task Library

Set Environment Variable Task

The SetEnvVar task allows environment variables to be set from within an

4/28/2005 11:23:10 PM - Neil
MSBuild project file:

  using System;
  using Microsoft.Build.Framework;
  using Microsoft.Build.Utilities;


  namespace Acme.Tools.MSBuildTasks
  {
    public class SetEnvVar : Task
    {
        private string _variable;
        private string _value;


        [Required]
        public string Variable
        {
            get { return _variable; }
            set { _variable = value; }
        }


        [Required]
        public string Value
        {
            get { return _value; }
            set { _value = value; }
        }


        public override bool Execute()
        {
            Environment.SetEnvironmentVariable(_variable, _value);
            return true;
        }
    }
  }

Use it like so:

  <Project DefaultTargets="SetupPath" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <UsingTask TaskName="Acme.Tools.MSBuildTasks.SetEnvVar" AssemblyFile="Acme.dll"/>
    <PropertyGroup>
      <Path>$(PATH);C:\Tools\Bin</Path>
    </PropertyGroup>


    <Target Name="SetupPath">
      <SetEnvVar Variable="PATH" Value="$(Path)"/>
    </Target>
  </Project>

SmtpMail Task

This task allows you to send email from a target:

    using System;
    using System.Collections.Generic;
    using System.Net.Mail;
    using Microsoft.Build.Framework;
    using Microsoft.Build.Utilities;


    namespace Acme.Tools.MSBuildTasks
    {
        public class SmtpMail : Task
        {
            private string _smtpHost;
            private string _from;
            private string _to;
            private string _cc = "";
            private string _subject = "";
            private string _body;
            private string _attachments = "";
            private int _timeout = 30 * 1000;
            private Nullable<int> _portNumber;


            [Required]
            public string SmtpHost
            {
                get { return _smtpHost; }
                set { _smtpHost = value; }
            }


            public string SmtpPort
            {
                get
                {
                    string s = _portNumber.HasValue ? _portNumber.ToString() : "";
                    return s;
                }
                set 
                {
                    int port;
                    if (Int32.TryParse(value, out port))
                    {
                        _portNumber = port;
                    }
                }
            }


            public int Timeout
            {
                get { return _timeout; }
                set { _timeout = value; }
            }


            [Required]
            public string From
            {
                get { return _from; }
                set { _from = value; }
            }


            [Required]
            public string To
            {
                get { return _to; }
                set { _to = value; }
            }


            public string CC
            {
                get { return _cc; }
                set { _cc = ((value == null) ? "" : value); }
            }


            public string Subject
            {
                get { return _subject; }
                set { _subject = ((value == null) ? "" : value); }
            }


            [Required]
            public string Body
            {
                get { return _body; }
                set { _body = value; }
            }


            public string Attachments
            {
                get { return _attachments; }
                set { _attachments = value; }
            }


            public override bool Execute()
            {
                MailMessage message = new MailMessage();


                message.From = new MailAddress(_from);


                string[] toAddresses = 
                    _to.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                foreach (string address in toAddresses)
                {
                    message.To.Add(address);
                }


                string[] ccAddresses = 
                    _cc.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                foreach (string address in ccAddresses)
                {
                    message.CC.Add(address);
                }


                string[] attachments = 
                    _attachments.Split(";".ToCharArray(),
                                              StringSplitOptions.RemoveEmptyEntries);
                foreach (string attachment in attachments)
                {
                    message.Attachments.Add(new Attachment(attachment.Trim()));
                }


                message.Subject = _subject;
                message.Body = _body;


                SmtpClient smtpClient = new SmtpClient(_smtpHost);
                smtpClient.Timeout = _timeout;


                if (_portNumber.HasValue)
                {
                    smtpClient.Port = _portNumber.Value;
                }


                smtpClient.Send(message);


                return true;
            }
        }
    }

Use it like so:

  <Project DefaultTargets="SendMail" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <UsingTask TaskName="Acme.Tools.MSBuildTasks.SmtpMail" AssemblyFile="Acme.dll"/>
    <Target Name="SendMail">
      <SmtpMail SmtpHost="smtp.mail.com" From="john_doe@acme.com" To="jane_doe@acme.com"
                Subject="Build Succeeded!!" Body="$(MessageInAVariable)"/>
    </Target>
  </Project>