Cross compiling Rust using Docker | Day in my life

February 21, 2021

Cross compiling Rust using Docker

If you are reading this blog, you are probably looking for ways to cross-compile Rust; into another target platform other than the one you are coding. If you are then, you are in for a slice of my take on how I have achieved it.

A solution, as you may have probably guessed from the title, is to use Docker. I start up a local Docker container with all the necessary tools and cross-build dependencies. I later use the running docker container to cross-compile rust code. Enough talk, let’s see the code and first off is Dockerfile.

FROM rust:latest

WORKDIR /usr/src/app

RUN apt-get update && apt-get upgrade -qq && apt-get install -qq gcc-arm-linux-gnueabihf
RUN rustup target add armv7-unknown-linux-gnueabihf

CMD ["sleep", "infinity"]

I’m building the docker image basing it on the rust:latest, which is in turn based on Debian stable. I add a cross-compile dependency arm gcc linker. Finally, use the rustup to add the arm as the target platform. If you are targeting, another platform you need to change the above Dockerfile to point to the right linker and add the same target in rustup.

As the first part of the puzzle around setting up the toolchain is done. How do you use it? Makefile to the rescue. Let’s see some of the targets that make this setup usable.

CONTAINER_NAME=rust-arm-builder

# check if rust builder is running
RUNNING = $(shell docker ps --format '{{.Names}}' | grep -w $(CONTAINER_NAME) -m1)

# build the rust image with cross compile dependencies and runs a container in background
start-env:
ifneq ($(RUNNING), $(CONTAINER_NAME))
	docker build -t rust-arm-builder .
	docker run --name $(CONTAINER_NAME) --volume $(PWD):/usr/src/app --detach $(CONTAINER_NAME)
endif

# stops the docker container
stop-env:
ifeq ($(RUNNING), $(CONTAINER_NAME))
	docker stop $(CONTAINER_NAME) && docker rm $(CONTAINER_NAME)
endif

# setting linker in .cargo/config doesn't seem to work
build: start-env
	docker exec -it $(CONTAINER_NAME) /bin/bash -c \
		"RUSTFLAGS='-C linker=arm-linux-gnueabihf-gcc' cargo build --target armv7-unknown-linux-gnueabihf --release"

target: start-env builds the docker image as specified in the above Dockerfile. Starts the container and mounts the current directory into the container giving access to source code. The guarded target only starts the container when none is running.

target: build builds the code. I had to pass in RUSTFLAGS for cargo to use the linker specified. For some reason, I couldn’t get the ~/.cargo/config to work.

I hope the above solution helps you with your cross-compiling tasks. I liked the outcome cause all dependencies are self-contained in docker. And don’t have to mess my local machine with n number of installation of dependencies and keeping it up to date. If you want to see a working solution, head over here

© Nataraj Basappa 2021