views:

357

answers:

3

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)

+1  A: 

It works fine if you override MSBuildExtensionsPath directly in the web app .csproj file.

<PropertyGroup>
  <MSBuildExtensionsPath>C:\Users\madgnome\Desktop\msbuild</MSBuildExtensionsPath>

  <!-- It works too with relative path -->
  <!--<MSBuildExtensionsPath>..\msbuild</MSBuildExtensionsPath>-->
</PropertyGroup>

<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
madgnome
Yes, that's right -- I have tried that. Sorry for not mentioning it in the post. I would prefer to not have to modify the actual csproj file, since I would like my build system to work properly with "default" projects as created by visual studio, and I don't want to have to deal with visual studio yelling at me (or other devs) when they open this "modified" csproj file.
Stuart Lange
+1  A: 

This is a bug in MSBuild 3.5 but it is fixed in MSBuild 4.

If you can, switch to MSBuild 4 (you still can compile your 3.5 projects), otherwise you'll have to override the property in the project file.

madgnome
+1  A: 

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:

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

Chuck England