帮助官方 NuGet 解掉 Bug,制作绝对不会传递依赖的 NuGet 包

帮助官方 NuGet 解掉 Bug,制作绝对不会传递依赖的 NuGet 包

发布于 2018-08-05 13:22 更新于 2018-08-18 03:04

如果你希望做一个 NuGet 工具包,那么这个包一定不能作为依赖传递给下一个包。典型的例子,做一个生成版本号的工具 NuGet 包,或者做一个代码分析器。

本文将解决 NuGet 的几个坑,真正做到绝对没有的依赖传递。


我们遇到了什么问题

如果你使用了 GitVersion 这款 NuGet 包来自动修改你的版本号,那么你可能会遇到这个问题。GitTools/GitVersion: Easy Semantic Versioning (http://semver.org) for projects using Git

假想我们希望开发一个 NuGet 包 Walterlv.PackageDemo.A。另一位小伙伴想要使用我 A 包的功能做一个 Walterlv.PackageDemo.B 包。于是其他小伙伴可以安装 B 包去做自己的项目 C。

那么,除非我在 B 包安装完之后,明确在 B 的 csproj 文件中写以下代码,否则 B 包发布出去后,安装 B 包的项目 C 就会同时安装上 A 包。

<ItemGroup>
  <PackageReference Include="Walterlv.PackageDemo.A" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>

显然,由于 A 是个工具包,只是为了给安装了 A 的 B 包提供版本号或其他编译期功能的。C 不需要这样的功能!

然而我们希望做出来的 A 包具备这样的特点:

  1. 小伙伴给 B 安装 A 包的时候,不用额外为 A 包写配置依赖的代码;
  2. 小伙伴为 C 安装 B 的时候,不会出现 A 乱入的情况。

如果你依然对这样的问题存有疑惑,可以阅读以下文章,这是切实的例子。

官方提供的解决方案

官方在非常早期的 2.7 版本就提供了 developmentDependency 属性,可以在 nuspec 文件中写。但实际上这个属性在后面版本的 NuGet 开发中就丢掉了。不生效。

官方提供了 IsTool 属性可以使用,但这依然不能阻止 B 安装了 A 包之后,C 包被迫安装 A 包的问题。

我试图寻找的解决方案

为 A 项目添加去除依赖的代码

我们创建一个项目 Walterlv.PackageDemo.A 模拟前面提到的包 A,创建一个项目 Walterlv.PackageDemo.B 模拟前面提到的包 B,创建一个项目 Walterlv.ProjectDemo.C 模拟前面的项目 C。注意,实际场景中,这三个项目通常在不同的仓库中,由不同的开发者开发。

不过,为了方便起见,我打算直接在一个解决方案中模拟这样的效果:

我在 A 中试图创建一个 build\Walterlv.PackageDemo.A.props 或 build\Walterlv.PackageDemo.A.targets 文件,并在里面写一些阻止 A 被依赖的代码。

<Project>

  <ItemGroup>
    <PackageReference Update="Walterlv.PackageDemo.A" PrivateAssets="All" />
  </ItemGroup>

</Project>

为了通用一点,我取名为 Package.targets 文件,并在 A 项目编译的时候改名为 Walterlv.PackageDemo.A.targets。

▲ 项目的结构

以下是 A 项目的 csproj 文件,包含将 Package.targets 在打包 NuGet 包时改名的部分。

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

  <PropertyGroup>
    <TargetFramework>netcoreapp2.1</TargetFramework>
    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
  </PropertyGroup>

  <ItemGroup>
    <None Include="Assets\build\Package.targets" Pack="True" PackagePath="build\$(PackageId).targets" />
  </ItemGroup>

</Project>

在 B 项目中进行测试

本地调试当然用不着推送到 https://nuget.org。我们本地新建一个源,专门用于调试。

在 “工具 -> 选项 -> NuGet 包管理器” 中,我们可以设置 NuGet 源:

▲ 添加调试用的 NuGet 源

我们把刚刚 A 项目的输出目录填进去添加一个新的源。于是我们就能在 B 项目中安装 A 包了。

于是 B 项目的 csproj 文件全文内容如下:

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

  <PropertyGroup>
    <TargetFramework>netcoreapp2.1</TargetFramework>
    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Walterlv.PackageDemo.A" Version="1.0.0" />
  </ItemGroup>

</Project>

当以上 A 和 B 项目被 Visual Studio 编译的时候,一切符合预期;就像下图这样,B 项目中没有声明对 A 的依赖:

令人遗憾的结果

然而使用命令行编译的时候,就不按照预期工作了;如下图这样,B 项目中出现了对 A 的依赖。

命令行编译时使用这些命令效果都是一样的不管用。

nuget restore
msbuild
dotnet restore
dotnet build

不过,令人难以置信的时,如果此时 Visual Studio 打开了此项目,命令行编译却能符合预期。

另外,我还尝试将 Package.targets 中的所有内容放到 <Target /> 里面以获得延迟到编译期执行的效果,但结论依然与上面一致,即仅能在 Visual Studio 中正常工作。

<Project>

  <Target Name="ForceWalterlvDemoPrivateAssets" BeforeTargets="CollectPackageReferences">
    <ItemGroup>
      <PackageReference Update="Walterlv.PackageDemo.A" PrivateAssets="All" />
    </ItemGroup>
  </Target>

</Project>

一个真的能解决依赖问题的方案

临时:在以上使用过程中额外发现命令行中存在不符合预期的结果,所以,这一节暂时注释。

本文会经常更新,请阅读原文: https://walterlv.com/post/prevent-nuget-package-been-depended.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Crossin的编程教室

Windows 下的包管理器

这是一篇读者投稿。 包管理器的概念源自 Linux,与 Windows 单独下载安装软件不同,包管理器可以管理各种软件,做到统一的安装、更新和删除。某种程度上来...

36650
来自专栏逆向技术

win32之进程概念

  学习WindowsAPI. 之前.我们必须理解什么是进程. 在windows环境下.进程就是一个运行起来的exe程序

10620
来自专栏大内老A

“前.NET Core时代”如何实现跨平台代码重用 ——源文件重用

微软在2002年推出了第一个版本的 .NET Framework,这是一个主要面向Windows 桌面(Windows Forms)和服务器(ASP.NET W...

22560
来自专栏walterlv - 吕毅的博客

如何最快速地将旧的 NuGet 包 (2.x, packages.config) 升级成新的 NuGet 包 (4.x, PackageReference)

发布于 2018-05-13 09:07 更新于 2018-06...

6810
来自专栏FreeBuf

如何利用日志来监控和限制PowerShell攻击活动

写在前面的话 近期,我一直在我客户的网络环境中分析PowerShell攻击,根据我的分析以及研究结果,我发现了几种方法来帮助研究人员检测潜在的PowerShel...

27250
来自专栏图像识别与深度学习

Android学期项目指导

20550
来自专栏张善友的专栏

认识ASP.NET 5项目结构和项目文件xproj

ASP.NET 5 在项目结构上做了很大的改变,我们以前熟悉的目录结构与项目文件内容都不太一样了,本篇文章带大家了解 ASP.NET 5 到底跟以前有哪些不一样...

27780
来自专栏施炯的IoT开发专栏

Windows 10 IoT Serials 8 – 如何改变UWP应用的目标平台

    Windows Insider计划直接加速了Windows系统的迭代,缩短了系统发布的周期。就Windows 10 IoT Core而言,迭代的速度和W...

19570
来自专栏张高兴的博客

张高兴的 Xamarin.Android 学习笔记:(一)环境配置

39860
来自专栏FreeBuf

Office CVE-2017-8570远程代码执行漏洞复现

CVE-2017-8570漏洞是一个逻辑漏洞,利用方法简单,影响范围广。由于该漏洞和三年前的SandWorm(沙虫)漏洞非常类似,因此我们称之为“沙虫”二代漏洞...

48150

扫码关注云+社区

领取腾讯云代金券