.NET 使用 ILMerge 合并多个程序集,避免引入额外的依赖
原文:.NET 使用 ILMerge 合并多个程序集,避免引入额外的依赖

我们有多种工具可以将程序集合并成为一个。打包成一个程序集可以避免分发程序的时候带上一堆依赖而出问题。
ILMerge 可以用来将多个程序集合并成一个程序集。本文介绍使用 ILMerge 工具和其 NuGet 工具包来合并程序集和其依赖。
以 NuGet 包的形式使用 ILMerge
ILMerge 提供了可供你项目使用的 NuGet 包。如果你在团队项目当中安装了 ILMerge 的 NuGet 包,那么无论团队其他人是否安装了 ILMerge 的工具,都可以使用 ILMerge 工具。这可以避免要求团队所有成员安装工具或者将工具内置到项目的源代码管理中。
要以 NuGet 包的形式来使用 ILMerge,需要首先安装 ILMerge 的 NuGet 包:
或者直接在你的项目的 csproj 文件中添加 PackageReference
:
<ItemGroup>
<PackageReference Include="ILMerge" Version="3.0.29" />
</ItemGroup>
- 1
- 2
- 3
我现在有一个项目 Walterlv.Demo.AssemblyLoading,这是一个控制台程序。这个程序引用了一个 NuGet 包 Ben.Demystifier。为此带来了三个额外的依赖。
- Walterlv.Demo.AssemblyLoading.exe
- Ben.Demystifier.dll
- System.Collections.Immutable.dll
- System.Reflection.Metadata.dll
- 1
- 2
- 3
- 4
而我们可以使用 ILMerge 将这些依赖和我们生成的主程序合并成一个程序集,这样分发程序的时候只需要一个程序集即可。
那么,我们现在需要编辑我们的项目文件:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net48</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Ben.Demystifier" Version="0.1.4" />
<PackageReference Include="ILMerge" Version="3.0.29" />
</ItemGroup>
++ <Target Name="ILMerge">
++ <Exec Command=""$(ILMergeConsolePath)" /ndebug /target:exe /out:$(OutputPath)$(AssemblyName).exe /log $(OutputPath)$(AssemblyName).exe /log $(OutputPath)Ben.Demystifier.dll /log $(OutputPath)System.Collections.Immutable.dll /log $(OutputPath)System.Reflection.Metadata.dll /targetplatform:v4" />
++ </Target>
</Project>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
我们只增加了三行,添加了一个名称为 ILMerge 的 Target。(注意到项目文件中我有额外引用一个其他的 NuGet 包 Ben.Demystifier,这是为了演示将依赖进行合并而添加的 NuGet 包,具体是什么都没有关系,我们只是在演示依赖的合并。)在这个 Target 里面,我们使用 Exec 的 Task 来执行 ILMerge 命令。具体这个命令代表的含义我们在下一节介绍 ILMerge 工具的时候会详细介绍。如果你希望在你的项目当中进行尝试,可以把所有 /log
参数之后的那些程序集名称改为你自己的名称。
那么在编译的时候使用命令 msbuild /t:ILMerge
就可以完成程序集的合并了。
注意,你普通编译的话是不会进行 IL 合并的。
如果你希望常规编译也可以进行 IL 合并,或者说希望在 Visual Studio 里面点击生成按钮的时候也能完成 IL 合并的话,那么你还需要增加一个跳板的编译目标 Target。
我将这个名为 _ProjectRemoveDependencyFiles
的 Target 增加到了下面。它的目的是在 AfterBuild
这个编译目标完成之后(AfterTargets)执行,然后执行前需要先执行(DependsOnTargets)ILMerge 这个 Target。在这个编译目标执行的时候还会将原本的三个依赖删除掉,这样在生成的目录下我们将只会看到我们最终期望的程序集 Walterlv.Demo.AssemblyLoading.exe 而没有其他依赖程序集。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net48</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Ben.Demystifier" Version="0.1.4" />
<PackageReference Include="ILMerge" Version="3.0.29" />
</ItemGroup>
<Target Name="ILMerge">
<Exec Command=""$(ILMergeConsolePath)" /ndebug /target:exe /out:$(OutputPath)$(AssemblyName).exe /log $(OutputPath)$(AssemblyName).exe /log $(OutputPath)Ben.Demystifier.dll /log $(OutputPath)System.Collections.Immutable.dll /log $(OutputPath)System.Reflection.Metadata.dll /targetplatform:v4" />
</Target>
++ <Target Name="_ProjectRemoveDependencyFiles" AfterTargets="AfterBuild" DependsOnTargets="ILMerge">
++ <ItemGroup>
++ <_ProjectDependencyFile Include="$(OutputPath)Ben.Demystifier.dll" />
++ <_ProjectDependencyFile Include="$(OutputPath)System.Collections.Immutable.dll" />
++ <_ProjectDependencyFile Include="$(OutputPath)System.Reflection.Metadata.dll" />
++ </ItemGroup>
++ <Delete Files="@(_ProjectDependencyFile)" />
++ </Target>
</Project>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
最终生成的输出目录下只有我们最终期望生成的程序集:
以命令行工具的形式使用 ILMerge
你可以在这里下载到 ILMerge:
实际上 ILMerge 已经开源,你可以在 GitHub 上找到它:
装完之后,如果将 ILMerge 的可执行目录加入到环境变量,那么你将可以在任意的目录下在命令行中直接使用 ILMerge 命令了。加入环境变量的方法我就不用说了,可以在网上搜索到非常多的资料。
ILMerge 装完的默认目录在 C:\Program Files (x86)\Microsoft\ILMerge
,所以如果你保持默认路径安装,那么几乎可以直接把这个路径加入到环境变量中。
那么 ILMerge 的命令行如何使用呢?它的参数列表是怎样的呢?
我们来写一个简单的例子:
ilmerge /ndebug /target:exe /out:Walterlv.Demo.AssemblyLoading.exe /log Walterlv.Demo.AssemblyLoading.exe /log Ben.Demystifier.dll /log System.Collections.Immutable.dll /log System.Reflection.Metadata.dll /targetplatform:v4
- 1
其中:
/ndebug
表示以非调试版本编译,如果去掉,将会生成 pdb 文件/target
合并之后的程序集类型,如果是控制台程序,则为 exe/out
输出文件的名称(或路径)(此路径可以和需要合并的程序集名称相同,这样在合并完之后会覆盖同名称的那个程序集)/log
所有需要合并的程序集名称(或路径)/targetplatform
目标平台,如果是 .NET Framework 4.0 - .NET Framework 4.8 之间,则都是 v4
在合并完成之后,我们反编译可以发现程序集中已经包含了依赖程序集中的全部类型了。
以封装的 NuGet 包来使用 ILRepack
安装 NuGet 包:
之后,你就能直接使用 ILRepack
这个编译任务了,而不是在 MSBuild 中使用 Exec 来间接执行 ILRepack 的任务。
关于此 NuGet 包的使用,GitHub 中有很棒的例子,可以查看:
需要注意
如果使用新的基于 Sdk 的项目文件,那么默认生成的 PDB 是 Portable PDB,但是 ILMerge 暂时不支持 Portable PDB,会在编译时提示错误:
An exception occurred during merging:
ILMerge.Merge: There were errors reported in dotnetCampus.EasiPlugin.Sample's metadata.
数组维度超过了支持的范围。
在 ILMerging.ILMerge.Merge()
在 ILMerging.ILMerge.Main(String[] args)
- 1
- 2
- 3
- 4
- 5
或者英文提示:
An exception occurred during merging:
ILMerge.Merge: There were errors reported in ReferencedProject's metadata.
Array dimensions exceeded supported range.
at ILMerging.ILMerge.Merge()
at ILMerging.ILMerge.Main(String[] args)
- 1
- 2
- 3
- 4
- 5
目前,GitHub 上有 issue 在追踪此问题:
参考资料
- [C#]使用ILMerge将源DLL合并到目标EXE(.NET4.6.2) - cnc - 博客园
- dotnet/ILMerge: ILMerge is a static linker for .NET Assemblies.
- NuGet Gallery - ilmerge
- jbevain/cecil: Cecil is a library to inspect, modify and create .NET programs and libraries.
- gluck/il-repack: Open-source alternative to ILMerge
- Support for portable PDBs · Issue #11 · dotnet/ILMerge
- Merging assemblies using ILRepack - Meziantou’s blog
- peters/ILRepack.MSBuild.Task: MSBuild task for ILRepack which is an open-source alternative to ILMerge.
我的博客会首发于 https://blog.walterlv.com/,而 CSDN 会从其中精选发布,但是一旦发布了就很少更新。
如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://walterlv.blog.csdn.net/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。
.NET 使用 ILMerge 合并多个程序集,避免引入额外的依赖的更多相关文章
- .NET 使用 ILRepack 合并多个程序集(替代 ILMerge),避免引入额外的依赖
原文:.NET 使用 ILRepack 合并多个程序集(替代 ILMerge),避免引入额外的依赖 我们有多种工具可以将程序集合并成为一个.比如 ILMerge.Mono.Merge.前者不可定制.运 ...
- 使用ILmerge合并Exe、Dll文件的帮助类
原文:使用ILmerge合并Exe.Dll文件的帮助类 using System; using System.Collections.Generic; using System.Text; using ...
- C#中用ILMerge合并DLL和exe文件成一个exe文件或者DLL
ILMerge是一个将多个.NET程序集合并到一个程序集中的实用程序.它既可以作为 开源使用,也可以作为NuGet包使用. 如果您在使用它时遇到任何问题,请与我们联系.(mbarnett _at_ ...
- ILMerge合并多个DLL
序言 如果你的项目要提供多个dll给别人用,那么不妨让你的dll合并为一个,让别人看起来简洁,引用起来不会过于繁琐. 本篇比较少,但也算是比较实用吧. 下载微软的辅助工具ILMerge Imerge下 ...
- ILMerge合并程序
在DOS窗口中,进入到ILMerge的安装目录 中 如图所示,之后写合并代码, 使用命令进行捆绑,以如图为例,将CSkin.dll和MyTool.exe捆绑成一个新的newtool.exe文件./ou ...
- ilmerge合并多个组件
原文 http://www.cnblogs.com/margiex/archive/2008/06/24/302329.html 年初的一篇文章中提到过一下: http://margiex.cnblo ...
- ILMerge合并多个DLL (转)
最近在研究CodeDom,用到ILMerge 序言 如果你的项目要提供多个dll给别人用,那么不妨让你的dll合并为一个,让别人看起来简洁,引用起来不会过于繁琐. 本篇比较少,但也算是比较实用吧. 下 ...
- C# winform页面可视化设计打开失败,提示未能加载程序集或他的一个依赖项,dll错误
这种情况发生在最初项目是x86属性,改成x64后,一些原来dll,页面没有及时更新,导致页面找不到dll, 最简单的解决方式,把项目属性改成AnyCpu,重新编译下,就可以打开可视化设计窗口了.
- .NET 的程序集加载上下文
原文:.NET 的程序集加载上下文 我们编写的 .NET 应用程序会使用到各种各样的依赖库.我们都知道 CLR 会在一些路径下帮助我们程序找到依赖,但如果我们需要手动控制程序集加载路径的话,需要了解程 ...
随机推荐
- 思科、华为、H3C常用命令对比大全
思科.华为.H3C这些网络设备的命令还是挺多的,不过我们一般用的路由交换机设备都是最简单的了,先学基本命令,再学其他功能命令.路由命令是最基本的命令了. 思科.华为.H3C常用命令对比大全 CI ...
- CSS Blur() 将高斯模糊应用于输出图片
一.Css Blur() blur() CSS 方法将高斯模糊应用于输出图片. 结果为 <filter-function>. blur(radius) radius模糊的半径,值为< ...
- error: dereferencing pointer to incomplete type
/******************************************************************************* * error: dereferenc ...
- MySQL索引原理(一)
MySQL索引原理 索引目的 索引的目的在于提高查询效率,可以类比字典,如果要查“mysql”这个单词,我们肯定需要定位到m字母,然后从下往下找到y字母,再找到剩下的sql.如果没有索引,那么你可能需 ...
- 对数损失函数logloss详解和python代码
python机器学习-乳腺癌细胞挖掘(博主亲自录制视频)https://study.163.com/course/introduction.htm?courseId=1005269003&ut ...
- 从 SVN 迁移至 Git 并保留所有 commit 记录
yum install -y git-svn 用户映射文件user.txt,等号左边为svn账号,右边为Git用户名和邮箱.注意:svn中有多少用户就要映射多少 test1=test1<1472 ...
- angular的路由例子
app.routing.module.ts里面,关键部分 const routes: Routes = [ { path: '', redirectTo : 'c3/c2/mmc', pathMatc ...
- c# Mono.Cecil IL方式 读MethodBody
using Kufen.Common.Definitions; using Mono.Cecil; using System; using System.Collections.Generic; us ...
- PMP 第6章错题总结
项目进度管理的步骤: 1.项目的商业价值指特定项目的成果能够为相关方带来的有形的或无形的效益. 其中有形的效益包括:股东权益.市场份额.货币资产 无形的效益包括:品牌认知度.商誉.战略一致性等2. ...
- 爬虫相关-scrapy框架介绍
性能相关-进程.线程.协程 在编写爬虫时,性能的消耗主要在IO请求中,当单进程单线程模式下请求URL时必然会引起等待,从而使得请求整体变慢. 串行执行 import requests def fetc ...