views:

2593

answers:

3

I have an odd issue with how msbuild is behaving with a VS2008 Web Deployment Project and would like to know why it seems to randomly misbehave.

I need to remove a number of files from a deployment folder that should only exist in my development environment. The files have been generated by the web application during dev/testing and are not included in my Visual Studio project/solution.

The configuration I am using is as follows:

<!--  Partial extract from Microsoft Visual Studio 2008 Web Deployment Project -->
<ItemGroup>
  <DeleteAfterBuild Include="$(OutputPath)data\errors\*.xml" /> <!-- Folder 1: 36 files -->
  <DeleteAfterBuild Include="$(OutputPath)data\logos\*.*" />    <!-- Folder 2: 2 files -->
  <DeleteAfterBuild Include="$(OutputPath)banners\*.*" />       <!-- Folder 3: 1 file -->
</ItemGroup>

<Target Name="AfterBuild">
  <Message Text="------ AfterBuild process starting ------" Importance="high" />
    <Delete Files="@(DeleteAfterBuild)">
      <Output TaskParameter="DeletedFiles" PropertyName="deleted" />
    </Delete>
    <Message Text="DELETED FILES: $(deleted)" Importance="high" />
  <Message Text="------ AfterBuild process complete ------" Importance="high" />
</Target>

The problem I have is that when I do a build/rebuild of the Web Deployment Project it "sometimes" removes all the files but other times it will not remove anything! Or it will remove only one or two of the three folders in the DeleteAfterBuild item group. There seems to be no consistency in when the build process decides to remove the files or not.

When I've edited the configuration to include only Folder 1 (for example), it removes all the files correctly. Then adding Folder 2 and 3, it starts removing all the files as I want. Then, seeming at random times, I'll rebuild the project and it won't remove any of the files!

I have tried moving these items to the ExcludeFromBuild item group (which is probably where it should be) but it gives me the same unpredictable result.

Has anyone experienced this? Am I doing something wrong? Why does this happen?

+2  A: 

Hi, Check out the blog entry I just posted MSBuild: Item & Property Evaluation, I think it might help you. Let me know if not.

Sayed Ibrahim Hashimi
Thanks Sayed, I started to realise what was happening and found some pages explaining when the ItemGroup evaluation happens (much of the answers were written by you in 2005!) - just found your reply here too. Thank you. Finally I know why this looked unpredictable!
Alex
+10  A: 

The <ItemGroup> is evaluated when the script loads and before the <Target> has been processed.

There appears to be more than one way to do this correctly -

  1. Include the <ItemGroup> inside of the <Target> and it should be evaluated at the correct time. This will work with MS-Build v3.5+

  2. Use <CreateItem> to generate the item list

Example build script for this -

<!-- Using ItemGroup -->
<Target Name="AfterBuild">
  <ItemGroup>
    <DeleteAfterBuild Include="$(OutputPath)data\errors\*.xml" />
  </ItemGroup>
  <Delete Files="@(DeleteAfterBuild)" />
</Target>

<!-- Using CreateItem -->
<Target Name="AfterBuild">
  <CreateItem Include="$(OutputPath)data\errors\*.xml">
    <Output TaskParameter="Include" ItemName="DeleteAfterBuild"/>
  </CreateItem>
  <Delete Files="@(DeleteAfterBuild)" />
</Target>

To explain why the delete process was generating 'unpredictable' results -

  1. Start with a clean build output path
  2. Build #1 - build the Web Deployment Project. @(DeleteAfterBuild) will evaluate with no files as the files don't exist in the $(OutputPath) folder and will not delete any files as part of the AfterBuild target
  3. Build #2 - build the Web Deployment Project. @(DeleteAfterBuild) will evaluate with all the expected files in the $(OutputPath) folder and will remove the files as part of the AfterBuild target
  4. Basically we're now back to stage 2. Repeat. The results are of-course predictable - scratch your head no longer.

Reference material: How To: Create Item Groups on the Fly, Delayed evaluation of items in MSBUILD file

Alex
+1 - I was having the exact same head-scratch. This was a perfect answer. Thanks!
spot
A: 

I realise this has already been answered but I thought I would add my 5 cents. For a Web Deployment project there's no need to use the provided targets, just add an item group containing ExcludeFromBuild elements. I've provided the relevant section from the bottom of my deployment project file for reference.

  <Import Project="$(MSBuildExtensionsPath)\Microsoft\WebDeployment\v9.0\Microsoft.WebDeployment.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.WebDeployment.targets.
  <Target Name="BeforeBuild">
  </Target>
  <Target Name="BeforeMerge">
  </Target>
  <Target Name="AfterMerge">
  </Target>
  <Target Name="AfterBuild">
  </Target>
  -->
  <ItemGroup>
    <ExcludeFromBuild Include="$(SourceWebPhysicalPath)\obj\**\*.*" />
    <ExcludeFromBuild Include="$(SourceWebPhysicalPath)\Properties\**\*.*" />
    <ExcludeFromBuild Include="$(SourceWebPhysicalPath)\**\*.csproj*" />
    <ExcludeFromBuild Include="$(SourceWebPhysicalPath)\**\*.resx" />
    <ExcludeFromBuild Include="$(SourceWebPhysicalPath)\**\*.Publish.xml" />
    <ExcludeFromBuild Include="$(SourceWebPhysicalPath)\LocalTestRun.testrunconfig" />
    <ExcludeFromBuild Include="$(SourceWebPhysicalPath)\TestResults\**\*.*" />
  </ItemGroup>
David Clarke