How do I control the error-handling behavior of the ""MSBuild"" task when building multiple child projects?


All sufficiently complicated builds will have scenarios that involve calling the ""MSBuild"" task in order to build multiple child projects.

Example 1


		 <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
		    [<ItemGroup>]
		        [<ChildProject] Include="ProjA.proj" />
		        [<ChildProject] Include="ProjB.proj" />
		        [<ChildProject] Include="ProjC.proj" />
		    [</ItemGroup>]
	

		    <Target Name="Build">
		        [<MSBuild] Projects="@(ChildProject)" />
		    </Target>
		 </Project>
	

""Now, suppose there is an error in ProjB.proj. Maybe ProjB doesn't exist on disk, maybe the project file has syntax errors, or maybe some build step in ProjB returns failure. For some situations, you want to continue building the remaining projects (e.g. ProjC.proj) despite the errors, and in other situations you want to stop immediately.""

""In the Beta 1 drop, the example provided above will continue building the remaining projects (ProjC) even if there are failures/errors in ProjB. This is what most people want. In fact, this is the behavior we recommend for all tasks that consume a list of inputs. We recommend that the task should iterate through the inputs, operating on them one at a time. If there is an error processing the Nth input, the task should log a relevant error, but still continue processing the remaining inputs. At the end of the whole list, if there were any errors, then the task should report an overall failure by returning "false" from the Execute() method. This is exactly how the MSBuild task behaves.""

""If you want the opposite behavior ... that is, you want the MSBuild task (or any task for that matter) to stop as soon as it encounters an error, then you need to take advantage of a feature we call "task batching". This essentially means that you need to invoke the task separately for each item in the input list, such that the task is only receiving one item at a time. This way, if the task fails for that one item, the MSBuild engine will abort the remainder of the build. Here's an example of how to use "task batching" to accomplish this:""

Example 2


		 <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
		    [<ItemGroup>]
		        [<ChildProject] Include="ProjA.proj" />
		        [<ChildProject] Include="ProjB.proj" />
		        [<ChildProject] Include="ProjC.proj" />
		    [</ItemGroup>]
	

		    <Target Name="Build">
		        [<MSBuild] Projects="%(ChildProject.Identity)" />
		    </Target>
		 </Project>
	

""Pre-Beta1"" Behavior

In the ""Pre-Beta1"" drop, the default behavior for the ""MSBuild"" task was actually the opposite of what the correct Beta 1 behavior is. So, for Example 1 above, the ""Pre-Beta1"" bits will actually stop building when it encounters the first error, which is a bug. In the meantime, if all you care about is skipping non-existent projects, then here's one way to workaround the bug:

		 <Project DefaultTargets="BuildProject">
		    [<ItemGroup>]
		        <Item Type="ChildProject" Include="ProjA.proj" />
		        <Item Type="ChildProject" Include="ProjB.proj" />
		        <Item Type="ChildProject" Include="ProjC.proj" />
		    [</ItemGroup>]
	

		    <Target Name="Build">
		        <Task Name="MSBuild" Projects="%(ChildProject.Identity)" 
		              Condition="Exists('%(ChildProject.Identity)')" />
		    </Target>
		 </Project>
	
Microsoft Communities