Cross Compiling Rust for Raspbian

The sensation of cross compilation

I've been teaching myself the Rust programming language for a while. It is a fun language with powerful tools to enforce memory safety; the compiler will refuse to build your project if you try to give multiple parts of your project ownership of the same object. This paradigm is different from other languages I've worked with, which has forced me to think about programming in a new way.

Also new to me was the process of building the project to run on a machine with a different architecture. My laptop is MacBook with an x86-64 Intel CPU, while the Raspberry Pi I planned to run the project from has a 64-bit ARM CPU (earlier models have a 32-bit CPU). Using cargo build --release would build a binary that ran fine on my laptop, but was totally incompatible with the rpi, and targeting the ARM architecture with

cargo build --target aarch64-unknown-linux-gnu

would fail because of incompatibilities between homebrew-installed OpenSSL on my mac and apt-installed libssl-dev installed on the linux rpi.

Enter cross, a project that cross-compiles Rust projects between different architectures. Under the hood, cross uses docker images built with all of the libraries, dependencies, and missing pieces needed to cross compile Rust projects. And since all of these components live in the docker images, they don't tangle anything up in your dev environment.

cross has great documentation on getting started, and once installed, cross compiling is as simple as invoking cross where you'd normally use cargo. Assuming you have docker and Rust (and its toolchain) installed, all you need is:

# create a new Rust project
cargo new myproject && cd myproject

# install cross
cargo install cross

#compile a binary for your target architecture
cross build --target aarch64-unknown-linux-gnu --release

Once the build has finished, you'll have a cross-compiled binary in your project's target directory:

ls myproject/target/aarch64-unknown-linux-gnu/release
#build       deps        examples    incremental myproject   myproject.d

Building a Docker Image

With our cross-compiled binary in hand, building a docker image for our raspberry pi is straightforward.

FROM debian:buster-slim # or whatever base image you want to use

COPY ["target/aarch64-unknown-linux-gnu/release/myproject" "./"]

CMD ["./myproject"]

Next, build the image:

docker build --platform linux/arm64/v8 -t myproject:mytag -f path/to/Dockerfile .

Finally, save the image as a tar and copy it to your raspberry pi:

docker save myproject:mytag > myproject.tar
scp myproject.tar user@my-rpi:~/