延伸 Visual Studio 建置流程
Visual Studio 建置處理序是由匯入至專案檔的一系列 MSBuild .targets
檔案所定義。 如果您使用 SDK 做為 Visual Studio 專案,則這些匯入是隱含的。 可以擴充其中一個已匯入的檔案 (Microsoft.Common.targets),以讓您在建置處理序的數個點執行自訂工作。 本文說明您可以使用三種方法來擴充 Visual Studio 建置處理序:
建立自定義目標,並指定何時應該使用
BeforeTargets
和AfterTargets
屬性執行。覆寫通用目標中定義的
DependsOn
屬性。覆寫通用目標中定義的特定預先定義目標 (Microsoft.Common.targets 或其匯入的檔案)。
AfterTargets 和 BeforeTargets
您可以在自訂目標上使用 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 匯入所覆寫。 如需預先定義的目標清單, 請參閱本文結尾的資料表 。
擴充 DependsOn 屬性
擴充組建流程圖的另一種方式是使用 DependsOn
屬性 (例如 BuildDependsOn
),指定應在標準目標之前執行的目標。
這個方法最好覆寫預先定義的目標,下一節會討論。 覆寫預先定義的目標是擴充尚獲支援的較舊方法,但因為 MSBuild 會循序評估目標的定義,所以沒有任何方法可防止另一個匯入您專案的專案覆寫您已覆寫的目標。 因此,例如,在匯入所有其他專案之後,專案檔中所定義的最後一個 AfterBuild
目標就是建置期間所使用的目標。
您可以覆寫整個通用目標中 DependsOnTargets
屬性中所使用的 DependsOn
屬性,來防止意外覆寫目標。 例如,Build
目標包含 "$(BuildDependsOn)"
的 DependsOnTargets
屬性值。 考量:
<Target Name="Build" DependsOnTargets="$(BuildDependsOn)"/>
這部分的 XML 指出必須先執行 BuildDependsOn
屬性中所指定的所有目標,才能執行 Build
目標。 BuildDependsOn
屬性定義為:
<PropertyGroup>
<BuildDependsOn>
$(BuildDependsOn);
BeforeBuild;
CoreBuild;
AfterBuild
</BuildDependsOn>
</PropertyGroup>
您可以在專案檔結尾宣告名為 BuildDependsOn
的另一個屬性,來覆寫此屬性值。 在 SDK 樣式專案中,這表示您必須使用明確的匯入。 請參閱 隱含和明確匯入,讓您可以在上次匯入之後放置 DependsOn
屬性。 在新屬性中包含先前的 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 屬性
識別通用目標中您要覆寫的預先定義
DependsOn
屬性。 如需經常覆寫的DependsOn
屬性清單,請參閱下表。在專案檔結尾定義另一個屬性執行個體。 在新屬性中,包含原始屬性 (例如
$(BuildDependsOn)
)。在屬性定義之前或之後,定義您的自訂目標。
建置專案檔。
經常覆寫的 DependsOn 屬性
屬性名稱 | 新增目標在此點之前執行: |
---|---|
BuildDependsOn |
主要組建進入點。 如果您想要在整個建置處理序之前或之後插入自訂目標,這是要覆寫此屬性。 |
RebuildDependsOn |
Rebuild |
RunDependsOn |
最終組建輸出的執行 (如果是.EXE) |
CompileDependsOn |
編譯目標名稱 (Compile 目標)。 如果您想要在編譯步驟之前或之後插入自訂處理序,這是要覆寫此屬性。 |
CreateSatelliteAssembliesDependsOn |
建立附屬組件 |
CleanDependsOn |
目標 Clean (刪除所有中繼和最終組建輸出)。 如果您想要清除自訂建置處理序的輸出,這是要覆寫此屬性。 |
PostBuildEventDependsOn |
PostBuildEvent 目標 |
PublishBuildDependsOn |
組建發佈 |
ResolveAssemblyReferencesDependsOn |
目標 ResolveAssemblyReferences (尋找給定相依性之相依性的可轉移關閉)。 請參閱 ResolveAssemblyReference 。 |
範例:BuildDependsOn 和 CleanDependsOn
下列範例與 BeforeTargets
和 AfterTargets
範例類似,但示範如何達到類似的功能。 其會使用 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->'$(_OutputCopyLocation)%(RecursiveDir)%(Filename)%(Extension)')"/>
<Copy SourceFiles="@(_FilesToCopy)"
DestinationFiles="@(_FilesToCopy->'$(_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 目標檔案之後,BuildDependsOn
和 CleanDependsOn
元素必須出現。
覆寫預先定義的目標
通用 .targets
檔案包含一組預先定義的空目標,可在建置處理序的部分主要目標之前和之後呼叫。 例如,MSBuild 在主要 CoreBuild
目標之前呼叫 BeforeBuild
目標,而在 CoreBuild
目標之後呼叫 AfterBuild
目標。 根據預設,通用目標中的空目標不會執行任何作業,但是您可以在專案檔中定義您想要的目標,以覆寫其預設行為。 本文稍早所述的方法是慣用的方法,但您可能會遇到使用此方法的較舊程序代碼。
如果您的專案使用 SDK (例如 Microsoft.Net.Sdk
),您必須從隱含匯入變更為明確匯入,如顯式和隱式匯入中所述。
覆寫預先定義的目標
如果專案使用
Sdk
屬性,請將它變更為顯示的匯入語法。 請參閱顯式和隱式匯入。識別通用目標中您要覆寫的預先定義目標。 如需您可安全覆寫之目標的完整清單,請參閱下表。
在專案檔結尾,於
</Project>
標記及匯入顯式 SDK 後的正前方定義目標。 例如:<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>
請注意,
Sdk
已移除最上層Project
元素上的屬性。建置專案檔。
預先定義目標的資料表
下表顯示通用目標中您可覆寫的所有目標。
目標名稱 | 描述 |
---|---|
BeforeCompile 、AfterCompile |
在核心編譯完成之前或之後,會執行插入至其中一個目標的工作。 大部分的自訂是在這兩個目標的其中一個中完成。 |
BeforeBuild 、AfterBuild |
在組建的任何其他項目之前或之後,將會執行其中一個目標中插入的工作。 注意︰BeforeBuild 和 AfterBuild 目標定義於大部分專案檔結尾的註解中,可讓您輕鬆地將建置前和建置後事件新增至專案檔。 |
BeforeRebuild 、AfterRebuild |
在叫用核心重建功能之前或之後,執行插入至其中一個目標的工作。 Microsoft.Common.targets 中的目標執行順序是:BeforeRebuild 、Clean 、Build 和 AfterRebuild 。 |
BeforeClean 、AfterClean |
在叫用核心清除功能之前或之後,執行插入至其中一個目標的工作。 |
BeforePublish 、AfterPublish |
在叫用核心發行功能之前或之後,執行插入至其中一個目標的工作。 |
BeforeResolveReferences 、AfterResolveReferences |
在解析組件參考之前或之後,會執行插入至其中一個目標的工作。 |
BeforeResGen 、AfterResGen |
在產生資源之前或之後,會執行插入至其中一個目標的工作。 |
建置系統和 .NET SDK 中有更多目標,請參閱 MSBuild 目標 - SDK 和預設組建目標。
自訂目標的最佳做法
屬性 DependsOnTargets
和 BeforeTargets
可以同時指定目標必須在另一個目標之前執行,但在不同的案例中都需要它們。 其差異在於指定相依性需求的目標。 您只能控制自己的目標,且無法安全地修改系統目標或其他匯入的目標,讓條件約束您所選擇的方法。
撰寫自定義目標時,請遵循這些一般指導方針,以確保您的目標是以預定順序執行。
DependsOnTargets
使用屬性來指定目標執行之前必須完成的目標。 對於您控制的目標鏈結,每個目標都可以在DependsOnTargets
中指定鏈結的上一個成員。針對
BeforeTargets
您不控制之前必須執行的任何目標使用 (例如BeforeTargets="PrepareForBuild"
,針對需要在組建中早期執行的目標)。針對
AfterTargets
您不控制的任何目標使用,以確保您所需的輸出可供使用。 例如,針對將修改參考清單的專案指定AfterTargets="ResolveReferences"
。您可以使用這些組合。 例如:
DependsOnTargets="GenerateAssemblyInfo" BeforeTargets="BeforeCompile"
。
請參閱顯式和隱式匯入
Visual Studio 產生的專案通常會在專案元素上使用 Sdk
屬性。 這些類型的專案稱為 SDK 樣式專案。 查看使用 MSBuild 專案 SDK 以下是範例:
<Project Sdk="Microsoft.Net.Sdk">
當您的專案使用 Sdk
屬性時,會隱含新增兩個匯入,一個是在專案檔開頭,另一個是在結尾。
隱式匯入相當於在 Project
元素的專案檔中第一行匯入陳述式如下:
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
和下列匯入陳述式作為專案檔中的最後一行:
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
此語法稱為顯示 SDK 匯入。 當您使用此顯式的語法時,應該省略項目元素上的 Sdk
屬性。
隱含 SDK 匯入相當於匯入舊版專案檔中一般建構的特定「通用」 .props
或 .targets
檔案,例如:
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
及
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
任何這類舊參考都應該取代為本章節稍早所示的顯式 SDK 語法。
使用顯示 SDK 語法表示您可以在第一次匯入之前或最終的 SDK 匯入之後新增自己的程式代碼。 這表示您可以在第一次匯入生效於匯入 .props
檔案之前設定屬性來變更行為,而且您可以在最終匯入之後覆寫其中一個 SDK .targets
檔案中定義的目標。 使用此方法時,您可以覆寫 BeforeBuild
或 AfterBuild
,如下所述。
下一步
您也可以使用 MSBuild 來自訂組建。 請參閱自訂您的組建。