Steve's blog

A blog about what's going on in Analysis UK...

MSBuild Item List Iteration

Repeating a task for each item in a MSBuild item list is really easy if the task supports ; seperated lists, if not, well it's still easy but trying to remember the syntax is another matter!

This keeps catching me out and I've seen few examples of its use around so if for no other reason than as a reminder to myself here's how to iterate a list of items with MSBuild.

Define your item list as something like:

<ItemGroup>
<InstallerFiles Include="$(MSBuildProjectDirectory)\Setup\**\Release\*.msi" />
</ItemGroup>

In this example I have a folder .\Setup\ which contains multiple subfolders of different setup projects which generate msi files.

Method 1:

<Target Name="CopyOutput">
<Message Text="Copying installers to Build Output folder. Source = @(InstallerFiles)"/>
<Copy SourceFiles="@(InstallerFiles)" DestinationFolder="$(BuildOutputFolder)" />
</Target>

This example copies all of the msi files into the BuildOutputFolder.

This method relies on the Copy target to accept a “;” separated list of values. Sadly if you have a task that only takes a single value then your a little stuffed with the @ operator.

Method 2:

<Target Name="CopyOutput">
<Message Text="Copying installers to Build Output folder. Source = %(InstallerFiles.FullPath)"/>
<Copy SourceFiles="%(InstallerFiles.FullPath)" DestinationFolder="$(BuildOutputFolder)" />
</Target>

This method will call the Copy task once for each item in the InstallerFiles list. But that's not really a fair example as you can just use the @ operator for Copy.

Here's another example that's more appropriate.

<PropertyGroup>
<DeploymentLocalPath>$(CCNetWorkingDirectory)\Setup\</DeploymentLocalPath>
<VersionUpdater>VersionVDProj.exe</VersionUpdater>
<VerisonFile>$(CCNetWorkingDirectory)\Version.txt</VerisonFile>
</PropertyGroup>

<ItemGroup>
<SetupProjects Include="$(DeploymentLocalPath)**\*.vdproj" />
</ItemGroup>

<Target Name="Version">
<!-- Some bits to set MSIVersion normally go here and have been removed for clarity →

<Exec Command='$(VersionUpdater) -msi "%(SetupProjects.FullPath)" version=$(MSIVersion)'/>
</Target>

Here I use the VS.NET Deployment Project Version Updater (http://www.codeproject.com/KB/install/VersionVDProj.aspx) to update each of the setup projects with the MSIVersion number, but this could be any colsole application that took a filename.

The .FullPath is a “Wellknown Item Metadata” property. Theirs more listed on the MSDN page

Sara Ford also has an example of a good use of metadata for recursive copying (http://blogs.msdn.com/saraford/archive/2005/11/08/490445.aspx).

Theirs also an interesting MSBuild wiki over on Channel 9.