To me, merging .Net binaries so that I'm left with one single file executable was always a both interesting and important feature. From what I've read, there are three approaches regarding this:
ILMerge
- Really merges the assemblies. Comparable to creating one single .csproj and pulling in all source code.
Fody.Costura
- Keeps the assemblies seperate but just includes their files as ressources and bends the resolution accordingly.
.Net Core 3+ PublishSingleFile / Warp
- From what I understand the approaches are similar. Extract the files to a temp dir on first execution and just run them normally.
While the topics are quite related and sometimes do overlap I don't intend to mix up single file, self-contained deployments and trimming - I'm mainly thinking of the single file part here.
I think any of the three approaches has it's own advantages and disadvantages and no one is "the" way to go.
ILMerge
- + Seems like the most natural option for "merging"
- + Requires no runtime loading, if everything is there and works than that's it; once the image is in memory we're done
- - Loading everything at once everytime might lead to lower startup performance, relevant to desktop apps
- - Changes quite much about the internals, assembly names change, probably library internals get more involved with app code etc.; this might lead to hard to debug errors, in the worst case popping up in production, I guess. It just changes things developers may have taken for granted (i.e. name of their assembly).
- - Probably not conceptual but doesn't support .Net Core => dealbreaker
Fody.Costura
- + Enables compression for dependencies
- + Changes much less internals, probably leading to less unforeseen situations
- + Probably requires the OS to load the full image but not the CLR to load the libs at once so might improve start-up performance
- - Requires de-gzipping the dependencies every time; might be a performance penalty CPU-wise, could be a performance benefit IO-wise
Self extracting
- + Compressed too
- + Once the code is extracted and running, the only difference is where it's located. Thus, lib developers can even still find their assembly on disk etc., making the least runtime differences I guess.
- - First time startup can take some noticable time and is quite IO intensive, kind of an installation.
- + Subsequent runs behave like non-single-file deployments, having their respective (dis)advantages compared to the other two.
- - Comes with some non trivial problems. When to update the extracted files? Who deletes old versions of the extracted files? What if the disk is full/...?
- - Executing is not side-effect free anymore.
- - More then 2x the disk space needed.
In my personal obviously non-exhaustive and non-scientific experience, (3) sometimes had some rough edges and showed strange behaviour while (2) worked really good in the years I've been using it (comaring Framework vs Core though). But probably thats either due to the implementation of (3) beeing quite new or my misunderstanding.
The intention of me posting this question is to (a) understand this better in general and (b) understand why Microsoft chose (3). So, it'd be great
- if I missed differences between the three to point them out (feel free to edit the question to keep things in one place).
- if I made false assumptions to point them out.
- if there are more options in general or if I'm wrong about one of the concepts to point them out
- if there's content about the reasoning, why Microsoft selected (3) as the superior way (that would probably deepen my understanding for the pros and cons) to point me in the right direction.
While I understand it's hard to compete with a framework feature, I'm a bit sad that Costura is in maintenance mode, given I think it has advantages over the frameworks approach, making the second option a viable choice. Thus, one of the reasons I'm asking this question is to understand if there are any severe advantages of (3) over (2) I'm overlooking.