How to call a custom target after building each individual solution (sln) in Team Build?

 

Issue: -

I would like to invoke a specific target just after each solution is compiled. Unfortunately, team build performs a build including all solution in an atomic MSBuild call. I don’t want to make changes in my sln or csproj files but I am OK with modifying the tfsbuild.proj. What can I do to get the desired behavior?

 

Solution:-

This is possible in Team Build but is slightly complicated. You need to override the default CoreCompile target with your own version.

To give you the idea of what needs to be done, please refer this simplified script -

In this proj file, all the three projects (classlibrary1, classlibrary2, classlibrary3) are build for each configuration (Debug|AnyCPU, Release|AnyCPU) and the target CommonPostCompileStep is invoked after project is build for a particular configuration.  

 

<?xml version="1.0" encoding="utf-8"?>

<Project xmlns="https://schemas.microsoft.com/developer/msbuild/2003">

 

  <ItemGroup>

    <!-- List of projects to build -->

    <SolutionToBuild Include=".\ClassLibrary1\ClassLibrary1.sln" />

    <SolutionToBuild Include=".\ClassLibrary2\ClassLibrary2.sln" />

    <SolutionToBuild Include=".\ClassLibrary2\ClassLibrary2.sln" />

  </ItemGroup>

 

  <ItemGroup>

    <!-- Configuration to build -->

    <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>

  </ItemGroup>

 

  <Target Name="CoreCompile">

 

    <!-- Call the compile for each configuration -->

    <MSBuild

            Projects="$(MSBuildProjectFile)"

            Targets="RunCoreCompileWithConfiguration"

            Properties="Platform=%(ConfigurationToBuild.PlatformToBuild);Flavor=%(ConfigurationToBuild.FlavorToBuild);" />

  </Target>

  <Target Name="RunCoreCompileWithConfiguration">

 

    <!-- Call the compile for each project (per configuration), with your custom target -->

    <MSBuild

            Projects="$(MSBuildProjectFile)"

        Targets="RunCoreCompileForProject; CommonPostCompileStep"

            RunEachTargetSeparately="true"

            Properties="Platform=$(Platform);Flavor=$(Flavor);

ProjectToBuild=%(SolutionToBuild.Identity)" />

  </Target>

  <Target Name="RunCoreCompileForProject">

 

    <!-- Actual compile for a particular {configuration, project} pair -->

    <MSBuild

          Condition="'$(ProjectToBuild)'!='' "

          Projects="$(ProjectToBuild)"

          Properties="Configuration=$(Flavor);Platform=$(Platform);

SkipInvalidConfigurations=true;"

          Targets="ReBuild" />

  </Target>

  <Target Name="CommonPostCompileStep">

 

    <!-- Custom target to execute after building each project/solution-->

    <Message Text="Common postcompile target to execute after building solution"/>

  </Target>

 

 

</Project>

 

 

 

 

Note the following issues when making changes in your tfsbuild.proj file

 

  1. In msbuild task, Projects property – use $(SolutionRoot)\TeamBuildTypes\$(BuildType)\tfsbuild.proj instead of using "$(MSBuildProjectFile). Otherwise the teambuild logger will crash
  2. As you are spanning msbuild process for each project (per configuration) separately, this will have performance implication.
  3. If making change in your csproj file to include custom post build step is easier. However if you are building large number of solutions and making changes in individual files is painful then this might be an better approach.

Please feel free to use this example as hint and customize your CoreCompile target.