Gotcha: MSBuild nested loops (double batching)

I admit I didn’t get it on my first, quick, reading of “Inside the Microsoft Build Engine: Using MSBuild and Team Foundation Build” that MSBuild batching is somewhat counter-intuitive.

I needed to have a nested loop in my script, like this pseudo code:

 foreach (solution in solutionItemGroup) {
    foreach (configuration in solution.Configurations) {
        build solution's package using configuration
    }
}

It turned out that a naive solution doesn’t work: two different ItemGroup in the same Task are interpreted by MSBuild, like the following pseudo code:

 foreach (solution in solutionItemGroup) {
    build solution's package using configuration
}
foreach (configuration in solution.Configurations) {
    build solution's package using configuration
}

The solution I found is to work at the target level using a local property, an MSBuild 3.5 feature. MSBuild creates a first batch for the property, the outer loop, the a second, nested batch for the task at hand; some real code may better explain this.

 <Target Name="DeployToLocalServer" DependsOnTargets="MyResolveSolutionPaths">
    <PropertyGroup>
      <CurrentConfiguration>%(ConfigurationToBuild.FlavorToBuild)</CurrentConfiguration>
    </PropertyGroup>
    <MSBuild Projects="%(LocalSolutionToBuild.RootDir)%(LocalSolutionToBuild.Directory)%(LocalSolutionToBuild.Filename).Deployment.proj"
             Targets="Deploy"
             Properties="Configuration=$(CurrentConfiguration);OutDir=$(OutDir)"
             StopOnFirstFailure="$(StopOnFirstFailure)"
             ContinueOnError="false"
             Condition="'%(LocalSolutionToBuild.Deploy)'=='true'"
             />
</Target>

The CurrentConfiguration property batches (loops) on the ConfigurationToBuild; this property is used in the MSBuild task as a simple value ($ sign), and the interpreter over all its possible values before evaluating the batches in the task.

 

Happy Building!