我的组织有一些在构建服务器上运行的大型构建,构建了大量与 ProjectReferences 链接的 MSBUILD 项目。我们需要能够与msbuild /m
.
我的问题是我有一个项目被大量其他项目引用,但项目本身不可重入。如果超过两个或更多节点尝试并行构建该项目,它将失败。
如何将这个项目或它的目标包装在关键部分中?
我真正需要做的是这样的事情:
<Target>
<EnterCriticalSection ID=$(ProjectGuid) />
<Exec something />
<LeaveCriticalSection ID=$(ProjectGuid) />
</Target>
这个想法是,如果多个 MSBUILD 节点尝试并行构建此项目,则只有一个节点可以执行,其余节点将不得不等待(或去做其他事情)。
我想我可以编写自定义的 MSBUILD 任务来执行此操作,但是在 MSBUILD 系统中没有内置的方法吗?
=== 2013 年 4 月 5 日编辑。澄清一下,该项目正在使用为其提供的构建脚本构建一个 3rd-party 库。完全重写他们的构建脚本以使其可重入——通过确保每个构建使用不同的一组文件夹来存储中间文件等——在理论上是可能的,但不是一个实际的解决方案。一方面,所有这些工作都必须在该库的每个新版本上重新完成。
=== 2013 年 4 月 6 日编辑。进一步思考,我认为甚至理论上不可能确保项目是可重入的。让我解释:
假设项目 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 将使用相同的Platform|Configuration 组合和相同的工作目录构建 XYZ 项目 4 次。
只要您在没有 /m 选项的情况下构建并且 MSBuild 运行单个线程,这将正常工作。根据 XYZ 项目的编写方式,第 2、第 3 和第 4 次构建可能什么都不做,因为输出是最新的,或者它可能会做一些多余的工作,但最终结果将是正确的并且构建会成功.
但是,一旦您开始使用并行 MSBuild,这个构建就被破坏了! 现在有一个竞争条件,多个线程可以同时进入 XYZ 项目的目标,并使用相同的工作目录开始构建,这将失败。