当前位置: 首页 > news >正文

在制作多框架项目的 NuGet 包时应该注意的问题(buildMultiTargeting TargetFrameworks)

制作一个 dll 引用的 NuGet 包简直是一键完成,无论是不是多框架项目;制作 dotnet-tools 也是如此。但如果需要自定义一些编译步骤,那么就需要在制作 NuGet 包时做很多的特殊处理了。

本文介绍制作适用于多框架项目的 NuGet 工具包时应该注意的问题。


本文内容

    • 背景知识
      • NuGet 包内的文件夹结构
      • 制作有自定义功能的 NuGet 包
    • 执行时机
      • 1. 仅含 `build` 文件夹的 NuGet 包装到单框架项目中
      • 2. 仅含 `build` 文件夹的 NuGet 包装到多框架项目中
      • 3. 包含 `build` 和 `buildMultiTargeting` 文件夹的 NuGet 包装到单框架项目中
      • 4. 包含 `build` 和 `buildMultiTargeting` 文件夹的 NuGet 包装到多框架项目中

背景知识

NuGet 包内的文件夹结构

回顾一下 NuGet 包的文件夹结构:

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

由于涉及到自定义 NuGet 包的代码都写在 build buildMultiTargetingbuildTransitive 中,其他都不涉及到 NuGet 包在编译期间会做的事情,另外,buildTransitive 是用来处理包传递过程中的编译过程的,所以我们本文只说也只需要说 buildbuildMultiTargeting

这里面的代码都是用 Target 写出来的,如果你对此不了解,建议阅读这些博客:

  • 理解 C# 项目 csproj 文件格式的本质和编译流程 - walterlv
  • 从零开始制作 NuGet 源代码包(全面支持 .NET Core / .NET Framework / WPF 项目) - walterlv

制作有自定义功能的 NuGet 包

我之前写过一些关于如何制作各种高级功能的 NuGet 包的博客:

  • 如何创建一个基于命令行工具的跨平台的 NuGet 工具包 - walterlv
  • 如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包 - walterlv
  • 从零开始制作 NuGet 源代码包(全面支持 .NET Core / .NET Framework / WPF 项目) - walterlv

按照上面的博客制作出来的 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" 的方式扩展编译任务:

可依赖的 TargetbuildbuildMultiTargeting
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 都是会执行的。


参考资料

  • Create a NuGet package using nuget.exe CLI - Microsoft Docs
  • Allow package authors to define build assets transitive behavior · NuGet/Home Wiki

我的博客会首发于 https://blog.walterlv.com/,而 CSDN 会从其中精选发布,但是一旦发布了就很少更新。

如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

知识共享许可协议

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://walterlv.blog.csdn.net/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。

相关文章:

  • 专业团队:推荐一个网站,生成巨幅文字注释
  • .NET 将多个程序集合并成单一程序集的 4+3 种方法
  • 通过设置启用 Visual Studio 默认关闭的大量强大的功能提升开发效率
  • 使用 SetWindowCompositionAttribute 来控制程序的窗口边框和背景(可以做 Acrylic 亚克力效果、模糊效果、主题色效果等)
  • input 不能为空 js_我用JS刷LeetCode | Day 9 | Implement strStr()
  • 宝塔同时安装苹果cms海洋cms_maccms安装了后接下来做什么?
  • hive 修改表的存储格式_Hive存储格式
  • mac redis 链接_2018 MAC下安装Redis和Redis可视化工具RDM并连接Redis
  • python的lib文件夹在哪_如何导入模块中的lib文件夹
  • heidisql连接不是本地_本地备份与云备份:选择您的备份计划
  • ibaties 更新 数据类型不一致_关于Python 3.9,那些你不知道的事
  • join为什么每个字符都分割了 js_【项目总结】之——JS分割字符串
  • rto净化效率计算公式_吕梁油墨厂RTO焚烧炉设计计算
  • 修改段落内容_为什么论文修改后重复率还变高?
  • windbg找不到pdb文件_使用Windbg时关于符号文件路径设置问题
  • Golang-长连接-状态推送
  • JavaScript设计模式系列一:工厂模式
  • nfs客户端进程变D,延伸linux的lock
  • Redash本地开发环境搭建
  • vue2.0开发聊天程序(四) 完整体验一次Vue开发(下)
  • 分享自己折腾多时的一套 vue 组件 --we-vue
  • 观察者模式实现非直接耦合
  • 回顾2016
  • 计算机常识 - 收藏集 - 掘金
  • 思考 CSS 架构
  • 为什么要用IPython/Jupyter?
  • 温故知新之javascript面向对象
  • linux 淘宝开源监控工具tsar
  • Redis4.x新特性 -- 萌萌的MEMORY DOCTOR
  • ​flutter 代码混淆
  • ​直流电和交流电有什么区别为什么这个时候又要变成直流电呢?交流转换到直流(整流器)直流变交流(逆变器)​
  • #13 yum、编译安装与sed命令的使用
  • #android不同版本废弃api,新api。
  • #stm32整理(一)flash读写
  • $con= MySQL有关填空题_2015年计算机二级考试《MySQL》提高练习题(10)
  • $NOIp2018$劝退记
  • (1)(1.13) SiK无线电高级配置(六)
  • (13):Silverlight 2 数据与通信之WebRequest
  • (2021|NIPS,扩散,无条件分数估计,条件分数估计)无分类器引导扩散
  • (C语言)字符分类函数
  • (solr系列:一)使用tomcat部署solr服务
  • (附源码)spring boot公选课在线选课系统 毕业设计 142011
  • (使用vite搭建vue3项目(vite + vue3 + vue router + pinia + element plus))
  • (一)ClickHouse 中的 `MaterializedMySQL` 数据库引擎的使用方法、设置、特性和限制。
  • (原)本想说脏话,奈何已放下
  • (原创) cocos2dx使用Curl连接网络(客户端)
  • (转)重识new
  • .cn根服务器被攻击之后
  • .form文件_SSM框架文件上传篇
  • .NET/C# 推荐一个我设计的缓存类型(适合缓存反射等耗性能的操作,附用法)
  • .NET开发不可不知、不可不用的辅助类(三)(报表导出---终结版)
  • .NET开源的一个小而快并且功能强大的 Windows 动态桌面软件 - DreamScene2
  • /proc/interrupts 和 /proc/stat 查看中断的情况
  • @EnableConfigurationProperties注解使用
  • [20171101]rman to destination.txt