.NET Power Tip 10: Merging Assemblies
Say, you have a .NET Project with a lot of assemblies in the project output and you don’t like it. For your personal reasons, you would like to just deliver one assembly that contains your entire project.
There are several ways to do so. This post presents three methods: ILMerge.exe, Embedded Resource and AppDomain.ResolveAssembly and Fody.Costura.
If you are interested in the approache I like most, skip down to Fody.Costura!
IL-Merge is a command line application that can be used to combine several assemblies into one.
You can download it here: https://www.microsoft.com/en-us/download/details.aspx?id=17630
and find out how it works here: http://www.codeproject.com/Articles/9364/Merging-NET-assemblies-using-ILMerge
The official page with the documentation can be found here: http://research.microsoft.com/en-us/people/mbarnett/ILMerge.aspx
There is even a GUI that takes the command line fiddling away: https://ilmergegui.codeplex.com/
However, there are some problems with ILMerge, that are discussed on StackOverflow: http://stackoverflow.com/questions/9376/ilmerge-best-practices
The main issues are:
- ILMerge strips XML comments from code files
- It doesn’t correctly handle PDB files
- It fails with XAML files in the solution
- It doesn’t guarantee the correctness of the merge. You need to run PEVerify after each merge operation.
Lets look at the next approach that does not require any third party tools.
2. Embedded Resource and AppDomain.ResolveAssembly, aka the Jeffrey Richter approach
In his book “CLR via C#”, Jeffrey Richter describes a sexy approach:
Basically, you add the assemblies to the project and set their Build Action to “Embedded Resource”. Then you register a callback method with the ResolveAssembly event of the AppDomain class, like so:
An improved version of this approach with less manual work is implemented in the Nuget package Fody.Costura that is presented in the next section.
Fody is a .NET Assembly Weaver. Think of a tool that assists you in metaprogramming. And by metaprogramming I mean modifying .NET assemblies on the intermediate language (MSIL) level, after the build process. Fody is designed as an extendable platform with a long list of plugins. Check out the full list on https://github.com/Fody/Fody/
Costura is one of these plugins that can be used to merge assemblies as embedded resources.
It makes two changes:
1. Includes all assemblies and pdb files with “Copy Local” as resources in the target assembly.
2. Injects code into the module initializer to load the included assemblies.
Lets use Fody.Costura in an example project:
We have a demo project that uses log4net, Google Maps and some other 3rd party assemblies. The project output looks like this:
Usage is trivial:
1. Add the nuget package Costura.Fody to your project
That is it. From now on your referenced assemblies are included in the main project assembly. Taking a look at IlSpy (www.ilspy.com) reveals the included resources and the AssemblyLoader class that has been added by Costura.
However, you might be disappointed at first. The project output has not changed at all. Its still the same:
Fody.Costura does include the referenced assemblies into MergeAssemblies1.exe but ist DOES NOT clean up the project output directory. Therefore, all the referenced assemblies will still be there. You can copy MergeAssemblies1.exe to another location and execute it from there.
With a couple of lines, you can add a cleanup target to your *.csproj file. Add this code:
<Delete Files="@(ReferenceCopyLocalPaths->'$(OutDir)%(DestinationSubDirectory)%(Filename)%(Extension)')" />
And voilà, a beautifully clean output directory:
Fody.Costura does only include assemblies referenced by your top project. If project MergeAssemblies1.exe would reference another project, let’s call it “ProjectB” which references a third party assembly such as Entity Framework, you would need to add Entity Framework to your top level project (MergeAssemblies1) for Costura to include it.