Skip to main content

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

Popular posts from this blog

C# Snippet - Shuffling a Dictionary [Beginner]

Randomizing something can be a daunting task, especially with all the algorithms out there. However, sometimes you just need to shuffle things up, in a simple, yet effective manner. Today we are going to take a quick look at an easy and simple way to randomize a dictionary, which is most likely something that you may be using in a complex application. The tricky thing about ordering dictionaries is that...well they are not ordered to begin with. Typically they are a chaotic collection of key/value pairs. There is no first element or last element, just elements. This is why it is a little tricky to randomize them. Before we get started, we need to build a quick dictionary. For this tutorial, we will be doing an extremely simple string/int dictionary, but rest assured the steps we take can be used for any kind of dictionary you can come up with, no matter what object types you use. Dictionary < String , int > origin = new Dictionary < string , int >();

C# Snippet - The Many Uses Of The Using Keyword [Beginner]

What is the first thing that pops into your mind when you think of the using keyword for C#? Probably those lines that always appear at the top of C# code files - the lines that import types from other namespaces into your code. But while that is the most common use of the using keyword, it is not the only one. Today we are going to take a look at the different uses of the using keyword and what they are useful for. The Using Directive There are two main categories of use for the using keyword - as a "Using Directive" and as a "Using Statement". The lines at the top of a C# file are directives, but that is not the only place they can go. They can also go inside of a namespace block, but they have to be before any other elements declared in the namespace (i.e., you can't add a using statement after a class declaration). Namespace Importing This is by far the most common use of the keyword - it is rare that you see a C# file that does not h

C# WPF Printing Part 2 - Pagination [Intermediate]

About two weeks ago, we had a tutorial here at SOTC on the basics of printing in WPF . It covered the standard stuff, like popping the print dialog, and what you needed to do to print visuals (both created in XAML and on the fly). But really, that's barely scratching the surface - any decent printing system in pretty much any application needs to be able to do a lot more than that. So today, we are going to take one more baby step forward into the world of printing - we are going to take a look at pagination. The main class that we will need to do pagination is the DocumentPaginator . I mentioned this class very briefly in the previous tutorial, but only in the context of the printing methods on PrintDialog , PrintVisual (which we focused on last time) and PrintDocument (which we will be focusing on today). This PrintDocument function takes a DocumentPaginator to print - and this is why we need to create one. Unfortunately, making a DocumentPaginator is not as easy as