Creating Build Definitions with the TFS2008 Object Model

September 28, 2007 at 5:48 am | In Team Foundation Server |

Recently I’ve been working on a project that requires many team build definitions to be created using the TFS Object Model (OM).

TFS2005 Team Build

In TFS2005 this could be achieved using the classes available under the Microsoft.TeamFoundation.VersionControl.Client namespace.

This is because a “Team Build Type” was just three files sitting in a well-known path e.g. $/MyTeamProject/TeamBuildTypes/MyBuildType/TfsBuild.proj

  1. TFSBuild.proj – The MSBuild project file that Team Build uses,
  2. TFSBuild.rsp – The MSBuild response file for passing parameters, and
  3. WorkspaceMapping.xml – The workspace definition to map for the build.

TFS2008 Team Build

In TFS2008, there some great changes with Team Builds:

  • “Team Build Types” are now called “Build Definitions”
  • The WorkspaceMapping.xml file is gone
  • Build Definitions are stored in the TFS databases (don’t touch them there! use the OM)
  • There’s a comprehensive Team Build Object Model
  • We now have “Build Agents”

This rest of this post will focus on how to create a new Build Definition using the Microsoft.TeamFoundation.Build.* object model.

Prerequisites

  • You need Visual Studio Team Explorer 2008 installed. Get it off the Team Foundation Server 2008 media under the \tfc directory.
  • Add references to the OM assemblies located in C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies
    • Microsoft.TeamFoundation.dll
    • Microsoft.TeamFoundation.Build.Client.dll
    • Microsoft.TeamFoundation.Build.Common.dll
    • Microsoft.TeamFoundation.Client.dll
    • Microsoft.TeamFoundation.Common.dll
    • Microsoft.TeamFoundation.VersionControl.Client.dll (optional)
    • Microsoft.TeamFoundation.VersionControl.Common.dll (optional)
    • System.Windows.Forms (optional – I’m using SystemInformation.ComputerName)

Some Helper Methods

The TFS OM uses the service locator pattern. You get a reference to the TeamFoundationServer object then call the GetService() method with the type of service you’re after (VersionControlServer, ICommonStructureService, etc).

Typically it looks something like this:

VersionControlServer vcs = (VersionControlServer)tfs.GetService(typeof(VersionControlServer));

As with any development, over time you build up a collection of useful routines that you use all the time. These two methods are part of any TFS utility I write. To make life easier we can use generics:

private static T GetService<T>()
{
   
return (T)GetTeamFoundationServer().GetService(typeof

(T));
}

private static TeamFoundationServer GetTeamFoundationServer()
{
    TeamFoundationServer tfs = TeamFoundationServerFactory.GetServer( SERVER_URL,
new UICredentialsProvider() );
    tfs.Authenticate();
   
return tfs;
}

Note: SERVER_URL and TEAM_PROJECT should be replaced with appropriate values or retrived from a configuration store.

IBuildDefinition

What we are ultimately trying to create is a valid Microsoft.TeamFoundation.Build.Client.IBuildDefinition.

IBuildDefinition buildDefinition = GetService<IBuildServer>().CreateBuildDefinition(TEAM_PROJECT);
buildDefinition.Name = “My Build Definition”;
buildDefinition.ConfigurationFolderPath =
“$/MyTeamProject/SolutionDir”;
buildDefinition.DefaultBuildAgent = GetSingleBuildAgent(TEAM_PROJECT);
buildDefinition.DefaultDropLocation =
string.Format(@”\\{0}\Drops”

, buildDefinition.DefaultBuildAgent.MachineName);
buildDefinition.Save();

 

The properties of a build definition a self-explanatory. ConfigurationFolderPath is the path where Team Build should expect to find the TFSBuild.proj file.

  • If you don’t specify a DefaultBuildAgent you get an error:
    • TF215055: You must specify a default build agent.
  • If you don’t specify a DefaultDropLocation you get an error as well:
    • TF215056: You must specify a default build drop location.

So how do we get a Default Build Agent?

IBuildAgent

We can use the QueryBuildAgents(string teamProject) method on the IBuildServer service to get a list of build agents configured for a specific team project and return the first one it finds.

/// <summary>
/// Gets the first build agent for specified team project.
/// Creates and gets a dummy build agent if none are found.
/// </summary>
private static IBuildAgent GetSingleBuildAgent(string teamProject)
{
    IBuildServer buildServer = GetService<IBuildServer>();
    IBuildAgent[] buildAgents = buildServer.QueryBuildAgents(teamProject);

    if (buildAgents.Length == 0)
    {
        buildAgents = buildServer.SaveBuildAgents(new IBuildAgent[] { GetDummyBuildAgent(teamProject) });
    }

    return buildAgents[0];
}

If there are no Build Agents configured for the specified team project, then we need to create a new build agent.

This is done by calling the CreateBuildAgent(string teamProject) method on the IBuildServer service. (Who would’ve guessed!?)

/// <summary>
/// Creates a build agent for the specified team project
/// using the current computer name and the default build directory.
/// </summary>
private static IBuildAgent GetDummyBuildAgent(string teamProject)
{
    IBuildAgent buildAgent = GetService<IBuildServer>().CreateBuildAgent(teamProject);

    buildAgent.Name = SystemInformation.ComputerName;
    buildAgent.MachineName = SystemInformation.ComputerName;
    buildAgent.BuildDirectory = @”$(Temp)\$(BuildDefinitionPath)”;
    buildAgent.Description = “Dummy build agent - may not actually exist.”;

    return buildAgent;
}

We add some intelligent defaults to the dummy build agent and a description to warn others who might try and use it.

IProjectFile

With the code above, we have enough to create a new Build Definition and the server is happy. However when we try and run the build, it complains that it can’t find a TFSBuild.proj file.

To my surprise, you can also create a TFSBuild.proj file using the OM.

First of all, we want to check and see if one exists.

/// <summary>
/// Determine whether a TFSBuild.proj file exists in the specified build configuration path
/// </summary>
/// <param name=”configurationFolderPath”></param>
private static bool ProjectFileExists(string configurationFolderPath)
{
    string projectFilePath = VersionControlPath.Combine(configurationFolderPath, BuildConstants.ProjectFileName);
    return GetService<VersionControlServer>().ServerItemExists(projectFilePath, VersionSpec.Latest, DeletedState.NonDeleted, ItemType.File);
}

Then we can call the IBuildDefinition.CreateProjectFile() method and get a new IProjectFile.

if (!ProjectFileExists(buildDefinition.ConfigurationFolderPath))
{
    IProjectFile projectFile = buildDefinition.CreateProjectFile();
    //Optionally set a few properties on the project file.
    //projectFile.RunCodeAnalysis = CodeAnalysisRunType.Always;
    //projectFile.RunTest = true;
    projectFile.Save(buildDefinition.ConfigurationFolderPath);
}

When we call IProjectFile.Save(), the OM performs a silent checkin of TFSBuild.proj to source control with the comment “Checking in new MSBuild Project and Response files”.

Now when we call IBuildDefinition.Save() and then try and kick off a build, we will have a valid Build Definition and an almost valid TFSBuild.proj file. All that you need to do now is modify TFSBuild.proj to include the <SolutionsToBuild> section.

If you need to automate this, it can also be done by calling IProjectFile.AddSolutionToBuild() which returns a new ISolutionToBuild.

Summary

Overall its a MUCH nicer experience creating and managing build definitions in TFS2008 over TFS2005. The Microsoft.TeamFoundation.Build.* namespace is now a first-class citizen.

Source Code: CreateBuildDefinitionProgram.cs (4 KB).

Go forth and build!

1 Comment »

RSS feed for comments on this post. TrackBack URI

  1. I used this code to create the build. But i cant see the build in the team explorer.
    Can i start this build?

    Comment by Priti — November 9, 2007 #

Leave a comment

XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Blog at WordPress.com. | Theme: Pool by Borja Fernandez.
Entries and comments feeds.