item-management

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

MSBuild Item Management Patterns

MSBuild 项组管理模式

Canonical patterns for working with item groups, from
Microsoft.Common.CurrentVersion.targets
.
来自
Microsoft.Common.CurrentVersion.targets
的项组操作标准模式。

Include / Remove / Update — Three Operations

Include / Remove / Update — 三种操作

OperationPurposeWhen to use
Include
Add new items to the groupCreating items with identity + metadata
Remove
Remove items matching a patternExcluding files or clearing a group
Update
Modify metadata on existing itemsAdding/changing metadata without re-adding
操作用途使用场景
Include
向组中添加新项创建带有标识+元数据的项
Remove
删除匹配模式的项排除文件或清空组
Update
修改现有项的元数据添加/更改元数据而无需重新添加项

Include — Add Items

Include — 添加项

xml
<ItemGroup>
  <Compile Include="Generated\*.cs">
    <AutoGen>true</AutoGen>
  </Compile>
</ItemGroup>
xml
<ItemGroup>
  <Compile Include="Generated\*.cs">
    <AutoGen>true</AutoGen>
  </Compile>
</ItemGroup>

Remove — Subtract Items

Remove — 删除项

xml
<ItemGroup>
  <!-- Remove specific items -->
  <Reference Remove="$(AdditionalExplicitAssemblyReferences)" />

  <!-- Set subtraction: prior minus current -->
  <_CleanOrphanFileWrites Include="@(_CleanPriorFileWrites)"
      Exclude="@(_CleanCurrentFileWrites)" />

  <!-- Clear an entire group -->
  <_Temporary Remove="@(_Temporary)" />
</ItemGroup>
xml
<ItemGroup>
  <!-- 删除特定项 -->
  <Reference Remove="$(AdditionalExplicitAssemblyReferences)" />

  <!-- 集合减法:原有项减去当前项 -->
  <_CleanOrphanFileWrites Include="@(_CleanPriorFileWrites)"
      Exclude="@(_CleanCurrentFileWrites)" />

  <!-- 清空整个组 -->
  <_Temporary Remove="@(_Temporary)" />
</ItemGroup>

Update — Modify Existing Items

Update — 修改现有项

xml
<ItemGroup>
  <EmbeddedResource Update="@(EmbeddedResource)"
      Condition="'%(NuGetPackageId)' == 'Microsoft.CodeAnalysis.Collections'">
    <GenerateSource>true</GenerateSource>
    <ClassName>Microsoft.CodeAnalysis.Collections.SR</ClassName>
  </EmbeddedResource>
</ItemGroup>
Update
does not add items — it only modifies items already in the group.
xml
<ItemGroup>
  <EmbeddedResource Update="@(EmbeddedResource)"
      Condition="'%(NuGetPackageId)' == 'Microsoft.CodeAnalysis.Collections'">
    <GenerateSource>true</GenerateSource>
    <ClassName>Microsoft.CodeAnalysis.Collections.SR</ClassName>
  </EmbeddedResource>
</ItemGroup>
Update
不会添加项——它仅修改组中已存在的项。

Item Batching — %(Metadata)

项批处理 — %(Metadata)

When
%(Metadata)
appears in target attributes or task parameters, MSBuild batches execution per unique metadata value.
%(Metadata)
出现在目标属性或任务参数中时,MSBuild会按唯一元数据值分批执行。

Target-level batching (Outputs)

目标级批处理(Outputs)

xml
<Target Name="GenerateSatelliteAssemblies"
    Inputs="$(MSBuildAllProjects);@(_SatelliteAssemblyResourceInputs)"
    Outputs="$(IntermediateOutputPath)%(Culture)\$(TargetName).resources.dll">
  <!-- Runs once per unique Culture value -->
</Target>
xml
<Target Name="GenerateSatelliteAssemblies"
    Inputs="$(MSBuildAllProjects);@(_SatelliteAssemblyResourceInputs)"
    Outputs="$(IntermediateOutputPath)%(Culture)\$(TargetName).resources.dll">
  <!-- 每个唯一Culture值执行一次 -->
</Target>

Task-level batching

任务级批处理

xml
<Copy SourceFiles="@(_SourceItems)"
    DestinationFiles="@(_SourceItems->'$(OutDir)%(TargetPath)')">
</Copy>
xml
<Copy SourceFiles="@(_SourceItems)"
    DestinationFiles="@(_SourceItems->'$(OutDir)%(TargetPath)')">
</Copy>

Per-item filtering with Condition

使用Condition进行逐项过滤

xml
<ItemGroup>
  <_ResxOutput Include="@(EmbeddedResource->'%(OutputResource)')"
      Condition="'%(EmbeddedResource.WithCulture)' == 'false'" />
</ItemGroup>
xml
<ItemGroup>
  <_ResxOutput Include="@(EmbeddedResource->'%(OutputResource)')"
      Condition="'%(EmbeddedResource.WithCulture)' == 'false'" />
</ItemGroup>

Batching rules

批处理规则

  • %(Metadata)
    in
    Condition
    or
    Outputs
    → target batches per unique value.
  • %(Metadata)
    in task parameters → task batches per unique value.
  • Do not mix
    %()
    from different item groups
    in the same expression — this causes a cross-product (see Common Pitfalls).
  • %(Metadata)
    Condition
    Outputs
    中 → 目标按唯一值分批执行。
  • %(Metadata)
    在任务参数中 → 任务按唯一值分批执行。
  • 不要在同一表达式中混合来自不同项组的
    %()
    ——这会导致交叉产品批处理(请参见常见问题)。

Item Transforms — @(Item->'expression')

项转换 — @(Item->'expression')

Transforms create new item lists by applying an expression to each item:
xml
<!-- Transform file paths to destinations -->
<Copy SourceFiles="@(IntermediateAssembly)"
    DestinationFiles="@(IntermediateAssembly->'$(OutDir)%(Filename)%(Extension)')"/>

<!-- Transform with separator for display -->
<Message Text="Files: @(Compile->'%(Filename)', ', ')" />
转换通过对每个项应用表达式来创建新的项列表:
xml
<!-- 将文件路径转换为目标路径 -->
<Copy SourceFiles="@(IntermediateAssembly)"
    DestinationFiles="@(IntermediateAssembly->'$(OutDir)%(Filename)%(Extension)')"/>

<!-- 使用分隔符转换用于显示 -->
<Message Text="Files: @(Compile->'%(Filename)', ', ')" />

Exclude Pattern — Set Subtraction on Include

排除模式 — Include上的集合减法

xml
<ItemGroup>
  <Compile Include="**\*.cs" Exclude="Generated\**;Tests\**" />
</ItemGroup>
Exclude
only works on
Include
— it cannot be used with
Update
or
Remove
.
xml
<ItemGroup>
  <Compile Include="**\*.cs" Exclude="Generated\**;Tests\**" />
</ItemGroup>
Exclude
仅适用于
Include
——不能与
Update
Remove
一起使用。

Conditional Item Inclusion

条件性项包含

xml
<!-- Condition on ItemGroup — all or nothing -->
<ItemGroup Condition="'$(NetCoreBuild)' == 'true'">
  <PackageReference Include="System.IO.Pipelines" />
</ItemGroup>

<!-- Condition on individual items -->
<ItemGroup>
  <PackageReference Include="System.IO.Pipelines"
      Condition="'$(NetCoreBuild)' == 'true'" />
</ItemGroup>
xml
<!-- 对ItemGroup设置条件 — 全包含或全不包含 -->
<ItemGroup Condition="'$(NetCoreBuild)' == 'true'">
  <PackageReference Include="System.IO.Pipelines" />
</ItemGroup>

<!-- 对单个项设置条件 -->
<ItemGroup>
  <PackageReference Include="System.IO.Pipelines"
      Condition="'$(NetCoreBuild)' == 'true'" />
</ItemGroup>

PrivateAssets on Tool/Analyzer Packages

工具/分析器包的PrivateAssets

xml
<ItemGroup>
  <PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" PrivateAssets="all" />
  <PackageReference Include="StyleCop.Analyzers" PrivateAssets="all" />
</ItemGroup>
xml
<ItemGroup>
  <PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" PrivateAssets="all" />
  <PackageReference Include="StyleCop.Analyzers" PrivateAssets="all" />
</ItemGroup>

Common Pitfalls

常见问题

Cross-product batching

交叉产品批处理

Referencing
%(Metadata)
from two different item groups creates O(N×M) executions:
xml
<!-- BAD: Cross-product of @(Source) × @(Config) -->
<Exec Command="process %(Source.Identity) with %(Config.Identity)" />

<!-- GOOD: Reference one group via batching, the other via property -->
<Exec Command="process %(Source.Identity) with $(ConfigFile)" />
引用来自两个不同项组的
%(Metadata)
会产生O(N×M)次执行:
xml
<!-- 错误:@(Source) × @(Config) 的交叉产品 -->
<Exec Command="process %(Source.Identity) with %(Config.Identity)" />

<!-- 正确:通过批处理引用一个组,通过属性引用另一个组 -->
<Exec Command="process %(Source.Identity) with $(ConfigFile)" />

Generated files in source tree

源目录中的生成文件

Write to
$(IntermediateOutputPath)
(obj/), not the source directory. Source-tree generation pollutes version control and can cause duplicate compilation via globs.
写入
$(IntermediateOutputPath)
(obj/),而不是源目录。源目录生成会污染版本控制,并可能通过通配符导致重复编译。

Missing FileWrites

缺失FileWrites

Every file created during a target must be added to
@(FileWrites)
for
dotnet clean
support.
目标期间创建的每个文件都必须添加到
@(FileWrites)
中,以支持
dotnet clean