Overriding the ToolsVersion for Your Projects in Team Build

Recently I got a question about how to compile projects against multiple .NET Frameworks (e.g. 2.0 and 3.5) in Team Build.  MSBuild added support for this sort of thing in the 3.5 framework (they call it multi-targeting), but it is not particularly easy to take advantage of it in Team Build.  Here's the text of my response:

MSBuild added a bunch of different mechanisms for specifying the ToolsVersion (i.e. Framework) to be used when building particular projects.  In particular:

  • You can specify the ToolsVersion at the command-line when invoking msbuild.exe using the /tv option.
  • You can specify the ToolsVersion when invoking the MSBuild task using the ToolsVersion property.
  • You can specify a default ToolsVersion using the ToolsVersion attribute on the <Project> element for a particular project.
  • You can specify a ToolsVersion metadata item when using the MSBuild task to build an item group containing one or more projects – see https://blogs.msdn.com/msbuild/archive/2007/07/17/toolsversion-metadata-for-items-used-in-msbuild-task-s.aspx.

None of these are particularly easy to use in combination with Team Build, unfortunately.  The /tv option will not work, since the Team Build traversal project (TfsBuild.proj + Microsoft.TeamFoundation.Build.targets) uses 3.5 syntax, and will not build using the 2.0 engine.  We don’t expose the ability to set ToolsVersion directly on the MSBuild task when we invoke your solution (we didn’t realize this would be useful, given the existence of the ToolsVersion attribute on the <Project> element).  And we build solutions by invoking the MSBuild task on a property containing the name of your solution, so you cannot use a ToolsVersion metadata item.

The simplest workaround here takes advantage of the properties made available by MSBuild in its generated *.sln.cache files (these are the msbuild projects created to build solutions – they used to be in-memory only, but are now typically saved to disk and reused).  In particular, something like the following should do the trick:

   <ItemGroup>
    <SolutionToBuild Include="SomeSolution.sln">
      <Properties>ProjectToolsVersion=3.5</Properties>
    </SolutionToBuild>
    <SolutionToBuild Include="SomeSolution.sln">
      <Properties>ProjectToolsVersion=2.0</Properties>
    </SolutionToBuild>
  </ItemGroup>

The idea is to build the solution twice, specifying the tools version to be used each time using the ProjectToolsVersion property exposed in the *.sln.cache file. 

There are all kinds of wacky properties exposed in the *.sln.cache files - I highly recommend taking a look at these if you're looking to do anything fancy with individual solutions in Team Build.  If you run msbuild 3.5 from the command-line on a solution, you will find a *.sln.cache file sitting alongside the *.sln file when you are done - open it up in VS (or notepad, if you're not into fancy formatting) and have a look!