A Linker for Joel

In January 2004 Joel Spolsky wrote a blog post titled “Please Sir May I Have a Linker?”, where he described his tribulations trying to install a small .NET app in computers not bundled with the original .NET framework.

18 years later, his wish has been granted. Well, maybe it’s been granted a while ago, but I learnt about this only recently.

Here’s the thing: one can cross-compile .NET Core 6.0 applications in any platform to any other platform, just like with Go for example. Here’s how to create a macOS application (with Intel CPU) on a Linux box:

dotnet publish --configuration Release --runtime osx-x64 \
  -property:PublishReadyToRun=true -property:PublishSingleFile=true \
  --self-contained true

This command specifically requests the creation of a single file application using ReadyToRun, which optimizes its startup time.

The standalone executable will be located in bin/Release/net6.0/osx-x64/publish and indeed, fully statically linked, it does not require anything else to run. Copy it to another machine, run, done.

Using my Conway project as an example (there’s a C#, a VB.NET, and an F# implementation there) I get a ~60 MB (gasp) application that happily runs without any other requirements.

Well, what Joel asked for included a specific requirement:

Then, it removes any library functions that your program does not use.

Clearly, if my final executable for such a simple project as this one weighs 60 MB… there’s probably a bit of code to remove. Go does this much better than .NET, at least at the time of this writing.

Anyway. Replace the --runtime property from osx-x64 to win-x64 and you can build a Windows application, whatever operating system you’re using:

dotnet publish --configuration Release --runtime win-x64 \
  -property:PublishReadyToRun=true -property:PublishSingleFile=true \
  --self-contained true

In this case, the standalone executable will be located in bin/Release/net6.0/win-x64/publish and again, it does not require any other dependencies.

The complete catalog of runtime IDs is available in the documentation of .NET, but they include pretty much all combinations of Windows, macOS, and Linux with 32 and 64 variants of both Intel and ARM CPU architectures–well, only 64 bits for macOS, of course.