Up to this point, this article has described the inner workings of MSBuild and how you can write project files that can be consumed from
msbuild.exe. Next I'll show you how to extend MSBuild and how to write your own Tasks that can be fully integrated within MSBuild. You will see how you can write a simple task that changes the output date of the source files to the current date.
First, what exactly is a 'user-defined Task'? Simply put, it's a managed class implementing the interface Microsoft.Build.Framework.ITask defined in the assembly MSBuildFramework.dll. Let's have a look at the definition of the interface:
public interface ITask
{
public bool Execute();
public IBuildEngine BuildEngine { get; set; }
public object HostObject { get; set; }
}
 | |
| Figure 1. The Class Model: This image shows the class model that is located behind a Task. |
The most important method is Execute. Execute is called when the Task is started through MSBuild. Furthermore you can find in the namespace Microsoft.Build.Utilities (assembly MSBuildUtilities) the class Task that is already implementing the interface ITask. The only thing you must do when deriving from this class is override the method Execute and implement your own build process logic to execute. Figure 1 shows the class model that is located behind a Task.
Your own Tasks must be registered within MSBuild when you want to use them. For the registration you can use the XML element <UsingTask> within the project file, as shown here:
<Project MSBuildVersion="2.0" DefaultTargets="MainTarget"
xmlns="http://schemas.microsoft.com/developer/
msbuild/2003">
<UsingTask TaskName="MyNamespace.MyTaskClass"
AssemblyName="MyAssembly" />
</Project>
After this registration the Task can be used as every other Task shipped with MSBuild. But you'll also need to know how a concrete Task can be implemented. The following code implements a Task that shows a Hello World message box.
using System;
using System.Windows.Forms;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace MyTasks
{
public class HelloWorld : Task
{
public override bool Execute()
{
MessageBox.Show("Hello World from MSBuild!");
return true;
}
}
}
Finally, here's how to register and use a user-defined Task within a project file:
<Project MSBuildVersion="2.0" DefaultTargets="MainTarget"
xmlns="http://schemas.microsoft.com/developer/
msbuild/2003">
<UsingTask TaskName="MyTasks.HelloWorld"
AssemblyName="MyAssembly" />
<Target Name="MainTarget">
<HelloWorld />
</Target>
</Project>
As you already know you have the possibility to connect several Tasks through the use of Input and Output Items. The next thing to learn is how to create a user-defined Task that produces Output Items that can be consumed from other Tasks as Input Items. Every public property within the implemented Task class can be accessed from the project file as an XML attribute. Properties that are marked with the attribute [Output] are Output Items.
The following example declares the property Files that accepts a list of files for which the last write time should be changed to the current time. Furthermore this property is marked with the attribute
MSBuild is completely extensible and scalable for large-scale enterprise applications. MSBuild was used for several years internally at Microsoft for building its own software products. Even better, there are rumors that Microsoft may decide to migrate the whole build process for Longhorn to MSBuild. While that doesn't change what MSBuild can do for you it does provide some welcome 'eating their own dog food'-level comfort. If Longhorn can be built with MSBuild then this tool is a first-class citizen for our own enterprise applications.