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

将 .NET Core 项目打一个最简单的 NuGet 源码包,安装此包就像直接把源码放进项目一样

NuGet 原本就提供了生成源码包的功能。不过,NuGet 原生的源码包仅用于调试时自带调试信息和调试源码。

本文将以最简单的方式制作一个源码引用包。安装 NuGet 包后,不会生成任何程序集引用,而是相当于将源码直接放入被安装的程序集中一样。


本文内容

    • 准备工作
    • 将源码加入 NuGet 包
    • 安装 NuGet 包时引入源码

准备工作

我们需要一个可以用来打 NuGet 包的 .NET Core 项目,只需要在 Visual Studio 中新建一个即可。在本例中,我的项目名字是 Walterlv.Demo。

将源码加入 NuGet 包

在 项目文件中的已知 NuGet 属性(使用这些属性,创建 NuGet 包就可以不需要 nuspec 文件啦) 中,我说到了项目文件中的各种 NuGet 属性。在本文中,我们将使用到其中的一部分。

这些属性将设置到项目文件 Walterlv.Demo.csproj 中。

<!-- 将源码引入包中。 -->
<IncludeSource>true</IncludeSource>
<!-- 如果指定为 true,那么生成的 dll 将拷贝到 NuGet 包的 tools 目录下。 -->
<IsTool>true</IsTool>

为了避免将打出来的 NuGet 包作为 dll 被安装的程序集引用,我们需要设置 <IsTool>true</IsTool> 属性。这样,生成的 dll 将只会放入 tools 文件夹中,而不会被引用。

这时,项目的 csproj 文件像这样:

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

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
    <IncludeSource>True</IncludeSource>
    <NoPackageAnalysis>true</NoPackageAnalysis>
    <IsTool>True</IsTool>
    <DevelopmentDependency>true</DevelopmentDependency>
  </PropertyGroup>
  
</Project>

设置后编译项目,我们将在输出目录得到 Walterlv.Demo.nupkg 和 Walterlv.Demo.1.0.0.symbols.nupkg 两个文件。

在这里插入图片描述

这种带后缀形式的包在只是 NuGet 的辅助包而已,不是主包。在 How to create NuGet symbol packages - Microsoft Docs 中说明这种 symbols 的包只是用于调试的。然而,我们需要的是将其作为直接引用的主包。这种情况下,Walterlv.Demo.nupkg 因为不能满足我们的目的,所以我们并不能使用它。所以,我们需要做的是,将 Walterlv.Demo.1.0.0.symbols.nupkg 变成主包。

于是,我们编写一个 <Target /> 将 symbols 包替换主包:

<Target Condition="$(IncludeSource) == 'True' Or $(IncludeSymbols) == 'True'" Name="UseSymbolsInsteadOfLib" AfterTargets="GenerateNuspec">
  <Delete Files="$(PackageOutputAbsolutePath)$(PackageId).$(PackageVersion).nupkg" />
  <Move SourceFiles="$(PackageOutputAbsolutePath)$(PackageId).$(PackageVersion).symbols.nupkg" DestinationFiles="$(PackageOutputAbsolutePath)$(PackageId).$(PackageVersion).nupkg" />
</Target>

这里使用到了 <Delete /><Move /> 两个自带的 Task,用于将功能不全的主包删除,然后将我们的源码包替换成为主包。我此前写过 如何编写基于 Microsoft.NET.Sdk 的跨平台的 MSBuild Target 介绍了一些自带的 Task。如果你想了解更多 <Target /> 编写相关的知识,也可以阅读这篇文章。

在增加了上面的一段 <Target /> 之后,最终我们将只会得到一个 NuGet 包,打开后能发现其中包含源码。

在这里插入图片描述

安装 NuGet 包时引入源码

为了让源码能随着包的安装加入到目标项目,我们需要 targets 文件来将源码引入。

在项目中新建 Assets 文件夹,这将用来放即将存入 NuGet 包中的文件。新建 Assets\build\Package.targets 文件,这个文件会被自动引入到被安装的项目中。

在这里插入图片描述

于是我们在 csproj 中额外添加一些代码将这个文件在打包时改名为正确的名称。

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

于是,整个 csproj 文件看起来是这样:

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

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
    <IncludeSource>True</IncludeSource>
    <NoPackageAnalysis>true</NoPackageAnalysis>
    <IsTool>True</IsTool>
    <DevelopmentDependency>true</DevelopmentDependency>
  </PropertyGroup>
  
  <ItemGroup>
    <None Include="Assets\build\*.targets" Pack="True" PackagePath="build\$(PackageId).targets" />
  </ItemGroup>

  <Target Condition="$(IncludeSource) == 'True'" Name="UseSymbolsInsteadOfLib" AfterTargets="GenerateNuspec">
    <Delete Files="$(PackageOutputAbsolutePath)$(PackageId).$(PackageVersion).nupkg" />
    <Move SourceFiles="$(PackageOutputAbsolutePath)$(PackageId).$(PackageVersion).symbols.nupkg" DestinationFiles="$(PackageOutputAbsolutePath)$(PackageId).$(PackageVersion).nupkg" />
  </Target>

</Project>

而至于我们刚刚新建的 Package.targets 文件中,我们也需要为目标项目填写一些内容:

<Project>

  <Target Name="WalterlvDemoIncludeSource" BeforeTargets="CoreCompile">
    <Message Text="$(MSBuildThisFileDirectory)..\src\Walterlv.Demo\**\*.cs" />
    <ItemGroup>
      <Compile Include="$(MSBuildThisFileDirectory)..\src\Walterlv.Demo\**\*.cs" />
    </ItemGroup>
  </Target>

</Project>

这样,一旦目标程序集安装了这个 NuGet 包,便会将所有的 cs 文件加入到目标项目的编译中。

在这里插入图片描述


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

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

知识共享许可协议

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

相关文章:

  • 使用 LINQ to XML,.NET 让生成 XML 文件变得和直接写 XML 一样轻松
  • git subtree 不断增加的推送时间,解不玩的冲突!这篇文章应该能救你
  • 阻止某个 NuGet 包意外升级
  • 解读 Microsoft.NET.Sdk 的源码,你能定制各种奇怪而富有创意的编译过程
  • 在 Visual Studio 的解决方案资源管理器中隐藏一些文件
  • 长期支持 LTS(Long-term Support)是怎样的一种支持方式
  • .NET Standard 的管理策略
  • 如何在 .NET/C# 代码中安全地结束掉一个控制台应用程序?通过发送 Ctrl+C 信号来结束
  • Windows 10 应用创建模糊背景窗口的三种方法
  • 使用 PInvoke.net Visual Studio Extension 辅助编写 Win32 函数签名
  • 程序员与英语:即时聊天中的英语缩写 lol / lmao / idk
  • 使用 IFTTT 做 RSS 的邮件订阅服务
  • .NET/C# 使窗口永不激活(No Activate 永不获得焦点)
  • 语法高亮不够漂亮?这里有你想要的 Rouge 主题
  • 理解 UWP 视图的概念,让 UWP 应用显示多个窗口(多视图)
  • Angular 响应式表单之下拉框
  • css布局,左右固定中间自适应实现
  • CSS选择器——伪元素选择器之处理父元素高度及外边距溢出
  • nodejs调试方法
  • SpiderData 2019年2月13日 DApp数据排行榜
  • vagrant 添加本地 box 安装 laravel homestead
  • 复习Javascript专题(四):js中的深浅拷贝
  • 基于Mobx的多页面小程序的全局共享状态管理实践
  • 协程
  • 怎么把视频里的音乐提取出来
  • ​LeetCode解法汇总2808. 使循环数组所有元素相等的最少秒数
  • (day6) 319. 灯泡开关
  • (react踩过的坑)antd 如何同时获取一个select 的value和 label值
  • (板子)A* astar算法,AcWing第k短路+八数码 带注释
  • (附源码)ssm基于微信小程序的疫苗管理系统 毕业设计 092354
  • (论文阅读31/100)Stacked hourglass networks for human pose estimation
  • (数位dp) 算法竞赛入门到进阶 书本题集
  • (转)winform之ListView
  • (转载)利用webkit抓取动态网页和链接
  • .bat批处理(三):变量声明、设置、拼接、截取
  • .gitignore文件_Git:.gitignore
  • .Net 高效开发之不可错过的实用工具
  • .net 开发怎么实现前后端分离_前后端分离:分离式开发和一体式发布
  • .net 重复调用webservice_Java RMI 远程调用详解,优劣势说明
  • .NET大文件上传知识整理
  • /bin/bash^M: bad interpreter: No such file or directory
  • @AutoConfigurationPackage的使用
  • @RequestMapping-占位符映射
  • @拔赤:Web前端开发十日谈
  • [2015][note]基于薄向列液晶层的可调谐THz fishnet超材料快速开关——
  • [Android]使用Retrofit进行网络请求
  • [AndroidStudio]_[初级]_[修改虚拟设备镜像文件的存放位置]
  • [AR Foundation] 人脸检测的流程
  • [Avalon] Avalon中的Conditional Formatting.
  • [bzoj1006]: [HNOI2008]神奇的国度(最大势算法)
  • [BZOJ3211]:花神游历各国(小清新线段树)
  • [BZOJ3223]文艺平衡树
  • [c#基础]值类型和引用类型的Equals,==的区别
  • [C++]类和对象【下】
  • [CakePHP] 在Controller中使用Helper