内容简介:如果你希望做一个 NuGet 工具包,那么这个包一定不能作为依赖传递给下一个包。典型的例子,做一个生成版本号的工具 NuGet 包,或者做一个代码分析器。本文将解决 NuGet 的几个坑,真正做到绝对没有的依赖传递。如果你使用了 GitVersion 这款 NuGet 包来自动修改你的版本号,那么你可能会遇到这个问题。
如果你希望做一个 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 包具备这样的特点:
- 小伙伴给 B 安装 A 包的时候,不用额外为 A 包写配置依赖的代码;
- 小伙伴为 C 安装 B 的时候,不会出现 A 乱入的情况。
如果你依然对这样的问题存有疑惑,可以阅读以下文章,这是切实的例子。
官方提供的解决方案
官方在非常早期的 2.7 版本就提供了 developmentDependency
属性,可以在 nuspec 文件中写。但实际上这个属性在后面版本的 NuGet 开发中就丢掉了。不生效。
官方提供了 IsTool
属性可以使用,但这依然不能阻止 B 安装了 A 包之后,C 包被迫安装 A 包的问题。
我试图寻找的解决方案
我们创建一个项目 Walterlv.PackageDemo.A 模拟前面提到的包 A,创建一个项目 Walterlv.PackageDemo.B 模拟前面提到的包 B,创建一个项目 Walterlv.ProjectDemo.C 模拟前面的项目 C。注意,实际场景中,这三个项目通常在不同的仓库中,由不同的开发者开发。
不过,为了方便起见,我打算直接在一个解决方案中模拟这样的效果:
我在 A 中试图创建一个 build\Walterlv.PackageDemo.A.props 文件,并在里面写一些阻止 A 被依赖的代码。
<Project> <ItemGroup> <PackageReference Update="Walterlv.PackageDemo.A" PrivateAssets="All" /> </ItemGroup> </Project>
当这段代码被 Visual Studio 编译的时候,一切符合预期。然而使用命令行编译的时候,就不按照预期工作了。
我提供的强力解决方案
为 A 项目添加强力去除依赖
我们需要为 A 项目添加一个 build\Walterlv.PackageDemo.A.targets 文件。这份文件会在其他项目安装 A 并且编译的时候执行。
里面的内容为:
<Project> <Target Name="ForceWalterlvDemoPrivateAssets" BeforeTargets="CollectPackageReferences"> <ItemGroup> <PackageReference Update="Walterlv.PackageDemo.A" PrivateAssets="All" /> </ItemGroup> </Target> </Project>
就是在 CollectPackageReferences
之前,即 MSBuild 开始编译之前收集 NuGet 引用之前执行。将 Walterlv.PackageDemo.A 的所有内容设为私有依赖。
最关键的就是 PrivateAssets="All"
这一句,将所有内容设为私有依赖。阅读 如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包 可以了解更多关于 NuGet 打包相关的知识。
▲ 项目的结构
为了通用一点,我取名为 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>
现在编译 B 项目,我们能得到 B 的 NuGet 包。打开 B 项目的 NuGet 包后,我们发现其中没有对 A 的依赖。这种情况下其他项目安装 B 是不会出现 A 项目的额外引用的。
使用 Visual Studio 编译和命令行编译效果是一样的。至此,我们的问题就是真的解决了。
本文会经常更新,请阅读原文: https://walterlv.github.io/post/prevent-nuget-package-been-depended.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接: https://walterlv.github.io ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- C++ 值传递、指针传递、引用传递详解
- 简明笔记:指针传递和值传递
- golang中的函数参数值传递和引用传递
- 现代编程语言的值传递与引用传递
- 这一次,彻底解决Java的值传递和引用传递
- Python函数中参数是值传递,还是引用传递?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。