Search code examples
asp.net-3.5tfsbuildaspnet-compiler

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?


Solution

  • 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.