MSBuild Target Batching (For Each) Simplified

roger's picture

It's actually quite simple:

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0" DefaultTargets="Default">
   <ItemGroup>
      <ProjectsToPublish Include="AdminConsole\AdminConsole.csproj" />
      <ProjectsToPublish Include="AdminService\AdminService.csproj" />
   </ItemGroup>

   <Target Name="Default">
      <CallTarget Targets="PublishProjectOutput" />
   </Target>
   
   <Target Name="PublishProjectOutput" Inputs="@(ProjectsToPublish)" Outputs="%(Identity).Dummy">
      <Message Text="@(ProjectsToPublish)" />
   </Target>
</Project>

We set up an item group containing the items that we'd like to process. Our "Default" target is just to demonstrate that we don't need to do anything clever with CallTarget.

In order to get MSBuild to run the PublishProjectOutput target for each item in the item group, the necessary magic is in the Inputs and Outputs stuff. The trick is that the value in Outputs is the item metadata of the value in Inputs. That is: %(Identity) is actually treated as if it was %(ProjectsToPublish.Identity). MSBuild batches where the metadata values match. Since Identity is unique, each batch will contain a single item, giving us the "for each" behaviour we're looking for.

In order to get MSBuild to run the target at all, we need to specify the output files that will be generated. If we were to specify just %(Identity), MSBuild would decide that the outputs were up-to-date and would skip the target. So we dirty up the output by adding a fake extension to the filename. What this is doesn't particularly matter. %(Identity).Quack would work just as well (as long as you don't habitually have files called Foo.csproj.Quack, of course).

Comments

Thanks

Thanks for the post - simple and clear.

You saved me so much pain

You saved me so much pain with this article. Thanks.

Another way to achieve this

Another way to achieve this is to omit the Inputs attribute and set Outputs="%(ProjectsToPublish.Identity)"

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.