Inline Tasks for MSBuild that work with TFS

Someone asked a question about how to do something in MSBuild the other day. They were using the UpgradeTemplate.xaml, so there build process was done almost entirely in MSBuild. What they wanted to do was fairly simple to do in a Task, but they didn't really want the overhead of creating and maintaining a Task assembly. So, I suggested an inline Task (new to MSBuild 4.0). Of course, the next question was, how can I use that with TFS. So, I started playing around and created this extremely simple inline task that prints out the Start Time of the build by reading it from the server.

This is a very simplified TfsBuild.proj that doesn't actually build anything - I took out the SolutionsToBuild and the ConfigurationsToBuild stuff. The TfsBuild.proj file looks like this:

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="DesktopBuild" xmlns="https://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
  <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\TeamBuild\Microsoft.TeamFoundation.Build.targets" />
  <ProjectExtensions>
    <!-- Team Foundation Build Version - DO NOT CHANGE -->
    <ProjectFileVersion>2</ProjectFileVersion>
  </ProjectExtensions>

  <UsingTask
    TaskName="PrintBuild"
    TaskFactory="CodeTaskFactory"
    AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
    <ParameterGroup>
      <TeamFoundationServerUrl Required="True"/>
      <BuildUri Required="True"/>
    </ParameterGroup>
    <Task>
      <Reference Include="mscorlib" />
      <Reference Include="Microsoft.TeamFoundation.Build.Client" />
      <Reference Include="Microsoft.TeamFoundation.Build.Common" />
      <Reference Include="Microsoft.TeamFoundation.Client" />

      <Using Namespace="System" />
      <Using Namespace="Microsoft.TeamFoundation.Build.Client" />
      <Using Namespace="Microsoft.TeamFoundation.Build.Common" />
      <Using Namespace="Microsoft.TeamFoundation.Client" />

      <Code Type="Fragment" Language="cs">

        <![CDATA[
           Log.LogMessage("parameters to PrintBuild", MessageImportance.High);
           Log.LogMessage(TeamFoundationServerUrl, MessageImportance.High);
           Log.LogMessage(BuildUri, MessageImportance.High);
           if (!String.IsNullOrEmpty(TeamFoundationServerUrl))
           {
               Uri serverUri = TfsTeamProjectCollection.GetFullyQualifiedUriForName(TeamFoundationServerUrl);
               // Attempt authentication early. On failure, this is where a good error message will be logged
               TfsTeamProjectCollection collection = new TfsTeamProjectCollection(serverUri);
               collection.Authenticate();             

               IBuildServer buildServer = (IBuildServer)collection.GetService(typeof(IBuildServer));
               IBuildDetail buildDetail = buildServer.GetBuild(new Uri(BuildUri));
               Log.LogMessage(buildDetail.StartTime.ToString(), MessageImportance.High);
           }
        ]]>

      </Code>
    </Task>
  </UsingTask>

<Target Name="BeforeEndToEndIteration">
      <PrintBuild TeamFoundationServerUrl="$(TeamFoundationServerUrl)" BuildUri="$(BuildUri)" />
  </Target>

</Project>

The blue highlighted part is where the Task is defined. If you want more information on the parts and pieces of the inline Task, see this article.

The yellow highlighted part is where the Task is called. Notice that I put it in an override of the BeforeEndToEndIteration. If you aren't familiar with editing a TfsBuild.proj file, this won't make any sense. But this is just a hook to put my targets into the overall build process. In this case, my targets executes very early in the process.

Here is the output in the log:

Build started 8/22/2011 2:14:07 PM.

Project "C:\Builds\1\Project One\TestMsBuild\BuildType\TFSBuild.proj" on node 1 (EndToEndIteration target(s)).

Project file contains ToolsVersion="3.5". This toolset is unknown or missing. You may be able to resolve this by installing the appropriate .NET Framework for this toolset. Treating the project as if it had ToolsVersion="4.0".

InitializeBuildProperties:

  GetBuildProperties TeamFoundationServerUrl="https://localhost:8080/tfs/DefaultCollection" BuildUri="vstfs:///Build/Build/8"

BeforeEndToEndIteration:

  parameters to PrintBuild

  https://localhost:8080/tfs/DefaultCollection

  vstfs:///Build/Build/8

  8/22/2011 2:13:52 PM

InitializeEndToEndIteration:

 

I know this was a short and useless example, but you can easily see how to Get the TFS url and the Build object. This should be enough to get you any information you need.

Good Luck and happy coding!

Comments

  • Anonymous
    August 22, 2011
    Cool example Jason!