【发布时间】:2013-03-27 08:14:46
【问题描述】:
我的组织有一些在构建服务器上运行的大型构建,构建了大量与 ProjectReferences 链接的 MSBUILD 项目。我们需要能够与msbuild /m 并行构建项目和配置。
我的问题是我有一个项目被大量其他项目引用,但该项目本身是不可可重入的。如果超过两个或更多节点尝试并行构建该项目,它将失败。
如何将这个项目或它的目标包装在关键部分中?
我真正需要做的是这样的事情:
<Target>
<EnterCriticalSection ID=$(ProjectGuid) />
<Exec something />
<LeaveCriticalSection ID=$(ProjectGuid) />
</Target>
这个想法是,如果多个 MSBUILD 节点尝试并行构建此项目,则只有一个节点可以执行,其余节点必须等待(或去做其他事情)。
我想我可以编写自定义的 MSBUILD 任务来执行此操作,但是 MSBUILD 系统中没有内置的方法吗?
=== 编辑 4/5/13。澄清一下,该项目正在使用为其提供的构建脚本构建一个 3rd-party 库。完全重写他们的构建脚本以使其可重入——通过确保每个构建对中间文件使用不同的文件夹集等——在理论上是可能的,但不是一个实际的解决方案。一方面,所有这些工作都必须在该库的每个新版本上重做。
=== 编辑 4/6/13。进一步思考,我认为甚至理论上不可能确保项目是可重入的。让我解释一下:
假设项目 XYZ 设置为根据平台和配置以通常的方式使用不同的临时目录:
XYZ.proj:
<PropertyGroup>
<MyWorkingDir>tmp.$(Platform).$(Configuration)</MyWorkingDir>
</PropertyGroup>
现在假设一些其他项目 GraphicsWindow 通过 ProjectReferences 或 MSBuild 任务引用项目 XYZ。并假设可以构建 GraphicsWindow 项目以使用各种图形 API。即,有一个OpenGL版本,一个DirectX 9版本,一个DirectX 10版本......
所以在某个地方有一个 .proj 或 .targets 文件,其中包含构建所有四个版本的目标:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ProjectToBuild Include="GraphicsWindow.proj">
<Properties>GraphicsApi=OpenGL</Properties>
</ProjectToBuild>
<ProjectToBuild Include="GraphicsWindow.proj">
<Properties>GraphicsApi=D3D9</Properties>
</ProjectToBuild>
<ProjectToBuild Include="GraphicsWindow.proj">
<Properties>GraphicsApi=D3D10</Properties>
</ProjectToBuild>
<ProjectToBuild Include="GraphicsWindow.proj">
<Properties>GraphicsApi=D3D11</Properties>
</ProjectToBuild>
</ItemGroup>
<Target Name="All">
<MSBuild Projects="@(ProjectToBuild)" BuildInParallel="true" />
</Target>
</Project>
或使用批处理的等效项。
现在 MSBuild 将使用 same 平台|配置组合和相同的工作目录构建 XYZ 项目 4 次。
只要您在没有 /m 选项的情况下构建并且 MSBuild 运行单个线程,这将正常工作。根据 XYZ 项目的编写方式,第 2 次、第 3 次和第 4 次构建可能什么都不做,因为输出是最新的,或者它可能会做一些多余的工作,但最终结果将是正确的并且构建会成功.
但是一旦你开始使用并行 MSBuild,这个构建就被破坏了!现在有一个竞争条件,多个线程可以同时进入 XYZ 项目的目标,并开始使用相同的工作目录,这将失败。
【问题讨论】:
-
您确定需要并行构建多个项目吗?
Msbuild已经并行并同时编译多个源文件。由于硬盘访问瓶颈,尝试并行执行多个构建可能会导致性能下降。
标签: msbuild