Cross-compiling Crystal for the Raspberry Pi

Despite only learning about it a couple of years ago, the Crystal programming language has fast become one of my favourite programming languages. The syntax and core library are a joy to work with, while the nature of compiling the language allows for more lightweight installs and faster execution than an interpreted language like Ruby or JavaScript.

With these advantages, one of my first thoughts was to use this language for embedded applications. Yet, unfortunately, all this time later, there still isn’t an official build of the compiler for ARM processors, like those used in the Raspberry Pi. So the choices become, either go through the arduous task of building the compiler from source, or cross-compile code on a different type of machine.

While Crystal’s official documentation does describe how to cross compile Crystal apps, there are some specifics needed for the Raspberry Pi. So, if like me, you’re not familiar with command line compilers (I haven’t used one in about 20 years), then hopefully you’ll find these instructions useful.

Preparing the Pi

I tested these commands on a fresh install of Raspbian, so they should cover every package needed to link and run a basic Crystal app.

First thing we do is install necessary packages.

$ apt-get install llvm libgc-dev libpcre3-dev libevent-dev

For sake of completion, here’s what all of these packages do. Feel free to skip this part over if you’re not interested in these details. You’ll still be able to complete the process.

  • llvm – A set of compiler and toolchain technologies. The Crystal compiler uses the llvm to build efficient native code. In our case, it’s able to give information required for cross compiling code for another processor architecture.
  • libgc-dev – The Boehm–Demers–Weiser garbage collector, which the garbage collector used by the Crystal language. This library is one of the reasons why Crystal needs an operating system to run, and can’t be used on bare metal platforms.
  • libpcre3-dev – Perl Compatible Regular Expressions library. A common regular expression library used by the Crystal language.
  • libevent-dev – This library provides asynchronous event notifications and a mechanism for event based callbacks.

The other library that we need is a Crystal library, that we need to build from source. Fortunately, it’s just one file, so it won’t give you a headache. Just run the following commands from the directory where you plan to compile your Crystal program.

$ wget https://raw.githubusercontent.com/crystal-lang/crystal/master/src/ext/sigfault.c
$ cc -c -o sigfault.o sigfault.c
$ ar -rcs libcrystal.a sigfault.o

This will produce the file libcrystal.a. We’ll need this in our final step.

Before we create our program on a different machine, there’s one more command we need to run to get the necessary information.

$ llvm-config --host-target

This command provides a string describing the target flag that we’ll need when running the compiler on our main computer. In my case, it returned armv6k-unknown-linux-gnueabihf, but depending on what version of Pi you’re using, yours may be slightly different.

Cross-Compiling in macOS

I used a Mac for this portion of the process. I assume that it’ll also work in Linux, but if anyone does try on Linux without success, please let me know.

The next step is creating a Crystal app. For demonstration purposes, a simple one liner will do:

puts "Hello, world!"

Let’s save this as hello.cr.

Next, we run the command that gives us our (.o) object file, and a few instructions.

$ crystal build your_program.cr --cross-compile --target "armv6k-unknown-linux-gnueabihf"

The italicized part of this command should be replaced with whatever you saw on your Pi when you ran the llvm-config --host-target command. This command produces the hello.o file, and provides an instruction on how to link the file. On my Mac, this instruction looks like this:

cc hello.o -o hello -rdynamic -L/usr/local/Cellar/crystal/0.35.1_1/embedded/lib -L/usr/local/lib -lpcre -lm -lgc -lpthread /usr/local/Cellar/crystal/0.35.1_1/src/ext/libcrystal.a -L/usr/local/Cellar/libevent/2.1.12/lib -levent -lrt -ldl

Unfortunately, this instruction won’t work on the Pi, because it’s dependent on having Crystal installed through Homebrew, which isn’t going to be there on our Pi. However, the hello.o file created is useful to us, so let’s copy that over to the Pi.

Linking on the Pi

We’re finally at our final steps. First, ensure that the libcrystal.a file that you created in the first section is in the same folder as the hello.o file that you copied over from your main computer. Simply run this command:

$ cc hello.o -o hello -rdynamic -lpcre -lm -lgc -lpthread libcrystal.a -levent -lrt -ldl

This will produce a file named ‘hello’ without any extension. Running this file with:

$ ./hello

Will produce our desired output:

Hello, world!

Congratulations. You’ve now cross compiled a Crystal program and run it on your Raspberry Pi.

Conclusion

While it’s my express wish that this helps you get started running Crystal on your Raspberry Pi, there is more to running cross compiled apps. This program was very simple, just a single output. Some programs can be more complicated, and may need more libraries installed on your Pi, and these instructions won’t take care of that.

I also maintain hope that in the future, we’ll have an official build of the Crystal compiler for ARM, and a community of developers who want to develop Crystal libraries and programs for the Raspberry Pi. In the meantime though, at least we have these instructions to get us started.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s