通过MSBuild并行编译delphi项目



我有一个脚本,可以编译我的解决方案的所有项目(大约50个),比如下面的

msbuild "myProjName.dproj" /t:build /p:config="Release" /fileLogger /flp:ErrorsOnly /nologo

这工作得很好,但需要很长时间才能编译。为了加快构建速度,我一直在尝试利用我们现代多核机器的所有潜力,使用此处解释的"/maxcpucount"开关:http://msdn.microsoft.com/library/bb651793.aspx

在我的4核CPU开发机器上,我得到了大约相同的编译时间。没有绩效收益。

显然,只有当项目需要建立依赖关系时,这才能起作用。然后,其他"工作者"将并行地构建这些依赖项项目作为主项目。所以我试着在delphi中建立一个项目组,并将我所有的项目添加到其中,然后在这个.groupproj上运行msbuild命令,但它仍然像往常一样慢。

你们中有人用msbuild同时建造了多个项目吗?如果是,你能给我一个解释吗?

谢谢!

以下内容适用于RAD Studio XE4,但也可能适用于早期或更高版本。此外,.groupproj中定义的依赖项将不会被此方法所接受。我试图并行化的.groupproj没有项目间的依赖关系,所以我不知道如何处理它

当使用BuildCleanMake目标使用MSBuild生成.groupproj文件时,生成不会并行运行,因为这些目标使用CallTarget任务执行其他目标,但CallTarget不会并行执行其目标。

为了并行生成单独的项目,MSBuild项目必须使用单个MSBuild任务同时生成多个项目。目标必须这样定义:

  <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>

将这些指令添加到.groupproj,然后删除其他<Target>指令和<Import>指令。(当您要求只生成项目的子集时,CodeGear.Group.Targets定义了一些目标以按正确的顺序生成项目,并生成依赖项,但它会覆盖.groupproj中定义的BuildCleanMake目标。)请注意,这只允许您生成所有项目,而不仅仅是一个子集。

BuildInParallel已添加到MSBuild 3.5中。但是,由于.groupproj文件没有指定ToolsVersion属性,MSBuild将使用2.0版中定义的MSBuild任务,该版本不支持BuildInParallel。有两个选项可以解决此问题:

  1. ToolsVersion="3.5"(或更高版本)添加到.groupproj文件的根<Project>元素中
  2. 使用/toolsversion:3.5(或简称/tv:3.5)命令行参数运行MSBuild(/toolsversion覆盖所有项目文件中指定的ToolsVersion

完成此操作后,使用/maxcpucount(或/m)参数运行MSBuild,您的项目应该并行生成。但是,RAD Studio无法正确处理这个转换后的项目组,因此您可能需要为文件提供不同的扩展名,以明确它不是标准的RAD Studio项目组(任何以proj结尾的扩展名都可以)。

下面的XSLT样式表执行上面描述的转换:

<?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>

您可以使用以下项目文件(其中groupproj2parallel.xslt是上面的XSLT文件)将此样式表应用于MSBuild(4.0或更高版本:XslTransformation已添加到MSBuild 4.0中):

<?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>

您需要使用/p:InputPaths="..." /p:OutputPaths="..."在命令行中显式指定InputPathsOutputPaths,或者在MSBuild任务的Properties参数中指定它们。(或者,您也可以对项目文件中的文件名进行硬编码。)


MSBuild为C#和Visual Basic项目提供的目标定义通过使用项目文件中定义的<ProjectReference>项来处理依赖项,而不是在解决方案文件中定义依赖项。Delphi.droj文件和C++Builder.cbproj文件不支持这一点,因为底层的CodeGear.Common.Targets不重用Microsoft.Common.Targets中为<ProjectReference>定义的机制。

有两种方法可以构建Delphi项目:MSBuildDCC32.exe。建议使用MSBuild,因为项目文件(dprojgroupproj)封装了所有配置设置。

然而,与普通的旧DCC32.exe相比,使用MSBuild有额外的开销。此外,使用MSBuild构建Delphi项目组(.groupproj)并没有给多核CPU带来任何好处。构建性能与单核CPU相同。

以下是我在一个groupproj:中构建290个dproj文件的统计数据

MSBuild a `groupproj` contains 290 `dproj` on 2C/4T CPU: ~100s
MSBuild a `groupproj` contains 290 `dproj` on 4C/8T CPU: ~100s
MSBuild 290 `dproj` run in multi-threads on 2C/4T CPU: ~121s
MSBuild 290 `dproj` run in multi-threads on 4C/8T CPU: ~50s
DCC 290 `dproj` run in multi-threads on 2C/4T CPU: ~37s
DCC 290 `dproj` run in multi-threads on 4C/8T CPU: ~24s

从阅读中,我们可以得出结论,与DCC32相比,MSBuild引入了额外的开销。为了充分利用可用的CPU内核和线程,DCC32是牺牲.DPROJ项目配置封装设计方便的方法。

并行构建Delphi groupprojmsbuild脚本可在https://github.com/ccy/msbuild.delphi.parallel

有点偏离主题:您可以尝试IDE修复包的fastdcc部分以获得更快的构建:http://andy.jgknet.de/blog/ide-tools/ide-fix-pack/例如,我的构建时间从1分钟降到了22秒!

最新更新