> If you want a single-file exe that's a bit more advanced and requires a few properties in the csproj.
This. This is what's wrong. Why is single-file exe "a bit more advanced". In early 2000s Delphi could build a single file exe in seconds, and that was the default behaviour.
What changed since early 2000s that having an exe is a) advanced and b) requires manually fiddling with some unspecified properties in the abomination that is a csproj file?
> This. This is what's wrong. Why is single-file exe "a bit more advanced".
Because that's how it works for very every single interpreted and bytecode compiled language?
And the thing that changed in the early 2000s was a massive shift toward using interpreted and bytecode compiled languages.
If we're specifically talking .NET, the thing that changed since the early 2000s is that creating a self-contained executable became possible in the first place. On Windows, .NET *.exe files were still run by an outside runtime, it's just that, since Microsoft owned the whole platform, it was easy for them to hide all that behind a curtain, ensure .NET is preinstalled with Windows, etc. The design constraints changed when .NET became cross-platform. OS X and Linux require a bit more (or at least different) finagling in order to achieve good user experience.
> Because that's how it works for very every single interpreted and bytecode compiled language?
I went ahead and searched for C# executable around 2005-2006. Guess what, this wasn't even a question then. Because, apparently, building an .exe was also the default setting for C# projects in Visual Studio.
So. What changed?
> If we're specifically talking .NET, the thing that changed since the early 2000s is that creating a self-contained executable became possible in the first place.
It was always possible.
> On Windows, .NET .exe files were still run by an outside runtime
1. We're literally in the thread to a question about Windows apps. On Windows
2. If you've ever did anything on Windows such as played a game, you'd know that you almost always need something external to run: be it msvcrt (c++ runtime) or clr.
> The design constraints changed when .NET became cross-platform.
What you mean is: it's still perfectly fine to create a standalone executable, but for some reason it's now called a "more advanced operation". The thing that's changed is that now it's hidden behind a ton of inconsistently named parameters
But, per the last paragraph of my comment, those .exe files were not really executable files. At least not in the sense of, say, an AOT-compiled C++ application.
They were much more comparable to an "executable" Python or Perl script where the OS knows to look at the hash-bang line to figure out what interpreter to use to run it. If you try to execute one of those .NET .exes on a computer that doesn't have a suitable .NET run-time installed, you'll get more-or-less the same error as you'd get trying to run a Python script on a computer that doesn't have Python installed.
The part that was being criticized a few comments up was about how to create self-contained .NET apps with the runtime bundled in and everything. Specifically, these guys: https://docs.microsoft.com/en-us/dotnet/core/deploying/#publ... That kind of executable simply did not exist in the old Windows-only .NET Framework; it's a feature that was first introduced in .NET Core 3.0.
No application on a modern OS is standalone. They all rely on either having many components the need already installed, and then try to bring along others that may not be installed. As the commonly installed base changes, the included pieces also change.
I for one don't want every application to include 100's of MB of standard components that every other such app also brings (such as Electron style apps). I'd much rather have an app tell the OS to fetch missing pieces once, and once only, then future apps share.
And this also mitigates a significant source of security holes. Nothing like linking everything and the kitchen sink so your system is riddled with unknown, hidden vulnerabilities in binaries.
For example, I recent worked on tools to search for such things - they are EVERYWHERE. OpenSCAD, for example, includes a ssh engine, which has known vulnerabilites, but OpenSCAD does not list them. I found thousands and thousands of embedded binary libraries in applications with known and unpatched vulnerabilities.
Too bad all those didn't use a decent package manager, allowing systemwide updates to common functionality. I suspect the future is more components, not less, for these reasons.
"Oops, we couldn't find a shared library that this program depends on" is not exactly the same error as, "Oops, we couldn't find the interpreter / VM that you need to run any programs written in this non-AOT-compiled language."
In other words, compare missing msvcrt.dll more to missing libc.so than to not having a JVM or whatever installed. I guess from end user perspective the end result is the same - the program won't run - but what's actually going on under the hood is very different.
Which is exactly why I dont use any of those. I will stick to Go, or Rust, or Zig. People expect to be able to produce a single EXE. Maybe not as the default, but it should be an option, and an easy option. Any language that cant do that is a failure in my opinion.
Also please dont lump interpreted languages with C#. At least with interpreted languages, once you have the runtime on your computer, you can have a single script file to work with. With C#, you STILL have to have the runtime on your computer, then after you "compile", youre left with a folder of one EXE and literally over 100 DLL.
With Python, if I publish my script with 100 dependencies, and someone `pip install`s it, they will also end up with 100 packages being copied to their computer.
The main difference is that Python installs them globally (or at least thinks it does, if you're using virtual environments), while .NET apps isolate themselves from each other.
Also, let's make a fair comparison. Is that hypothetical Rust application of yours depending on literally 100 crates? If so, what is the size of your binary?
Please don't use Python package management as a baseline. Aim higher.
I love Python, but I cringe whenever someone asks me why a Python program isn't running properly on their machine. Obligatory xkcd: https://xkcd.com/1987/
Take PHP with composer: it works quite fine, but still you need all the dependencies downloaded from somewhere on Internet. Just a PHP script and the PHP interpreter works, but this is not how 99.9% of the software is written.
The php world invented the phar format [1] to deal with the single file tool/app distribution issue.
In fact, composer uses dependencies managed by itself in their sources. Then it gets packaged and distributed as a single file that includes all dependencies (composer.phar). That single file can be run by php as if it was any regular php file (including executing it directly and letting your system detect the required interpreter through the shebang).
> With C#, you STILL have to have the runtime on your computer, then after you "compile", youre left with a folder of one EXE and literally over 100 DLL.
No you don't. If you'd rather increase the download size for the user then you can turn on self-contained (include runtime) and single-file app.
If we're talking modern .NET ( 6 for example ) you have 4 options,
let's assume a simple hello world without third party dependencies:
1. Build it runtime dependent: ( which requires the NET 6 runtime to be pre installed ) on your computer in order to be able to run:
You get a single exe file.
2. Build it self contained: You get a directory with a bunch of dlls and one exe
But no runtime needs to be installed on the target computer
3. Build it self contained + single exe: You a get a single exe that embeds all those dlls and unpacks them in memory ( since net 6, in net 5 it would copy them to a temp directory )
4. Build it using AOT Mode: You get a single, statically linked exe.
This is probably the closest to a standard Rust (statically linked) build.
However AOT mode is not yet official and requires some fiddling still, but should become stable for NET 7 next year.
And you loose out on some features obviously like runtime code generation
The reason it's more complicated is to support reflection. C# allows you to create objects and call methods at runtime based on data not available at compile time, including classes and methods that don't get used by the normal (non-reflection) code.
That means that by default you can't do tree shaking, which means you would end up with enormous exes, which will probably annoy the type of people who want a single exe.
The bit more advanced is to tell the compiler which types you might want to reference by reflection so that it can shake the rest out of the tree.
Do you know of any production GUI applications that are literally a single-file EXE and aren't like, small utilities? There's just no reason to try to pack everything into a single file, Go-style. The self-contained publish (which is literally a single flag) is a quite reasonable result - a directory of files that are completely sufficient to run your app on any Windows computer, without any dependencies to install.
> Do you know of any production GUI applications that are literally a single-file EXE and aren't like, small utilities?
The old Delphi-based version of Skype fell into that category. Thinking of that example, I can understand why some people think modern software is decadent.
It's amazing that most people on this thread seems to take this nonsense as being completely normal and acceptable now. It really shows how much windows dev has devolved over the last decade.
Why does every linux app I download come as a self-extracting installer, run in a container, or download dependencies? Those aren't single-file executables.
> I'm not sure why people are making it out as if this is some very complicated feature.
OP asked how.
The answer was, quote "If you want a single-file exe (as in completely statically compiled without self-extraction) that's a bit more advanced and requires a few properties in the csproj to configure how to handle native assemblies that expect to exist on disk. If you want it to have no runtime dependencies, add `--self-contained`"
Somehow creating an exe is "more advanced", and requires changing of unspecified parameters in the project file. wat.
.NET Core is cross platform, it was created with ASP.Net as the driver and web development is not about exe files. "dotnet run" runs your code, on any platform, that's one of the default intended ways to run code. If you want a platform-specific executable you've done more work and made the code less general. If you also want to package the entire .Net framework into one binary on any platform, why is it unbelievably impossible to understand that this is more effort and desired by fewer people, so isn't as easy to do?
It is trivial if you can embed most of the OS inside of your executable, if there can be only 1 version of the OS, if you do not use any libraries you cannot statically link and so on.
This. This is what's wrong. Why is single-file exe "a bit more advanced". In early 2000s Delphi could build a single file exe in seconds, and that was the default behaviour.
What changed since early 2000s that having an exe is a) advanced and b) requires manually fiddling with some unspecified properties in the abomination that is a csproj file?