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. js 图片查看器

    将以前用angular 写的 自定义指令 封装成 插件,无需引用jquery.angular. 下载下来即可查看效果. github网址: https://github.com/wzhGitH/img ...

  2. 【三小时学会Kubernetes!(四) 】Deployment实践

    Deployment 部署 Kubernetes 部署可以帮助每一个应用程序的生命都保持相同的一点:那就是变化.此外,只有挂掉的应用程序才会一尘不变,否则,新的需求会源源不断地涌现,更多代码会被开发出 ...

  3. ctci1.2

    ;     ; i < len/; i++){         tmp = *(str+i);         *(str+i) = *(str+len--i);         *(str+l ...

  4. selenium的三种等待时间设置

    为了提高脚本的稳定性,我们需要在脚本中增加等待时间 第一种:强制等待 Thread.sleep():固定休眠时间设置,Java的Thread类里提供了休眠方法sleep,导入包后就能使用 sleep( ...

  5. flask学习(四):debug模式

    一. 设置debug模式 1. flask 1.0之前 在app.run()中传入一个关键字参数debug,app.run(debug=True),就设置当前项目为debug模式 2. flask 1 ...

  6. 1-16-2 LVM管理和ssm存储管理器使用&磁盘配额

    ssm存储管理器使用&磁盘配额 ssm存储管理器使用 系统存储管理器的使用 系统存储管理器(又称ssm,即system-storage-manager),是RHEL7/CentOS7新增的功能 ...

  7. 十八 Python分布式爬虫打造搜索引擎Scrapy精讲—Scrapy启动文件的配置—xpath表达式

    我们自定义一个main.py来作为启动文件 main.py #!/usr/bin/env python # -*- coding:utf8 -*- from scrapy.cmdline import ...

  8. java 不可不知的数据库知识-----事物

    每一个java开发对数据库都不会陌生,提到数据库,那么就一定要了解的一个知识点------事物,下面就对事物简单记录一下相关知识点. 最初接触事物的时候其实是从JDBC事物开始的,JDBC比较基础,这 ...

  9. cat 命令|more命令|less命令

    cat主要有三大功能:1.一次显示整个文件:cat [-n] filename2.从键盘创建一个文件:cat > filename 3.将几个文件合并为一个文件:cat file1 file2 ...

  10. 大白话,讲编程之《ES6系列连载》汇总

    如果你经历过2,3年前的前端开发,你一定感受过兼容IE6,7的痛苦,一定用过网页三剑客的dreamweaver编写html,面试的时候面试官一定会问你:会用PS切图吗? 刚开始的时候你发现,web前端 ...