Freigeben über


Erweitern des Visual Studio-Buildprozesses

Der Visual Studio-Buildprozess wird durch eine Reihe von MSBuild-Dateien .targets definiert, die in Ihre Projektdatei importiert werden. Diese Importe sind implizit, wenn Sie ein SDK verwenden, wie es bei Visual Studio-Projekten in der Regel der Fall ist. Eine dieser importierten Dateien, Microsoft.Common.targets, kann erweitert werden, damit Sie benutzerdefinierte Aufgaben an mehreren Stellen im Buildprozess ausführen können. In diesem Artikel werden drei Methoden erläutert, mit denen Sie den Visual Studio-Buildprozess erweitern können:

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

  • Überschreiben Sie die DependsOn in den gemeinsamen Zielen definierten Eigenschaften.

  • Überschreiben Sie bestimmte vordefinierte Ziele, die in den gemeinsamen Zielen definiert sind (Microsoft.Common.targets oder die dateien, die sie importiert).

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.

Das folgende Beispiel zeigt, wie Sie das AfterTargets Attribut verwenden, um ein benutzerdefiniertes Ziel hinzuzufügen, das mit den Ausgabedateien eine Aktion ausführt. In diesem Fall kopiert sie die Ausgabedateien in einen neuen Ordner CustomOutput. Im Beispiel wird auch gezeigt, wie die vom benutzerdefinierten Buildvorgang erstellten Dateien mit einem CustomClean Ziel mithilfe eines BeforeTargets Attributs bereinigt und angegeben werden, 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

Achten Sie darauf, andere Namen als die vordefinierten Ziele zu verwenden (z. B. ist das benutzerdefinierte Buildziel hier CustomAfterBuild und nicht AfterBuild), da der SDK-Import diese vordefinierten Ziele überschreibt und ebenfalls definiert. Eine Liste vordefinierter Ziele finden Sie in der Tabelle am Ende dieses Artikels.

Erweitern der DependsOn-Eigenschaften

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

Es ist vorzuziehen, diese Methode anstatt das Überschreiben vordefinierter Ziele zu verwenden, was im nächsten Abschnitt erläutert wird. Das Überschreiben vordefinierter Build-Ziele ist eine ältere Methode, die noch unterstützt wird. Da MSBuild die Definition von Build-Zielen sequenziell auswertet, gibt es keine Möglichkeit zu verhindern, dass ein anderes Projekt, das Ihr Projekt importiert, die von Ihnen bereits überschriebenen Ziele erneut überschreibt. Beispielsweise wird das letzte AfterBuild in der Projektdatei definierte Ziel, dasjenige, nachdem alle anderen Projekte importiert wurden, während des Builds verwendet.

Sie können unbeabsichtigte Überschreibungen von Zielen verhindern, indem Sie die DependsOn Eigenschaften überschreiben, die in DependsOnTargets Attributen in den üblichen Zielen verwendet werden. Zum Beispiel enthält das Build Ziel einen DependsOnTargets Attributwert von "$(BuildDependsOn)". Berücksichtigen Sie dabei Folgendes:

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

Dieser Teil von XML gibt an, dass alle in der Build Eigenschaft angegebenen Ziele zuerst ausgeführt werden müssen, bevor das BuildDependsOn Ziel ausgeführt werden kann. Die BuildDependsOn Eigenschaft wird wie folgt definiert:

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

Sie können diesen Eigenschaftswert überschreiben, indem Sie eine andere Eigenschaft deklarieren, die am Ende der Projektdatei benannt ist BuildDependsOn . In einem SDK-Projekt bedeutet dies, dass Sie explizite Importe verwenden müssen. Siehe Implizite und explizite Importe, damit Sie die DependsOn-Eigenschaft nach dem letzten Import platzieren können. Indem Sie die vorherige BuildDependsOn Eigenschaft in die neue Eigenschaft einschließen, können Sie am Anfang und Ende der Zielliste neue Ziele hinzufügen. 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, die Ihre Projektdatei importieren, können diese Eigenschaften weiter erweitern, ohne die vorgenommenen Anpassungen zu überschreiben.

So ändern Sie eine DependsOn-Eigenschaft

  1. Identifizieren Sie eine vordefinierte DependsOn Eigenschaft in den allgemeinen Zielen, die Sie außer Kraft setzen möchten. Eine Liste der häufig überschriebenen DependsOn Eigenschaften finden Sie in der folgenden Tabelle.

  2. Definieren Sie eine weitere Instanz der Eigenschaft oder der Eigenschaften am Ende Ihrer 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

Eigenschaftsname Hinzugefügte Ziele werden vor diesem Punkt ausgeführt:
BuildDependsOn Der Haupteinstiegspunkt für den Build. Überschreiben Sie diese Eigenschaft, wenn Sie benutzerdefinierte Ziele vor oder nach dem gesamten Buildprozess einfügen möchten.
RebuildDependsOn Die 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 Satellitenassemblies
CleanDependsOn Das Clean Ziel (Löschen aller Zwischenergebnisse und Endergebnisse der Build-Ausgaben). Überschreiben Sie diese Eigenschaft, wenn Sie die Ausgabe des benutzerdefinierten Buildprozesses bereinigen möchten.
PostBuildEventDependsOn Das PostBuildEvent Ziel
PublishBuildDependsOn Veröffentlichung erstellen
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 ist dem BeforeTargets und AfterTargets Beispiel ähnlich, zeigt jedoch, wie eine ähnliche Funktionalität erzielt werden kann. Es erweitert den Build, indem es BuildDependsOn verwendet, um eine eigene Aufgabe CustomAfterBuild hinzuzufügen, die die Ausgabedateien nach dem Build kopiert, und fügt die entsprechende CustomClean Aufgabe auch mithilfe von CleanDependsOn hinzu.

In diesem Beispiel handelt es sich um ein SDK-Projekt. Wie zu Beginn dieses Artikels im Hinweis auf SDK-basierte Projekte erwähnt, müssen Sie die manuelle Importmethode anstelle des Sdk-Attributs verwenden, das von Visual Studio beim Erstellen von Projektdateien genutzt 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 ist wichtig. Die BuildDependsOn- und CleanDependsOn-Elemente müssen nach dem Importieren der Standard-SDK-Zieldatei erscheinen.

Überschreiben vordefinierter Ziele

Die allgemeinen .targets Dateien enthalten einen Satz vordefinierter leerer Ziele, die vor und nach einigen der wichtigsten Ziele im Buildprozess aufgerufen werden. Beispielsweise ruft MSBuild das BeforeBuild Ziel vor dem Hauptziel CoreBuild und dem AfterBuild Ziel nach dem CoreBuild Ziel auf. Standardmäßig tun die leeren Ziele in den gemeinsamen Zielen nichts, aber Sie können ihr Standardverhalten außer Kraft setzen, indem Sie die gewünschten Ziele in einer Projektdatei definieren. Die weiter oben in diesem Artikel beschriebenen Methoden werden bevorzugt, sie können jedoch auf ältere 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 expliziten und impliziten Importen erläutert.

Ein vordefiniertes Ziel überschreiben

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

  2. Identifizieren Sie ein vordefiniertes Ziel in den allgemeinen Zielen, die Sie außer Kraft setzen möchten. In der folgenden Tabelle finden Sie die vollständige Liste der Ziele, die Sie sicher außer Kraft setzen können.

  3. Definieren Sie das Ziel oder die Ziele am Ende der Projektdatei, unmittelbar vor dem </Project> Tag und nach dem expliziten SDK-Import. 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 Element der obersten Ebene Project entfernt wurde.

  4. Erstellen Sie die Projektdatei.

Tabelle der vordefinierten Ziele

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

Zielname BESCHREIBUNG
BeforeCompile, AfterCompile Aufgaben, die in einem dieser Ziele eingefügt werden, werden vor oder nach Abschluss der Kernkompilierung ausgeführt. Die meisten Anpassungen werden in einem dieser beiden Ziele durchgeführt.
BeforeBuild, AfterBuild Aufgaben, die in einem dieser Ziele eingefügt werden, werden vor oder nach allen anderen Vorgängen im Build ausgeführt. Anmerkung: Die BeforeBuild und AfterBuild Targets sind bereits am Ende der meisten Projektdateien in Kommentaren definiert, sodass Sie Ihrer Projektdatei problemlos Pre-Build- und Post-Build-Ereignisse hinzufügen können.
BeforeRebuild, AfterRebuild Aufgaben, die in einem dieser Aufgabenbereiche eingefügt werden, werden vor oder nach dem Aufrufen der Kernwiederherstellungsfunktionalität ausgeführt. Die Reihenfolge der Zielausführung in Microsoft.Common.targets lautet: BeforeRebuild, Clean, , Buildund dann AfterRebuild.
BeforeClean, AfterClean Aufgaben, die in eines dieser Ziele eingefügt werden, werden ausgeführt, bevor oder nachdem die Kernbereinigungsfunktion aufgerufen wird.
BeforePublish, AfterPublish Aufgaben, die in einem dieser Ziele eingefügt werden, werden vor oder nach dem Aufrufen der kernigen Veröffentlichungsfunktionalität ausgeführt.
BeforeResolveReferences, AfterResolveReferences Aufgaben, die in eines dieser Targets eingefügt werden, werden ausgeführt, bevor oder nachdem Assemblyverweise aufgelöst werden.
BeforeResGen, AfterResGen Aufgaben, die in einem dieser Ziele eingefügt werden, werden vor oder nach dem Generieren von Ressourcen ausgeführt.

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 beide sind in verschiedenen Szenarien erforderlich. Sie unterscheiden sich darin, für welches Zielelement die Abhängigkeitsanforderung festgelegt wird. Sie haben nur die Kontrolle über Ihre eigenen Ziele und können die Systemziele oder andere importierte Ziele nicht sicher ändern, sodass Die Auswahl von Methoden begrenzt wird.

Befolgen Sie beim Erstellen eines benutzerdefinierten Ziels diese allgemeinen Richtlinien, um sicherzustellen, dass Ihr Ziel in der beabsichtigten Reihenfolge ausgeführt wird.

  1. Verwenden Sie das DependsOnTargets Attribut, um Ziele anzugeben, die vor der Ausführung des Ziels ausgeführt werden müssen. Für eine Kette von Zielen, die Sie steuern, kann jedes Ziel das vorherige Element der Kette in DependsOnTargetsangeben.

  2. Wenden Sie BeforeTargets für jedes Ziel an, das Sie nicht kontrollieren und das Sie ausführen müssen, bevor Sie es nutzen können (z. B. BeforeTargets="PrepareForBuild" für ein Ziel, das früh im Build ausgeführt werden muss).

  3. Verwenden Sie AfterTargets für jedes Ziel, das Sie nicht steuern, um sicherzustellen, dass die benötigten Ausgaben verfügbar sind. Geben Sie z. B. für etwas an AfterTargets="ResolveReferences" , das eine Liste von Verweisen ändert.

  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-Stilprojekte bezeichnet. Siehe Verwenden von MSBuild-Projekt-SDKs. Ein Beispiel:

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

Wenn Ihr Projekt das Sdk Attribut verwendet, werden implizit zwei Importe hinzugefügt, eines am Anfang der Projektdatei und eines am Ende.

Die impliziten Importe entsprechen einer Importanweisungen wie der folgenden als erste Zeile in der Projektdatei nach dem Project Element:

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

und die folgende Import-Anweisung 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, z. B.:

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

und

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

Alle solchen alten Verweise sollten durch die explizite SDK-Syntax ersetzt werden, die weiter oben in diesem Abschnitt gezeigt 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, der in der importierten .props Datei wirksam wird, und Sie können ein Ziel außer Kraft setzen, das nach dem endgültigen Import in einer der SDK-Dateien .targets definiert ist. Mit dieser Methode können Sie BeforeBuild oder AfterBuild überschreiben, wie im Folgenden erläutert.

Nächste Schritte

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