Install Crystal on Mac M1

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

1 Like

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.

6 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.

I extracted crystal-1.0.0-1-darwin-x86_64.tar.gz into ~/crystal-prebuilt, and I have:

LICENSES	
embedded	
license-cache	
src
bin		
etc		
samples

I extracted a second time, just to make sure. And I checked against 1.1.1, too, to see if it had the Makefile, but it is missing from that version, too.

Looks like the tar file referenced in the blog post doesn’t contain the Makefile, but the “source code” (https://github.com/crystal-lang/crystal/archive/refs/tags/1.1.0.tar.gz) tar does. I’ll give that a shot, and report back if that works.

Not sure how long it takes to build, but I’ve been running make overnight and it’s still running…

You also need to actually check out the current crystal source code, a step that appears to be missing in the blog post, probably for the same reason a fish might forget to start their instructions with “first, are you in water”…

This should do it:

git clone https://github.com/crystal-lang/crystal.git

…then cd crystal, and. that’s where you want to run make.

You might also run into a problem with LLVM: the homebrew version has updated to 12 in the meantime, and the patch to update crystal to it hasn’t been merged yet. So, either merge these changes into your local sources, or brew install llvm@11 / ibrew install llvm@11 followed by adjusting all the LLVM environment variables.

And it works! After trying those “Omnibus” scripts, which may well win the hardcoded-paths Award at the Friends of Historical Ruby Releases’ annual gala, this was surprisingly straightforward, thank you!

1 Like

Ahh yes, sorry about that! @KarlKemp is absolutely correct. The blog post is to be run in the context of the Crystal compiler source code, not the compiled release distributions. Will update the post to make that more clear.

(Though I believe a universal macOS release build ticks ever closer…)

1 Like

Hi @KarlKemp, out of curiosity, how long does it took you to compile?

It’s rather quick. I didn’t quite catch when it was done, but <10 min.If it hasn’t moved for hours, there’s probably some spurious punctuation or something and it’s waiting for input. Check top, activity monitor, or ps ax to see what it’s doing.

I tried to extract the sequence of commands from my shell history, but it’s too much of a mess, I believe.

This is checking out the source and patching it for LLVM 12 (EDIT: no longer necessary, since the PR was merged earlier today):

git clone https://github.com/crystal-lang/crystal.git
cd crystal
# git remote add llvm https://github.com/maxfierke/crystal.git
# git pull llvm
# git merge llvm/mf-llvm_12

If it’s any help, here’s the end result: iCloud sharing: crystal

r ~ % ./crystal -v
Crystal 1.1.1 (2021-07-26)

LLVM: 11.1.0
Default target: aarch64-apple-darwin21.0.0

(That binary was actually compiled with homebrew, which was relatively straightforward once there’s an existing, working aarch64 binary. )

1 Like

The blog post from my earlier post has been updated to reference LLVM 11 specifically and address the initial cloning of the source code. I would caution against building with LLVM 12 at this time, as Beta reminded me in another post it does not contain a vital patch that Homebrew’s LLVM 11 has that ensures release mode builds still function correctly. The patch only exists in LLVM master (and will likely be included in LLVM 13) but is not applied to LLVM 12 in Homebrew or elsewhere, as far as I know.

3 Likes

Let me clarify something. The patch is needed for x86 boxes only. For consistency, since we mostly have x86 architectures, we can only claim support for LLVM 10, as this is what we know it works. But that doesn’t mean it won’t work with 11 or 12. In fact, it’s OK if you go one step ahead of us and tell us if you find issues with these versions, so we can anticipate them prior to the 13 :smiley:

1 Like

Crystal 1.2.2 works with M1, you can install it with asdf

asdf plugin add crystal
asdf install crystal latest
asdf global crystal latest

2 Likes