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 type with a custom task.
Required Permissions
To complete this walkthrough, you must have the have the Administer a build permission set to Allow. For more information, see Team Foundation Server Permissions.
Creating a Build Type
Use the New Team Build Type Creation Wizard to create a new build type. This creates the TfsBuild.proj file and checks it in to source control. You edit this file to customize the build type. For more information on creating build types, see How to: Create a New Build Type.
Creating Custom Tasks
Tasks provide the code that runs during the build process and are contained in Target elements of MSBuild project files. MSBuild is the engine behind Team Foundation Build and the 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 cases, 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 succeeded or false if it failed. 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. You will find TfsBuild.proj file in the folder $/MyTeamProject/TeamBuildTypes/MyBuildName in your Visual Studio Team System source control, where 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 type when you created it using the New Team Build Type Creation Wizard. Each build type is stored in a folder in the TeamBuildTypes folder under the team project root node.
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 Source Control.
Registering Your Task
After you have created 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
Open the TfsBuild.proj file.
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"/>
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
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.
Open TfsBuild.proj and add the Target element that you chose above.
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 theBeforeGet
target, which runs immediately before theGet
target.<Target Name="BeforeGet"> <SimpleTask /> </Target>
Save the file.
Checking in the Files
You must check in the TfsBuild.proj file for the changes to take affect. Team Foundation Build copies this file from source control onto the build computer, so any changes made to your local copy 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 MyBuildType build type 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.log, 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 computer.
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 computer during a build.
The code below 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 New Team Build Type 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
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\v8.0\TeamBuild\Microsoft.TeamFoundation.Build.targets" />
<ProjectExtensions>
<!-- DESCRIPTION
The description is associated with a build type. Edit the value for making changes.
-->
<Description>
</Description>
<!-- BUILD MACHINE
Name of the machine which will be used to build the solutions selected.
-->
<BuildMachine>MyBuildMachine</BuildMachine>
</ProjectExtensions>
<PropertyGroup>
<!-- TEAM PROJECT
The team project which will be built using this build type.
-->
<TeamProject>MyTeamProject</TeamProject>
<!-- BUILD DIRECTORY
The directory on the build machine that will be used to build the
selected solutions. The directory must be a local path on the build
machine (for example, c:\build).
-->
<BuildDirectoryPath>d:\build</BuildDirectoryPath>
<!-- DROP LOCATION
The location to drop (copy) the built binaries and the log files after
the build is complete. This location must be a valid UNC path of the
form \\Server\Share. The build machine service account and application
tier account must have read write permission on this share.
-->
<DropLocation>\\MyDropServer\drops</DropLocation>
<!-- TESTING
Set this flag to enable/disable running tests as a post build step.
-->
<RunTest>true</RunTest>
<!-- WorkItemFieldValues
Add or 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>Priority=1;Severity=1</WorkItemFieldValues>
<!-- CODE ANALYSIS
To change CodeAnalysis behavior, edit this value. Valid values for this
can be Default, Always, or Never.
Default - To perform code analysis according to individual project settings.
Always - To always perform code analysis irrespective of project settings.
Never - To never perform code analysis irrespective of project settings.
-->
<RunCodeAnalysis>Default</RunCodeAnalysis>
<!-- UPDATE ASSOCIATED WORK ITEMS
Set this flag to enable/disable updating associated work items on a successful build
-->
<UpdateAssociatedWorkItems>true</UpdateAssociatedWorkItems>
</PropertyGroup>
<ItemGroup>
<!-- SOLUTIONS
The path of the solutions to build. To add or delete solutions, edit this
value. For example, to add a solution MySolution.sln, add following line -
<SolutionToBuild Include="$(SolutionRoot)\path\MySolution.sln" />
To change the order in which the solutions are built, modify the order in
which the solutions appear below.
-->
<SolutionToBuild Include="$(SolutionRoot)\ConsoleApplication7\ConsoleApplication7.sln" />
</ItemGroup>
<ItemGroup>
<!-- CONFIGURATIONS
The list of configurations to build. To add or 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>
<ConfigurationToBuild Include="Debug|Any CPU">
<FlavorToBuild>Debug</FlavorToBuild>
<PlatformToBuild>Any CPU</PlatformToBuild>
</ConfigurationToBuild>
<ConfigurationToBuild Include="Debug|x86">
<FlavorToBuild>Debug</FlavorToBuild>
<PlatformToBuild>x86</PlatformToBuild>
</ConfigurationToBuild>
<ConfigurationToBuild Include="Release|x86">
<FlavorToBuild>Release</FlavorToBuild>
<PlatformToBuild>x86</PlatformToBuild>
</ConfigurationToBuild>
</ItemGroup>
<ItemGroup>
<!-- TEST ARGUMENTS
If the RunTest is set to true then the following test arguments will be
used to run tests.
To add or delete new testlist or to choose a metadata file (.vsmdi) file, edit this value.
For example, to run BVT1 and BVT2 type tests mentioned in the Helloworld.vsmdi file, add the following -
<MetaDataFile Include="$(SolutionRoot)\HelloWorld\HelloWorld.vsmdi">
<TestList>BVT1;BVT2</TestList>
</MetaDataFile>
Where BVT1 and BVT2 are valid test types defined in the HelloWorld.vsmdi file.
MetaDataFile - Full path to test metadata file.
TestList - The test list in the selected metadata file to run.
Please note that you must specify the .vsmdi file relative to $(SolutionRoot)
-->
<MetaDataFile Include="$(SolutionRoot)\ConsoleApplication7\ConsoleApplication7.vsmdi">
<TestList>P1 Tests;P2 Tests</TestList>
</MetaDataFile>
</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>