Team Build and Web Deployment Projects

Various issues arise when trying to use Team Build with Web Deployment Projects (which are a Visual Studio 2005 add-in available for download here).  I'm going to try and use this post as a repository for these issues and their workarounds, etc.

Team Build + Web Deployment Project + Web Application Project Combination is Broken

Background:  These three technologies don't play particularly well together, since Web Deployment Projects (WDPs) expect Web Application Projects (WAPs) to be compiled into a bin subdirectory directly under their source location.  As such, the WDP invocation of aspnet_compiler.exe fails to locate the compiled WAP and produces a rather confusing error message.

Symptoms:  You will see an error message of the form

/WebApplication1.csproj/Default.aspx(1): error ASPPARSE: Could not load type 'WebApplication1._Default'.

Workaround:  There are three potential workarounds here:

  1. Point the web deployment project to the overridden location of the web application output - right click on the WDP in Solution Explorer, select Open Project File, and modify the value of the SourceWebPhysicalPath property.  You may want to conditionalize this property value such that the WDP will work both in the IDE and in a Team Build build. 

    In Team Build v1, the following will typically do the trick:

         <SourceWebPhysicalPath Condition=" '$(TeamBuildConstants)'=='' ">..\WebApplication1</SourceWebPhysicalPath>
        <SourceWebPhysicalPath Condition=" '$(TeamBuildConstants)'!='' ">$(OutDir)_PublishedWebsites\WebApplication1</SourceWebPhysicalPath>
    

    (...Where WebApplication1 is replaced with the name of your WAP.)

  2. In Orcas, you can tell Team Build to stop redirecting the outputs of the WAP, in which case the WDP should be able to locate them just as it would in a build within the IDE.  A discussion of this Orcas Team Build feature can be found in a previous blog post, here.  Note that using this approach you will need to make sure that all of the binaries (and other build artifacts) that you want copied to your drop location end up in the Team Build output directory (see the $(TeamBuildOutDir) property in the linked blog post).

  3. Copy the WAP outputs back to the WAPs bin subdirectory in the WAPs AfterBuild target.  See this blog post by Martijn Beenes for more information.

 Links:  
  • Forum post reporting the issue.
  • ASP.Net forum post that inspired the above workaround and has additional background info on this issue.  Note that the recommended solution will not quite work, since $(MSBuildProjectName) will give the name of the WDP rather than the WAP.  Thanks to Brian Lalonde for pointing this out (my recommended solution also originally had this issue, but has since been updated).

Team Build Orcas + Web Deployment Project + CleanCompilationOutputOnly

Background:  Team Build Orcas introduces the ability to do a "real" clean during a Team Build build.  That is, Team Build v1 just deleted the entire source tree during its Clean target, which prevented making a distinction between an incremental Get and an incremental Build (see here for more info).  Team Build Orcas, on the other hand, includes the property CleanCompilationOutputOnly, which when true causes Team Build to invoke the Clean target of each solution in the SolutionToBuild item group, rather than deleting the source tree.  This new property defaults to false, and will typically be set to true through the (also new) convenience property IncrementalGet rather than being set directly. 

Unfortunately, the Clean target of a web deployment project does not actually do anything on its own, since aspnet_compiler.exe, which it invokes, does not support doing a clean by itself (instead it supports incremental builds and, with the -c option, full rebuilds). 

Symptoms:  Your WDP will do an incremental build, rather than a full rebuild.

Workaround:  Set the Clean property to true for the solution containing your WDP.  This property is not used by standard MSBuild projects and thus should only affect your WDP.  Locate the appropriate solution in your SolutionToBuild item group and add Clean=true to its Properties metadata.  For example:

     <SolutionToBuild Include="$(BuildProjectFolderPath)/../../WDPSolution/WDPSolution.sln">
        <Targets></Targets>
        <Properties>Clean=true</Properties>
    </SolutionToBuild> 

Web Deployment Project + WAP + MSBuild 2.0 Solution Logic

Background:  MSBuild includes functionality to build Visual Studio solutions by converting them into MSBuild project files in memory and then executing these project files.  In the first version of MSBuild, included in the 2.0 .NET Framework that shipped with Visual Studio 2005, these in-memory project files were fairly simple and essentially just built the solution's projects in the order in which they were specified in the solution file (note that this is not the same as the order in which the project's are displayed in the IDE) - note that the standard MSBuild dependency analysis would still cause dependent projects to be built before their consumers.

Because Web Deployment Projects are typically added to solutions after their dependencies (i.e. after the Website or WAP that they are associated with), they will typically be built after all their dependencies.  If you have manually edited your solution, however, or have changed the project that a WDP is configured to build, etc. it is possible that this behavior of MSBuild will cause your WDP to be built before its dependencies, leading to an error.

Symptoms:  You will see an error message of the form

/WebApplication1.csproj/Default.aspx(1): error ASPPARSE: Could not load type 'WebApplication1._Default'.

Note that this is the same error message as the Team Build + WAP + WDP issue discussed above.  That issue should be much more common than this one, so typically you should investigate it first.  If this solution processing logic is the issue, you should see the WDP getting built before the WAP in your build log.

Workaround:  Open up your solution file in Notepad and move the WDP to the end of the list of projects (or at least to a position after its associated WAP). 

Orcas Beta 1 / Orcas Beta 2 and WDP

Background:  In the second version of MSBuild, included in the 3.5 .NET Framework that is shipping with Visual Studio 2008, the in-memory project files have gotten more complicated and build projects in an order based on MSBuild's dependency analysis (which looks at project references, etc.).  This logic was broken for web deployment projects, since they don't include project references to their associated web application projects.  In certain cases, then, this will result in MSBuild attempting to build web deployment projects before their dependencies have been built, leading to an error.

Symptoms:  Again, you will see an error message of the form

/WebApplication1.csproj/Default.aspx(1): error ASPPARSE: Could not load type 'WebApplication1._Default'.

Note once again that this is the same error message as the Team Build + WAP + WDP issue discussed above.  If you are running Orcas Beta 1 or Orcas Beta 2, however, this issue is more likely to be the one impacting your build and should be investigated first.

Workaround:  Set the environment variable MSBuildEmitSolution to one and build your solution from the command-line (msbuild.exe foo.sln /Build).  MSBuild will emit the in-memory project generated for your solution to a file called foo.sln.proj.  You will need to modify this project file such that your web application is built after its dependencies and then build the project rather than the solution.  That is, you will want to check the project into source control and modify your build script (TfsBuild.proj) to include foo.sln.proj in its SolutionToBuild item group in place of foo.sln.  This bug should be fixed in Orcas RTM (i.e. Visual Studio 2008), at which point you can go back to building the solution directly.

 

That's it so far.  If you run into other issues that you think deserve mention here, please let me know!