我之前寫過一篇 理解 C# 項目 csproj 文件格式的本質和編譯流程,其中,Target
節點就是負責編譯流程的最關鍵的節點。但因爲篇幅限制,那篇文章不便詳說。於是,我在本文說說 Target
節點。
Target 的節點結構
<Target>
內部幾乎有着跟 <Project>
一樣的節點結構,內部也可以放 PropertyGroup
和 ItemGroup
,不過還能放更加厲害的 Task
。按照慣例,我依然用思維導圖將節點結構進行了總結:
▲ 上面有綠線和藍線區分,僅僅是因爲出現了交叉,怕出現理解歧義
<Hash>
和 <WriteCodeFragment>
都是 Task
。我們可以看到,Task
是多種多樣的,它可以佔用一個 xml 節點。而本例中,WriteCodeFragment
Task 就是生成代碼文件,並且將生成的文件作爲一項 Compile
的 Item 和 FileWrites
的 Item。在 理解 C# 項目 csproj 文件格式的本質和編譯流程 中我們提到 ItemGroup
的節點,其作用由 Target
指定。所有 Compile
會在名爲 CoreCompile
的 Target
中使用,而 FileWrites
在 Microsoft.NET.Sdk 的多處都生成了這樣的節點,不過目前從我查看到的全部 Microsoft.NET.Sdk 中,發現內部並沒有使用它。
Target 執行的時機和先後順序
既然 <Target>
內部節點很大部分跟 <Project>
一樣,那區別在哪裏呢?<Project>
裏的 <PropertyGroup>
和 <ItemGroup>
是靜態的狀態,如果使用 Visual Studio 打開項目,那麼所有的狀態將會直接在 Visual Studio 的項目文件列表和項目屬性中顯示;而 <Target>
內部的 <PropertyGroup>
和 <ItemGroup>
是在編譯期間動態生成的,不會在 Visual Studio 中顯示;不過,它爲我們提供了一種在編譯期間動態生成文件或屬性的能力。
總結起來就是——Target 是在編譯期間執行的。
不過,同樣是編譯期間,那麼多個 Target
,它們之間的執行時機是怎麼確定的呢?
一共有五個屬性決定了 Target 之間的執行順序:
- Project 的屬性
InitialTargets
項目初始化的時候應該執行的 TargetDefaultTargets
如果沒有指定執行的 Target,那麼這個屬性將指定執行的 Target
- Target 的屬性
DependsOnTargets
在執行此 Target 之前應該執行的另一個或多個 TargetBeforeTargets
這是 MSBuild 4.0 新增的,指定應該在另一個或多個 Target 之前執行AfterTargets
這也是 MSBuild 4.0 新增的,指定應該在另一個或多個 Target 之後執行
通過指定這些屬性,我們的 Target
能夠被 MSBuild 自動選擇合適的順序進行執行。例如,當我們希望自定義版本號,那麼就需要趕在我們此前提到的 GenerateAssemblyInfo
之前執行。
Microsoft.NET.Sdk 爲我們提供的現成可用的 Task
有 Microsoft.NET.Sdk 的幫助,我們可以很容易地編寫自己的 Target,因爲很多功能它都幫我們實現好了,我們排列組合一下就好。
Copy
複製文件 Rosyln 如何使用 MSBuild Copy 複製文件Move
移動文件 Move TaskDelete
刪除文件Message
顯示一個輸出信息(我在 如何創建一個基於 MSBuild Task 的跨平臺的 NuGet 工具包 中利用這個進行調試)Warning
顯示一個警告信息Error
報錯(這樣,編譯就會以錯誤結束)CombinePath
,ConvertToAbsolutePath
拼接路徑,轉成絕對路徑CreateItem
,CreateProperty
創建項或者屬性Csc
調用 csc.exe 編譯 Csc TaskMSBuild
編譯一個項目 MSBuild TaskExec
執行一個外部命令(我在 如何創建一個基於命令行工具的跨平臺的 NuGet 工具包 一文中利用到了這個 Task 執行命令)WriteCodeFragment
生成一段代碼 WriteCodeFragment TaskWriteLinesToFile
向文件中寫文字 WriteLinesToFile Task
提供的 Task 還有更多,如果上面不夠你寫出想要的功能,可以移步至官方文檔翻閱:MSBuild Task Reference - Visual Studio - Microsoft Docs。
使用自己寫的 Task
我有另外的一篇文章來介紹如何創建一個基於 MSBuild Task 的跨平臺的 NuGet 工具包 - 呂毅。如果希望自己寫 Ta
差量編譯
如果你認爲自己寫的 Target
執行比較耗時,那麼就可以使用差量編譯。我另寫了一篇文章專門來說 Target 的差量編譯:每次都要重新編譯?太慢!讓跨平臺的 MSBuild/dotnet build 的 Target 支持差量編譯 - 呂毅。
參考資料
- Target Build Order - Visual Studio - Microsoft Docs
- MSBuild Task Reference - Visual Studio - Microsoft Docs
我的博客會首發於 https://blog.walterlv.com/,而 CSDN 會從其中精選發佈,但是一旦發佈了就很少更新。
如果在博客看到有任何不懂的內容,歡迎交流。我搭建了 dotnet 職業技術學院 歡迎大家加入。
本作品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名呂毅(包含鏈接:https://walterlv.blog.csdn.net/),不得用於商業目的,基於本文修改後的作品務必以相同的許可發佈。如有任何疑問,請與我聯繫。