Nim 2.0 on Apple Silicon

The Nim 2.0 release candidate is out, and includes a whole host of amazing changes and additions. I had been holding off on seriously exploring Nim mostly because I was waiting for ORC and multi-threading to become the default. They have been available for a long time, but defaults matter - it’s what first time and first-time-in-a-while users experience. It forms their opinion of what the language is about.

Installing Nim on Apple Silicon Macs is actually pretty straight forward. The official installer sets up choosenim and a default stable release:

curl https://nim-lang.org/choosenim/init.sh -sSf | sh

However, both Nim itself and the compiler output will be amd64 (Intel) binaries. To go fully native, and to benefit from Nim 2 right now, a bit more configuration is required.

Set up Nim 2 natively

We need a source build, and thankfully that’s extremely easy:

git clone https://github.com/nim-lang/Nim
cd Nim
git checkout version-2-0
sh build_all.sh

Yep, that’s it. We don’t have to mess with our PATH either. Instead, we can now point choosenim at the new build:

choosenim <location>/Nim

And confirm the architecture with:

$ nim -v
Nim Compiler Version 1.9.1 [MacOSX: arm64]
Compiled at 2023-01-14
Copyright (c) 2006-2022 by Andreas Rumpf

(2.0 RC self-reports as 1.9.1 as of Jan 15 2023)

To switch back, just use choosenim stable.

Native & universal binaries

Building a project with this toolchain will still produce an Intel binary. To get a full build on macOS, we have to follow Apple’s guidelines and set the correct compiler and linker targets. In your project’s nimble (nimble init if you don’t have one yet), add a new target:

task release_clang, "Build a production release (macOS)":
  --verbose
  --forceBuild:on
  --cc:clang
  --define:release
  --deepcopy:on
  --cpu:arm64
  --passC:"-flto -target arm64-apple-macos11" 
  --passL:"-flto -target arm64-apple-macos11"
  --hints:off
  --outdir:"."
  setCommand "c", "hello.nim"
  • -flto is required because clang doesn’t like Nim’s default passing of LTO parameters
  • note the cpu and -target parameters
  • the rest are standard release flags

ORC and threads are default now, so no need to enable them here anymore. It’s a good idea to pin the Nim version in the nimble file as well, in this case to >= 1.9.1.

Running nimble release_clang now gives us a nice native binary:

$ file hello
hello: Mach-O 64-bit executable arm64

If you want to build an Intel binary, add another task and change the target to x86_64-apple-macos10.12. If you need a universal binary, build both the Intel and ARM targets first, then merge them using lipo:

lipo -create -output universal_app x86_app arm_app

BTW: If you’re wondering what the cover image is about - “Nimm 2” (“take two”) is a popular brand of gummy candy in Germany.