
Support Single-File Apps in .NET 5 - eatonphil
https://github.com/dotnet/runtime/issues/36590
======
netacean
I just really wish they start investing into true static compilation for .NET.

Some serious projects [1] started adopting CoreRT [2], despite Microsoft's
neglect for their own runtime. CoreRT seems to really deliver on the single-
file fast-startup small-size .NET promise. Getting this project folded into
the LTS .NET 6 release should be a priority.

The source generators [3] that are coming to .NET 5 easily cover 99% of our
Reflection.Emit use cases, so the JIT is going to be more of a legacy burden
once .NET 5 comes out.

I want Go's small size, fast compile times, fast startup, without Go's bs
(explicit error checking, seriously?).

Pieces of our software get deployed over a satellite link so yeah, megabytes
matter (and that's why I don't even dare to propose using .NET for those
parts). Sharing code with the rest of our .NET stack is a PITA though so
people are getting itchy to rewrite the rest of our .NET stuff in Go for
better sharing (the Reflection.Emit parts would be replaced with "go generate"
which is... a source generator). It would be good to get some clarity on the
static compilation roadmap in .NET because I like my job, but I also don't
want to become a fulltime Go developer.

[1]
[https://github.com/dotnet/corert/issues/7200#issuecomment-62...](https://github.com/dotnet/corert/issues/7200#issuecomment-624618015)
[2] [https://github.com/dotnet/corert](https://github.com/dotnet/corert) [3]
[https://devblogs.microsoft.com/dotnet/introducing-c-
source-g...](https://devblogs.microsoft.com/dotnet/introducing-c-source-
generators/)

~~~
legulere
C# code still uses too much reflection (serialization, asp net routing, ORM,
view model binding) to be statically compileable. They seem to be doing
something against that with code generators

~~~
voxic11
Reflection isn't an issue. CoreRT allows you to include the symbols for
reflecting on though it does increase the filesize significantly.

~~~
randombytes6869
This is exactly what newer versions of Java allow you to do. The problem is,
nobody does it. I really like C# as a language, its better than Java in nearly
every way. But this is a rare case where C# is implementing a feature Java has
had for a very long time. And just like in Java, I don't think it will have
the impact advertised.

------
kyriakos
Even small utilities in dot net core are ridiculously big and have tens of dll
files. Maybe a proper dependency management is needed so that only what parts
of the framework that are actually used are included in the executable.

~~~
monadic2
Why not statically compile and tree-shake? If this already exists why is it
not effective?

~~~
localhost
C# Source Generators [1] will help improve things considerably re: reflection.

[1] [https://devblogs.microsoft.com/dotnet/introducing-c-
source-g...](https://devblogs.microsoft.com/dotnet/introducing-c-source-
generators/)

~~~
randombytes6869
Unfortunately I have doubts. This is one of the few features copied from Java
instead of the other way around. You can pre-process code in Java and generate
classes at build time instead of reflecting, but I know of just a handful of
libraries that leverage this.

------
rb808
BTW One thing I've always liked about dotnet is the binary is an executable
and you run that not the interpreter/vm process. When you look at list of
prcesses running you see your process, not java or python.

On windows the dotnet runtime is so ubiquitous I'm a bit surprised this is
needed.

~~~
rndgermandude
It's not just the runtime, but dependent assemblies too.

E.g. I have one app that has 4 aseemblies (the program + 3 internal
libraries), 5 more "internal" general assemblies/libraries of mine, and 12
dependencies from nuget, for a grand total of 21 (+ 1 wrapper .exe) files. Now
double that number if you want the pdbs (debug/symbol files) too. Without the
runtime.

Shipping 21 files isn't horrible (I have seen node_modules directories in the
hundreds of thousands of files), but shipping one single self-contained .exe
would be even nicer.

You can kinda use IlMerge (and some hacking) to achieve a similar result, but
it can break in subtle ways (especially when you try to debug something) and
does not work with netcore as far as I know. And it's not officially supported
either.

~~~
rb808
OK I get it, makes sense.

------
2bitencryption
I'm confused about something -- does this "single-file" contain the full .NET
runtime, a-la a Go executable? Or is this single-file just the full set of
managed code, packed together, and still requires a runtime to execute?

~~~
mdasen
Yes, it can contain the .NET runtime like a Go executable. You can already
(mostly) do this today with .NET Core 3/3.1 (I say "mostly" just because it
simply unzips the runtime on first run). I deploy to Linux with everything in
a single file and it's been perfect - headache-less deploys. You can read more
at
[https://github.com/dotnet/designs/blob/master/accepted/2020/...](https://github.com/dotnet/designs/blob/master/accepted/2020/single-
file/design_3_0.md) and [https://docs.microsoft.com/en-
us/dotnet/core/tools/dotnet-pu...](https://docs.microsoft.com/en-
us/dotnet/core/tools/dotnet-publish). I haven't had any problems with it at
all. The size is a bit bigger, but I don't really care about that and then I
can just run it like anything else without worrying about making sure that the
.NET runtime is on my deploy system and kept up to date.

You can also use the PublishReadyToRun flag to do AOT compilation so that
startup times are faster ([https://docs.microsoft.com/en-us/dotnet/core/whats-
new/dotne...](https://docs.microsoft.com/en-us/dotnet/core/whats-new/dotnet-
core-3-0#readytorun-images)). It still keeps the IL (intermediate language)
around for certain things so the file sizes can be a bit larger, but if
startup times are a concern, it's an option.

~~~
cm2187
I wish there was an intermediary solution, where it wouldn't ship the runtime
but would still be single file.

The full .net framework had ILMerge that was doing exactly that. But it isn't
compatible with .net core.

~~~
shawnz
It seems like you can set PublishSingleFile true with self-contained false,
isn't that what you want?

~~~
cm2187
If we can do that (and don't pay a sluggish start up time as some mentioned we
currently do) then yes, absolutely.

It doesn't bloat the executable with binaries that are already installed on
the machine, and the executable benefits from future updates to the framework.

------
alkonaut
Single exes are nice, but people still don't want to download and run single
exes. Once you use an installer or package manager to grab it anyway, doesn't
the benefit become smaller?

I guess for some scenarios such as deploying webapps etc, it might be very
handy to copy ONE file and run it. But for client apps, is there a visible
improvement to having 1 exe instead of 1 exe and 5 dlls, which are the same
size together?

~~~
pathartl
I've written a few utilities that have been released to the public. In my eyes
the most important aspect of a single executable is simplicity. I personally
prefer applications that I can just drag to any folder. I think, even though I
don't like the OS as a whole, macOS does a pretty good job at creating a
compromise with .app folders. You can keep framework dependencies, embedded
resources, etc contained within one area that you can move around easily, but
you don't have to package it up in the binary.

I see the whole thing as more of an issue of your target demographic and the
scale of your application. Things that run as background services would make
sense to distribute as an installable package. Larger applications like Office
or Visual Studio are much too large to throw in a single executable. Something
else though like Acrobat Reader or FileZilla I think would make sense to
distribute as a single executable. Most times I just don't want to install
anything. I use FTP so seldomly that I'd rather want to just download a
portable FTP client than keep something installed or even have to extract a
multi-file archive somewhere.

------
makach
I wrote an amalgamation tool once that took all my project files
hierarchically together and built one big source file, after compiling it
became one EXE file. Made it super easy to move projects to different machines
for compilation.

on most windows machines where the .net framework is installed you can find a
compiler.

my motivation for building this tool was to have access to some basic tools in
a locked-down enterprise environment _ducks_

------
at_a_remove
About a decade back I had to write this goofy little custom client-server
deal. I hadn't done Visual Basic since 6, but I thought I'd give the client a
go in VB.NET, using whatever we had at the time. Aside from feeling like a
chimpanzee dropped into the cockpit of a F-22 at the IDE (which kept _doing
things_ ), the people who wanted the project were fairly annoyed that I
couldn't figure out how to smash everything down into a single .exe.

I ended up being so annoyed by the experience I wrote the server backend in
Python and trying out the Python "compilers" for the next client.

When you have something that is going to be scattered across maybe ten or
twenty machines tops for a small project, for whatever reason, people just
like the .exe and I can't say I blame them.

~~~
bad_user
I'm confused, how did Python solve your problem?

~~~
at_a_remove
The first project was VB.NET client, Python server.

Project two was a different problem, and compiled Python client, Python
server. I dimly recall using something like "freeze" for Python to generate
something with many fewer files. I changed my methodology partially based on
my dislike of the IDE that Visual Basic had at the time and partially due to
the criticisms of the VB.NET client I had produced.

------
polskibus
There is a fody plugin that can combine assemblies into one, works also for
Net framework:
[https://github.com/Fody/Costura](https://github.com/Fody/Costura)

~~~
gfody
this plugin is obsolete since .net core 3 introduced single-file executables
and assembly linking

------
AnonC
Will this make .NET. a contender for Go in places where .NET is used widely
and Go is used to develop standalone tools that are easily distributed?

~~~
JamesSwift
I actually very recently wrote a project originally as a single binary .net
WebAPI, but rewrote it to go because runtime memory usage was comparatively
insane. That is likely WebAPI's fault though, so I'm optimistic about sticking
with.net in the future for these single-binary scenarios.

The thing go has though is that single-binary cross-compilation is _much_
easier to grok. It took a lot of reading .net docs to understand what
incantations I needed to pass to the compiler.

~~~
aliswe
Was this on .netcore 2? If yes, then memory usage for moderate apps has gone
down significantly. Turns out they just optimized for demanding apps before.

~~~
JamesSwift
Yes, it was on the latest. I even tried enabling 'desktop' mode for memory,
which helped but barely.

One of my main goals was to have a single binary hosted on heroku free dyno
(512mb ram), and this was using ~1gb vs go which idled at ~50mb.

~~~
davidfowl
What was the API doing? 1gb with client GC? I’m assuming a single core as
well?

~~~
JamesSwift
Nothing really, it just loaded up a JSON file into memory and then served 2
endpoints to query that structure. I dont have the .net version uploaded, but
here is the go version [1]. The 1gb might have been while running load testing
on it, I dont remember entirely. It was very consistently hitting it though.

[1] - [https://github.com/J-Swift/GamesDbMirror-
go/blob/master/pkg/...](https://github.com/J-Swift/GamesDbMirror-
go/blob/master/pkg/server/server.go)

------
akerro
Interesting to see Java moving fast enough to overtake .Net in some aspects
that .Net needs to copy from Java now

~~~
ak39
But hasn't .NET been always copying from Java from day 1?

------
moron4hire
I'm confused. Isn't this already a feature of .NET Core?

~~~
tybit
Currently the single file solution is a zip that unzips into directory with
many files. It was a great stop gap solution but not the final thing
(hopefully).

~~~
moron4hire
Okay, didn't realize that was the case. I had used the single-file solution a
few times, but mostly just for the convenience of deployment, never really
paying attention to any other aspects of it.

