Öğe meta verilerine göre görevleri veya hedefleri toplu olarak çalıştırma

MSBuild, öğe listelerini öğe meta verilerine göre farklı kategorilere veya toplu işlemlere böler ve her toplu işlemle bir kez bir hedef veya görev çalıştırır.

Görev toplu işlemi

Görev toplu işlemi, öğe listelerini farklı toplu işlemlere bölmenin ve bu toplu işlerden her birini ayrı olarak bir göreve geçirmenin bir yolunu sağlayarak proje dosyalarınızı basitleştirmenizi sağlar. Bu, bir proje dosyasının birkaç kez çalıştırılsa bile görevi ve özniteliklerini yalnızca bir kez bildirilmesi gerektiği anlamına gelir.

MSBuild'in görev özniteliklerinden birinde gösterimi kullanarak %(ItemMetaDataName) bir görevle toplu işlem gerçekleştirmesini istediğinizi belirtirsiniz. Aşağıdaki örnek, öğe listesini öğe meta veri değerine göre Color toplu işlemlere böler Example ve toplu işlemlerin MyTask her birini göreve ayrı olarak geçirir.

Dekont

Görev özniteliklerinin başka bir yerinde öğe listesine başvurmuyorsanız veya meta veri adı belirsiz olabilirse, toplu işlem için kullanılacak öğe meta veri değerini tam olarak nitelemesi için %(<ItemCollection.ItemMetaDataName>) gösterimini kullanabilirsiniz.

<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>

Daha ayrıntılı toplu işleme örnekleri için bkz . Görev toplu işlemede öğe meta verileri.

Hedef toplu işlem

MSBuild, hedefi çalıştırmadan önce hedefin giriş ve çıkışlarının güncel olup olmadığını denetler. Hem girişler hem de çıkışlar güncelse hedef atlanır. Hedefin içindeki bir görev toplu işlem kullanıyorsa, MSBuild'in her öğe toplu işlemi için giriş ve çıkışların güncel olup olmadığını belirlemesi gerekir. Aksi takdirde hedef her isabet edildiğinde yürütülür.

Aşağıdaki örnekte, gösterimine sahip %(ItemMetadataName) bir öznitelik içeren bir Outputs öğe gösterilmektedirTarget. MSBuild, öğe listesini öğe meta verilerine göre Color toplu işlemlere böler Example ve her toplu iş için çıkış dosyalarının zaman damgalarını analiz eder. Toplu iş çıktıları güncel değilse hedef çalıştırılır. Aksi takdirde hedef atlanır.

<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>

Hedef toplu işlemeye başka bir örnek için bkz . Hedef toplu işlemde öğe meta verileri.

Madde ve özellik mutasyonları

Bu bölümde, hedef toplu işlem veya görev toplu işlemi kullanılırken özellikleri ve/veya öğe meta verilerini değiştirmenin etkilerinin nasıl anlaşıldığı açıklanmaktadır.

Hedef toplu işlem ve görev toplu işlemi iki farklı MSBuild işlemi olduğundan, MSBuild'in her durumda hangi toplu işlem biçimini kullandığını tam olarak anlamak önemlidir. Toplu iş söz dizimi %(ItemMetadataName) hedefteki bir görevde göründüğünde ancak Hedefteki bir öznitelikte görünmediğinde, MSBuild görev toplu işlemini kullanır. Hedef toplu işlemi belirtmenin tek yolu, genellikle özniteliği olan Target özniteliğinde toplu iş söz dizimini Outputs kullanmaktır.

Hem hedef toplu işlem hem de görev toplu işlemiyle toplu işlerin bağımsız olarak çalıştırılacağı düşünülebilir. Tüm toplu işlemler, özellik ve öğe meta veri değerlerinin aynı ilk durumunun bir kopyasıyla başlar. Toplu yürütme sırasında özellik değerlerinin mutasyonları diğer toplu işlemler tarafından görülemez. Aşağıdaki örneği inceleyin:

  <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>

Çıktı şu olur:

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

ItemGroup hedefteki örtük olarak bir görevdir ve %(Color) özniteliğinde Condition ile görev toplu işlemi gerçekleştirilir. Biri kırmızı, diğeri mavi için iki toplu iş vardır. özelliği %(NeededColorChange) yalnızca meta veriler maviyse %(Color) ayarlanır ve ayar yalnızca mavi toplu iş çalıştırıldığında koşulla eşleşen tek tek öğeyi etkiler. Görev MessageText özniteliği, söz dizimine rağmen %(ItemMetadataName) toplu işlemi tetiklemez, çünkü bir öğe dönüşümü içinde kullanılır.

Toplu işler bağımsız olarak çalışır, ancak paralel olarak çalışmaz. Toplu yürütmede değişen meta veri değerlerine eriştiğinizde bu bir fark yaratır. Toplu yürütmedeki bazı meta verileri temel alan bir özellik ayarladığınızda, özellik son değer kümesini alır:

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

Toplu yürütmeden sonra özelliği son değerini %(MetadataValue)korur.

Toplu işler bağımsız olarak çalışsa da, hedef toplu işlem ile görev toplu işlemi arasındaki farkı göz önünde bulundurmanız ve durumunuz için hangi türün geçerli olduğunu bilmeniz önemlidir. Bu ayrımın önemini daha iyi anlamak için aşağıdaki örneği göz önünde bulundurun.

Görevler açık yerine örtük olabilir ve bu da örtük görevlerle görev toplu işlemi gerçekleştiğinde kafa karıştırıcı olabilir. bir PropertyGroup veya öğesi içinde Targetgöründüğünde, gruptaki her özellik bildirimi örtük olarak ayrı bir CreateProperty veya CreateItemItemGroup görevi gibi işlenir. Bu, hedef toplu işlendiğinde davranışın farklı olduğu ve hedef toplu işlenmediğinde (yani öznitelikte Outputs söz dizimi olmadığında%(ItemMetadataName)) farklı olduğu anlamına gelir. Hedef toplu işlendiğinde, ItemGroup hedef başına bir kez yürütülür, ancak hedef toplu işlenmediğinde, veya CreateProperty görevlerinin CreateItem örtük eşdeğerleri görev toplu işlemi kullanılarak toplu işlenir, bu nedenle hedef yalnızca bir kez yürütülür ve gruptaki her öğe veya özellik, görev toplu işlemi kullanılarak ayrı ayrı toplu işler.

Aşağıdaki örnekte meta verilerin kapatıldığı durumlarda hedef toplu işleme ile görev toplu işlemi karşılaştırması gösterilmektedir. Bazı dosyaların bulunduğu A ve B klasörlerinin bulunduğu bir durumu göz önünde bulundurun:

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

Şimdi bu iki benzer projenin çıkışına bakın.

    <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>

Çıktı şu olur:

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

Şimdi hedef toplu işlemi belirten özniteliği kaldırın Outputs .

    <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>

Çıktı şu olur:

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

Başlığın Test1 yalnızca bir kez yazdırıldığına, ancak önceki örnekte iki kez yazdırıldığına dikkat edin. Bu, hedefin toplu işlenmediği anlamına gelir. Sonuç olarak çıkış kafa karıştırıcı bir şekilde farklıdır.

Bunun nedeni, hedef toplu işlemi kullanırken her hedef toplu işlemin tüm özelliklerin ve öğelerin kendi bağımsız kopyasıyla hedefteki her şeyi yürütmesi, ancak özniteliği atladığınızda Outputs özellik grubundaki tek tek satırların ayrı, potansiyel olarak toplu işlenmiş görevler olarak kabul edilmesidir. Bu durumda, ComponentDir görev toplu işlenir (söz dizimini %(ItemMetadataName) kullanır), böylece ComponentName satır yürütülürken, satırın ComponentDir her iki toplu işlemi de tamamlanır ve çalıştırılan ikinci grup ikinci satırda görüldüğü gibi değeri belirler.

Meta verileri kullanan özellik işlevleri

Toplu işleme, meta verileri içeren özellik işlevleri tarafından denetlenebilir. Örneğin:

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

, bir kök klasör yolunu Derleme öğesi yoluyla birleştirmek için kullanır Combine .

Özellik işlevleri meta veri değerlerinde görünmeyebilir. Örneğin:

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

izin verilmiyor.

Özellik işlevleri hakkında daha fazla bilgi için bkz . Özellik işlevleri.

Kendi kendine başvuran meta verilerde öğe toplu işlemi

Bir öğe tanımının içinden meta veriye başvurmak için aşağıdaki örneği göz önünde bulundurun:

<ItemGroup>
  <i Include='a/b.txt' MyPath='%(Filename)%(Extension)' />
  <i Include='c/d.txt' MyPath='%(Filename)%(Extension)' />
  <i Include='g/h.txt' MyPath='%(Filename)%(Extension)' />
</ItemGroup>

Herhangi bir hedef dışında ve hedef içinde tanımlandığında davranışın farklı olduğunu unutmayın.

Herhangi bir hedefin dışında öğeye kendi kendine başvuran meta veriler

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <i Include='a/b.txt' MyPath='%(Filename)%(Extension)' />
    <i Include='c/d.txt' MyPath='%(Filename)%(Extension)' />
    <i Include='g/h.txt' MyPath='%(Filename)%(Extension)' />
  </ItemGroup>
  <Target Name='ItemOutside'>
    <Message Text="i=[@(i)]" Importance='High' />
    <Message Text="i->MyPath=[@(i->'%(MyPath)')]" Importance='High' />
  </Target>
</Project>

Meta veri başvuru öğesi örneği başına çözümlenir (önceden tanımlanmış veya oluşturulmuş öğe örneklerinden etkilenmez) ve beklenen çıkışa yol açar:

  i=[a/b.txt;c/d.txt;g/h.txt]
  i->MyPath=[b.txt;d.txt;h.txt]

Hedefin içinde öğeye kendi kendine başvuran meta veriler

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Target Name='ItemInside'>  
    <ItemGroup>
      <i Include='a/b.txt' MyPath='%(Filename)%(Extension)' />
      <i Include='c/d.txt' MyPath='%(Filename)%(Extension)' />
      <i Include='g/h.txt' MyPath='%(Filename)%(Extension)' />
    </ItemGroup>
    <Message Text="i=[@(i)]" Importance='High' />
    <Message Text="i->MyPath=[@(i->'%(MyPath)')]" Importance='High' />
  </Target>
</Project>

Bu durumda başvurulan meta veriler toplu işleme yol açar ve bu da beklenmeyen ve istenmeyen çıkışlar verir:

  i=[a/b.txt;c/d.txt;g/h.txt;g/h.txt]
  i->MyPath=[;b.txt;b.txt;d.txt]

Altyapı, her öğe örneği için önceden var olan tüm öğe örneklerinin meta verilerini uygular (bu nedenle MyPath ilk öğe için boş ve ikinci öğe için içerir b.txt ). Daha önceden var olan örnekler söz konusu olduğunda, bu durum geçerli öğe örneğinin çarpmasına neden olur (bu nedenle g/h.txt öğe örneği, sonuçta elde edilen listede iki kez gerçekleşir).

Bu, muhtemelen istenmeyen davranış, MSBuild'in sonraki sürümleri hakkında açıkça bilgi edinmek için sorun iletisi MSB4120:

proj.proj(4,11):  message : MSB4120: Item 'i' definition within target is referencing self via metadata 'Filename' (qualified or unqualified). This can lead to unintended expansion and cross-applying of pre-existing items. More info: https://aka.ms/msbuild/metadata-self-ref
proj.proj(4,11):  message : MSB4120: Item 'i' definition within target is referencing self via metadata 'Extension' (qualified or unqualified). This can lead to unintended expansion and cross-applying of pre-existing items. More info: https://aka.ms/msbuild/metadata-self-ref
proj.proj(5,11):  message : MSB4120: Item 'i' definition within target is referencing self via metadata 'Filename' (qualified or unqualified). This can lead to unintended expansion and cross-applying of pre-existing items. More info: https://aka.ms/msbuild/metadata-self-ref
proj.proj(5,11):  message : MSB4120: Item 'i' definition within target is referencing self via metadata 'Extension' (qualified or unqualified). This can lead to unintended expansion and cross-applying of pre-existing items. More info: https://aka.ms/msbuild/metadata-self-ref
proj.proj(6,11):  message : MSB4120: Item 'i' definition within target is referencing self via metadata 'Filename' (qualified or unqualified). This can lead to unintended expansion and cross-applying of pre-existing items. More info: https://aka.ms/msbuild/metadata-self-ref
proj.proj(6,11):  message : MSB4120: Item 'i' definition within target is referencing self via metadata 'Extension' (qualified or unqualified). This can lead to unintended expansion and cross-applying of pre-existing items. More info: https://aka.ms/msbuild/metadata-self-ref
  i=[a/b.txt;c/d.txt;g/h.txt;g/h.txt]
  i->MyPath=[;b.txt;b.txt;d.txt]

Kendi kendine başvuru kasıtlıysa, gerçek senaryoya ve tam gereksinimlere bağlı olarak birkaç seçeneğiniz vardır:

Yardımcı öğeyi ve dönüştürmeyi kullanma

Meta veri başvurusunun neden olduğu toplu işleme davranışını önlemek istiyorsanız, bunu ayrı bir öğe tanımlayarak ve ardından istenen meta verilerle öğe örnekleri oluşturmak için dönüştürme işleminden yararlanarak elde edebilirsiniz:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Target Name='ItemOutside'>  
    <ItemGroup>
      <j Include='a/b.txt' />
      <j Include='c/*' />
      <i Include='@(j)' MyPath="%(Filename)%(Extension)" />
    </ItemGroup>
    <Message Text="i=[@(i)]" Importance='High' />
    <Message Text="i->MyPath=[@(i->'%(MyPath)')]" Importance='High' />
  </Target>
</Project>