Monday, May 24, 2010

MSBuild Batching & Item Metadata

When it comes to build scripts, I'm in the NAnt camp. However, I recently found a scenario where it made more sense to use MSBuild and found the basics easy to understand because the ideas are the same as NAnt. One of the more interesting things in MSBuild is batching. Batching allows you to execute a task over a set of items in a foreach loop style.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Sample">
  
  <Target Name="Sample">
    <ItemGroup>
      <MyItem Include="#1">
        <Name>Item 1</Name>
        <Description>Description for Item 1</Description>
      </MyItem>
      <MyItem Include="#2">
        <Name>Item 2</Name>
        <Description>Description for Item 2</Description>
      </MyItem>
      <MyItem Include="#3">
        <Name>Item 3</Name>
        <Description>Description for Item 3</Description>
      </MyItem>
      <MyItem Include="#4">
        <Name>Item 4</Name>
        <Description>Description for Item 4</Description>
      </MyItem>
    </ItemGroup>

    <Message Text="-- Using '@' --"/>
    <Message Text="@(MyItem)" />
    <Message Text=" "/>
    <Message Text="-- Using '%' --"/>
    <Message Text="%(MyItem.Identity): %(MyItem.Name) - %(MyItem.Description)" />
  </Target>
  
</Project>
Running the above gives you:
-- Using '@'--
  #1;#2;#3;#4

  -- Using '%' --
  #1: Item 1 - Description for Item 1
  #2: Item 2 - Description for Item 2
  #3: Item 3 - Description for Item 3
  #4: Item 4 - Description for Item 4

The difference in the two is how the item group is prefixed. The '%' character tells MSBuild to expand the batch and iterate over each item in the batch; the '@' symbol tells MSBuild to join the items into one list. Also notice how I've used metadata (i.e. Name, Description) to have properties associated with each item that can be used in each iteration. The built in property 'Identity' returns whatever is in the 'Include' attribute of the item. One thing you should know is that the 'Include' attribute must be unique and is required if you are going to execute a batch.

Usefulness
In my scenario, I had a set of files that I needed copied from one location to another using xcopy.
<Target Name="RealWorldScenario">
    <ItemGroup>
      <Component Include="ProjectA" />
      <Component Include="ProjectB" />
      <Component Include="ProjectC" />
    </ItemGroup>

    <Exec Command="xcopy /Y $(BaseDir)\bin\%(Component.Identity)\*.dll $(Output)" />
    <Exec Command="xcopy /Y $(BaseDir)\bin\%(Component.Identity)\*.xml $(Output)" />
    <Exec Command="xcopy /Y $(BaseDir)\bin\%(Component.Identity)\*.config $(Output)" />
    
  </Target>

In my situation, I didn't want to duplicate the 3 copy commands for each component. I wanted to make it so that as new components are developed, you only have to add it to the item group.

No comments:

Post a Comment