Andrew
Andrew

Reputation: 14457

How do I get Team Build 2010 to publish web applications as non-updatable?

I'm trying to switch our build from CruiseControl.NET running a custom .msbuild file to Team Build 2010. The application being compiled is a VS2008 solution with numerous projects, two of which are web projects.

Using DefaultTemplate.xaml, it appears that the two web projects are deployed to Binaries\_PublishedWebsites\(ProjectName). That default location is fine. However, the contents of the output directories appear to be updateable, as though aspnet_compiler.exe was called with -u, or as though an MSBuild <AspNetCompiler> task was used with Updateable="true". So, two questions:

  1. How do I make Team Build produce non-updateable output to the _PublishedWebsites directory?
  2. How can I also set the IIS VirtualPath as if I was doing the following in an MSBuild task:

    <AspNetCompiler Clean="true" Force="true" VirtualPath="/My-IIS-Virtual-Path" />
    

I have found in earlier troubleshooting that the only way I can get IIS 6 to serve a web service compiled with aspnet_compiler.exe in non-updateable mode is to specify the virtual path in the command, which is why I am asking about #2.

Edit:

Upon seeing the one answer thus far, I realized I should have been much clearer about what the issue is. I realize that, if I can do something in MSBuild, I can just call MSBuild from the build template. However, I am wondering a little more about how change what happens to copy the output to the _PublishedWebsites directory. "Find the task that copies the website and change it" would work well, except that I don't see what is actually copying the output into _PublishedWebsites. What I'm really wanting to do is to modify the step in the template that accomplishes this.

The build log references a compile target called _CopyWebApplication that appears to do the work of copying the files needed for a web application. However, I am unsure how to modify this compile target, as I do not see it anywhere in the build template nor in any file in the solution. Further, whatever runs _CopyWebApplication appears to be running it only for web application projects, not the many other projects in the solution. This is a good thing, except that I do not know where the logic exists that determines whether to use _CopyWebApplication.

Maybe there is some default MSBuild file that I am missing? Some build parameter that I could be using? How do I alter the aforementioned build step?

Upvotes: 4

Views: 3383

Answers (3)

Andrew
Andrew

Reputation: 14457

KMoraz pointed me in the right direction, and I had to do a few more things to get this working. I really did not want to edit the built-in .targets files, since that would create some maintenance problems down the line for any other developer who did not know what I did. I ended up editing the .csproj files for the two web applications to this, starting with the default <Import> element near the end of the file and ending before the default <ProjectExtensions> element:

  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
  <!-- Since this Import serves no real purpose in VS2008 under Team Build, I'm commenting it out to remove
       a _CopyWebApplication step that we don't want.
  <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v9.0\WebApplications\Microsoft.WebApplication.targets" />
  -->
  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
       Other similar extension points exist, see Microsoft.Common.targets.
  <Target Name="BeforeBuild">
  </Target>
  <Target Name="AfterBuild">
  </Target>
  -->
  <!-- Now, for Team Build, do the AspNetCompile steps ourselves. -->
  <PropertyGroup>
      <WebProjectOutputDir Condition="'$(OutDir)' != '$(OutputPath)'">$(OutDir)_CompiledWebsites\$(MSBuildProjectName)</WebProjectOutputDir>
  </PropertyGroup>
  <PropertyGroup>
      <BuildDependsOn>
          $(BuildDependsOn);
          DoAspNetCompile
      </BuildDependsOn>
  </PropertyGroup>
  <Target Name="DoAspNetCompile" Condition="'$(CompileWebsites)' == 'True' And '$(OutDir)' != '$(OutputPath)'">
      <Message Text="Performing AspNetCompile step for $(MSBuildProjectName)" />
      <Message Text="Output will have IIS virtual directory '$(IISVirtualPath)'" />
      <Message Text="ProjectDir is $(ProjectDir)" />
      <Message Text="IsDebug is $(IsDebug)" />
      <RemoveDir Directories="$(WebProjectOutputDir)" ContinueOnError="true" />
      <MakeDir Directories="$(WebProjectOutputDir)" />
      <!-- We need the /bin directory, populated with some DLLs and PDBs -->
      <CreateItem Include="$(OutDir)*.dll;$(OutDir)*.pdb">
          <Output TaskParameter="Include" ItemName="BinariesToCopy" />
      </CreateItem>
      <Copy DestinationFolder="$(ProjectDir)\bin" SourceFiles="@(BinariesToCopy)" />
      <AspNetCompiler Clean="True" Force="True" Debug="$(IsDebug)" Updateable="False" VirtualPath="$(IISVirtualPath)" PhysicalPath="$(ProjectDir)" TargetPath="$(WebProjectOutputDir)" />
  </Target>

Some explanations:

  1. It says right in the .csproj file, as you can see, that C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v9.0\WebApplications\Microsoft.WebApplication.targets is in use by default. I did not realize previously that was how MSBuild knew to use that .targets file. Since the only way not to use the target file's _CopyWebApplication target under VS2008 is not to use the file, and since the VS2010 version really did not help me either, I just commented out the import.
  2. Just to distinguish my output from the default, I changed the name of the output directory to _CompiledWebsites instead of _PublishedWebsites.
  3. I added the <BuildDependsOn> element so that any other project files down the road that modify the extension points do not disable my target.
  4. I do require that CompileWebsites is set to true (using something like the /p:CompileWebsites=true parameter passed to MSBuild, though within Team Build, there are also other ways to accomplish this if desired). That way, the default local build is unaffected.
  5. The <Message> elements are for debugging. I have set $(IsDebug) to True or False in the configurations near the top of the file. We have many configurations besides vanilla "Debug" and "Release", so this flag was necessary so I could tell AspNetCompiler whether to include debugging symbols. $(IISVirtualPath) is also set in the configurations near the top of the file. It is necessary so that IIS 6 will serve a web service compiled in this manner.
  6. AspNetCompiler cannot find the precompiled binaries where Team Build puts them by default, so I copy them to the bin\ directory of the project, where AspNetCompiler expects to find them.
  7. I did not have to do anything special with the .xaml template in Team Build to get this to work. I did have to ensure that the build definition includes the /p:CompileWebsites=True parameter. To accomplish this, right-click the build definition in the Team Explorer window (under Builds under your team project), click "Process", expand the "3. Advanced" entry, and enter /p:CompileWebsites=True for the line labeled "MSBuild Arguments".

If I had more than two projects that needed this build configuration, I would probably create a file with the DoAspNetCompile target and import that file.

Upvotes: 4

KMoraz
KMoraz

Reputation: 14164

All logic of the _CopyWebApplication target is defined in:

C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets

The target itself is rather simple but it carries a bunch of pre-conditions:

<Target Name="_CopyWebApplication" 
        Condition="!$(Disable_CopyWebApplication) And '$(OutDir)' != '$(OutputPath)'" 
        DependsOnTargets="$(_CopyWebApplicationDependsOn)">

  <CallTarget Condition="'$(OnAfter_CopyWebApplication)' != ''" Targets="$(OnAfter_CopyWebApplication)" RunEachTargetSeparately="true" />

</Target>

You can control the process once getting to the desired set of flags. The following are the defaults:

<WebProjectOutputDirInsideProjectDefault>True</WebProjectOutputDirInsideProjectDefault>
<WebProjectOutputDirInsideProjectDefault  Condition="('$(OutDir)' != '$(OutputPath)') Or ('$(IsDesktopBuild)' == 'False')" >False</WebProjectOutputDirInsideProjectDefault>
<DisableLinkInCopyWebApplicaton Condition="'$(DisableLinkInCopyWebApplicaton)'==''">False</DisableLinkInCopyWebApplicaton>
<Disable_CopyWebApplication Condition="'$(Disable_CopyWebApplication)' == ''">False</Disable_CopyWebApplication>
<UseWPP_CopyWebApplication Condition="'$(UseWPP_CopyWebApplication)' == ''">False</UseWPP_CopyWebApplication>
<CleanWebProjectOutputDir>True</CleanWebProjectOutputDir>
<CleanWebProjectOutputDir Condition="$(WebProjectOutputDirInsideProject)" >False</CleanWebProjectOutputDir>

Upvotes: 4

Duat Le
Duat Le

Reputation: 3596

The DefaultTemplate.xaml in TFS 2010 Build still uses MSBuild to build your projects, so if the two things that you list can be done with MSBuild.exe, they can be done with the 2010 build process. All you need to do is to add the MSBuild arguments you want to the Process Parameters of your build definition. More details about updating your build definition can be found on MSDN.

Upvotes: 2

Related Questions