问题 通过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同时建立多个项目? 如果是,你可以给我一个解释吗?

谢谢!


10581
2017-10-16 16:03


起源

当我需要通过使用Python编写构建脚本时,我总是自己完成这个 - David Heffernan
这将是一个解决方法是但如果msbuild有一个“内置”机制来实现它,我宁愿不这样做。 - tabasko


答案:


以下内容适用于RAD Studio XE4,但也可能适用于早期版本或更高版本。此外,.groupproj中定义的依赖关系不会受到此方法的影响。我试图并行化的.groupproj没有项目间的依赖关系,所以我没有弄清楚如何处理这个问题。

当您使用MSBuild构建.groupproj文件时 BuildClean 要么 Make 目标,构建不会并行运行,因为这些目标使用 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 定义一些目标以按正确顺序构建项目,并在要求仅构建项目的子集时构建依赖项,但它会覆盖 BuildClean 和 Make .groupproj中定义的目标。)请注意,这只允许您构建所有项目,而不仅仅是子集。

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

  1. ToolsVersion="3.5" (或更高版本)到根 <Project> .groupproj文件的元素。
  2. 用。运行MSBuild /toolsversion:3.5 (要么 /tv:3.5 简而言之)命令行参数(/toolsversion 覆盖了 ToolsVersion 在所有项目文件中指定。)

执行此操作后,运行MSBuild /maxcpucount (要么 /m)参数和你的项目应该并行构建。但是,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>

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

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

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


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


7
2018-05-28 00:45





有两种方法可以构建Delphi项目: MSBuild 要么 DCC32.exeMSBuild 建议作为项目文件(dproj 和 groupproj)封装所有配置设置。

但是,有额外的头部使用 MSBuild 比较普通的 DCC32.exe。此外,使用 MSBuild 建立德尔福项目组(.groupproj)不会为多核CPU带来任何好处。构建性能与单核CPU相同。

以下是我建立290的统计数据 dproj 单个文件 groupproj

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

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

一个 msbuild 脚本来构建Delphi groupproj 并行可在 https://github.com/ccy/msbuild.delphi.parallel


3
2017-12-01 09:05



很好的分析,您能提供用于这些290个项目的多线程MSBuild和DCC编译的脚本吗? - tabasko


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


1
2017-10-17 06:07



这应该是一个评论 - David Heffernan
我刚刚安装了它。每个项目我获得了大约20%,这很好。但是,与在4或8核上分割任务时获得的结果相比,它没什么可比的。不管怎么说,还是要谢谢你! - tabasko