Friday, February 15, 2008 11:03 PM bart

The custom MSBuild task cookbook

A few years ago I wrote about building custom MSBuild tasks. I wanted to bring the topic back in the spotlight in order to prepare for a follow-up post. Since my previous post on PowerShell cmdlet development (Easy Windows PowerShell cmdlet development and debugging) has been very well-received, I decided to create a similar cookbook for those of you who're interested in building and debugging your own custom MSBuild tasks. The focus of this post is primarily on seamless development and debugger rather than on task functionality.

 

Step 1 - Create a Class Library project

As usual for these kind of extensions to an existing system (e.g. PowerShell, MMC, MSBuild, provider-based technologies, etc) we start by creating a class library project:

image

 

Step 2 - Import references

In order to create MSBuild tasks we need to reference a few libraries. In Solution Explorer, right click your project and select Add References. Now select Microsoft.Build.Utilities.v3.5 and Microsoft.Build.Framework from the list, both from the .NET Framework 3.5 band (note: I won't use 3.5 specific functionality in this post, so you could use the 2.0 assemblies as well, but as a general recommendation don't mix different versions):

image

Solution Explorer should look like this (notice I've removed a few other references I don't need but obviously this depends on your goals for the custom task):

image

 

Step 3 - Implement the task skeleton

Implementing custom MSBuild tasks isn't very difficult - all you need to do is implement the task interface ITask. However, if you try to do that you'll see there are few members to be implemented that just add boilerplate code. Instead one can derive from the abstract base class Task:

image 

This will require the Microsoft.Build.Utilities namespace to be imported as shown above. I revealed the 'functionality' of this task in the class name in the meantime, I hope it's not too shocking :-). After importing the namespace, implement the base class:

image

Just one method to go, not too frightening:

image 

 

Step 4 - Task parameterization

Before we dig into the Execute method we should think of adding some parameterization in order to communicate with the outside world. Although it's not really required, I bet there's little you can do without it... Parameters are simply properties on the class, more or less like parameters on cmdlets in PowerShell. Let's add a property using the prop snippet:

image

Press TAB twice and fill in the placeholders:

image

In reality you'd typically add (array) parameters of type ITaskItem because typically you'll want to reference certain files in the build system. ITaskItem is the gateway to do this but let's not go there for now. In order to make parameters required, simply add a RequiredAttribute on them. This requires importing Microsoft.Build.Framework:

image

 

Step 5 - Implementing functionality

Now it's time to provide the real functionality in the Execute method body. Let's do something simple, i.e. logging some message to the build system. In reality you'd manipulate files or so, possibly generating output (see the Output attribute) but simplicity is key in this post:

image

A simple two-liner: first log something in String.Format style, also specifying some importance level for the message (when running MSBuild you can control the "verbosity") and returning success (true) or failure.

 

Step 6 - Setting up debugging

Now comes the key take-away of this post: how to configure debugging? There are various ways of doing it, the one uglier than the other. But the following approach is pretty clean though. First, add a new item to the project (choose Add New Item in the context menu on the project node in Solution Explorer) and choose for XML file. Name it Debug.testproj:

image

In this file, add the following piece of XML:

image

Let's explain a few things:

  • On the Project node we refer to Debug in the DefaultTargets. We define this target a bit further in the Target node.
  • The UsingTask node is the most important one. Basically MSBuild loads tasks from assemblies using reflection. The TaskName attribute needs to match the class name in the assembly, in our case HelloWorldTask. To reference the assembly there are two options: AssemblyName to specify the name (e.g. MyTask, Version=1.0.0.0, PublicKeyToken=..., Culture=neutral) typically for tasks in the GAC or AssemblyFile to reference an assembly directly. We use the latter option, referring to the output of our class library project (make sure this references the the right folder where you created the project, suffixed by bin\Debug\assembly.dll).
  • Finally we define the Target with name Debug (as referred to in the Project node), calling our task. Calling a task consists of specifying its name as a tag and adding any of the parameters (in our case the required Name property) as attributes.

That's it:

image

Don't worry about the blue squirrel line, the MSBuild schema doesn't know about our custom task but that's fine. It will find it if you got the UsingTask declaration right (comparable to a using statement in C#).

Time to configure the debugger. Right-click your project and choose Properties. Go to the Debug tab and specify the following:

image

In the 'Start external program' textbox enter the path to your MSBuild.exe file. Make sure to use the one that matches the versions of the references assemblies in step 1 (I chose for the 3.5 assemblies, so I refer to %windir%\Microsoft.NET\Framework\v3.5\MSBuild.exe). Under 'Command line arguments' enter the path to the Debug.testproj file created above (you can find the path by marking the file in Solution Explorer and copying the Full Path property from the Properties pane).

 

Step 7 - Set a breakpoint and run

Time to test drive. Set a breakpoint in the code:

image

and hit F5. You'll see that MSBuild starts:

image

and our breakpoint is hit:

image

You can hover over the variables to get runtime information:

image

Press F10 to step to the next line and switch back to the MSBuild window:

image

Congratulations! You've successfully stepped through your first MSBuild task in the debugger.

 

Step 8 - Advanced debugging

Of course when more complex interactions with a complex build file are required, you'll need to do more "live debugging". In such a case multiple solutions exist:

  • Simply tweak the 'Command line arguments' in step 6 to point to the more complex build file and add your custom task in a similar way as described in step 6, hooking it up in the right spot.
  • Create a debugger assistant task that pops up a message box (MessageBox.Show) with a message "Attach debugger here" and hook it up in your to-be-tested project as a pre-build step. When launching the build (maybe even on an external machine using remote debugging) the message box will block further execution, allowing you to go to Debug, Attach to Process. You'll recognize the process to attach to by the message box's title "Attach debugger here". Set breakpoints and click OK on the message box and you're in business.
  • If you need to debug startup code in your task (such as a static constructor - which isn't really the best idea in most cases), the first approach will be the best since you're attaching to MSBuild right from the start.

Nevertheless, the technique outlined in this post should be good enough to cover most MSBuild custom task debugging cases.

 

Happy debugging!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Filed under:

Comments

# The managed installer custom actions cookbook

Friday, February 15, 2008 11:48 PM by B# .NET Blog

While I'm in the mood of writing up those cookbook posts: The custom MSBuild task cookbook Easy Windows

# re: The custom MSBuild task cookbook

Friday, February 15, 2008 11:48 PM by Francis Norton

Thanks - I knew how to write the code but the debugging options were new to me and very helpful.

# Invoking PowerShell scripts from MSBuild

Saturday, February 16, 2008 2:22 AM by B# .NET Blog

I'm a firm believer of the "innovation through integration" theme. As a fan of MSBuild

# Link Listing - February 15, 2008

Saturday, February 16, 2008 5:01 AM by Christopher Steen

MSBuild The custom MSBuild task cookbook [Via: bart ] ASP.NET Simplifying ASP.NET ListView Control...

# Link Listing - February 15, 2008

Saturday, February 16, 2008 5:01 AM by Christopher Steen

Link Listing - February 15, 2008

# » Daily Bits - February 16, 2008 Alvin Ashcraft’s Daily Geek Bits: Daily links, development, gadgets and raising rugrats.

Pingback from  » Daily Bits - February 16, 2008 Alvin Ashcraft’s Daily Geek Bits: Daily links, development, gadgets and raising rugrats.

# 3 Links Today (2008-02-17)

Sunday, February 17, 2008 7:18 AM by 3 Links Today (2008-02-17)

Pingback from  3 Links Today (2008-02-17)

# Reflective Perspective - Chris Alcock » The Morning Brew #35

Tuesday, February 19, 2008 12:05 AM by Reflective Perspective - Chris Alcock » The Morning Brew #35

Pingback from  Reflective Perspective - Chris Alcock  » The Morning Brew #35

# Visual Studio Links #2 : Mostly Programming Stuff

Wednesday, February 20, 2008 4:32 AM by Visual Studio Links #2 : Mostly Programming Stuff

Pingback from  Visual Studio Links #2 : Mostly Programming Stuff

# Resumo da semana - 25/02/2008

Monday, February 25, 2008 3:06 AM by Console.Write(this.Opinion)

Resumo da semana - 25/02/2008

# The managed MMC 3.0 snap-in cookbook

Wednesday, February 27, 2008 12:04 AM by B# .NET Blog

My recent series of "cookbook" posts has been very well-received and coincidentally I got mail

# The custom MSBuild task cookbook

Monday, March 03, 2008 1:38 AM by DotNetKicks.com

You've been kicked (a good thing) - Trackback from DotNetKicks.com

# http://blogs.bartdesmet.net/blogs/bart/archive/2008/02/15/the-custom-msbuild-task-cookbook.aspx

# Rudamentary WiX Orphan Prevention | James Reuben Knowles

Wednesday, January 20, 2010 8:19 PM by Rudamentary WiX Orphan Prevention | James Reuben Knowles

Pingback from  Rudamentary WiX Orphan Prevention | James Reuben Knowles

# MSBuild S3 Publisher

Sunday, April 25, 2010 6:07 AM by Steve's blog

MSBuild S3 Publisher

# writing msbuild script - Programmers Goodies

Sunday, October 09, 2011 9:20 PM by writing msbuild script - Programmers Goodies

Pingback from  writing msbuild script - Programmers Goodies

# Отладка собственных задач для MSBuild | ТиЭс Софт

Pingback from  Отладка собственных задач для MSBuild | ТиЭс Софт