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 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 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. 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, 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, 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 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 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 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>
	

Below are the series of 5 shots - cut into fragments. Take each fragment, and remix the highlighted portions. When we mix them together, it'll be a global collaboration where within every shot, multiple creators will be present.

Criminal Justice school AND PhD economics

Of course, if you want to remix other sections, or use this in other work, thats up to you! But the plan that exists now is to take the Times Square bits and rotoscoping them, photoshop them, re-create them, and remix the private corporate world back into the public domain.

biology degree AND BE degree AND distance learning degree

Microsoft Communities