MSBuild 項目

MSBuild 項目是建置系統的輸入,而且它們通常代表檔案 (檔案是在 Include 屬性中指定)。 項目 (Item) 會依據它們的項目 (Element) 名稱分組為項目 (Item) 類型。 項目類型是具名的項目清單,可用來做為工作的參數。 工作會使用項目值來執行建置程序的步驟。

由於項目是根據其所屬的項目類型來命名,因此可交換使用「項目」和「項目值」等詞彙。

在專案檔中建立項目

您會將專案檔中的項目 (Item) 宣告為 ItemGroup 項目 (Element) 的子項目 (Element)。 有效的項目名稱以大寫或小寫字母或底線 (_) 開頭;有效的後續字元包括英數字元 (字母或數字)、底線和連字號 (-)。 子項目 (Element) 的名稱是項目 (Item) 的類型。 項目 (Element) 的 Include 屬性會指定要包含於該項目 (Item) 類型的項目 (Item) (檔案)。 例如,下列 XML 會建立名為 Compile 的項目類型,其中包含兩個檔案。

<ItemGroup>
    <Compile Include = "file1.cs"/>
    <Compile Include = "file2.cs"/>
</ItemGroup>

file2.cs 項目不會取代 file1.cs 項目;而是會將檔案名稱附加至 Compile 項目類型的值清單。

下列 XML 會在一個 Include 屬性中宣告這兩個檔案,來建立相同的項目類型。 請注意,檔案名稱是以分號分隔的。

<ItemGroup>
    <Compile Include = "file1.cs;file2.cs"/>
</ItemGroup>

Include 屬性是相對於專案檔資料夾 ($(MSBuildProjectPath)) 解譯的路徑,即使項目位於匯入的檔案中,例如 .targets 檔案。

執行期間建立項目

Target 項目 (Element) 以外的項目 (Item) 值是在組建的評估階段所指派。 在後續的執行階段,可以使用下列方式來建立或修改項目:

  • 任何工作均可發出項目。 若要發出項目 (Item),Task 項目 (Element) 必須含有具 ItemName 屬性的子系 Output 項目 (Element)。

  • CreateItem 工作可以發出項目。 這種使用方式已過時。

  • Target 元素可能會包含 ItemGroup 元素,其中可能包含 Item 元素。

參考專案檔中的項目

若要在整個專案檔中參考項目類型,您可以使用語法 @(ItemType)。 例如,您應該使用 @(Compile),來參考前一個範例中的項目類型。 使用下列語法,您可以藉由指定項目類型做為工作的參數,來將項目傳遞給該工作。 如需詳細資訊,請參閱如何:選取要建置的檔案

根據預設,項目類型的項目在展開時會以分號 (;) 分隔。 您可以使用語法 @(ItemType, 'separator') 來指定非預設的分隔符號。 如需詳細資訊,請參閱如何:顯示以逗號分隔的項目清單

使用萬用字元指定項目

您可以使用 ***? 萬用字元指定一組檔案作為組建的輸入,而不是個別列出每個檔案。

  • ? 萬用字元會比對單一字元。
  • * 萬用字元會比對零或多個字元。
  • ** 萬用字元會循序比對部分路徑。

例如,您可以在專案檔中使用下列項目,來指定包含該專案檔之目錄中的所有 .cs 檔案。

<CSFile Include="*.cs"/>

下列項目會選取 D: 磁碟機上的所有 .vb 檔案:

<VBFile Include="D:/**/*.vb"/>

如果您想要在沒有萬用字元展開的項目中包含常值 *? 字元,您必須逸出萬用字元

如需萬用字元的詳細資訊,請參閱如何:選取要建置的檔案

使用 Exclude 屬性

Item 項目 (Element) 可以包含 Exclude 屬性,以從項目 (Item) 類型中排除特定的項目 (Item) (檔案)。 Exclude 屬性通常會與萬用字元搭配使用。 例如,下列 XML 會將目錄中的每個 .cs 檔案新增至 CSFile 項目類型,但 DoNotBuild.cs 檔案除外。

<ItemGroup>
    <CSFile  Include="*.cs"  Exclude="DoNotBuild.cs"/>
</ItemGroup>

Exclude 屬性只會影響包含這兩者之 Item 項目 (Element) 中由 Include 屬性所加入的項目 (Item)。 下列範例不會排除 Form1.cs 檔案,這是在前一個 Item 項目中新增的檔案。

<Compile Include="*.cs" />
<Compile Include="*.res" Exclude="Form1.cs">

如需詳細資訊,請參閱如何︰從組建中排除檔案

項目中繼資料

IncludeExclude 屬性中,除了資訊,項目可能還會包含中繼資料。 若工作需要更多關於項目的資訊,就會使用此中繼資料,或使用此中繼資料來批次處理工作和目標。 如需詳細資訊,請參閱批次處理

中繼資料是一個索引鍵值組的集合,可在專案檔中宣告為 Item 項目的子項目。 子項目的名稱是中繼資料的名稱,而子項目的值是中繼資料的值。

中繼資料會與包含它的 Item 項目相關聯。 例如,下列 XML 會將值為 FrCulture 中繼資料新增至 CSFile 項目類型的 one.cstwo.cs 項目。

<ItemGroup>
    <CSFile Include="one.cs;two.cs">
        <Culture>Fr</Culture>
    </CSFile>
</ItemGroup>

項目可以有零或多個中繼資料值。 您可以隨時變更中繼資料值。 如果您將中繼資料設為空值,就能有效地從組建中移除它。

在專案檔中參考項目中繼資料

您可以使用語法 %(ItemMetadataName),在整個專案檔中參考項目中繼資料。 如果發生模稜兩可的情況,您可以使用項目類型的名稱來限定參考。 例如,您可以指定 %(ItemType.ItemMetaDataName)。 下列範例會使用 Display 中繼資料來批次處理 Message 工作。 如需如何使用項目中繼資料進行批次處理的詳細資訊,請參閱工作批次處理中的項目中繼資料

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <ItemGroup>
        <Stuff Include="One.cs" >
            <Display>false</Display>
        </Stuff>
        <Stuff Include="Two.cs">
            <Display>true</Display>
        </Stuff>
    </ItemGroup>
    <Target Name="Batching">
        <Message Text="@(Stuff)" Condition=" '%(Display)' == 'true' "/>
    </Target>
</Project>

已知的項目中繼資料

將項目加入至項目類型時,即會為該項目指派一些已知的中繼資料。 例如,所有項目都具有已知的中繼資料 %(Filename),其值為項目的檔案名稱 (不含副檔名)。 如需詳細資訊,請參閱已知的項目中繼資料

使用中繼資料轉換項目類型

您可以使用中繼資料,來將項目清單轉換為新的項目清單。 例如,您可以使用運算式 @(CppFiles -> '%(Filename).obj'),將含有代表 .cpp 檔案之項目的項目類型 CppFiles 轉換為 .obj 檔案的對應清單。

下列程式碼會建立 CultureResource 項目類型,其中包含所有具 Culture 中繼資料之 EmbeddedResource 項目的複本。 Culture 中繼資料值會成為新中繼資料 CultureResource.TargetDirectory 的值。

<Target Name="ProcessCultureResources">
    <ItemGroup>
        <CultureResource Include="@(EmbeddedResource)"
            Condition="'%(EmbeddedResource.Culture)' != ''">
            <TargetDirectory>%(EmbeddedResource.Culture) </TargetDirectory>
        </CultureResource>
    </ItemGroup>
</Target>

如需項目的更多作業,請參閱 MSBuild 項目函式轉換

項目定義

您可以使用 ItemDefinitionGroup 元素,將預設的中繼資料加入至任何項目類型。 如同已知的中繼資料,預設的中繼資料會與您指定之項目類型的所有項目相關聯。 您可以在項目定義中明確覆寫預設的中繼資料。 例如,下列 XML 會為 Compile 項目 one.csthree.cs,提供值為 "Monday" 的中繼資料 BuildDay。 此程式碼會為項目 two.cs 提供值為 "Tuesday" 的中繼資料 BuildDay

<ItemDefinitionGroup>
    <Compile>
        <BuildDay>Monday</BuildDay>
    </Compile>
</ItemDefinitionGroup>
<ItemGroup>
    <Compile Include="one.cs;three.cs" />
    <Compile Include="two.cs">
        <BuildDay>Tuesday</BuildDay>
    </Compile>
</ItemGroup>

如需詳細資訊,請參閱項目定義

目標 ItemGroup 中的項目屬性

Target 元素可能會包含 ItemGroup 元素,其中可能包含 Item 元素。 如果已針對 ItemGroup (位於 Target) 中的項目指定本節中的屬性,則它們是有效的。

移除屬性

Remove 屬性會移除項目類型中的特定項目 (檔案)。 此屬性是在 .NET Framework 3.5 中引入的 (僅限目標內部)。 從 MSBuild 15.0 開始,支援內部和外部目標。

下列範例會從 Compile 項目類型移除每個 .config 檔案。

<Target>
    <ItemGroup>
        <Compile Remove="*.config"/>
    </ItemGroup>
</Target>

MatchOnMetadata 屬性

MatchOnMetadata 屬性只適用於參考其他項目 (例如,Remove="@(Compile);@(Content)") 的 Remove 屬性,並指示 Remove 作業根據指定中繼資料名稱的值比對項目,而不是根據項目值進行比對。

B Remove="@(A)" MatchOnMetadata="M" 的比對規則:從具有中繼資料 MB 中移除所有項目,其針對 M 的中繼資料值 V 符合 A 中具有值為 V 之中繼資料 M 的任何項目。

<Project>
  <ItemGroup>
    <A Include='a1' M1='1' M2='a' M3="e"/>
    <A Include='b1' M1='2' M2='x' M3="f"/>
    <A Include='c1' M1='3' M2='y' M3="g"/>
    <A Include='d1' M1='4' M2='b' M3="h"/>

    <B Include='a2' M1='x' m2='c' M3="m"/>
    <B Include='b2' M1='2' m2='x' M3="n"/>
    <B Include='c2' M1='2' m2='x' M3="o"/>
    <B Include='d2' M1='3' m2='y' M3="p"/>
    <B Include='e2' M1='3' m2='Y' M3="p"/>
    <B Include='f2' M1='4'        M3="r"/>
    <B Include='g2'               M3="s"/>

    <B Remove='@(A)' MatchOnMetadata='M1;M2'/>
  </ItemGroup>

  <Target Name="PrintEvaluation">
    <Message Text="%(B.Identity) M1='%(B.M1)' M2='%(B.M2)' M3='%(B.M3)'" />
  </Target>
</Project>

在此範例中,項目值 b2c2d2 會從項目 B 中移除,因為:

  • 來自 Bb2c2 會與 M1=2M2=x 上來自 Ab1 進行比對
  • 來自 Bd2 會與 M1=3M2=y 上來自 Ac1 進行比對

Message 工作會輸出下列內容:

  a2 M1='x' M2='c' M3='m'
  e2 M1='3' M2='Y' M3='p'
  f2 M1='4' M2='' M3='r'
  g2 M1='' M2='' M3='s'

MSBuildMatchOnMetadata 的範例使用方式:

      <_TransitiveItemsToCopyToOutputDirectory Remove="@(_ThisProjectItemsToCopyToOutputDirectory)" MatchOnMetadata="TargetPath" MatchOnMetadataOptions="PathLike" />

這一行會從 _ThisProjectItemsToCopyToOutputDirectory 中移除與 _TransitiveItemsToCopyToOutputDirectory 中的項目具有相同 TargetPath 中繼資料值的項目

MatchOnMetadataOptions 屬性

指定 MatchOnMetadata 用來比對項目之間中繼資料值的字串比對策略 (不區分大小寫的情況下,中繼資料名稱一律相符)。 可能的值為 CaseSensitiveCaseInsensitivePathLike。 預設值是 CaseSensitive

PathLike 會將路徑感知正規化套用至正規化斜線方向、忽略尾端斜線、排除 ... 等值,並將所有相對路徑變成目前目錄的絕對路徑。

KeepMetadata 屬性

如果在目標內產生項目 (Item),則 Item 項目 (Element) 可以包含 KeepMetadata 屬性。 如果指定了這個屬性,只會將以分號分隔之名稱清單中指定的中繼資料從來源項目傳輸到目標項目。 若此屬性的值為空值,就相當於未指定它。 KeepMetadata 屬性是在 .NET Framework 4.5 中引入的。

下列範例示範如何使用 KeepMetadata 屬性。

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

    <ItemGroup>
        <FirstItem Include="rhinoceros">
            <Class>mammal</Class>
            <Size>large</Size>
        </FirstItem>

    </ItemGroup>
    <Target Name="MyTarget">
        <ItemGroup>
            <SecondItem Include="@(FirstItem)" KeepMetadata="Class" />
        </ItemGroup>

        <Message Text="FirstItem: %(FirstItem.Identity)" />
        <Message Text="  Class: %(FirstItem.Class)" />
        <Message Text="  Size:  %(FirstItem.Size)"  />

        <Message Text="SecondItem: %(SecondItem.Identity)" />
        <Message Text="  Class: %(SecondItem.Class)" />
        <Message Text="  Size:  %(SecondItem.Size)"  />
    </Target>
</Project>

<!--
Output:
  FirstItem: rhinoceros
    Class: mammal
    Size:  large
  SecondItem: rhinoceros
    Class: mammal
    Size:
-->

RemoveMetadata 屬性

如果在目標內產生項目 (Item),則 Item 項目 (Element) 可以包含 RemoveMetadata 屬性。 如果指定了此屬性,則會將所有中繼資料從來源項目傳輸到目標項目,但名稱位於以分號分隔之名稱清單內的中繼資料除外。 若此屬性的值為空值,就相當於未指定它。 RemoveMetadata 屬性是在 .NET Framework 4.5 中引入的。

下列範例示範如何使用 RemoveMetadata 屬性。

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

    <PropertyGroup>
        <MetadataToRemove>Size;Material</MetadataToRemove>
    </PropertyGroup>

    <ItemGroup>
        <Item1 Include="stapler">
            <Size>medium</Size>
            <Color>black</Color>
            <Material>plastic</Material>
        </Item1>
    </ItemGroup>

    <Target Name="MyTarget">
        <ItemGroup>
            <Item2 Include="@(Item1)" RemoveMetadata="$(MetadataToRemove)" />
        </ItemGroup>

        <Message Text="Item1: %(Item1.Identity)" />
        <Message Text="  Size:     %(Item1.Size)" />
        <Message Text="  Color:    %(Item1.Color)" />
        <Message Text="  Material: %(Item1.Material)" />
        <Message Text="Item2: %(Item2.Identity)" />
        <Message Text="  Size:     %(Item2.Size)" />
        <Message Text="  Color:    %(Item2.Color)" />
        <Message Text="  Material: %(Item2.Material)" />
    </Target>
</Project>

<!--
Output:
  Item1: stapler
    Size:     medium
    Color:    black
    Material: plastic
  Item2: stapler
    Size:
    Color:    black
    Material:
-->

如需項目的更多作業,請參閱 MSBuild 項目函式

KeepDuplicates 屬性

如果在目標內產生項目 (Item),則 Item 項目 (Element) 可以包含 KeepDuplicates 屬性。 KeepDuplicatesBoolean 屬性,會指定項目如果與現有項目完全重複,是否應加入目標群組。

如果來源和目標項目具有相同的 Include 值,但中繼資料不同,則即使 KeepDuplicates 設定為 false,也會加入該項目。 若此屬性的值為空值,就相當於未指定它。 KeepDuplicates 屬性是在 .NET Framework 4.5 中引入的。

下列範例示範如何使用 KeepDuplicates 屬性。

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

    <ItemGroup>
        <Item1 Include="hourglass;boomerang" />
        <Item2 Include="hourglass;boomerang" />
    </ItemGroup>

    <Target Name="MyTarget">
        <ItemGroup>
            <Item1 Include="hourglass" KeepDuplicates="false" />
            <Item2 Include="hourglass" />
        </ItemGroup>

        <Message Text="Item1: @(Item1)" />
        <Message Text="  %(Item1.Identity)  Count: @(Item1->Count())" />
        <Message Text="Item2: @(Item2)" />
        <Message Text="  %(Item2.Identity)  Count: @(Item2->Count())" />
    </Target>
</Project>

<!--
Output:
  Item1: hourglass;boomerang
    hourglass  Count: 1
    boomerang  Count: 1
  Item2: hourglass;boomerang;hourglass
    hourglass  Count: 2
    boomerang  Count: 1
-->

由於除了項目值之外,KeepDuplicates 屬性也會考量項目的中繼資料,因此務必知道中繼資料發生什麼事情。 例如,請參閱使用中繼資料項目函式時偵測重複項目

更新目標外部 ItemGroup 中項目的中繼資料

目標外部的項目可透過 Update 屬性更新其現有的中繼資料。 此屬性適用於目標之下的項目。

<Project>
    <PropertyGroup>
        <MetadataToUpdate>pencil</MetadataToUpdate>
    </PropertyGroup>

    <ItemGroup>
        <Item1 Include="stapler">
            <Size>medium</Size>
            <Color>black</Color>
            <Material>plastic</Material>
        </Item1>
        <Item1 Include="pencil">
            <Size>small</Size>
            <Color>yellow</Color>
            <Material>wood</Material>
        </Item1>
        <Item1 Include="eraser">
            <Color>red</Color>
        </Item1>
        <Item1 Include="notebook">
            <Size>large</Size>
            <Color>white</Color>
            <Material>paper</Material>
        </Item1>

        <Item2 Include="notebook">
            <Size>SMALL</Size>
            <Color>YELLOW</Color>
        </Item2>

        <!-- Metadata can be expressed either as attributes or as elements -->
        <Item1 Update="$(MetadataToUpdate);stapler;er*r;@(Item2)" Price="10" Material="">
            <Color>RED</Color>
        </Item1>
    </ItemGroup>

    <Target Name="MyTarget">
        <Message Text="Item1: %(Item1.Identity)
    Size: %(Item1.Size)
    Color: %(Item1.Color)
    Material: %(Item1.Material)
    Price: %(Item1.Price)" />
    </Target>
</Project>

<!--  
Item1: stapler
    Size: medium
    Color: RED
    Material:
    Price: 10
Item1: pencil
    Size: small
    Color: RED
    Material:
    Price: 10
Item1: eraser
    Size:
    Color: RED
    Material:
    Price: 10
Item1: notebook
    Size: large
    Color: RED
    Material:
    Price: 10
-->

在 MSBuild 16.6 版和更新版本中,Update 屬性支援合格的中繼資料參考,以利從兩個或多個項目匯入中繼資料。

<Project>
    <ItemGroup>
        <Item1 Include="stapler">
            <Size>medium</Size>
            <Color>black</Color>
            <Material>plastic</Material>
        </Item1>
        <Item1 Include="pencil">
            <Size>small</Size>
            <Color>yellow</Color>
            <Material>wood</Material>
        </Item1>
        <Item1 Include="eraser">
            <Size>small</Size>
            <Color>red</Color>
            <Material>gum</Material>
        </Item1>
        <Item1 Include="notebook">
            <Size>large</Size>
            <Color>white</Color>
            <Material>paper</Material>
        </Item1>

        <Item2 Include="pencil">
            <Size>MEDIUM</Size>
            <Color>RED</Color>
            <Material>PLASTIC</Material>
            <Price>10</Price>
        </Item2>

        <Item3 Include="notebook">
            <Size>SMALL</Size>
            <Color>BLUE</Color>
            <Price>20</Price>
        </Item3>

        <!-- Metadata can be expressed either as attributes or as elements -->
        <Item1 Update="@(Item2);er*r;@(Item3)" Size="%(Size)" Color="%(Item2.Color)" Price="%(Item3.Price)" Model="2020">
            <Material Condition="'%(Item2.Material)' != ''">Premium %(Item2.Material)</Material>
        </Item1>
    </ItemGroup>

    <Target Name="MyTarget">
        <Message Text="Item1: %(Item1.Identity)
    Size: %(Item1.Size)
    Color: %(Item1.Color)
    Material: %(Item1.Material)
    Price: %(Item1.Price)
    Model: %(Item1.Model)" />
    </Target>
</Project>

<!--  
Item1: stapler
    Size: medium
    Color: black
    Material: plastic
    Price:
    Model:
Item1: pencil
    Size: small
    Color: RED
    Material: Premium PLASTIC
    Price:
    Model: 2020
Item1: eraser
    Size: small
    Color:
    Material: gum
    Price:
    Model: 2020
Item1: notebook
    Size: large
    Color:
    Material: paper
    Price: 20
    Model: 2020
-->

備註:

  • 不合格的中繼資料 (%(MetadataName)) 會繫結至正在更新的項目類型 (上述範例中的 Item1)。 合格的中繼資料 (%(Item2.Color)) 會在 Update 運算式中擷取的相符項目類型集合內繫結。
  • 如果項目在多個參照項目之內和之間多次符合:
    • 系統會從每個參照項目類型擷取最後出現的項目 (因此每個項目類型都會擷取一個項目)。
    • 這符合目標之下的工作項目批次處理行為。
  • 其中可以放置 %() 參考:
    • 中繼資料
    • 中繼資料條件
  • 中繼資料名稱比對不區分大小寫。

在目標的 ItemGroup 中更新項目的中繼資料

中繼資料也可經由表達能力比 Update 差的語法在目標內修改:

<Project>
    <ItemGroup>
        <Item1 Include="stapler">
            <Size>medium</Size>
            <Color>black</Color>
            <Material>plastic</Material>
        </Item1>
        <Item1 Include="pencil">
            <Size>small</Size>
            <Color>yellow</Color>
            <Material>wood</Material>
        </Item1>
        <Item1 Include="eraser">
            <Size>small</Size>
            <Color>red</Color>
            <Material>gum</Material>
        </Item1>
        <Item1 Include="notebook">
            <Size>large</Size>
            <Color>white</Color>
            <Material>paper</Material>
        </Item1>

        <Item2 Include="pencil">
            <Size>MEDIUM</Size>
            <Color>RED</Color>
            <Material>PLASTIC</Material>
            <Price>10</Price>
        </Item2>

        <Item2 Include="ruler">
            <Color>GREEN</Color>
        </Item2>

    </ItemGroup>

    <Target Name="MyTarget">
        <ItemGroup>
            <!-- Metadata can be expressed either as attributes or as elements -->
            <Item1 Size="GIGANTIC" Color="%(Item2.Color)">
                <Material Condition="'%(Item2.Material)' != ''">Premium %(Item2.Material)</Material>
            </Item1>
        </ItemGroup>

        <Message Text="Item1: %(Item1.Identity)
    Size: %(Item1.Size)
    Color: %(Item1.Color)
    Material: %(Item1.Material)
    Price: %(Item1.Price)
    Model: %(Item1.Model)" />
    </Target>
</Project>

<!--  
Item1: stapler
    Size: GIGANTIC
    Color: GREEN
    Material: Premium PLASTIC
    Price:
    Model:
Item1: pencil
    Size: GIGANTIC
    Color: GREEN
    Material: Premium PLASTIC
    Price:
    Model:
Item1: eraser
    Size: GIGANTIC
    Color: GREEN
    Material: Premium PLASTIC
    Price:
    Model:
Item1: notebook
    Size: GIGANTIC
    Color: GREEN
    Material: Premium PLASTIC
    Price:
    Model:
-->