Freigeben über


Erweitern des Visual Studio-Buildprozesses

Der Visual Studio-Buildprozess wird durch eine Reihe von MSBuild-.targets-Dateien definiert, die in Ihre Projektdatei importiert werden. Diese Importe sind implizit, wenn Sie SDK in der Regel als Visual Studio-Projekte verwenden. Eine dieser importierten Dateien (Microsoft.Common.targets) kann erweitert werden, um Ihnen das Ausführen benutzerdefinierter Aufgaben in unterschiedlichen Phasen während des Buildprozesses zu ermöglichen. In diesem Artikel werden die drei Methoden erläutert, mit denen der Visual Studio-Buildprozess erweitert werden kann:

  • Erstellen Sie ein benutzerdefiniertes Ziel und geben Sie mithilfe von BeforeTargets- und AfterTargets-Attributen an, wann es ausgeführt werden soll.

  • Überschreiben Sie die DependsOn-Eigenschaften, die in den allgemeinen Zielen festgeschrieben sind.

  • Überschreiben Sie bestimmte vordefinierte Ziele, die in den allgemeinen Zielen (Microsoft.Common.targets oder den Dateien, die importiert werden) festgeschrieben sind.

„AfterTargets“ und „BeforeTargets“

Sie können die Attribute AfterTargets und BeforeTargets für Ihr benutzerdefiniertes Ziel verwenden, um anzugeben, wann es ausgeführt werden soll.

Im folgenden Beispiel sehen Sie, wie Sie mit dem AfterTargets-Attribut ein benutzerdefiniertes Ziel hinzufügen, das eine Aktion für die Ausgabedateien ausführt. In diesem Beispiel werden die Ausgabedateien in den neuen Ordner CustomOutput kopiert. Im Beispiel wird auch veranschaulicht, wie die vom benutzerdefinierten Buildvorgang mit einem CustomClean-Ziel erstellten Dateien bereinigt werden, indem das BeforeTargets-Attribut verwendet und angegeben wird, dass der benutzerdefinierte Bereinigungsvorgang vor dem CoreClean-Ziel ausgeführt wird.

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
     <TargetFramework>netcoreapp3.1</TargetFramework>
     <_OutputCopyLocation>$(OutputPath)..\..\CustomOutput\</_OutputCopyLocation>
  </PropertyGroup>

  <Target Name="CustomAfterBuild" AfterTargets="Build">
    <ItemGroup>
      <_FilesToCopy Include="$(OutputPath)**\*"/>
    </ItemGroup>
    <Message Text="_FilesToCopy: @(_FilesToCopy)" Importance="high"/>

    <Message Text="DestFiles:
        @(_FilesToCopy->'$(_OutputCopyLocation)%(RecursiveDir)%(Filename)%(Extension)')"/>

    <Copy SourceFiles="@(_FilesToCopy)"
          DestinationFiles=
          "@(_FilesToCopy->'$(_OutputCopyLocation)%(RecursiveDir)%(Filename)%(Extension)')"/>
  </Target>

  <Target Name="CustomClean" BeforeTargets="CoreClean">
    <Message Text="Inside Custom Clean" Importance="high"/>
    <ItemGroup>
      <_CustomFilesToDelete Include="$(_OutputCopyLocation)**\*"/>
    </ItemGroup>
    <Delete Files='@(_CustomFilesToDelete)'/>
  </Target>
</Project>

Warnung

Verwenden Sie dabei unbedingt andere Namen als für die vordefinierten Ziele (das benutzerdefinierte Buildziel lautet hier beispielsweise CustomAfterBuild und nicht AfterBuild). Das liegt daran, dass die vordefinierten Ziele vom SDK-Import überschrieben werden, der sie auch definiert. Eine Liste vordefinierter Ziele finden Sie in der Tabelle am Ende dieses Artikels.

Erweiterung der DependsOn-Eigenschaften

Eine weitere Möglichkeit zum Erweitern des Buildprozesses ist die Verwendung der DependsOn-Eigenschaften (z. B. BuildDependsOn), um Ziele festzulegen, die vor einem Standardziel ausgeführt werden sollten.

Diese Methode ist dem Überschreiben vordefinierter Ziele vorzuziehen, das im nächsten Abschnitt behandelt wird. Das Überschreiben von vordefinierten Zielen ist ein älteres Verfahren, das nach wie vor unterstützt wird. Da MSBuild die Definition von Zielen aber nacheinander auswertet, können Sie ein anderes Projekt, das Ihr Projekt importiert, nicht daran hindern, die bereits überschriebenen Ziele noch einmal zu überschreiben. Das letzte in der Projektdatei definierte AfterBuild-Ziel wird z.B. während des Buildprozesses verwendet, nachdem alle anderen Projekte importiert wurden.

Sie können sich vor dem unbeabsichtigten Überschreiben von Zielen schützen, indem Sie die DependsOn-Eigenschaften überschreiben, die in den DependsOnTargets-Attributen der allgemeinen Ziele verwendet werden. So enthält das Build-Ziel z.B. einen DependsOnTargets-Attributwert von "$(BuildDependsOn)". Zu berücksichtigen:

<Target Name="Build" DependsOnTargets="$(BuildDependsOn)"/>

Dieser XML-Codeausschnitt gibt an, dass vor der Ausführung des Build-Ziels alle in der BuildDependsOn-Eigenschaft angegebenen Ziele zuerst ausgeführt werden müssen. Die BuildDependsOn-Eigenschaft ist definiert als:

<PropertyGroup>
    <BuildDependsOn>
        $(BuildDependsOn);
        BeforeBuild;
        CoreBuild;
        AfterBuild
    </BuildDependsOn>
</PropertyGroup>

Sie können diesen Eigenschaftswert überschreiben, indem Sie eine andere Eigenschaft mit dem Namen BuildDependsOn am Ende der Projektdatei deklarieren. Bei einem SDK-Projekt bedeutet das, dass Sie explizite Importe verwenden müssen. Siehe implizite und explizite Importe, damit Sie die DependsOn-Eigenschaft nach dem letzten Import platzieren können. Durch Einschließen der vorherigen BuildDependsOn-Eigenschaft in die neue Eigenschaft können Sie neue Ziele am Anfang und Ende der Zielliste hinzufügen. Zum Beispiel:

<PropertyGroup>
    <BuildDependsOn>
        MyCustomTarget1;
        $(BuildDependsOn);
        MyCustomTarget2
    </BuildDependsOn>
</PropertyGroup>

<Target Name="MyCustomTarget1">
    <Message Text="Running MyCustomTarget1..."/>
</Target>
<Target Name="MyCustomTarget2">
    <Message Text="Running MyCustomTarget2..."/>
</Target>

Projekte, in die Ihre Projektdateie importiert wird, können diese Eigenschaften noch mehr erweitern, ohne die von Ihnen vorgenommenen Anpassungen zu überschreiben.

So überschreiben Sie eine DependsOn-Eigenschaft

  1. Identifizieren Sie eine vordefinierte DependsOn-Eigenschaft in den allgemeinen Zielen, die Sie überschreiben möchten. In der folgenden Tabelle finden Sie eine Liste der häufig überschriebenen DependsOn-Eigenschaften.

  2. Definieren Sie eine andere Instanz der Eigenschaft oder Eigenschaften am Ende der Projektdatei. Schließen Sie die ursprüngliche Eigenschaft, z.B. $(BuildDependsOn), in die neue Eigenschaft ein.

  3. Definieren Sie Ihre benutzerdefinierten Ziele vor oder nach der Eigenschaftsdefinition.

  4. Erstellen Sie die Projektdatei.

Häufig überschriebene DependsOn-Eigenschaften

Eigenschaftenname Die hinzugefügten Ziele werden vor diesem Punkt ausgeführt:
BuildDependsOn dem Hauptbuild-Einstiegspunkt. Überschreiben Sie diese Eigenschaft, wenn Sie benutzerdefinierte Ziele vor oder nach dem gesamten Buildprozess einfügen möchten.
RebuildDependsOn Der Rebuild
RunDependsOn Die Ausführung der endgültigen Buildausgabe (wenn es sich um eine .EXE handelt)
CompileDependsOn Die Kompilierung (Compile-Ziel). Überschreiben Sie diese Eigenschaft, wenn Sie benutzerdefinierte Prozesse vor oder nach dem Kompilierungsschritt einfügen möchten.
CreateSatelliteAssembliesDependsOn Die Erstellung der Satellitenassemblys
CleanDependsOn Das Clean-Ziel (Löschen aller zwischen- und endgültigen Buildausgaben). Überschreiben Sie diese Eigenschaft, wenn Sie die Ausgabe Ihres benutzerdefinierten Buildprozesses bereinigen möchten.
PostBuildEventDependsOn Das PostBuildEvent-Ziel.
PublishBuildDependsOn Build-Veröffentlichung
ResolveAssemblyReferencesDependsOn Das ResolveAssemblyReferences-Ziel (Ermitteln der transitiven Schließung von Abhängigkeiten für eine bestimmte Abhängigkeit). Siehe ResolveAssemblyReference.

Beispiel: „BuildDependsOn“ und „CleanDependsOn“

Das folgende Beispiel ähnelt in vielerlei Hinsicht dem Beispiel BeforeTargets und AfterTargets, zeigt jedoch, wie eine ähnliche Funktionalität erzielt werden kann. Es wird der Build erweitert, indem mithilfe von BuildDependsOn Ihre eigene CustomAfterBuild-Aufgabe hinzugefügt wird, die die Ausgabedateien nach dem Buildvorgang kopiert. Zudem wird mithilfe von CleanDependsOn die entsprechende CustomClean-Aufgabe hinzugefügt.

In diesem Beispiel handelt es sich um ein Projekt im SDK-Format. Wie bereits weiter oben in diesem Artikel im Hinweis zu Projekten im SDK-Format erwähnt, müssen Sie die manuelle Importmethode anstelle des Sdk-Attributs verwenden, das in Visual Studio für das Generieren von Projektdateien verwendet wird.

<Project>
  <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk"/>

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk"/>

  <PropertyGroup>
    <BuildDependsOn>
      $(BuildDependsOn);CustomAfterBuild
    </BuildDependsOn>

    <CleanDependsOn>
      $(CleanDependsOn);CustomClean
    </CleanDependsOn>

    <_OutputCopyLocation>$(OutputPath)..\..\CustomOutput\</_OutputCopyLocation>
  </PropertyGroup>

  <Target Name="CustomAfterBuild">
    <ItemGroup>
      <_FilesToCopy Include="$(OutputPath)**\*"/>
    </ItemGroup>
    <Message Importance="high" Text="_FilesToCopy: @(_FilesToCopy)"/>

    <Message Text="DestFiles:
      @(_FilesToCopy-&gt;'$(_OutputCopyLocation)%(RecursiveDir)%(Filename)%(Extension)')"/>

    <Copy SourceFiles="@(_FilesToCopy)"
          DestinationFiles="@(_FilesToCopy-&gt;'$(_OutputCopyLocation)%(RecursiveDir)%(Filename)%(Extension)')"/>
  </Target>

  <Target Name="CustomClean">
    <Message Importance="high" Text="Inside Custom Clean"/>
    <ItemGroup>
      <_CustomFilesToDelete Include="$(_OutputCopyLocation)**\*"/>
    </ItemGroup>
    <Delete Files="@(_CustomFilesToDelete)"/>
  </Target>
</Project>

Die Reihenfolge der Elemente spielt eine wichtige Rolle. Die Elemente BuildDependsOn und CleanDependsOn müssen nach dem Import der Standarddatei für SDK-Ziele angezeigt werden.

Überschreiben vordefinierter Ziele

Die allgemeinen .targets-Dateien enthalten eine Reihe vordefinierter, leerer Ziele, die vor und nach einigen der wichtigen Ziele im Buildprozess aufgerufen werden. Beispielsweise ruft MSBuild das BeforeBuild-Ziel vor dem CoreBuild-Hauptziel und das AfterBuild-Ziel nach dem CoreBuild-Ziel auf. Die leeren Ziele in den allgemeinen Zielen führen standardmäßig keine Aktionen aus. Sie können aber deren Standardverhalten überschreiben, indem Sie die Ziele definieren, die in einer Projektdatei enthalten sein sollen. Die zuvor in diesem Artikel beschriebenen Methoden sind die bevorzugten Methoden. Es kann aber sein, dass Sie auf älteren Code stoßen, der diese Methode verwendet.

Wenn Ihr Projekt ein SDK verwendet (z. B. Microsoft.Net.Sdk), müssen Sie eine Änderung von impliziten zu expliziten Importen vornehmen, wie in Explizite und implizite Importen erläutert.

So überschreiben Sie ein vordefiniertes Ziel

  1. Wenn das Projekt das Sdk-Attribut verwendet, ändern Sie dieses in die explizite Importsyntax. Siehe Explizite und implizite Importe.

  2. Identifizieren Sie ein vordefiniertes Ziel in den allgemeinen Zielen, das Sie überschreiben möchten. In der folgenden Tabelle finden Sie die vollständige Liste der Ziele, die Sie bedenkenlos überschreiben können.

  3. Definieren Sie das Ziel bzw. die Ziele am Ende Ihrer Projektdatei, unmittelbar vor dem </Project>-Tag und nach dem expliziten SDK-Import. Zum Beispiel:

    <Project>
        <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
        ...
        <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
        <Target Name="BeforeBuild">
            <!-- Insert tasks to run before build here -->
        </Target>
        <Target Name="AfterBuild">
            <!-- Insert tasks to run after build here -->
        </Target>
    </Project>
    

    Beachten Sie, dass das Sdk-Attribut für das Project-Element der obersten Ebene entfernt wurde.

  4. Erstellen Sie die Projektdatei.

Tabelle der vordefinierten Ziele

In der folgenden Tabelle werden alle Ziele in den allgemeinen Zielen aufgeführt, die Sie überschreiben können.

Zielname Beschreibung
BeforeCompile, AfterCompile Aufgaben, die in eines dieser Ziele eingefügt wurden, werden vor oder nach Abschluss der Kompilierung ausgeführt. Die meisten Anpassungen werden in einem dieser beiden Ziele ausgeführt.
BeforeBuild, AfterBuild Aufgaben, die in eines dieser Ziele eingefügt wurden, werden ausgeführt, bevor oder nachdem alles Weitere erstellt wurde. Hinweis: Die Ziele BeforeBuild und AfterBuild wurden bereits in Kommentaren am Ende der meisten Projektdateien definiert. Dadurch können Sie ohne großen Aufwand Pre- und Postbuildereignisse zu Ihrer Projektdatei hinzufügen.
BeforeRebuild, AfterRebuild Aufgaben, die in eines dieser Ziele eingefügt wurden, werden ausgeführt, bevor oder nachdem die Funktion zum Neuerstellen von Kernen aufgerufen wurde. Die Reihenfolge der Ausführung der Ziele in Microsoft.Common.targets lautet wie folgt: BeforeRebuild, Clean, Build und anschließend AfterRebuild.
BeforeClean, AfterClean Aufgaben, die in eines dieser Ziele eingefügt wurden, werden ausgeführt, bevor oder nachdem die Funktion zum Löschen von Kernen aufgerufen wurde.
BeforePublish, AfterPublish Aufgaben, die in eines dieser Ziele eingefügt wurden, werden ausgeführt, bevor oder nachdem die Funktion zum Veröffentlichen von Kernen aufgerufen wurde.
BeforeResolveReferences, AfterResolveReferences Aufgaben, die in eines dieser Ziele eingefügt wurden, werden ausgeführt, bevor oder nachdem die Assemblyverweise aufgelöst wurden.
BeforeResGen, AfterResGen Aufgaben, die in eines dieser Ziele eingefügt wurden, werden ausgeführt, bevor oder nachdem Ressourcen generiert wurden.

Es gibt viele weitere Ziele im Buildsystem und .NET SDK. Siehe MSBuild-Ziele – SDK und Standardbuildziele.

Bewährte Methoden für benutzerdefinierte Ziele

Die Eigenschaften DependsOnTargets und BeforeTargets können beide angeben, dass ein Ziel vor einem anderen Ziel ausgeführt werden muss, aber die beiden werden in unterschiedlichen Szenarien benötigt. Sie unterscheiden sich darin, in welchem Ziel die Abhängigkeitsanforderung spezifiziert wird. Sie haben nur die Kontrolle über Ihre eigenen Ziele und können die Systemziele oder andere importierte Ziele nicht sicher ändern, was die dafür in Frage kommenden Methoden einschränkt.

Beachten Sie bei der Erstellung eines benutzerdefinierten Ziels die folgenden allgemeinen Richtlinien, um sicherzustellen, dass Ihr Ziel in der vorgesehenen Reihenfolge ausgeführt wird.

  1. Verwenden Sie das DependsOnTargets-Attribut, um Ziele anzugeben, die vor der Ausführung Ihres Ziels ausgeführt werden müssen. Bei einer Kette von Zielen, die Sie kontrollieren, kann jedes Ziel das vorherige Glied der Kette als DependsOnTargets angeben.

  2. Verwenden Sie BeforeTargets für jedes Ziel, das Sie nicht kontrollieren und das Sie vorher ausführen müssen (z. B. BeforeTargets="PrepareForBuild" für ein Ziel, das zu Beginn des Builds ausgeführt werden muss).

  3. Verwenden Sie AfterTargets für jedes Ziel, das Sie nicht kontrollieren, um zu gewährleisten, dass die von Ihnen benötigten Ausgaben verfügbar sind. Geben Sie z. B. AfterTargets="ResolveReferences" für etwas an, das eine Liste von Referenzen ändern wird.

  4. Sie können diese in Kombination verwenden. Beispiel: DependsOnTargets="GenerateAssemblyInfo" BeforeTargets="BeforeCompile".

Explizite und implizite Importe.

Von Visual Studio generierte Projekte verwenden in der Regel das Sdk-Attribut für das Projektelement. Diese Arten von Projekten werden als SDK-ähnliche Projekte bezeichnet. Siehe Verwenden von MSBuild-Projekt-SDK. Ein Beispiel:

<Project Sdk="Microsoft.Net.Sdk">

Wenn Ihr Projekt das Sdk-Attribut verwendet, werden implizit zwei Importe hinzugefügt: einer am Anfang Ihrer Projektdatei und einer am Ende.

Die impliziten Importe sind gleichbedeutend mit einer Importanweisung wie dieser, die in der ersten Zeile der Projektdatei nach dem Element Project steht:

<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />

und die folgende Importanweisung als letzte Zeile in der Projektdatei:

<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />

Diese Syntax wird als explizite SDK-Importe bezeichnet. Wenn Sie diese explizite Syntax verwenden, sollten Sie das Sdk-Attribut für das Projektelement weglassen.

Der implizite SDK-Import entspricht dem Importieren der spezifischen „allgemeinen“ .props- oder .targets-Dateien, die ein typisches Konstrukt in älteren Projektdateien sind, wie etwa:

<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />

und

<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

Alle diese alten Referenzen sollten durch die explizite SDK-Syntax ersetzt werden, die weiter oben in diesem Abschnitt behandelt wurde.

Wenn Sie die explizite SDK-Syntax verwenden, können Sie vor dem ersten Import oder nach dem endgültigen SDK-Import Ihren eigenen Code hinzufügen. Das bedeutet, dass Sie das Verhalten ändern können, indem Sie Eigenschaften vor dem ersten Import festlegen, die in der importierten .props-Datei wirksam werden. Sie können außerdem ein Ziel außer Kraft setzen, das nach dem endgültigen Import in einer der SDK-.targets-Dateien definiert ist. Mit dieser Methode können Sie BeforeBuild oder AfterBuild außer Kraft setzen, wie im Folgenden erläutert wird.

Nächste Schritte

Es gibt noch viel mehr, was Sie mit MSBuild tun können, um den Build nach Ihren Wünschen anzupassen. Weitere Informationen finden Sie unter Anpassen Ihres Builds.