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# 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

C# WPF Tutorial - Implementing IScrollInfo [Advanced]

The ScrollViewer in WPF is pretty handy (and quite flexible) - especially when compared to what you had to work with in WinForms ( ScrollableControl ). 98% of the time, I can make the ScrollViewer do what I need it to for the given situation. Those other 2 percent, though, can get kind of hairy. Fortunately, WPF provides the IScrollInfo interface - which is what we will be talking about today. So what is IScrollInfo ? Well, it is a way to take over the logic behind scrolling, while still maintaining the look and feel of the standard ScrollViewer . Now, first off, why in the world would we want to do that? To answer that question, I'm going to take a an example from a tutorial that is over a year old now - Creating a Custom Panel Control . In that tutorial, we created our own custom WPF panel (that animated!). One of the issues with that panel though (and the WPF WrapPanel in general) is that you have to disable the horizontal scrollbar if you put the panel in a ScrollV