Walkthrough: Customizing Team Foundation Build with a Custom Task

You can extend Team Foundation Build by creating your own custom tasks and running them during a build. This topic explains the steps needed to extend a build definition with a custom task.

Required Permissions

To complete this walkthrough, you must have the Administer a build permission set to Allow. For more information, see Team Foundation Server Permissions.

Creating a Build Definition

Use the Build Definition dialog box to create a new build definition. You can either share an existing TFSBuild.proj file, or create a new one with the MSBuild Project File Creation Wizard. You edit the TFSBuild.proj file to customize each build definition that is associated with it. For more information about creating build definitions, see How to: Create a Build Definition.

Creating Custom Tasks

Tasks provide the code that runs during the build process. These tasks are contained in Target elements of MSBuild project files. MSBuild is the engine behind Team Foundation Build. Custom tasks have to be in a format that MSBuild understands. Each task has to be implemented as a .NET class that implements the ITask interface, which is defined in the Microsoft.Build.Framework.dll assembly.

There are two approaches you can use when implementing a task:

  • Implement the ITask interface directly.

  • Derive your class from the helper class, Task, which is defined in the Microsoft.Build.Utilities.dll assembly. Task implements ITask and provides default implementations of some ITask members.

In both approaches, you must add to your class a method named Execute, which is the method called when the task runs. This method takes no parameters and returns a Boolean value: true if the task succeeds or false if it fails. The following example shows a task that performs no action and returns true.

using System;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

namespace MyTasks
{
    public class SimpleTask : Task
    {
        public override bool Execute()
        {
            return true;
        }
    }
}

Tasks also can accept parameters, raise events, and log output. For more information, see MSBuild Tasks and MSBuild Overview.

Checking Out TFSBuild.proj

After you have written your task, you have to register it and call it out in one of the targets so that your task code executes at the desired point in the build process. If you used the MSBuild Project File Creation Wizard to create an MSBuild project file, and accepted the default location in source control, your TFSBuild.proj file is located in the folder $/MyTeamProject/TeamBuildTypes/MyBuildName in your Visual Studio Team System source control. In this scenario, MyTeamProject is the name of your team project and is the root node of all your team project sources, and MyBuildName is the name you gave to your build definition for which you originally created the TFSBuild.proj file. 

To determine the source control location of the TFSBuild.proj file, select the build definition in the Builds folder in Team Explorer, right-click it, and then click Edit. The TFSBuild.proj file's source control location is displayed on the Project File pane of the Build Definition dialog.

Note

Do not edit the Microsoft.TeamFoundation.Build.targets file because the customizations will apply to all the builds on that computer.

For information on checking out files, see Working with Team Foundation Version Control.

Registering Your Task

After you create your task, you must register it by specifying your task in a UsingTask element in the TFSBuild.proj file. The UsingTask element maps the task to the assembly that contains the task's implementation. For more information, see UsingTask Element (MSBuild).

To register a custom task

  1. Open the TFSBuild.proj file.

  2. Add a UsingTask element to the file and specify the details of your task.

    For example:

    <UsingTask 
        TaskName="MyTasks.SimpleTask" 
        AssemblyName="MyAssembly.Build.Tasks"/>
    

    -Or-

    <UsingTask 
        TaskName="MyTasks.SimpleTask" 
        AssemblyFile="MyAssembly.Build.Tasks.dll"/>
    

    -Or-

    <UsingTask
        TaskName="MyTasks.SimpleTask"
        AssemblyFile="c:\somediskpath\MyAssembly.Build.Tasks.dll"/>
    
  3. Save the file.

Running the Custom Task

Now that you have created and registered your task, you must specify the point in the build process that you want to run your task.

To run a task

  1. Decide where in the build process you want to run your custom task.

    For more information on where you can extend the build process, see Understanding Team Foundation Build Configuration Files.

  2. Open TFSBuild.proj and add the Target element that you chose above.

  3. Add the task element to run your task inside the Target element.

    For example, the following XML in TFSBuild.proj runs the SimpleTask task in the BeforeGet target, which runs immediately before the Get target.

    <Target Name="BeforeGet">
        <SimpleTask />
    </Target>
    
  4. Save the file.

Checking in the Files

You must check in the TFSBuild.proj file for the changes to take effect. Team Foundation Build copies this file from source control onto the build computer, so any changes made to the local copy on your computer will not affect the build. For more information on checking files into source control, see How to: Check In Pending Changes.

If you need Team Foundation Build to copy the task DLL to the build computer, you must add the task DLL to source control under the team project node.

Example Task

This example creates a custom task that extends the build definition or definitions that are associated with the TFSBuild.proj file by logging the size of the files produced by the build. The example contains two parts:

  • The task code.

  • The TFSBuild.proj file.

The logged information from this task can be seen in the build log file, Buildlog.txt, which is located in the build drop folder. The build log contains information similar to the following:

The total size is 9216 bytes in d:\BuildDir\MyTeamProj\MyBuildType\sources\..\Binaries\Release dir

C# Task Code

The example below contains the code that calculates the total binary size by adding the size of the files in the binaries folder.

Note

All binaries generated during a build are located in the Binaries folder in the build directory folder on the build agent.

For this task to be included in a build, the compiled DLL must be checked into source control under the team project folder. This guarantees that the file will be copied to the build agent during a build.

The following code calculates the size of binaries present in the Binaries folder of the build directory. The solution root property is passed to this task by the Team Foundation Build script.

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Build.Framework; 
using Microsoft.Build.Utilities;
using System.Diagnostics;
using System.IO;

namespace BuildTask
{
    public class BinSize : Task
    {
        private string sourceDir;

        [Required]
        public string SourceDir
        {
            get { return sourceDir; }
            set { sourceDir = value; }
        }
        public override bool Execute()
        {
            string szDir = sourceDir + "\\..\\Binaries";
            ProcessDirectory(szDir);
            return true;
        }
        private void ProcessDirectory(string targetDirectory)
        {
            // Process the list of files found in the directory.
            string[] fileEntries = Directory.GetFiles(targetDirectory, "*.*");
            if (fileEntries.Length > 0)
            {
                dwSize = 0;
                szCurrDir = targetDirectory;
                foreach (string fileName in fileEntries)
                    ProcessFile(fileName);
                ////////////////////////////////////////////////////////////////////////
                // This log message would just print out a line in the build log file. 
                // You need to add code to do what you need to do with this data. e.g. 
                // publishing it into the warehouse for reporting. 
                ///////////////////////////////////////////////////////////////////////
                Log.LogMessage("The total size of is {0} bytes in {1} dir",
                    dwSize, targetDirectory);
            }
            // Recurse into subdirectories of this directory.
            string[] subdirectoryEntries = Directory.GetDirectories(targetDirectory);
            foreach (string subdirectory in subdirectoryEntries)
                ProcessDirectory(subdirectory);
        }
        private void ProcessFile(string path)
        {
            FileInfo fi = new FileInfo(path);
            dwSize = dwSize + fi.Length;
        }
        private long dwSize;
        private string szCurrDir;
    }
}

TFSBuild.proj File

Once the task is compiled and checked into source control, it must be called from the TFSBuild.proj file. In this example, the task should be called after the files have been compiled and all binaries have been copied to the Binaries directory. Therefore, the task should be run in the BeforeDropBuild target. For more information on the extensible targets in TFSBuild.proj, see Understanding Team Foundation Build Configuration Files.

The example below contains the code in the modified TFSBuild.proj file. The example XML is almost entirely generated by the MSBuild Project File Creation Wizard, with the exception of the UsingTask element and the Target element located at the end of the file.

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="DesktopBuild" xmlns="https://schemas.microsoft.com/developer/msbuild/2003">
  <!-- TO EDIT BUILD TYPE DEFINITION

   TODO: Update all of the comments in this file!

   To edit the build type, you will need to edit this file which was generated
   by the Create New Build Type wizard.  This file is under source control and
   needs to be checked out before making any changes.

   The file is available at:
   
       $/{TeamProjectName}/TeamBuildTypes/{BuildTypeName}
       
   where you will need to replace TeamProjectName and BuildTypeName with your
   Team Project and Build Type name that you created

   Checkout the file
     1. Open Source Control Explorer by selecting View -> Other Windows -> Source Control Explorer
     2. Ensure that your current workspace has a mapping for the $/{TeamProjectName}/TeamBuildTypes folder and 
        that you have done a "Get Latest Version" on that folder
     3. Browse through the folders to {TeamProjectName}->TeamBuildTypes->{BuildTypeName} folder
     4. From the list of files available in this folder, right click on TfsBuild.Proj. Select 'Check Out For Edit...'


   Make the required changes to the file and save

   Checkin the file
     1. Right click on the TfsBuild.Proj file selected in Step 3 above and select 'Checkin Pending Changes'
     2. Use the pending checkin dialog to save your changes to the source control

   Once the file is checked in with the modifications, all future builds using
   this build type will use the modified settings
  -->

  <!-- Do not edit this -->
  <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\TeamBuild\Microsoft.TeamFoundation.Build.targets" />

  <ProjectExtensions>

    <!-- Team Foundation Build Version - DO NOT CHANGE -->
    <ProjectFileVersion>2</ProjectFileVersion>

    <!--  DESCRIPTION
     TODO: Obsolete.
    -->
    <Description>this one automatically  builds on check in</Description>

    <!--  BUILD MACHINE
     TODO: Obsolete.
    -->
    <BuildMachine>ahetod-test2</BuildMachine>

  </ProjectExtensions>

  <PropertyGroup>

    <!-- Properties set by the build type creation wizard -->
    
    <!--  TEAM PROJECT
     TODO: Obsolete.
    -->
    <TeamProject>TeamProjectName</TeamProject>

    <!--  BUILD DIRECTORY
     TODO: Obsolete.
    -->
    <BuildDirectoryPath>C:\Documents and Settings\user\Local Settings\Temp\1\TeamProjectName\BuildDefinitionName</BuildDirectoryPath>

    <!--  DROP LOCATION
     TODO: Obsolete.
    -->
    <DropLocation>\\UNKNOWN\drops</DropLocation>

    <!--  TESTING
     Set this flag to enable/disable running tests as a post build step.
    -->
    <RunTest>false</RunTest>

    <!--  CODE ANALYSIS
     To change CodeAnalysis behavior edit this value. Valid values for this
     can be Default,Always or Never.

         Default - To perform code analysis as per the individual project settings
         Always  - To always perform code analysis irrespective of project settings
         Never   - To never perform code analysis irrespective of project settings
     -->
    <RunCodeAnalysis>Never</RunCodeAnalysis>

    <!-- Additional Properties -->

    <!--  WorkItemType
     The type of the work item created on a build break - if empty, "Bug" will be used 
     -->
    <WorkItemType Condition=" '$(WorkItemType)'=='' "></WorkItemType>

    <!--  WorkItemFieldValues
     Add/edit key value pairs to set values for fields in the work item created
     during the build process. Please make sure the field names are valid 
     for the work item type being used.
     -->
    <WorkItemFieldValues>Symptom=build break;Steps To Reproduce=Start the build using Team Build</WorkItemFieldValues>

    <!--  WorkItemTitle
     Title for the work item created on build failure 
     -->
    <WorkItemTitle>Build failure in build:</WorkItemTitle>

    <!--  DescriptionText
     Description for the work item created on a build failure 
     -->
    <DescriptionText>This work item was created by Team Build on a build failure.</DescriptionText>

    <!--  BuildLogText
     Additional text for the work item create on a build failure.
     -->
    <BuildlogText>The build log file is at:</BuildlogText>

    <!--  ErrorWarningLogText
     Additional text for the work item create on a build failure 
     -->
    <ErrorWarningLogText>The errors/warnings log file is at:</ErrorWarningLogText>

    <!--  UpdateAssociatedWorkItems
     Set this flag to enable/disable updating associated workitems on a successful build
     -->
    <UpdateAssociatedWorkItems>true</UpdateAssociatedWorkItems>

    <!--  AdditionalVCOverrides
     Additional text for the VCOverrides file generated for Visual C++ projects
     -->
    <AdditionalVCOverrides></AdditionalVCOverrides>

    <!--  CustomPropertiesForClean
     Custom properties to pass to the MSBuild task while calling the "Clean" target for all solutions.
     The format should be: PropertyName1=value1;PropertyName2=value2;...
     -->
    <CustomPropertiesForClean></CustomPropertiesForClean>

    <!--  CustomPropertiesForBuild
     Custom properties to pass to the MSBuild task while calling the default targets for all solutions.
     The format should be: PropertyName1=value1;PropertyName2=value2;...  To pass custom properties to
     individual solutions, use the Properties metadata item of the SolutionToBuild ItemGroup.
     -->
    <CustomPropertiesForBuild></CustomPropertiesForBuild>

  </PropertyGroup>

  <ItemGroup>
    <!--  SOLUTIONS
     The paths of the solutions to build. To add/delete solutions, edit this
     ItemGroup. For example, to add a solution MySolution.sln, add the following line:
         
         <SolutionToBuild Include="$(BuildProjectFolderPath)\path\MySolution.sln" />

     To change the order in which the solutions are built, modify the order in
     which the solutions appear below.
     
     To call a target (or targets) other than the default, add a metadata item named
     Targets.  To pass custom properties to the solution, add a metadata item named
     Properties.  For example, to call the targets MyCustomTarget1 and MyCustomTarget2,
     passing in properties Property1 and Property2, add the following:
         
         <SolutionToBuild Include="$(BuildProjectFolderPath)\path\MySolution.sln">
             <Targets>MyCustomTarget1;MyCustomTarget2</Targets>
             <Properties>Property1=Value1;PropertyTwo=Value2</Properties>
         </SolutionToBuild>
    -->
    <SolutionToBuild Include="$(BuildProjectFolderPath)/../../SimpleAppToBuild/SimpleAppToBuild.sln">
        <Targets></Targets>
        <Properties></Properties>
    </SolutionToBuild>

  </ItemGroup>

  <ItemGroup>
    <!--  CONFIGURATIONS
     The list of configurations to build. To add/delete configurations, edit
     this value. For example, to add a new configuration, add the following lines:
         
         <ConfigurationToBuild Include="Debug|x86">
             <FlavorToBuild>Debug</FlavorToBuild>
             <PlatformToBuild>x86</PlatformToBuild>
         </ConfigurationToBuild>

     The Include attribute value should be unique for each ConfigurationToBuild node.
    -->
    <ConfigurationToBuild Include="Release|Any CPU">
        <FlavorToBuild>Release</FlavorToBuild>
        <PlatformToBuild>Any CPU</PlatformToBuild>
    </ConfigurationToBuild>

  </ItemGroup>

  <ItemGroup>
    <!--  TEST ARGUMENTS
     If the RunTest property is set to true then the following test arguments will be used to run 
     tests. Tests can be run by specifying one or more test lists and/or one or more test containers.

     To run tests using test lists, add MetaDataFile items and associated TestLists here:
     
        <MetaDataFile Include="$(SolutionRoot)\HelloWorld\HelloWorld.vsmdi">
            <TestList>BVT1;BVT2</TestList>
        </MetaDataFile>

     To run tests using test containers, add TestContainer items here:
     
        <TestContainer Include="$(OutDir)\HelloWorldTests.dll" />
        <TestContainer Include="$(SolutionRoot)\TestProject\WebTest1.webtest" />
        <TestContainer Include="$(SolutionRoot)\TestProject\LoadTest1.loadtest" />

    -->

  </ItemGroup>

  <ItemGroup>
    <!--  ADDITIONAL REFERENCE PATH
     The list of additional reference paths to use while resolving references.
     For example:
     
         <AdditionalReferencePath Include="C:\MyFolder\" />
         <AdditionalReferencePath Include="C:\MyFolder2\" />
    -->
  </ItemGroup> 
 <UsingTask TaskName="BuildTask.BinSize" AssemblyFile="$(SolutionRoot)\tools\BuildTask.dll" />

  <Target Name="BeforeDropBuild">
    <BinSize SourceDir="$(SolutionRoot)" />
  </Target>
  
</Project>

See Also

Tasks

How to: Write a Task

Concepts

MSBuild

Other Resources

Customizing Team Foundation Build