3

我的组织有一些在构建服务器上运行的大型构建,构建了大量与 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 项目的目标,并使用相同的工作目录开始构建,这将失败。

4

1 回答 1

3

无论您如何执行 MSBuild,无论是否使用 multi-proc 选项 /m,都可以保证为构建请求的每个配置执行一次项目。这是来自MSDN的引用:

当 Microsoft 构建引擎在使用并行构建来构建项目时遇到项目到项目 (P2P) 引用时,它只会构建一次引用。如果两个项目具有相同的 P2P 引用,则不会为每个项目重新构建该引用。相反,构建引擎为依赖它的两个项目返回相同的 P2P 引用。会话中对同一目标的未来请求将提供相同的 P2P 参考。

如果您看到多次构建同一个项目,这意味着它在两个(或多个)不同的配置中被引用。这里的配置是指传递给项目的一组参数,例如项目平台(x86、x64、AnyCPU 等)风格(调试/零售)、本地化语言、您可能使用的任何其他参数。

通常,这是项目平台混合的问题。例如,您有为 x64 构建的项目 A,为 AnyCPU 构建的项目 B,并且 A 和 B 都引用 C。现在必须为 x64 和 AnyCPU 构建两次 C。如果 C 通过将输出清晰地分开到单独的目录中正确处理这两个平台,则没有问题。但是,如果 C 将 x64 和 AnyCPU 视为相同,它将在多进程构建中随机失败。

首先检查您的解决方案配置对话框。确保所有项目都具有一致的平台/配置参数集。如果您需要在不同的配置中构建相同的项目,请确保将输出放在不同的位置。

于 2013-04-05T14:49:22.873 回答