NuGet 提供了工具类型的包支持,生成一个基于 .NET Core 的 dll 或者基于 .NET Framework 的 exe 之后,你几乎可以对项目做任何事情。但是,默认情况下,NuGet 不会将这些工具的依赖一起打包进入 NuGet 包 nupkg 文件内,这就使得功能比较复杂的跨平台 NuGet 工具包几乎是无法正常工作的。

本文将介绍将这些依赖加入 NuGet 包中的方法,使得复杂的工具能够正常使用。


 

问题

你可能是在 创建一个基于命令行工具的跨平台 NuGet 工具包 的时候遇到依赖问题的,也可能是自己做到另外什么工具遇到的。

典型的例子,我正在做一个基于 Roslyn 的 NuGet 工具包。于是整个 Roslyn 的大量 dll 都是我的依赖。但默认情况下,打出来的包并不包含 Roslyn 相关的 dll。

探索

官方关于 NuGet 的文档 并没有提及任何关于额外添加依赖文件的方法,擅长 NuGet 的大神 Nate McMaster 虽然有一篇关于加入 NuGet 依赖的博客 MSBuild tasks with dependencies,但依然没有很简单地解决。

尝试找一个实际将这些依赖 Include 进来,但是不知道什么时机合适。太早了依赖文件还没有生成,太晚了 NuGet 包中即将打的文件早已确认,Include 了也没用。

于是,我去阅读了 Microsoft.NET.Sdk 的源码,找到了并没有公开的内部方法来解决这个问题。关于阅读 Microsoft.NET.Sdk 源码的方式,可以参考 解读 Microsoft.NET.Sdk 的源码,你能定制各种奇怪而富有创意的编译过程Reading the Source Code of Microsoft.NET.Sdk, Writing the Creative Extension of Compiling

NuGet 打包的核心在 NuGet.Build.Tasks.Pack.targets 文件,主要是这段代码(省略了大量内容,留下了看起来有点儿关系的部分):

<!-- 已删减大量内容,全部内容请自己阅读源码。 -->
<PackTask PackItem="$(PackProjectInputFile)"
PackageFiles="@(_PackageFiles)"
PackageFilesToExclude="@(_PackageFilesToExclude)"
PackageTypes="$(PackageType)"
IsTool="$(IsTool)"
IncludeBuildOutput="$(IncludeBuildOutput)"/>
</Target>

PackageTypesIsTool 是我放来灌水的,这两个属性决定了我们打出来的包的类型(是否是工具类型)。看起来像的是 PackageFilesPackageFilesToExclude 属性,不过这两个属性用到了私有的属性 @(_PackageFiles)@(_PackageFilesToExclude)

所以接下来需要搜索到底是那里在为 @(_PackageFiles)@(_PackageFilesToExclude) 赋值。搜索 <_PackageFiles 可以找到赋值的地方就在 NuGet.Build.Tasks.Pack.targets 文件中:

<!-- 已删减大量内容,全部内容请自己阅读源码。 -->
<Target Name="_GetPackageFiles" Condition="$(IncludeContentInPack) == 'true'">
<ItemGroup>
<_PackageFilesToExclude Include="@(Content)" Condition="'%(Content.Pack)' == 'false'"/>
</ItemGroup>
<!-- Include PackageFiles and Content of the project being packed -->
<ItemGroup>
<_PackageFiles Include="@(Content)" Condition=" %(Content.Pack) != 'false' ">
<BuildAction Condition = "'%(Content.BuildAction)' == ''">Content</BuildAction>
</_PackageFiles>
<_PackageFiles Include="@(Compile)" Condition=" %(Compile.Pack) == 'true' ">
<BuildAction Condition = "'%(Compile.BuildAction)' == ''">Compile</BuildAction>
</_PackageFiles>
<_PackageFiles Include="@(None)" Condition=" %(None.Pack) == 'true' ">
<BuildAction Condition = "'%(None.BuildAction)' == ''">None</BuildAction>
</_PackageFiles>
<!-- 已删减大量内容,全部内容请自己阅读源码。 -->
</ItemGroup>
</Target>

这是一个私有 Target,所以答案已经呼之欲出了。

答案

我们写一个 Target,将 _GetPackageFiles 设为我们的前置 Target。然后,我们就可以把输出目录中除了 NuGet 自然而然会帮我们打入 NuGet 包中的所有文件都加入到 NuGet 包中的对应目录下。

具体来说,是将下面的 Target 添加到项目文件的末尾。

<Target Name="IncludeAllDependencies" BeforeTargets="_GetPackageFiles">
<ItemGroup>
<None Include="$(OutputPath)*.*" Exclude="$(OutputPath)$(AssemblyName).exe;$(OutputPath)$(AssemblyName).pdb" Pack="True" PackagePath="tools\net47" />
</ItemGroup>
</Target>

在制作跨平台的 NuGet 工具包时,如何将工具(exe/dll)的所有依赖一并放入包中的更多相关文章

  1. 如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包

    MSBuild 的 Task 为我们扩展项目的编译过程提供了强大的扩展性,它使得我们可以用 C# 语言编写扩展:利用这种扩展性,我们可以为我们的项目定制一部分的编译细节.NuGet 为我们提供了一种自 ...

  2. 如何创建一个基于命令行工具的跨平台的 NuGet 工具包

    命令行可是跨进程通信的一种非常方便的手段呢,只需启动一个进程传入一些参数即可完成一些很复杂的任务.NuGet 为我们提供了一种自动导入 .props 和 .targets 的方法,同时还是一个 .NE ...

  3. 在查询时将查询条件放入Session中,导出时直接根据qpniRGaFiler取查询条件即可

    在查询时将查询条件放入Session中,导出时直接根据qpniRGaFiler取查询条件即可

  4. MAVEN打包时没有将src/main/cache文件夹打到到WAR包中

    某项目中ehcache配置文件写在src/main/cache中,结果用maven打包时,得到的WAR包里面没有这个文件夹 因为maven打包时默认只打包src/main/java中的文件和src/m ...

  5. Kendo UI中TreeView 放入tabstrip中,大数据量时超过边框的解决方案。

    参考http://www.kendoui.com/forums/ui/tabstrip/tabstip-with-treeview-treeview-breaking-out-of-tabstrip. ...

  6. 【转】【Android测试技巧】01. root后adb shell默认不是root用户时,如何将文件放入手机系统中

    http://blog.csdn.net/wirelessqa/article/details/8624208 有些机器root后通过adb shell 后,默认不是root用户,需要输入 su才能切 ...

  7. installshield制作的安装包卸载时提示重启动的原因以及解决办法

    原文:installshield制作的安装包卸载时提示重启动的原因以及解决办法 有时候卸载installshield制作的安装包程序,卸载完会提示是否重启电脑以完成所有卸载,产生这个提示的常见原因有如 ...

  8. Nuget~打包时添加powershell初始化脚本

    对于进行nuget打包时,有时我们需要添加一个配置文件,就是一些文本文件,而这些文件我们在网站发布时需要同时复制到输出目录,否则会出现文件丢失的问题,我们在打包时通过添加powershell脚本来解决 ...

  9. 在 Visual Studio 生成项目时,会发现一些 dll 并没有被复制到输出目录,导致最终程序的执行错误

    发现与解决 检查了一下项目文件,发现是因为这些 dll 文件的引用其中一个叫做 嵌入互操作类型(EmbedInteropTypes)的属性被设为了 True,此时 复制本地 属性会被强制设为 Fals ...

随机推荐

  1. log4j和logback

    Log4j和logback Log4j简介 Log4j(log for java) 1.是Apache的一个开源项目: 2.是使用Java语言编写的一个日志框架: 3.用于记录程序中的日志信息: 4. ...

  2. angular指令与指令交互

    app.directive('mansory',function(){ return { controller:function($scope){ this.changed = function(){ ...

  3. thinkphp3.2笔记(1)目录,控制器及url模式,地址解析

    一.目录 Application  :  tp默认的应用代码存储的目录 Public :     Tp 默认的存储静态资源的目录,img,css,js ThinkPHP  :   Tp  框架的源代码 ...

  4. VC中release和debug版本的区别

    DEBUG版本 包含调试信息,编译器生成的代码方便调试 RELEASE版本 算是发布版本,RELEASE下不能调试,生成的文件更小,编译器生成的程序速度更快但是Debug下生成的exe文件在单独运行时 ...

  5. Linux命令详解-mkdir

    linux mkdir 命令用来创建指定的名称的目录,要求创建目录的用户在当前目录中具有写权限,并且指定的目录名不能是当前目录中已有的目录. 1.命令格式: mkdir [选项] 目录... 2.命令 ...

  6. fastdfs 集群配置

    fastdfs 简介FastDFS是一个国产开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储.文件同步.文件访问(文件上传.文件下载)等,解决了大容量存储和负载均衡的问题.特别适合以文 ...

  7. 文件上传及时显示, 前端js和后端php相互结合使用

    文件读取 javascript 绑定文件上传变化事件 onchange 利用window对象 FileReader 调用方法 readerAsDataURL onload 方法 异步读取 属性:fil ...

  8. taskset -pc PID 查看线程占用cpu核

    taskset -pc  PID 可以用于 查看 当前线程 对应绑定的 在 哪个核上面. 这个 可以用于 程序优化, 查看 哪个线程占用的 cpu 比重比较高 首先 可以通过  top  -H   - ...

  9. rsync的服务端和客户端搭建

    首先要看看有没有rsync,没有就按装一个rsync 1配置文件 然后创建rsyncd.conf文件,并添加如下内容(文件默认不存在) [root@chensiqi2 backup]# cat /et ...

  10. 前台cookie的操作

    <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> &l ...