MSBuild - 比较ItemGroups元数据

时间:2011-02-02 00:22:08

标签: msbuild

我正在尝试为源代码树编写构建脚本。该树由(大量)解决方案组成,它们之间具有汇编引用。我创建了一个包含所有解决方案的ItemGroup,并在这个ItemGroup上进行批处理以构建解决方案。

我还需要将一些项目输出复制到“exes output”文件夹,每个文件夹都在自己的文件夹中。我已经将一些元数据附加到指向我想要从中获取输出的项目的解决方案项。因为我可以从每个解决方案中输出多个项目,所以我通过为元数据提供传递给ItemGroup的Include以分别创建ItemGroup的值来实现这一点。这很有效,我可以批量处理这个动态创建的ItemGroup。

我想要做的最后一步,令我头疼的是,对于那些输出项目的一些,我想指定一个特殊的文件夹名称。现在,我可以通过更改正在执行工作的目标内的元数据来实现此目的,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
         DefaultTargets="Build">

  <!-- Define all the solutions to build, and any projects we want to handle the output of -->
  <ItemGroup>

    <SolutionsToBuild Include="$(MSBuildProjectDirectory)\Solution1\Solution1.sln">
      <ProjectsToOutput>
        $(MSBuildProjectDirectory)\Solution1\Project1A\Project1A.csproj;
        $(MSBuildProjectDirectory)\Solution1\Project1B\Project1B.csproj;
      </ProjectsToOutput>
    </SolutionsToBuild>
    <SolutionsToBuild Include="$(MSBuildProjectDirectory)\Solution2\Solution2.sln">
      <ProjectsToOutput>
        $(MSBuildProjectDirectory)\Solution2\Project2A\Project2A.csproj;
        $(MSBuildProjectDirectory)\Solution2\Project2B\Project2B.csproj;
      </ProjectsToOutput>
    </SolutionsToBuild>

  </ItemGroup>

  <Target Name="Build">

    <CallTarget Targets="DoBuild" />

  </Target>

  <Target Name="DoBuild" Outputs="%(SolutionsToBuild.Identity)">

    <Message Text="Building project: %(SolutionsToBuild.FullPath)" />
    <!-- e.g. <MSBuild Projects="%(SolutionsToBuild.FullPath)" /> -->

    <PropertyGroup>
      <ProjectsToOutputIncludeMask>%(SolutionsToBuild.ProjectsToOutput)</ProjectsToOutputIncludeMask>
    </PropertyGroup>

    <ItemGroup>
      <OutputProjects Include="$(ProjectsToOutputIncludeMask)" />

      <!-- Now create the OutputTo metadata -->
      <!-- Default to the same name as the project file -->
      <OutputProjects>
        <OutputTo>%(OutputProjects.FileName)</OutputTo>
      </OutputProjects>

      <!-- Now override specific projects OutputTo metadata -->
      <OutputProjects Condition="'%(OutputProjects.FileName)' == 'Project1A'">
        <OutputTo>ArbitraryFolder1</OutputTo>
      </OutputProjects>
      <OutputProjects Condition="'%(OutputProjects.FileName)' == 'Project2B'">
        <OutputTo>ArbitraryFolder2</OutputTo>
      </OutputProjects>

    </ItemGroup>

    <Message Text=" Outputting project: %(OutputProjects.FullPath) -> %(OutputTo)" />

  </Target>      
</Project>

但是,此构建脚本需要由团队中的所有开发人员维护,而不是所有人都熟悉MSBuild。因此,我想在脚本的顶部定义另一个ItemGroup,定义任何特殊名称。然后,他们可以忽略所有目标和任务,只需维护ItemGroups,如下所示:

<ItemGroup>

  <SolutionsToBuild Include="$(MSBuildProjectDirectory)\Solution1\Solution1.sln">
    <ProjectsToOutput>
      $(MSBuildProjectDirectory)\Solution1\Project1A\Project1A.csproj;
      $(MSBuildProjectDirectory)\Solution1\Project1B\Project1B.csproj;
    </ProjectsToOutput>
  </SolutionsToBuild>
  <SolutionsToBuild Include="$(MSBuildProjectDirectory)\Solution2\Solution2.sln">
    <ProjectsToOutput>
      $(MSBuildProjectDirectory)\Solution2\Project2A\Project2A.csproj;
      $(MSBuildProjectDirectory)\Solution2\Project2B\Project2B.csproj;
    </ProjectsToOutput>
  </SolutionsToBuild>

  <OutputNames Include="Project1A">
    <OutputFolder>ArbitraryFolder1</OutputFolder>
  </OutputNames>
  <OutputNames Include="Project2B">
    <OutputFolder>ArbitraryFolder2</OutputFolder>
  </OutputNames>

</ItemGroup>

然而,我试图让DoBuild目标更新元数据的任何方式都落在了它的脸上。我以为我能做到这一点:

  <!-- Now override specific projects OutputTo metadata -->
  <OutputProjects Condition="'%(OutputProjects.FileName)' == '%(OutputNames.Identity)'">
    <OutputTo>%(OutputNames.OutputFolder)</OutputTo>
  </OutputProjects>

但是该代码对OutputProjects项目组进行批处理,然后然后对OutputNames项目组进行批处理,因此条件永远不为真(比较的其中一个参数始终为空)。

我很遗憾,在此阶段,无法更改解决方案结构或输出文件夹结构要求。是否有任何我想念的MSBuild技巧可以帮助我?我并不反对包含自定义任务来完成这项工作,但如果可能的话,我更喜欢直接的MSBuild解决方案。

如果它有所不同,我正在使用MSBuild v4。

1 个答案:

答案 0 :(得分:3)

阿。在玩这个时,偶然发现了 的答案。

首先,我正在调查this post关于获取两个项目组的交集。因此,我将OutputNames项组更改为与OutputProjects ItemGroup具有相同的标识:

<OutputNames Include="$(MSBuildProjectDirectory)\Solution1\Project1A\Project1A.csproj">
  <OutputFolder>ArbitraryFolder1</OutputFolder>
</OutputNames>
<OutputNames Include="$(MSBuildProjectDirectory)\Solution2\Project2B\Project2B.csproj">
  <OutputFolder>ArbitraryFolder2</OutputFolder>
</OutputNames>

这让我在%(身份)上批处理并获得这样的交集:

<Message Condition="'%(Identity)' != '' and
         '@(OutputProjects)' != '' and
         '@(OutputNames)' != ''"
         Text="Found a match for %(Identity)" />

但是,当同时在同一个Task中引用OutputFolder元数据时,它也成为批处理的一部分,导致以下内容永远不会打印到输出:

<Message Condition="'%(Identity)' != '' and
         '@(OutputProjects)' != '' and
         '@(OutputNames)' != ''"
         Text="Found a match for %(Identity): %(OutputNames.OutputFolder)" />

但是,通过对属性使用转换而不是直接访问,它不会被视为批处理的一部分:

<Message Condition="'%(Identity)' != '' and
         '@(OutputProjects)' != '' and
         '@(OutputNames)' != ''"
         Text="Found a match for %(Identity): @(OutputNames->'%(OutputFolder)')" />

因此,我可以执行以下操作来更新元数据:

  <!-- Now override specific projects OutputTo metadata -->
  <OutputProjects Condition="'%(Identity)' != '' AND
                             '@(OutputProjects)' != '' AND
                             '@(OutputNames)' != ''">
    <OutputTo>@(OutputNames->'%(OutputFolder)')</OutputTo>
  </OutputProjects>