110 lines
5.3 KiB
Markdown
110 lines
5.3 KiB
Markdown
---
|
|
title: Setting Up Crystal on ARM
|
|
date: 2019-03-02 06:47:50.027
|
|
tags: ["Crystal", "ARM"]
|
|
---
|
|
Having found myself using a Chromebook with Arch Linux, I acutely felt the lack of official [Crystal](https://crystal-lang.org) 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.
|
|
|
|
To get started, I had to download the compiler on that machine:
|
|
```
|
|
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 a 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!
|