本文介绍了MSBuild-比较ItemGroups元数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为我们的源代码树编写一个构建脚本.该树由(大量)解决方案以及它们之间的程序集引用组成.我已经创建了一个包含所有解决方案的ItemGroup,并且正在对该ItemGroup进行批处理以构建解决方案.

I am attempting to write a build script for our source tree. This tree consists of a (large) number of solutions with assembly references between them. I have created an ItemGroup containing all the solutions and am batching over this ItemGroup to build the solutions.

我还需要将 some 项目输出复制到"exes output"文件夹中,每个文件夹都位于自己的文件夹中.我已将一些元数据附加到解决方案项,以指向要从中获取输出的项目.由于每个解决方案都可能有多个项目要输出,因此我通过为元数据提供传递给ItemGroup的Include的值来单独创建ItemGroup来实现此目的.这项工作很愉快,我可以对这个动态创建的ItemGroup进行批处理.

I also need to copy some project outputs to an "exes output" folder, each in their own folder. I've attached some metadata to the solution item pointing to the projects that I want to grab the output from. As I can have potentially more than one project to output from each solution, I am doing this by giving the metadata the value that is passed to an ItemGroup's Include to create an ItemGroup separately. This works happily and I am able to batch over this dynamically created ItemGroup.

我要做的最后一步使我头疼,那就是对于这些输出项目中的 some ,我想指定一个特殊的文件夹名称.现在,我可以通过更改正在执行工作的目标内部的元数据来做到这一点,就像这样:

The final step I want to do, which is causing me a headache, is that for some of those output projects, I want to specify a special folder name. Now, I can do this by altering the metadata inside the target that is doing the work, like this:

<?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,如下所示:

However, this build script needs to be maintained by all developers on the team, not all of whom are familiar with MSBuild. Therefore, I'd like to define another ItemGroup at the top of the script that defines any special names. They can then ignore all the targets and tasks and just maintain the ItemGroups, like this:

<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目标以更新元数据的任何方法都浮出水面.我以为我可以做到这一点:

However, any way I've tried to get the DoBuild target to update the metadata falls on its face. I thought I could do this:

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

但是该代码将在OutputProjects项组中批处理,然后将 then 遍及OutputNames项组,因此条件永远不会为真(比较的参数之一始终为空).

But that code batches over The OutputProjects item group and then over the OutputNames item group, so the condition is never true (one of the arguments to the comparison is always empty).

很遗憾,在这个阶段,我无法更改解决方案结构或输出文件夹结构的要求.我缺少MSBuild的任何技巧可以在这里帮助我吗?我不反对包含自定义任务来完成这项工作,但在可能的情况下,我希望使用直接的MSBuild解决方案.

I'm unfortunately, at this stage, unable to change either the solution structure or the output folder structure requirements. Is there any trick of MSBuild that I'm missing that could help me here? I'm not averse to including a custom task to do the job, but would prefer a straight MSBuild solution if possible.

如果有所作为,我正在使用MSBuild v4.

If it makes a difference, I am using MSBuild v4.

推荐答案

啊.在玩这个游戏时偶然发现了一个 答案.

Ah. Stumbled on an answer whilst playing around with this.

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

Firstly, I was investigating this post about getting the intersection of two item groups. I therefore changed my OutputNames item group to have the same Identity as the OutputProjects ItemGroup:

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

这让我对%(Identity)进行批处理,并得到这样的交集:

This let me batch on %(Identity) and get the intersection like this:

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

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

However, when also referring to the OutputFolder metadata in the same Task, that became part of the batching as well resulting in the following never printing to the output:

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

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

But, by using a transform over the property instead of direct access, it isn't treated as part of the batching:

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

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

Therefore, I can do the following to update my metadata:

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

这篇关于MSBuild-比较ItemGroups元数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-03 03:12