Cross compiling in Rust

Overview

Easiest way that I have found is to use cross. It cross-compilers in docker (or podman) containers, that it manages itself.

$ cargo install cross

You can also use cargodirectly with cargo build --target=$target, but I havent given that more than a cursory attempt (but I should, to understand how cross relates to cargo better).

You can get a list of supported targets (platforms?) from rustc:

$ rustc --print target-list

For more details on the different targets and how well they are supported, check the rustc documentation about about Platform suppport and the for cross specifically, the README.md also has a list.

Configuring cargo

If you are cross compiling, then you are most likely targeting a platform that you can't or don't want to compile on and most likley you'l want a statically linked binary. You don't configure that in Cargo.toml, because that is for configuration of your project itself, and we need to configure cargo (not the project that it is building).

Note

Targets with musl are generally preferred for building statically linked binaries.

So you need to add that to cargo.toml, recommended paths are:

  • $HOME/.cargo/config.toml
  • $PWD/.cargo/config.toml

Note

Sometimes config.toml referred to without the .toml suffix as .cargo/config -- that's a legacy name for the same file.

Making static builds

To get a static build, you need to pass -C target-feature=+crt-static to rustc. So if you're running rustc directly, this should work:

$ rustc -C target-feature=+crt-static

But you probably arent, and you probably want to use the cargo config. If you use the cargo config, you can specify options for multiple targets in the same place, which is kind of neat:

[target.x86_64-unknown-linux-musl]
rustflags = ["-C", "target-feature=+crt-static"]

# my vacuum cleaner
[target.aarch64-unknown-linux-musl]
rustflags = ["-C", "target-feature=+crt-static"]

# my router
[target.armv7-unknown-linux-musleabihf]
rustflags = ["-C", "target-feature=+crt-static"]

# my access point
[target.mips-unknown-linux-musl]
rustflags = ["-C", "target-feature=+crt-static"]

Then to compile, say for my vacuum cleaner:

$ cross build --target aarch64-unknown-linux-musl --release
Compiling hello_robot v0.1.0 (/project)
  Finished release [optimized] target(s) in 0.16s

You'll find the compiled binary in target/${target}/release (assuming you built with --release):

$ file target/aarch64-unknown-linux-musl/release/hello_robot
target/aarch64-unknown-linux-musl/release/hello_robot: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, with debug_info, not stripped

Transfer it somehow to the machine with the architectory you targetted, and it will .. just work:

localhost$ scp target/aarch64-unknown-linux-musl/release/hello_robot vacuum-robot:/data
hello_robot

vacuum-robot# /data/hello_robot
Hello, world!

As a sidenote, if you have a robot vacuum and you havent rooted it, then you are missing out.

Useful commands

If youre somewhere without rust installed, can can use the docker images. To get the list of supported targets for example:

$ docker run --rm \
    --user "$(id -u)":"$(id -g)" \
    rust:latest rustc --print target-list