Install Crystal on Mac M1

The basic homebrew command cannot install Crystal properly on Apple Silicon M1. And the “install” page did not mention anything about it.

llvm@9: The x86_64 architecture is required for this software.
Error: An unsatisfied requirement failed this build.

I have seen that we can try to install it under Rosetta 2, but I did not find any proper instruction related to this. I have tried a few ways to install brew and software under Rosetta 2 (found on google), but all of them are not working for Crystal.

So if there is any solution, or it is not supported yet, I suggest mentioning it out on the “install” page of the crystal-lang website.

Appreciate!

1 Like

Solved by installing the pkg file.

I am still looking forward to install and update it by homebrew in the future!

2 Likes

Thanks for sharing this. I wouldn’t have even thought to check for a pkg file.

For those wondering where the pkg file is, it’s on the release page on github. For 1.0.0, it’s found here

1 Like

One of the challenges of a self-hosted compiler (the Crystal compiler is written in Crystal) is that support for other architectures must be released before it can be used to compile itself for that architecture.

The tooling inside the compiler to allow it to compile itself for the M1 processor was just released with 1.0 so M1 builds on Homebrew won’t be available until at least the next release.

The pkg file from the releases page is built for the x86_64 architecture, so it’ll run on the M1 via Rosetta 2 but it isn’t yet optimized for the native ARM architecture of the M1 processor.

The good news is that there are instructions for building Crystal on the M1. On my M1 MBP I compiled the compiler from source with those instructions. I did add --release to compile the compiler in the last step to optimize compilation times for my apps, though. With that in place, I’m seeing compile times on my apps up to 30% faster than on my iMac — which is the Intel Mac with the fastest single-core benchmarks on Geekbench.

5 Likes

Thanks for this link also Jamie. I took a look at the build instructions for the M1 but I kept getting errors about linker & architecture issues. I just skimmed the thread (and followed the post you linked directly so it’s possible I overlooked something).

I think I’ll just need to wait until the next release when we have some

For those looking at the omni, I just ran into some issues with linker issues due to SSL architecture issues so even the omni may not solve every issue. Even docker images won’t run correctly locally so I may just wait until this sorts itself out.

Yeah, it took me a few tries to get it working. I started from scratch 2 or 3 times, IIRC.

I gave this multiple run thrus from scratch last night & again today. Upgraded big sur, xcode, etc. Something seems to have changed. I made it all the way to the last step last night (with small tweaks here and there)

Pulled down latest changes from main branch today and now it dies early (sigfault file is missing for one)

Upside though: Docker works now so i think there was an update.

Yes, https://github.com/crystal-lang/crystal/pull/10463 got merged into master. The segfault handler is now implemented in Crystal and there’s no need to build it separately anymore.

2 Likes

Thanks, that’s helpful to know. I’m still going to wait a bit until this gets homebrew support or has some additional instructions. I wasn’t interested as much in crystal development as I was just writing code in it.

Some of the steps I’m noticing failing for M1 support (like libcrystal.a not compiling, and missing for example) are a bit too much for me to step into right now.

Excited to see M1 support coming soon though!

Has anyone had any luck getting Lucky working on an M1 by chance? For brevity’s sake, here’s the link to the Github of what I’ve tried to do to get Crystal and Lucky running. I managed to get it mostly working but I run into an issue when running script/setup at database creation. OpenSSL doesn’t seem to play nice with Crystal and I’m guessing it might be because OpenSSL is native and Crystal is running as x86? I installed Crystal 0.36.1 from the .pkg file since Lucky isn’t compatible with 1.0 yet.

1 Like

I’m seeing the same issue you seem to see; when using the omni installer pkg file, openssl doesn’t seem to link/work correctly. The instructions that Jamie linked above, here, might fix that issue. I haven’t been able to build it myself off of those instructions yet.

1 Like

I’ll take a shot at it this weekend and see what I can get. Finally got Flutter running on my M1 and that took forever.

Those instructions are a little bit out of date. I would recommend using the instructions I wrote up for the aarch64 support PR: Add aarch64 support for macOS/darwin targets by maxfierke · Pull Request #10348 · crystal-lang/crystal · GitHub as I think I capture a few more of the required environment variables there. It’s also avoids some of the gotchas of accidentally linking against x86_64 libs instead of arm64 libs and vice-versa.

Couple of errata on those instructions:

  • libcrystal.a is no longer needed, so you can skip any mention of it (don’t need to build or link it).
  • You also need to be using LLVM 11 on an M1 for it work (both on the x86_64 and arm64 sides of Homebrew).

Other than that, the instructions should still work, but I haven’t built from scratch on an M1 in a little bit. I’m hoping to find some time this week to sit down and write up a blog post on doing it, so that I can keep that up-to-date as things change.

Once full support for LLVM 11 lands in Crystal land, and the x86_64 macOS build of crystal is built with LLVM 11 it should be possible to build an arm64 build via Homebrew from the x86_64 build.

3 Likes

Max thanks for this. I took at a look at your post, it’s mostly similar to the original one linked but the compilation near the end is different. I think the only piece I’m missing is what to do with the reference to libcrystal.a in the snippet from:

   LDFLAGS="-L$ARM_LLVM_ROOT/lib" \
   CPPFLAGS="-I$ARM_LLVM_ROOT/include" \
   $ARM_LLVM_ROOT/bin/clang crystal.o -o .build/crystal  -rdynamic -L$ARM_BREW_PREFIX/lib src/llvm/ext/llvm_ext.o `"$ARM_LLVM_ROOT/bin/llvm-config" --libs --system-libs --ldflags 2> /dev/null` -lstdc++ -lpcre libgc.a -lpthread libcrystal.a -L$(brew --prefix libevent)/lib -levent -liconv -ldl

Even removing the reference to libcrystal.a returns this error:

Undefined symbols for architecture arm64:
  "___aarch64_cas8_relax", referenced from:
      _GC_mark_local in libgc.a(mark.o)
  "___aarch64_ldadd8_relax", referenced from:
      _GC_generic_malloc_many in libgc.a(mallocx.o)
  "___aarch64_ldset8_relax", referenced from:
      _GC_dirty_inner in libgc.a(os_dep.o)
ld: symbol(s) not found for architecture arm64
clang-11: error: linker command failed with exit code 1 (use -v to see invocation)

I feel like I’m missing something obvious. I wrapped these inside of a gist here: Build crystal · GitHub

You can remove any reference to libcrystal.a. It doesn’t exist any more. There’s nothing to replace it with, just remove it anywhere it shows up

As for the other messages related to libgc.a, it’s possible it compiled libgc for x86_64 instead. The good news is, we don’t actually need to install it from source anymore, as Homebrew now include the proper patches in their version. You can try by using bdw-gc installed via Brew (brew install bdw-gc, assuming you’ve installed Homebrew for ARM). If using the Brew version, you can remove any references to libgc.a.

Working on the blog post right now, and hopefully will have something to show later that goes into the changes more.

1 Like

I’m happy to take a look at the blog post when you’ve got it posted

I think I got it sorted with your suggestions. I had to brew unlink bdw-gc, then brew install libatomic_ops and then brew link bdw-gc --HEAD. Then copy the right libgc.a file into the crystal root. I updated the instructions in my linked gist. Still have to run specs, but it runs and reports a version

Posted the blog post here: https://maxfierke.com/blog/posts/so-you-want-to-compile-crystal-on-arm64-macos

Tested it all this evening, and was able to get working shards, as well as another application compiled and running without making any changes to either of those.

I need to tweak the code blog styles on my blog a bit so that it’s not so ugly, but after that I’ll publish more broadly.

5 Likes

This looks great. I appreciate the instructions for building the shards repo as well.

Looking forward for this to make it into the omni but this will definitely get me started. Thanks!

@maxfierke , thank you so much for the blog post and the effort you’ve put into this.

I’m hitting a snag, though, on trying to compile for x86_64. Make is returning the error “make: *** No targets specified and no makefile found. Stop.”. Could you help me understand which makefile needs to be processed? I’m just not familiar at all with LLVM (or crystal)! I’m also in zsh, which I’m not sure if that matters. I double-checked all the env variables, and they seem correct, too.

LLVM_CONFIG="$INTEL_LLVM_CONFIG" \
   LDFLAGS="-L$INTEL_LLVM_ROOT/lib" \
   CPPFLAGS="-I$INTEL_LLVM_ROOT/include" \
   CC="$INTEL_LLVM_ROOT/bin/clang" \
   AR="$INTEL_LLVM_ROOT/bin/llvm-ar" \
   CRYSTAL="$HOME/crystal-prebuilt/crystal-1.0.0-1/bin/crystal" \
   arch -x86_64 make

Are you running it from the crystal directory (the base directory for the Crystal repository)? There should be a Makefile in there.