Calling Custom Targets in Team Build, Part 3

I've done a couple of posts now on calling custom targets during a Team Build build - this one focused on calling custom targets in the solution/project being built, and this one focused on calling custom targets in your TfsBuild.proj file itself before/after the compilation of individual solutions or configurations.  In the past week I've gotten two questions on whether one can call custom targets before/after the compilation of individual projects within a solution.  That is, whether Microsoft.TeamFoundation.Build.targets supports Before/AfterCompileProject targets in the same way it supports Before/AfterCompileConfiguration and Before/AfterCompileSolution ones.

The short answer is that this is not really supported in Team Build 2008 (or 2005, for that matter), since by the time your solutions are getting built we have essentially ceded control to MSBuild.  There are three potential methods (that I could think of) for doing this sort of thing:

  1. You could specify the individual projects, rather than the solution that contains them, in the SolutionToBuild item group in your TfsBuild.proj file and then just use the Before/AfterCompileSolution targets to execute your custom logic.  This is only appropriate if your solutions are simple containers for your projects - in particular, if you don't have build order logic and/or project-to-project references in your solution that you would need to re-implement in your TfsBuild.proj file.
  2. You could use a custom MSBuild "traversal" project in place of your solution, and insert your custom logic into this traversal project before/after the individual projects are compiled.  The simplest thing here would be to start with the the project that MSBuild generates from your solution - in MSBuild 3.5 this project will be written to disk as a *.sln.cache file if you build your solution from the command-line.  The disadvantage to this approach is that you then have two files to maintain in parallel - your solution and your custom traversal project.  One possible workaround here would be to write a custom MSBuild task to generate this traversal project automatically as part of the build.  If anybody is adventurous enough to attempt this task and generous enough to share, please let me know and I'd be happy to post your efforts in a follow up. 
  3. Finally, you could put the before/after compile logic into the individual projects themselves, either directly or via in imported *.targets file.  Individual .NET projects have their own Before/AfterBuild extensibility targets that could be utilized for this purpose. 

This last option strikes me as the best one when including simplicity in the calculation...  The targets file would look something like this:

 <?xml version="1.0" encoding="utf-8"?>
<Project xmlns="https://schemas.microsoft.com/developer/msbuild/2003">

  <!-- Only execute this custom logic when the project is building under Team Build. -->
  <Target Name="AfterBuild" Condition=" '$(TeamBuildConstants)' == '_TEAM_BUILD_' ">
    <!-- Put your custom logic here. -->
  </Target>
  
</Project>

You would then check this targets file in alongside your TfsBuild.proj file and import it into your individual projects after the import of Microsoft.Common.targets using a relative path.  For example, the end of a *.csproj file might look like:

   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <Import Project="..\..\TeamBuildTypes\SomeDefinition\AfterCompileProject.targets"/>
</Project>