Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I build multiple configurations in parallel?

Tags:

msbuild

I have a Visual Studio 2012 Solution with twelve Solution Configurations. Each Solution Configuration is independent (i.e., the outputs of each configuration are entirely unrelated).

The Question: How can I build all twelve configurations in one step, i.e. by running a single MSBuild command on the command line, and how can I get the configurations to be built in parallel?

As an example, if there were just two configurations, Release/AnyCPU and Debug/AnyCPU, I would want both of these to build at the same time, in parallel.

For completeness, the following is what I have tried; I don't yet have a solution to this problem.


To build all of the projects at once, I created a new project file with a Build target that runs the MSBuild task on the Solution file for each configuration:

<Target Name="Build">
  <MSBuild Projects="cxxreflect.sln" Properties="SolutionDir=$(MSBuildProjectDirectory)\;Configuration=Release;Platform=Win32"     Targets="$(BuildCommand)" />
  <MSBuild Projects="cxxreflect.sln" Properties="SolutionDir=$(MSBuildProjectDirectory)\;Configuration=Release;Platform=x64"       Targets="$(BuildCommand)" />
  <MSBuild Projects="cxxreflect.sln" Properties="SolutionDir=$(MSBuildProjectDirectory)\;Configuration=Release;Platform=ARM"       Targets="$(BuildCommand)" />
  <MSBuild Projects="cxxreflect.sln" Properties="SolutionDir=$(MSBuildProjectDirectory)\;Configuration=Release(ZW);Platform=Win32" Targets="$(BuildCommand)" />
  <MSBuild Projects="cxxreflect.sln" Properties="SolutionDir=$(MSBuildProjectDirectory)\;Configuration=Release(ZW);Platform=x64"   Targets="$(BuildCommand)" />
  <MSBuild Projects="cxxreflect.sln" Properties="SolutionDir=$(MSBuildProjectDirectory)\;Configuration=Release(ZW);Platform=ARM"   Targets="$(BuildCommand)" />

  <MSBuild Projects="cxxreflect.sln" Properties="SolutionDir=$(MSBuildProjectDirectory)\;Configuration=Debug;Platform=Win32"       Targets="$(BuildCommand)" />
  <MSBuild Projects="cxxreflect.sln" Properties="SolutionDir=$(MSBuildProjectDirectory)\;Configuration=Debug;Platform=x64"         Targets="$(BuildCommand)" />
  <MSBuild Projects="cxxreflect.sln" Properties="SolutionDir=$(MSBuildProjectDirectory)\;Configuration=Debug;Platform=ARM"         Targets="$(BuildCommand)" />
  <MSBuild Projects="cxxreflect.sln" Properties="SolutionDir=$(MSBuildProjectDirectory)\;Configuration=Debug(ZW);Platform=Win32"   Targets="$(BuildCommand)" />
  <MSBuild Projects="cxxreflect.sln" Properties="SolutionDir=$(MSBuildProjectDirectory)\;Configuration=Debug(ZW);Platform=x64"     Targets="$(BuildCommand)" />
  <MSBuild Projects="cxxreflect.sln" Properties="SolutionDir=$(MSBuildProjectDirectory)\;Configuration=Debug(ZW);Platform=ARM"     Targets="$(BuildCommand)" />
</Target>

This works great, except that each MSBuild task is invoked in sequence, so there's no parallelism (yes, there is parallelism within each configuration build, but I'd really like to get parallelism across configuration builds).

In an attempt to get the configurations to build in parallel, I tried to make use of the MSBuild task's BuildInParallel property. I wrote a pre-build task that generated project files for each configuration, then attempted to build all of these generated projects in parallel:

<Target Name="PreBuild" Outputs="%(ProjectConfiguration.Identity)" Returns="%(BuildProject.Identity)">
  <Message Text="Cloning Solution for Configuration:  %(ProjectConfiguration.Identity)" />
  <PropertyGroup>
    <BuildProjectPath>$(IntPath)\%(ProjectConfiguration.Platform)\%(ProjectConfiguration.Configuration)\build.proj</BuildProjectPath>
  </PropertyGroup>
  <ItemGroup>
    <BuildProject Include="$(BuildProjectPath)" />
  </ItemGroup>
  <MakeDir Directories="$(IntPath)\%(ProjectConfiguration.Platform)\%(ProjectConfiguration.Configuration)" />
  <WriteLinesToFile
    File="$(BuildProjectPath)"
    Lines="&lt;?xml version='1.0' encoding='utf-8'?&gt;
&lt;Project DefaultTargets='Build' ToolsVersion='4.0' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
  &lt;Target Name='Build'&gt;
    &lt;MSBuild
      Projects='$(SlnPath)'
      Properties='
        SolutionDir=$(MSBuildProjectDirectory)\%3b
        Configuration=%(ProjectConfiguration.Configuration)%3b
        Platform=%(ProjectConfiguration.Platform)
      '
      Targets='$(BuildCommand)'
    /&gt;
  &lt;/Target&gt;
&lt;/Project&gt;"
    Overwrite="true" />

</Target>
<Target Name="Build" DependsOnTargets="PreBuild">
  <Message Text="%(BuildProject.Identity)" />
  <MSBuild Projects="%(BuildProject.Identity)" Properties="BuildCommand=$(BuildCommand)" BuildInParallel="true" />
</Target>

Unfortunately, while this does build all twelve configurations, it builds them serially. My guess is that when MSBuild performs dependency analysis on the set of projects to be built, it identifies that they all depend on the same Solution file, so it decides they cannot be built in parallel. (This is just a guess; I could be completely wrong. I know very little about MSBuild.)

I am also using the /m switch when building.

like image 226
James McNellis Avatar asked Oct 20 '12 03:10

James McNellis


1 Answers

Take advantage of the MSBuild Task's BuildInParallel option, and pass all the projects in a single call. The examples here give the basic approach:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <ItemGroup>
        <ProjectToBuild Include="a1.sln">
            <Properties>Configuration=Debug</Properties>
        </ProjectToBuild>
        <ProjectToBuild Include="a1.sln">
            <Properties>Configuration=Release</Properties>
        </ProjectToBuild>
    </ItemGroup>
    <Target Name="Build">
        <MSBuild Projects="@(ProjectToBuild)" BuildInParallel="true" />
    </Target>
</Project>
like image 88
Jason Malinowski Avatar answered Oct 30 '22 18:10

Jason Malinowski