C# - Creating MSBuild Tasks [Beginner]


At some point with any decently complex project, you are going to need to start customizing your build process. Visual Studio makes this possible, and not too terribly painful, by giving developers the ability to write custom MSBuild project files. But eventually, you are going to hit the limit of what the MSBuild XML syntax can do for you - but when you reach that point, there is something else waiting - custom tasks! MSBuild gives you the ability to write custom build tasks in C# (or any .NET language), and use those tasks inside of MSBuild project files. We are going to be taking a look today at how to create these tasks and how to use them.

So first, we need to create a task. To do this, you need a new Visual Studio project - any type will do, but it is easiest to go with a class library:

Creating a class library project in
studio.

Once you have a new blank project, we need to add a few references in order to get the classes we need to create a task. To do this, right click on "References" in Solution Explorer and choose "Add Reference":

Add Reference Context
Menu

Clicking that will bring up the Add Reference dialog. Once in here, you need to choose two things Microsoft.Build.Framework and Microsoft.Build.Utilities.v3.5:

Add Reference
Dialog

Now it is time to actually start creating the task. Every task derives from Microsoft.Build.Utilities.Task, which is a abstract class with a single method that you are required to implement:
using System;
using Microsoft.Build.Utilities;

namespace BuildTask
{
  public class MyTask : Task
  {
    public override bool Execute()
    { throw new NotImplementedException(); }
  }
}
 
This execute method is what will get called when your task is executed by MSBuild. For today we are going to create an extremely simple execute method, but really the sky is the limit. You are in C# code now - so you have everything that C# and the framework offers you right at your fingertips.
using System;
using Microsoft.Build.Utilities;
using System.IO;

namespace BuildTask
{
  public class MyTask : Task
  {
    public string FileName { get; set; }

    public override bool Execute()
    {
      try
      { File.WriteAllText(FileName, DateTime.Now.ToString()); }
      catch
      { return false; }

      return true;
    }
  }
}
 
All our task here does is write the current date/time to the file given by FileName. If it succeeds, it returns true, otherwise, it returns false. Every property that you expose on your task class is one that you can set when using the task (although since you have to set it in XML, it is generally best to go with strings). The return value of the Execute method is used by MSBuild to determine if your task successfully completed or not - true means success, false means failure.

And that is really it for writing a task. Now on to actually using it. To do this, we first have to write our own custom MSBuild project file. If you are already customizing your build process, you probably have one already. Here we are going to have one that is about as simple as it can get:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="All" 
         xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <UsingTask AssemblyFile="BuildTask.dll" TaskName="MyTask"/>

  <Target Name="RunMyTask">
    <MyTask FileName="MyDate.txt" />
  </Target>

</Project>
 
The UsingTask tag lets MSBuild know that you will be using a task named MyTask from the dll "BuildTask.dll". Once you have that set up, you can use the task inside of any target in your project file - here we are running it in the "RunMyTask" target, and giving it a file name of "MyDate.txt".

To actually run this target through MSBuild, you will need to do the following on the command line (I created a batch file):
call "%VS90COMNTOOLS%\vsvars32.bat"
msbuild /target:RunMyTask TaskTest.proj
 
And when you do this, you will get some output that looks like the following:
C:\BuildTask\bin\Debug>TaskTest.bat

C:\BuildTask\bin\Debug>call "c:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools\\vsvars32.bat"
Setting environment for using Microsoft Visual Studio 2008 x86 tools.

C:\BuildTask\bin\Debug>msbuild /target:RunMyTask TaskTest.proj
Microsoft (R) Build Engine Version 3.5.30729.1
[Microsoft .NET Framework, Version 2.0.50727.3074]
Copyright (C) Microsoft Corporation 2007. All rights reserved.

Build started 5/27/2009 7:42:12 PM.

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:00.01

C:\BuildTask\BuildTask\bin\Debug>
 
And guess what? There is a file sitting in that directory called "MyDate.txt" that holds the current date/time.

Well, that about covers the basics of writing and using custom build tasks. This really only scratches the surface of what you can do, both with custom tasks and MSBuild in general, but I think I'll save that for a different tutorial. If you have any questions, please leave them in the comments.

Comments