在製作多框架項目的 NuGet 包時應該注意的問題(buildMultiTargeting TargetFrameworks)

製作一個 dll 引用的 NuGet 包簡直是一鍵完成,無論是不是多框架項目;製作 dotnet-tools 也是如此。但如果需要自定義一些編譯步驟,那麼就需要在製作 NuGet 包時做很多的特殊處理了。

本文介紹製作適用於多框架項目的 NuGet 工具包時應該注意的問題。


背景知識

NuGet 包內的文件夾結構

回顧一下 NuGet 包的文件夾結構:

+ /
+ lib/
+ ref/
+ runtimes/
+ content/
+ build/
+ buildMultiTargeting/
+ buildTransitive
+ tools/

由於涉及到自定義 NuGet 包的代碼都寫在 build buildMultiTargetingbuildTransitive 中,其他都不涉及到 NuGet 包在編譯期間會做的事情,另外,buildTransitive 是用來處理包傳遞過程中的編譯過程的,所以我們本文只說也只需要說 buildbuildMultiTargeting

這裏面的代碼都是用 Target 寫出來的,如果你對此不瞭解,建議閱讀這些博客:

製作有自定義功能的 NuGet 包

我之前寫過一些關於如何製作各種高級功能的 NuGet 包的博客:

按照上面的博客製作出來的 NuGet 包其實是適用於單框架項目和多框架項目的,甚至也適用於傳統的非 SDK 風格的項目。

關於單框架和多框架項目,就是項目文件中這裏的差別:

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

  <PropertyGroup>
    <!-- 單框架項目 -->
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

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

  <PropertyGroup>
    <!-- 多框架項目 -->
    <TargetFrameworks>netcoreapp3.1;net48</TargetFrameworks>
  </PropertyGroup>

</Project>

但是,有的小夥伴希望探索一些更高級的用法,所以可能會遇到在多框架項目中,NuGet 包自定義的功能不執行的問題。

接下來,我們瞭解一下在單框架和多框架下 NuGet 包執行上的不同。

執行時機

我們打出這樣的兩種 NuGet 包,一種是僅包含 build 文件夾而不包含 buildMultiTargeting 文件夾;一種是包含 build 文件夾和 buildMultiTargeting 文件夾。

我們的目標項目一種是單框架項目;一種是多框架項目。

於是我們可以得到這樣的四種不同的組合情況:

  1. 僅含 build 文件夾的 NuGet 包裝到單框架項目中
  2. 僅含 build 文件夾的 NuGet 包裝到多框架項目中
  3. 包含 buildbuildMultiTargeting 文件夾的 NuGet 包裝到單框架項目中
  4. 包含 buildbuildMultiTargeting 文件夾的 NuGet 包裝到多框架項目中

1. 僅含 build 文件夾的 NuGet 包裝到單框架項目中

在這種情況下,build 文件夾中的 .props.targets 文件在目標項目編譯時正常執行。

2. 僅含 build 文件夾的 NuGet 包裝到多框架項目中

在這種情況下,build 文件夾中的 .props.targets 文件,會分別在目標項目編譯每個框架的時候執行一次。

例如這種項目:

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

  <PropertyGroup>
    <!-- 多框架項目 -->
    <TargetFrameworks>netcoreapp3.1;net48</TargetFrameworks>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Walterlv.NullableAttributes.Source" Version="0.15.0" />
  </ItemGroup>

</Project>

那麼,在編譯 netcoreapp3.1 框架的時候會執行一次 Walterlv.NullableAttributes.Source 包中 build 文件夾中的編譯任務;在編譯 net48 框架的時候又會執行一次 Walterlv.NullableAttributes.Source 包中 build 文件夾中的編譯任務。

3. 包含 buildbuildMultiTargeting 文件夾的 NuGet 包裝到單框架項目中

在這種情況下,buildMultiTargeting 中的任何編譯任務相當於不存在。編譯過程與情況 1 是完全一樣的。

4. 包含 buildbuildMultiTargeting 文件夾的 NuGet 包裝到多框架項目中

從 NuGet 5.x 版本開始在這種情況下,build 中的內容和 buildMultiTargeting 中的編譯任務會同時參與編譯。

依然舉例這樣的目標項目(不過使用了含 buildMultiTargeting 的 NuGet 包):

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

  <PropertyGroup>
    <!-- 多框架項目 -->
    <TargetFrameworks>netcoreapp3.1;net48</TargetFrameworks>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Walterlv.NullableAttributes.Source" Version="2.1.1" />
  </ItemGroup>

</Project>

編譯一開始,會將 buildMultiTargeting 中的編譯任務加入執行。在編譯 netcoreapp3.1 框架的時候會執行一次 Walterlv.NullableAttributes.Source 包中 build 文件夾中的編譯任務;在編譯 net48 框架的時候又會執行一次 Walterlv.NullableAttributes.Source 包中 build 文件夾中的編譯任務。而這兩個單獨框架的編譯結束後,buildMultiTargeting 中的任務纔會結束。

也就是說,這兩個編譯任務文件夾中的編譯任務是都會執行的。但是:

兩者參與編譯的 Targets 不一樣

下表中列出了在你沒有編寫任何擴展的任務或者干預已有 Target 執行的情況下,默認可以依賴的 Target(指的是可以通過 BeforeTargets="xx"AfterTargets="xx" 的方式擴展編譯任務:

可依賴的 Target build buildMultiTargeting
BeforeCompile
Compile
CoreCompile
AfterCompile
BeforeBuild
Build
AfterBuild
BeforeRebuild
Rebuild ✔(如果強行執行)
AfterRebuild
BeforeClean ✔(如果強行執行)
Clean ✔(如果強行執行) ✔(如果強行執行)
AfterClean ✔(如果強行執行)

注:強制執行說的是一般編譯時不會執行,你需要在命令中指定執行這個 Target。也對應到 Visual Studio 裏的“重新編譯”和“清理”的功能。

爲了更好理解上表,這裏給出一個例子。下面的代碼如果在 build 文件夾中則會在編譯過程輸出一堆星號,而如果在 buildMultiTargeting 文件夾中則不會執行。而無論目標項目是否是多框架的。但換成 AfterBuild 則會兩個文件夾中都輸出。

<Target Name="WalterlvDemoTarget" AfterTargets="Build">
    <Message Text="****************************************************************" />
</Target>

當然,不要被這個第 4 種情況帶歪了!如果你的 NuGet 包依然只有一個 build 文件夾,那麼上面的所有 Targets 都是會執行的。


參考資料


我的博客會首發於 https://blog.walterlv.com/,而 CSDN 會從其中精選發佈,但是一旦發佈了就很少更新。

如果在博客看到有任何不懂的內容,歡迎交流。我搭建了 dotnet 職業技術學院 歡迎大家加入。

知識共享許可協議

本作品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名呂毅(包含鏈接:https://walterlv.blog.csdn.net/),不得用於商業目的,基於本文修改後的作品務必以相同的許可發佈。如有任何疑問,請與我聯繫

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章