Overriding MSBuildExtensionsPath in the MSBuild task is flaky

Stuart Lange picture Stuart Lange · May 20, 2010 · Viewed 15.7k times · Source

This is already cross-posted at MS Connect:

https://connect.microsoft.com/VisualStudio/feedback/details/560451

I am attempting to override the property $(MSBuildExtensionsPath) when building a solution containing a C# web application project via msbuild. I am doing this because a web application csproj file imports the file "$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v9.0\WebApplications\Microsoft.WebApplication.targets". This file is installed by Visual Studio to the standard $(MSBuildExtensionsPath) location (C:\Program Files\MSBuild). I would like to eliminate the dependency on this file being installed on the machine (I would like to keep my build servers as "clean" as possible). In order to do this, I would like to include the Microsoft.WebApplication.targets in source control with my project, and then override $(MSBuildExtensionsPath) so that the csproj will import this included version of Microsoft.WebApplication.targets. This approach allows me to remove the dependency without requiring me to manually modify the web application csproj file.

This scheme works fine when I build my solution file from the command line, supplying the custom value of $(MSBuildExtensionsPath) at the command line to msbuild via the /p flag. However, if I attempt to build the solution using the MSBuild task in a custom msbuild project file (overriding MSBuildExtensionsPath using the "Properties" attribute), it fails because the web app csproj file is attempting to import the Microsoft.WebApplication.targets from the "standard" Microsoft.WebApplication.targets location (C:\Program Files\MSBuild). Notably, if I run msbuild using the "Exec" task in my custom project file, it works. Even more notably, the FIRST time I run the build using the "MSBuild" task AFTER I have run the build using the "EXEC" task (or directly from the command line), the build works.

Has anyone seen behavior like this before? Am I crazy? Is anyone aware of the root cause of this problem, a possible workaround, or whether this is a legitimate bug in MSBuild?

Steps to Reproduce:

1) Create a new empty solution in MSVS 2008 (Fake.sln)

2) Add a new C# web application to the solution (WebApplication1.csproj)

3) Close MSVS

4) Copy the contents of "C:\Program Files\MSBuild\" to a directory called "MSBuildExtensions" in the directory containing your solution.

5) rename the directory "C:\Program Files\MSBuild\Microsoft\VisualStudio\v9.0\WebApplications" so that WebApplication1.csproj will not be able to import Microsoft.WebApplication.targets from that location.

6) Create a custom MSBuild project file called "TestBuild.proj" in the same directory as the solution. It should have the following content:

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

<PropertyGroup>
    <MSBuildExtensionsPath>$(MSBuildProjectDirectory)\MSBuildExtensions\</MSBuildExtensionsPath>
    <BuildThis>Fake.sln</BuildThis>
</PropertyGroup>

<Target Name="BuildMSBuild">
    <MSBuild Projects="$(BuildThis)" Properties="MSBuildExtensionsPath=$(MSBuildExtensionsPath);" Targets="Clean" />
    <MSBuild Projects="$(BuildThis)" Properties="MSBuildExtensionsPath=$(MSBuildExtensionsPath);"/>
</Target>

</Project>

7) execute "msbuild TestBuild.proj" from a MSVS command prompt (note: the build may succeed the first time, but will fail if you run more than once)

Answer

Chuck England picture Chuck England · Jun 25, 2010

Did you try setting the environment variable MSBuildExtensionPath in the CMD prompt and then running your build?

For example:

C:\> SET MSBuildExtensionsPath=C:\My\MSBuild\Extensons

Then on this project file:

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Target Name="Build">
    <Message Text='MSBuildExtensionsPath="$(MSBuildExtensionsPath)"' />
  </Target>
</Project>

you will get the following output:

c:\Users\chuckeng\Desktop\ConsoleApplication1>"C:\Windows\Microsoft.NET\Framework\v3.5\MSBuild.exe" my.proj
Microsoft (R) Build Engine Version 3.5.30729.4926
[Microsoft .NET Framework, Version 2.0.50727.4927]
Copyright (C) Microsoft Corporation 2007. All rights reserved.

Build started 6/25/2010 1:04:05 PM.
Project "c:\my.proj" on node 0 (default targets).
  MSBuildExtensionsPath="C:\My\MSBuild\Extensons"
Done Building Project "c:\my.proj" (default targets).


Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:00.03

This works from v4.0 as well. Although, support is generally better in v4.0 for things like this. And, v4.0 is 100% backward compatible (bugs not withstanding). So, you can build your v3.5 and prior projects with v4.0. Just select ToolsVersion 3.5.

msbuild my.proj /tv:3.5

Hope this helps...

Chuck England Visual Studio Program Manager - MSBuild