MSBuild 批处理MSBuild batching

MSBuild 根据项元数据将项列表划分为不同的类别或批,并对每批一次运行一个目标或任务。MSBuild divides item lists into different categories, or batches, based on item metadata, and runs a target or task one time with each batch.

任务批处理Task batching

借助任务批处理,可以用某种方式将项列表划分为不同批次,同时将每个批次单独地传递到任务中来简化项目文件。Task batching allows you to simplify your project files by providing a way to divide item lists into different batches and pass each of those batches into a task separately. 这意味着项目文件只需要声明一次任务及其特性就可多次运行该任务。This means that a project file only needs to have the task and its attributes declared once, even though it can be run several times.

通过在任务特性之一中使用 %(ItemMetaDataName) 表示法,指定希望 MSBuild 对任务执行批处理。You specify that you want MSBuild to perform batching with a task by using the %(ItemMetaDataName) notation in one of the task attributes. 以下示例基于 Color 项元数据值将 Example 项列表划分为几个批次,并将每个批次单独地传递到 MyTask 任务。The following example splits the Example item list into batches based on the Color item metadata value, and passes each of the batches to the MyTask task separately.

备注

如果没有在任务特性中的其他位置引用项列表,或者如果元数据名称可能不明确,则可以使用 %(<ItemCollection.ItemMetaDataName>) 表示法来完全限定要用于批处理的项元数据值。If you do not reference the item list elsewhere in the task attributes, or the metadata name may be ambiguous, you can use the %(<ItemCollection.ItemMetaDataName>) notation to fully qualify the item metadata value to use for batching.

<Project
    xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

    <ItemGroup>
        <Example Include="Item1">
            <Color>Blue</Color>
        </Example>
        <Example Include="Item2">
            <Color>Red</Color>
        </Example>
    </ItemGroup>

    <Target Name="RunMyTask">
        <MyTask
            Sources = "@(Example)"
            Output = "%(Color)\MyFile.txt"/>
    </Target>

</Project>

有关更具体的批处理示例,请参阅任务批处理中的项元数据For more specific batching examples, see Item metadata in task batching.

目标批处理Target batching

MSBuild 先检查目标的输入和输出是否是最新的,再运行目标。MSBuild checks if the inputs and outputs of a target are up to date before it runs the target. 如果输入和输出都是最新的,则跳过目标。If both inputs and outputs are up to date, the target is skipped. 如果目标内的任务使用批处理,则 MSBuild 需要确定每批项的输入和输出是否是最新的。If a task inside of a target uses batching, MSBuild needs to determine if the inputs and outputs for each batch of items is up to date. 否则,目标将在每次命中时执行。Otherwise, the target is executed every time it's hit.

以下示例展示了包含使用 %(ItemMetadataName) 表示法的 Outputs 特性的 Target 元素。The following example shows a Target element that contains an Outputs attribute with the %(ItemMetadataName) notation. MSBuild 会基于 Color 项元数据将 Example 项列表划分为几个批次,并分析每个批次的输出文件的时间戳。MSBuild will divide the Example item list into batches based on the Color item metadata, and analyze the timestamps of the output files for each batch. 如果批处理的输出不是最新的,则运行目标。If the outputs from a batch aren't up to date, the target is run. 否则,跳过该目标。Otherwise, the target is skipped.

<Project
    xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

    <ItemGroup>
        <Example Include="Item1">
            <Color>Blue</Color>
        </Example>
        <Example Include="Item2">
            <Color>Red</Color>
        </Example>
    </ItemGroup>

    <Target Name="RunMyTask"
        Inputs="@(Example)"
        Outputs="%(Color)\MyFile.txt">
        <MyTask
            Sources = "@(Example)"
            Output = "%(Color)\MyFile.txt"/>
    </Target>

</Project>

有关目标批处理的另一个示例,请参阅目标批处理中的项元数据For another example of target batching, see Item metadata in target batching.

项和属性突变Item and property mutations

此部分介绍了在使用目标批处理或任务批处理时,如何理解更改属性和/或项元数据的影响。This section describes how to understand the effects of changing properties and/or item metadata, when using target batching or task batching.

由于目标批处理和任务批处理是两种不同的 MSBuild 操作,因此请务必确切了解在每种情况下 MSBuild 使用哪种形式的批处理。Because target batching and task batching are two different MSBuild operations, it is important to understand exactly which form of batching MSBuild uses in each case. 如果批处理语法 %(ItemMetadataName) 出现在目标内的任务中,但没有出现在目标特性中,则 MSBuild 使用任务批处理。When the batching syntax %(ItemMetadataName) appears in a task in a target, but not in an attribute on the Target, then MSBuild uses task batching. 指定目标批处理的唯一方法是,对目标特性(通常是 Outputs 特性)使用批处理语法。The only way to specify target batching is by using the batching syntax on a Target attribute, usually the Outputs attribute.

对于目标批处理和任务批处理,可以认为批处理是独立运行的。With both target batching and task batching, batches can be considered to run independently. 所有批处理都从复制处于相同初始状态的属性和项元数据值开始。All batches begin with a copy of the same initial state of property and item metadata values. 批处理执行期间的任何属性值突变对其他批处理都不可见。Any mutations of property values during batch execution is not visible to other batches. 请看下面的示例:Consider the following example:

  <ItemGroup>
    <Thing Include="2" Color="blue" />
    <Thing Include="1" Color="red" />
  </ItemGroup>

  <Target Name="DemoIndependentBatches">
    <ItemGroup>
      <Thing Condition=" '%(Color)' == 'blue' ">
        <Color>red</Color>
        <NeededColorChange>true</NeededColorChange>
      </Thing>
    </ItemGroup>
    <Message Importance="high"
             Text="Things: @(Thing->'%(Identity) is %(Color); needed change=%(NeededColorChange)')"/>
  </Target>

输出为:The output is:

Target DemoIndependentBatches:
  Things: 2 is red; needed change=true;1 is red; needed change=

目标中的 ItemGroup 是一项隐式任务,借助 Condition 特性中的 %(Color),执行的是任务批处理。The ItemGroup in the target is implicitly a task, and with the %(Color) in the Condition attribute, task batching is performed. 有两个批处理:一个用于红色,另一个用于蓝色。There are two batches: one for red and the other for blue. 只有在 %(Color) 元数据为 blue 时,才设置属性 %(NeededColorChange),并且此设置只影响在运行蓝色批处理时与条件匹配的单个项。The property %(NeededColorChange) is only set if the %(Color) metadata is blue, and the setting only affects the individual item that matched the condition when the blue batch was run. 尽管有 %(ItemMetadataName) 语法,Message 任务的 Text 特性也不会触发批处理,因为是在项转换内使用了它。The Message task's Text attribute does not trigger batching, despite the %(ItemMetadataName) syntax, because it is used inside an item transform.

批处理单独运行,而不是并行运行。Batches run independently, but not in parallel. 当你访问在批处理执行中更改的元数据值时,这就会产生差异。That makes a difference when you access metadata values that change in the batched execution. 如果设置基于批处理执行中的某元数据的属性,则此属性接受最后设置的值:In the case where you set a property based on some metadata in the batched execution, the property would take the last value set:

   <PropertyGroup>
       <SomeProperty>%(SomeItem.MetadataValue)</SomeProperty>
   </PropertyGroup>

在批处理执行后,此属性保留 %(MetadataValue) 的最终值。After batch execution, the property retains the final value of %(MetadataValue).

尽管批处理是独立运行的,但也请务必注意目标批处理与任务批处理之间的区别,并知道哪种类型适用于你的情况。Although batches run independently, it's important to consider the difference between target batching and task batching and know which type applies to your situation. 请参阅以下示例,以更好地理解这种区别的重要性。Consider the following example to understand better the importance of this distinction.

任务可以是隐式的,而不是显式的,当对隐式任务执行任务批处理时,这可能会令人困惑。Tasks can be implicit, rather than explicit, which can be confusing when task batching occurs with implicit tasks. Target 中出现 PropertyGroupItemGroup 元素时,组中的每个属性声明都会得到隐式处理,有点像独立的 CreatePropertyCreateItem 任务。When a PropertyGroup or ItemGroup element appears in a Target, each property declaration in the group is implicitly treated somewhat like a separate CreateProperty or CreateItem task. 也就是说,对目标执行批处理和不执行批处理(即 Outputs 特性中缺少 %(ItemMetadataName) 语法)时的行为是不同的。This means that the behavior is different when the target is batched, versus when the target is not batched (that is, when it lacks the %(ItemMetadataName) syntax in the Outputs attribute). 如果对目标执行批处理,ItemGroup 每目标执行一次,但如果对目标不执行批处理,就会使用任务批处理对 CreateItemCreateProperty 任务的隐式等效项进行批处理,所以目标只执行一次,并且组中的每个项或属性都使用任务批处理分别进行批处理。When the target is batched, the ItemGroup executes once per target, but when the target is not batched, the implicit equivalents of the CreateItem or CreateProperty tasks are batched using task batching, so the target only executes once, and each item or property in the group is batched separately using task batching.

下面的示例演示了在元数据突变时的目标批处理与任务批处理。The following example illustrates target batching vs. task batching in the case where metadata is mutated. 假设你在文件夹 A 和 B 中有一些文件:Consider a situation where you have folders A and B with some files:

A\1.stub
B\2.stub
B\3.stub

现在看看这两个类似项目的输出。Now look at the output of these two similar projects.

    <ItemGroup>
      <StubFiles Include="$(MSBuildThisFileDirectory)**\*.stub"/>

      <StubDirs Include="@(StubFiles->'%(RecursiveDir)')"/>
    </ItemGroup>

    <Target Name="Test1" AfterTargets="Build" Outputs="%(StubDirs.Identity)">
      <PropertyGroup>
        <ComponentDir>%(StubDirs.Identity)</ComponentDir>
        <ComponentName>$(ComponentDir.TrimEnd('\'))</ComponentName>
      </PropertyGroup>

      <Message Text=">> %(StubDirs.Identity) '$(ComponentDir)' '$(ComponentName)'"/>
    </Target>

输出为:The output is:

Test1:
  >> A\ 'A\' 'A'
Test1:
  >> B\ 'B\' 'B'

现在删除指定了目标批处理的 Outputs 特性。Now remove the Outputs attribute that specified target batching.

    <ItemGroup>
      <StubFiles Include="$(MSBuildThisFileDirectory)**\*.stub"/>

      <StubDirs Include="@(StubFiles->'%(RecursiveDir)')"/>
    </ItemGroup>

    <Target Name="Test1" AfterTargets="Build">
      <PropertyGroup>
        <ComponentDir>%(StubDirs.Identity)</ComponentDir>
        <ComponentName>$(ComponentDir.TrimEnd('\'))</ComponentName>
      </PropertyGroup>

      <Message Text=">> %(StubDirs.Identity) '$(ComponentDir)' '$(ComponentName)'"/>
    </Target>

输出为:The output is:

Test1:
  >> A\ 'B\' 'B'
  >> B\ 'B\' 'B'

我们注意到,标题 Test1 只打印了一次,但是在上一个示例中,它打印了两次。Notice that the heading Test1 is only printed once, but in the previous example, it was printed twice. 也就是说,目标未进行批处理。That means the target is not batched. 因此,输出是不同的,且令人困惑。And as a result, the output is confusingly different.

原因在于,在使用目标批处理时,每个目标批处理都使用自己的独立副本(其中包含所有属性和项)来执行目标中的所有内容,但如果省略 Outputs 特性,属性组中的各个行就会被视为可能已批处理的不同任务。The reason is that when using target batching, each target batch executes everything in the target with its own independent copy of all the properties and items, but when you omit the Outputs attribute, the individual lines in the property group are treated as distinct, potentially batched tasks. 在此示例中,批处理的是 ComponentDir 任务(它使用 %(ItemMetadataName) 语法),这样在 ComponentName 行执行时,ComponentDir 行的两个批处理都已完成,并且运行的第二个批处理确定了第二行中所示的值。In this case, the ComponentDir task is batched (it uses the %(ItemMetadataName) syntax), so that by the time the ComponentName line executes, both batches of the ComponentDir line have completed, and the second one that ran determined the value as seen in the second line.

使用元数据的属性函数Property functions using metadata

可通过包括元数据的属性函数控制批处理。Batching can be controlled by property functions that include metadata. 例如,应用于对象的For example,

$([System.IO.Path]::Combine($(RootPath),%(Compile.Identity)))

使用 Combine 合并根文件夹路径与编译项路径。uses Combine to combine a root folder path with a Compile item path.

属性函数可能不会出现在元数据值内。Property functions may not appear within metadata values. 例如,应用于对象的For example,

%(Compile.FullPath.Substring(0,3))

是不允许的。is not allowed.

有关属性函数详细信息,请参阅属性函数For more information about property functions, see Property functions.

请参阅See also