The following applies to RAD Studio XE4, but it may also apply to earlier or later versions. Also, the dependencies defined in the .groupproj will not be honored with this method. The .groupproj I was trying to parallelize had no inter-project dependencies, so I didn't figure out how to handle this.
When you build a .groupproj file with MSBuild using the Build
, Clean
or Make
target, the build doesn't run in parallel because these targets use the CallTarget
task to execute other targets, but CallTarget
doesn't execute its targets in parallel.
In order to build separate projects in parallel, the MSBuild project must use a single MSBuild
task to build multiple projects at once. The targets must be defined like this:
<Target Name="Build">
<MSBuild Projects="@(Projects)" BuildInParallel="true"/>
</Target>
<Target Name="Clean">
<MSBuild Targets="Clean" Projects="@(Projects)" BuildInParallel="true"/>
</Target>
<Target Name="Make">
<MSBuild Targets="Make" Projects="@(Projects)" BuildInParallel="true"/>
</Target>
Add these to the .groupproj, then remove the other <Target>
directives as well as the <Import>
directive. (CodeGear.Group.Targets
defines some targets to build the projects in the proper order and to build dependencies when you ask to build only a subset of the projects, but it overrides the Build
, Clean
and Make
targets defined in the .groupproj.) Note that this only allows you to build all projects, not just a subset.
BuildInParallel
was added in MSBuild 3.5. However, since .groupproj files don't specify the ToolsVersion
attribute, MSBuild will use the MSBuild
task as defined in version 2.0, which didn't support BuildInParallel
. There are two options to fix this:
- Add
ToolsVersion="3.5"
(or a later version) to the root <Project>
element of your .groupproj file.
- Run MSBuild with the
/toolsversion:3.5
(or /tv:3.5
for short) command-line parameter (/toolsversion
overrides the ToolsVersion
specified in all project files.)
After doing this, run MSBuild with the /maxcpucount
(or /m
) argument and your projects should build in parallel. However, RAD Studio doesn't handle this transformed project group correctly, so you may want to give the file a different extension to make it clear that it's not a standard RAD Studio project group (any extension that ends in proj
will do).
The following XSLT stylesheet performs the transformation described above:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
exclude-result-prefixes="msbuild"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
xmlns:msbuild="http://schemas.microsoft.com/developer/msbuild/2003"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="//msbuild:Project">
<xsl:copy>
<xsl:attribute name="ToolsVersion">3.5</xsl:attribute>
<xsl:apply-templates select="@* | node()"/>
<Target Name="Build">
<MSBuild Projects="@(Projects)" BuildInParallel="true"/>
</Target>
<Target Name="Clean">
<MSBuild Targets="Clean" Projects="@(Projects)" BuildInParallel="true"/>
</Target>
<Target Name="Make">
<MSBuild Targets="Make" Projects="@(Projects)" BuildInParallel="true"/>
</Target>
</xsl:copy>
</xsl:template>
<xsl:template match="//msbuild:Target">
<!-- Do not copy -->
</xsl:template>
<xsl:template match="//msbuild:Import">
<!-- Do not copy -->
</xsl:template>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
You can apply this stylesheet with MSBuild (4.0 or later: XslTransformation
was added in MSBuild 4.0) using this project file (where groupproj2parallel.xslt
is the XSLT file above):
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Build" Inputs="$(InputPaths)" Outputs="$(OutputPaths)">
<XslTransformation
XmlInputPaths="$(InputPaths)"
XslInputPath="groupproj2parallel.xslt"
OutputPaths="$(OutputPaths)" />
</Target>
</Project>
You need to specify InputPaths
and OutputPaths
explicitly on the command line with /p:InputPaths="..." /p:OutputPaths="..."
, or by specifying them on the Properties
parameter of an MSBuild
task. (Alternatively, you can just hardcode the file names in the project file.)
The target definitions provided with MSBuild for C# and Visual Basic projects handle dependencies by using the <ProjectReference>
items defined in project files, instead of defining dependencies in the solution file. Delphi .dproj files and C++ Builder .cbproj files don't support this, as the underlying CodeGear.Common.Targets
doesn't reuse the machinery defined in Microsoft.Common.Targets
for <ProjectReference>
.