Cross compile Rust to your Raspberry Pi

Who is this for?

If you want to get started with ...

  • ...cross compiling code for your Pi,
  • ...packaging your programs in .deb archives,
  • ...launching your program when your Pi boots, or
  • ...all of the above

then this guide is for you. This post serves as a starting point for your endeavours, so instead of going too much in depth, I will try to point you in the right direction.

Getting started

In this guide I assume that you already have rust and cargo installed. On top of those, I will use cross and cargo-deb.

Cross compilation

For cross compilation, I recommend the cross tool for cargo. Note that it relies on Docker being installed.

cargo install cross

Once it's installed, it's very easy to cross compile to a wide range of architectures. One thing thing to keep in mind is that it can be very tricky to cross-compile code (or dependencies) that bind to C code. Cross describes how to produce your own Dockerfile, but I found it fairly difficult to get this to run at all, let alone smoothly. Pure Rust projects work very well, though.

Raspberry Pi 1 and Zero

cross build --release --target arm-unknown-linux-gnueabihf

If you prefer to create statically linked executables, use arm-unknown-linux-musleabihf instead.

Raspberry Pi 2, 3, and 4

cross build --release --target armv7-unknown-linux-gnueabihf

Again, if you prefer to create statically linked executables, use armv7-unknown-linux-musleabihf instead.

Create a deb package

I recommend using cargo-deb.

cargo install cargo-deb

Before running any of the following commands, make sure that your code has already been built. This is because cargo-deb defaults to using cargo instead of cross for compilation.

Raspberry Pi 1 and Zero

cargo deb --no-build --target arm-unknown-linux-musleabihf

Raspberry Pi 2, 3, and 4

cargo deb --no-build --target armv7-unknown-linux-gnueabihf

Note that this creates a bare-bones archive. You can find more information on this here.

Launch on boot

The easiest way to get your service up and running at boot is to create a systemd.service file (or a service unit configuration file).

If you're installing your service with a deb file (as described above), and you went with the minimal approach described here, then your executable(s) will be installed in /usr/bin by default. You will probably also want the network to already be available when your program starts. Based on these assumptions, here's what a basic unit could look like:

Store this file in your project under assets/my-awesome-service.service.

[Unit]
Description=My awesome service
After=network.target

[Service]
ExecStart=/usr/bin/my-awesome-service
WorkingDirectory=/home/pi/
StandardOutput=inherit
StandardError=inherit
Restart=always
User=pi

[Install]
WantedBy=multi-user.target

To add it to your deb archive, you'll want to edit your Cargo.toml file, and add an asset under [package.metadata.deb].

[package.metadata.deb]
# Your other settings...
assets = [
    # Your other assets...
    ["assets/my-awesome-service.service", "etc/systemd/system/", "755"],
]

(If that didn't make sense, you'll want to look at cargo-deb's configuration).

Once this is installed, you should now be able to monitor your service using systemd's service command.

sudo service my-awesome-service start
sudo service my-awesome-service status
sudo service my-awesome-service stop

Note that the executable will run as the pi user, and use /home/pi as its working directory. This might not be right for you. If you want to do this right, you'll probably want to useradd -r a system account, and giving it as few privileges as possible, as well as a place to store configurations under /etc/my-awesome-service. You may also want to store its logs under /var/log/my-awesome-service/main.log, which you of course want to rotate, etc. This post isn't really about any that, though. It's just about getting you started :)

Good luck!