Team Foundation Server 2010 - Managing TFS Build Drop Location

Discussion Items:

  • Overview
  • TFSBuild DropLocation
  • Calling TFSBuild Activity Multiple Times
  • Preventing DropLocation Corruption
  • Conclusion

Overview

In Team Foundation Server 2010 (TFS2010), the UpgradeTemplate.xaml allows you to call a TFSBuild custom workflow activity that utilizes the same build scripts, properties, etc. used in Team Foundation Server 2008 (TFS2008)  to compile your projects and solutions.  You may find yourself wanting to make different versions of the UpgradeTemplate.xaml to be able to make multiple calls of theTFSBuild workflow activity within the same build, and may wonder how you can leverage the same drop location.  As you will see, this requires a little extra work than you might expect. 

 

TFSBuild DropLocation

When writing team build scripts in Team Foundation Server 2008 (TFS2008), we relied on the $(DropLocation) property to know where to handle the output of the build. For example, we may copy various 3rd party assemblies there, do some configuration file changes, restructure the output, and any variety of changes to this final build location.  By default, the drop location starts off as the share location passed in to the queue build window: 

 

The DropLocation property is captured internally and created within the Microsoft.TeamFoundation.Build.targets file (i.e. C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\TeamBuild\Microsoft.TeamFoundation.Build.targets).  So when the DropLocation is first constructed it starts with \\server\share and is pulled from the TFS database using the GetBuildProperties custom task within the InitializeBuildProperties target:

 

 

 

You can override the build number that gets added to the end of the original drop location using the skeleton target BuildNumberOverrideTarget that gets called after initializing these properties, but before concatenating the original $(DropLocation) property with the $(BuildNumber) property forming the new updated $(DropLocation) property: 

 

 

 

In the InitializeEndToEndIteration MSBuild target, the original $(DropLocation) is added to the $(BuildNumber) and is then updated in the TFS database:

 

 

 

 This is how your first build of the day would end up with the $(DropLocation) property being: \\server\share\MyBuildDefinition_20110407.1.  The $(BuildNumber) piece is the part in yellow.

 

 

Updating Drop Location Using Internal Targets

 

In MSBuild scripts, you can update the final drop location by either overriding the BuildNumberOverrideTarget and creating different properties like this:
(in TFSBuild.proj or other imported .proj file)

 

Since my team project is brandoh, this would create a droplocation such as:  \\server\share\brandoh_MyBuildDefinition_20110407.1  . 

 

 

Updating Drop Location Using Custom C# Tasks 

 

The next challenge might come if you want to do some sort of special modification to the droplocation utilizing values derived from some custom task that you wrote.  In this case, this may not work because the assemblies containing your custom tasks will not exist in the workspace yet, because these internal targets take place before the get (before downloading files from source control).  In this case you need to update the drop location registered in the database, after the get.

 

You may have noticed the AfterGet target skeleton target, which is an empty target that you can override which gets called in the Microsoft.TeamFoundation.Build.targets file. Since, I have shown some example custom targets to run (RunTarget1, RunTarget2, and RunTarget3), I will use RunTarget1 to demo how to perform an additional custom update:

 

 

 

Basically, I got today's date and created a new date-based folder to store builds in and then updated the database with the new drop location. 

 

Important: You may wonder why the $(DropLocation) property is the original droplocation in this context and not the one modified earlier in the InitializeEndToEndIteration target.  The answer to this question is somewhat complex, but basically, custom properties only have a scope that pertains to the group of targets within a DependsOnTargets target.  Using the AfterGet example above, the RunTarget2 and RunTarget3 targets will have access to custom properties created in RunTarget1, but no other targets will have access to any other property of other MSBuild.exe target calls, unless they are passed in internally via the command-line (i.e. msbuild.exe targets.proj /p:DropLocation=[ModifiedDropLocation]) or through an MSBuild custom task call.  When Team Build re-enters it's own internal targets, it loses scope of modified properties not pulled again from the database.

 

Solution: So how do you gain access to modified properties when they lose scope through-out different portions of the build?  This is a great question!  What I recommend as a possible solution to this is to have a simple InitializeVariables target that you call at the beginning of all DependsOnTargets target lists like this:

 

 

 

As you can see, when the build stops compiling solutions/projects, Team Build calls the AfterDropBuild target which will start by running the InitializeVariables target to setup any custom properties derived from custom tasks.  Now, these properties will exist in their correct form throughout all other targets in that DependsOnTargets attribute (RunTarget4, RunTarget5, and RunTarget6).

 

CustomPropertiesForBuild: You might be wondering what this property is.  This property is an internal property that when you group your property = value semi-colon delimited pairs within it (i.e.  a=1;b=2;c=3, etc.), that property gets passed throughout many of the internal targets via msbuild calls (i.e. BeforeCompileSolution, AfterCompileSolution, etc.) and makes them even more available.

 

Calling TFSBuild Activity Multiple Times

Everything discussed so far also applies to TFS2008 as well as the UpgradeTemplate in TFS2010.  So what happens if you want to create a new version of the UpgradeTemplate.xaml in TFS2010, where you call the TFSBuild workflow activity more than once?  (i.e. when trying to run different builds in parallel or trying to run TFSBuild multiple times in the same build but in different ways each time)

 

 

 

What happens is that the DropLocation will get corrupted, because it will retrieve the current value in the database and then use it to create a new value appending the build number again.  You will start seeing repeating build numbers like this:   \\server\share\brandoh_MyBuildDefinition_20110407.1\brandoh_MyBuildDefinition_20110407.1\brandoh_MyBuildDefinition_20110407.1.

 

 

Preventing DropLocation Corruption

There is hope!  One way to prevent DropLocation corruption is by first capturing the "original" droplocation before adding the build number and then passing it into the TFSBuild workflow activity teaching it to correctly determine it's droplocation based on what was passed in, NOT based on what is internally pulled from the database.

 

Before we can pass in the original drop location into the TFSBuild activity we first need to get the original droplocation in the workflow.  You can create a variable to capture the original drop location:

 

 

Then use the assign activity assign it the value of BuildDetail.DropLocationRoot at the beginning of the build:

 

 

 

Then you will need to pass this value into the TFSBuild workflow activities you want to consider this original droplocation:

 

 

 

 

Then you can change your msbuild code for RunTarget1 to set the droplocation based on whether or not this value is passed in.  As you can see, if the droplocation property is empty, it means the value wasn't passed in, so we can let the build update as normal (see RunTarget1 above to compare).  If it's not empty, we can use the OriginalDropLocation value to correctly set the droplocation rather than use the value that is pulled internally from the database in the Microsoft.TeamFoundation.Build.targets file: 

 

 

 

Conclusion

By leveraging the principles discussed in this blog you can effectively manage the drop location in a variety of ways, including reflecting the desired drop location in multiple TFSBuild workflow activities.  The ability to utilize the same drop location in different workflow activities lets you utilize modified versions of the UpgradeTemplate.xaml to employ parallel build activities or to engage entire build processes sequentially but in different ways. 

 

In the future, if I get a chance, I will try and show how you can do some really cool parallel build processes. ;-)