Having found myself using a Chromebook with Arch Linux, I acutely felt the lack of official Crystal builds for ARM. After spending an hour or so looking online for a comprehensive guide to getting a working compiler on ARM, I think I now know the general idea. I will lay it out here, for myself, as well as for others who may be in the same situation.

### To compile Crystal, you need Crystal

To compile Crystal without bootstrapping it from older versions, you need a different machine which is capable of cross-compilation. Fortunately for me, my friends and I have previously set up a server hosting several x86_64 virtual machines, one of which we dedicated to cross-compiling various programs.

sudo pacman -S crystal


Note that this isn't the ARM Chromebook. Running this command on the Chromebook would not work.

### Building on the x86_64 Machine

After getting the compiler, I also needed to download the compiler source. This was done using git:

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


I also installed llvm6, which is required on both the machine that's building the compiler and the machine for which the compiler is being built:

sudo pacman -S llvm6


From here on in, I ran commands from inside the directory that was downloaded via git clone:

cd crystal


Finally, I didn't want to compile the "master" version of the compiler. It wasn't a release! To check out the latest release (0.27.2 at the time of writing), I used git:

git checkout 0.27.2


Now, I had the compiler and the source. I was ready to compile the source to get myself a nice ARM Crystal compiler. But how? The official guide specified two options for cross compilation:

• --cross-compile - This option is basically a flag. You just add it to the command to enable cross compilation.
• --target=<llvm target triple> - This specifies the target architecture you're building for.

In order to get the second option right, I had to know the LLVM target triple for my target machine. To find it, I ran the following command on that machine:

gcc -dumpmachine


This produced the output armv7l-unknown-linux-gnueabihf. This was exactly what I needed to know!

Finally, looking through the Makefile in the repository, I found three more flags that are used by default in the process:

• -D without_openssl
• -D without_zlib
• --release - for faster compiler

To compile the compiler, I had to compile the src/compiler/crystal.cr file. With all these options, the command came out to be:

crystal build src/compiler/crystal.cr --cross-compile --target=armv7l-unknown-linux-gnueabihf -D without_openssl -D without_zlib --release


There is only one more trick to cross-compiling Crystal: although the official guide specifies the options --cross-compile and --target=..., and although you can just attempt to use the crystal command on the source file, this won't work. You need to use the wrapper script that Crystal provides. I had to replace crystal with ./bin/crystal:

./bin/crystal build src/compiler/crystal.cr --cross-compile --target=armv7l-unknown-linux-gnueabihf -D without_openssl -D without_zlib --release


With this, I finally obtained a crystal.o file. After downloading this onto my target machine, and making sure to copy the command the compiler printed out, I was ready to proceed.

### Compiling on the Target ARM Machine

Just like with the x86_64 machine, I needed llvm6:

sudo pacman -S llvm6


I also needed the Crystal source, again!

git clone https://github.com/crystal-lang/crystal.git && cd crystal
git checkout 0.27.2


Finally, I needed a few more libraries. These are gc (the Garbage Collector Crystal uses) and libevent:

sudo pacman -S gc libevent


With these dependencies installed, I could compile the last two files needed to build a working compiler:

make deps


After this, the command I noted down from earlier (on the x86_64 machine) was all that was left (note that I placed crystal.o in the clone of the Crystal repository):

cc 'crystal.o' -o 'crystal'  -rdynamic src/llvm/ext/llvm_ext.o /usr/bin/llvm-config --libs --system-libs --ldflags 2> /dev/null -lstdc++ -lpcre -lm -lgc -lpthread src/ext/libcrystal.a -levent -lrt -ldl -L/usr/lib -L/usr/local/lib


This produced a fresh, new crystal executable!

I was not done. The executable couldn't compile a basic "Hello, world"! This is because I once again needed the wrapper script. This script searches the .build directory for the Crystal executable, and so I placed it there:

mkdir .build
mv crystal .build


Finally, I made sure to add the ./bin directory to my PATH.

### Shards

Crystal is not complete without its package manager, shards. This program doesn't need to be cross compiled, but it does come separately from the compiler. First, I cloned the repository:

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


Then, I installed libyaml, which is necessary to compile shards:

sudo pacman -S libyaml


And finally, I ran the Makefile provided in the repository:

make


I once again added the ./bin directory to my path. And finally, a working Crystal environment!