Creating MSBuild Tasks in C#

Skill

Creating MSBuild Tasks in C#

Posted in:

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, and you can grab a zip file with the Visual Studio solution, the MSBuild project file, and the batch file right below.

den123
05/28/2009 - 13:11

Very nice article! Thanks!

reply

Anonymous
01/14/2010 - 20:11

what you had posted in here is incomplete, dude

reply

Anonymous
01/14/2010 - 20:14

The code in MyTask.cs have basic syntax error, don't follow this post throughly, guys, you need to adda this to get it work:
private string mFileName = "";
and modify the get/set property as such:
pulic string FileName
{
get {return mFileName;}
set {mFileName = value;}
}

you also have to build the .dll file first, then you can successfully run the .bat file.

reply

Anonymous
01/21/2010 - 08:50

I assume you are using .NET Framework 2.0 which doesn't support auto properties? If you compiled this under .NET Framework 3.5 it's fine.

reply

Add Comment

Put code snippets inside language tags:
[language] [/language]

Examples:
[javascript] [/javascript]
[actionscript] [/actionscript]
[csharp] [/csharp]

See here for supported languages.

Javascript must be enabled to submit anonymous comments - or you can login.

Sponsors