Teilen über


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

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

Aufgabenbündelung

Mit der Aufgabenbatchierung können Sie Ihre Projektdateien vereinfachen, indem Sie eine Möglichkeit zum Aufteilen von Elementlisten in verschiedene Batches bereitstellen und die einzelnen Batches separat an einen Vorgang übergeben. Batchverarbeitung bedeutet, dass eine Projektdatei nur einmal die Aufgabe und die zugehörigen Attribute deklariert haben muss, auch wenn sie mehrmals ausgeführt werden kann.

Sie geben an, dass MSBuild batching mit einer Aufgabe ausführen soll, indem Sie die %(ItemMetaDataName) Notation in einem der Aufgabenattribute verwenden. Im folgenden Beispiel wird die Example Elementliste basierend auf dem Color Elementmetadatenwert in Batches aufgeteilt und die einzelnen Batches separat an die MyTask Aufgabe übergeben.

Hinweis

Wenn Sie nicht an anderer Stelle in den Aufgabenattributen auf die Elementliste verweisen oder der Metadatenname mehrdeutig sein kann, können Sie die %(<ItemCollection.ItemMetaDataName>)-Notation verwenden, um den Elementmetadatenwert vollständig zu qualifizieren, der für die Batchverarbeitung verwendet werden soll.

<Project>

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

Spezifischere Batchverarbeitungsbeispiele finden Sie unter Elementmetadaten im Aufgabenbatching.

Zielgruppen-Batching

MSBuild überprüft, ob die Eingaben und Ausgaben eines Ziels auf dem neuesten Stand sind, bevor das Ziel ausgeführt wird. Wenn sowohl Eingaben als auch Ausgaben auf dem neuesten Stand sind, wird das Ziel übersprungen. Wenn eine Aufgabe innerhalb eines Ziels Batchverarbeitung verwendet, muss MSBuild ermitteln, ob die Eingaben und Ausgaben für jeden Batch von Elementen auf dem neuesten Stand sind. Andernfalls wird das Ziel jedes Mal bei einem Treffer aktiviert.

Das folgende Beispiel zeigt ein Target Element, das ein Outputs Attribut mit der %(ItemMetadataName) Notation enthält. MSBuild teilt die Example Elementliste basierend auf den Color Elementmetadaten in Batches auf und analysiert die Zeitstempel der Ausgabedateien für jeden Batch. Wenn die Ergebnisse eines Batches nicht aktuell sind, wird das Zielobjekt ausgeführt. Andernfalls wird das Ziel übersprungen.

<Project>

    <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 Zielbatchierung finden Sie unter Elementmetadaten im Zielbatching.

Element- und Eigenschaftsmutationen

In diesem Abschnitt wird beschrieben, wie Sie die Auswirkungen ändernder Eigenschaften und/oder Elementmetadaten bei Verwendung von Zielbatching oder Aufgabenbatching verstehen.

Da die Zielbatchierung und die Aufgabenbatchierung zwei unterschiedliche MSBuild-Vorgänge sind, ist es wichtig zu verstehen, welche Form des Batchings MSBuild in jedem Fall verwendet. Wenn die Batchverarbeitungssyntax %(ItemMetadataName) in einem Vorgang in einem Ziel angezeigt wird, aber nicht in einem Attribut für das Ziel, verwendet MSBuild die Aufgabenbatchierung. Die einzige Möglichkeit zum Angeben des Zielbatches ist die Verwendung der Batchverarbeitungssyntax für ein Target-Attribut, in der Regel das Outputs Attribut.

Sowohl bei der Zielbatchverarbeitung als auch bei der Aufgabenbatchierung können Batches unabhängig voneinander ausgeführt werden. Alle Batches beginnen mit einer Kopie desselben Anfangszustands von Eigenschafts- und Elementmetadatenwerten. Alle Mutationen von Eigenschaftswerten während der Batchausführung sind für andere Batches nicht sichtbar. Betrachten Sie das folgenden 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>

Die Ausgabe lautet wie folgt:

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

Das ItemGroup im Ziel ist implizit eine Aufgabe, und mit dem %(Color) im Condition-Attribut wird die Batchverarbeitung von Aufgaben ausgeführt. Es gibt zwei Batches: eine für Rot und die andere für Blau. Die Eigenschaft %(NeededColorChange) wird nur festgelegt, wenn die %(Color) Metadaten blau sind, und die Einstellung wirkt sich nur auf das einzelne Element aus, das der Bedingung entspricht, wenn der blaue Batch ausgeführt wurde. Das Message Attribut der Text Aufgabe löst trotz der %(ItemMetadataName) Syntax keine Batchverarbeitung aus, da sie innerhalb einer Elementtransformation verwendet wird.

Batches werden unabhängig, aber nicht parallel ausgeführt. Das macht einen Unterschied, wenn Sie auf Metadatenwerte zugreifen, die sich in der Batchausführung ändern. Wenn Sie eine Eigenschaft basierend auf einigen Metadaten in der Batchausführung festlegen, nimmt die Eigenschaft den letzten Wertsatz:

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

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

Obwohl Batches unabhängig ausgeführt werden, ist es wichtig, den Unterschied zwischen Zielbatching und Aufgabenbatching 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 und nicht explizit sein, was verwirrend sein kann, wenn die Aufgabenbatchierung mit impliziten Aufgaben erfolgt. Wenn ein PropertyGroup- oder ItemGroup-Element in einer Target Gruppe angezeigt wird, wird jede Eigenschaftsdeklaration implizit wie eine separate CreateProperty- oder CreateItem-Aufgabe behandelt. Dieses Verhalten bedeutet, dass die Buildausführung anders ist, wenn das Ziel im Batch gebatchiert wird und wenn das Ziel nicht batchiert ist (d. h., wenn die %(ItemMetadataName) Syntax im Outputs Attribut fehlt). Wenn das Ziel batchiert wird, wird das ItemGroup Ziel einmal pro Ziel ausgeführt, aber wenn das Ziel nicht batchiert wird, werden die impliziten Entsprechungen der CreateItem Oder CreateProperty Aufgaben mithilfe der Aufgabenbatchierung zusammengefasst, sodass das Ziel nur einmal ausgeführt wird, und jedes Element oder jede Eigenschaft in der Gruppe wird separat mithilfe der Aufgabenbatchierung zusammengefasst.

Das folgende Beispiel veranschaulicht die Zielbatchierung im Vergleich zur Aufgabenbatchierung im Fall von mutierten Metadaten. Berücksichtigen Sie eine Situation, in der Sie 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>

Die Ausgabe lautet wie folgt:

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

Entfernen Sie nun das Outputs Attribut, das das angegebene Zielbatching angibt.

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

Die Ausgabe lautet wie folgt:

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

Beachten Sie, dass die Überschrift Test1 nur einmal gedruckt wird, aber im vorherigen Beispiel wurde sie zweimal gedruckt. Dies bedeutet, dass das Ziel nicht gruppiert ist. Daher ist das Ergebnis verwirrend unterschiedlich.

Der Grund dafür ist, dass jeder Zielbatch bei Verwendung der Zielbatchierung alles im Ziel mit einer eigenen unabhängigen Kopie aller Eigenschaften und Elemente ausführt, aber wenn Sie das Outputs Attribut weglassen, werden die einzelnen Zeilen in der Eigenschaftengruppe als unterschiedliche, potenziell batchierte Aufgaben behandelt. In diesem Fall wird die ComponentDir Aufgabe im Batch-Modus ausgeführt (sie verwendet die %(ItemMetadataName) Syntax), sodass die ComponentName Zeile ausgeführt wird, nachdem beide Batches der ComponentDir Zeile abgeschlossen sind, wobei der zweite Lauf den Wert bestimmt hat, wie in der zweiten Zeile zu sehen ist.

Eigenschaftenfunktionen mithilfe von Metadaten

Batchverarbeitung kann durch Eigenschaftenfunktionen gesteuert werden, die Metadaten enthalten. Beispiel:

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

verwendet Combine , um einen Stammordnerpfad mit einem Kompilierungselementpfad zu kombinieren.

Eigenschaftsfunktionen werden möglicherweise nicht innerhalb von Metadatenwerten angezeigt. Beispiel:

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

ist nicht zulässig.

Weitere Informationen zu Eigenschaftenfunktionen finden Sie unter Eigenschaftenfunktionen.

Elementbatching für selbstverweisende 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.

Element selbstverweisende Metadaten außerhalb eines Ziels

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

Metadatenverweise werden für jede Elementinstanz einzeln aufgelöst und sind daher nicht von zuvor definierten oder erstellten Instanzen betroffen, was zu der erwarteten Ausgabe führt:

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

Element selbstverweisende Metadaten innerhalb eines Ziels

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

Metadatenreferenzierungen führen in diesem Fall zur Batchverarbeitung, die möglicherweise unerwartete und unbeabsichtigte Ausgaben erzeugt.

  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 das Modul Metadaten aller bereits vorhandenen Elementinstanzen an (aus diesem Grund ist dies MyPath leer für das erste Element und enthält b.txt für das zweite Element). Bei mehr bereits vorhandenen Instanzen führt dieses Verhalten zur Multiplikation der aktuellen Elementinstanz (deshalb tritt die g/h.txt Elementinstanz zweimal in der resultierenden Liste auf).

Um explizit über dieses möglicherweise unbeabsichtigte Verhalten zu informieren, geben spätere Versionen von MSBuild die Nachricht MSB4120 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 Selbstverweis beabsichtigt ist, haben Sie je nach tatsächlichem Szenario und genauen Anforderungen wenige Optionen:

  • Behalten Sie den Code bei, und ignorieren Sie die Nachricht.
  • Das Element außerhalb des Ziels definieren
  • Verwenden eines Hilfselements und des Transformationsvorgangs

Verwenden Sie ein Hilfselement und die Transformationsoperation

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 verwenden, um Elementinstanzen mit den gewünschten Metadaten zu erstellen:

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