Executing MSBuild Targets in Parallel – Part 2


[ In this series – Part 1, Part 2 ]

Part 1 provided an introduction to using the new parallel task in the MSBuild Extension Pack. Here is the file I ended Part 1 with, having knocked a great deal of execution time off the build.

<Project ToolsVersion="4.0" DefaultTargets="Default" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$<MSBuildExtensionsPath>\ExtensionPack\4.0\MSBuild.ExtensionPack.tasks"/>
  <ItemGroup>
    <MyTargetSets Include="1">
      <Targets>Target2</Targets>
    </MyTargetSets>
    <MyTargetSets Include="2">
      <Targets>Target1;Target3</Targets>
    </MyTargetSets>
  </ItemGroup>
  <Target Name="Default">
    <MSBuild.ExtensionPack.Framework.Parallel TaskAction="BuildTargetSetsInParallel" Targets="@<MyTargetSets>"/>
  </Target>
  <Target Name="Target1">
    <MSBuild.ExtensionPack.Framework.Thread TaskAction="Sleep" Timeout="1000"/>
  </Target>
  <Target Name="Target2">
    <MSBuild.ExtensionPack.Framework.Thread TaskAction="Sleep" Timeout="4000"/>
  </Target>
  <Target Name="Target3">
    <MSBuild.ExtensionPack.Framework.Thread TaskAction="Sleep" Timeout="2000"/>
  </Target>
</Project>

In this part I’ll cover maintaining state, limitations and some general tips.

State

Let’s start with state. By state I mean the Properties and ItemGroups that have been set during execution, the static ones should be fine assuming we don’t alter any conditional logic evaluations.

Limitation 1 – there is no support for passing ItemGroups within and between parallel executions

Limitation 2 – there is no support for passing Properties within and between parallel executions. You may of course use environment variables, files, registry etc. but no vanilla support.

What is supported is the passing of calling properties to the parallel target sets. Take this sample, executed using msbuild parallel2.proj /p:MyProperty=Mike

<Project ToolsVersion="4.0" DefaultTargets="Default" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <Import Project="$(MSBuildExtensionsPath)\ExtensionPack\4.0\MSBuild.ExtensionPack.tasks"/>
    <ItemDefinitionGroup>
        <MyTargetSets1>
            <Properties>MyProperty=$(MyProperty)</Properties>
        </MyTargetSets1>
        <MyTargetSets2>
            <Properties>MyProperty=$(MyProperty)</Properties>
        </MyTargetSets2>
    </ItemDefinitionGroup>
    <ItemGroup>
        <MyTargetSets1 Include="1">
            <Targets>Target2</Targets>
        </MyTargetSets1>
        <MyTargetSets1 Include="2">
            <Targets>Target1;Target3</Targets>
        </MyTargetSets1>
        <MyTargetSets2 Include="1">
            <Targets>Target4</Targets>
        </MyTargetSets2>
        <MyTargetSets2 Include="2">
            <Targets>Target5</Targets>
        </MyTargetSets2>
    </ItemGroup>
    <Target Name="Default" DependsOnTargets="BuildSet1;BuildSet2"/>
    <Target Name="BuildSet1">
        <MSBuild.ExtensionPack.Framework.Parallel TaskAction="BuildTargetSetsInParallel" Targets="@(MyTargetSets1)"/>
    </Target>
    <Target Name="BuildSet2">
        <MSBuild.ExtensionPack.Framework.Parallel TaskAction="BuildTargetSetsInParallel" Targets="@(MyTargetSets2)"/>
    </Target>
    <Target Name="Target1">
        <MSBuild.ExtensionPack.Framework.Thread TaskAction="Sleep" Timeout="1000"/>
    </Target>
    <Target Name="Target2">
        <MSBuild.ExtensionPack.Framework.Thread TaskAction="Sleep" Timeout="4000"/>
        <Message Text="Target2 Value - $(MyProperty)" Importance="high"/>
    </Target>
    <Target Name="Target3">
        <MSBuild.ExtensionPack.Framework.Thread TaskAction="Sleep" Timeout="2000"/>
    </Target>
    <Target Name="Target4">
        <MSBuild.ExtensionPack.Framework.Thread TaskAction="Sleep" Timeout="4000"/>
        <Message Text="Target4 Value - $(MyProperty)" Importance="high"/>
    </Target>
    <Target Name="Target5">
        <MSBuild.ExtensionPack.Framework.Thread TaskAction="Sleep" Timeout="4000"/>
    </Target>
</Project>

We get

image

The key part here is the ItemGroupDefinition data set during the initial call. This saves any configured properties for use during execution.

Thumbs up Tip: If you have an InitialTarget set, this will be executed for every Target execution. You should either remove the InitialTarget configuration and call it manually, or condition it’s execution and then use the AdditionalProperties property so skip it’s execution on all but a pre-determined target.

Limitation 3 – there is no support for cancellation within a parallel execution. If a target fails though, any subsequent parallel target sets are not executed.

The cancellation limitation is something I plan to address shortly for the next release of the MSBuild Extension Pack.

Thumbs up Tip: If you only need a single target within a set to complete, set WaitAll=”false”. Note though that the other targets will still execute, but execution of the rest of your project will continue.

Thanks for taking time to read this and let me know how you get along with the task..

Mike

8 Replies to “Executing MSBuild Targets in Parallel – Part 2”

  1. Hi Mike.. I have a .proj file like following –

    Debug
    Any CPU

    Debug
    Any CPU

    Release
    Any CPU

    on passing ‘Set’ parameter to msbuild command line, it is not making any impact. it always build debug config regardless of the ‘Set’ parameter value I pass.

    Do you have any idea?

    Thanks,
    Pravesh

  2. Hi Mike.. I have a .proj file like following –

    Debug
    Any CPU

    Debug
    Any CPU

    Release
    Any CPU

    on passing ‘Set’ parameter to msbuild command line, it is not making any impact. it always build debug config regardless of the ‘Set’ parameter value I pass.

    Do you have any idea?

  3. Thanks for the post, this was really helpful. I have a case where I want to execute a same target in parallel depending upon the item group passed. Currently my target executes in a sequence for each item in item group, for improving performance I want to execute these targets in parallel. There is no requirement for sharing properties or items between targets. Can something like this be achieved in parallel execution mode?
    Thanks.

  4. This is great, thank you.

    I’ve got a question, though. Our msbuild script is running unit tests and FxCop code analysis. I configured the targets to output an error message in case a unit test fails or fx cop analysis detects errors. A message contains specific information like “Unit Test FooViewModelTest.Close_ShouldCloseWindow() failed with .. exception”.

    Now when i run this using the Parallel task all i get is “Error Code: 1”. How can i get my error messages printed to the console?

  5. Thanks this has been really helpful to get me started.

    Is there an easy way to set a log prefix for each parrallel task in MSBuild? Once you move beyond trivial examples the output can get pretty messy trying to decifer the logs.

Leave a comment