Welcome to MSDN Blogs Sign in | Join | Help

Adding BuildSteps to Team Build through a Custom Task

Team Build displays Build Steps in the build report form within Visual Studio.  By default, build steps are added at various points during the course of a build - while getting sources (in the Get task), compiling solutions / projects, copying files to the drop location, etc.  Team Build allows users to insert their own build steps using the publicly accessible BuildStore web service - in particular, the AddBuildStep and UpdateBuildStep methods.  The following sample (I make no claims as to the awesomeness or lack thereof of this sample, etc.) TeamBuildTask class illustrates how this can be done:

using System;
using System.Web.Services;
using Microsoft.Build.Framework;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Build.Common;
using Microsoft.TeamFoundation.Build.Proxy;

/// <summary>
/// Summary description for Class1
/// </summary>
namespace MyNamespace
{
    public abstract class TeamBuildTask : ITask
    {
        /// <summary>
        /// Put real task logic in this method.
        /// </summary>
        /// <returns>True if task is successful, otherwise false.</returns>
        protected abstract bool ExecuteInternal();

        /// <summary>
        /// Returns the name of the build step to be added for this task.
        /// </summary>
        /// <returns>Name of the build step to be added.</returns>
        protected abstract string GetBuildStepName();

        /// <summary>
        /// Returns the message of the build step to be added for this task - this is the
        /// string displayed in the Team Build GUI.
        /// </summary>
        /// <returns>Message of the build step to be added.</returns>
        protected abstract string GetBuildStepMessage();

        /// <summary>
        /// ITask implementation - BuildEngine property.
        /// </summary>
        public IBuildEngine BuildEngine
        {
            get
            {
                return m_buildEngine;
            }
            set
            {
                m_buildEngine = value;
            }
        }

        /// <summary>
        /// ITask implementation - HostObject property.
        /// </summary>
        public ITaskHost HostObject
        {
            get
            {
                return m_hostObject;
            }
            set
            {
                m_hostObject = value;
            }
        }

        /// <summary>
        /// The Url of the Team Foundation Server.
        /// </summary>
        [Required]
        public string TeamFoundationServerUrl
        {
            get
            {
                return m_tfsUrl;
            }
            set
            {
                m_tfsUrl = value;
            }
        }

        /// <summary>
        /// The Uri of the Build for which this task is executing.
        /// </summary>
        [Required]
        public string BuildUri
        {
            get
            {
                return m_buildUri;
            }
            set
            {
                m_buildUri = value;
            }
        }

        /// <summary>
        /// Lazy init property that gives access to the TF Server specified by TeamFoundationServerUrl.
        /// </summary>
        protected TeamFoundationServer Tfs
        {
            get
            {
                if (m_tfs == null)
                {
                    if (String.IsNullOrEmpty(TeamFoundationServerUrl))
                    {
                        // Throw some exception.
                    }
                    m_tfs = TeamFoundationServerFactory.GetServer(TeamFoundationServerUrl);
                }
                return m_tfs;
            }
        }

        /// <summary>
        /// Lazy init property that gives access to the BuildStore service of the TF Server.
        /// </summary>
        protected BuildStore BuildStore
        {
            get
            {
                if (m_buildStore == null)
                {
                    m_buildStore = (BuildStore)Tfs.GetService(typeof(BuildStore));
                }
                return m_buildStore;
            }
        }

        /// <summary>
        /// ITask implementation - Execute method.
        /// </summary>
        /// <returns>
        /// True if the task succeeded, false otherwise.
        /// </returns>
        public bool Execute()
        {
            bool returnValue = false;

            try
            {
                AddBuildStep();
                returnValue = ExecuteInternal();
            }
            catch (Exception e)
            {
                AddExceptionBuildStep(e);
                throw;
            }
            finally
            {
                UpdateBuildStep(returnValue);
            }

            return returnValue;
        }

        private void AddBuildStep()
        {
            BuildStore.AddBuildStep(BuildUri, GetBuildStepName(), GetBuildStepMessage());
        }

 

        private void UpdateBuildStep(bool result)
        {

            BuildStepStatus status = result ? BuildStepStatus.Succeeded : BuildStepStatus.Failed;
            BuildStore.UpdateBuildStep(BuildUri, GetBuildStepName(), DateTime.Now, status);
        }


        private void AddExceptionBuildStep(Exception e)
        {
            try
            {
                BuildStore.AddBuildStep(BuildUri, "Exception", e.Message);
                BuildStore.UpdateBuildStep(BuildUri, "Exception", DateTime.Now, BuildStepStatus.Failed);
            }
            catch
            {
                // Eat any exceptions.
            }
        }

        private IBuildEngine m_buildEngine;
        private ITaskHost m_hostObject;
        private string m_tfsUrl;
        private string m_buildUri;
        private TeamFoundationServer m_tfs;
        private BuildStore m_buildStore;
    }
}

To use this base class, just override the ExecuteInternal method - put the actual task logic here.  Then override the GetBuildStepName and GetBuildStepMessage methods to specify the Name of the build step (which serves as its ID and should therefore be reasonably unique) and the Message of the build step (which will be the string displayed in the build report form).

The Execute method will add a build step (with the specified Name and Message) when task execution starts, and update the build step with the appropriate status and time when execution completes.  If an exception is thrown by the ExecuteInternal method, an exception build step will be added (if possible) by the AddExceptionBuildStep method.

Note the TeamFoundationServerUrl and BuildUri properties.  These properties will need to be set for derived tasks to function properly, and can be easily set to the TeamFoundationServerUrl and BuildURI properties available during a Team Build (e.g. within TfsBuild.proj).

<Target Name="BeforeBuild">
	<SomeCustomTask 
		TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
		BuildUri="$(BuildURI)" />
</Target>

Happy building!

Published Tuesday, August 29, 2006 3:14 PM by aaronhallberg
Filed under: ,

Comments

# Team Build blogger: Aaron Hallberg

Aaron Hallberg, a developer working on Team Build,&amp;nbsp;has taken the plunge and started a blog.&amp;nbsp;...
Tuesday, August 29, 2006 3:45 PM by Buck Hodges

# Now Blogging: Aaron Hallberg

I learned from Buck Hodges that a developer on his Team Foundation Build team in North Carolina, Aaron...
Tuesday, August 29, 2006 5:27 PM by Rob Caron

# Speaking of Team Build...

One of the things that made working with the early betas and CTPs of VSTS great was the transparency
Tuesday, August 29, 2006 11:39 PM by Ponder .NET

# Adding Custom Loggers to Team Build

Most of the magic in a Team Build is done using either (a) customized tasks, or (b) a customized logger.&amp;nbsp;&amp;nbsp;Well,...
Wednesday, August 30, 2006 9:02 AM by Aaron Hallberg

# VSTS Links - 09/01/2006

Buck Hodges on Team Build blogger: Aaron Halberg.

And speaking of Aaron Halberg, here is his post...
Friday, September 01, 2006 8:57 AM by Team System News

# Custom Build Task.

一直在思考是否有真正的範例,來整合 Team Build 與 Java Compiler... Well .. 不幸的是 還真的沒有 超級現成的範例。 但至少目前 Aaron 的腳本 給了一點方向,透過...
Thursday, September 07, 2006 8:34 PM by Refines.Info["Polo Lee"]

# Determining Whether Tests Passed in Team Build

In a forum post a while back, I laid out a method for determining whether tests had passed during a build.&amp;nbsp;...
Thursday, September 21, 2006 1:26 PM by Aaron Hallberg

# Adding Build Steps to Team Build in orcas

Way back in August I did a post (my 2nd ever!) on adding build steps to Team Build using a custom task.

Wednesday, March 28, 2007 11:01 AM by Aaron Hallberg

# Building Non-MSBuild Projects With Team Build

Building non-MSBuild projects in Team Build has never been a particularly nice experience... You can

Friday, March 30, 2007 12:03 PM by Aaron Hallberg

# Better integrating other build tools into your build

Aaron Hallberg wrote a great post today showing how to use a custom task to better integrate other build

Friday, March 30, 2007 9:43 PM by Buck Hodges

# Getting the Modified Files for a Team Build Build

I've had several people inquire recently about how to figure out which files have changed since the previous

Wednesday, April 04, 2007 3:04 PM by Aaron Hallberg

# Determining Whether Tests Passed in Team Build

In a forum post a while back, I laid out a method for determining whether tests had passed during a build.

Wednesday, April 04, 2007 3:06 PM by Aaron Hallberg

# Adding Custom Loggers to Team Build

Most of the magic in a Team Build is done using either (a) customized tasks, or (b) a customized logger

Wednesday, April 04, 2007 3:09 PM by Aaron Hallberg

# Customizing Team Build

I'm speaking at TechEd 2007 in Orlando this coming June on the whys and hows of customizing TFS. As part

Wednesday, April 25, 2007 8:31 AM by Jeff Beehler's Blog

# Customizing Team Build

Sharing this information from Jeff Beehler's Blog Aaron’s posts on how to extend team build through custom

Wednesday, May 02, 2007 5:09 AM by Srikanth R - A VSTS and TFS Blog

# Team Build: Extending Team Build to show Unit Test failures in the summary page.

As those who are doing unit testing in Visual Studio Team System will know, it is not always the easiest

Wednesday, June 06, 2007 9:30 PM by Stuart Preston's Blog

# Team System Chat - December 5, 2007

DavidKean_MS (Moderator): The Visual Studio Team System chat will begin in 15 minutes. DavidKean_MS...

Wednesday, December 05, 2007 2:04 PM by Team System News

# re: Adding BuildSteps to Team Build through a Custom Task

Hi Aaron,

Could you please check my post at Microsoft forums?

Here it is: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2892292&SiteID=1

I use the recommended approach under TFS 2005 and everything is OK except that BuildStepData.BuildStepName always returns an empty string (BuildStepData.BuildStepMessage returns correct value at the same time). Because of that I'm lack of unique ID to identify buils steps in my custom "Monitor build progress" dialog :(

Saturday, February 23, 2008 6:13 AM by AlexeyYumashin

# re: Adding BuildSteps to Team Build through a Custom Task

Is there a way to mention a URL in message so that we can click it and browse the URL ?

Monday, April 21, 2008 2:50 AM by Raman Chandrayan

# re: Adding BuildSteps to Team Build through a Custom Task

Hi Aaron,

Do you have an update that will work with TFS 2008?

Friday, June 06, 2008 5:59 PM by myost2921

# re: Adding BuildSteps to Team Build through a Custom Task

TFS 2008 includes a BuildStep task in the core product - see http://msdn.microsoft.com/en-us/library/bb399129.aspx.

Monday, June 09, 2008 8:59 AM by aaronhallberg
Anonymous comments are disabled
 
Page view tracker