ILMerge to the Rescue?

As I continue to work on the ‘nu’ story, one of the things I am thinking about a lot is how we are going to make this happen. Last week I also had the pleasure to spend some time talking about version management with Udi Dahan and Chris Patterson while they were both in Kansas City. While we discussed a variety of topics one of the things we discussed was how to manage dll dependencies. After this discussion I have come to my current ‘thought.’

We should use ilmerge internal more. I have no doubt it has its issues but let me work this out. And then you can tell me what you think.

I am currently working on a new area of the Magnum project that is adding a nice abstraction on top of the file system. One of the features is handling zip files. For this I am using a third party library to handle all that nastiness. It was been a pleasure so far to work with. The problem is that in order to use this you now have to reference 3 assembilies:

  • Magnum.dll
  • Magnum.FileSystem.dll
  • Ionic.Zip.dll (DotNetZip)

So, some of this is due to the Magnum core philosophy that we are going to take dependencies with this library. So because I want to use Ionic.Zip I have to now have a new dll Magnum.FileSystem. That’s fine but adding 3 references sucks. Not only that but I now have to add a new gem dependency for magnum as well. :( As I thought about this it occurred to me: “Why does the user care about Ionic.Zip?” the user never interacts with this library and if I replaced it with the next hot thing you wouldn’t even know. :) Ok, so what if I just ‘ilmerge’ this bad boy in. What would happen? Well I think I would have fully encapsulated my code. And if I ilmerge internal it, you could actually use your own version of ‘Ionic.Zip’ and we wouldn’t conflict with each other! How cool is that? Once, I have ilmerged that in I can probably just either drop the Magnum.FileSystem project or I can just ilmerge it in to Magnum as well. Suh-weet.

So lets take a second and think about what this would do for us as a process. Lets look at NHibernate:

  • NHibernate.dll
  • Antlr3.Runtime.dll
  • Iesi.Collections.dll
  • log4net.dll
  • YourByteCodeProvider and its dlls

NHibernate.dll of course that guy is going to stay around, he’s the beef! What about Antlr3.Runtime.dll? If you are going to use Antlr3.Runtime in your own project do you want to be limited/constricted to whatever NHibernate is using? I would think not, so that puppy gets ilmerged in. Iesi.Collections? Gotta be honest, I should probably use more set based classes, but I don’t; and when I do I use HashedSet<T> which I am pretty sure NH supports at this point. But because this dll isn’t fully encapsulated, I am pretty sure we can’t just absorb this guy in. Question? Why don’t we just merge the Iesi code base into NHibernate, I have never used it outside of NH. Have you? Last we have log4net, the venerable work horse of logging. This is another dll that I see us having a hard time with, we could merge it in, but configuring it would get interesting and I really like the log4net story. So that gets us from 4 -> 3 dlls. Not to much of a win.

So lets take a look at MassTransit. A fully configured MassTransit setup (at least as of now) could put the following dlls into your mix:





{YourContainersStuff}.dll (from 1 to possibly 4 dlls)

That a whopping 5 dlls, and if you use the saga support that is another 7-8 dlls, bringing the total to what could be 13 dlls. Schnikeys!


So as you can see, its not a 100% win, but I THINK the idea is going in the right direction. Framework builders, lets try to encapsulate our dependencies and ilmerge internal them. It will give our users a better experience and should make our out of the box experience that much better.


Please let me know what you think. Is this trash? Or is it good?



About Dru Sellers

Sr. Software Engineer at Dovetail Software.
This entry was posted in Nu. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Ralf Westphal

    Here´s another option for those who want to deploy whole applications (EXE) as just one assembly: the tool NETZ ( stuffs assemblies into resources and loads them dynamically from there at runtime.

    No IL munging going on here. All assemblies are taken as is. This works even with assemblies which cannot be ilmerged.

    Give it a try. It´s open source.


  • Ryan Riley

    So here’s another potential route: multi-file assemblies [1]. Everyone these days seems to use full on assemblies no matter what they build. I understand why: it’s the default and only tooling-supported option in VS, multi-file modules are often difficult to understand, etc.

    However, as we move forward, I think compiling helper assemblies as .netmodules will actually make them more reusable for just this sort of problem. You don’t and generally can’t use these helper libraries on their own, and as you said they are generally used for better separation of concerns. Instead of IL merging, just assembly-link them. I am not sure, but I’m betting you would have fewer problems with versioning when compiling in different .netmodules.



  • drusellers

    @Steve: #1 – I agree, but the thing to keep in mind is that in this case the user is another developer. #2- I agree that you have to follow licensing but I am thinking about the case of an OSS project library. So I am less worried about it, but its a great point that I should pay attention to. #3 – I could be wrong but if I ilmerge internal the Iconic.Zip then you won’t get in conflicts and that is the point of the internal merge.

    @Ralf: Thanks for the thoughts, I look forward to discussing them more later this year. :)

    @Anthony: The problem you mention with NHibernate is the issue that I am hoping to solve. Well except for MassTransit which has its own jungle of dll’s at the moment. :)

    @Chuck: Yeah, I would rather see them in the project ref than not, its just making more explicit something that would be implicit anyways, and that’s a trait I enjoy.

    @Martin: Thanks for the pointers. I will have to check this out.

  • Martin Jul

    Nice post. I like the thinking, too – just for the simple fact that we sometimes have apps with components from different projects that depend on different versions of the same DLL.

    You might be interested in looking at how the leiningen (“lein”) build tool works for Clojure – it has the uberjar option to merge everything into a single file on build.

    Working a lot in Clojure, I really appreciate the central repository (Maven) and the ease of use provided by leiningen on the client side (the project dependencies) and online repositories like for easily publishing components for consumption by other projects.

    When I am working in the “sharp”-languages I really miss this.

    @mjul —

  • Chuck

    I agree with the earlier poster, the users don’t care (the next person to touch the code will). I tried to IlMerge all of the NHibernate assemblies the other day. It fell down on trying to load the Bytecode Provider, and I gave up.

    One thing that I have to remember is that the dependencies are often there solely to get the dll to deploy to the output directory – Outside of the Composition Root and DI Container Config, they are not even used. If I wanted some more pain, I could late bind it all and use a configuration file and just make sure that the files got put in the directory…then I could change them w/o recompile.

    But, that’s a rare situation, right?

  • Anthony Jones

    Thanks for posting – this issue has been bothering me for a while now. To be honest I hadn’t considered the ILMerge route!

    When I download NHibernate for use in my project, I just want to download an ORM. NHibernate’s dependencies are implementation details that I don’t really care about. If I want to use Castle or log4net in my project, I’ll download them myself. I really appreciate what NHibernate is and does, but can’t you give me something a little cleaner?

    If I reference two assemblies with different version of log4net merged into them, is that a problem? If so, it would be nice if we could avoid the versioning issue, maybe using AppDomains. Perhaps assemblies would then be a little easier to use and deploy.

    Nice post – some thought provoking stuff – thanks!

  • Ralf Westphal

    @Dru: I´m have been delivering software like that for a while. Due to component oriented development my applications consist of dozens and more assemblies – which I ilmerge down to 1 or just a few. This is not so much out of necessity, but for some people (customers, developers) who feel uneasy if they see more than a couple of assemblies deployed.

    This works very well – except for some cases where it doesn´t. Some 3rd party libs like SmartInspect (if I remember correctly) withstand ilmerge.

    And I do that even more so whenever I develop libraries. Usability is king not only for API usage but also for handling a library. I don´t care if a lib is 50 KB or 1 MB. But I do care if I need to decide whether to reference 1 or 2 or 5 assemblies.

    @Steve: You´re right, customers should care less. But sometimes they do. So why argue with them?

    Versioning: What you describe is a CI problem. During development each project references what it needs. One needs Magnum, another one just Iconic.ZIP.

    But then during CI when ilmerge is run you leave out Iconic.ZIP. The assembly referencing just it will be satisfied by Magnum.

    If different versions of Iconic.ZIP are needed, then even separate assemblies for Magnum won´t help, I guess. Because you can´t just use the newer version; it might not fit the needs of Magnum.


  • Steve Py

    1) Fluff: Whether you distribute a couple big assemblies, or a larger number of smaller ones, the user generally couldn’t care less. As long as an icon appears in their start menu, quicklaunch bar, or desktop to run the app, how many files sit in the directory is pedantics.
    2) Licensing: More of a question point. I’m curious how something like this would fit into licensing and conditions such as distrubuting as provided, etc. I’d think that having a log4net.dll or castle.x.dll etc. in a project folder would emphasize these projects are a part of an offering. Bundling them up into your own assemblies slightly conceals that.
    3) Versioning: Question – If you bundle a version of Iconic.ZIP into Magnum, and I use your Magnum as a reference in one of my projects, but I also use a reference to a different version of Iconic.ZIP, how is this going to look during deploy? It’ll probably mean 2 copies of Iconic packaged unless I get the source for Magnum and update the reference?