延伸 Visual Studio 建置流程

Visual Studio 建置處理序是由匯入至專案檔的一系列 MSBuild .targets 檔案所定義。 可以擴充其中一個已匯入的檔案 (Microsoft.Common.targets),以讓您在建置處理序的數個點執行自訂工作。 本文說明您可以使用兩種方法來擴充 Visual Studio 建置處理序:

  • 覆寫通用目標中定義的特定預先定義目標 (Microsoft.Common.targets 或其匯入的檔案)。

  • 覆寫通用目標中定義的 "DependsOn" 屬性。

覆寫預先定義的目標

通用目標包含一組預先定義的空目標,可在建置處理序的部分主要目標之前和之後呼叫。 例如,MSBuild 在主要 CoreBuild 目標之前呼叫 BeforeBuild 目標,而在 CoreBuild 目標之後呼叫 AfterBuild 目標。 根據預設,通用目標中的空目標不會執行任何作業,但是您可以在匯入通用目標的專案檔中定義您想要的目標,以覆寫其預設行為。 覆寫預先定義的目標,即可使用 MSBuild 工作,更充分地控制建置處理序。

注意

SDK 樣式專案在專案檔的最後一行之後,隱含匯入目標。 這表示除非如如何:使用 MSBuild 專案 SDK 所述手動指定匯入,否則您無法覆寫預設目標。

覆寫預先定義的目標

  1. 識別通用目標中您要覆寫的預先定義目標。 如需您可安全覆寫之目標的完整清單,請參閱下表。

  2. 在專案檔結尾,於 </Project> 標記的正前方定義目標。 例如:

    <Project>
        ...
        <Target Name="BeforeBuild">
            <!-- Insert tasks to run before build here -->
        </Target>
        <Target Name="AfterBuild">
            <!-- Insert tasks to run after build here -->
        </Target>
    </Project>
    
  3. 建置專案檔。

下表顯示通用目標中您可安全覆寫的所有目標。

目標名稱 描述
BeforeCompile, AfterCompile 在核心編譯完成之前或之後,會執行插入至其中一個目標的工作。 大部分的自訂是在這兩個目標的其中一個中完成。
BeforeBuild, AfterBuild 在組建的任何其他項目之前或之後,將會執行其中一個目標中插入的工作。 注意︰BeforeBuildAfterBuild 目標定義於大部分專案檔結尾的註解中,可讓您輕鬆地將建置前和建置後事件新增至專案檔。
BeforeRebuild, AfterRebuild 在叫用核心重建功能之前或之後,執行插入至其中一個目標的工作。 Microsoft.Common.targets 中的目標執行順序是:BeforeRebuildCleanBuildAfterRebuild
BeforeClean, AfterClean 在叫用核心清除功能之前或之後,執行插入至其中一個目標的工作。
BeforePublish, AfterPublish 在叫用核心發行功能之前或之後,執行插入至其中一個目標的工作。
BeforeResolveReferences, AfterResolveReferences 在解析組件參考之前或之後,會執行插入至其中一個目標的工作。
BeforeResGen, AfterResGen 在產生資源之前或之後,會執行插入至其中一個目標的工作。

範例:AfterTargets 和 BeforeTargets

下列範例示範如何使用 AfterTargets 屬性來新增自訂目標,以使用輸出檔案執行某些動作。 在此情況下,它會將輸出檔案複製到新的資料夾 CustomOutput。 此範例也會示範如何使用 BeforeTargets 屬性並指定自訂清除作業在 CoreClean 目標之前執行,清除具有 CustomClean 目標的自訂建置作業所建立的檔案。

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

警告

務必使用與上一節表格中所列的預先定義目標不同的名稱 (例如,我們在此將自訂建置目標命名為 CustomAfterBuild,而不是 AfterBuild),因為這些預先定義的目標會由也定義目標的 SDK 匯入所覆寫。 您看不到覆寫這些目標的目標檔案匯入,但是當您使用參考 SDK 的 Sdk 屬性方法時,它會隱含地新增至專案檔的結尾。

覆寫 DependsOn 屬性

覆寫預先定義的目標是擴充建置處理序的簡單方法,但因為 MSBuild 會循序評估目標的定義,所以沒有任何方法可防止另一個匯入您專案的專案覆寫您已覆寫的目標。 因此,例如,在匯入所有其他專案之後,專案檔中所定義的最後一個 AfterBuild 目標就是建置期間所使用的目標。

您可以覆寫整個通用目標中 DependsOnTargets 屬性中所使用的 DependsOn 屬性,來防止意外覆寫目標。 例如,Build 目標包含 "$(BuildDependsOn)"DependsOnTargets 屬性值。 考量:

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

這部分的 XML 指出必須先執行 BuildDependsOn 屬性中所指定的所有目標,才能執行 Build 目標。 BuildDependsOn 屬性定義為:

<PropertyGroup>
    <BuildDependsOn>
        BeforeBuild;
        CoreBuild;
        AfterBuild
    </BuildDependsOn>
</PropertyGroup>

您可以在專案檔結尾宣告名為 BuildDependsOn 的另一個屬性,來覆寫此屬性值。 在新屬性中包含先前的 BuildDependsOn 屬性,即可將新目標新增至目標清單開頭和結尾。 例如:

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

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

匯入您專案檔的專案可以覆寫這些屬性,而不覆寫您進行的自訂。

覆寫 DependsOn 屬性

  1. 識別通用目標中您要覆寫的預先定義 DependsOn 屬性。 如需經常覆寫的 DependsOn 屬性清單,請參閱下表。

  2. 在專案檔結尾定義另一個屬性執行個體。 在新屬性中,包含原始屬性 (例如 $(BuildDependsOn))。

  3. 在屬性定義之前或之後,定義您的自訂目標。

  4. 建置專案檔。

經常覆寫的 DependsOn 屬性

屬性名稱 描述
BuildDependsOn 如果您想要在整個建置處理序之前或之後插入自訂目標,這是要覆寫的屬性。
CleanDependsOn 如果您想要清除自訂建置處理序的輸出,這是要覆寫的屬性。
CompileDependsOn 如果您想要在編譯步驟之前或之後插入自訂處理序,這是要覆寫的屬性。

範例:BuildDependsOn 和 CleanDependsOn

下列範例與 BeforeTargetsAfterTargets 範例類似,但示範如何達到類似的功能。 其會使用 BuildDependsOn 擴充組建,以新增自己的工作 CustomAfterBuild,該工作會在建置之後複製輸出檔案,也會使用 CleanDependsOn 新增對應的 CustomClean 工作。

在此範例中,這是 SDK 樣式專案。 如本文稍早的 SDK 樣式專案相關附註所述,您必須使用手動匯入方法,而不是 Visual Studio 在產生專案檔時所使用的 Sdk 屬性。

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

元素的順序很重要。 匯入標準 SDK 目標檔案之後,BuildDependsOnCleanDependsOn 元素必須出現。