When we use a lot of external dependencies, the generated folder may contain a lot of Dlls that are not conducive to distribution.
Here are two ways to pack multiple assemblies into one.
Costura.Fody
Costura is an add-in for Fody. Fody is an extensible tool for weaving .net assemblies. It enables the manipulating the IL of an assembly as part of a build.
Costura is easy to use. Install Fody and Costura.Fody NuGet package to the target project, and add a file called FodyWeavers.xml with the following content to project root folder.
<Weavers>
<Costura/>
</Weavers>
Regenerate the project, you will find that all the dependencies are gone and only the main program remains.
All config options are accessed by modifying the Costura node in FodyWeavers.xml. For detailed instructions, please see the instructions in the github repo.
Config | Defaults |
---|---|
CreateTemporaryAssemblies | false |
IncludeDebugSymbols | true |
IncludeRuntimeReferences | true |
DisableCompression | false |
DisableCleanup | false |
LoadAtModuleInit | true |
IgnoreSatelliteAssemblies | false |
ExcludeAssemblies / ExcludeRuntimeAssemblies | null |
IncludeAssemblies / IncludeRuntimeAssemblies | null |
Unmanaged32Assemblies & Unmanaged64Assemblies | null |
ILMerge
ILMerge is a static linker for .NET Assemblies by Microsoft. It's a stand-alone application so we can use it in the command line. It is very easy to use but the more recommended usage is as a Nuget package.
Install ILMerge Nuget package to the target project. Edit the project .csproj
file (inside the respective <Project> .. </Project>
tags). Here is the basic usage example.
<Target Name="ILMerge" AfterTargets="Build">
<!-- the ILMergePath property points to the location of ILMerge.exe console application -->
<Exec Command="$(ILMergeConsolePath) /out:$(OutputPath)$(AssemblyName).dll $(OutputPath)ClassLibrary1.dll $(OutputPath)ClassLibrary2.dll" />
</Target>
<!-- delete the dependency files after merge -->
<Target Name="_ProjectRemoveDependencyFiles" AfterTargets="ILMerge">
<ItemGroup>
<_ProjectDependencyFile Include="$(OutputPath)ClassLibrary1.dll" />
<_ProjectDependencyFile Include="$(OutputPath)ClassLibrary2.dll" />
</ItemGroup>
<Delete Files="@(_ProjectDependencyFile)" />
</Target>
Regenerate the project and get the merged assembly.
The configuration and default values are also given here. For a detailed description, please see the repo wiki.
command line options | defaults |
---|---|
[/allowDup[:typeName]]* |
no duplicates of public types allowed |
/wildcards |
false |
/zeroPeKind |
false |
/attr:filename |
null |
/closed |
false |
/copyattrs |
false |
/ndebug |
true |
/internalize[:excludeFile] |
null |
/align:n |
512 |
/internalize[:excludeFile] |
false |
/keyfile:filename |
null |
/log |
false |
/log[:logfile] |
null |
/out:filename |
null |
/useFullPublicKeyForReferences |
true |
/lib:directory |
|
/targetplatform:version,platformdirectory |
|
/target:(library|exe|winexe) |
ILMerge.Kind.SameAsPrimaryAssembly |
/union |
false |
/ver:version |
null |
/xmldocs |
false |
The full command line for ILMerge is:
ilmerge [/lib:directory]* [/log[:filename]] [/keyfile:filename [/delaysign]] [/internalize[:filename]]
[/t[arget]:(library|exe|winexe)] [/closed] [/ndebug] [/ver:version] [/copyattrs [/allowMultiple]]
[/xmldocs] [/attr:filename] ([/targetplatform:<version>[,<platformdir>]]|v1|v1.1|v2|v4)
[/useFullPublicKeyForReferences] [/zeroPeKind] [/wildcards] [/allowDup[:typename]]*
[/allowDuplicateResources] [/union] [/align:n]
/out:filename <primary assembly> [<other assemblies>...]
Difference Between Two Ways
Decompile the generated assembly in dnspy, and we found:
- Costura.Fody
- Costura embedded resources as a method of merging assemblies.
- Use AssemblyLoader into the module initializer when assembly is loaded into memory.
- ILMerge
By recompiling and packaging IL code, all types of related dependencies are directly included in the target assembly.
If you care about performance and memory usage very much, ILMerge will be a better choice.