Ausführen von Aufgaben oder Zielen in Batches basierend auf Elementmetadaten

MSBuild unterteilt Elementlisten basierend auf den Elementmetadaten in verschiedene Kategorien oder Batches und führt ein Ziel oder eine Aufgabe einmal mit jedem Batch aus.

Aufgabenbatchverarbeitung

Durch die Aufgabenbatchverarbeitung können Sie Ihre Projektdateien vereinfachen, indem Sie Elementlisten in verschiedene Batches unterteilen und jeden dieser Batches separat an eine Aufgabe übergeben können. Dies bedeutet, dass die Aufgabe und ihre Attribute für eine Projektdatei nur einmal deklariert werden müssen, obwohl sie mehrmals ausgeführt werden können.

Sie geben an, dass MSBuild die Batchverarbeitung mit einer Aufgabe ausführen soll, indem Sie die Notation %(ItemMetaDataName) in einem der Attribute der Aufgabe verwenden. Im folgenden Beispiel wird die Elementliste Example basierend auf dem Metadatenwert des Elements Color in Batches aufgeteilt, und alle Batches werden separat an die Aufgabe MyTask übergeben.

Hinweis

Wenn Sie an keiner anderen Stelle in den Attributen der Aufgabe auf die Elementliste verweisen oder der Metadatenname mehrdeutig sein kann, können Sie die Notation %(<ItemCollection.ItemMetaDataName>) verwenden, um die Elementmetadaten vollständig für die Batchverarbeitung zu qualifizieren.

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

Weitere Beispiele für die Batchverarbeitung finden Sie unter Elementmetadaten bei der Aufgabenbatchverarbeitung.

Zielbatchverarbeitung

MSBuild überprüft, ob die Ein- und Ausgaben eines Ziels aktuell sind, bevor das Ziel ausgeführt wird. Wenn die Ein- und Ausgaben aktuell sind, wird das Ziel übersprungen. Wenn eine Aufgabe innerhalb eines Ziels die Batchverarbeitung verwendet, muss MSBuild bestimmen, ob die Ein- und Ausgaben für jeden Batch mit Elementen aktuell sind. Andernfalls wird das Ziel jedes Mal ausgeführt, wenn es erreicht wird.

Im folgenden Beispiel wird ein Target-Element dargestellt, das ein Outputs-Attribut mit der Notation %(ItemMetadataName) enthält. MSBuild unterteilt die Example-Elementliste basierend auf den Metadaten des Color-Elements in Batches und analysiert die Zeitstempel der Ausgabedateien jedes Batchs. Wenn die Ausgaben eines Batchs nicht aktuell sind, wird das Ziel ausgeführt. Andernfalls wird das Ziel übersprungen.

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

Ein weiteres Beispiel für die Zielbatchverarbeitung finden Sie unter Elementmetadaten bei der Zielbatchverarbeitung.

Änderungen von Elementen und Eigenschaften

In diesem Abschnitt wird beschrieben, wie die Auswirkungen des Änderns von Eigenschaften und/oder der Metadaten von Elementen bei der Batchverarbeitung von Zielen oder Aufgaben zu verstehen sind.

Da die Batchverarbeitung von Zielen und Aufgaben zwei verschiedene MSBuild-Vorgänge sind, ist es wichtig, genau zu verstehen, welche Form der Batchverarbeitung MSBuild im jeweiligen Fall wählt. Wenn die Batchverarbeitungssyntax %(ItemMetadataName) in einer Aufgabe in einem Ziel, aber nicht in einem Attribut für das Ziel angezeigt wird, verwendet MSBuild die Aufgabenbatchverarbeitung. Die einzige Möglichkeit, die Zielbatchverarbeitung anzugeben, ist die Verwendung der Batchverarbeitungssyntax für ein Zielattribut, normalerweise das Attribut Outputs.

Sowohl bei der Ziel- als auch bei der Aufgabenbatchverarbeitung kann davon ausgegangen werden, dass die Batches unabhängig voneinander ausgeführt werden. Alle Batches beginnen mit einer Kopie desselben Anfangszustands der Werte für Eigenschaften und Metadaten von Elementen. Alle Änderungen von Eigenschaftswerten während der Batchverarbeitung sind für andere Batches nicht sichtbar. Betrachten Sie das folgende Beispiel:

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

Ausgabe:

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

ItemGroup im Ziel ist implizit eine Aufgabe, und bei %(Color) im Attribut Condition erfolgt eine Aufgabenbatchverarbeitung. Es gibt zwei Batches: eines für rot und das andere für blau. Die Eigenschaft %(NeededColorChange) wird nur festgelegt, wenn die Metadaten von %(Color) blau sind. Die Einstellung wirkt sich nur auf das einzelne Element aus, das der Bedingung bei Ausführung des blauen Batches entsprach. Das Attribut Text der Aufgabe Message löst trotz der Syntax %(ItemMetadataName) keine Batchverarbeitung aus, da es innerhalb einer Elementtransformation verwendet wird.

Batches werden unabhängig, aber nicht parallel ausgeführt. Das ist ein Unterschied, wenn Sie auf Metadatenwerte zugreifen, die sich bei der Batchausführung ändern. Für den Fall, dass Sie eine Eigenschaft basierend auf einigen Metadaten in der Batchausführung festlegen, würde die Eigenschaft die letzte Wertgruppe verwenden:

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

Nach der Batchausführung behält die Eigenschaft den endgültigen Wert %(MetadataValue) bei.

Obwohl Batches unabhängig ausgeführt werden, ist es wichtig, den Unterschied zwischen der Ziel- und der Aufgabenbatchverarbeitung zu berücksichtigen und zu wissen, welcher Typ für Ihre Situation gilt. Betrachten Sie das folgende Beispiel, um die Bedeutung dieser Unterscheidung besser zu verstehen.

Aufgaben können implizit statt explizit sein, was verwirrend sein kann, wenn die Aufgabenbatchverarbeitung mit impliziten Aufgaben erfolgt. Wenn ein Element des Typs PropertyGroup oder ItemGroup in einem Element des Typs Target vorkommt, wird jede Eigenschaftsdeklaration in der Gruppe implizit wie eine separate Aufgabe des Typs CreateProperty oder CreateItem behandelt. Das bedeutet, dass das Verhalten bei einer Zielbatchverarbeitung anders ist als bei einer Nicht-Zielbatchverarbeitung (d. h., wenn die Syntax %(ItemMetadataName) im Attribut Outputs fehlt). Bei der Zielbatchverarbeitung wird ItemGroup einmal pro Ziel ausgeführt. Wenn für das Ziel keine Batchverarbeitung erfolgt, werden die impliziten Äquivalente der Aufgaben CreateItem oder CreateProperty mithilfe der Aufgabenbatchverarbeitung von Aufgaben als Batch verarbeitet. Das Ziel wird also nur einmal ausgeführt, und jedes Element oder jede Eigenschaft in der Gruppe wird separat mithilfe der Aufgabenbatchverarbeitung als Batch verarbeitet.

Die folgende Abbildung veranschaulicht die Zielbatchverarbeitung gegenüber der Aufgabenbatchverarbeitung für den Fall, dass die Metadaten verändert wurden. Stellen Sie sich eine Situation vor, in der Sie die Ordner A und B mit einigen Dateien haben:

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

Sehen Sie sich nun die Ausgabe dieser beiden ähnlichen Projekte an.

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

Ausgabe:

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

Entfernen Sie nun das Attribut Outputs, das die Zielbatchverarbeitung angegeben hat.

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

Ausgabe:

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

Beachten Sie, dass die Überschrift Test1 nur einmal ausgegeben wurde, im vorherigen Beispiel jedoch zweimal. Das bedeutet, dass für das Ziel keine Batchverarbeitung erfolgt. Das führt dazu, dass die Ergebnisse verwirrend unterschiedlich ausfallen.

Der Grund dafür ist, dass bei der Verwendung der Zielbatchverarbeitung jedes Zielbatch alles im Ziel mit seiner eigenen unabhängigen Kopie aller Eigenschaften und Elemente ausführt. Wenn Sie jedoch das Attribut Outputs weglassen, werden die einzelnen Zeilen in der Eigenschaftsgruppe als unterschiedliche, potenzielle Batchaufgaben verarbeitet. In diesem Fall erfolgt für die Aufgabe ComponentDir eine Batchverarbeitung (mit der Syntax %(ItemMetadataName)). Dadurch wird bewirkt, dass zum Zeitpunkt, an dem die Zeile ComponentName ausgeführt wird, beide Batches der Zeile ComponentDir abgeschlossen sind und der zweite, der ausgeführt wurde, den Wert ermittelt hat, wie in der zweiten Zeile zu sehen ist.

Eigenschaftenfunktionen mit Metadaten

Die Batchverarbeitung kann von den Eigenschaftenfunktionen kontrolliert werden, die Metadaten enthalten. Ein auf ein Objekt angewendeter

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

Combine wird verwendet, um den Stammpfad eines Ordners mit dem Elementpfad einer Kompilierung zu kombinieren.

Eigenschaftenfunktionen werden möglicherweise nicht innerhalb der Metadatenwerte angezeigt. Ein auf ein Objekt angewendeter

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

ist nicht zulässig.

Weitere Informationen zu Eigenschaftenfunktionen finden Sie unter Eigenschaftenfunktionen.

Batchverarbeitung von Elementen für auf sich selbst verweisende Metadaten

Betrachten Sie das folgende Beispiel für das Verweisen auf Metadaten aus einer Elementdefinition:

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

Es ist wichtig zu beachten, dass sich das Verhalten unterscheidet, wenn es außerhalb eines Ziels und innerhalb des Ziels definiert ist.

Auf sich selbst verweisendes Metadatenelement außerhalb eines Ziels

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

Das Verweisen auf Metadaten wird pro Elementinstanz aufgelöst (nicht von zuvor definierten oder erstellten Elementinstanzen betroffen), was zur erwarteten folgenden Ausgabe führt:

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

Auf sich selbst verweisendes Metadatenelement innerhalb eines Ziels

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

Metadaten, die in diesem Fall verweisen, führen zu Batchverarbeitungen, die möglicherweise eine unerwartete und unbeabsichtigte Ausgabe ergeben:

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

Für jede Elementinstanz wendet die Engine Metadaten aller bereits vorhandenen Elementinstanzen an (aus diesem Grund ist MyPath für das erste Element leer und enthält b.txt für das zweite Element). Im Fall weiterer bereits vorhandener Instanzen führt dies zur Multiplikation der aktuellen Elementinstanz (deshalb tritt die Elementinstanzg/h.txt zweimal in der Ergebnisliste auf).

Um explizite Informationen zu diesem möglicherweise unbeabsichtigten Verhalten bereitzustellen, geben spätere Versionen ein MSBuild-Problem folgende MSB4120-Meldung aus:

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]

Wenn der Verweis auf sich selbst beabsichtigt ist, haben Sie je nach tatsächlichem Szenario und genauen Anforderungen folgende Optionen:

Verwenden des Hilfselements und der Transformation

Wenn Sie das durch den Metadatenverweis induzierte Batchverhalten verhindern möchten, können Sie dies erreichen, indem Sie ein separates Element definieren und dann den Transformationsvorgang nutzen, um Elementinstanzen mit den gewünschten Metadaten zu erstellen:

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